Bug 1112480 part 3 - Add AnimationPlayer::StartOnNextTick; r=jwatt
authorBrian Birtles <birtles@gmail.com>
Fri, 09 Jan 2015 07:57:58 +0900
changeset 248640 73091940bedfc769250e199d9489e3545ccf235e
parent 248639 dfba5219541ff18c96f1b43e52f4d54bd4bc707d
child 248641 69a084bd65e4fe5a47f6d4f36f3dccfb126c975b
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1112480
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1112480 part 3 - Add AnimationPlayer::StartOnNextTick; r=jwatt Adds a method that schedules an animation player to begin on the next tick using the supplied time as the start time. We don't call this yet, however, but simply add the method and the mPendingReadyTime member it sets.
dom/animation/AnimationPlayer.cpp
dom/animation/AnimationPlayer.h
--- a/dom/animation/AnimationPlayer.cpp
+++ b/dom/animation/AnimationPlayer.cpp
@@ -136,16 +136,33 @@ AnimationPlayer::Tick()
   // FIXME (bug 1112969): Check if we are pending but have lost access to the
   // pending player tracker. If that's the case we should probably trigger the
   // animation now.
 
   UpdateSourceContent();
 }
 
 void
+AnimationPlayer::StartOnNextTick(const Nullable<TimeDuration>& aReadyTime)
+{
+  // Normally we expect the play state to be pending but it's possible that,
+  // due to the handling of possibly orphaned players in Tick() [coming
+  // in a later patch in this series], this player
+  // got started whilst still being in another document's pending player
+  // map.
+  if (PlayState() != AnimationPlayState::Pending) {
+    return;
+  }
+
+  // TODO: Make sure the player starts even if |aReadyTime| is null
+  // (this is covered by a subsequent patch in this series)
+  mPendingReadyTime = aReadyTime;
+}
+
+void
 AnimationPlayer::StartNow()
 {
   MOZ_ASSERT(PlayState() == AnimationPlayState::Pending,
              "Expected to start a pending player");
   MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
              "Expected an active timeline");
 
   ResumeAt(mTimeline->GetCurrentTime().Value());
@@ -346,16 +363,17 @@ AnimationPlayer::CancelPendingPlay()
   if (doc) {
     PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
     if (tracker) {
       tracker->RemovePlayPending(*this);
     }
   }
 
   mIsPending = false;
+  mPendingReadyTime.SetNull();
 }
 
 StickyTimeDuration
 AnimationPlayer::SourceContentEnd() const
 {
   if (!mSource) {
     return StickyTimeDuration(0);
   }
--- a/dom/animation/AnimationPlayer.h
+++ b/dom/animation/AnimationPlayer.h
@@ -91,37 +91,69 @@ public:
   // in future we will likely have to flush style in
   // CSSAnimationPlayer::PauseFromJS so we leave it for now.
   void PauseFromJS() { Pause(); }
 
   void SetSource(Animation* aSource);
   void Tick();
 
   /**
-   * Sets the start time of a player that is waiting to play to the current
-   * time of its timeline.
-   *
-   * This will reset the pending flag if the call succeeded. The caller is
-   * responsible for removing the player from the PendingPlayerTracker though.
-   *
    * Typically, when a player is played, it does not start immediately but is
    * added to a table of pending players on the document of its source content.
-   * In the meantime it sets its hold time to the time from which should
-   * begin playback.
+   * In the meantime it sets its hold time to the time from which playback
+   * should begin.
    *
    * When the document finishes painting, any pending players in its table
-   * are started by calling this method.
+   * are marked as being ready to start by calling StartOnNextTick.
+   * The moment when the paint completed is also recorded, converted to a
+   * timeline time, and passed to StartOnTick. This is so that when these
+   * players do start, they can be timed from the point when painting
+   * completed.
+   *
+   * After calling StartOnNextTick, players remain in the pending state until
+   * the next refresh driver tick. At that time they transition out of the
+   * pending state using the time passed to StartOnNextTick as the effective
+   * time at which they resumed.
    *
    * This approach means that any setup time required for performing the
    * initial paint of an animation such as layerization is not deducted from
    * the running time of the animation. Without this we can easily drop the
    * first few frames of an animation, or, on slower devices, the whole
    * animation.
+   *
+   * Furthermore:
+   *
+   * - Starting the player immediately when painting finishes is problematic
+   *   because the start time of the player will be ahead of its timeline
+   *   (since the timeline time is based on the refresh driver time).
+   *   That's a problem because the player is playing but its timing suggests
+   *   it starts in the future. We could update the timeline to match the start
+   *   time of the player but then we'd also have to update the timing and style
+   *   of all animations connected to that timeline or else be stuck in an
+   *   inconsistent state until the next refresh driver tick.
+   *
+   * - If we simply use the refresh driver time on its next tick, the lag
+   *   between triggering an animation and its effective start is unacceptably
+   *   long.
+   *
+   * Note that the caller of this method is responsible for removing the player
+   * from any PendingPlayerTracker it may have been added to.
    */
+  void StartOnNextTick(const Nullable<TimeDuration>& aReadyTime);
+
+  // Testing only: Start a pending player using the current timeline time.
+  // This is used to support existing tests that expect animations to begin
+  // immediately. Ideally we would rewrite the those tests and get rid of this
+  // method, but there are a lot of them.
+  //
+  // As with StartOnNextTick, the caller of this method is responsible for
+  // removing the player from any PendingPlayerTracker it may have been added
+  // to.
   void StartNow();
+
   void Cancel();
 
   const nsString& Name() const {
     return mSource ? mSource->Name() : EmptyString();
   }
 
   bool IsPaused() const { return PlayState() == AnimationPlayState::Paused; }
   bool IsRunning() const;
@@ -171,16 +203,17 @@ protected:
   virtual css::CommonAnimationManager* GetAnimationManager() const = 0;
   AnimationPlayerCollection* GetCollection() const;
 
   nsRefPtr<AnimationTimeline> mTimeline;
   nsRefPtr<Animation> mSource;
   // The beginning of the delay period.
   Nullable<TimeDuration> mStartTime; // Timeline timescale
   Nullable<TimeDuration> mHoldTime;  // Player timescale
+  Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
 
   // A Promise that is replaced on each call to Play() (and in future Pause())
   // and fulfilled when Play() is successfully completed.
   // This object is lazily created by GetReady.
   nsRefPtr<Promise> mReady;
 
   // Indicates if the player is in the pending state. We use this rather
   // than checking if this player is tracked by a PendingPlayerTracker.