Bug 1321428 - Support timelines that do not track wallclock time. r=birtles draft
authorBotond Ballo <botond@mozilla.com>
Mon, 16 Jan 2017 16:58:18 -0500
changeset 542206 2e1ea0b30e07d37dd378a7d29c87f75e58d0284a
parent 542205 2b54ba1a0f8f7ad024beda72c6d1efffb7f87800
child 550749 44298331fd949e8f7b81a5720db4e1e0def6fa8b
push id50961
push userbballo@mozilla.com
push dateFri, 24 Mar 2017 22:03:27 +0000
reviewersbirtles
bugs1321428
milestone55.0a1
Bug 1321428 - Support timelines that do not track wallclock time. r=birtles MozReview-Commit-ID: Kjla42XRVT0
dom/animation/AnimationTimeline.h
dom/animation/DocumentTimeline.h
dom/animation/PendingAnimationTracker.cpp
layout/painting/nsDisplayList.cpp
--- a/dom/animation/AnimationTimeline.h
+++ b/dom/animation/AnimationTimeline.h
@@ -22,16 +22,17 @@
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
 namespace mozilla {
 namespace dom {
 
 class Animation;
+class DocumentTimeline;
 class ScrollTimeline;
 
 class AnimationTimeline
   : public nsISupports
   , public nsWrapperCache
 {
 public:
   AnimationTimeline()
@@ -96,16 +97,19 @@ public:
    * time.
    */
   bool HasAnimations() const {
     return !mAnimations.IsEmpty();
   }
 
   virtual void RemoveAnimation(Animation* aAnimation);
 
+  virtual DocumentTimeline* AsDocumentTimeline() { return nullptr; }
+  virtual const DocumentTimeline* AsDocumentTimeline() const { return nullptr; }
+
   virtual ScrollTimeline* AsScrollTimeline() { return nullptr; }
   virtual const ScrollTimeline* AsScrollTimeline() const { return nullptr; }
 
 protected:
   nsCOMPtr<nsIGlobalObject> mWindow;
 
   // Animations observing this timeline
   //
--- a/dom/animation/DocumentTimeline.h
+++ b/dom/animation/DocumentTimeline.h
@@ -87,16 +87,19 @@ public:
   void RemoveAnimation(Animation* aAnimation) override;
 
   // nsARefreshObserver methods
   void WillRefresh(TimeStamp aTime) override;
 
   void NotifyRefreshDriverCreated(nsRefreshDriver* aDriver);
   void NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver);
 
+  DocumentTimeline* AsDocumentTimeline() override { return this; }
+  const DocumentTimeline* AsDocumentTimeline() const override { return this; }
+
 protected:
   TimeStamp GetCurrentTimeStamp() const;
   nsRefreshDriver* GetRefreshDriver() const;
   void UnregisterFromRefreshDriver();
 
   nsCOMPtr<nsIGlobalObject> mWindow;
   nsCOMPtr<nsIDocument> mDocument;
 
--- a/dom/animation/PendingAnimationTracker.cpp
+++ b/dom/animation/PendingAnimationTracker.cpp
@@ -61,27 +61,37 @@ PendingAnimationTracker::TriggerPendingA
       // If the animation does not have a timeline, just drop it from the map.
       // The animation will detect that it is not being tracked and will trigger
       // itself on the next tick where it has a timeline.
       if (!timeline) {
         iter.Remove();
         continue;
       }
 
-      // When the timeline's refresh driver is under test control, its values
-      // have no correspondance to wallclock times so we shouldn't try to
+      // If the timeline doesn't track wallclock time, we shouldn't try to
       // convert aReadyTime (which is a wallclock time) to a timeline value.
-      // Instead, the animation will be started/paused when the refresh driver
-      // is next advanced since this will trigger a call to
-      // TriggerPendingAnimationsNow.
-      if (!timeline->TracksWallclockTime()) {
+      // This can happen in two cases:
+      //   - The timeline is a document timeline, and its refresh driver is
+      //     under test control. In this case, the animation will be started/
+      //     paused when the refresh driver is next advanced since this will
+      //     trigger a call to TriggerPendingAnimationsNow().
+      //   - The timeline is of a kind that never tracks wallclock time, like
+      //     a scroll timeline. In this case, just use the timeline's current
+      //     time.
+      bool tracksWallclockTime = timeline->TracksWallclockTime();
+      if (timeline->AsDocumentTimeline() && !tracksWallclockTime) {
         continue;
       }
 
-      Nullable<TimeDuration> readyTime = timeline->ToTimelineTime(aReadyTime);
+      Nullable<TimeDuration> readyTime;
+      if (tracksWallclockTime) {
+        readyTime = timeline->ToTimelineTime(aReadyTime);
+      } else {
+        readyTime = timeline->GetCurrentTime();
+      }
       animation->TriggerOnNextTick(readyTime);
 
       iter.Remove();
     }
   };
 
   triggerAnimationsAtReadyTime(mPlayPendingSet);
   triggerAnimationsAtReadyTime(mPausePendingSet);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -653,19 +653,23 @@ AddAnimationsForProperty(nsIFrame* aFram
     // will have their start time updated with the current wallclock time.
     // If we can't convert that wallclock time back to an equivalent timeline
     // time, we won't be able to update the content animation and it will end
     // up being out of sync with the layer animation.
     //
     // Currently this only happens when the timeline is driven by a refresh
     // driver under test control. In this case, the next time the refresh
     // driver is advanced it will trigger any pending animations.
+    //
+    // This does not apply to scroll timelines, which don't track wallclock
+    // time, and also don't have their start time updated with a wallclock time.
     if (anim->PlayState() == AnimationPlayState::Pending &&
         (anim->GetTimeline() &&
-         !anim->GetTimeline()->TracksWallclockTime())) {
+         ((anim->GetTimeline()->AsDocumentTimeline() || anim->GetCurrentOrPendingStartTime().IsNull()) &&
+          !anim->GetTimeline()->TracksWallclockTime()))) {
       continue;
     }
 
     AddAnimationForProperty(aFrame, *property, anim, aLayer, aData, aPending);
     keyframeEffect->SetIsRunningOnCompositor(aProperty, true);
     sentAnimations = true;
   }