Bug 1804574 - Part 1: Add auto to animation-duration longhand. r=firefox-style-system-reviewers,layout-reviewers,emilio

Also, in order to backwards-compatibility with Level 1, we have to
serialize auto as 0s for getComputedStyle in some cases, which depend on
the value of animation-timeline.

Differential Revision: https://phabricator.services.mozilla.com/D217871
This commit is contained in:
Boris Chiou 2024-08-06 18:23:14 +00:00
parent 7aa1a52024
commit b17423ff3b
18 changed files with 189 additions and 41 deletions

View File

@ -421,6 +421,7 @@ cbindgen-types = [
{ gecko = "StyleAnimationFillMode", servo = "crate::values::computed::AnimationFillMode" },
{ gecko = "StyleAnimationPlayState", servo = "crate::values::computed::AnimationPlayState" },
{ gecko = "StyleAnimationComposition", servo = "crate::values::computed::AnimationComposition" },
{ gecko = "StyleAnimationDuration", servo = "crate::values::computed::AnimationDuration" },
{ gecko = "StyleTimelineName", servo = "crate::values::computed::TimelineName" },
{ gecko = "StyleScrollAxis", servo = "crate::values::computed::ScrollAxis" },
{ gecko = "StyleViewTimelineInset", servo = "crate::values::computed::ViewTimelineInset" },

View File

@ -300,8 +300,9 @@ static already_AddRefed<CSSAnimation> BuildAnimation(
return nullptr;
}
const StyleAnimationDuration& duration = aStyle.GetAnimationDuration(animIdx);
TimingParams timing = TimingParamsFromCSSParams(
aStyle.GetAnimationDuration(animIdx).ToMilliseconds(),
duration.IsAuto() ? 0.0f : duration.AsTime().ToMilliseconds(),
aStyle.GetAnimationDelay(animIdx).ToMilliseconds(),
aStyle.GetAnimationIterationCount(animIdx),
aStyle.GetAnimationDirection(animIdx),

View File

@ -1132,7 +1132,7 @@ struct StyleAnimation {
return mTimingFunction;
}
const StyleTime& GetDelay() const { return mDelay; }
const StyleTime& GetDuration() const { return mDuration; }
const StyleAnimationDuration& GetDuration() const { return mDuration; }
nsAtom* GetName() const { return mName._0.AsAtom(); }
StyleAnimationDirection GetDirection() const { return mDirection; }
StyleAnimationFillMode GetFillMode() const { return mFillMode; }
@ -1149,7 +1149,7 @@ struct StyleAnimation {
private:
StyleComputedTimingFunction mTimingFunction{
StyleComputedTimingFunction::Keyword(StyleTimingKeyword::Ease)};
StyleTime mDuration{0.0f};
StyleAnimationDuration mDuration = StyleAnimationDuration::Auto();
StyleTime mDelay{0.0f};
StyleAnimationName mName;
StyleAnimationDirection mDirection = StyleAnimationDirection::Normal;
@ -1157,7 +1157,7 @@ struct StyleAnimation {
StyleAnimationPlayState mPlayState = StyleAnimationPlayState::Running;
StyleAnimationIterationCount mIterationCount{1.0f};
StyleAnimationComposition mComposition = StyleAnimationComposition::Replace;
StyleAnimationTimeline mTimeline{StyleAnimationTimeline::Auto()};
StyleAnimationTimeline mTimeline = StyleAnimationTimeline::Auto();
};
struct StyleScrollTimeline {
@ -1665,7 +1665,8 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
const mozilla::StyleTime& GetAnimationDelay(uint32_t aIndex) const {
return mAnimations[aIndex % mAnimationDelayCount].GetDelay();
}
const mozilla::StyleTime& GetAnimationDuration(uint32_t aIndex) const {
const mozilla::StyleAnimationDuration& GetAnimationDuration(
uint32_t aIndex) const {
return mAnimations[aIndex % mAnimationDurationCount].GetDuration();
}
mozilla::StyleAnimationDirection GetAnimationDirection(

View File

@ -13958,6 +13958,8 @@ if (IsCSSPropertyPrefEnabled("layout.css.scroll-driven-animations.enabled")) {
gCSSProperties["-moz-animation"].subproperties.push("animation-timeline");
gCSSProperties["-webkit-animation"].subproperties.push("animation-timeline");
gCSSProperties["animation-duration"].initial_values.push("auto");
gCSSProperties["animation-timeline"] = {
domProp: "animationTimeline",
inherited: false,

View File

@ -1452,6 +1452,12 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-
self.mTransitionPropertyCount > 0
}
/// Returns whether animation-timeline is initial value. We need this information to resolve
/// animation-duration.
pub fn has_initial_animation_timeline(&self) -> bool {
self.mAnimationTimelineCount == 1 && self.animation_timeline_at(0).is_auto()
}
pub fn animations_equals(&self, other: &Self) -> bool {
return self.mAnimationNameCount == other.mAnimationNameCount
&& self.mAnimationDelayCount == other.mAnimationDelayCount

View File

@ -224,16 +224,15 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"animation-duration",
"Time",
"computed::Time::zero()",
"AnimationDuration",
"computed::AnimationDuration::auto()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
initial_specified_value="specified::AnimationDuration::auto()",
vector=True,
need_index=True,
animation_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
spec="https://drafts.csswg.org/css-animations-2/#animation-duration",
affects="",
)}

View File

@ -1614,6 +1614,13 @@ pub mod style_structs {
})
}
/// Returns whether animation-timeline is initial value. We need this information to
/// resolve animation-duration.
#[cfg(feature = "servo")]
pub fn has_initial_animation_timeline(&self) -> bool {
self.animation_timeline_count() == 1 && self.animation_timeline_at(0).is_auto()
}
/// Returns whether there is any named progress timeline specified with
/// scroll-timeline-name other than `none`.
#[cfg(feature = "gecko")]

View File

@ -351,9 +351,11 @@ macro_rules! try_parse_one {
//
// https://drafts.csswg.org/css-animations-2/#animation-shorthand
//
// FIXME: Bug 1804574. The initial value of duration should be auto, per
// css-animations-2.
let has_duration = !self.animation_duration.0[i].is_zero();
// Note: animation-timeline is not serialized for now because it is always the
// initial value in this loop.
let has_duration = !self.animation_duration.0[i].is_auto()
&& (static_prefs::pref!("layout.css.scroll-driven-animations.enabled")
|| !self.animation_duration.0[i].is_zero());
let has_timing_function = !self.animation_timing_function.0[i].is_ease();
let has_delay = !self.animation_delay.0[i].is_zero();
let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
@ -368,7 +370,7 @@ macro_rules! try_parse_one {
let mut writer = SequenceWriter::new(dest, " ");
// To avoid ambiguity, we have to serialize duration if both duration is initial
// To avoid ambiguity, we have to serialize duration if duration is initial
// but delay is not. (In other words, it's ambiguous if we serialize delay only.)
if has_duration || has_delay {
writer.item(&self.animation_duration.0[i])?;

View File

@ -4,9 +4,10 @@
//! Computed values for properties related to animations and transitions
use crate::values::computed::{Context, LengthPercentage, ToComputedValue};
use crate::values::computed::{Context, LengthPercentage, Time, ToComputedValue};
use crate::values::generics::animation as generics;
use crate::values::specified::animation as specified;
use crate::values::CSSFloat;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
@ -15,6 +16,20 @@ pub use crate::values::specified::animation::{
ScrollAxis, TimelineName, TransitionBehavior, TransitionProperty,
};
/// A computed value for the `animation-duration` property.
pub type AnimationDuration = generics::GenericAnimationDuration<Time>;
impl AnimationDuration {
/// Returns the amount of seconds this time represents.
#[inline]
pub fn seconds(&self) -> CSSFloat {
match *self {
Self::Auto => 0.0,
Self::Time(ref t) => t.seconds(),
}
}
}
/// A computed value for the `animation-iteration-count` property.
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]
#[repr(C)]

View File

@ -42,9 +42,9 @@ pub use self::align::{
pub use self::align::{AlignSelf, JustifySelf};
pub use self::angle::Angle;
pub use self::animation::{
AnimationComposition, AnimationDirection, AnimationFillMode, AnimationIterationCount,
AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis, TimelineName,
TransitionBehavior, TransitionProperty, ViewTimelineInset,
AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
AnimationIterationCount, AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis,
TimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset,
};
pub use self::background::{BackgroundRepeat, BackgroundSize};
pub use self::basic_shape::FillRule;

View File

@ -5,6 +5,7 @@
//! Computed time values.
use crate::values::CSSFloat;
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
@ -22,11 +23,6 @@ impl Time {
Time { seconds }
}
/// Returns `0s`.
pub fn zero() -> Self {
Self::from_seconds(0.0)
}
/// Returns the amount of seconds this time represents.
#[inline]
pub fn seconds(&self) -> CSSFloat {
@ -43,3 +39,13 @@ impl ToCss for Time {
dest.write_char('s')
}
}
impl Zero for Time {
fn zero() -> Self {
Self::from_seconds(0.0)
}
fn is_zero(&self) -> bool {
self.seconds == 0.
}
}

View File

@ -6,9 +6,76 @@
use crate::values::generics::length::GenericLengthPercentageOrAuto;
use crate::values::specified::animation::{ScrollAxis, ScrollFunction, TimelineName};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
/// The `animation-duration` property.
///
/// https://drafts.csswg.org/css-animations-2/#animation-duration
#[derive(
Clone,
Copy,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToShmem,
)]
#[repr(C, u8)]
pub enum GenericAnimationDuration<T> {
/// The initial value. However, we serialize this as 0s if the preference is disabled.
Auto,
/// The time value, <time [0s,∞]>.
Time(T),
}
pub use self::GenericAnimationDuration as AnimationDuration;
impl<T> AnimationDuration<T> {
/// Returns the `auto` value.
pub fn auto() -> Self {
Self::Auto
}
/// Returns true if it is `auto`.
pub fn is_auto(&self) -> bool {
matches!(*self, Self::Auto)
}
}
impl<T: Zero> Zero for AnimationDuration<T> {
fn zero() -> Self {
Self::Time(T::zero())
}
fn is_zero(&self) -> bool {
match *self {
Self::Time(ref t) => t.is_zero(),
_ => false,
}
}
}
impl<T: ToCss + Zero> ToCss for AnimationDuration<T> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
Self::Auto => {
if static_prefs::pref!("layout.css.scroll-driven-animations.enabled") {
dest.write_str("auto")
} else {
Self::Time(T::zero()).to_css(dest)
}
},
Self::Time(ref t) => t.to_css(dest),
}
}
}
/// The view() notation.
/// https://drafts.csswg.org/scroll-animations-1/#view-notation
#[derive(

View File

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Resolved animation values.
use super::{Context, ToResolvedValue};
use crate::values::computed::time::Time;
use crate::values::computed::AnimationDuration;
impl ToResolvedValue for AnimationDuration {
type ResolvedValue = Self;
fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {
match self {
// For backwards-compatibility with Level 1, when the computed value of
// animation-timeline is auto (i.e. only one list value, and that value being auto),
// the resolved value of auto for animation-duration is 0s whenever its used value
// would also be 0s.
// https://drafts.csswg.org/css-animations-2/#animation-duration
Self::Auto if context.style.get_ui().has_initial_animation_timeline() => {
Self::Time(Time::from_seconds(0.0f32))
},
_ => self,
}
}
#[inline]
fn from_resolved_value(value: Self::ResolvedValue) -> Self {
value
}
}

View File

@ -13,6 +13,7 @@ use crate::ArcSlice;
use servo_arc::Arc;
use smallvec::SmallVec;
mod animation;
mod color;
mod counters;

View File

@ -7,7 +7,7 @@
use crate::parser::{Parse, ParserContext};
use crate::properties::{NonCustomPropertyId, PropertyId, ShorthandId};
use crate::values::generics::animation as generics;
use crate::values::specified::{LengthPercentage, NonNegativeNumber};
use crate::values::specified::{LengthPercentage, NonNegativeNumber, Time};
use crate::values::{CustomIdent, DashedIdent, KeyframesName};
use crate::Atom;
use cssparser::Parser;
@ -150,6 +150,24 @@ impl TransitionBehavior {
}
}
/// A specified value for the `animation-duration` property.
pub type AnimationDuration = generics::GenericAnimationDuration<Time>;
impl Parse for AnimationDuration {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if static_prefs::pref!("layout.css.scroll-driven-animations.enabled")
&& input.try_parse(|i| i.expect_ident_matching("auto")).is_ok()
{
return Ok(Self::auto());
}
Time::parse_non_negative(context, input).map(AnimationDuration::Time)
}
}
/// https://drafts.csswg.org/css-animations/#animation-iteration-count
#[derive(
Copy, Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem,
@ -568,7 +586,7 @@ impl Parse for TimelineName {
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
return Ok(Self::none())
return Ok(Self::none());
}
DashedIdent::parse(context, input).map(TimelineName)
@ -581,7 +599,7 @@ impl ToCss for TimelineName {
W: Write,
{
if self.is_none() {
return dest.write_str("none")
return dest.write_str("none");
}
self.0.to_css(dest)

View File

@ -29,9 +29,9 @@ pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution};
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
pub use self::angle::{AllowUnitlessZeroAngle, Angle};
pub use self::animation::{
AnimationComposition, AnimationDirection, AnimationFillMode, AnimationIterationCount,
AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis, TimelineName,
TransitionBehavior, TransitionProperty, ViewTimelineInset,
AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,
AnimationIterationCount, AnimationName, AnimationPlayState, AnimationTimeline, ScrollAxis,
TimelineName, TransitionBehavior, TransitionProperty, ViewTimelineInset,
};
pub use self::background::{BackgroundRepeat, BackgroundSize};
pub use self::basic_shape::FillRule;

View File

@ -95,6 +95,7 @@ include = [
"AnimationFillMode",
"AnimationPlayState",
"AnimationComposition",
"AnimationDuration",
"Appearance",
"Au",
"BreakBetween",

View File

@ -1,15 +1,3 @@
[animation-duration-auto.tentative.html]
[A value of auto can be specified for animation-duration]
expected: FAIL
[e.style['animation-duration'\] = "auto" should set the property value]
expected: FAIL
[Property animation-duration value 'auto']
expected: FAIL
[e.style['animation'\] = "auto cubic-bezier(0, -2, 1, 3) -3s 4 reverse both paused anim" should set the property value]
expected: FAIL
[Property animation value 'auto cubic-bezier(0, -2, 1, 3) -3s 4 reverse both paused anim']
expected: FAIL