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