Bug 1274520 part 2 - Listen control events in video controls on system group. r=gijs draft
authorXidorn Quan <me@upsuper.org>
Wed, 25 May 2016 18:55:06 +1000
changeset 372665 f1755a506bf8f19a108b8750f5631958bd97c2a8
parent 372664 6a40b3d0cec138f63ab3d91b068dbfeb45967c7e
child 372666 d33d5451aff5dade23b6457242c255c0c93b82fc
push id19556
push userxquan@mozilla.com
push dateSun, 29 May 2016 23:37:12 +0000
reviewersgijs
bugs1274520
milestone49.0a1
Bug 1274520 part 2 - Listen control events in video controls on system group. r=gijs Listeners in the system group are invoked after listeners in the default group, so we can check defaultPrevented synchronously, and we have to change the play state according to the state of the play button. Due to the same reason above, we need to check the UI async in tests. MozReview-Commit-ID: Bs4MZvGefJj
toolkit/content/tests/widgets/test_videocontrols.html
toolkit/content/widgets/videocontrols.xml
--- a/toolkit/content/tests/widgets/test_videocontrols.html
+++ b/toolkit/content/tests/widgets/test_videocontrols.html
@@ -303,27 +303,29 @@ function runTest(event) {
       ok(!isMuteButtonMuted(), "Mute button says it's not muted");
 
       synthesizeMouse(video, fullscreenButtonCenterX, fullscreenButtonCenterY, { });
       break;
 
     case 24:
       is(event.type, "mozfullscreenchange", "checking event type");
       is(video.volume, 0.6, "Volume should still be 0.6");
-      isVolumeSliderShowingCorrectVolume(video.volume);
-
-      synthesizeKey("VK_ESCAPE", {});
+      SimpleTest.executeSoon(function() {
+        isVolumeSliderShowingCorrectVolume(video.volume);
+        synthesizeKey("VK_ESCAPE", {});
+      });
       break;
 
     case 25:
       is(event.type, "mozfullscreenchange", "checking event type");
       is(video.volume, 0.6, "Volume should still be 0.6");
-      isVolumeSliderShowingCorrectVolume(video.volume);
-
-      SimpleTest.finish();
+      SimpleTest.executeSoon(function() {
+        isVolumeSliderShowingCorrectVolume(video.volume);
+        SimpleTest.finish();
+      });
       break;
 
     default:
       throw "unexpected test #" + testnum + " w/ event " + event.type;
   }
 
   testnum++;
 }
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -629,18 +629,20 @@
                             this.log("!!! event " + aEvent.type + " not handled!");
                     }
                 },
 
                 terminateEventListeners : function () {
                     for (let event of this.videoEvents)
                         this.video.removeEventListener(event, this, false);
 
-                    for (let element of this.controlListeners)
-                        element.item.removeEventListener(element.event, element.func, false);
+                    for (let element of this.controlListeners) {
+                        element.item.removeEventListener(element.event, element.func,
+                                                         { mozSystemGroup: true });
+                    }
 
                     delete this.controlListeners;
 
                     this.log("--- videocontrols terminated ---");
                 },
 
                 hasError : function () {
                     return (this.video.error != null || this.video.networkState == this.video.NETWORK_NO_SOURCE);
@@ -947,21 +949,25 @@
                         return;
 
                     this.scrubber.dragStateChanged(false);
                     element.hidden = true;
                 },
 
                 _triggeredByControls: false,
 
+                startPlay : function () {
+                    this._triggeredByControls = true;
+                    this.hideClickToPlay();
+                    this.video.play();
+                },
+
                 togglePause : function () {
                     if (this.video.paused || this.video.ended) {
-                        this._triggeredByControls = true;
-                        this.hideClickToPlay();
-                        this.video.play();
+                        this.startPlay();
                     } else {
                         this.video.pause();
                     }
 
                     // We'll handle style changes in the event listener for
                     // the "play" and "pause" events, same as if content
                     // script was controlling video playback.
                 },
@@ -1028,31 +1034,23 @@
                     if (this.hasError() && !this.suppressError) {
                         // Errors that can be dismissed should be placed here as we discover them.
                         if (this.video.error.code != this.video.error.MEDIA_ERR_ABORTED)
                             return;
                         this.statusOverlay.hidden = true;
                         this.suppressError = true;
                         return;
                     }
