Skip to main content

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