Bug 1286476 part 2 - Respect the playback rate when calculating phase boundaries; r?hiro draft
authorBrian Birtles <birtles@gmail.com>
Wed, 17 Aug 2016 08:28:41 +0900
changeset 401427 810357e53ebf2099f5136e7c18acfbf483b2790a
parent 401426 268884a8c2719582e038e476380d75a421e120bb
child 528477 19fd8cc2b7f9170b0c4df314412311decaa30b46
push id26447
push userbbirtles@mozilla.com
push dateTue, 16 Aug 2016 23:29:33 +0000
reviewershiro
bugs1286476, 1049975
milestone51.0a1
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
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
gfx/layers/composite/AsyncCompositionManager.cpp
layout/style/nsTransitionManager.cpp
testing/web-platform/meta/web-animations/timing-model/animation-effects/phases-and-states.html.ini
--- 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