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