Bug 1328060 - re-adjust controls when get metadata of video duration. r=jaws
authorRay Lin <ralin@mozilla.com>
Mon, 06 Feb 2017 10:22:37 +0800
changeset 341111 0ea9dbb84c037e1b4bd01a0467afa8e2ff1ed806
parent 341110 d1bef3268b21d02e8810a3a4559f1aaac9f27f3f
child 341112 300f05ba7536892ae693528e5714fbee30176d0d
push id31326
push userkwierso@gmail.com
push dateTue, 07 Feb 2017 23:57:02 +0000
treeherdermozilla-central@c80c2c7bc043 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1328060
milestone54.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 1328060 - re-adjust controls when get metadata of video duration. r=jaws MozReview-Commit-ID: AKY5Umg08P6
toolkit/content/tests/widgets/test_videocontrols.html
toolkit/content/widgets/videocontrols.xml
toolkit/themes/shared/media/videocontrols.css
--- a/toolkit/content/tests/widgets/test_videocontrols.html
+++ b/toolkit/content/tests/widgets/test_videocontrols.html
@@ -25,17 +25,17 @@ const videoHeight = 240;
 const videoDuration = 3.8329999446868896;
 
 const controlBarMargin = 9;
 
 const playButtonWidth = 30;
 const playButtonHeight = 40;
 const muteButtonWidth = 30;
 const muteButtonHeight = 40;
