Bug 1166961 - Show click to play button on Fennec when autoplay is blocked. r=mfinkle

--HG--
extra : rebase_source : c2e78e8c5720e14cdcab1be8f12c92b60055a996
This commit is contained in:
Randall Barker 2015-09-16 16:22:00 +02:00
Родитель 4b7b981193
Коммит b1c0d79c4e
5 изменённых файлов: 178 добавлений и 0 удалений

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

@ -2223,6 +2223,13 @@ HTMLMediaElement::Play(ErrorResult& aRv)
&& !EventStateManager::IsHandlingUserInput()
&& !nsContentUtils::IsCallerChrome()) {
LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
#if defined(MOZ_WIDGET_ANDROID)
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
static_cast<nsIContent*>(this),
NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
false,
false);
#endif
return;
}

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

@ -63,6 +63,8 @@ var CastingApps = {
_castMenuId: -1,
mirrorStartMenuId: -1,
mirrorStopMenuId: -1,
_blocked: null,
_bound: null,
init: function ca_init() {
if (!this.isCastingEnabled()) {
@ -96,6 +98,9 @@ var CastingApps = {
BrowserApp.deck.addEventListener("pageshow", this, true);
BrowserApp.deck.addEventListener("playing", this, true);
BrowserApp.deck.addEventListener("ended", this, true);
BrowserApp.deck.addEventListener("MozAutoplayMediaBlocked", this, true);
// Note that the XBL binding is untrusted
BrowserApp.deck.addEventListener("MozNoControlsVideoBindingAttached", this, true, true);
},
_mirrorStarted: function(stopMirrorCallback) {
@ -223,6 +228,28 @@ var CastingApps = {
}
break;
}
case "MozAutoplayMediaBlocked": {
if (this._bound && this._bound.has(aEvent.target)) {
aEvent.target.dispatchEvent(new CustomEvent("MozNoControlsBlockedVideo"));
} else {
if (!this._blocked) {
this._blocked = new WeakMap;
}
this._blocked.set(aEvent.target, true);
}
break;
}
case "MozNoControlsVideoBindingAttached": {
if (!this._bound) {
this._bound = new WeakMap;
}
this._bound.set(aEvent.target, true);
if (this._blocked && this._blocked.has(aEvent.target)) {
this._blocked.delete(aEvent.target);
aEvent.target.dispatchEvent(new CustomEvent("MozNoControlsBlockedVideo"));
}
break;
}
}
},

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

@ -304,6 +304,12 @@ select:disabled > button {
-moz-binding: url("chrome://global/content/bindings/videocontrols.xml#touchControls");
}
/* display click to play when autoplay is blocked for videos */
video:not([controls]) > xul|videocontrols {
visibility: visible;
-moz-binding: url("chrome://global/content/bindings/videocontrols.xml#noControls");
}
*:-moz-any-link:active,
*[role=button]:active,
button:not(:disabled):active,

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

@ -220,3 +220,17 @@
text-align: center;
}
/* 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);
}

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

@ -1805,4 +1805,128 @@
</handlers>
</binding>
<binding id="noControls">
<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">
<vbox flex="1" class="statusOverlay" hidden="true">
<box flex="1">
<box class="clickToPlay" flex="1"/>
</box>
</vbox>
</xbl:content>
<implementation>
<constructor>
<![CDATA[
this.randomID = 0;
this.Utils = {
randomID : 0,
videoEvents : ["play",
"playing"],
controlListeners: [],
terminateEventListeners : function () {
for each (let event in this.videoEvents)
this.video.removeEventListener(event, this, false);
for each(let element in this.controlListeners)
element.item.removeEventListener(element.event, element.func, false);
delete this.controlListeners;
},
hasError : function () {
return (this.video.error != null || this.video.networkState == this.video.NETWORK_NO_SOURCE);
},
handleEvent : function (aEvent) {
// If the binding is detached (or has been replaced by a
// newer instance of the binding), nuke our event-listeners.
if (this.binding.randomID != this.randomID) {
this.terminateEventListeners();
return;
}
switch (aEvent.type) {
case "play":
this.noControlsOverlay.hidden = true;
break;
case "playing":
this.noControlsOverlay.hidden = true;
break;
default:
//this.log("!!! event " + aEvent.type + " not handled!");
}
},
blockedVideoHandler : function () {
if (this.binding.randomID != this.randomID) {
this.terminateEventListeners();
return;
} else if (this.hasError()) {
this.noControlsOverlay.hidden = true;
return;
}
this.noControlsOverlay.hidden = false;
},
clickToPlayClickHandler : function (e) {
if (this.binding.randomID != this.randomID) {
this.terminateEventListeners();
return;
} else if (e.button != 0) {
return;
}
this.noControlsOverlay.hidden = true;
this.video.play();
},
init : function (binding) {
this.binding = binding;
this.randomID = Math.random();
this.binding.randomID = this.randomID;
this.video = binding.parentNode;
this.clickToPlay = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay");
this.noControlsOverlay = document.getAnonymousElementByAttribute(binding, "class", "statusOverlay");
let self = this;
function addListener(elem, eventName, func) {
let boundFunc = func.bind(self);
self.controlListeners.push({ item: elem, event: eventName, func: boundFunc });
elem.addEventListener(eventName, boundFunc, false);
}
addListener(this.clickToPlay, "click", this.clickToPlayClickHandler);
addListener(this.video, "MozNoControlsBlockedVideo", this.blockedVideoHandler);
for each (let event in this.videoEvents) {
this.video.addEventListener(event, this, false);
}
if (this.video.autoplay && !this.video.mozAutoplayEnabled) {
this.blockedVideoHandler();
}
}
};
this.Utils.init(this);
this.Utils.video.dispatchEvent(new CustomEvent("MozNoControlsVideoBindingAttached"));
]]>
</constructor>
<destructor>
<![CDATA[
// randomID used to be a <field>, which meant that the XBL machinery
// undefined the property when the element was unbound. The code in
// this file actually depends on this, so now that randomID is an
// expando, we need to make sure to explicitly delete it.
delete this.randomID;
]]>
</destructor>
</implementation>
</binding>
</bindings>