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