Bug 927349 part 5 - Cancel players that are no longer used; r=jwatt
authorBrian Birtles <birtles@gmail.com>
Thu, 18 Dec 2014 08:42:41 +0900
changeset 220178 f47c180eb1c3b82538f9562dce44e2fad7cc61ab
parent 220177 7441537d3f5ba558acddc11b4e0c166e2de58624
child 220179 19407030d97a2c2f66d3207e9a39201e09b8e7ef
push id53039
push userbbirtles@mozilla.com
push dateWed, 17 Dec 2014 23:43:01 +0000
treeherdermozilla-inbound@1e4ce2a592b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs927349
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 927349 part 5 - Cancel players that are no longer used; r=jwatt This patch adds a means of terminating an animation so that is has no effect. The procedure is defined by Web Animations: http://w3c.github.io/web-animations/#cancelling-a-player-section We don't implement all of this, however, since we don't currently support the finished promise or custom effects. In a later bug we will expose this as the cancel() method on AnimationPlayer. We call this method for terminated animations in nsAnimationManager and nsTransitionManager to ensure they get removed from the pending player tracker and so that, for example, the ready promise of CSS Animation player objects is rejected when the corresponding item is removed from animation-name.
dom/animation/AnimationPlayer.cpp
dom/animation/AnimationPlayer.h
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.cpp
layout/style/nsTransitionManager.cpp
--- a/dom/animation/AnimationPlayer.cpp
+++ b/dom/animation/AnimationPlayer.cpp
@@ -155,16 +155,30 @@ AnimationPlayer::ResolveStartTime()
   mStartTime.SetValue(readyTime.Value() - mHoldTime.Value());
   mHoldTime.SetNull();
 
   if (mReady) {
     mReady->MaybeResolve(this);
   }
 }
 
