Bug 887934 - Add closed caption button to video control. r=jaws

MozReview-Commit-ID: JykXcNBkSLr

--HG--
extra : transplant_source : %0C%0F%7C%86%89f%BC%FEU%ECI%3F%A6%AD%25%0Fq%C2%B6%18
This commit is contained in:
Ray Lin 2016-06-07 15:07:03 +08:00
Родитель 9e12cef183
Коммит 98f56e3561
10 изменённых файлов: 301 добавлений и 1 удалений

Просмотреть файл

@ -215,6 +215,7 @@
<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"
@ -238,6 +239,7 @@
<box class="volumeForeground" anonid="volumeForeground"/>
<scale class="volumeControl" movetoclick="true"/>
</stack>
<button class="closedCaptionButton"/>
<button class="fullscreenButton"
enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
@ -272,7 +274,9 @@
clickToPlay : null,
controlsOverlay : null,
fullscreenButton : null,
currentTextTrackIndex: 0,
textTracksCount: 0,
randomID : 0,
videoEvents : ["play", "pause", "ended", "volumechange", "loadeddata",
"loadstart", "timeupdate", "progress",
@ -407,6 +411,7 @@
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;
this.controlBar.hidden = true;
@ -897,6 +902,7 @@
return;
this.startFadeOut(this.controlBar, false);
this.textTrackList.setAttribute("hidden", "true");
clearTimeout(this._showControlsTimeout);
Utils._controlsHiddenByTimeout = false;
}
@ -1206,6 +1212,182 @@
event.preventDefault(); // Prevent page scrolling
},
get videoSubtitles() {
return Array.prototype.filter.call(this.video.textTracks, function (tt) {
return tt.kind === "subtitles";
});
},
isClosedCaptionOn : function () {
for (let tt of this.videoSubtitles) {
if (tt.mode === "showing") {
return true;
}
}
return false;
},
setClosedCaptionButtonState : function () {
if (!this.videoSubtitles.length) {
this.closedCaptionButton.setAttribute("hidden", "true");
return;
}
this.closedCaptionButton.removeAttribute("hidden");
if (this.isClosedCaptionOn()) {
this.closedCaptionButton.setAttribute("enabled", "true");
} else {
this.closedCaptionButton.removeAttribute("enabled");
}
let ttItems = this.textTrackList.childNodes;
for (let tti of ttItems) {
const idx = +tti.getAttribute("index");
if (idx == this.currentTextTrackIndex) {
tti.setAttribute("on", "true");
} else {
tti.removeAttribute("on");
}
}
},
addNewTextTrack : function (tt) {
if (tt.kind !== "subtitles") {
return;
}
if (tt.index && tt.index < this.textTracksCount) {
return;
}
tt.index = this.textTracksCount++;
const label = tt.label || "";
const ttText = document.createTextNode(label);
const ttBtn = document.createElement("button");
ttBtn.classList.add("textTrackItem");
ttBtn.setAttribute("index", tt.index);
ttBtn.addEventListener("click", function (event) {
event.stopPropagation();
this.changeTextTrack(tt.index);
}.bind(this));
ttBtn.appendChild(ttText);
this.textTrackList.appendChild(ttBtn);
if (tt.mode === "showing" && tt.index) {
this.changeTextTrack(tt.index);
}
},
changeTextTrack : function (index) {
for (let tt of this.videoSubtitles) {
if (tt.index === index) {
tt.mode = "showing";
this.currentTextTrackIndex = tt.index;
} else {
tt.mode = "disabled";
}
}
// should fallback to off
if (this.currentTextTrackIndex !== index) {
this.currentTextTrackIndex = 0;
}
this.textTrackList.setAttribute("hidden", "true");
this.setClosedCaptionButtonState();
},
onControlBarTransitioned : function () {
this.textTrackList.setAttribute("hidden", "true");
},
toggleClosedCaption : function () {
if (this.videoSubtitles.length === 1) {
const lastTTIdx = this.videoSubtitles[0].index;
return this.changeTextTrack(this.isClosedCaptionOn() ? 0 : lastTTIdx);
}
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) {
const toRemoveIndex = trackEvent.track.index;
const ttItems = this.textTrackList.childNodes;
if (!ttItems) {
return;
}
for (let tti of ttItems) {
const idx = +tti.getAttribute("index");
if (idx === toRemoveIndex) {
tti.remove();
this.textTracksCount--;
}
if (idx === this.currentTextTrackIndex) {
this.currentTextTrackIndex = 0;
this.video.dispatchEvent(new CustomEvent("texttrackchange"));
}
}
this.setClosedCaptionButtonState();
},
initTextTracks : function () {
const offLabel = this.textTrackList.getAttribute("offlabel");
this.addNewTextTrack({
label: offLabel,
kind: "subtitles"
});
for (let tt of this.videoSubtitles) {
this.addNewTextTrack(tt);
}
this.setClosedCaptionButtonState();
},
isEventWithin : function (event, parent1, parent2) {
function isDescendant (node) {
while (node) {
@ -1233,6 +1415,7 @@
_durationLabelWidth : 0,
_muteButtonWidth : 0,
_volumeControlWidth : 0,
_closedCaptionButtonWidth : 0,
_fullscreenButtonWidth : 0,
_controlBarHeight : 0,
_overlayPlayButtonHeight : 64,
@ -1249,6 +1432,7 @@
this._durationLabelWidth +
this._muteButtonWidth +
this._volumeControlWidth +
this._closedCaptionButtonWidth +
this._fullscreenButtonWidth;
let isFullscreenUnavailable = this.controlBar.hasAttribute("fullscreen-unavailable");
@ -1326,10 +1510,13 @@
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.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
@ -1343,7 +1530,6 @@
}
var self = this;
this.controlListeners = [];
// Helper function to add an event listener to the given element
@ -1354,6 +1540,7 @@
}
addListener(this.muteButton, "command", this.toggleMute);
addListener(this.closedCaptionButton, "command", this.toggleClosedCaption);
addListener(this.playButton, "click", this.clickToPlayClickHandler);
addListener(this.fullscreenButton, "command", this.toggleFullscreen);
addListener(this.clickToPlay, "click", this.clickToPlayClickHandler);
@ -1362,8 +1549,12 @@
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.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)

