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