Bug 1493089 - Cancel the pending animation instead of reversing it r=Gijs
authorTimothy Guan-tin Chien <timdream@gmail.com>
Wed, 26 Sep 2018 19:38:29 +0000
changeset 438345 7856d822921f3133aab589ad96e98d2eabd98eec
parent 438344 a4c0087797d6752c7f18563c8d2ead7e87eb2e9c
child 438346 6d25c44c39ea33ebe25829d945b00963dad39ea4
push id69984
push usertchien@mozilla.com
push dateWed, 26 Sep 2018 19:40:24 +0000
treeherderautoland@7856d822921f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs
bugs1493089, 1449532
milestone64.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 1493089 - Cancel the pending animation instead of reversing it r=Gijs My understanding of Web Animation API was wrong in bug 1449532. If a running animation is pending, playing a new (reversed) animation will cause it to play from the beginning of the new animation, instead of having the new animation to play, in reverse, from the current position. This has caused the user to see a fade out animation that shouldn't take place when the video loops, i.e. going from "seeking" to "seeked". The code now probe into the state of the animation instance and cancel the pending animation, reverse the running animation, or start the new (reversed) animation accordingly. Differential Revision: https://phabricator.services.mozilla.com/D6704
toolkit/content/widgets/videocontrols.js
toolkit/content/widgets/videocontrols.xml
--- a/toolkit/content/widgets/videocontrols.js
+++ b/toolkit/content/widgets/videocontrols.js
@@ -1153,20 +1153,46 @@ this.VideoControlsImplPageWidget = class
             return;
           }
         }
 
         element.classList.toggle("fadeout", !fadeIn);
         element.classList.toggle("fadein", fadeIn);
         let finishedPromise;
         if (!immediate) {
-          animation.playbackRate = fadeIn ? 1 : -1;
-          animation.play();
-          finishedPromise = animation.finished;
-        } else {
+          // At this point, if there is a pending animation, we just stop it to avoid it happening.
+          // If there is a running animation, we reverse it, to have it rewind to the beginning.
+          // If there is an idle/finished animation, we schedule a new one that reverses the finished one.
+          if (animation.pending) {
+            // Animation is running but pending.
+            // Just cancel the pending animation to stop its effect.
+            animation.cancel();
+            finishedPromise = Promise.resolve();
+          } else {
+            switch (animation.playState) {
+              case "idle":
+              case "finished":
+                // There is no animation currently playing.
+                // Schedule a new animation with the desired playback direction.
+                animation.updatePlaybackRate(fadeIn ? 1 : -1);
+                animation.play();
+                break;
+              case "running":
+                // Allow the animation to play from its current position in
+                // reverse to finish.
+                animation.reverse();
+                break;
+              case "pause":
+                throw new Error("Animation should never reach pause state.");
+              default:
+                throw new Error("Unknown Animation playState: " + animation.playState);
+            }
+            finishedPromise = animation.finished;
+          }
+        } else { // immediate
           animation.cancel();
           finishedPromise = Promise.resolve();
         }
         finishedPromise.then(() => {
           if (element == this.controlBar) {
             this.onControlBarAnimationFinished();
           }
           element.classList.remove(fadeIn ? "fadein" : "fadeout");
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1179,20 +1179,45 @@
             this.controlsSpacer.setAttribute("hideCursor", true);
           }
         }
 
         element.classList.toggle("fadeout", !fadeIn);
         element.classList.toggle("fadein", fadeIn);
         let finishedPromise;
         if (!immediate) {
-          animation.playbackRate = fadeIn ? 1 : -1;
-          animation.play();
-          finishedPromise = animation.finished;
-        } else {
+          // At this point, if there is a pending animation, we just stop it to avoid it happening.
+          // If there is a running animation, we reverse it, to have it rewind to the beginning.
+          // If there is an idle/finished animation, we schedule a new one that reverses the finished one.
+          if (animation.pending) {
+            // Animation is running but pending.
+            // Just cancel the pending animation to stop its effect.
+            animation.cancel();
+            finishedPromise = Promise.resolve();
+          } else {
+            switch (animation.playState) {
+              case "idle":
+              case "finished":
+                // There is no animation currently playing.
+                // Schedule a new animation with the desired playback direction.
+                animation.updatePlaybackRate(fadeIn ? 1 : -1);
+                animation.play();
+                break;
+              case "running":
+                // Allow the animation to play from its current position in
+                // reverse to finish.
+                animation.reverse();
+                break;
+              case "pause":
+              default:
+                throw new Error("Unknown Animation playState: " + animation.playState);
+            }
+            finishedPromise = animation.finished;
+          }
+        } else { // immediate
           animation.cancel();
           finishedPromise = Promise.resolve();
         }
         finishedPromise.then(() => {
           if (element == this.controlBar) {
             this.onControlBarAnimationFinished();
           }
           element.classList.remove(fadeIn ? "fadein" : "fadeout");