Просмотреть файл

@ -9,6 +9,7 @@
<!ENTITY fullscreenButton.enterfullscreenlabel "Full Screen">
<!ENTITY fullscreenButton.exitfullscreenlabel "Exit Full Screen">
<!ENTITY castingButton.castingLabel "Cast to Screen">
<!ENTITY closedCaption.off "Off">
<!ENTITY stats.media "Media">
<!ENTITY stats.size "Size">

Просмотреть файл

@ -147,6 +147,8 @@ toolkit.jar:
skin/classic/global/media/unmuteButton@2x.png (media/unmuteButton@2x.png)
skin/classic/global/media/noAudio.png (media/noAudio.png)
skin/classic/global/media/noAudio@2x.png (media/noAudio@2x.png)
skin/classic/global/media/closeCaptionButton.png (media/closeCaptionButton.png)
skin/classic/global/media/closeCaptionButton@2x.png (media/closeCaptionButton@2x.png)
skin/classic/global/media/fullscreenButton.png (media/fullscreenButton.png)
skin/classic/global/media/fullscreenButton@2x.png (media/fullscreenButton@2x.png)
skin/classic/global/media/scrubberThumb.png (media/scrubberThumb.png)

Двоичные данные
toolkit/themes/osx/global/media/closeCaptionButton.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 583 B

Двоичные данные
toolkit/themes/osx/global/media/closeCaptionButton@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -12,6 +12,7 @@
.playButton,
.muteButton,
.closedCaptionButton,
.fullscreenButton {
background-color: transparent;
background-repeat: no-repeat;
@ -27,12 +28,14 @@
.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;
}
@ -63,6 +66,18 @@
display: none;
}
.closedCaptionButton {
background-image: url(chrome://global/skin/media/closeCaptionButton.png);
}
.closedCaptionButton[enabled] {
opacity: 1;
}
.closedCaptionButton[hidden] {
display: none;
}
.fullscreenButton {
background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 16, 16, 0);
}
@ -92,6 +107,41 @@
background-clip: content-box;
}
.textTrackList {
display: -moz-box;
-moz-appearance: none;
-moz-box-pack: end;
-moz-box-align: end;
padding: 0;
}
.textTrackList[hidden] {
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);
white-space: nowrap;
}
.textTrackList > html|*.textTrackItem[on] {
color: white;
background-color: black;
}
.textTrackList > html|*.textTrackItem:hover {
background-color: rgba(0,0,0,.55);
}
.controlBar[fullscreen-unavailable] > .volumeStack {
/* This value is duplicated in the videocontrols.xml adjustControlSize function. */
margin-inline-end: 8px;
@ -340,6 +390,10 @@ html|table {
background-image: url(chrome://global/skin/media/noAudio@2x.png);
background-size: 33px 28px;
}
.closeCaptionButton {
background-image: url(chrome://global/skin/media/closeCaptionButton@2x.png);
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;

Просмотреть файл

@ -101,6 +101,8 @@
skin/classic/global/media/muteButton.png (../../windows/global/media/muteButton.png)
skin/classic/global/media/unmuteButton.png (../../windows/global/media/unmuteButton.png)
skin/classic/global/media/noAudio.png (../../windows/global/media/noAudio.png)
skin/classic/global/media/closeCaptionButton.png (../../windows/global/media/closeCaptionButton.png)
skin/classic/global/media/closeCaptionButton@2x.png (../../windows/global/media/closeCaptionButton@2x.png)
skin/classic/global/media/fullscreenButton.png (../../windows/global/media/fullscreenButton.png)
skin/classic/global/media/scrubberThumb.png (../../windows/global/media/scrubberThumb.png)
skin/classic/global/media/scrubberThumbWide.png (../../windows/global/media/scrubberThumbWide.png)

Двоичные данные
toolkit/themes/windows/global/media/closeCaptionButton.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 583 B

Двоичные данные
toolkit/themes/windows/global/media/closeCaptionButton@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -12,6 +12,7 @@
.playButton,
.muteButton,
.closedCaptionButton,
.fullscreenButton {
background-color: transparent;
background-repeat: no-repeat;
@ -27,12 +28,14 @@
.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;
}
@ -63,6 +66,18 @@
display: none;
}
.closedCaptionButton {
background-image: url(chrome://global/skin/media/closeCaptionButton.png);
}
.closedCaptionButton[enabled] {
opacity: 1;
}
.closedCaptionButton[hidden] {
display: none;
}
.fullscreenButton {
background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 16, 16, 0);
}
@ -92,6 +107,41 @@
background-clip: content-box;
}
.textTrackList {
display: -moz-box;
-moz-appearance: none;
-moz-box-pack: end;
-moz-box-align: end;
padding: 0;
}
.textTrackList[hidden] {
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);
white-space: nowrap;
}
.textTrackList > html|*.textTrackItem[on] {
color: white;
background-color: black;
}
.textTrackList > html|*.textTrackItem:hover {
background-color: rgba(0,0,0,.55);
}
.controlBar[fullscreen-unavailable] > .volumeStack {
/* This value is duplicated in the videocontrols.xml adjustControlSize function. */
margin-inline-end: 8px;