style/stylesheets/
keyframes_rule.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
6
7use crate::error_reporting::ContextualParseError;
8use crate::parser::ParserContext;
9use crate::properties::{
10    longhands::{
11        animation_composition::single_value::SpecifiedValue as SpecifiedComposition,
12        transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction,
13    },
14    parse_property_declaration_list, LonghandId, PropertyDeclaration, PropertyDeclarationBlock,
15    PropertyDeclarationId, PropertyDeclarationIdSet,
16};
17use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
18use crate::shared_lock::{Locked, ToCssWithGuard};
19use crate::stylesheets::rule_parser::VendorPrefix;
20use crate::stylesheets::{CssRuleType, StylesheetContents};
21use crate::values::{serialize_percentage, KeyframesName};
22use cssparser::{
23    parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserInput, ParserState,
24    QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
25};
26use servo_arc::Arc;
27use std::borrow::Cow;
28use std::fmt::{self, Write};
29use style_traits::{
30    CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss,
31};
32
33/// A [`@keyframes`][keyframes] rule.
34///
35/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
36#[derive(Debug, ToShmem)]
37pub struct KeyframesRule {
38    /// The name of the current animation.
39    pub name: KeyframesName,
40    /// The keyframes specified for this CSS rule.
41    pub keyframes: Vec<Arc<Locked<Keyframe>>>,
42    /// Vendor prefix type the @keyframes has.
43    pub vendor_prefix: Option<VendorPrefix>,
44    /// The line and column of the rule's source code.
45    pub source_location: SourceLocation,
46}
47
48impl ToCssWithGuard for KeyframesRule {
49    // Serialization of KeyframesRule is not specced.
50    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
51        dest.write_str("@keyframes ")?;
52        self.name.to_css(&mut CssWriter::new(dest))?;
53        dest.write_str(" {")?;
54        let iter = self.keyframes.iter();
55        for lock in iter {
56            dest.write_str("\n")?;
57            let keyframe = lock.read_with(&guard);
58            keyframe.to_css(guard, dest)?;
59        }
60        dest.write_str("\n}")
61    }
62}
63
64impl KeyframesRule {
65    /// Returns the index of the last keyframe that matches the given selector.
66    /// If the selector is not valid, or no keyframe is found, returns None.
67    ///
68    /// Related spec:
69    /// <https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule>
70    pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {
71        let mut input = ParserInput::new(selector);
72        if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) {
73            for (i, keyframe) in self.keyframes.iter().enumerate().rev() {
74                if keyframe.read_with(guard).selector == selector {
75                    return Some(i);
76                }
77            }
78        }
79        None
80    }
81}
82
83impl DeepCloneWithLock for KeyframesRule {
84    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
85        KeyframesRule {
86            name: self.name.clone(),
87            keyframes: self
88                .keyframes
89                .iter()
90                .map(|x| Arc::new(lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard))))
91                .collect(),
92            vendor_prefix: self.vendor_prefix.clone(),
93            source_location: self.source_location.clone(),
94        }
95    }
96}
97
98/// A number from 0 to 1, indicating the percentage of the animation when this
99/// keyframe should run.
100#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]
101pub struct KeyframePercentage(pub f32);
102
103impl ::std::cmp::Ord for KeyframePercentage {
104    #[inline]
105    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
106        // We know we have a number from 0 to 1, so unwrap() here is safe.
107        self.0.partial_cmp(&other.0).unwrap()
108    }
109}
110
111impl ::std::cmp::Eq for KeyframePercentage {}
112
113impl ToCss for KeyframePercentage {
114    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
115    where
116        W: Write,
117    {
118        serialize_percentage(self.0, dest)
119    }
120}
121
122impl KeyframePercentage {
123    /// Trivially constructs a new `KeyframePercentage`.
124    #[inline]
125    pub fn new(value: f32) -> KeyframePercentage {
126        debug_assert!(value >= 0. && value <= 1.);
127        KeyframePercentage(value)
128    }
129
130    fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> {
131        let token = input.next()?.clone();
132        match token {
133            Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => {
134                Ok(KeyframePercentage::new(0.))
135            },
136            Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => {
137                Ok(KeyframePercentage::new(1.))
138            },
139            Token::Percentage {
140                unit_value: percentage,
141                ..
142            } if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)),
143            _ => Err(input.new_unexpected_token_error(token)),
144        }
145    }
146}
147
148/// A keyframes selector is a list of percentages or from/to symbols, which are
149/// converted at parse time to percentages.
150#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
151#[css(comma)]
152pub struct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>);
153
154impl KeyframeSelector {
155    /// Return the list of percentages this selector contains.
156    #[inline]
157    pub fn percentages(&self) -> &[KeyframePercentage] {
158        &self.0
159    }
160
161    /// A dummy public function so we can write a unit test for this.
162    pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector {
163        KeyframeSelector(percentages)
164    }
165
166    /// Parse a keyframe selector from CSS input.
167    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
168        input
169            .parse_comma_separated(KeyframePercentage::parse)
170            .map(KeyframeSelector)
171    }
172}
173
174/// A keyframe.
175#[derive(Debug, ToShmem)]
176pub struct Keyframe {
177    /// The selector this keyframe was specified from.
178    pub selector: KeyframeSelector,
179
180    /// The declaration block that was declared inside this keyframe.
181    ///
182    /// Note that `!important` rules in keyframes don't apply, but we keep this
183    /// `Arc` just for convenience.
184    pub block: Arc<Locked<PropertyDeclarationBlock>>,
185
186    /// The line and column of the rule's source code.
187    pub source_location: SourceLocation,
188}
189
190impl ToCssWithGuard for Keyframe {
191    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
192        self.selector.to_css(&mut CssWriter::new(dest))?;
193        dest.write_str(" { ")?;
194        self.block.read_with(guard).to_css(dest)?;
195        dest.write_str(" }")?;
196        Ok(())
197    }
198}
199
200impl Keyframe {
201    /// Parse a CSS keyframe.
202    pub fn parse<'i>(
203        css: &'i str,
204        parent_stylesheet_contents: &StylesheetContents,
205        lock: &SharedRwLock,
206    ) -> Result<Arc<Locked<Self>>, ParseError<'i>> {
207        let url_data = parent_stylesheet_contents.url_data.read();
208        let namespaces = parent_stylesheet_contents.namespaces.read();
209        let mut context = ParserContext::new(
210            parent_stylesheet_contents.origin,
211            &url_data,
212            Some(CssRuleType::Keyframe),
213            ParsingMode::DEFAULT,
214            parent_stylesheet_contents.quirks_mode,
215            Cow::Borrowed(&*namespaces),
216            None,
217            None,
218        );
219        let mut input = ParserInput::new(css);
220        let mut input = Parser::new(&mut input);
221
222        let mut rule_parser = KeyframeListParser {
223            context: &mut context,
224            shared_lock: &lock,
225        };
226        parse_one_rule(&mut input, &mut rule_parser)
227    }
228}
229
230impl DeepCloneWithLock for Keyframe {
231    /// Deep clones this Keyframe.
232    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Keyframe {
233        Keyframe {
234            selector: self.selector.clone(),
235            block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
236            source_location: self.source_location.clone(),
237        }
238    }
239}
240
241/// A keyframes step value. This can be a synthetised keyframes animation, that
242/// is, one autogenerated from the current computed values, or a list of
243/// declarations to apply.
244///
245/// TODO: Find a better name for this?
246#[derive(Clone, Debug, MallocSizeOf)]
247pub enum KeyframesStepValue {
248    /// A step formed by a declaration block specified by the CSS.
249    Declarations {
250        /// The declaration block per se.
251        #[cfg_attr(
252            feature = "gecko",
253            ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile"
254        )]
255        #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
256        block: Arc<Locked<PropertyDeclarationBlock>>,
257    },
258    /// A synthetic step computed from the current computed values at the time
259    /// of the animation.
260    ComputedValues,
261}
262
263/// A single step from a keyframe animation.
264#[derive(Clone, Debug, MallocSizeOf)]
265pub struct KeyframesStep {
266    /// The percentage of the animation duration when this step starts.
267    pub start_percentage: KeyframePercentage,
268    /// Declarations that will determine the final style during the step, or
269    /// `ComputedValues` if this is an autogenerated step.
270    pub value: KeyframesStepValue,
271    /// Whether an animation-timing-function declaration exists in the list of
272    /// declarations.
273    ///
274    /// This is used to know when to override the keyframe animation style.
275    pub declared_timing_function: bool,
276    /// Whether an animation-composition declaration exists in the list of
277    /// declarations.
278    ///
279    /// This is used to know when to override the keyframe animation style.
280    pub declared_composition: bool,
281}
282
283impl KeyframesStep {
284    #[inline]
285    fn new(
286        start_percentage: KeyframePercentage,
287        value: KeyframesStepValue,
288        guard: &SharedRwLockReadGuard,
289    ) -> Self {
290        let mut declared_timing_function = false;
291        let mut declared_composition = false;
292        if let KeyframesStepValue::Declarations { ref block } = value {
293            for prop_decl in block.read_with(guard).declarations().iter() {
294                match *prop_decl {
295                    PropertyDeclaration::AnimationTimingFunction(..) => {
296                        declared_timing_function = true;
297                    },
298                    PropertyDeclaration::AnimationComposition(..) => {
299                        declared_composition = true;
300                    },
301                    _ => continue,
302                }
303                // Don't need to continue the loop if both are found.
304                if declared_timing_function && declared_composition {
305                    break;
306                }
307            }
308        }
309
310        KeyframesStep {
311            start_percentage,
312            value,
313            declared_timing_function,
314            declared_composition,
315        }
316    }
317
318    /// Return specified PropertyDeclaration.
319    #[inline]
320    fn get_declared_property<'a>(
321        &'a self,
322        guard: &'a SharedRwLockReadGuard,
323        property: LonghandId,
324    ) -> Option<&'a PropertyDeclaration> {
325        match self.value {
326            KeyframesStepValue::Declarations { ref block } => {
327                let guard = block.read_with(guard);
328                let (declaration, _) = guard
329                    .get(PropertyDeclarationId::Longhand(property))
330                    .unwrap();
331                match *declaration {
332                    PropertyDeclaration::CSSWideKeyword(..) => None,
333                    // FIXME: Bug 1710735: Support css variable in @keyframes rule.
334                    PropertyDeclaration::WithVariables(..) => None,
335                    _ => Some(declaration),
336                }
337            },
338            KeyframesStepValue::ComputedValues => {
339                panic!("Shouldn't happen to set this property in missing keyframes")
340            },
341        }
342    }
343
344    /// Return specified TransitionTimingFunction if this KeyframesSteps has
345    /// 'animation-timing-function'.
346    pub fn get_animation_timing_function(
347        &self,
348        guard: &SharedRwLockReadGuard,
349    ) -> Option<SpecifiedTimingFunction> {
350        if !self.declared_timing_function {
351            return None;
352        }
353
354        self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
355            .map(|decl| {
356                match *decl {
357                    PropertyDeclaration::AnimationTimingFunction(ref value) => {
358                        // Use the first value
359                        value.0[0].clone()
360                    },
361                    _ => unreachable!("Unexpected PropertyDeclaration"),
362                }
363            })
364    }
365
366    /// Return CompositeOperation if this KeyframesSteps has 'animation-composition'.
367    pub fn get_animation_composition(
368        &self,
369        guard: &SharedRwLockReadGuard,
370    ) -> Option<SpecifiedComposition> {
371        if !self.declared_composition {
372            return None;
373        }
374
375        self.get_declared_property(guard, LonghandId::AnimationComposition)
376            .map(|decl| {
377                match *decl {
378                    PropertyDeclaration::AnimationComposition(ref value) => {
379                        // Use the first value
380                        value.0[0].clone()
381                    },
382                    _ => unreachable!("Unexpected PropertyDeclaration"),
383                }
384            })
385    }
386}
387
388/// This structure represents a list of animation steps computed from the list
389/// of keyframes, in order.
390///
391/// It only takes into account animable properties.
392#[derive(Clone, Debug, MallocSizeOf)]
393pub struct KeyframesAnimation {
394    /// The difference steps of the animation.
395    pub steps: Vec<KeyframesStep>,
396    /// The properties that change in this animation.
397    pub properties_changed: PropertyDeclarationIdSet,
398    /// Vendor prefix type the @keyframes has.
399    pub vendor_prefix: Option<VendorPrefix>,
400}
401
402/// Get all the animated properties in a keyframes animation.
403fn get_animated_properties(
404    keyframes: &[Arc<Locked<Keyframe>>],
405    guard: &SharedRwLockReadGuard,
406) -> PropertyDeclarationIdSet {
407    let mut ret = PropertyDeclarationIdSet::default();
408    // NB: declarations are already deduplicated, so we don't have to check for
409    // it here.
410    for keyframe in keyframes {
411        let keyframe = keyframe.read_with(&guard);
412        let block = keyframe.block.read_with(guard);
413        // CSS Animations spec clearly defines that properties with !important
414        // in keyframe rules are invalid and ignored, but it's still ambiguous
415        // whether we should drop the !important properties or retain the
416        // properties when they are set via CSSOM. So we assume there might
417        // be properties with !important in keyframe rules here.
418        // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
419        for declaration in block.normal_declaration_iter() {
420            let declaration_id = declaration.id();
421
422            if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display) {
423                continue;
424            }
425
426            if !declaration_id.is_animatable() {
427                continue;
428            }
429
430            ret.insert(declaration_id);
431        }
432    }
433
434    ret
435}
436
437impl KeyframesAnimation {
438    /// Create a keyframes animation from a given list of keyframes.
439    ///
440    /// This will return a keyframe animation with empty steps and
441    /// properties_changed if the list of keyframes is empty, or there are no
442    /// animated properties obtained from the keyframes.
443    ///
444    /// Otherwise, this will compute and sort the steps used for the animation,
445    /// and return the animation object.
446    pub fn from_keyframes(
447        keyframes: &[Arc<Locked<Keyframe>>],
448        vendor_prefix: Option<VendorPrefix>,
449        guard: &SharedRwLockReadGuard,
450    ) -> Self {
451        let mut result = KeyframesAnimation {
452            steps: vec![],
453            properties_changed: PropertyDeclarationIdSet::default(),
454            vendor_prefix,
455        };
456
457        if keyframes.is_empty() {
458            return result;
459        }
460
461        result.properties_changed = get_animated_properties(keyframes, guard);
462        if result.properties_changed.is_empty() {
463            return result;
464        }
465
466        for keyframe in keyframes {
467            let keyframe = keyframe.read_with(&guard);
468            for percentage in keyframe.selector.0.iter() {
469                result.steps.push(KeyframesStep::new(
470                    *percentage,
471                    KeyframesStepValue::Declarations {
472                        block: keyframe.block.clone(),
473                    },
474                    guard,
475                ));
476            }
477        }
478
479        // Sort by the start percentage, so we can easily find a frame.
480        result.steps.sort_by_key(|step| step.start_percentage);
481
482        // Prepend autogenerated keyframes if appropriate.
483        if result.steps[0].start_percentage.0 != 0. {
484            result.steps.insert(
485                0,
486                KeyframesStep::new(
487                    KeyframePercentage::new(0.),
488                    KeyframesStepValue::ComputedValues,
489                    guard,
490                ),
491            );
492        }
493
494        if result.steps.last().unwrap().start_percentage.0 != 1. {
495            result.steps.push(KeyframesStep::new(
496                KeyframePercentage::new(1.),
497                KeyframesStepValue::ComputedValues,
498                guard,
499            ));
500        }
501
502        result
503    }
504}
505
506/// Parses a keyframes list, like:
507/// 0%, 50% {
508///     width: 50%;
509/// }
510///
511/// 40%, 60%, 100% {
512///     width: 100%;
513/// }
514struct KeyframeListParser<'a, 'b> {
515    context: &'a mut ParserContext<'b>,
516    shared_lock: &'a SharedRwLock,
517}
518
519/// Parses a keyframe list from CSS input.
520pub fn parse_keyframe_list<'a>(
521    context: &mut ParserContext<'a>,
522    input: &mut Parser,
523    shared_lock: &SharedRwLock,
524) -> Vec<Arc<Locked<Keyframe>>> {
525    let mut parser = KeyframeListParser {
526        context,
527        shared_lock,
528    };
529    RuleBodyParser::new(input, &mut parser)
530        .filter_map(Result::ok)
531        .collect()
532}
533
534impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {
535    type Prelude = ();
536    type AtRule = Arc<Locked<Keyframe>>;
537    type Error = StyleParseErrorKind<'i>;
538}
539
540impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
541    type Declaration = Arc<Locked<Keyframe>>;
542    type Error = StyleParseErrorKind<'i>;
543}
544
545impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {
546    type Prelude = KeyframeSelector;
547    type QualifiedRule = Arc<Locked<Keyframe>>;
548    type Error = StyleParseErrorKind<'i>;
549
550    fn parse_prelude<'t>(
551        &mut self,
552        input: &mut Parser<'i, 't>,
553    ) -> Result<Self::Prelude, ParseError<'i>> {
554        let start_position = input.position();
555        KeyframeSelector::parse(input).map_err(|e| {
556            let location = e.location;
557            let error = ContextualParseError::InvalidKeyframeRule(
558                input.slice_from(start_position),
559                e.clone(),
560            );
561            self.context.log_css_error(location, error);
562            e
563        })
564    }
565
566    fn parse_block<'t>(
567        &mut self,
568        selector: Self::Prelude,
569        start: &ParserState,
570        input: &mut Parser<'i, 't>,
571    ) -> Result<Self::QualifiedRule, ParseError<'i>> {
572        let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| {
573            parse_property_declaration_list(&p, input, &[])
574        });
575        Ok(Arc::new(self.shared_lock.wrap(Keyframe {
576            selector,
577            block: Arc::new(self.shared_lock.wrap(block)),
578            source_location: start.source_location(),
579        })))
580    }
581}
582
583impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>>
584    for KeyframeListParser<'a, 'b>
585{
586    fn parse_qualified(&self) -> bool {
587        true
588    }
589    fn parse_declarations(&self) -> bool {
590        false
591    }
592}