Bug 1328060 - re-adjust controls when get metadata of video duration. r=jaws draft
authorRay Lin <ralin@mozilla.com>
Mon, 06 Feb 2017 10:22:37 +0800
changeset 479732 fe8c2251ae07dd0e74fd7908aec9c0b0cde40051
parent 479651 af8a2573d0f1e9cc6f2ba0ab67d7a702a197f177
child 544759 d356ea62e45978b3b0a9b645b0f3d9b590a42b0b
push id44336
push userbmo:ralin@mozilla.com
push dateTue, 07 Feb 2017 04:18:34 +0000
reviewersjaws
bugs1328060
milestone54.0a1
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 {