Bug 1334583 - Pass a separate timeOrigin and startTime for compositor animations. r=hiro, a=gchang
authorBrian Birtles <bbirtles>
Thu, 11 May 2017 18:55:00 -0400
changeset 393939 87a47b9dff16ef7fbd9bdcbd9e8d9f571a89c6fc
parent 393938 0f4b47c9fab1a82abab0a1e5418dd0125f6c181d
child 393940 702b6b99b181f7b76e7a9399fbc24a00292b9e65
push id7309
push userryanvm@gmail.com
push dateMon, 15 May 2017 16:07:20 +0000
treeherdermozilla-beta@b0a01d9a5589 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershiro, gchang
bugs1334583
milestone54.0
Bug 1334583 - Pass a separate timeOrigin and startTime for compositor animations. r=hiro, a=gchang 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). 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,8 +20,9 @@ skip-if(stylo) pref(dom.animations-api.c
 pref(dom.animations-api.core.enabled,true) load 1324554-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1325193-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1330190-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1330190-2.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1330513-1.html # bug 1311257
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1333539-1.html
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1333539-2.html
 skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1333418-1.html # bug 1311257
+skip-if(stylo) pref(dom.animations-api.core.enabled,true) load 1334583-1.html
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -93,25 +93,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()
-      : (aPoint - animation.startTime())
-          .MultDouble(animation.playbackRate());
+      : (aPoint - 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
@@ -276,18 +276,25 @@ Layer::StartPendingAnimations(const Time
       [&aReadyTime](Layer *layer)
       {
         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.
-          if (anim.startTime().IsNull() && !anim.isNotPlaying()) {
-            anim.startTime() = aReadyTime - anim.holdTime() + anim.delay();
+          if (anim.startTime().type() == MaybeTimeDuration::Tnull_t &&
+              !anim.originTime().IsNull() &&
+              !anim.isNotPlaying()) {
+            TimeDuration readyTime = aReadyTime - anim.originTime();
+            anim.startTime() =
+              anim.playbackRate() == 0
+              ? 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
@@ -152,16 +152,21 @@ union TransformFunction {
   Scale;
   Skew;
   SkewX;
   SkewY;
   Translation;
   TransformMatrix;
 };
 
+union MaybeTimeDuration {
+  null_t;
+  TimeDuration;
+};
+
 union Animatable {
   null_t;
   float;
   TransformFunction[];
 };
 
 struct AnimationSegment {
   Animatable startState;
@@ -185,25 +190,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
@@ -514,25 +514,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;