From 74f6246cda3923564d232c5c36841aecb2ca8445 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Thu, 7 Jul 2022 18:33:42 +0000 Subject: [PATCH] Bug 1775327 - Part 3: Do normalization for NormalizedTiming(). r=firefox-animation-reviewers,birtles This implements the normalization of the specified time, defined in [web-animations-2]: https://drafts.csswg.org/web-animations-2/#normalize-specified-timing. However, it is possible to update this, based on the spec issue: https://github.com/w3c/csswg-drafts/issues/4862. For now, we just do normalization for delay, end delay, and iteration duration based on the end time. And make sure the end time is equal to the timeline duration. Differential Revision: https://phabricator.services.mozilla.com/D149685 --- dom/animation/AnimationEffect.cpp | 6 +- dom/animation/ScrollTimeline.h | 3 +- dom/animation/TimingParams.cpp | 77 +++++- dom/animation/TimingParams.h | 6 +- ...imation-longhand-properties.tentative.html | 235 ++++++++++++++++++ 5 files changed, 315 insertions(+), 12 deletions(-) create mode 100644 testing/web-platform/tests/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html diff --git a/dom/animation/AnimationEffect.cpp b/dom/animation/AnimationEffect.cpp index a833771b41fb9..ab35d28e45c23 100644 --- a/dom/animation/AnimationEffect.cpp +++ b/dom/animation/AnimationEffect.cpp @@ -356,8 +356,10 @@ void AnimationEffect::UpdateNormalizedTiming() { return; } - mNormalizedTiming.emplace(mTiming); - mNormalizedTiming->Normalize(); + // Since `mAnimation` has a scroll timeline, we can be sure `GetTimeline()` + // and `TimelineDuration()` will not return null. + mNormalizedTiming.emplace( + mTiming.Normalize(mAnimation->GetTimeline()->TimelineDuration().Value())); } Nullable AnimationEffect::GetLocalTime() const { diff --git a/dom/animation/ScrollTimeline.h b/dom/animation/ScrollTimeline.h index fa6a4630c7a13..061fab5477b23 100644 --- a/dom/animation/ScrollTimeline.h +++ b/dom/animation/ScrollTimeline.h @@ -12,9 +12,10 @@ #include "mozilla/HashTable.h" #include "mozilla/PairHash.h" #include "mozilla/ServoStyleConsts.h" -#include "mozilla/TimingParams.h" #include "mozilla/WritingModes.h" +#define PROGRESS_TIMELINE_DURATION_MILLISEC 100000 + class nsIScrollableFrame; namespace mozilla { diff --git a/dom/animation/TimingParams.cpp b/dom/animation/TimingParams.cpp index bfd4d0e190d74..83c87a3e0458f 100644 --- a/dom/animation/TimingParams.cpp +++ b/dom/animation/TimingParams.cpp @@ -214,13 +214,78 @@ bool TimingParams::operator==(const TimingParams& aOther) const { mFunction == aOther.mFunction; } -void TimingParams::Normalize() { - // FIXME: Bug 1775327, do normalization, instead of using these magic numbers. - mDuration = Some(StickyTimeDuration::FromMilliseconds( - PROGRESS_TIMELINE_DURATION_MILLISEC)); - mDelay = TimeDuration::FromMilliseconds(0); +// FIXME: This is a tentative way to normalize the timing which is defined in +// [web-animations-2] [1]. I borrow this implementation and some concepts for +// the edge cases from Chromium [2] so we can match the behavior with them. The +// implementation here ignores the case of percentage of start delay, end delay, +// and duration because Gecko doesn't support them. We may have to update the +// calculation if the spec issue [3] gets any update. +// +// [1] +// https://drafts.csswg.org/web-animations-2/#time-based-animation-to-a-proportional-animation +// [2] https://chromium-review.googlesource.com/c/chromium/src/+/2992387 +// [3] https://github.com/w3c/csswg-drafts/issues/4862 +TimingParams TimingParams::Normalize( + const TimeDuration& aTimelineDuration) const { + MOZ_ASSERT(aTimelineDuration, + "the timeline duration of scroll-timeline is always non-zero now"); + + TimingParams normalizedTiming(*this); + + // Handle iteration duration value of "auto" first. + // FIXME: Bug 1676794: Gecko doesn't support `animation-duration:auto` and we + // don't support JS-generated scroll animations, so we don't fall into this + // case for now. Need to check this again after we support ScrollTimeline + // interface. + if (!mDuration) { + // If the iteration duration is auto, then: + // Set start delay and end delay to 0, as it is not possible to mix time + // and proportions. + normalizedTiming.mDelay = TimeDuration(); + normalizedTiming.mEndDelay = TimeDuration(); + normalizedTiming.Update(); + return normalizedTiming; + } + + if (mEndTime.IsZero()) { + // mEndTime of zero causes division by zero so we handle it here. + // + // FIXME: The spec doesn't mention this case, so we might have to update + // this based on the spec issue, + // https://github.com/w3c/csswg-drafts/issues/7459. + normalizedTiming.mDelay = TimeDuration(); + normalizedTiming.mEndDelay = TimeDuration(); + normalizedTiming.mDuration = Some(TimeDuration()); + } else if (mEndTime == TimeDuration::Forever()) { + // The iteration count or duration may be infinite; however, start and + // end delays are strictly finite. Thus, in the limit when end time + // approaches infinity: + // start delay / end time = finite / infinite = 0 + // end delay / end time = finite / infinite = 0 + // iteration duration / end time = 1 / iteration count + // This condition can be reached by switching to a scroll timeline on + // an existing infinite duration animation. + // + // FIXME: The spec doesn't mention this case, so we might have to update + // this based on the spec issue, + // https://github.com/w3c/csswg-drafts/issues/7459. + normalizedTiming.mDelay = TimeDuration(); + normalizedTiming.mEndDelay = TimeDuration(); + normalizedTiming.mDuration = + Some(aTimelineDuration.MultDouble(1.0 / mIterations)); + } else { + // Convert to percentages then multiply by the timeline duration. + const double endTimeInSec = mEndTime.ToSeconds(); + normalizedTiming.mDelay = + aTimelineDuration.MultDouble(mDelay.ToSeconds() / endTimeInSec); + normalizedTiming.mEndDelay = + aTimelineDuration.MultDouble(mEndDelay.ToSeconds() / endTimeInSec); + normalizedTiming.mDuration = Some(StickyTimeDuration( + aTimelineDuration.MultDouble(mDuration->ToSeconds() / endTimeInSec))); + } - Update(); + normalizedTiming.Update(); + return normalizedTiming; } } // namespace mozilla diff --git a/dom/animation/TimingParams.h b/dom/animation/TimingParams.h index 9bf193c784859..6ec056666d364 100644 --- a/dom/animation/TimingParams.h +++ b/dom/animation/TimingParams.h @@ -21,8 +21,6 @@ #include "mozilla/dom/AnimationEffectBinding.h" // for FillMode // and PlaybackDirection -#define PROGRESS_TIMELINE_DURATION_MILLISEC 100000 - namespace mozilla { namespace dom { @@ -204,7 +202,9 @@ struct TimingParams { return mFunction; } - void Normalize(); + // This is called only for progress-based timeline (i.e. non-monotonic + // timeline). That is, |aTimelineDuration| should be resolved already. + TimingParams Normalize(const TimeDuration& aTimelineDuration) const; private: void Update() { diff --git a/testing/web-platform/tests/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html b/testing/web-platform/tests/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html new file mode 100644 index 0000000000000..d8005963c204f --- /dev/null +++ b/testing/web-platform/tests/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html @@ -0,0 +1,235 @@ + +The various animation longhands with progress based animations + + + + + + + +
+ +