Bug 962560 - Audio volume UI resets to full volume after toggling the screen size of a video. r=gijs
authorJared Wein <jwein@mozilla.com>
Tue, 10 May 2016 08:54:00 -0400
changeset 325531 43ee68814e2395de77b888f43945e638611dd0f6
parent 325530 a9e91607762fb4637d7fc2ecb4a7055f35638d34
child 325532 7fc6b24beea495728bc5070a5b621b31e403b82d
push idunknown
push userunknown
push dateunknown
reviewersgijs
bugs962560
milestone49.0a1
Bug 962560 - Audio volume UI resets to full volume after toggling the screen size of a video. r=gijs MozReview-Commit-ID: HHPOHZQoK50
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
@@ -25,26 +25,30 @@ const videoHeight = 240;
 const videoDuration = 3.8329999446868896;
 
 const playButtonWidth = 28;
 const playButtonHeight = 28;
 const muteButtonWidth = 33;
 const muteButtonHeight = 28;
 const durationWidth = 34;
 const fullscreenButtonWidth = document.fullscreenEnabled ? 28 : 0;
+const fullscreenButtonHeight = document.fullscreenEnabled ? 28 : 0;
 const volumeSliderWidth = 32;
 const scrubberWidth = videoWidth - playButtonWidth - durationWidth - muteButtonWidth - volumeSliderWidth - fullscreenButtonWidth;
 const scrubberHeight = 28;
 
 // Play button is on the bottom-left
 const playButtonCenterX = 0 + Math.round(playButtonWidth / 2);
 const playButtonCenterY = videoHeight - Math.round(playButtonHeight / 2);
 // Mute button is on the bottom-right before the full screen button and volume slider
 const muteButtonCenterX = videoWidth - Math.round(muteButtonWidth / 2) - volumeSliderWidth - fullscreenButtonWidth;
 const muteButtonCenterY = videoHeight - Math.round(muteButtonHeight / 2);
+// Fullscreen button is on the bottom-right at the far end
+const fullscreenButtonCenterX = videoWidth - Math.round(fullscreenButtonWidth / 2);
+const fullscreenButtonCenterY = videoHeight - Math.round(fullscreenButtonHeight / 2);
 // Scrubber bar is between the play and mute buttons. We don't need it's
 // X center, just the offset of its box.
 const scrubberOffsetX = 0 + playButtonWidth;
 const scrubberCenterY = videoHeight - Math.round(scrubberHeight / 2);
 
 var testnum = 1;
 var video = document.getElementById("video");
 
@@ -56,16 +60,23 @@ function getButtonByAttribute(aName, aVa
   return document.getAnonymousElementByAttribute(videocontrols, aName, aValue);
 }
 
 function isMuteButtonMuted() {
   var muteButton = getButtonByAttribute('class', 'muteButton');
   return muteButton.getAttribute('muted') === 'true';
 }
 
