Bug 1334583 - Pass a separate timeOrigin and startTime for compositor animations; r?hiro draft
authorBrian Birtles <birtles@gmail.com>
Tue, 02 May 2017 16:49:51 +0900
changeset 571207 fde92d9e66f431fb79cf2f058aad2a13cdaa5055
parent 571195 7a395d2cdfeb1c8e45f2df6edcd72b8cbaf7a6ad
child 626707 50d609776e9b2d117ed6d43ca4ce3dba2745b542
push id56727
push userbbirtles@mozilla.com
push dateTue, 02 May 2017 08:52:53 +0000
reviewershiro
bugs1334583
milestone55.0a1
Bug 1334583 - Pass a separate timeOrigin and startTime for compositor animations; r?hiro By passing the startTime as a TimeDuration we are able to represent times in the distant past (and with the same range as we can represent on the main thread so that if we do encounter range errors in future, they should not differ between the main thread and the compositor). This patch includes a crashtest. I have verified that, without the code changes included in this patch, this crashtest fails on debug builds on OSX. MozReview-Commit-ID: EDuKLzfEC0K
dom/animation/test/crashtests/1334583-1.html
dom/animation/test/crashtests/crashtests.list
gfx/layers/AnimationHelper.cpp
gfx/layers/Layers.cpp
gfx/layers/ipc/LayersMessages.ipdlh
layout/painting/nsDisplayList.cpp
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/crashtests/1334583-1.html
@@ -0,0 +1,9 @@
+<div style="width: 200px; height: 200px; background: purple" id=div>
+</div>
+<script>
+const animation = div.animate(
+  [ { transform: "scale(1)" },
+    { transform: "scale(2)" } ],
+    { iterations: Infinity, duration: 512 } );
+animation.currentTime = 2147483647;
+</script>
--- a/dom/animation/test/crashtests/crashtests.list
+++ b/dom/animation/test/crashtests/crashtests.list
@@ -20,10 +20,11 @@ pref(dom.animations-api.core.enabled,tru
 pref(dom.animations-api.core.enabled,true) load 1324554-1.html
 pref(dom.animations-api.core.enabled,true) load 1325193-1.html
 pref(dom.animations-api.core.enabled,true) load 1330190-1.html
 pref(dom.animations-api.core.enabled,true) load 1330190-2.html
 pref(dom.animations-api.core.enabled,true) load 1330513-1.html
 pref(dom.animations-api.core.enabled,true) load 1333539-1.html
 pref(dom.animations-api.core.enabled,true) load 1333539-2.html
 pref(dom.animations-api.core.enabled,true) load 1333418-1.html
+pref(dom.animations-api.core.enabled,true) load 1334583-1.html
 pref(dom.animations-api.core.enabled,true) load 1343589-1.html
 pref(dom.animations-api.core.enabled,true) load 1359658-1.html
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -154,25 +154,28 @@ AnimationHelper::SampleAnimationForEachN
 
   // Process in order, since later aAnimations override earlier ones.
   for (size_t i = 0, iEnd = aAnimations.Length(); i < iEnd; ++i) {
     Animation& animation = aAnimations[i];
     AnimData& animData = aAnimationData[i];
 
     activeAnimations = true;
 
-    MOZ_ASSERT(!animation.startTime().IsNull() ||
+    MOZ_ASSERT((!animation.originTime().IsNull() &&
+                animation.startTime().type() != MaybeTimeDuration::Tnull_t) ||
                animation.isNotPlaying(),
-               "Failed to resolve start time of play-pending animations");
+               "If we are playing, we should have an origin time and a start"
+               " time");
     // If the animation is not currently playing , e.g. paused or
     // finished, then use the hold time to stay at the same position.
     TimeDuration elapsedDuration = animation.isNotPlaying()
       ? animation.holdTime()
-      : (aTime - animation.startTime())
-          .MultDouble(animation.playbackRate());
+      : (aTime - animation.originTime() -
+         animation.startTime().get_TimeDuration())
+        .MultDouble(animation.playbackRate());
     TimingParams timing;
     timing.mDuration.emplace(animation.duration());
     timing.mDelay = animation.delay();
     timing.mEndDelay = animation.endDelay();
     timing.mIterations = animation.iterations();
     timing.mIterationStart = animation.iterationStart();
     timing.mDirection =
       static_cast<dom::PlaybackDirection>(animation.direction());
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -288,22 +288,25 @@ Layer::StartPendingAnimations(const Time
       {
         bool updated = false;
         for (size_t animIdx = 0, animEnd = layer->mAnimations.Length();
              animIdx < animEnd; animIdx++) {
           Animation& anim = layer->mAnimations[animIdx];
 
           // If the animation is play-pending, resolve the start time.
           // This mirrors the calculation in Animation::StartTimeFromReadyTime.
-          if (anim.startTime().IsNull() && !anim.isNotPlaying()) {
+          if (anim.startTime().type() == MaybeTimeDuration::Tnull_t &&
+              !anim.originTime().IsNull() &&
+              !anim.isNotPlaying()) {
+            TimeDuration readyTime = aReadyTime - anim.originTime();
             anim.startTime() =
               anim.playbackRate() == 0
-              ? aReadyTime
-              : aReadyTime - anim.holdTime().MultDouble(1.0 /
-                                                        anim.playbackRate());
+              ? readyTime
+              : readyTime - anim.holdTime().MultDouble(1.0 /
+                                                       anim.playbackRate());
             updated = true;
           }
         }
         if (updated) {
           layer->Mutated();
         }
       });
 }
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -157,16 +157,21 @@ union TransformFunction {
   Scale;
   Skew;
   SkewX;
   SkewY;
   Translation;
   TransformMatrix;
 };
 
+union MaybeTimeDuration {
+  null_t;
+  TimeDuration;
+};
+
 union Animatable {
   null_t;
   float;
   TransformFunction[];
 };
 
 struct AnimationSegment {
   Animatable startState;
@@ -190,25 +195,30 @@ struct TransformData {
 };
 
 union AnimationData {
   null_t;
   TransformData;
 };
 
 struct Animation {
-  TimeStamp startTime;
+  // The zero time of this Animation's timeline. May be null if isNotPlaying is
+  // true.
+  TimeStamp originTime;
+  // The start time is relative to the originTime. This allows us to represent
+  // start times in the distant past that cannot be expressed using a TimeStamp.
+  MaybeTimeDuration startTime;
   TimeDuration delay;
   TimeDuration endDelay;
   // The value of the animation's current time at the moment it was sent to the
   // compositor.  This value will be used for below cases:
   // 1) Animations that are play-pending. Initially these animations will have a
-  //    null |startTime|. Once the animation is read to start (i.e. painting has
-  //    finished), we calculate an appropriate value of |startTime| such that
-  //    playback begins from |holdTime|.
+  //    null |startTime|. Once the animation is ready to start (i.e. painting
+  //    has finished), we calculate an appropriate value of |startTime| such
+  //    that playback begins from |holdTime|.
   // 2) Not playing animations (e.g. paused and finished animations). In this
   //   case the |holdTime| represents the current time the animation will
   //   maintain.
   TimeDuration holdTime;
   TimeDuration duration;
   // For each frame, the interpolation point is computed based on the
   // startTime, the direction, the duration, and the current time.
   // The segments must uniquely cover the portion from 0.0 to 1.0
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -521,25 +521,32 @@ AddAnimationForProperty(nsIFrame* aFrame
       aAnimation->GetEffect() &&
       aAnimation->GetEffect()->AsTransition()) {
     // We update startValue from the replaced transition only if the effect is
     // an ElementPropertyTransition.
     aAnimation->GetEffect()->AsTransition()->
       UpdateStartValueFromReplacedTransition();
   }
 
+  animation->originTime() = !aAnimation->GetTimeline()
+                            ? TimeStamp()
+                            : aAnimation->GetTimeline()->
+                                ToTimeStamp(TimeDuration());
+
+  Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
+  if (startTime.IsNull()) {
+    animation->startTime() = null_t();
+  } else {
+    animation->startTime() = startTime.Value();
+  }
+
+  animation->holdTime() = aAnimation->GetCurrentTime().Value();
+
   const ComputedTiming computedTiming =
     aAnimation->GetEffect()->GetComputedTiming();
-  Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
-  animation->startTime() = startTime.IsNull() || !aAnimation->GetTimeline()
-                           ? TimeStamp()
-                           : aAnimation->GetTimeline()->
-                              ToTimeStamp(startTime.Value());
-  animation->holdTime() = aAnimation->GetCurrentTime().Value();
-
   animation->delay() = timing.mDelay;
   animation->endDelay() = timing.mEndDelay;
   animation->duration() = computedTiming.mDuration;
   animation->iterations() = computedTiming.mIterations;
   animation->iterationStart() = computedTiming.mIterationStart;
   animation->direction() = static_cast<uint8_t>(timing.mDirection);
   animation->fillMode() = static_cast<uint8_t>(computedTiming.mFill);
   animation->property() = aProperty.mProperty;