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        KeyframePercentage(value)
129    }
130
131    fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> {
132        let token = input.next()?.clone();
133        match token {
134            Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => {
135                Ok(KeyframePercentage::new(0.))
136            },
137            Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => {
138                Ok(KeyframePercentage::new(1.))
139            },
140            Token::Percentage {
141                unit_value: percentage,
142                ..
143            } if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)),
144            _ => Err(input.new_unexpected_token_error(token)),
145        }
146    }
147}
148
149/// A single `<keyframe-selector>`:
150/// `<keyframe-selector> = from | to | <percentage [0,100]> | <timeline-range-name> <percentage>`
151/// It could be a percentage, from/to, or a timeline range name together with a percentage.
152/// https://drafts.csswg.org/scroll-animations-1/#named-range-keyframes
153#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]
154pub struct KeyframeSelector {
155    /// The named timeline range name component of the selector. If it is omitted, we use
156    /// `TimelineRangeName::None`. Note that `TimelineRangeName::Normal` is not used for the
157    /// selector.
158    pub range_name: TimelineRangeName,
159    /// The percentage component of the selector. It is a percentage or a from/to symbol, which is
160    /// converted at parse time to percentage.
161    pub percentage: KeyframePercentage,
162}
163
164impl KeyframeSelector {
165    /// Returns Self as a percentage.
166    fn from_percentage(percentage: KeyframePercentage) -> Self {
167        debug_assert!(percentage.0 >= 0. && percentage.0 <= 1.);
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::from_percentage(percentage));
179        }
180
181        // We parse the the extension of keyframe selector for scroll-driven animation.
182        if !static_prefs::pref!("layout.css.scroll-driven-animations.enabled") {
183            let location = input.current_source_location();
184            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
185        }
186
187        // `<timeline-range-name> <percentage>`
188        // Note that <percentage> could be out of [0,100].
189        Ok(Self {
190            range_name: TimelineRangeName::parse(input)?,
191            percentage: KeyframePercentage::new(input.expect_percentage()?),
192        })
193    }
194}
195
196/// A list of `<keyframe-selector>`s.
197#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]
198#[css(comma)]
199pub struct KeyframeSelectors(#[css(iterable)] Vec<KeyframeSelector>);
200
201impl KeyframeSelectors {
202    /// A dummy public function so we can write a unit test for this.
203    pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelectors {
204        KeyframeSelectors(
205            percentages
206                .into_iter()
207                .map(KeyframeSelector::from_percentage)
208                .collect(),
209        )
210    }
211
212    /// Parse the keyframe selectors from CSS input.
213    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
214        input
215            .parse_comma_separated(KeyframeSelector::parse)
216            .map(KeyframeSelectors)
217    }
218}
219
220/// A keyframe.
221#[derive(Debug, ToShmem)]
222pub struct Keyframe {
223    /// The selector this keyframe was specified from.
224    pub selector: KeyframeSelectors,
225
226    /// The declaration block that was declared inside this keyframe.
227    ///
228    /// Note that `!important` rules in keyframes don't apply, but we keep this
229    /// `Arc` just for convenience.
230    pub block: Arc<Locked<PropertyDeclarationBlock>>,
231
232    /// The line and column of the rule's source code.
233    pub source_location: SourceLocation,
234}
235
236impl ToCssWithGuard for Keyframe {
237    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
238        self.selector.to_css(&mut CssWriter::new(dest))?;
239        dest.write_str(" { ")?;
240        self.block.read_with(guard).to_css(dest)?;
241        dest.write_str(" }")?;
242        Ok(())
243    }
244}
245
246impl Keyframe {
247    /// Parse a CSS keyframe.
248    pub fn parse<'i>(
249        css: &'i str,
250        parent_stylesheet_contents: &StylesheetContents,
251        lock: &SharedRwLock,
252    ) -> Result<Arc<Locked<Self>>, ParseError<'i>> {
253        let url_data = &parent_stylesheet_contents.url_data;
254        let namespaces = &parent_stylesheet_contents.namespaces;
255        let mut context = ParserContext::new(
256            parent_stylesheet_contents.origin,
257            &url_data,
258            Some(CssRuleType::Keyframe),
259            ParsingMode::DEFAULT,
260            parent_stylesheet_contents.quirks_mode,
261            Cow::Borrowed(&*namespaces),
262            None,
263            None,
264            /* attr_taint */ Default::default(),
265        );
266        let mut input = ParserInput::new(css);
267        let mut input = Parser::new(&mut input);
268
269        let mut rule_parser = KeyframeListParser {
270            context: &mut context,
271            shared_lock: &lock,
272        };
273        parse_one_rule(&mut input, &mut rule_parser)
274    }
275}
276
277impl DeepCloneWithLock for Keyframe {
278    /// Deep clones this Keyframe.
279    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Keyframe {
280        Keyframe {
281            selector: self.selector.clone(),
282            block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),
283            source_location: self.source_location.clone(),
284        }
285    }
286}
287
288/// A keyframes step value. This can be a synthetised keyframes animation, that
289/// is, one autogenerated from the current computed values, or a list of
290/// declarations to apply.
291///
292/// TODO: Find a better name for this?
293#[derive(Clone, Debug, MallocSizeOf)]
294pub enum KeyframesStepValue {
295    /// A step formed by a declaration block specified by the CSS.
296    Declarations {
297        /// The declaration block per se.
298        #[cfg_attr(
299            feature = "gecko",
300            ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile"
301        )]
302        #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")]
303        block: Arc<Locked<PropertyDeclarationBlock>>,
304    },
305    /// A synthetic step computed from the current computed values at the time
306    /// of the animation.
307    ComputedValues,
308}
309
310/// A single step from a keyframe animation.
311#[derive(Clone, Debug, MallocSizeOf)]
312pub struct KeyframesStep {
313    /// The offset of the animation duration when this step starts.
314    pub start_offset: KeyframeSelector,
315    /// Declarations that will determine the final style during the step, or
316    /// `ComputedValues` if this is an autogenerated step.
317    pub value: KeyframesStepValue,
318    /// Whether an animation-timing-function declaration exists in the list of
319    /// declarations.
320    ///
321    /// This is used to know when to override the keyframe animation style.
322    pub declared_timing_function: bool,
323    /// Whether an animation-composition declaration exists in the list of
324    /// declarations.
325    ///
326    /// This is used to know when to override the keyframe animation style.
327    pub declared_composition: bool,
328}
329
330impl KeyframesStep {
331    #[inline]
332    fn new(
333        start_offset: KeyframeSelector,
334        value: KeyframesStepValue,
335        guard: &SharedRwLockReadGuard,
336    ) -> Self {
337        let mut declared_timing_function = false;
338        let mut declared_composition = false;
339        if let KeyframesStepValue::Declarations { ref block } = value {
340            for prop_decl in block.read_with(guard).declarations().iter() {
341                match *prop_decl {
342                    PropertyDeclaration::AnimationTimingFunction(..) => {
343                        declared_timing_function = true;
344                    },
345                    PropertyDeclaration::AnimationComposition(..) => {
346                        declared_composition = true;
347                    },
348                    _ => continue,
349                }
350                // Don't need to continue the loop if both are found.
351                if declared_timing_function && declared_composition {
352                    break;
353                }
354            }
355        }
356
357        KeyframesStep {
358            start_offset,
359            value,
360            declared_timing_function,
361            declared_composition,
362        }
363    }
364
365    /// Return specified PropertyDeclaration.
366    #[inline]
367    fn get_declared_property<'a>(
368        &'a self,
369        guard: &'a SharedRwLockReadGuard,
370        property: LonghandId,
371    ) -> Option<&'a PropertyDeclaration> {
372        match self.value {
373            KeyframesStepValue::Declarations { ref block } => {
374                let guard = block.read_with(guard);
375                let (declaration, _) = guard
376                    .get(PropertyDeclarationId::Longhand(property))
377                    .unwrap();
378                match *declaration {
379                    PropertyDeclaration::CSSWideKeyword(..) => None,
380                    // FIXME: Bug 1710735: Support css variable in @keyframes rule.
381                    PropertyDeclaration::WithVariables(..) => None,
382                    _ => Some(declaration),
383                }
384            },
385            KeyframesStepValue::ComputedValues => {
386                panic!("Shouldn't happen to set this property in missing keyframes")
387            },
388        }
389    }
390
391    /// Return specified TransitionTimingFunction if this KeyframesSteps has
392    /// 'animation-timing-function'.
393    pub fn get_animation_timing_function(
394        &self,
395        guard: &SharedRwLockReadGuard,
396    ) -> Option<SpecifiedTimingFunction> {
397        if !self.declared_timing_function {
398            return None;
399        }
400
401        self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
402            .map(|decl| {
403                match *decl {
404                    PropertyDeclaration::AnimationTimingFunction(ref value) => {
405                        // Use the first value
406                        value.0[0].clone()
407                    },
408                    _ => unreachable!("Unexpected PropertyDeclaration"),
409                }
410            })
411    }
412
413    /// Return CompositeOperation if this KeyframesSteps has 'animation-composition'.
414    pub fn get_animation_composition(
415        &self,
416        guard: &SharedRwLockReadGuard,
417    ) -> Option<SpecifiedComposition> {
418        if !self.declared_composition {
419            return None;
420        }
421
422        self.get_declared_property(guard, LonghandId::AnimationComposition)
423            .map(|decl| {
424                match *decl {
425                    PropertyDeclaration::AnimationComposition(ref value) => {
426                        // Use the first value
427                        value.0[0].clone()
428                    },
429                    _ => unreachable!("Unexpected PropertyDeclaration"),
430                }
431            })
432    }
433}
434
435/// This structure represents a list of animation steps computed from the list
436/// of keyframes, in order.
437///
438/// It only takes into account animable properties.
439#[derive(Clone, Debug, MallocSizeOf)]
440pub struct KeyframesAnimation {
441    /// The different steps of the animation.
442    pub steps: Vec<KeyframesStep>,
443    /// The different steps of the animation. Those steps are only for keyframe selectors with
444    /// timeline range names. We intentionally use a different vector because it is unsorted and we
445    /// would like to maintain its specified order when grouping them. Also, per spec, the computed
446    /// order requires us to pull percentage-only keyframes to the front and sort them, so using a
447    /// separate vector makes it easier to group them to maintain the computed order.
448    /// https://drafts.csswg.org/css-animations-2/#keyframe-processing
449    /// https://github.com/w3c/csswg-drafts/issues/8507
450    pub steps_with_range_name: Vec<KeyframesStep>,
451    /// The properties that change in this animation.
452    pub properties_changed: PropertyDeclarationIdSet,
453    /// Vendor prefix type the @keyframes has.
454    pub vendor_prefix: Option<VendorPrefix>,
455}
456
457/// Get all the animated properties in a keyframes animation.
458fn get_animated_properties(
459    keyframes: &[Arc<Locked<Keyframe>>],
460    guard: &SharedRwLockReadGuard,
461) -> PropertyDeclarationIdSet {
462    let mut ret = PropertyDeclarationIdSet::default();
463    // NB: declarations are already deduplicated, so we don't have to check for
464    // it here.
465    for keyframe in keyframes {
466        let keyframe = keyframe.read_with(&guard);
467        let block = keyframe.block.read_with(guard);
468        // CSS Animations spec clearly defines that properties with !important
469        // in keyframe rules are invalid and ignored, but it's still ambiguous
470        // whether we should drop the !important properties or retain the
471        // properties when they are set via CSSOM. So we assume there might
472        // be properties with !important in keyframe rules here.
473        // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824
474        for declaration in block.normal_declaration_iter() {
475            let declaration_id = declaration.id();
476
477            if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display) {
478                continue;
479            }
480
481            if !declaration_id.is_animatable() {
482                continue;
483            }
484
485            ret.insert(declaration_id);
486        }
487    }
488
489    ret
490}
491
492impl KeyframesAnimation {
493    /// Create a keyframes animation from a given list of keyframes.
494    ///
495    /// This will return a keyframe animation with empty steps and
496    /// properties_changed if the list of keyframes is empty, or there are no
497    /// animated properties obtained from the keyframes.
498    ///
499    /// Otherwise, this will compute and sort the steps used for the animation,
500    /// and return the animation object.
501    pub fn from_keyframes(
502        keyframes: &[Arc<Locked<Keyframe>>],
503        vendor_prefix: Option<VendorPrefix>,
504        guard: &SharedRwLockReadGuard,
505    ) -> Self {
506        let mut result = KeyframesAnimation {
507            steps: vec![],
508            steps_with_range_name: vec![],
509            properties_changed: PropertyDeclarationIdSet::default(),
510            vendor_prefix,
511        };
512
513        if keyframes.is_empty() {
514            return result;
515        }
516
517        result.properties_changed = get_animated_properties(keyframes, guard);
518        if result.properties_changed.is_empty() {
519            return result;
520        }
521
522        // The steps with percentage only.
523        let mut steps = vec![];
524
525        for keyframe in keyframes {
526            let keyframe = keyframe.read_with(&guard);
527            for selector in keyframe.selector.0.iter() {
528                let step = KeyframesStep::new(
529                    *selector,
530                    KeyframesStepValue::Declarations {
531                        block: keyframe.block.clone(),
532                    },
533                    guard,
534                );
535
536                if !selector.range_name.is_none() {
537                    result.steps_with_range_name.push(step);
538                } else {
539                    steps.push(step);
540                }
541            }
542        }
543
544        // Sort by the percentage, so we can easily find a frame. Note that we only sort the
545        // keyframes with percentage since we have to maintain the order of keyframes with
546        // TimelineRange as specified.
547        steps.sort_by_key(|step| step.start_offset.percentage);
548
549        // Prepend autogenerated keyframes if appropriate.
550        //
551        // FIXME: Bug 2037642. For animation-timeline: none or auto, if all the keyframes use
552        // `<timeline-range-name>`, we shouldn't generate 0% and 100% keyframes. The better way is
553        // to fill the implicit keyframes lazily, in getKeyframes() or when using them, after they
554        // have `computedOffset` set.
555        //
556        // https://github.com/w3c/csswg-drafts/issues/13872
557        // https://drafts.csswg.org/css-animations-2/#keyframe-processing
558        if steps.is_empty() || steps[0].start_offset.percentage.0 != 0. {
559            steps.insert(
560                0,
561                KeyframesStep::new(
562                    KeyframeSelector::from_percentage(KeyframePercentage::new(0.)),
563                    KeyframesStepValue::ComputedValues,
564                    guard,
565                ),
566            );
567        }
568
569        if steps.last().unwrap().start_offset.percentage.0 != 1. {
570            steps.push(KeyframesStep::new(
571                KeyframeSelector::from_percentage(KeyframePercentage::new(1.)),
572                KeyframesStepValue::ComputedValues,
573                guard,
574            ));
575        }
576
577        result.steps = steps;
578        result
579    }
580}
581
582/// Parses a keyframes list, like:
583/// 0%, 50% {
584///     width: 50%;
585/// }
586///
587/// 40%, 60%, 100% {
588///     width: 100%;
589/// }
590struct KeyframeListParser<'a, 'b> {
591    context: &'a mut ParserContext<'b>,
592    shared_lock: &'a SharedRwLock,
593}
594
595/// Parses a keyframe list from CSS input.
596pub fn parse_keyframe_list<'a>(
597    context: &mut ParserContext<'a>,
598    input: &mut Parser,
599    shared_lock: &SharedRwLock,
600) -> Vec<Arc<Locked<Keyframe>>> {
601    let mut parser = KeyframeListParser {
602        context,
603        shared_lock,
604    };
605    RuleBodyParser::new(input, &mut parser)
606        .filter_map(Result::ok)
607        .collect()
608}
609
610impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {
611    type Prelude = ();
612    type AtRule = Arc<Locked<Keyframe>>;
613    type Error = StyleParseErrorKind<'i>;
614}
615
616impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
617    type Declaration = Arc<Locked<Keyframe>>;
618    type Error = StyleParseErrorKind<'i>;
619}
620
621impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {
622    type Prelude = KeyframeSelectors;
623    type QualifiedRule = Arc<Locked<Keyframe>>;
624    type Error = StyleParseErrorKind<'i>;
625
626    fn parse_prelude<'t>(
627        &mut self,
628        input: &mut Parser<'i, 't>,
629    ) -> Result<Self::Prelude, ParseError<'i>> {
630        let start_position = input.position();
631        KeyframeSelectors::parse(input).map_err(|e| {
632            let location = e.location;
633            let error = ContextualParseError::InvalidKeyframeRule(
634                input.slice_from(start_position),
635                e.clone(),
636            );
637            self.context.log_css_error(location, error);
638            e
639        })
640    }
641
642    fn parse_block<'t>(
643        &mut self,
644        selector: Self::Prelude,
645        start: &ParserState,
646        input: &mut Parser<'i, 't>,
647    ) -> Result<Self::QualifiedRule, ParseError<'i>> {
648        let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| {
649            parse_property_declaration_list(&p, input, &[])
650        });
651        Ok(Arc::new(self.shared_lock.wrap(Keyframe {
652            selector,
653            block: Arc::new(self.shared_lock.wrap(block)),
654            source_location: start.source_location(),
655        })))
656    }
657}
658
659impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>>
660    for KeyframeListParser<'a, 'b>
661{
662    fn parse_qualified(&self) -> bool {
663        true
664    }
665    fn parse_declarations(&self) -> bool {
666        false
667    }
668}