+void
+AnimationPlayer::Cancel()
+{
+  if (mIsPending) {
+    CancelPendingPlay();
+    if (mReady) {
+      mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    }
+  }
+
+  mHoldTime.SetNull();
+  mStartTime.SetNull();
+}
+
 bool
 AnimationPlayer::IsRunning() const
 {
   if (IsPaused() || !GetSource() || GetSource()->IsFinishedTransition()) {
     return false;
   }
 
   ComputedTiming computedTiming = GetSource()->GetComputedTiming();
@@ -235,28 +249,18 @@ AnimationPlayer::DoPlay()
   mReady = nullptr;
 
   ResolveStartTime();
 }
 
 void
 AnimationPlayer::DoPause()
 {
-  // Cancel a pending play
   if (mIsPending) {
-    nsIDocument* doc = GetRenderedDocument();
-    if (doc) {
-      PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
-      if (tracker) {
-        tracker->RemovePlayPending(*this);
-      }
-    }
-
-    mIsPending = false;
-
+    CancelPendingPlay();
     // Resolve the ready promise since we currently only use it for
     // players that are waiting to play. Later (in bug 1109390), we will
     // use this for players waiting to pause as well and then we won't
     // want to resolve it just yet.
     if (mReady) {
       mReady->MaybeResolve(this);
     }
   }
@@ -284,16 +288,34 @@ void
 AnimationPlayer::PostUpdate()
 {
   AnimationPlayerCollection* collection = GetCollection();
   if (collection) {
     collection->NotifyPlayerUpdated();
   }
 }
 
+void
+AnimationPlayer::CancelPendingPlay()
+{
+  if (!mIsPending) {
+    return;
+  }
+
+  nsIDocument* doc = GetRenderedDocument();
+  if (doc) {
+    PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
+    if (tracker) {
+      tracker->RemovePlayPending(*this);
+    }
+  }
+
+  mIsPending = false;
+}
+
 StickyTimeDuration
 AnimationPlayer::SourceContentEnd() const
 {
   if (!mSource) {
     return StickyTimeDuration(0);
   }
 
   return mSource->Timing().mDelay
--- a/dom/animation/AnimationPlayer.h
+++ b/dom/animation/AnimationPlayer.h
@@ -87,16 +87,17 @@ public:
   void PauseFromJS() { Pause(); }
 
   void SetSource(Animation* aSource);
   void Tick();
   // Sets the start time of the player to the current time of its timeline.
   // This should only be called on a player that is currently waiting to play
   // (and therefore has a null start time but a fixed hold time).
   void ResolveStartTime();
+  void Cancel();
 
   const nsString& Name() const {
     return mSource ? mSource->Name() : EmptyString();
   }
 
   bool IsPaused() const { return PlayState() == AnimationPlayState::Paused; }
   bool IsRunning() const;
 
@@ -127,16 +128,20 @@ public:
                     bool& aNeedsRefreshes);
 
 protected:
   void DoPlay();
   void DoPause();
 
   void FlushStyle() const;
   void PostUpdate();
+  // Remove this player from the pending player tracker and resets mIsPending
+  // as necessary. The caller is responsible for resolving or aborting the
+  // mReady promise as necessary.
+  void CancelPendingPlay();
   StickyTimeDuration SourceContentEnd() const;
 
   nsIDocument* GetRenderedDocument() const;
   nsPresContext* GetPresContext() const;
   virtual css::CommonAnimationManager* GetAnimationManager() const = 0;
   AnimationPlayerCollection* GetCollection() const;
 
   nsRefPtr<AnimationTimeline> mTimeline;
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -229,16 +229,19 @@ struct AnimationPlayerCollection : publi
                       "must call destructor through element property dtor");
     MOZ_COUNT_DTOR(AnimationPlayerCollection);
     PR_REMOVE_LINK(this);
     mManager->ElementCollectionRemoved();
   }
 
   void Destroy()
   {
+    for (size_t playerIdx = mPlayers.Length(); playerIdx-- != 0; ) {
+      mPlayers[playerIdx]->Cancel();
+    }
     // This will call our destructor.
     mElement->DeleteProperty(mElementProperty);
   }
 
   static void PropertyDtor(void *aObject, nsIAtom *aPropertyName,
                            void *aPropertyValue, void *aData);
 
   void Tick();
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -326,29 +326,35 @@ nsAnimationManager::CheckAnimationRule(n
           }
 
           // Replace new animation with the (updated) old one and remove the
           // old one from the array so we don't try to match it any more.
           //
           // Although we're doing this while iterating this is safe because
           // we're not changing the length of newPlayers and we've finished
           // iterating over the list of old iterations.
+          newPlayer->Cancel();
           newPlayer = nullptr;
           newPlayers.ReplaceElementAt(newIdx, oldPlayer);
           collection->mPlayers.RemoveElementAt(oldIdx);
         }
       }
     } else {
       collection =
         GetAnimationPlayers(aElement, aStyleContext->GetPseudoType(), true);
     }
     collection->mPlayers.SwapElements(newPlayers);
     collection->mNeedsRefreshes = true;
     collection->Tick();
 
+    // Cancel removed animations
+    for (size_t newPlayerIdx = newPlayers.Length(); newPlayerIdx-- != 0; ) {
+      newPlayers[newPlayerIdx]->Cancel();
+    }
+
     TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
     UpdateStyleAndEvents(collection, refreshTime,
                          EnsureStyleRule_IsNotThrottled);
     // We don't actually dispatch the mPendingEvents now.  We'll either
     // dispatch them the next time we get a refresh driver notification
     // or the next time somebody calls
     // nsPresShell::FlushPendingNotifications.
     if (!mPendingEvents.IsEmpty()) {
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -295,16 +295,17 @@ nsTransitionManager::StyleContextChanged
       if ((checkProperties &&
            !allTransitionProperties.HasProperty(prop.mProperty)) ||
           // properties whose computed values changed but delay and
           // duration are both zero
           !ExtractComputedValueForTransition(prop.mProperty, aNewStyleContext,
                                              currentValue) ||
           currentValue != segment.mToValue) {
         // stop the transition
+        player->Cancel();
         players.RemoveElementAt(i);
         collection->UpdateAnimationGeneration(mPresContext);
       }
     } while (i != 0);
 
     if (players.IsEmpty()) {
       collection->Destroy();
       collection = nullptr;