Bug 1486278 - Workaround Bug 1495350 in video controls r=Gijs,birtles
authorTimothy Guan-tin Chien <timdream@gmail.com>
Tue, 02 Oct 2018 19:11:31 +0000
changeset 487613 4392b5198fb7773f6148d2caedff82da5f527bfe
parent 487612 cfa50dc1f61600b08d966210d0319b052ac86627
child 487614 0d4e73bc2cd705d7a021c75a0e8aeb174ab4db59
child 487629 5e5d6abb39afe0a23555db526a5bbbf3d59b4cd9
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersGijs, birtles
bugs1486278, 1495350
milestone64.0a1
Bug 1486278 - Workaround Bug 1495350 in video controls r=Gijs,birtles For compositor animations, we don't guarantee the finished promise callback to run in the first frame after the animation finishes. By setting fill: both, we set the animation to fill until the main thread has a chance to catch up. A filling animation has to be cancelled explicitly, otherwise the value of the animating style will be locked at the last frame of the animation. Differential Revision: https://phabricator.services.mozilla.com/D7317
toolkit/content/widgets/videocontrols.js
toolkit/content/widgets/videocontrols.xml
--- a/toolkit/content/widgets/videocontrols.js
+++ b/toolkit/content/widgets/videocontrols.js
@@ -1108,36 +1108,41 @@ this.VideoControlsImplPageWidget = class
         clickToPlay: {
           keyframes: [
             { transform: "scale(3)", opacity: 0 },
             { transform: "scale(1)", opacity: 0.55 },
           ],
           options: {
             easing: "ease",
             duration: 400,
+             // The fill mode here and below is a workaround to avoid flicker
+             // due to bug 1495350.
+             fill: "both",
           },
         },
         controlBar: {
           keyframes: [
             { opacity: 0 },
             { opacity: 1 },
           ],
           options: {
             easing: "ease",
             duration: 200,
+            fill: "both",
           },
         },
         statusOverlay: {
           keyframes: [
             { opacity: 0 },
             { opacity: 0, offset: .72 }, // ~750ms into animation
             { opacity: 1 },
           ],
           options: {
             duration: 1050,
+            fill: "both",
           },
         },
       },
 
       startFade(element, fadeIn, immediate = false) {
         let animationProp =
           this.animationProps[element.id];
         if (!animationProp) {
@@ -1198,17 +1203,17 @@ this.VideoControlsImplPageWidget = class
             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.playbackRate = 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":
@@ -1217,24 +1222,31 @@ this.VideoControlsImplPageWidget = class
                 throw new Error("Unknown Animation playState: " + animation.playState);
             }
             finishedPromise = animation.finished;
           }
         } else { // immediate
           animation.cancel();
           finishedPromise = Promise.resolve();
         }
-        finishedPromise.then(() => {
+        finishedPromise.then(animation => {
           if (element == this.controlBar) {
             this.onControlBarAnimationFinished();
           }
           element.classList.remove(fadeIn ? "fadein" : "fadeout");
           if (!fadeIn) {
             element.hidden = true;
           }
+          if (animation) {
+            // Explicitly clear the animation effect so that filling animations
+            // stop overwriting stylesheet styles. Remove when bug 1495350 is
+            // fixed and animations are no longer filling animations.
+            // This also stops them from accumulating (See bug 1253476).
+            animation.cancel();
+          }
         }, () => { /* Do nothing on rejection */ });
       },
 
       _triggeredByControls: false,
 
       startPlay() {
         this._triggeredByControls = true;
         this.hideClickToPlay();
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1099,36 +1099,41 @@
         clickToPlay: {
           keyframes: [
             { transform: "scale(3)", opacity: 0 },
             { transform: "scale(1)", opacity: 0.55 },
           ],
           options: {
             easing: "ease",
             duration: 400,
+            // The fill mode here and below is a workaround to avoid flicker
+            // due to bug 1495350.
+            fill: "both",
           },
         },
         controlBar: {
           keyframes: [
             { opacity: 0 },
             { opacity: 1 },
           ],
           options: {
             easing: "ease",
             duration: 200,
+            fill: "both",
           },
         },
         statusOverlay: {
           keyframes: [
             { opacity: 0 },
             { opacity: 0, offset: .72 }, // ~750ms into animation
             { opacity: 1 },
           ],
           options: {
             duration: 1050,
+            fill: "both",
           },
         },
       },
 
       startFade(element, fadeIn, immediate = false) {
         // Bug 493523, the scrubber doesn't call valueChanged while hidden,
         // so our dependent state (eg, timestamp in the thumb) will be stale.
         // As a workaround, update it manually when it first becomes unhidden.
@@ -1193,17 +1198,17 @@
             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.playbackRate = 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":
@@ -1211,24 +1216,31 @@
                 throw new Error("Unknown Animation playState: " + animation.playState);
             }
             finishedPromise = animation.finished;
           }
         } else { // immediate
           animation.cancel();
           finishedPromise = Promise.resolve();
         }
-        finishedPromise.then(() => {
+        finishedPromise.then(animation => {
           if (element == this.controlBar) {
             this.onControlBarAnimationFinished();
           }
           element.classList.remove(fadeIn ? "fadein" : "fadeout");
           if (!fadeIn) {
             element.hidden = true;
           }
+          if (animation) {
+            // Explicitly clear the animation effect so that filling animations
+            // stop overwriting stylesheet styles. Remove when bug 1495350 is
+            // fixed and animations are no longer filling animations.
+            // This also stops them from accumulating (See bug 1253476).
+            animation.cancel();
+          }
         }, () => { /* Do nothing on rejection */ });
       },
 
       _triggeredByControls: false,
 
       startPlay() {
         this._triggeredByControls = true;
         this.hideClickToPlay();