+function isVolumeSliderShowingCorrectVolume(expectedVolume) {
+  var volumeButton = getButtonByAttribute('anonid', 'volumeForeground');
+  let expectedPaddingRight = (1 - expectedVolume) * volumeSliderWidth + "px";
+  is(volumeButton.style.paddingRight, expectedPaddingRight,
+     "volume slider should match expected volume");
+}
+
 function runTest(event) {
   ok(true, "----- test #" + testnum + " -----");
 
   switch (testnum) {
     /*
      * Check operation of play/pause/mute/unmute buttons.
      */
     case 1:
@@ -284,16 +295,32 @@ function runTest(event) {
 
     case 23:
       is(event.type, "volumechange", "checking event type");
       is(video.volume, 0.6, "Volume should be 0.6.");
       ok(!video.muted, "Video is not muted.");
 
       ok(!isMuteButtonMuted(), "Mute button says it's not muted");
 
+      synthesizeMouse(video, fullscreenButtonCenterX, fullscreenButtonCenterY, { });
+      break;
+
+    case 24:
+      is(event.type, "fullscreenchange", "checking event type");
+      is(video.volume, 0.6, "Volume should still be 0.6");
+      isVolumeSliderShowingCorrectVolume(video.volume);
+
+      synthesizeKey("VK_ESCAPE", {});
+      break;
+
+    case 25:
+      is(event.type, "fullscreenchange", "checking event type");
+      is(video.volume, 0.6, "Volume should still be 0.6");
+      isVolumeSliderShowingCorrectVolume(video.volume);
+
       SimpleTest.finish();
       break;
 
     default:
       throw "unexpected test #" + testnum + " w/ event " + event.type;
   }
 
   testnum++;
@@ -304,16 +331,17 @@ function runTest(event) {
 function canplaythroughevent(event) {
   video.removeEventListener("canplaythrough",  canplaythroughevent, false);
   // Other events expected by the test.
   video.addEventListener("play",  runTest, false);
   video.addEventListener("pause", runTest, false);
   video.addEventListener("volumechange", runTest, false);
   video.addEventListener("seeking", runTest, false);
   video.addEventListener("seeked", runTest, false);
+  document.addEventListener("fullscreenchange", runTest, false);
   // Begin the test.
   runTest(event);
 }
 
 function startMediaLoad() {
   // Kick off test once video has loaded, in its canplaythrough event handler.
   video.src = "seek_with_sound.ogg";
   video.addEventListener("canplaythrough", canplaythroughevent, false);
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -402,23 +402,19 @@
                  * removed from the document and then reinserted). Thus, some one-time events may
                  * have already fired, and so we'll need to explicitly check the initial state.
                  */
                 setupInitialState : function() {
                     this.randomID = Math.random();
                     this.videocontrols.randomID = this.randomID;
 
                     this.setPlayButtonState(this.video.paused);
-                    this.updateMuteButtonState();
 
                     this.setFullscreenButtonState();
 
-                    var volume = this.video.muted ? 0 : Math.round(this.video.volume * 100);
-                    this.volumeControl.value = volume;
-
                     var duration = Math.round(this.video.duration * 1000); // in ms
                     var currentTime = Math.round(this.video.currentTime * 1000); // in ms
                     this.log("Initial playback position is at " + currentTime + " of " + duration);
                     // It would be nice to retain maxCurrentTimeSeen, but it would be difficult
                     // to determine if the media source changed while we were detached.
                     this.maxCurrentTimeSeen = currentTime;
                     this.showPosition(currentTime, duration);
 
@@ -469,16 +465,21 @@
                     this._durationLabelWidth = this.durationLabel.clientWidth;
                     this._muteButtonWidth = this.muteButton.clientWidth;
                     this._volumeControlWidth = this.volumeControl.clientWidth;
                     this._fullscreenButtonWidth = this.fullscreenButton.clientWidth;
                     this._controlBarHeight = this.controlBar.clientHeight;
                     this.controlBar.hidden = true;
                     this.adjustControlSize();
 
+                    // Can only update the volume controls once we've computed
+                    // _volumeControlWidth, since the volume slider implementation
+                    // depends on it.
+                    this.updateVolumeControls();
+
                     // Preserve Statistics when toggling fullscreen mode due to bug 714071.
                     if (this.video.mozMediaStatisticsShowing)
                         this.showStatistics(true);
 
                     this._handleCustomEventsBound = this.handleCustomEvents.bind(this);
                     this.video.addEventListener("media-showStatistics", this._handleCustomEventsBound, false, true);
                 },
 
@@ -519,16 +520,24 @@
                     // If the video hits an error, suppress controls if it
                     // hasn't managed to do anything else yet.
                     if (!this.firstFrameShown && this.hasError())
                         enabled = false;
 
                     return enabled;
                 },
 
+                updateVolumeControls() {
+                    var volume = this.video.muted ? 0 : this.video.volume;
+                    var volumePercentage = Math.round(volume * 100);
+                    this.updateMuteButtonState();
+                    this.volumeControl.value = volumePercentage;
+                    this.volumeForeground.style.paddingRight = (1 - volume) * this._volumeControlWidth + "px";
+                },
+
                 handleEvent : function (aEvent) {
                     this.log("Got media event ----> " + aEvent.type);
 
                     // If the binding is detached (or has been replaced by a
                     // newer instance of the binding), nuke our event-listeners.
                     if (this.videocontrols.randomID != this.randomID) {
                         this.terminateEventListeners();
                         return;
@@ -556,21 +565,17 @@
                             // We throttle timechange events, so the thumb might not be
                             // exactly at the end when the video finishes.
                             this.showPosition(Math.round(this.video.currentTime * 1000),
                                               Math.round(this.video.duration * 1000));
                             this.startFadeIn(this.controlBar);
                             this.setupStatusFader();
                             break;
                         case "volumechange":
-                            var volume = this.video.muted ? 0 : this.video.volume;
-                            var volumePercentage = Math.round(volume * 100);
-                            this.updateMuteButtonState();
-                            this.volumeControl.value = volumePercentage;
-                            this.volumeForeground.style.paddingRight = (1 - volume) * this._volumeControlWidth + "px";
+                            this.updateVolumeControls();
                             // Show the controls to highlight the changing volume,
                             // but only if the click-to-play overlay has already
                             // been hidden (we don't hide controls when the overlay is visible).
                             if (this.clickToPlay.hidden) {
                                 this.startFadeIn(this.controlBar);
                                 clearTimeout(this._hideControlsTimeout);
                                 this._hideControlsTimeout = setTimeout(this._hideControlsFn, this.HIDE_CONTROLS_TIMEOUT_MS);
                             }