-
-                    // Read defaultPrevented and the playback state asynchronously,
-                    // since Web content may want to consume the "click" event
-                    // but will only receive it after us. If web content
-                    // doesn't use preventDefault but still toggles playback,
-                    // we will treat that act the same as a call to preventDefault()
-                    // so the web content-initiated toggle is not reverted.
-                    let previousState = this.video.paused;
-                    setTimeout(() => {
-                        if (e.defaultPrevented ||
-                            this.video.paused != previousState) {
-                            return;
-                        }
-                        this.togglePause();
-                    }, 0);
+                    if (e.defaultPrevented)
+                        return;
+                    if (this.playButton.hasAttribute("paused")) {
+                        this.startPlay();
+                    } else {
+                        this.video.pause();
+                    }
                 },
                 hideClickToPlay : function () {
                     let videoHeight = this.video.clientHeight;
                     let videoWidth = this.video.clientWidth;
 
                     // The play button will animate to 3x its size. This
                     // shows the animation unless the video is too small
                     // to show 2/3 of the animation.
@@ -1338,29 +1336,29 @@
                     var self = this;
 
                     this.controlListeners = [];
 
                     // Helper function to add an event listener to the given element
                     function addListener(elem, eventName, func) {
                       let boundFunc = func.bind(self);
                       self.controlListeners.push({ item: elem, event: eventName, func: boundFunc });
-                      elem.addEventListener(eventName, boundFunc, false);
+                      elem.addEventListener(eventName, boundFunc, { mozSystemGroup: true });
                     }
 
                     addListener(this.muteButton, "command", this.toggleMute);
                     addListener(this.playButton, "click", this.clickToPlayClickHandler);
                     addListener(this.fullscreenButton, "command", this.toggleFullscreen);
                     addListener(this.clickToPlay, "click", this.clickToPlayClickHandler);
                     addListener(this.controlsSpacer, "click", this.clickToPlayClickHandler);
                     addListener(this.controlsSpacer, "dblclick", this.toggleFullscreen);
 
                     addListener(this.videocontrols, "resizevideocontrols", this.adjustControlSize);
                     addListener(this.videocontrols, "transitionend", this.onTransitionEnd);
-                    addListener(this.video.ownerDocument, "mozfullscreenchange", this.onFullscreenChange);
+                    addListener(this.video.ownerDocument, "fullscreenchange", this.onFullscreenChange);
                     addListener(this.video, "keypress", this.keyHandler);
 
                     addListener(this.videocontrols, "dragstart", function(event) {
                         event.preventDefault(); //prevent dragging of controls image (bug 517114)
                     });
 
                     this.log("--- videocontrols initialized ---");
                 }
@@ -1666,18 +1664,20 @@
             randomID : 0,
             videoEvents : ["play",
                            "playing"],
             controlListeners: [],
             terminateEventListeners : function () {
               for (let event of this.videoEvents)
                 this.video.removeEventListener(event, this, false);
 
-              for (let element of this.controlListeners)
-                element.item.removeEventListener(element.event, element.func, false);
+              for (let element of this.controlListeners) {
+                element.item.removeEventListener(element.event, element.func,
+                                                 { mozSystemGroup: true });
+              }
 
               delete this.controlListeners;
             },
 
             hasError : function () {
               return (this.video.error != null || this.video.networkState == this.video.NETWORK_NO_SOURCE);
             },
 
@@ -1731,17 +1731,17 @@
               this.video = binding.parentNode;
               this.clickToPlay       = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay");
               this.noControlsOverlay = document.getAnonymousElementByAttribute(binding, "class", "statusOverlay");
 
               let self = this;
               function addListener(elem, eventName, func) {
                 let boundFunc = func.bind(self);
                 self.controlListeners.push({ item: elem, event: eventName, func: boundFunc });
-                elem.addEventListener(eventName, boundFunc, false);
+                elem.addEventListener(eventName, boundFunc, { mozSystemGroup: true });
               }
               addListener(this.clickToPlay, "click", this.clickToPlayClickHandler);
               addListener(this.video, "MozNoControlsBlockedVideo", this.blockedVideoHandler);
 
               for (let event of this.videoEvents) {
                 this.video.addEventListener(event, this, false);
               }