tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

keyframes_rule.rs (20648B)


      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 
      7 use crate::derives::*;
      8 use crate::error_reporting::ContextualParseError;
      9 use crate::parser::ParserContext;
     10 use 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 };
     18 use crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
     19 use crate::shared_lock::{Locked, ToCssWithGuard};
     20 use crate::stylesheets::rule_parser::VendorPrefix;
     21 use crate::stylesheets::{CssRuleType, StylesheetContents};
     22 use crate::values::{serialize_percentage, KeyframesName};
     23 use cssparser::{
     24    parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserInput, ParserState,
     25    QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,
     26 };
     27 use servo_arc::Arc;
     28 use std::borrow::Cow;
     29 use std::fmt::{self, Write};
     30 use 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)]
     38 pub 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 
     49 impl 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 
     65 impl 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 
     84 impl 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)]
    102 pub struct KeyframePercentage(pub f32);
    103 
    104 impl ::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 
    112 impl ::std::cmp::Eq for KeyframePercentage {}
    113 
    114 impl 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 
    123 impl 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)]
    153 pub struct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>);
    154 
    155 impl 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)]
    177 pub 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 
    191 impl 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 
    201 impl 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 
    231 impl 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)]
    248 pub 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)]
    266 pub 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 
    284 impl 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)]
    394 pub 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.
    404 fn 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 
    438 impl 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 /// }
    515 struct KeyframeListParser<'a, 'b> {
    516    context: &'a mut ParserContext<'b>,
    517    shared_lock: &'a SharedRwLock,
    518 }
    519 
    520 /// Parses a keyframe list from CSS input.
    521 pub 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 
    535 impl<'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 
    541 impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {
    542    type Declaration = Arc<Locked<Keyframe>>;
    543    type Error = StyleParseErrorKind<'i>;
    544 }
    545 
    546 impl<'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 
    584 impl<'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 }