Bug 1029370 part 1 - Move active duration calculation to GetComputedTimingAt; r=dholbert
authorBrian Birtles <birtles@gmail.com>
Wed, 25 Jun 2014 09:42:19 +0900
changeset 190606 b31a50d545f72c573d693643a61d2a260a298ed9
parent 190605 e7ae48a8fac8ab5f94c0e29ed546a36c1f2245ec
child 190607 695c4dc0efc7f09fd1a7cd1ad8135cf918f0a176
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdholbert
bugs1029370
milestone33.0a1
Bug 1029370 part 1 - Move active duration calculation to GetComputedTimingAt; r=dholbert This patch makes the active duration a property of the ComputedTiming struct and returns this as part of calculating GetComputedTimingAt. GetComputedTimingAt was already calling the method to calculate the ActiveDuration and the only other callers of ActiveDuration() were also calling GetComputedTimingAt so this doesn't make us do any unnecessary calculation. I've left ActiveDuration as a public method on ElementAnimation for now since it's a struct and just about everything there is public. At some point in the future we'll probably make this more class-like to hide some details but that can happen as a separate step. This patch does, however, move the definition of ActiveDuration inside the .cpp file. In tidying up GetComputedTimingAt we also replace all the references to TimeDuration() and TimeDuration(0) with a single local variable representing zero duration. This should be easier to read and possibly a little faster. We don't use a function static variable since this method is called from different threads and the initialization of function statics is not guaranteed to be thread-safe until C++0x.
layout/style/AnimationCommon.cpp
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.cpp
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -442,80 +442,82 @@ ElementAnimation::HasAnimationOfProperty
   }
   return false;
 }
 
 ComputedTiming
 ElementAnimation::GetComputedTimingAt(TimeDuration aLocalTime,
                                       const AnimationTiming& aTiming)
 {
+  const TimeDuration zeroDuration;
+
   // Currently we expect negative durations to be picked up during CSS
   // parsing but when we start receiving timing parameters from other sources
   // we will need to clamp negative durations here.
   // For now, if we're hitting this it probably means we've overflowing
   // integer arithmetic in mozilla::TimeStamp.
-  MOZ_ASSERT(aTiming.mIterationDuration >= TimeDuration(0),
+  MOZ_ASSERT(aTiming.mIterationDuration >= zeroDuration,
              "Expecting iteration duration >= 0");
 
   // Always return the same object to benefit from return-value optimization.
   ComputedTiming result;
 
-  TimeDuration activeDuration = ActiveDuration(aTiming);
+  result.mActiveDuration = ActiveDuration(aTiming);
 
   // When we finish exactly at the end of an iteration we need to report
   // the end of the final iteration and not the start of the next iteration
   // so we set up a flag for that case.
   bool isEndOfFinalIteration = false;
 
   // Get the normalized time within the active interval.
   TimeDuration activeTime;
-  if (aLocalTime >= aTiming.mDelay + activeDuration) {
+  if (aLocalTime >= aTiming.mDelay + result.mActiveDuration) {
     result.mPhase = ComputedTiming::AnimationPhase_After;
     if (!aTiming.FillsForwards()) {
       // The animation isn't active or filling at this time.
       result.mTimeFraction = ComputedTiming::kNullTimeFraction;
       return result;
     }
-    activeTime = activeDuration;
+    activeTime = result.mActiveDuration;
     // Note that infinity == floor(infinity) so this will also be true when we
     // have finished an infinitely repeating animation of zero duration.
     isEndOfFinalIteration =
       aTiming.mIterationCount != 0.0 &&
       aTiming.mIterationCount == floor(aTiming.mIterationCount);
   } else if (aLocalTime < aTiming.mDelay) {
     result.mPhase = ComputedTiming::AnimationPhase_Before;
     if (!aTiming.FillsBackwards()) {
       // The animation isn't active or filling at this time.
       result.mTimeFraction = ComputedTiming::kNullTimeFraction;
       return result;
     }
     // activeTime is zero
   } else {
-    MOZ_ASSERT(activeDuration != TimeDuration(),
+    MOZ_ASSERT(result.mActiveDuration != zeroDuration,
                "How can we be in the middle of a zero-duration interval?");
     result.mPhase = ComputedTiming::AnimationPhase_Active;
     activeTime = aLocalTime - aTiming.mDelay;
   }
 
   // Get the position within the current iteration.
   TimeDuration iterationTime;
-  if (aTiming.mIterationDuration != TimeDuration()) {
+  if (aTiming.mIterationDuration != zeroDuration) {
     iterationTime = isEndOfFinalIteration
                     ? aTiming.mIterationDuration
                     : activeTime % aTiming.mIterationDuration;
   } /* else, iterationTime is zero */
 
   // Determine the 0-based index of the current iteration.
   if (isEndOfFinalIteration) {
     result.mCurrentIteration =
       aTiming.mIterationCount == NS_IEEEPositiveInfinity()
       ? UINT64_MAX // FIXME: When we return this via the API we'll need
                    // to make sure it ends up being infinity.
       : static_cast<uint64_t>(aTiming.mIterationCount) - 1;
-  } else if (activeTime == TimeDuration(0)) {
+  } else if (activeTime == zeroDuration) {
     // If the active time is zero we're either in the first iteration
     // (including filling backwards) or we have finished an animation with an
     // iteration duration of zero that is filling forwards (but we're not at
     // the exact end of an iteration since we deal with that above).
     result.mCurrentIteration =
       result.mPhase == ComputedTiming::AnimationPhase_After
       ? static_cast<uint64_t>(aTiming.mIterationCount) // floor
       : 0;
@@ -528,17 +530,17 @@ ElementAnimation::GetComputedTimingAt(Ti
   if (result.mPhase == ComputedTiming::AnimationPhase_Before) {
     result.mTimeFraction = 0.0;
   } else if (result.mPhase == ComputedTiming::AnimationPhase_After) {
     result.mTimeFraction = isEndOfFinalIteration
                          ? 1.0
                          : fmod(aTiming.mIterationCount, 1.0f);
   } else {
     // We are in the active phase so the iteration duration can't be zero.
-    MOZ_ASSERT(aTiming.mIterationDuration != TimeDuration(0),
+    MOZ_ASSERT(aTiming.mIterationDuration != zeroDuration,
                "In the active phase of a zero-duration animation?");
     result.mTimeFraction =
       aTiming.mIterationDuration == TimeDuration::Forever()
       ? 0.0
       : iterationTime / aTiming.mIterationDuration;
   }
 
   bool thisIterationReverse = false;
@@ -558,16 +560,31 @@ ElementAnimation::GetComputedTimingAt(Ti
   }
   if (thisIterationReverse) {
     result.mTimeFraction = 1.0 - result.mTimeFraction;
   }
 
   return result;
 }
 
+TimeDuration
+ElementAnimation::ActiveDuration(const AnimationTiming& aTiming)
+{
+  if (aTiming.mIterationCount == mozilla::PositiveInfinity<float>()) {
+    // An animation that repeats forever has an infinite active duration
+    // unless its iteration duration is zero, in which case it has a zero
+    // active duration.
+    const TimeDuration zeroDuration;
+    return aTiming.mIterationDuration == zeroDuration
+           ? zeroDuration
+           : TimeDuration::Forever();
+  }
+  return aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount);
+}
+
 namespace css {
 
 bool
 CommonElementAnimationData::CanAnimatePropertyOnCompositor(const dom::Element *aElement,
                                                            nsCSSProperty aProperty,
                                                            CanAnimateFlags aFlags)
 {
   bool shouldLog = nsLayoutUtils::IsAnimationLoggingEnabled();
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -268,16 +268,20 @@ struct ComputedTiming
 {
   ComputedTiming()
   : mTimeFraction(kNullTimeFraction),
     mCurrentIteration(0)
   { }
 
   static const double kNullTimeFraction;
 
+  // The total duration of the animation including all iterations.
+  // Will equal TimeDuration::Forever() if the animation repeats indefinitely.
+  TimeDuration mActiveDuration;
+
   // Will be kNullTimeFraction if the animation is neither animating nor
   // filling at the sampled time.
   double mTimeFraction;
 
   // Zero-based iteration index (meaningless if mTimeFraction is
   // kNullTimeFraction).
   uint64_t mCurrentIteration;
 
@@ -340,30 +344,16 @@ public:
   mozilla::TimeDuration GetLocalTimeAt(mozilla::TimeStamp aTime) const {
     MOZ_ASSERT(!IsPaused() || aTime >= mPauseStart,
                "if paused, aTime must be at least mPauseStart");
     MOZ_ASSERT(!IsFinishedTransition(),
                "GetLocalTimeAt should not be called on a finished transition");
     return (IsPaused() ? mPauseStart : aTime) - mStartTime;
   }
 
-  // Return the duration of the active interval for the given timing parameters.
-  static mozilla::TimeDuration ActiveDuration(const AnimationTiming& aTiming) {
-    if (aTiming.mIterationCount == mozilla::PositiveInfinity<float>()) {
-      // An animation that repeats forever has an infinite active duration
-      // unless its iteration duration is zero, in which case it has a zero
-      // active duration.
-      const TimeDuration zeroDuration;
-      return aTiming.mIterationDuration == zeroDuration
-             ? zeroDuration
-             : mozilla::TimeDuration::Forever();
-    }
-    return aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount);
-  }
-
   // Return the duration from the start the active interval to the point where
   // the animation begins playback. This is zero unless the animation has
   // a negative delay in which case it is the absolute value of the delay.
   // This is used for setting the elapsedTime member of AnimationEvents.
   mozilla::TimeDuration InitialAdvance() const {
     return std::max(TimeDuration(), mTiming.mDelay * -1);
   }
 
@@ -371,16 +361,19 @@ public:
   // returns the computed timing at the specified moment.
   //
   // This function returns ComputedTiming::kNullTimeFraction for the
   // mTimeFraction 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(TimeDuration aLocalTime,
                                             const AnimationTiming& aTiming);
 
+  // Return the duration of the active interval for the given timing parameters.
+  static mozilla::TimeDuration ActiveDuration(const AnimationTiming& aTiming);
+
   nsString mName; // empty string for 'none'
   AnimationTiming mTiming;
   // The beginning of the delay period.  This is also set to a null
   // timestamp to mark transitions that have finished and are due to
   // be removed on the next throttle-able cycle.
   mozilla::TimeStamp mStartTime;
   mozilla::TimeStamp mPauseStart;
   uint8_t mPlayState;
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -72,38 +72,37 @@ nsAnimationManager::GetEventsAt(CommonEl
             std::max(iterationStart, anim->InitialAdvance());
           AnimationEventInfo ei(aEA->mElement, anim->mName, message,
                                 elapsedTime, aEA->PseudoElement());
           aEventsToDispatch.AppendElement(ei);
         }
         break;
 
       case ComputedTiming::AnimationPhase_After:
-        TimeDuration activeDuration =
-          ElementAnimation::ActiveDuration(anim->mTiming);
         // If we skipped the animation interval entirely, dispatch
         // 'animationstart' first
         if (anim->mLastNotification ==
             ElementAnimation::LAST_NOTIFICATION_NONE) {
           // Notifying for start of 0th iteration.
           // (This is overwritten below but we set it here to maintain
           // internal consistency.)
           anim->mLastNotification = 0;
           TimeDuration elapsedTime =
-            std::min(anim->InitialAdvance(), activeDuration);
+            std::min(anim->InitialAdvance(), computedTiming.mActiveDuration);
           AnimationEventInfo ei(aEA->mElement, anim->mName, NS_ANIMATION_START,
                                 elapsedTime, aEA->PseudoElement());
           aEventsToDispatch.AppendElement(ei);
         }
         // Dispatch 'animationend' when needed.
         if (anim->mLastNotification !=
             ElementAnimation::LAST_NOTIFICATION_END) {
           anim->mLastNotification = ElementAnimation::LAST_NOTIFICATION_END;
           AnimationEventInfo ei(aEA->mElement, anim->mName, NS_ANIMATION_END,
-                                activeDuration, aEA->PseudoElement());
+                                computedTiming.mActiveDuration,
+                                aEA->PseudoElement());
           aEventsToDispatch.AppendElement(ei);
         }
         break;
     }
   }
 }
 
 CommonElementAnimationData*