Bug 1286476 part 2 - Respect the playback rate when calculating phase boundaries; r?hiro
This implements the spec change in https://github.com/w3c/web-animations/commit/21de090dacc56efcc1ef7a7d04913126f96024ae
The spec change refers to a binary 'animation direction' flag. Instead of that,
however, we just pass the playback rate along and use it inside
GetComputedTimingAt since this seems simpler.
Also, this patch moves the implementation of
KeyframeEffectReadOnly::GetComputedTiming from the header file into the .cpp
file. This is because with this change, GetComputedTiming needs to call
mAnimation->PlaybackRate() and so mozilla::dom::Animation needs to be a complete
type. However, simply including Animation.h doesn't work because of a cyclic
dependency between KeyframeEffect.h and Animation.h. We might be able to fix
this later but since yet-to-land
bug 1049975 moves this code around a lot, I'd
rather not touch it too much just now.
MozReview-Commit-ID: 1h6XRh4xmfI
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -216,28 +216,31 @@ KeyframeEffectReadOnly::GetLocalTime() c
}
return result;
}
void
KeyframeEffectReadOnly::GetComputedTimingAsDict(
ComputedTimingProperties& aRetVal) const
{
+ double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
const Nullable<TimeDuration> currentTime = GetLocalTime();
GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
- SpecifiedTiming()),
+ SpecifiedTiming(),
+ playbackRate),
currentTime,
SpecifiedTiming(),
aRetVal);
}
ComputedTiming
KeyframeEffectReadOnly::GetComputedTimingAt(
const Nullable<TimeDuration>& aLocalTime,
- const TimingParams& aTiming)
+ const TimingParams& aTiming,
+ double aPlaybackRate)
{
const StickyTimeDuration zeroDuration;
// Always return the same object to benefit from return-value optimization.
ComputedTiming result;
if (aTiming.mDuration) {
MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
@@ -266,29 +269,35 @@ KeyframeEffectReadOnly::GetComputedTimin
if (aLocalTime.IsNull()) {
return result;
}
const TimeDuration& localTime = aLocalTime.Value();
// Calculate the time within the active interval.
// https://w3c.github.io/web-animations/#active-time
StickyTimeDuration activeTime;
- if (localTime >=
- std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
- result.mEndTime)) {
+
+ StickyTimeDuration beforePhaseBoundary =
+ std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime);
+ StickyTimeDuration afterPhaseBoundary =
+ std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
+ result.mEndTime);
+
+ if (localTime > afterPhaseBoundary ||
+ (aPlaybackRate >= 0 && localTime == afterPhaseBoundary)) {
result.mPhase = ComputedTiming::AnimationPhase::After;
if (!result.FillsForwards()) {
// The animation isn't active or filling at this time.
return result;
}
activeTime = std::max(std::min(result.mActiveDuration,
result.mActiveDuration + aTiming.mEndDelay),
zeroDuration);
- } else if (localTime <
- std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime)) {
+ } else if (localTime < beforePhaseBoundary ||
+ (aPlaybackRate < 0 && localTime == beforePhaseBoundary)) {
result.mPhase = ComputedTiming::AnimationPhase::Before;
if (!result.FillsBackwards()) {
// The animation isn't active or filling at this time.
return result;
}
// activeTime is zero
} else {
MOZ_ASSERT(result.mActiveDuration != zeroDuration,
@@ -386,16 +395,25 @@ KeyframeEffectReadOnly::GetComputedTimin
progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
}
MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
result.mProgress.SetValue(progress);
return result;
}
+ComputedTiming
+KeyframeEffectReadOnly::GetComputedTiming(const TimingParams* aTiming) const
+{
+ double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
+ return GetComputedTimingAt(GetLocalTime(),
+ aTiming ? *aTiming : SpecifiedTiming(),
+ playbackRate);
+}
+
// https://w3c.github.io/web-animations/#in-play
bool
KeyframeEffectReadOnly::IsInPlay() const
{
if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
return false;
}
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -259,26 +259,23 @@ public:
// active duration are calculated. All other members of the returned object
// are given a null/initial value.
//
// This function returns a null mProgress member of the return value
// if the animation should not be run
// (because it is not currently active and is not filling at this time).
static ComputedTiming
GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
- const TimingParams& aTiming);
+ const TimingParams& aTiming,
+ double aPlaybackRate);
// Shortcut for that gets the computed timing using the current local time as
// calculated from the timeline time.
ComputedTiming
- GetComputedTiming(const TimingParams* aTiming = nullptr) const
- {
- return GetComputedTimingAt(GetLocalTime(),
- aTiming ? *aTiming : SpecifiedTiming());
- }
+ GetComputedTiming(const TimingParams* aTiming = nullptr) const;
void
GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override;
bool IsInPlay() const;
bool IsCurrent() const;
bool IsInEffect() const;
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -647,17 +647,18 @@ SampleAnimations(Layer* aLayer, TimeStam
// assume that we should fill.
timing.mFill = dom::FillMode::Both;
timing.mFunction =
AnimationUtils::TimingFunctionToComputedTimingFunction(
animation.easingFunction());
ComputedTiming computedTiming =
dom::KeyframeEffectReadOnly::GetComputedTimingAt(
- Nullable<TimeDuration>(elapsedDuration), timing);
+ Nullable<TimeDuration>(elapsedDuration), timing,
+ animation.playbackRate());
MOZ_ASSERT(!computedTiming.mProgress.IsNull(),
"iteration progress should not be null");
uint32_t segmentIndex = 0;
size_t segmentSize = animation.segments().Length();
AnimationSegment* segment = animation.segments().Elements();
while (segment->endPortion() < computedTiming.mProgress.Value() &&
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -86,17 +86,18 @@ ElementPropertyTransition::UpdateStartVa
"We should have a valid document at this moment");
dom::DocumentTimeline* timeline = mTarget->mElement->OwnerDoc()->Timeline();
ComputedTiming computedTiming = GetComputedTimingAt(
dom::CSSTransition::GetCurrentTimeAt(*timeline,
TimeStamp::Now(),
mReplacedTransition->mStartTime,
mReplacedTransition->mPlaybackRate),
- mReplacedTransition->mTiming);
+ mReplacedTransition->mTiming,
+ mReplacedTransition->mPlaybackRate);
if (!computedTiming.mProgress.IsNull()) {
double valuePosition =
ComputedTimingFunction::GetPortion(mReplacedTransition->mTimingFunction,
computedTiming.mProgress.Value(),
computedTiming.mBeforeFlag);
StyleAnimationValue startValue;
if (StyleAnimationValue::Interpolate(mProperties[0].mProperty,
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animation-effects/phases-and-states.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[phases-and-states.html]
- type: testharness
- [Phase calculation for a simple animation effect with negative playback rate]
- expected: FAIL
- bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1286476