Bug 1474247 - Remove the animation from the pending tracker if the new target element is in a different document. r?birtles draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Wed, 11 Jul 2018 11:39:57 +0900
changeset 816370 6fc3248fa45d77ef5dd483e58ae8cf939500840a
parent 816369 fadf38c85f1b9305890c6af597c12a11de10178b
push id115815
push userhikezoe@mozilla.com
push dateWed, 11 Jul 2018 02:48:59 +0000
reviewersbirtles
bugs1474247
milestone63.0a1
Bug 1474247 - Remove the animation from the pending tracker if the new target element is in a different document. r?birtles MozReview-Commit-ID: 1usQWhujUMC
dom/animation/Animation.h
dom/animation/KeyframeEffect.cpp
dom/animation/test/mozilla/test_pending_animation_tracker.html
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -391,16 +391,23 @@ public:
 
   /**
    * Performs the same steps as CancelPendingTasks and also rejects and
    * recreates the ready promise if the animation was pending.
    */
   void ResetPendingTasks();
 
   /**
+   * Remove this animation from the pending animation tracker and reset
+   * mPendingReadyTime.
+   * Unlike CancelPendingTasks() this function preserves mPendingState.
+   */
+  void RemovePendingTasksFromTracker();
+
+  /**
    * Used by subclasses to synchronously queue a cancel event in situations
    * where the Animation may have been cancelled.
    *
    * We need to do this synchronously because after a CSS animation/transition
    * is canceled, it will be released by its owning element and may not still
    * exist when we would normally go to queue events on the next tick.
    */
   virtual void MaybeQueueCancelEvent(const StickyTimeDuration& aActiveTime) {};
@@ -465,24 +472,16 @@ protected:
   /**
    * Remove this animation from the pending animation tracker and reset
    * mPendingState as necessary. The caller is responsible for resolving or
    * aborting the mReady promise as necessary.
    */
   void CancelPendingTasks();
 
   /**
-   * Remove this animation from the pending animation tracker and reset
-   * mPendingReadyTime.
-   * Unlike the above CancelPendingTasks() this function preserves
-   * mPendingState.
-   */
-  void RemovePendingTasksFromTracker();
-
-  /**
    * Returns true if this animation is not only play-pending, but has
    * yet to be given a pending ready time. This roughly corresponds to
    * animations that are waiting to be painted (since we set the pending
    * ready time at the end of painting). Identifying such animations is
    * useful because in some cases animations that are painted together
    * may need to be synchronized.
    *
    * We don't, however, want to include animations with a fixed start time such
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -946,16 +946,26 @@ KeyframeEffect::SetTarget(const Nullable
     ResetIsRunningOnCompositor();
 
     RequestRestyle(EffectCompositor::RestyleType::Layer);
 
     nsAutoAnimationMutationBatch mb(mTarget->mElement->OwnerDoc());
     if (mAnimation) {
       if (!newTarget) {
         mAnimation->ResetPendingTasks();
+      } else if (mAnimation->Pending()) {
+        nsIDocument* newRenderedDocument =
+          newTarget->mElement->GetComposedDoc();
+        if (newRenderedDocument &&
+            newRenderedDocument != GetRenderedDocument()) {
+          // If the new target is in a different document, we need to remove
+          // the animation from the PendingAnimationTracker of the old document
+          // without changing pending state.
+          mAnimation->RemovePendingTasksFromTracker();
+        }
       }
       nsNodeUtils::AnimationRemoved(mAnimation);
     }
   }
 
   mTarget = newTarget;
 
   if (mTarget) {
--- a/dom/animation/test/mozilla/test_pending_animation_tracker.html
+++ b/dom/animation/test/mozilla/test_pending_animation_tracker.html
@@ -173,10 +173,34 @@ test(t => {
   anim.effect.target = target;
 
   assert_true(anim.pending, 'The animation should be still pending');
   assert_true(SpecialPowers.DOMWindowUtils.isAnimationPending(anim),
               'The animation should be now tracked by tracker');
 }, 'Setting target element to the orphaned animation starts being tracked ' +
    'by the tracker');
 
+test(t => {
+  const target = addDiv(t);
+  const anim = target.animate(null, 100 * MS_PER_SEC);
+  assert_true(anim.pending, 'The animation should be pending');
+  assert_true(SpecialPowers.DOMWindowUtils.isAnimationPending(anim),
+              'The animation should be tracked by tracker');
+
+  const targetInIframe = iframe.contentDocument.createElement('div');
+  iframe.contentDocument.body.appendChild(targetInIframe);
+  t.add_cleanup(() => targetInIframe.remove());
+
+  anim.effect.target = targetInIframe;
+
+  assert_true(anim.pending, 'The animation should be still pending');
+  assert_false(SpecialPowers.DOMWindowUtils.isAnimationPending(anim),
+               'The animation should no more be tracked by the tracker of ' +
+               'the parent document');
+  assert_true(SpecialPowers.getDOMWindowUtils(iframe.contentWindow)
+                           .isAnimationPending(anim),
+              'The animation should be now tracked by the tracker of ' +
+              'the iframe document');
+}, 'Moving target element to other document changes the pending animation ' +
+   'tracker');
+
 </script>
 </body>