-const positionAndDurationWidth = 85;
+const positionAndDurationWidth = 75;
 const fullscreenButtonWidth = 30;
 const fullscreenButtonHeight = 40;
 const volumeSliderWidth = 48;
 const volumeSliderMarginStart = 4;
 const volumeSliderMarginEnd = 6;
 const scrubberMargin = 9;
 const scrubberWidth = videoWidth - controlBarMargin - playButtonWidth - scrubberMargin * 2 - positionAndDurationWidth - muteButtonWidth - volumeSliderMarginStart - volumeSliderWidth - volumeSliderMarginEnd - fullscreenButtonWidth - controlBarMargin;
 const scrubberHeight = 40;
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -54,17 +54,17 @@
     <body>
       <![CDATA[
       // This method is a copy of the base binding's valueChanged(), except that it does
       // not dispatch a |change| event (to avoid exposing the event to web content), and
       // just calls the videocontrol's seekToPosition() method directly.
       switch (which) {
         case "curpos":
           // Update the time shown in the thumb.
-          this.positionValue = this.Utils.formatTime(newValue);
+          this.positionValue = this.Utils.formatTime(newValue, this.Utils.showHours);
           this.Utils.positionLabel.setAttribute("value", this.positionValue);
           // Update the value bar to match the thumb position.
           let percent = newValue / this.max;
           if (!isNaN(percent) && percent != Infinity) {
             this.valueBar.value = Math.round(percent * 10000); // has max=10000
           } else {
             this.valueBar.removeAttribute("value");
           }
@@ -218,16 +218,17 @@
       randomID : 0,
       videoEvents : ["play", "pause", "ended", "volumechange", "loadeddata",
                      "loadstart", "timeupdate", "progress",
                      "playing", "waiting", "canplay", "canplaythrough",
                      "seeking", "seeked", "emptied", "loadedmetadata",
                      "error", "suspend", "stalled",
                      "mozinterruptbegin", "mozinterruptend" ],
 
+      showHours: false,
       firstFrameShown : false,
       timeUpdateCount : 0,
       maxCurrentTimeSeen : 0,
       isPausedByDragging: false,
       _isAudioOnly : false,
 
       get isAudioOnly() { return this._isAudioOnly; },
       set isAudioOnly(val) {
@@ -240,16 +241,23 @@
         if (this._isAudioOnly) {
           this.video.style.height = this.controlBar.minHeight + "px";
           this.video.style.width = "66%";
         } else {
           this.video.style.removeProperty("height");
           this.video.style.removeProperty("width");
         }
       },
+
+      get isControlBarHidden() {
+        return this.controlBar.hidden ||
+               this.controlBar.hideByAdjustment ||
+               this.controlBar.getAttribute("fadeout") === "true";
+      },
+
       suppressError : false,
 
       setupStatusFader(immediate) {
         // Since the play button will be showing, we don't want to
         // show the throbber behind it. The throbber here will
         // only show if needed after the play button has been pressed.
         if (!this.clickToPlay.hidden) {
           this.startFadeOut(this.statusOverlay, true);
@@ -379,16 +387,26 @@
             },
             isAdjustableControl: {
               value: true
             },
             isWanted: {
               value: true,
               writable: true
             },
+            resized: {
+              value: false,
+              writable: true
+            },
+            resizedHandler: {
+              value: () => {
+                control.minWidth = control.clientWidth;
+              },
+              writable: true
+            },
             hideByAdjustment: {
               set: (v) => {
                 if (v) {
                   control.setAttribute("hidden", "true");
                 } else {
                   control.removeAttribute("hidden");
                 }
 
@@ -405,17 +423,21 @@
         // Cannot get minimal width of flexible scrubber and clickToPlay.
         // Rewrite to empirical value for now.
         this.controlBar.minHeight = 40;
         this.scrubberStack.minWidth = 64;
         this.volumeControl.minWidth = 48;
         this.clickToPlay.minWidth = 48;
 
         if (this.positionDurationBox) {
-          this.positionDurationBox.minWidth -= this.durationSpan.minWidth;
+          this.durationSpan.resized = true;
+          this.positionDurationBox.resized = true;
+          this.positionDurationBox.resizedHandler = () => {
+            this.positionDurationBox.minWidth = this.positionDurationBox.clientWidth - this.durationSpan.clientWidth;
+          };
         }
 
         this.adjustControlSize();
         this.controlBar.hidden = true;
 
         // Can only update the volume controls once we've computed
         // _volumeControlWidth, since the volume slider implementation
         // depends on it.
@@ -521,31 +543,35 @@
             // been hidden (we don't hide controls when the overlay is visible).
             if (this.clickToPlay.hidden && !this.isAudioOnly) {
               this.startFadeIn(this.controlBar);
               clearTimeout(this._hideControlsTimeout);
               this._hideControlsTimeout = setTimeout(this._hideControlsFn, this.HIDE_CONTROLS_TIMEOUT_MS);
             }
             break;
           case "loadedmetadata":
-            this.adjustControlSize();
             // If a <video> doesn't have any video data, treat it as <audio>
             // and show the controls (they won't fade back out)
             if (this.video instanceof HTMLVideoElement &&
                 (this.video.videoWidth == 0 || this.video.videoHeight == 0)) {
               this.isAudioOnly = true;
               this.clickToPlay.hidden = true;
               this.startFadeIn(this.controlBar);
               this.setFullscreenButtonState();
             }
-            this.showDuration(Math.round(this.video.duration * 1000));
+            this.showPosition(Math.round(this.video.currentTime * 1000), Math.round(this.video.duration * 1000));
+            if (this.positionDurationBox) {
+              this.positionDurationBox.resized = true;
+              this.durationSpan.resized = true;
+            }
             if (!this.isAudioOnly && !this.video.mozHasAudio) {
               this.muteButton.setAttribute("noAudio", "true");
               this.muteButton.setAttribute("disabled", "true");
             }
+            this.adjustControlSize();
             break;
           case "loadeddata":
             this.firstFrameShown = true;
             this.setupStatusFader();
             break;
           case "loadstart":
             this.maxCurrentTimeSeen = 0;
             this.controlsSpacer.removeAttribute("aria-label");
@@ -730,27 +756,27 @@
           return; // No error found.
         }
 
         let label = document.getAnonymousElementByAttribute(this.videocontrols, "anonid", error);
         this.controlsSpacer.setAttribute("aria-label", label.textContent);
         this.statusOverlay.setAttribute("error", error);
       },
 
-      formatTime(aTime) {
+      formatTime(aTime, showHours = false) {
         // Format the duration as "h:mm:ss" or "m:ss"
         aTime = Math.round(aTime / 1000);
         let hours = Math.floor(aTime / 3600);
         let mins  = Math.floor((aTime % 3600) / 60);
         let secs  = Math.floor(aTime % 60);
         let timeString;
         if (secs < 10) {
           secs = "0" + secs;
         }
-        if (hours) {
+        if (hours || showHours) {
           if (mins < 10) {
             mins = "0" + mins;
           }
           timeString = hours + ":" + mins + ":" + secs;
         } else {
           timeString = mins + ":" + secs;
         }
         return timeString;
@@ -803,16 +829,17 @@
           this.positionDurationBox.duration = timeString;
         }
 
         // "durationValue" property is used by scale binding to
         // generate accessible name.
         this.scrubber.durationValue = timeString;
 
         // If the duration is over an hour, thumb should show h:mm:ss instead of mm:ss
+        this.showHours = (duration >= 3600000);
 
         this.scrubber.max = duration;
         // XXX Can't set increment here, due to bug 473103. Also, doing so causes
         // snapping when dragging with the mouse, so we can't just set a value for
         // the arrow-keys.
         this.scrubber.pageIncrement = Math.round(duration / 10);
       },
 
@@ -879,17 +906,17 @@
         // by using the maximum playback position that's been seen.
         if (currentTime > this.maxCurrentTimeSeen) {
           this.maxCurrentTimeSeen = currentTime;
         }
         this.showDuration(duration);
 
         this.log("time update @ " + currentTime + "ms of " + duration + "ms");
 
-        let positionTime = this.formatTime(currentTime);
+        let positionTime = this.formatTime(currentTime, this.showHours);
 
         this.scrubber.value = currentTime;
         if (this.videocontrols.isTouchControls) {
           this.positionLabel.setAttribute("value", positionTime);
         } else {
           this.positionDurationBox.position = positionTime;
           this.updateScrubberProgress();
         }
@@ -1489,16 +1516,17 @@
 
         this.textTrackList.setAttribute("hidden", "true");
         this.setClosedCaptionButtonState();
       },
 
       onControlBarTransitioned() {
         this.textTrackList.setAttribute("hidden", "true");
         this.video.dispatchEvent(new CustomEvent("controlbarchange"));
+        this.adjustControlSize();
       },
 
       toggleClosedCaption() {
         if (this.textTrackList.hasAttribute("hidden")) {
           this.textTrackList.removeAttribute("hidden");
         } else {
           this.textTrackList.setAttribute("hidden", "true");
         }
@@ -1606,37 +1634,36 @@
         if (this.muteButton.hasAttribute("noAudio")) {
           this.volumeStack.isWanted = false;
         }
 
         let widthUsed = minControlBarPaddingWidth;
         let preventAppendControl = false;
 
         for (let control of prioritizedControls) {
+          if (!this.isControlBarHidden && control.resized && !control.hideByAdjustment) {
+            control.resizedHandler();
+            control.resized = false;
+          }
+
           if (!control.isWanted) {
             control.hideByAdjustment = true;
             continue;
           }
 
           control.hideByAdjustment = preventAppendControl ||
           widthUsed + control.minWidth > videoWidth;
 
           if (control.hideByAdjustment) {
             preventAppendControl = true;
           } else {
             widthUsed += control.minWidth;
           }
         }
 
-        if (this.durationSpan.hideByAdjustment) {
-          this.positionDurationBox.setAttribute("positionOnly", "true");
-        } else {
-          this.positionDurationBox.removeAttribute("positionOnly");
-        }
-
         if (videoHeight < this.controlBar.minHeight ||
             widthUsed === minControlBarPaddingWidth) {
           this.controlBar.setAttribute("size", "hidden");
           this.controlBar.hideByAdjustment = true;
         } else {
           this.controlBar.removeAttribute("size");
           this.controlBar.hideByAdjustment = false;
         }
--- a/toolkit/themes/shared/media/videocontrols.css
+++ b/toolkit/themes/shared/media/videocontrols.css
@@ -335,31 +335,26 @@ audio > xul|videocontrols {
 }
 
 .positionLabel,
 .durationLabel {
   display: none;
 }
 
 .positionDurationBox {
-  min-width: 9ch;
   text-align: center;
   padding-inline-start: 1px;
   padding-inline-end: 9px;
   white-space: nowrap;
   font: message-box;
   font-size: 13px;
-  font-size-adjust: 0.6;
+  font-size-adjust: 0.55;
   color: #ffffff;
 }
 
-.positionDurationBox[positionOnly] {
-  min-width: 4ch;
-}
-
 %ifdef XP_MACOSX
 .positionDurationBox {
   font-size-adjust: unset;
   font-family: "Helvetica Neue", "Helvetica", sans-serif;
 }
 %endif
 
 .duration {