--- 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;
}