style: Support animation-composition in keyframe at rules

So we can specify the keyframe-specific composite operation. However,
these is a spec issue about the default composite for CSS Animations:
https://github.com/w3c/csswg-drafts/issues/7476.

I choose to use auto as the default composite for missing keyframes to match
the definition in web-animations-1 because I think this makes more sense:
> If the keyframe-specific composite operation for a keyframe is not set, the
> composite operation specified for the keyframe effect as a whole is used for
> values specified in that keyframe.

Differential Revision: https://phabricator.services.mozilla.com/D150808
This commit is contained in:
Boris Chiou 2022-07-12 19:02:04 +00:00 committed by Martin Robinson
parent 06f81aea07
commit e53f4ee4c7
2 changed files with 87 additions and 36 deletions

View file

@ -287,7 +287,6 @@ ${helpers.single_keyword(
gecko_inexhaustive=True, gecko_inexhaustive=True,
gecko_pref="layout.css.animation-composition.enabled", gecko_pref="layout.css.animation-composition.enabled",
spec="https://drafts.csswg.org/css-animations-2/#animation-composition", spec="https://drafts.csswg.org/css-animations-2/#animation-composition",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)} )}
${helpers.predefined_type( ${helpers.predefined_type(

View file

@ -6,6 +6,7 @@
use crate::error_reporting::ContextualParseError; use crate::error_reporting::ContextualParseError;
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::properties::longhands::animation_composition::single_value::SpecifiedValue as SpecifiedComposition;
use crate::properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction; use crate::properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
use crate::properties::LonghandIdSet; use crate::properties::LonghandIdSet;
use crate::properties::{Importance, PropertyDeclaration}; use crate::properties::{Importance, PropertyDeclaration};
@ -279,40 +280,81 @@ pub struct KeyframesStep {
/// Declarations that will determine the final style during the step, or /// Declarations that will determine the final style during the step, or
/// `ComputedValues` if this is an autogenerated step. /// `ComputedValues` if this is an autogenerated step.
pub value: KeyframesStepValue, pub value: KeyframesStepValue,
/// Wether a animation-timing-function declaration exists in the list of /// Whether an animation-timing-function declaration exists in the list of
/// declarations. /// declarations.
/// ///
/// This is used to know when to override the keyframe animation style. /// This is used to know when to override the keyframe animation style.
pub declared_timing_function: bool, pub declared_timing_function: bool,
/// Whether an animation-composition declaration exists in the list of
/// declarations.
///
/// This is used to know when to override the keyframe animation style.
pub declared_composition: bool,
} }
impl KeyframesStep { impl KeyframesStep {
#[inline] #[inline]
fn new( fn new(
percentage: KeyframePercentage, start_percentage: KeyframePercentage,
value: KeyframesStepValue, value: KeyframesStepValue,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
) -> Self { ) -> Self {
let declared_timing_function = match value { let mut declared_timing_function = false;
KeyframesStepValue::Declarations { ref block } => block let mut declared_composition = false;
.read_with(guard) if let KeyframesStepValue::Declarations { ref block } = value {
.declarations() for prop_decl in block.read_with(guard).declarations().iter() {
.iter() match *prop_decl {
.any(|prop_decl| match *prop_decl { PropertyDeclaration::AnimationTimingFunction(..) => {
PropertyDeclaration::AnimationTimingFunction(..) => true, declared_timing_function = true;
_ => false, },
}), PropertyDeclaration::AnimationComposition(..) => {
_ => false, declared_composition = true;
}; },
_ => continue,
}
// Don't need to continue the loop if both are found.
if declared_timing_function && declared_composition {
break;
}
}
}
KeyframesStep { KeyframesStep {
start_percentage: percentage, start_percentage,
value: value, value,
declared_timing_function: declared_timing_function, declared_timing_function,
declared_composition,
} }
} }
/// Return specified TransitionTimingFunction if this KeyframesSteps has 'animation-timing-function'. /// Return specified PropertyDeclaration.
#[inline]
fn get_declared_property<'a>(
&'a self,
guard: &'a SharedRwLockReadGuard,
property: LonghandId,
) -> Option<&'a PropertyDeclaration> {
match self.value {
KeyframesStepValue::Declarations { ref block } => {
let guard = block.read_with(guard);
let (declaration, _) = guard
.get(PropertyDeclarationId::Longhand(property))
.unwrap();
match *declaration {
PropertyDeclaration::CSSWideKeyword(..) => None,
// FIXME: Bug 1710735: Support css variable in @keyframes rule.
PropertyDeclaration::WithVariables(..) => None,
_ => Some(declaration),
}
},
KeyframesStepValue::ComputedValues => {
panic!("Shouldn't happen to set this property in missing keyframes")
},
}
}
/// Return specified TransitionTimingFunction if this KeyframesSteps has
/// 'animation-timing-function'.
pub fn get_animation_timing_function( pub fn get_animation_timing_function(
&self, &self,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
@ -320,28 +362,38 @@ impl KeyframesStep {
if !self.declared_timing_function { if !self.declared_timing_function {
return None; return None;
} }
match self.value {
KeyframesStepValue::Declarations { ref block } => { self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
let guard = block.read_with(guard); .map(|decl| {
let (declaration, _) = guard match *decl {
.get(PropertyDeclarationId::Longhand(
LonghandId::AnimationTimingFunction,
))
.unwrap();
match *declaration {
PropertyDeclaration::AnimationTimingFunction(ref value) => { PropertyDeclaration::AnimationTimingFunction(ref value) => {
// Use the first value. // Use the first value
Some(value.0[0].clone()) value.0[0].clone()
}, },
PropertyDeclaration::CSSWideKeyword(..) => None, _ => unreachable!("Unexpected PropertyDeclaration"),
PropertyDeclaration::WithVariables(..) => None,
_ => panic!(),
} }
}, })
KeyframesStepValue::ComputedValues => {
panic!("Shouldn't happen to set animation-timing-function in missing keyframes")
},
} }
/// Return CompositeOperation if this KeyframesSteps has 'animation-composition'.
pub fn get_animation_composition(
&self,
guard: &SharedRwLockReadGuard,
) -> Option<SpecifiedComposition> {
if !self.declared_composition {
return None;
}
self.get_declared_property(guard, LonghandId::AnimationComposition)
.map(|decl| {
match *decl {
PropertyDeclaration::AnimationComposition(ref value) => {
// Use the first value
value.0[0].clone()
},
_ => unreachable!("Unexpected PropertyDeclaration"),
}
})
} }
} }