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_pref="layout.css.animation-composition.enabled",
spec="https://drafts.csswg.org/css-animations-2/#animation-composition",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.predefined_type(

View file

@ -6,6 +6,7 @@
use crate::error_reporting::ContextualParseError;
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::LonghandIdSet;
use crate::properties::{Importance, PropertyDeclaration};
@ -279,40 +280,81 @@ pub struct KeyframesStep {
/// Declarations that will determine the final style during the step, or
/// `ComputedValues` if this is an autogenerated step.
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.
///
/// This is used to know when to override the keyframe animation style.
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 {
#[inline]
fn new(
percentage: KeyframePercentage,
start_percentage: KeyframePercentage,
value: KeyframesStepValue,
guard: &SharedRwLockReadGuard,
) -> Self {
let declared_timing_function = match value {
KeyframesStepValue::Declarations { ref block } => block
.read_with(guard)
.declarations()
.iter()
.any(|prop_decl| match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => true,
_ => false,
}),
_ => false,
};
let mut declared_timing_function = false;
let mut declared_composition = false;
if let KeyframesStepValue::Declarations { ref block } = value {
for prop_decl in block.read_with(guard).declarations().iter() {
match *prop_decl {
PropertyDeclaration::AnimationTimingFunction(..) => {
declared_timing_function = true;
},
PropertyDeclaration::AnimationComposition(..) => {
declared_composition = true;
},
_ => continue,
}
// Don't need to continue the loop if both are found.
if declared_timing_function && declared_composition {
break;
}
}
}
KeyframesStep {
start_percentage: percentage,
value: value,
declared_timing_function: declared_timing_function,
start_percentage,
value,
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(
&self,
guard: &SharedRwLockReadGuard,
@ -320,28 +362,38 @@ impl KeyframesStep {
if !self.declared_timing_function {
return None;
}
match self.value {
KeyframesStepValue::Declarations { ref block } => {
let guard = block.read_with(guard);
let (declaration, _) = guard
.get(PropertyDeclarationId::Longhand(
LonghandId::AnimationTimingFunction,
))
.unwrap();
match *declaration {
self.get_declared_property(guard, LonghandId::AnimationTimingFunction)
.map(|decl| {
match *decl {
PropertyDeclaration::AnimationTimingFunction(ref value) => {
// Use the first value.
Some(value.0[0].clone())
// Use the first value
value.0[0].clone()
},
PropertyDeclaration::CSSWideKeyword(..) => None,
PropertyDeclaration::WithVariables(..) => None,
_ => panic!(),
_ => unreachable!("Unexpected PropertyDeclaration"),
}
},
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"),
}
})
}
}