--- a/dom/media/webvtt/vtt.jsm
+++ b/dom/media/webvtt/vtt.jsm
@@ -1146,17 +1146,17 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
overlay.removeChild(overlay.firstChild);
}
var controlBar;
var controlBarShown;
if (controls) {
controlBar = controls.ownerDocument.getAnonymousElementByAttribute(
- controls, "class", "controlBar");
+ controls, "anonid", "controlBar");
controlBarShown = controlBar ? !!controlBar.clientHeight : false;
}
var paddedOverlay = window.document.createElement("div");
paddedOverlay.style.position = "absolute";
paddedOverlay.style.left = "0";
paddedOverlay.style.right = "0";
paddedOverlay.style.top = "0";
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -41,16 +41,17 @@
</getter>
<setter>
<![CDATA[
this.setAttribute("showhours", val);
// If the duration becomes known while we're still showing the value
// for time=0, immediately update the value to show or hide the hours.
// It's less intrusive to do it now than when the user clicks play and
// is looking right next to the thumb.
+ if (!this.timeLabel) return;
var displayedTime = this.timeLabel.getAttribute("value");
if (val && displayedTime == "0:00")
this.timeLabel.setAttribute("value", "0:00:00");
else if (!val && displayedTime == "0:00:00")
this.timeLabel.setAttribute("value", "0:00");
]]>
</setter>
</property>
@@ -126,17 +127,21 @@
switch (which) {
case "curpos":
if (this.type == "scrubber") {
// Update the time shown in the thumb.
this.thumb.setTime(newValue);
this.Utils.positionLabel.setAttribute("value", this.thumb.timeLabel.value);
// Update the value bar to match the thumb position.
let percent = newValue / this.max;
- this.valueBar.value = Math.round(percent * 10000); // has max=10000
+ if (!isNaN(percent) && percent != Infinity) {
+ this.valueBar.value = Math.round(percent * 10000); // has max=10000
+ } else {
+ this.valueBar.removeAttribute("value");
+ }
}
// The value of userChanged is true when changing the position with the mouse,
// but not when pressing an arrow key. However, the base binding sets
// ._userChanged in its keypress handlers, so we just need to check both.
if (!userChanged && !this._userChanged)
return;
this.setAttribute("value", newValue);
@@ -198,84 +203,89 @@
<binding id="videoControls">
<resources>
<stylesheet src="chrome://global/content/bindings/videocontrols.css"/>
<stylesheet src="chrome://global/skin/media/videocontrols.css"/>
</resources>
- <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- class="mediaControlsFrame">
- <stack flex="1">
- <vbox flex="1" class="statusOverlay" hidden="true">
- <box class="statusIcon"/>
- <label class="errorLabel" anonid="errorAborted">&error.aborted;</label>
- <label class="errorLabel" anonid="errorNetwork">&error.network;</label>
- <label class="errorLabel" anonid="errorDecode">&error.decode;</label>
- <label class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</label>
- <label class="errorLabel" anonid="errorNoSource">&error.noSource2;</label>
- <label class="errorLabel" anonid="errorGeneric">&error.generic;</label>
- </vbox>
+ <xbl:content xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns="http://www.w3.org/1999/xhtml" class="mediaControlsFrame">
+ <div anonid="controlsContainer" class="controlsContainer" role="none">
+ <div anonid="statusOverlay" class="statusOverlay stackItem" hidden="true">
+ <div anonid="statusIcon" class="statusIcon"></div>
+ <span class="errorLabel" anonid="errorAborted">&error.aborted;</span>
+ <span class="errorLabel" anonid="errorNetwork">&error.network;</span>
+ <span class="errorLabel" anonid="errorDecode">&error.decode;</span>
+ <span class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</span>
+ <span class="errorLabel" anonid="errorNoSource">&error.noSource2;</span>
+ <span class="errorLabel" anonid="errorGeneric">&error.generic;</span>
+ </div>
- <vbox class="controlsOverlay">
- <stack flex="1">
- <spacer class="controlsSpacer" flex="1"/>
- <box class="clickToPlay" hidden="true" flex="1"/>
- <vbox class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></vbox>
- </stack>
- <hbox class="controlBar" hidden="true">
- <button class="playButton"
+ <div anonid="controlsOverlay" class="controlsOverlay stackItem">
+ <div class="controlsSpacerStack" aria-hideen="true">
+ <div anonid="controlsSpacer" class="controlsSpacer stackItem" role="none"></div>
+ <div anonid="clickToPlay" class="clickToPlay" hidden="true"></div>
+ </div>
+ <div anonid="controlBar" class="controlBar" hidden="true">
+ <button anonid="playButton"
+ class="playButton"
playlabel="&playButton.playLabel;"
pauselabel="&playButton.pauseLabel;"/>
- <stack class="scrubberStack" flex="1">
- <box class="backgroundBar"/>
- <progressmeter class="bufferBar"/>
- <progressmeter class="progressBar" max="10000"/>
- <scale class="scrubber" movetoclick="true"/>
- </stack>
- <vbox class="durationBox">
- <label class="positionLabel" role="presentation"/>
- <label class="durationLabel" role="presentation"/>
- </vbox>
- <button class="muteButton"
+ <div anonid="scrubberStack" class="scrubberStack progressContainer" role="none">
+ <div class="progressBackgroundBar stackItem" role="none">
+ <div class="progressStack" role="none">
+ <progress anonid="bufferBar" class="bufferBar" value="0" max="100"></progress>
+ <progress anonid="progressBar" class="progressBar" value="0" max="100"></progress>
+ </div>
+ </div>
+ <input type="range" anonid="scrubber" class="scrubber"/>
+ </div>
+ <span anonid="positionLabel" class="positionLabel" role="presentation"></span>
+ <span anonid="durationLabel" class="durationLabel" role="presentation"></span>
+ <div anonid="positionDurationBox" class="positionDurationBox" role="none">
+ &positionAndDuration.nameFormat;
+ </div>
+ <div anonid="controlBarSpacer" class="controlBarSpacer" hidden="true" role="none"></div>
+ <button anonid="muteButton"
+ class="muteButton"
mutelabel="&muteButton.muteLabel;"
unmutelabel="&muteButton.unmuteLabel;"/>
- <stack class="volumeStack">
- <box class="volumeBackground"/>
- <box class="volumeForeground" anonid="volumeForeground"/>
- <scale class="volumeControl" movetoclick="true"/>
- </stack>
- <button class="closedCaptionButton"/>
- <button class="fullscreenButton"
+ <div anonid="volumeStack" class="volumeStack progressContainer" role="none">
+ <input type="range" anonid="volumeControl" class="volumeControl" min="0" max="100" step="1"/>
+ </div>
+ <button anonid="closedCaptionButton" class="closedCaptionButton"/>
+ <button anonid="fullscreenButton"
+ class="fullscreenButton"
enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
- </hbox>
- </vbox>
- </stack>
+ </div>
+ <div anonid="textTrackList" class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></div>
+ </div>
+ </div>
</xbl:content>
<implementation>
<constructor>
<![CDATA[
- this.isTouchControl = false;
+ this.isTouchControls = false;
this.randomID = 0;
this.Utils = {
debug : false,
video : null,
videocontrols : null,
controlBar : null,
playButton : null,
muteButton : null,
volumeControl : null,
durationLabel : null,
positionLabel : null,
- scrubberThumb : null,
scrubber : null,
progressBar : null,
bufferBar : null,
statusOverlay : null,
controlsSpacer : null,
clickToPlay : null,
controlsOverlay : null,
fullscreenButton : null,
@@ -288,26 +298,27 @@
"playing", "waiting", "canplay", "canplaythrough",
"seeking", "seeked", "emptied", "loadedmetadata",
"error", "suspend", "stalled",
"mozinterruptbegin", "mozinterruptend" ],
firstFrameShown : false,
timeUpdateCount : 0,
maxCurrentTimeSeen : 0,
+ isPausedByDragging: false,
_isAudioOnly : false,
get isAudioOnly() { return this._isAudioOnly; },
set isAudioOnly(val) {
this._isAudioOnly = val;
this.setFullscreenButtonState();
if (!this.isTopLevelSyntheticDocument)
return;
if (this._isAudioOnly) {
- this.video.style.height = this._controlBarHeight + "px";
+ this.video.style.height = this.controlBar.minHeight + "px";
this.video.style.width = "66%";
} else {
this.video.style.removeProperty("height");
this.video.style.removeProperty("width");
}
},
suppressError : false,
@@ -361,16 +372,17 @@
this.setFullscreenButtonState();
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.initPositionDurationBox();
this.maxCurrentTimeSeen = currentTime;
this.showPosition(currentTime, duration);
// If we have metadata, check if this is a <video> without
// video data, or a video with no audio track.
if (this.video.readyState >= this.video.HAVE_METADATA) {
if (this.video instanceof HTMLVideoElement &&
(this.video.videoWidth == 0 || this.video.videoHeight == 0))
@@ -391,41 +403,92 @@
// If the first frame hasn't loaded, kick off a throbber fade-in.
if (this.video.readyState >= this.video.HAVE_CURRENT_DATA)
this.firstFrameShown = true;
// We can't determine the exact buffering status, but do know if it's
// fully loaded. (If it's still loading, it will fire a progress event
// and we'll figure out the exact state then.)
- this.bufferBar.setAttribute("max", 100);
+ this.bufferBar.max = 100;
if (this.video.readyState >= this.video.HAVE_METADATA)
this.showBuffered();
else
- this.bufferBar.setAttribute("value", 0);
+ this.bufferBar.value = 0;
// Set the current status icon.
if (this.hasError()) {
this.clickToPlay.hidden = true;
this.statusIcon.setAttribute("type", "error");
this.updateErrorText();
this.setupStatusFader(true);
}
// An event handler for |onresize| should be added when bug 227495 is fixed.
this.controlBar.hidden = false;
- this._playButtonWidth = this.playButton.clientWidth;
- this._durationLabelWidth = this.durationLabel.clientWidth;
- this._muteButtonWidth = this.muteButton.clientWidth;
- this._volumeControlWidth = this.volumeControl.clientWidth;
- this._closedCaptionButtonWidth = this.closedCaptionButton.clientWidth;
- this._fullscreenButtonWidth = this.fullscreenButton.clientWidth;
- this._controlBarHeight = this.controlBar.clientHeight;
+
+ let layoutControls = [
+ ...this.controlBar.children,
+ this.durationSpan,
+ this.controlBar,
+ this.clickToPlay
+ ];
+
+ for (let control of layoutControls) {
+ if (!control) {
+ break;
+ }
+
+ Object.defineProperties(control, {
+ minWidth: {
+ value: control.clientWidth,
+ writable: true
+ },
+ minHeight: {
+ value: control.clientHeight,
+ writable: true
+ },
+ isAdjustableControl: {
+ value: true
+ },
+ isWanted: {
+ value: true,
+ writable: true
+ },
+ hideByAdjustment: {
+ set: (v) => {
+ if (v) {
+ control.setAttribute("hidden", "true");
+ } else {
+ control.removeAttribute("hidden");
+ }
+
+ control._isHiddenByAdjustment = v;
+ },
+ get: () => control._isHiddenByAdjustment
+ },
+ _isHiddenByAdjustment: {
+ value: false,
+ writable: true
+ }
+ });
+ }
+ // 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.adjustControlSize();
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();
},
setupNewLoadState : function() {
@@ -438,18 +501,20 @@
// so that they don't get in the way of the playing video. Otherwise we'll
// go ahead and reveal the controls now, so they're an obvious user cue.
//
// (Note: the |controls| attribute is already handled via layout/style/html.css)
var shouldShow = !this.dynamicControls ||
(this.video.paused &&
!(this.video.autoplay && this.video.mozAutoplayEnabled));
// Hide the overlay if the video time is non-zero or if an error occurred to workaround bug 718107.
- this.startFade(this.clickToPlay, shouldShow && !this.isAudioOnly &&
- this.video.currentTime == 0 && !this.hasError(), true);
+ let shouldClickToPlayShow = shouldShow && !this.isAudioOnly &&
+ this.video.currentTime == 0 && !this.hasError();
+ this.startFade(this.clickToPlay, shouldClickToPlayShow, true);
+ this.startFade(this.controlsSpacer, shouldClickToPlayShow, true);
this.startFade(this.controlBar, shouldShow, true);
},
get dynamicControls() {
// Don't fade controls for <audio> elements.
var enabled = !this.isAudioOnly;
// Allow tests to explicitly suppress the fading of controls.
@@ -459,39 +524,43 @@
// 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;
},
+ updateVolume() {
+ const volume = this.volumeControl.value;
+ this.setVolume(volume / 100);
+ },
+
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;
}
switch (aEvent.type) {
case "play":
this.setPlayButtonState(false);
this.setupStatusFader();
- if (!this._triggeredByControls && this.dynamicControls && this.videocontrols.isTouchControl)
+ if (!this._triggeredByControls && this.dynamicControls && this.videocontrols.isTouchControls)
this.startFadeOut(this.controlBar);
if (!this._triggeredByControls)
this.clickToPlay.hidden = true;
this._triggeredByControls = false;
break;
case "pause":
// Little white lie: if we've internally paused the video
// while dragging the scrubber, don't change the button state.
@@ -580,20 +649,21 @@
// statusOverlay while we wait for HAVE_ENOUGH_DATA).
// If we've seen more than 2 timeupdate events,
// the count is no longer relevant to setupStatusFader.
if (this.timeUpdateCount <= 2)
this.setupStatusFader();
// If the user is dragging the scrubber ignore the delayed seek
// responses (don't yank the thumb away from the user)
- if (this.scrubber.isDragging)
+ if (this.scrubber.isDragging || this.scrubber.startToDrag)
return;
this.showPosition(currentTime, duration);
+ this.showBuffered();
break;
case "emptied":
this.bufferBar.value = 0;
this.showPosition(0, 0);
break;
case "seeking":
this.showBuffered();
this.statusIcon.setAttribute("type", "throbber");
@@ -739,41 +809,120 @@
mins = "0" + mins;
timeString = hours + ":" + mins + ":" + secs;
} else {
timeString = mins + ":" + secs;
}
return timeString;
},
+ initPositionDurationBox : function() {
+ if (this.videocontrols.isTouchControls) {
+ return;
+ }
+
+ const positionTextNode = Array.prototype.find.call(
+ this.positionDurationBox.childNodes, (n) => !!~n.textContent.search("#1"));
+ const durationSpan = this.durationSpan;
+ const durationFormat = durationSpan.textContent;
+ const positionFormat = positionTextNode.textContent;
+
+ durationSpan.classList.add("duration");
+ durationSpan.setAttribute("role", "none");
+
+ Object.defineProperties(this.positionDurationBox, {
+ durationSpan: {
+ value: durationSpan
+ },
+ position: {
+ set: (v) => {
+ positionTextNode.textContent = positionFormat.replace("#1", v);
+ }
+ },
+ duration: {
+ set: (v) => {
+ durationSpan.textContent = v ? durationFormat.replace("#2", v) : "";
+ }
+ }
+ });
+ },
+
showDuration : function(duration) {
let isInfinite = (duration == Infinity);
this.log("Duration is " + duration + "ms.\n");
if (isNaN(duration) || isInfinite)
duration = this.maxCurrentTimeSeen;
// Format the duration as "h:mm:ss" or "m:ss"
let timeString = isInfinite ? "" : this.formatTime(duration);
- this.durationLabel.setAttribute("value", timeString);
+ if (this.videocontrols.isTouchControls) {
+ this.durationLabel.setAttribute("value", timeString);
+ } else {
+ 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.scrubberThumb.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);
},
+ pauseVideoDuringDragging: function() {
+ if (!this.video.paused &&
+ !this.isPausedByDragging &&
+ this.scrubber.isDragging) {
+ this.isPausedByDragging = true;
+ this.video.pause();
+ }
+ },
+
+ onScrubberInput: function(e) {
+ const duration = Math.round(this.video.duration * 1000); // in ms
+ let time = this.scrubber.value;
+
+ if (!this.scrubber.startToDrag || this.scrubber.isDragging) {
+ this.seekToPosition(time);
+ this.showPosition(time, duration);
+ }
+
+ this.scrubber.startToDrag = true;
+ },
+
+ onScrubberChange: function(e) {
+ this.scrubber.startToDrag = false;
+ this.scrubber.isDragging = false;
+
+ if (this.isPausedByDragging) {
+ this.video.play();
+ this.isPausedByDragging = false;
+ }
+ },
+
+ updateScrubberProgress() {
+ if (this.videocontrols.isTouchControls) {
+ return;
+ }
+
+ const positionPercent = this.scrubber.value / this.scrubber.max * 100;
+
+ if (!isNaN(positionPercent) && positionPercent != Infinity) {
+ this.progressBar.value = positionPercent;
+ } else {
+ this.progressBar.value = 0;
+ }
+ },
+
seekToPosition : function(newPosition) {
newPosition /= 1000; // convert from ms
this.log("+++ seeking to " + newPosition);
if (this.videocontrols.isGonk) {
// We use fastSeek() on B2G, and an accurate (but slower)
// seek on other platforms (that are likely to be higher
// perf).
this.video.fastSeek(newPosition);
@@ -793,18 +942,25 @@
// it, or the video is a stream), then we want to fudge the duration
// 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");
- this.positionLabel.setAttribute("value", this.formatTime(currentTime));
+ let positionTime = this.formatTime(currentTime);
+
this.scrubber.value = currentTime;
+ if (this.videocontrols.isTouchControls) {
+ this.positionLabel.setAttribute("value", positionTime);
+ } else {
+ this.positionDurationBox.position = positionTime;
+ this.updateScrubberProgress();
+ }
},
showBuffered : function() {
function bsearch(haystack, needle, cmp) {
var length = haystack.length;
var low = 0;
var high = length;
while (low < high) {
@@ -826,18 +982,19 @@
return 1;
} else if (time >= buffered.start(i)) {
return 0;
}
return -1;
}
var duration = Math.round(this.video.duration * 1000);
- if (isNaN(duration))
+ if (isNaN(duration) || duration == Infinity) {
duration = this.maxCurrentTimeSeen;
+ }
// Find the range that the current play position is in and use that
// range for bufferBar. At some point we may support multiple ranges
// displayed in the bar.
var currentTime = this.video.currentTime;
var buffered = this.video.buffered;
var index = bsearch(buffered, currentTime, bufferedCompare);
var endTime = 0;
@@ -865,18 +1022,19 @@
Utils.startFade(Utils.controlBar, false);
Utils._hideControlsTimeout = 0;
Utils._controlsHiddenByTimeout = true;
}
},
HIDE_CONTROLS_TIMEOUT_MS : 2000,
onMouseMove : function(event) {
// Pause playing video when the mouse is dragging over the control bar.
- if (this.scrubber.isDragging) {
- this.scrubber.pauseVideoDuringDragging();
+ if (this.scrubber.startToDrag) {
+ this.scrubber.isDragging = true;
+ this.pauseVideoDuringDragging();
}
// If the controls are static, don't change anything.
if (!this.dynamicControls)
return;
clearTimeout(this._hideControlsTimeout);
@@ -954,25 +1112,29 @@
},
startFade : function(element, fadeIn, immediate) {
if (element.classList.contains("controlBar") && fadeIn) {
// Bug 493523, the scrubber doesn't call valueChanged while hidden,
// so our dependent state (eg, timestamp in the thumb) will be stale.
// As a workaround, update it manually when it first becomes unhidden.
if (element.hidden)
+ if (this.videocontrols.isTouchControls) {
this.scrubber.valueChanged("curpos", this.video.currentTime * 1000, false);
+ } else {
+ this.scrubber.value = this.video.currentTime * 1000;
+ }
}
if (immediate)
element.setAttribute("immediate", true);
else
element.removeAttribute("immediate");
- if (fadeIn) {
+ if (fadeIn && !(element.isAdjustableControl && element.hideByAdjustment)) {
element.hidden = false;
// force style resolution, so that transition begins
// when we remove the attribute.
element.clientTop;
element.removeAttribute("fadeout");
if (element.classList.contains("controlBar"))
this.controlsSpacer.removeAttribute("hideCursor");
} else {
@@ -990,17 +1152,19 @@
return;
var element = event.originalTarget;
// Nothing to do when a fade *in* finishes.
if (!element.hasAttribute("fadeout"))
return;
- this.scrubber.dragStateChanged(false);
+ if (this.videocontrols.isTouchControls) {
+ this.scrubber.dragStateChanged(false);
+ }
element.hidden = true;
},
_triggeredByControls: false,
startPlay : function() {
this._triggeredByControls = true;
this.hideClickToPlay();
@@ -1097,24 +1261,27 @@
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.
let animationScale = 2;
- if (this._overlayPlayButtonHeight * animationScale > (videoHeight - this._controlBarHeight) ||
- this._overlayPlayButtonWidth * animationScale > videoWidth) {
- this.clickToPlay.setAttribute("immediate", "true");
- this.clickToPlay.hidden = true;
+ let animationMinSize = this.clickToPlay.minWidth * animationScale;
+
+ if (animationMinSize > videoWidth ||
+ animationMinSize > (videoHeight - this.controlBar.minHeight)) {
+ this.clickToPlay.setAttribute("immediate", "true");
+ this.clickToPlay.hidden = true;
} else {
this.clickToPlay.removeAttribute("immediate");
}
this.clickToPlay.setAttribute("fadeout", "true");
+ this.controlsSpacer.setAttribute("fadeout", "true");
},
setPlayButtonState : function(aPaused) {
if (aPaused)
this.playButton.setAttribute("paused", "true");
else
this.playButton.removeAttribute("paused");
@@ -1248,43 +1415,48 @@
event.preventDefault(); // Prevent page scrolling
},
isSupportedTextTrack : function(textTrack) {
return textTrack.kind == "subtitles" ||
textTrack.kind == "captions";
},
+ get isClosedCaptionAvailable() {
+ return this.overlayableTextTracks.length && !this.videocontrols.isTouchControls;
+ },
+
get overlayableTextTracks() {
return Array.prototype.filter.call(this.video.textTracks, this.isSupportedTextTrack);
},
isClosedCaptionOn : function() {
for (let tt of this.overlayableTextTracks) {
if (tt.mode === "showing") {
return true;
}
}
return false;
},
setClosedCaptionButtonState : function() {
- if (!this.overlayableTextTracks.length || this.videocontrols.isTouchControl) {
+ if (!this.isClosedCaptionAvailable) {
this.closedCaptionButton.setAttribute("hidden", "true");
return;
}
this.closedCaptionButton.removeAttribute("hidden");
if (this.isClosedCaptionOn()) {
this.closedCaptionButton.setAttribute("enabled", "true");
} else {
this.closedCaptionButton.removeAttribute("enabled");
}
+ this.adjustControlSize();
let ttItems = this.textTrackList.childNodes;
for (let tti of ttItems) {
const idx = +tti.getAttribute("index");
if (idx == this.currentTextTrackIndex) {
tti.setAttribute("on", "true");
@@ -1348,43 +1520,21 @@
},
onControlBarTransitioned : function() {
this.textTrackList.setAttribute("hidden", "true");
this.video.dispatchEvent(new CustomEvent("controlbarchange"));
},
toggleClosedCaption : function() {
- if (this.overlayableTextTracks.length === 1) {
- const lastTTIdx = this.overlayableTextTracks[0].index;
- this.changeTextTrack(this.isClosedCaptionOn() ? 0 : lastTTIdx);
- return;
- }
-
if (this.textTrackList.hasAttribute("hidden")) {
this.textTrackList.removeAttribute("hidden");
} else {
this.textTrackList.setAttribute("hidden", "true");
}
-
- let maxButtonWidth = 0;
-
- for (let tti of this.textTrackList.childNodes) {
- if (tti.clientWidth > maxButtonWidth) {
- maxButtonWidth = tti.clientWidth;
- }
- }
-
- if (maxButtonWidth > this.video.clientWidth) {
- maxButtonWidth = this.video.clientWidth;
- }
-
- for (let tti of this.textTrackList.childNodes) {
- tti.style.width = maxButtonWidth + "px";
- }
},
onTextTrackAdd : function(trackEvent) {
this.addNewTextTrack(trackEvent.track);
this.setClosedCaptionButtonState();
},
onTextTrackRemove : function(trackEvent) {
@@ -1409,16 +1559,21 @@
this.video.dispatchEvent(new CustomEvent("texttrackchange"));
}
}
this.setClosedCaptionButtonState();
},
initTextTracks : function() {
+ if (!this.isClosedCaptionAvailable) {
+ this.closedCaptionButton.setAttribute("hidden", "true");
+ return;
+ }
+
const offLabel = this.textTrackList.getAttribute("offlabel");
this.addNewTextTrack({
label: offLabel,
kind: "subtitles"
});
for (let tt of this.overlayableTextTracks) {
@@ -1446,118 +1601,156 @@
},
get isTopLevelSyntheticDocument() {
let doc = this.video.ownerDocument;
let win = doc.defaultView;
return doc.mozSyntheticDocument && win === win.top;
},
- _playButtonWidth : 0,
- _durationLabelWidth : 0,
- _muteButtonWidth : 0,
- _volumeControlWidth : 0,
- _closedCaptionButtonWidth : 0,
- _fullscreenButtonWidth : 0,
- _controlBarHeight : 0,
- _overlayPlayButtonHeight : 64,
- _overlayPlayButtonWidth : 64,
- _controlBarPaddingEnd: 8,
adjustControlSize : function adjustControlSize() {
- let doc = this.video.ownerDocument;
+ if (!this.controlBar.minWidth || this.videocontrols.isTouchControls) {
+ return;
+ }
+
+ let videoWidth = this.video.clientWidth;
+ let videoHeight = this.video.clientHeight;
+ const minControlBarPaddingWidth = 18;
+
+ if (this.video.readyState >= this.video.HAVE_METADATA) {
+ if (!this.isAudioOnly && this.video.videoWidth && this.video.videoHeight) {
+ let rect = this.video.getBoundingClientRect();
+ let widthRatio = rect.width / this.video.videoWidth;
+ let heightRatio = rect.height / this.video.videoHeight;
+ let resizedWidth = this.video.videoWidth * Math.min(widthRatio, heightRatio);
+
+ this.controlsContainer.style.width = `${resizedWidth}px`;
+ this.controlsContainer.style.left = `${(videoWidth - resizedWidth) / 2}px`;
- // The scrubber has |flex=1|, therefore |minScrubberWidth|
- // was generated by empirical testing.
- let minScrubberWidth = 25;
- let minWidthAllControls = this._playButtonWidth +
- minScrubberWidth +
- this._durationLabelWidth +
- this._muteButtonWidth +
- this._volumeControlWidth +
- this._closedCaptionButtonWidth +
- this._fullscreenButtonWidth;
+ videoWidth = resizedWidth;
+ } else {
+ this.controlsContainer.style.width = "";
+ this.controlsContainer.style.left = "";
+ }
+ }
+ // Hide and show control in order.
+ const prioritizedControls = [
+ this.playButton,
+ this.muteButton,
+ this.fullscreenButton,
+ this.closedCaptionButton,
+ this.positionDurationBox,
+ this.scrubberStack,
+ this.durationSpan,
+ this.volumeStack
+ ];
- let isFullscreenUnavailable = this.controlBar.hasAttribute("fullscreen-unavailable");
- if (isFullscreenUnavailable) {
- // When the fullscreen button is hidden we add margin-end to the volume stack.
- minWidthAllControls -= this._fullscreenButtonWidth - this._controlBarPaddingEnd;
+ if (this.controlBar.hasAttribute("fullscreen-unavailable")) {
+ this.fullscreenButton.isWanted = false;
+ }
+ if (!this.isClosedCaptionAvailable) {
+ this.closedCaptionButton.isWanted = false;
+ }
+ if (this.muteButton.hasAttribute("noAudio")) {
+ this.volumeStack.isWanted = false;
+ }
+
+ let widthUsed = minControlBarPaddingWidth;
+ let preventAppendControl = false;
+
+ for (let control of prioritizedControls) {
+ if (!control.isWanted) {
+ control.hideByAdjustment = true;
+ continue;
}
- let minHeightForControlBar = this._controlBarHeight;
- let minWidthOnlyPlayPause = this._playButtonWidth + this._muteButtonWidth;
-
- let isAudioOnly = this.isAudioOnly;
- let videoHeight = isAudioOnly ? minHeightForControlBar : this.video.clientHeight;
- let videoWidth = isAudioOnly ? minWidthAllControls : this.video.clientWidth;
+ control.hideByAdjustment = preventAppendControl ||
+ widthUsed + control.minWidth > videoWidth;
- // Adapt the size of the controls to the size of the video
- if (this.video.readyState >= this.video.HAVE_METADATA) {
- if (!this.isAudioOnly && this.video.videoWidth && this.video.videoHeight) {
- var rect = this.video.getBoundingClientRect();
- var widthRatio = rect.width / this.video.videoWidth;
- var heightRatio = rect.height / this.video.videoHeight;
- var width = this.video.videoWidth * Math.min(widthRatio, heightRatio);
+ if (control.hideByAdjustment) {
+ preventAppendControl = true;
+ } else {
+ widthUsed += control.minWidth;
+ }
+ }
+
+ if (this.durationSpan.hideByAdjustment) {
+ this.positionDurationBox.setAttribute("positionOnly", "true");
+ } else {
+ this.positionDurationBox.removeAttribute("positionOnly");
+ }
- this.controlsOverlay.setAttribute("scaled", true);
- this.controlsOverlay.style.width = width + "px";
- this.controlsSpacer.style.width = width + "px";
- this.controlBar.style.width = width + "px";
- } else {
- this.controlsOverlay.removeAttribute("scaled");
- this.controlsOverlay.style.width = "";
- this.controlsSpacer.style.width = "";
- this.controlBar.style.width = "";
- }
- }
+ 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;
+ }
+
+ // Use flexible spacer to separate controls when scrubber is hidden.
+ // As long as muteButton hidden, which means only play button presents,
+ // hide spacer and make playButton centered.
+ this.controlBarSpacer.hidden = !this.scrubberStack.hidden || this.muteButton.hidden;
- if ((this._overlayPlayButtonHeight + this._controlBarHeight) > videoHeight ||
- this._overlayPlayButtonWidth > videoWidth) {
- this.clickToPlay.hidden = true;
- } else if (this.clickToPlay.hidden &&
- !this.video.played.length &&
- this.video.paused) {
- // Check this.video.paused to handle when a video is
- // playing but hasn't processed any frames yet
- this.clickToPlay.hidden = false;
+ // Adjust clickToPlayButton size.
+ const minVideoSideLength = Math.min(videoWidth, videoHeight);
+ const clickToPlayViewRatio = 0.15;
+ const clickToPlayScaledSize = Math.max(
+ this.clickToPlay.minWidth, minVideoSideLength * clickToPlayViewRatio);
+
+ if (clickToPlayScaledSize >= videoWidth ||
+ (clickToPlayScaledSize + this.controlBar.minHeight / 2 >= videoHeight / 2 )) {
+
+ this.clickToPlay.hideByAdjustment = true;
+ } else {
+ if (this.clickToPlay.hidden && !this.video.played.length && this.video.paused) {
+ this.clickToPlay.hideByAdjustment = false;
}
-
- let size = "normal";
- if (videoHeight < minHeightForControlBar)
- size = "hidden";
- else if (videoWidth < minWidthOnlyPlayPause)
- size = "hidden";
- else if (videoWidth < minWidthAllControls)
- size = "small";
- this.controlBar.setAttribute("size", size);
+ this.clickToPlay.style.width = `${clickToPlayScaledSize}px`;
+ this.clickToPlay.style.height = `${clickToPlayScaledSize}px`;
+ }
},
init : function(binding) {
this.video = binding.parentNode;
this.videocontrols = binding;
- this.statusIcon = document.getAnonymousElementByAttribute(binding, "class", "statusIcon");
- this.controlBar = document.getAnonymousElementByAttribute(binding, "class", "controlBar");
- this.playButton = document.getAnonymousElementByAttribute(binding, "class", "playButton");
- this.muteButton = document.getAnonymousElementByAttribute(binding, "class", "muteButton");
- this.volumeControl = document.getAnonymousElementByAttribute(binding, "class", "volumeControl");
- this.progressBar = document.getAnonymousElementByAttribute(binding, "class", "progressBar");
- this.bufferBar = document.getAnonymousElementByAttribute(binding, "class", "bufferBar");
- this.scrubber = document.getAnonymousElementByAttribute(binding, "class", "scrubber");
- this.scrubberThumb = document.getAnonymousElementByAttribute(this.scrubber, "class", "scale-thumb");
- this.durationLabel = document.getAnonymousElementByAttribute(binding, "class", "durationLabel");
- this.positionLabel = document.getAnonymousElementByAttribute(binding, "class", "positionLabel");
- this.statusOverlay = document.getAnonymousElementByAttribute(binding, "class", "statusOverlay");
- this.controlsOverlay = document.getAnonymousElementByAttribute(binding, "class", "controlsOverlay");
- this.controlsSpacer = document.getAnonymousElementByAttribute(binding, "class", "controlsSpacer");
- this.clickToPlay = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay");
- this.fullscreenButton = document.getAnonymousElementByAttribute(binding, "class", "fullscreenButton");
- this.volumeForeground = document.getAnonymousElementByAttribute(binding, "anonid", "volumeForeground");
- this.closedCaptionButton = document.getAnonymousElementByAttribute(binding, "class", "closedCaptionButton");
- this.textTrackList = document.getAnonymousElementByAttribute(binding, "class", "textTrackList");
+ this.controlsContainer = document.getAnonymousElementByAttribute(binding, "anonid", "controlsContainer");
+ this.statusIcon = document.getAnonymousElementByAttribute(binding, "anonid", "statusIcon");
+ this.controlBar = document.getAnonymousElementByAttribute(binding, "anonid", "controlBar");
+ this.playButton = document.getAnonymousElementByAttribute(binding, "anonid", "playButton");
+ this.controlBarSpacer = document.getAnonymousElementByAttribute(binding, "anonid", "controlBarSpacer");
+ this.muteButton = document.getAnonymousElementByAttribute(binding, "anonid", "muteButton");
+ this.volumeStack = document.getAnonymousElementByAttribute(binding, "anonid", "volumeStack");
+ this.volumeControl = document.getAnonymousElementByAttribute(binding, "anonid", "volumeControl");
+ this.progressBar = document.getAnonymousElementByAttribute(binding, "anonid", "progressBar");
+ this.bufferBar = document.getAnonymousElementByAttribute(binding, "anonid", "bufferBar");
+ this.scrubberStack = document.getAnonymousElementByAttribute(binding, "anonid", "scrubberStack");
+ this.scrubber = document.getAnonymousElementByAttribute(binding, "anonid", "scrubber");
+ this.durationLabel = document.getAnonymousElementByAttribute(binding, "anonid", "durationLabel");
+ this.positionLabel = document.getAnonymousElementByAttribute(binding, "anonid", "positionLabel");
+ this.positionDurationBox = document.getAnonymousElementByAttribute(binding, "anonid", "positionDurationBox");
+ this.statusOverlay = document.getAnonymousElementByAttribute(binding, "anonid", "statusOverlay");
+ this.controlsOverlay = document.getAnonymousElementByAttribute(binding, "anonid", "controlsOverlay");
+ this.controlsSpacer = document.getAnonymousElementByAttribute(binding, "anonid", "controlsSpacer");
+ this.clickToPlay = document.getAnonymousElementByAttribute(binding, "anonid", "clickToPlay");
+ this.fullscreenButton = document.getAnonymousElementByAttribute(binding, "anonid", "fullscreenButton");
+ this.closedCaptionButton = document.getAnonymousElementByAttribute(binding, "anonid", "closedCaptionButton");
+ this.textTrackList = document.getAnonymousElementByAttribute(binding, "anonid", "textTrackList");
+ if (this.positionDurationBox) {
+ this.durationSpan = this.positionDurationBox.getElementsByTagName("span")[0];
+ }
+
+ // XXX controlsContainer is a desktop only element. To determine whether
+ // isTouchControls or not during the whole initialization process, get
+ // this state overridden here.
+ this.videocontrols.isTouchControls = !this.controlsContainer;
this.isAudioOnly = (this.video instanceof HTMLAudioElement);
this.setupInitialState();
this.setupNewLoadState();
this.initTextTracks();
// Use the handleEvent() callback for all media events.
// Only the "error" event listener must capture, so that it can trap error
// events from <source> children, which don't bubble. But we use capture
@@ -1574,40 +1767,47 @@
// 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, { mozSystemGroup: true });
}
- addListener(this.muteButton, "command", this.toggleMute);
- addListener(this.closedCaptionButton, "command", this.toggleClosedCaption);
+ addListener(this.muteButton, "click", this.toggleMute);
+ addListener(this.closedCaptionButton, "click", this.toggleClosedCaption);
+ addListener(this.fullscreenButton, "click", this.toggleFullscreen);
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.videocontrols, "transitionend", this.onControlBarTransitioned);
+ addListener(this.controlBar, "transitionend", this.onControlBarTransitioned);
addListener(this.video.ownerDocument, "fullscreenchange", this.onFullscreenChange);
addListener(this.video, "keypress", this.keyHandler);
- addListener(this.video.textTracks, "addtrack", this.onTextTrackAdd);
- addListener(this.video.textTracks, "removetrack", this.onTextTrackRemove);
addListener(this.videocontrols, "dragstart", function(event) {
event.preventDefault(); // prevent dragging of controls image (bug 517114)
});
+ if (!this.videocontrols.isTouchControls) {
+ addListener(this.scrubber, "input", this.onScrubberInput);
+ addListener(this.scrubber, "change", this.onScrubberChange);
+ addListener(this.volumeControl, "input", this.updateVolume);
+ addListener(this.video.textTracks, "addtrack", this.onTextTrackAdd);
+ addListener(this.video.textTracks, "removetrack", this.onTextTrackRemove);
+ }
+
this.log("--- videocontrols initialized ---");
}
};
+
this.Utils.init(this);
]]>
</constructor>
<destructor>
<![CDATA[
this.Utils.terminateEventListeners();
// randomID used to be a <field>, which meant that the XBL machinery
// undefined the property when the element was unbound. The code in
@@ -1616,89 +1816,91 @@
delete this.randomID;
]]>
</destructor>
</implementation>
<handlers>
<handler event="mouseover">
- if (!this.isTouchControl)
+ if (!this.isTouchControls)
this.Utils.onMouseInOut(event);
</handler>
<handler event="mouseout">
- if (!this.isTouchControl)
+ if (!this.isTouchControls)
this.Utils.onMouseInOut(event);
</handler>
<handler event="mousemove">
- if (!this.isTouchControl)
+ if (!this.isTouchControls)
this.Utils.onMouseMove(event);
</handler>
</handlers>
</binding>
<binding id="touchControls" extends="chrome://global/content/bindings/videocontrols.xml#videoControls">
<xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="mediaControlsFrame">
<stack flex="1">
- <vbox flex="1" class="statusOverlay" hidden="true">
- <box class="statusIcon"/>
+ <vbox anonid="statusOverlay" flex="1" class="statusOverlay" hidden="true">
+ <box anonid="statusIcon" class="statusIcon"/>
<label class="errorLabel" anonid="errorAborted">&error.aborted;</label>
<label class="errorLabel" anonid="errorNetwork">&error.network;</label>
<label class="errorLabel" anonid="errorDecode">&error.decode;</label>
<label class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</label>
<label class="errorLabel" anonid="errorNoSource">&error.noSource2;</label>
<label class="errorLabel" anonid="errorGeneric">&error.generic;</label>
</vbox>
- <vbox class="controlsOverlay">
- <spacer class="controlsSpacer" flex="1"/>
+ <vbox anonid="controlsOverlay" class="controlsOverlay">
+ <spacer anonid="controlsSpacer" class="controlsSpacer" flex="1"/>
<box flex="1" hidden="true">
- <box class="clickToPlay" hidden="true" flex="1"/>
- <vbox class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></vbox>
+ <box anonid="clickToPlay" class="clickToPlay" hidden="true" flex="1"/>
+ <vbox anonid="textTrackList" class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></vbox>
</box>
- <vbox class="controlBar" hidden="true">
+ <vbox anonid="controlBar" class="controlBar" hidden="true">
<hbox class="buttonsBar">
- <button class="playButton"
+ <button anonid="playButton"
+ class="playButton"
playlabel="&playButton.playLabel;"
pauselabel="&playButton.pauseLabel;"/>
- <label class="positionLabel" role="presentation"/>
- <stack class="scrubberStack">
+ <label anonid="positionLabel" class="positionLabel" role="presentation"/>
+ <stack anonid="scrubberStack" class="scrubberStack">
<box class="backgroundBar"/>
<progressmeter class="flexibleBar" value="100"/>
- <progressmeter class="bufferBar"/>
- <progressmeter class="progressBar" max="10000"/>
- <scale class="scrubber" movetoclick="true"/>
+ <progressmeter anonid="bufferBar" class="bufferBar"/>
+ <progressmeter anonid="progressBar" class="progressBar" max="10000"/>
+ <scale anonid="scrubber" class="scrubber" movetoclick="true"/>
</stack>
- <label class="durationLabel" role="presentation"/>
- <button class="muteButton"
+ <label anonid="durationLabel" class="durationLabel" role="presentation"/>
+ <button anonid="muteButton"
+ class="muteButton"
mutelabel="&muteButton.muteLabel;"
unmutelabel="&muteButton.unmuteLabel;"/>
- <stack class="volumeStack">
- <box class="volumeBackground"/>
- <box class="volumeForeground" anonid="volumeForeground"/>
- <scale class="volumeControl" movetoclick="true"/>
+ <stack anonid="volumeStack" class="volumeStack">
+ <box anonid="volumeBackground" class="volumeBackground"/>
+ <box anonid="volumeForeground" class="volumeForeground"/>
+ <scale anonid="volumeControl" class="volumeControl" movetoclick="true"/>
</stack>
- <button class="castingButton" hidden="true"
+ <button anonid="castingButton" class="castingButton" hidden="true"
aria-label="&castingButton.castingLabel;"/>
- <button class="closedCaptionButton" hidden="true"/>
- <button class="fullscreenButton"
+ <button anonid="closedCaptionButton" class="closedCaptionButton" hidden="true"/>
+ <button anonid="fullscreenButton"
+ class="fullscreenButton"
enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
</hbox>
</vbox>
</vbox>
</stack>
</xbl:content>
<implementation>
-
<constructor>
<![CDATA[
- this.isTouchControl = true;
+ this.isTouchControls = true;
this.TouchUtils = {
videocontrols: null,
video: null,
controlsTimer: null,
controlsTimeout: 5000,
positionLabel: null,
castingButton: null,
@@ -1823,17 +2025,17 @@
this.Utils.scrubber.addEventListener("touchstart", function() {
self.clearTimer();
}, false);
this.Utils.scrubber.addEventListener("touchend", function() {
self.delayHideControls(self.controlsTimeout);
}, false);
this.Utils.muteButton.addEventListener("click", function() { self.delayHideControls(self.controlsTimeout); }, false);
- this.castingButton = document.getAnonymousElementByAttribute(binding, "class", "castingButton");
+ this.castingButton = document.getAnonymousElementByAttribute(binding, "anonid", "castingButton");
this.castingButton.addEventListener("command", function() {
self.startCasting();
}, false);
this.video.addEventListener("media-videoCasting", function(e) {
if (!e.isTrusted)
return;
self.updateCasting(e.detail);
@@ -1853,17 +2055,18 @@
// transitioned into or out of fullscreen mode, and we don't want
// the controls to remain visible. this.controlsTimeout is a full
// 5s, which feels too long after the transition.
if (this.video.currentTime !== 0) {
this.delayHideControls(this.Utils.HIDE_CONTROLS_TIMEOUT_MS);
}
}
};
- this.TouchUtils.init(this);
+
+ this.TouchUtils.init(this)
this.dispatchEvent(new CustomEvent("VideoBindingAttached"));
]]>
</constructor>
<destructor>
<![CDATA[
// XBL destructors don't appear to be inherited properly, so we need
// to do this here in addition to the videoControls destructor. :-(
delete this.randomID;
--- a/toolkit/locales/en-US/chrome/global/videocontrols.dtd
+++ b/toolkit/locales/en-US/chrome/global/videocontrols.dtd
@@ -32,8 +32,18 @@
<!ENTITY error.generic "Video playback aborted due to an unknown error.">
<!-- LOCALIZATION NOTE (scrubberScale.nameFormat): the #1 string is the current
media position, and the #2 string is the total duration. For example, when at
the 5 minute mark in a 6 hour long video, #1 would be "5:00" and #2 would be
"6:00:00", result string would be "5:00 of 6:00:00 elapsed".
-->
<!ENTITY scrubberScale.nameFormat "#1 of #2 elapsed">
+
+<!-- LOCALIZATION NOTE (positionAndDuration.nameFormat): the #1 string is the current
+media position, and the #2 string is the total duration. For example, when at
+the 5 minute mark in a 6 hour long video, #1 would be "5:00" and #2 would be
+"6:00:00", result string would be "5:00 / 6:00:00".
+Note that #2 is not always avaiable. For example, when at the 5 minute mark in an
+unknown duration video, #1 would be "5:00" and string which is surrounded by <span>
+would be deleted, result string would be "5:00".
+-->
+<!ENTITY positionAndDuration.nameFormat "#1<span> / #2</span>">
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -59,41 +59,24 @@ toolkit.jar:
skin/classic/global/reader/RM-Content-Width-Plus-44x16.svg (../../shared/reader/RM-Content-Width-Plus-44x16.svg)
skin/classic/global/reader/RM-Line-Height-Minus-38x14.svg (../../shared/reader/RM-Line-Height-Minus-38x14.svg)
skin/classic/global/reader/RM-Line-Height-Plus-38x24.svg (../../shared/reader/RM-Line-Height-Plus-38x24.svg)
skin/classic/global/media/TopLevelImageDocument.css (../../shared/media/TopLevelImageDocument.css)
skin/classic/global/media/TopLevelVideoDocument.css (../../shared/media/TopLevelVideoDocument.css)
skin/classic/global/media/imagedoc-lightnoise.png (../../shared/media/imagedoc-lightnoise.png)
skin/classic/global/media/imagedoc-darknoise.png (../../shared/media/imagedoc-darknoise.png)
* skin/classic/global/media/videocontrols.css (../../shared/media/videocontrols.css)
- skin/classic/global/media/pauseButton.png (../../shared/media/pauseButton.png)
- skin/classic/global/media/pauseButton@2x.png (../../shared/media/pauseButton@2x.png)
- skin/classic/global/media/playButton.png (../../shared/media/playButton.png)
- skin/classic/global/media/playButton@2x.png (../../shared/media/playButton@2x.png)
- skin/classic/global/media/muteButton.png (../../shared/media/muteButton.png)
- skin/classic/global/media/muteButton@2x.png (../../shared/media/muteButton@2x.png)
- skin/classic/global/media/unmuteButton.png (../../shared/media/unmuteButton.png)
- skin/classic/global/media/unmuteButton@2x.png (../../shared/media/unmuteButton@2x.png)
- skin/classic/global/media/noAudio.png (../../shared/media/noAudio.png)
- skin/classic/global/media/noAudio@2x.png (../../shared/media/noAudio@2x.png)
- skin/classic/global/media/closeCaptionButton.png (../../shared/media/closeCaptionButton.png)
- skin/classic/global/media/closeCaptionButton@2x.png (../../shared/media/closeCaptionButton@2x.png)
- skin/classic/global/media/fullscreenButton.png (../../shared/media/fullscreenButton.png)
- skin/classic/global/media/fullscreenButton@2x.png (../../shared/media/fullscreenButton@2x.png)
- skin/classic/global/media/scrubberThumb.png (../../shared/media/scrubberThumb.png)
- skin/classic/global/media/scrubberThumb@2x.png (../../shared/media/scrubberThumb@2x.png)
- skin/classic/global/media/scrubberThumbWide.png (../../shared/media/scrubberThumbWide.png)
- skin/classic/global/media/scrubberThumbWide@2x.png (../../shared/media/scrubberThumbWide@2x.png)
+ skin/classic/global/media/pauseButton.svg (../../shared/media/pauseButton.svg)
+ skin/classic/global/media/playButton.svg (../../shared/media/playButton.svg)
+ skin/classic/global/media/muteButton.svg (../../shared/media/muteButton.svg)
+ skin/classic/global/media/closedCaptionButton.svg (../../shared/media/closedCaptionButton.svg)
+ skin/classic/global/media/fullscreenButton.svg (../../shared/media/fullscreenButton.svg)
skin/classic/global/media/error.png (../../shared/media/error.png)
skin/classic/global/media/throbber.png (../../shared/media/throbber.png)
skin/classic/global/media/stalled.png (../../shared/media/stalled.png)
- skin/classic/global/media/volume-empty.png (../../shared/media/volume-empty.png)
- skin/classic/global/media/volume-empty@2x.png (../../shared/media/volume-empty@2x.png)
- skin/classic/global/media/volume-full.png (../../shared/media/volume-full.png)
- skin/classic/global/media/volume-full@2x.png (../../shared/media/volume-full@2x.png)
skin/classic/global/media/clicktoplay-bgtexture.png (../../shared/media/clicktoplay-bgtexture.png)
skin/classic/global/media/videoClickToPlayButton.svg (../../shared/media/videoClickToPlayButton.svg)
#ifdef MOZ_PLACES
skin/classic/mozapps/places/defaultFavicon.png (../../shared/places/defaultFavicon.png)
skin/classic/mozapps/places/defaultFavicon@2x.png (../../shared/places/defaultFavicon@2x.png)
skin/classic/mozapps/places/defaultFavicon-inverted.png (../../shared/places/defaultFavicon-inverted.png)
skin/classic/mozapps/places/defaultFavicon-inverted@2x.png (../../shared/places/defaultFavicon-inverted@2x.png)
#endif
--- a/toolkit/themes/shared/media/TopLevelVideoDocument.css
+++ b/toolkit/themes/shared/media/TopLevelVideoDocument.css
@@ -3,10 +3,11 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
body {
background-image: url("chrome://global/skin/media/imagedoc-darknoise.png");
background-color: rgb(33,33,33); /* Average color of that ^ image. */
}
video {
- box-shadow: 0 0 15px #000;
+ border: 1px #000000 solid;
+ box-shadow: 0 0 5px rgba(0,0,0,0.6);
}
deleted file mode 100644
index 469310fb1b21ed705925decd18710dd0df37d915..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 03350789221b94d830ebc4dafb1b6e4447f79f71..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/closedCaptionButton.svg
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #48a0f7;
+ }
+ use[id$="-active"] {
+ fill: #2d89e6;
+ }
+ use[id$="-focus"] {
+ fill: #48a0f7;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+ <symbol id="cc-off-shape">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M16.531,16.107H5.267l1.982-2H15c0.6,0,1-0.4,1-1V5.274
+ l1.946-1.964C17.963,3.399,18,3.483,18,3.576v11.031C18,15.407,17.331,16.107,16.531,16.107z M14.016,8.506h-1.218l1.005-1.014
+ C13.913,7.789,13.984,8.128,14.016,8.506z M11.786,12.361c-0.828,0-1.476-0.326-1.913-0.902l1.09-1.101
+ c0.136,0.323,0.374,0.541,0.796,0.541c0.514,0,0.695-0.44,0.756-1.014h1.535C13.908,11.43,13.071,12.361,11.786,12.361z
+ M1.496,16.106C0.697,16.104,0,15.406,0,14.607V3.576c0-0.8,0.7-1.5,1.5-1.5h12.846L16.299,0l1.316,1.283L2.615,17.13L1.496,16.106
+ z M3,4.107c-0.6,0-1,0.4-1,1v8c0,0.6,0.4,1,1,1h0.029l2.031-2.16c-0.757-0.503-1.191-1.457-1.191-2.744
+ c0-1.936,1.069-3.14,2.428-3.14c1.357,0,2.136,0.76,2.361,2.059l3.777-4.016H3z M8.298,8.506H7.355
+ c-0.047-0.623-0.49-1.23-0.99-1.23c-0.561,0-1.337,0.84-1.337,1.995c0,0.674,0.381,1.427,0.95,1.702L8.298,8.506z"/>
+ </symbol>
+
+ <symbol id="cc-shape">
+ <path d="M16.531,1.984H1.5c-0.8,0-1.5,0.7-1.5,1.5v11.031c0,0.8,0.7,1.5,1.5,1.5h15.031
+ c0.8,0,1.469-0.7,1.469-1.5V3.484C18,2.684,17.331,1.984,16.531,1.984z M16,13.016c0,0.6-0.4,1-1,1H3c-0.6,0-1-0.4-1-1v-8
+ c0-0.6,0.4-1,1-1h12c0.6,0,1,0.4,1,1V13.016z M6.426,10.807c-0.811,0-0.96-0.789-0.96-1.628c0-1.155,0.338-1.745,0.899-1.745
+ c0.5,0,0.818,0.357,0.866,0.98h1.484C8.585,6.877,7.785,5.972,6.297,5.972c-1.359,0-2.428,1.205-2.428,3.14
+ c0,1.944,0.974,3.157,2.583,3.157c1.285,0,2.153-0.93,2.295-2.476H7.244C7.183,10.367,6.94,10.807,6.426,10.807z M11.759,10.807
+ c-0.811,0-0.96-0.789-0.96-1.628c0-1.155,0.338-1.745,0.899-1.745c0.5,0,0.756,0.357,0.803,0.98h1.515
+ c-0.129-1.537-0.898-2.443-2.385-2.443c-1.359,0-2.396,1.205-2.396,3.14c0,1.944,0.943,3.157,2.552,3.157
+ c1.285,0,2.122-0.93,2.264-2.476h-1.535C12.454,10.367,12.273,10.807,11.759,10.807z"/>
+ </symbol>
+ <use id="cc" xlink:href="#cc-shape"/>
+ <use id="cc-hover" xlink:href="#cc-shape"/>
+ <use id="cc-active" xlink:href="#cc-shape"/>
+ <use id="cc-focus" xlink:href="#cc-shape"/>
+ <use id="cc-disabled" xlink:href="#cc-shape"/>
+
+ <use id="cc-off" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-hover" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-active" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-focus" xlink:href="#cc-off-shape"/>
+ <use id="cc-off-disabled" xlink:href="#cc-off-shape"/>
+</svg>
index 58e37283a73eff086420d0969544fa8bc5dd7924..362a293f223fe1c29751b711de8c48de950ebd49
GIT binary patch
literal 20345
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
D)qq~X
deleted file mode 100644
index ffbc3d5ae4627d9beb7c4e8c6ef80542e7e1414c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/fullscreenButton.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #48a0f7;
+ }
+ use[id$="-active"] {
+ fill: #2d89e6;
+ }
+ use[id$="-focus"] {
+ fill: #48a0f7;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+ <symbol id="fullscreen-shape">
+ <path d="M6.728,10.188l-3.235,3.094l0.017-2.267l-1.513-0.016l0,5l4.987-0.008l0.011-1.537l-2.281-0.022
+ l3.097-3.158L6.728,10.188z M14.453,11.004l-0.022,2.281l-3.158-3.097l-1.086,1.083l3.094,3.235l-2.267-0.017l-0.016,1.514l5,0
+ l-0.008-4.988L14.453,11.004z M11.015,2.01l-0.011,1.537l2.281,0.022l-3.097,3.158l1.083,1.086l3.235-3.094L14.49,6.986
+ l1.513,0.016v-5L11.015,2.01z M6.986,3.511l0.016-1.514l-5,0L2.01,6.985l1.537,0.011l0.022-2.281l3.158,3.097l1.086-1.083
+ L4.718,3.494L6.986,3.511z"/>
+ </symbol>
+ <symbol id="unfullscreen-shape">
+ <path d="M2.047,11.135l-0.011,1.537l2.281,0.022L1.22,15.851l1.083,1.086l3.235-3.094l-0.017,2.268l1.513,0.016
+ l0-5L2.047,11.135z M13.781,12.587l2.267,0.017l0.016-1.514l-5,0l0.008,4.988l1.537,0.011l0.022-2.281l3.158,3.097l1.086-1.083
+ L13.781,12.587z M16.058,5.578l-2.281-0.021l3.097-3.158l-1.083-1.086l-3.235,3.094l0.017-2.267L11.06,2.123v5l4.988-0.008
+ L16.058,5.578z M5.516,2.098L5.494,4.379L2.336,1.283L1.25,2.365L4.344,5.6L2.077,5.583L2.06,7.097l5,0L7.053,2.109L5.516,2.098z"/>
+ </symbol>
+ <use id="fullscreen" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-hover" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-active" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-focus" xlink:href="#fullscreen-shape"/>
+ <use id="fullscreen-disabled" xlink:href="#fullscreen-shape"/>
+
+ <use id="unfullscreen" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-hover" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-active" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-focus" xlink:href="#unfullscreen-shape"/>
+ <use id="unfullscreen-disabled" xlink:href="#unfullscreen-shape"/>
+</svg>
deleted file mode 100644
index b09ebbd43ce8dd62741992a0d00df1018897d164..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 894480761d0742015ffcfcc2d28bc10e88cbe4a9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/muteButton.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #48a0f7;
+ }
+ use[id$="-active"] {
+ fill: #2d89e6;
+ }
+ use[id$="-focus"] {
+ fill: #48a0f7;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+ <symbol id="unmute-shape">
+ <path d="M3.52,5.367c-1.332,0-2.422,1.09-2.422,2.422v2.422c0,1.332,1.09,2.422,2.422,2.422h1.516l4.102,3.633
+ V1.735L5.035,5.367H3.52z M12.059,9c0-0.727-0.484-1.211-1.211-1.211v2.422C11.574,10.211,12.059,9.727,12.059,9z M14.48,9
+ c0-1.695-1.211-3.148-2.785-3.512l-0.363,1.09C12.422,6.82,13.27,7.789,13.27,9c0,1.211-0.848,2.18-1.938,2.422l0.484,1.09
+ C13.27,12.148,14.48,10.695,14.48,9z M12.543,3.188l-0.484,1.09C14.238,4.883,15.691,6.82,15.691,9c0,2.18-1.453,4.117-3.512,4.601
+ l0.484,1.09c2.422-0.605,4.238-2.906,4.238-5.691C16.902,6.215,15.086,3.914,12.543,3.188z"/>
+ </symbol>
+ <symbol id="mute-shape">
+ <path d="M3.52,5.367c-1.332,0-2.422,1.09-2.422,2.422v2.422c0,1.332,1.09,2.422,2.422,2.422h1.516l4.102,3.633
+ V1.735L5.035,5.367H3.52z"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12.155,12.066l-1.138-1.138l4.872-4.872l1.138,1.138
+ L12.155,12.066z"/>
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M10.998,7.204l1.138-1.138l4.872,4.872l-1.138,1.138L10.998,7.204
+ z"/>
+ </symbol>
+ <symbol id="noaudio-shape">
+ <path d="M14.901,3.571l-4.412,3.422V1.919L6.286,5.46H4.869c-1.298,0-2.36,1.062-2.36,2.36v2.36
+ c0,1.062,0.708,1.888,1.652,2.242l-2.242,1.77l1.18,1.416L16.081,4.987L14.901,3.571z M10.489,16.081V11.36l-2.669,2.36
+ L10.489,16.081z"/>
+ </symbol>
+ <use id="unmute" xlink:href="#unmute-shape"/>
+ <use id="unmute-hover" xlink:href="#unmute-shape"/>
+ <use id="unmute-active" xlink:href="#unmute-shape"/>
+ <use id="unmute-focus" xlink:href="#unmute-shape"/>
+ <use id="unmute-disabled" xlink:href="#unmute-shape"/>
+
+ <use id="mute" xlink:href="#mute-shape"/>
+ <use id="mute-hover" xlink:href="#mute-shape"/>
+ <use id="mute-active" xlink:href="#mute-shape"/>
+ <use id="mute-focus" xlink:href="#mute-shape"/>
+ <use id="mute-disabled" xlink:href="#mute-shape"/>
+
+ <use id="noaudio" xlink:href="#noaudio-shape"/>
+</svg>
deleted file mode 100644
index b2cd21c5ebc271dbe97d72207827cb48e0b0fa05..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 3db8c973b6594cb8820661564eb8dacb673c09b9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 7de728b2d7a17a75cc0e5b44da308e7a9e8db013..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 944098ca19a4f63331ae852606c3907285e7e203..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/pauseButton.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #48a0f7;
+ }
+ use[id$="-active"] {
+ fill: #2d89e6;
+ }
+ use[id$="-focus"] {
+ fill: #48a0f7;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ </style>
+
+
+ <symbol id="pause-shape">
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M6.002,1.953C5.172,1.953,4.5,2.626,4.5,3.455v11.08
+ c0,0.83,0.672,1.502,1.502,1.502c0.829,0,1.502-0.672,1.502-1.502V3.455C7.504,2.626,6.831,1.953,6.002,1.953z M12,1.953
+ c-0.828,0-1.5,0.672-1.5,1.5v11.094c0,0.828,0.672,1.5,1.5,1.5s1.5-0.672,1.5-1.5V3.453C13.5,2.625,12.828,1.953,12,1.953z"/>
+ </symbol>
+
+ <use id="pause" xlink:href="#pause-shape"/>
+ <use id="pause-hover" xlink:href="#pause-shape"/>
+ <use id="pause-active" xlink:href="#pause-shape"/>
+ <use id="pause-focus" xlink:href="#pause-shape"/>
+ <use id="pause-disalbed" xlink:href="#pause-shape"/>
+</svg>
deleted file mode 100644
index df22919419082d251064811ac730eea234008ad6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 11e2731df64f9dd00623f263150d8a651ee1b1bc..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/playButton.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: #ffffff;
+ }
+ use[id$="-hover"] {
+ fill: #48a0f7;
+ }
+ use[id$="-active"] {
+ fill: #2d89e6;
+ }
+ use[id$="-focus"] {
+ fill: #48a0f7;
+ }
+ use[id$="-disabled"] {
+ fill: #ffffff;
+ }
+ use[id$="-clicktoplay"] {
+ fill: #000000;
+ }
+ </style>
+
+ <symbol id="play-shape">
+ <path d="M3.243,15.155c0,0.845,0.593,1.157,1.317,0.707l9.659-6.041c0.727-0.453,0.722-1.193,0-1.645L4.556,2.137
+ C3.827,1.682,3.237,2.014,3.237,2.844v12.312H3.243z"/>
+ </symbol>
+
+ <use id="play" xlink:href="#play-shape"/>
+ <use id="play-hover" xlink:href="#play-shape"/>
+ <use id="play-active" xlink:href="#play-shape"/>
+ <use id="play-focus" xlink:href="#play-shape"/>
+ <use id="play-clicktoplay" xlink:href="#play-shape"/>
+</svg>
deleted file mode 100644
index fb20075b24af20c17d2ab0f301e04b74c0fbdb50..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f159627631c536f0def829f45f3715f198db1a5e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f8790f46724c449f053f490ff840f279da03964b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 96d2ed75140584a92940e65b4f5d1f7a60a133c3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 52c9d72727e8d6c263c288454003b06cb2e819b9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5b76e2fa45d8481ca0a81e524c4a20508adfc284..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 86f21859eee91db51bab2e89ce0b05f2378f2dd3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/shared/media/videocontrols.css
+++ b/toolkit/themes/shared/media/videocontrols.css
@@ -1,440 +1,478 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-@namespace html url("http://www.w3.org/1999/xhtml");
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace url("http://www.w3.org/1999/xhtml");
+
+video > xul|videocontrols,
+audio > xul|videocontrols {
+ writing-mode: horizontal-tb;
+ width: 100%;
+ height: 100%;
+ display: inline-block;
+}
+
+.controlsContainer [hidden="true"],
+.controlBar[hidden] {
+ display: none;
+}
+
+.controlBar[size="hidden"] {
+ display: none;
+}
+
+.controlsContainer,
+.progressContainer {
+ position: relative;
+ height: 100%;
+}
+
+.stackItem {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.statusOverlay {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: rgb(80,80,80);
+}
+
+.controlsOverlay {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ position: relative;
+}
+
+.controlsSpacerStack {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ justify-content: center;
+ align-items: center;
+}
+
+.controlsSpacer {
+ background-color: rgba(255,255,255,.4);
+}
.controlBar {
- height: 28px;
- background-color: rgba(35,31,32,.74);
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ height: 40px;
+ padding: 0 9px;
+ background-color: rgba(26,26,26,.8);
}
.playButton,
.muteButton,
.closedCaptionButton,
.fullscreenButton {
+ height: 100%;
+ min-height: 30px;
+ min-width: 30px;
+ padding: 6px;
+ border: 0;
+ margin: 0;
background-color: transparent;
background-repeat: no-repeat;
background-position: center;
- -moz-appearance: none; /* Remove the native button appearance and styling */
- margin: 0;
- padding: 0;
- min-height: 28px;
- min-width: 28px;
- border: none;
- opacity: 0.7;
-}
-
-.playButton:hover,
-.muteButton:hover,
-.closedCaptionButton:hover,
-.fullscreenButton:hover {
- opacity: 1;
-}
-
-.playButton:hover:active,
-.muteButton:hover:active,
-.closedCaptionButton:hover:active,
-.fullscreenButton:hover:active {
- opacity: 0.4;
+ background-origin: content-box;
+ background-clip: content-box;
}
.playButton {
- background-image: url(chrome://global/skin/media/pauseButton.png);
- margin-right: -22px; /* 1/2 of scrubber thumb width, for overhang. */
- position: relative; /* Trick to work around negative margin interfering with clicking on the button. */
+ background-image: url(chrome://global/skin/media/pauseButton.svg#pause);
+}
+.playButton:hover {
+ background-image: url(chrome://global/skin/media/pauseButton.svg#pause-hover);
+}
+.playButton:hover:active {
+ background-image: url(chrome://global/skin/media/pauseButton.svg#pause-active);
}
-
.playButton[paused] {
- background-image: url(chrome://global/skin/media/playButton.png);
+ background-image: url(chrome://global/skin/media/playButton.svg#play);
+}
+.playButton[paused]:hover {
+ background-image: url(chrome://global/skin/media/playButton.svg#play-hover);
+}
+.playButton[paused]:hover:active {
+ background-image: url(chrome://global/skin/media/playButton.svg#play-active);
}
.muteButton {
- background-image: url(chrome://global/skin/media/muteButton.png);
- min-width: 33px;
+ background-image: url(chrome://global/skin/media/muteButton.svg#unmute);
+}
+.muteButton:hover {
+ background-image: url(chrome://global/skin/media/muteButton.svg#unmute-hover);
+}
+.muteButton:hover:active {
+ background-image: url(chrome://global/skin/media/muteButton.svg#unmute-active);
}
.muteButton[muted] {
- background-image: url(chrome://global/skin/media/unmuteButton.png);
+ background-image: url(chrome://global/skin/media/muteButton.svg#mute);
+}
+.muteButton[muted]:hover {
+ background-image: url(chrome://global/skin/media/muteButton.svg#mute-hover);
}
-
-.muteButton[noAudio] {
- background-image: url(chrome://global/skin/media/noAudio.png);
+.muteButton[muted]:hover:active {
+ background-image: url(chrome://global/skin/media/muteButton.svg#mute-active);
}
-
+.muteButton[noAudio],
+.muteButton[noAudio]:hover,
+.muteButton[noAudio]:hover:active {
+ background-image: url(chrome://global/skin/media/muteButton.svg#noaudio);
+}
.muteButton[noAudio] + .volumeStack {
display: none;
}
.closedCaptionButton {
- background-image: url(chrome://global/skin/media/closeCaptionButton.png);
- background-position: 4px;
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off);
}
-
+.closedCaptionButton:hover {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off-hover);
+}
+.closedCaptionButton:hover:active {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off-active);
+}
.closedCaptionButton[enabled] {
- opacity: 1;
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc);
}
-
-.closedCaptionButton[hidden] {
- display: none;
+.closedCaptionButton[enabled]:hover {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-hover);
+}
+.closedCaptionButton[enabled]:hover:active {
+ background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-active);
}
.fullscreenButton {
- background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 16, 16, 0);
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen);
+}
+.fullscreenButton:hover {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen-hover);
+}
+.fullscreenButton:hover:active {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen-active);
+}
+.fullscreenButton[fullscreened] {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen);
+}
+.fullscreenButton[fullscreened]:hover {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen-hover);
+}
+.fullscreenButton[fullscreened]:hover:active {
+ background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen-active);
+}
+
+.controlBarSpacer {
+ flex-grow: 1;
+}
+
+.volumeControl::-moz-range-thumb,
+.scrubber::-moz-range-thumb {
+ height: 13px;
+ width: 13px;
+ border: none;
+ border-radius: 50%;
+ background-color: #ffffff;
+ filter: drop-shadow(0px 0px 5px rgba(0,0,0,0.65));
}
-.fullscreenButton[fullscreened] {
- background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 32, 16, 16);
+.volumeControl::-moz-focus-outer,
+.scrubber::-moz-focus-outer {
+ border: 0;
+}
+
+.progressBackgroundBar {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
}
-.volumeControl {
- width: 32px;
- opacity: 0;
+.progressStack {
+ position: relative;
+ width: 100%;
+ height: 5px;
+}
+
+.scrubberStack {
+ min-width: 48px;
+ flex-basis: 48px;
+ flex-grow: 2;
+ flex-shrink: 0;
+ margin: 0 9px;
+}
+
+.volumeStack {
+ max-width: 60px;
+ min-width: 48px;
+ flex-grow: 1;
+ flex-shrink: 0;
+ margin-right: 6px;
+ margin-left: 4px;
}
+.bufferBar,
+.progressBar,
+.scrubber,
.volumeBackground,
-.volumeForeground {
- background-repeat: no-repeat;
- background-position: center;
- width: 32px;
+.volumeControl {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ border: 0;
+ border-radius: 2.5px;
+ margin: 0;
+ background: none;
+ background-color: transparent;
+}
+
+.bufferBar,
+.volumeBackground {
+ background-color: rgba(0,0,0,0.7);
+}
+
+.bufferBar::-moz-progress-bar,
+.progressBar::-moz-progress-bar,
+.volumeBackground::-moz-meter-bar {
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ border-radius: 2.5px;
+ background: none;
}
-.volumeBackground {
- background-image: url(chrome://global/skin/media/volume-empty.png);
+.scrubber:hover::-moz-range-thumb,
+.volumeControl:hover::-moz-range-thumb {
+ background-color: #48a0f7;
+}
+
+.scrubber:active::-moz-range-thumb,
+.volumeControl:active::-moz-range-thumb {
+ background-color: #2d89e6;
+}
+
+.scrubber::-moz-range-track,
+.scrubber::-moz-range-progress {
+ background-color: transparent;
}
-.volumeForeground {
- background-image: url(chrome://global/skin/media/volume-full.png);
- background-clip: content-box;
+.volumeControl::-moz-range-progress,
+.volumeControl::-moz-range-track {
+ height: 5px;
+ border-radius: 2.5px;
+}
+
+.volumeControl::-moz-range-progress {
+ background-color: #ffffff;
+}
+
+.volumeControl::-moz-range-track {
+ background-color: rgba(0,0,0,0.7);
+}
+
+
+.bufferBar::-moz-progress-bar {
+ background-color: rgba(255,255,255,0.3);
+ border-radius: 2.5px;
+}
+
+.progressBar::-moz-progress-bar {
+ background-color: #00b6f0;
}
.textTrackList {
- display: -moz-box;
- -moz-appearance: none;
- -moz-box-pack: end;
- -moz-box-align: end;
- padding: 0;
+ position: absolute;
+ right: 5px;
+ bottom: 45px;
+ max-width: 80%;
+ border: 1px solid #000000;
+ border-radius: 2.5px;
+ padding: 5px 0;
+ vertical-align: middle;
+ font-size: 12px;
+ background-color: #000000;
+ opacity: 0.7;
}
-.textTrackList[hidden] {
+.textTrackList > .textTrackItem {
+ display: block;
+ width: 100%;
+ height: 30px;
+ padding: 2px 10px;
+ border: none;
+ margin: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-align: left;
+ text-overflow: ellipsis;
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.textTrackList > .textTrackItem:hover {
+ background-color: #444444;
+}
+
+.textTrackList > .textTrackItem[on] {
+ color: #00b6f0;
+}
+
+.positionLabel,
+.durationLabel {
display: none;
}
-.textTrackList > html|*.textTrackItem {
- -moz-appearance: none;
- -moz-box-align: start;
- text-align: start;
- overflow: hidden;
- margin: 0;
- padding: 2px 10px;
- -moz-margin-end: 10px;
- border: none;
- color: rgba(255,255,255,.5);
- background-color: rgba(35,31,32,.74);
+.positionDurationBox {
+ min-width: 9ch;
+ text-align: center;
+ padding-inline-start: 1px;
+ padding-inline-end: 9px;
white-space: nowrap;
-}
-
-.textTrackList > html|*.textTrackItem[on] {
- color: white;
- background-color: black;
+ font: message-box;
+ font-size: 13px;
+ font-size-adjust: 0.6;
+ color: #ffffff;
}
-.textTrackList > html|*.textTrackItem:hover {
- background-color: rgba(0,0,0,.55);
-}
-
-.controlBar[fullscreen-unavailable] {
- /* This value is duplicated in the videocontrols.xml adjustControlSize function. */
- padding-inline-end: 8px;
-}
-
-.volumeControl .scale-thumb {
- min-width: 0;
- opacity: 0;
-}
-
-.durationBox {
- -moz-box-pack: center;
-}
-
-.durationLabel {
- margin-left: -22px; /* 1/2 of scrubber thumb width, for overhang. */
- padding-left: 8px; /* don't bump into the scrubber bar */
- color: rgba(255,255,255,.75);
- font: message-box;
- font-size: 11px;
+.positionDurationBox[positionOnly] {
+ min-width: 4ch;
}
%ifdef XP_MACOSX
-.durationLabel {
- padding-top: 2px; /* center vertically with scrubber bar */
-}
-%else
-.durationLabel {
- padding-top: 0; /* center vertically with scrubber bar */
+.positionDurationBox {
+ font-size-adjust: unset;
}
%endif
-.positionLabel {
- display: none;
-}
-
-.backgroundBar {
- /* margin top/bottom: make bar 8px tall (control height = 28, minus 2 * 10 margin) */
- /* margin left/right: 1/2 of scrubber thumb width, for overhang. */
- margin: 10px 22px;
- background-color: rgba(255,255,255,.5);
- border-radius: 2.5px;
-}
-
-.bufferBar,
-.progressBar {
- /* margin top/bottom: make bar 8px tall (control height = 28, minus 2 * 10 margin) */
- /* margin left/right: 1/2 of scrubber thumb width, for overhang. */
- margin: 10px 22px;
- -moz-appearance: none;
- border: none;
- background-color: transparent;
- min-width: 0;
- min-height: 0;
-}
-
-/* .progress-bar is an element inside the <progressmeter> implementation. */
-.bufferBar .progress-bar {
- /*
- * Note that this is drawn on top of the .backgroundBar. So although this
- * has the same background-color specified, the semitransparent
- * compositing gives it a different visual appearance.
- */
- background-color: rgba(255,255,255,.5);
- border-radius: 2.5px;
- -moz-appearance: none;
-}
-
-.progressBar .progress-bar {
- background-color: white;
- border-radius: 2.5px;
- -moz-appearance: none;
-}
-
-/* .scale-slider is an element inside the <scale> implementation. */
-.scrubber .scale-slider,
-.volumeControl .scale-slider {
- /* Hide the default horizontal bar. */
- -moz-appearance: none;
- background: none;
- margin: 0;
-}
-
-.scrubber .scale-slider {
- /* abs(margin-top) + margin-bottom + bar height == timeThumb height */
- margin-top: -10px;
- margin-bottom: 10px;
-}
-/* .scale-thumb is an element inside the <scale> implementation. */
-.scrubber .scale-thumb,
-.volumeControl .scale-thumb {
- /* Override the default thumb appearance with a custom image. */
- -moz-appearance: none;
- background: transparent;
- border: none;
-}
-
-.timeThumb {
- background: url(chrome://global/skin/media/scrubberThumb.png) no-repeat center;
- min-width: 45px;
- min-height: 28px;
- -moz-box-pack: center;
-}
-
-.timeThumb[showhours="true"] {
- background-image: url(chrome://global/skin/media/scrubberThumbWide.png);
-}
-
-.timeLabel {
- color: rgba(255,255,255,.75);
- font: message-box;
- font-size: 10px;
- text-shadow: rgba(0,0,0,.3) 0 1px;
- padding-top: 7px;
-}
-
-%ifdef XP_MACOSX
-.timeLabel {
- padding-top: 7px; /* center vertically with scrubber bar */
-}
-%else
-.timeLabel {
- padding-top: 5px; /* center vertically with scrubber bar */
-}
-%endif
-
-.statusOverlay {
- -moz-box-align: center;
- -moz-box-pack: center;
- background-color: rgba(0,0,0,.55);
+.duration {
+ display: inline-block;
+ white-space: pre;
+ color: #929292;
}
.statusIcon {
- margin-bottom: 28px; /* same height as .controlBar, to keep icon centered above it */
width: 36px;
height: 36px;
+ margin-bottom: 20px;
}
.statusIcon[type="throbber"] {
background: url(chrome://global/skin/media/throbber.png) no-repeat center;
}
.statusIcon[type="throbber"][stalled] {
background: url(chrome://global/skin/media/stalled.png) no-repeat center;
}
.statusIcon[type="error"] {
+ min-width: 70px;
+ min-height: 60px;
background: url(chrome://global/skin/media/error.png) no-repeat center;
+ background-size: contain;
}
/* Overlay Play button */
.clickToPlay {
- width: 64px;
- height: 64px;
- -moz-box-pack: center;
- -moz-box-align: center;
- opacity: 0.7;
- background-image: url(chrome://global/skin/media/clicktoplay-bgtexture.png),
- url(chrome://global/skin/media/videoClickToPlayButton.svg);
- background-repeat: repeat, no-repeat;
- background-position: center, center;
- background-size: auto, 64px 64px;
- background-color: hsla(0,0%,10%,.5);
-}
-.clickToPlay:hover {
- opacity: 1;
+ min-width: 48px;
+ min-height: 48px;
+ border-radius: 50%;
+ background-image: url(chrome://global/skin/media/playButton.svg#play);
+ background-repeat: no-repeat;
+ background-position: 54% 50%;
+ background-size: 40% 40%;
+ background-color: #1a1a1a;
+ opacity: 0.8;
+ position: relative;
+ top: 20px;
}
-/* Statistics formatting */
-html|*.statsDiv {
- position: relative;
-}
-html|td {
- height: 1em;
- max-height: 1em;
- padding: 0 2px;
+.controlsSpacerStack:hover > .clickToPlay,
+.clickToPlay:hover {
+ opacity: 0.55;
}
-html|table {
- font-family: Helvetica, Arial, sans-serif;
- font-size: 11px;
- color: white;
- text-shadow:
- -1px -1px 0 #000,
- 1px -1px 0 #000,
- -1px 1px 0 #000,
- 1px 1px 0 #000;
- min-width: 100%;
- background: rgba(68,68,68,.7);
- table-layout: fixed;
- border-collapse: collapse;
- position: absolute;
+
+.controlsSpacerStack:hover > .clickToPlay[fadeout] {
+ opacity: 0;
+}
+
+.controlBar[fullscreen-unavailable] .fullscreenButton {
+ display: none;
}
/* CSS Transitions */
.clickToPlay {
- transition-property: opacity, background-size;
+ transition-property: transform, opacity;
transition-duration: 400ms, 400ms;
}
-.clickToPlay[fadeout] {
- background-size: auto, 192px 192px;
+
+.controlsSpacer[fadeout] {
opacity: 0;
}
+
+.clickToPlay[fadeout] {
+ transform: scale(3);
+ opacity: 0;
+}
+
.clickToPlay[fadeout][immediate] {
transition-property: opacity, background-size;
transition-duration: 0s, 0s;
}
.controlBar:not([immediate]) {
transition-property: opacity;
transition-duration: 200ms;
}
.controlBar[fadeout] {
opacity: 0;
}
.volumeStack:not([immediate]) {
transition-property: opacity, margin-top;
transition-duration: 200ms, 200ms;
}
-.volumeStack[fadeout] {
- opacity: 0;
- margin-top: 0;
-}
.statusOverlay:not([immediate]) {
transition-property: opacity;
transition-duration: 300ms;
transition-delay: 750ms;
}
.statusOverlay[fadeout] {
opacity: 0;
}
/* Error description formatting */
.errorLabel {
- font-family: Helvetica, Arial, sans-serif;
- font-size: 11px;
- color: #bbb;
- text-shadow:
- -1px -1px 0 #000,
- 1px -1px 0 #000,
- -1px 1px 0 #000,
- 1px 1px 0 #000;
padding: 0 10px;
text-align: center;
+ font: message-box;
+ font-size: 14px;
+ color: #ffffff;
}
-@media (min-resolution: 2dppx) {
- .playButton {
- background-image: url(chrome://global/skin/media/pauseButton@2x.png);
- background-size: 28px 28px;
- }
- .playButton[paused] {
- background-image: url(chrome://global/skin/media/playButton@2x.png);
- background-size: 28px 28px;
- }
- .volumeBackground {
- background-image: url(chrome://global/skin/media/volume-empty@2x.png);
- background-size: 32px 16px;
- }
- .volumeForeground {
- background-image: url(chrome://global/skin/media/volume-full@2x.png);
- background-size: 32px 16px;
- }
- .muteButton {
- background-image: url(chrome://global/skin/media/muteButton@2x.png);
- background-size: 33px 28px;
- }
- .muteButton[muted] {
- background-image: url(chrome://global/skin/media/unmuteButton@2x.png);
- background-size: 33px 28px;
- }
- .muteButton[noAudio] {
- background-image: url(chrome://global/skin/media/noAudio@2x.png);
- background-size: 33px 28px;
- }
- .closedCaptionButton {
- background-image: url(chrome://global/skin/media/closeCaptionButton@2x.png);
- background-position: 4px;
- background-size: 28px 28px;
- }
- .fullscreenButton {
- background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton@2x.png"), 0, 32, 32, 0);
- background-size: 16px 16px;
- }
- .fullscreenButton[fullscreened] {
- background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton@2x.png"), 0, 64, 32, 32);
- background-size: 16px 16px;
- }
- .timeThumb {
- background-image: url(chrome://global/skin/media/scrubberThumb@2x.png);
- background-size: 33px 28px;
- }
- .timeThumb[showhours="true"] {
- background-image: url(chrome://global/skin/media/scrubberThumbWide@2x.png);
- background-size: 45px 28px;
- }
+.errorLabel {
+ display: none;
}
+
+[error="errorAborted"] > [anonid="errorAborted"],
+[error="errorNetwork"] > [anonid="errorNetwork"],
+[error="errorDecode"] > [anonid="errorDecode"],
+[error="errorSrcNotSupported"] > [anonid="errorSrcNotSupported"],
+[error="errorNoSource"] > [anonid="errorNoSource"],
+[error="errorGeneric"] > [anonid="errorGeneric"] {
+ display: inline;
+}
deleted file mode 100644
index 589abfbd5816bb3ec179a3fdf6530106356dba8e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ca494e89366563cbb509761eb1f8f3bcae60e2cd..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4398a569b8c76d0b42fd2900abfcdec1747a0483..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5bf63c7ee25a0a7c2fbfee5db50b95b20093b1e8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001