Bug 946454 - [Roku] Show 'casting' button in the video controls r=wesj, dolske

This commit is contained in:
Mark Finkle 2014-04-18 16:48:06 -04:00
parent b14d01f56e
commit 78297a38af
10 changed files with 164 additions and 11 deletions

View File

@ -29,6 +29,7 @@
.fullscreenButton,
.playButton,
.castingButton,
.muteButton {
-moz-appearance: none;
min-height: 42px;
@ -62,6 +63,10 @@
background: url("chrome://b2g/content/images/play-hdpi.png") no-repeat center;
}
.castingButton {
display: none;
}
.muteButton {
background: url("chrome://b2g/content/images/mute-hdpi.png") no-repeat center;
}

View File

@ -29,6 +29,7 @@
.fullscreenButton,
.playButton,
.castingButton,
.muteButton {
-moz-appearance: none;
min-height: 42px;
@ -62,6 +63,10 @@
background: url("chrome://browser/skin/images/play-hdpi.png") no-repeat center;
}
.castingButton {
display: none;
}
.muteButton {
background: url("chrome://browser/skin/images/mute-hdpi.png") no-repeat center;
}

View File

@ -63,6 +63,47 @@ var CastingApps = {
}
},
_sendEventToVideo: function _sendEventToVideo(aElement, aData) {
let event = aElement.ownerDocument.createEvent("CustomEvent");
event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(aData));
aElement.dispatchEvent(event);
},
handleVideoBindingAttached: function handleVideoBindingAttached(aTab, aEvent) {
// Let's figure out if we have everything needed to cast a video. The binding
// defaults to |false| so we only need to send an event if |true|.
let video = aEvent.target;
if (!video instanceof HTMLVideoElement) {
return;
}
if (SimpleServiceDiscovery.services.length == 0) {
return;
}
if (!this.getVideo(video, 0, 0)) {
return;
}
// Let the binding know casting is allowed
this._sendEventToVideo(video, { allow: true });
},
handleVideoBindingCast: function handleVideoBindingCast(aTab, aEvent) {
// The binding wants to start a casting session
let video = aEvent.target;
if (!video instanceof HTMLVideoElement) {
return;
}
// Close an existing session first. closeExternal has checks for an exsting
// session and handles remote and video binding shutdown.
this.closeExternal();
// Start the new session
this.openExternal(video, 0, 0);
},
makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) {
return Services.io.newURI(aURL, aOriginCharset, aBaseURI);
},
@ -221,7 +262,8 @@ var CastingApps = {
title: video.title,
source: video.source,
poster: video.poster
}
},
videoRef: Cu.getWeakReference(video.element)
};
}.bind(this), this);
}.bind(this));
@ -236,6 +278,12 @@ var CastingApps = {
this.session.remoteMedia.shutdown();
this.session.app.stop();
let video = this.session.videoRef.get();
if (video) {
this._sendEventToVideo(video, { active: false });
}
delete this.session;
},
@ -247,6 +295,11 @@ var CastingApps = {
aRemoteMedia.load(this.session.data);
sendMessageToJava({ type: "Casting:Started", device: this.session.service.friendlyName });
let video = this.session.videoRef.get();
if (video) {
this._sendEventToVideo(video, { active: true });
}
},
onRemoteMediaStop: function(aRemoteMedia) {

View File

@ -3010,11 +3010,14 @@ Tab.prototype = {
this.browser.addEventListener("blur", this, true);
this.browser.addEventListener("scroll", this, true);
this.browser.addEventListener("MozScrolledAreaChanged", this, true);
// Note that the XBL binding is untrusted
this.browser.addEventListener("PluginBindingAttached", this, true, true);
this.browser.addEventListener("pageshow", this, true);
this.browser.addEventListener("MozApplicationManifest", this, true);
// Note that the XBL binding is untrusted
this.browser.addEventListener("PluginBindingAttached", this, true, true);
this.browser.addEventListener("VideoBindingAttached", this, true, true);
this.browser.addEventListener("VideoBindingCast", this, true, true);
Services.obs.addObserver(this, "before-first-paint", false);
Services.obs.addObserver(this, "after-viewport-change", false);
Services.prefs.addObserver("browser.ui.zoom.force-user-scalable", this, false);
@ -3179,10 +3182,13 @@ Tab.prototype = {
this.browser.removeEventListener("blur", this, true);
this.browser.removeEventListener("scroll", this, true);
this.browser.removeEventListener("MozScrolledAreaChanged", this, true);
this.browser.removeEventListener("PluginBindingAttached", this, true);
this.browser.removeEventListener("pageshow", this, true);
this.browser.removeEventListener("MozApplicationManifest", this, true);
this.browser.removeEventListener("PluginBindingAttached", this, true, true);
this.browser.removeEventListener("VideoBindingAttached", this, true, true);
this.browser.removeEventListener("VideoBindingCast", this, true, true);
Services.obs.removeObserver(this, "before-first-paint");
Services.obs.removeObserver(this, "after-viewport-change");
Services.prefs.removeObserver("browser.ui.zoom.force-user-scalable", this);
@ -3934,6 +3940,16 @@ Tab.prototype = {
break;
}
case "VideoBindingAttached": {
CastingApps.handleVideoBindingAttached(this, aEvent);
break;
}
case "VideoBindingCast": {
CastingApps.handleVideoBindingCast(this, aEvent);
break;
}
case "MozApplicationManifest": {
OfflineApps.offlineAppRequested(aEvent.originalTarget.defaultView);
break;

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

View File

@ -62,6 +62,8 @@ chrome.jar:
skin/images/search-clear-30.png (images/search-clear-30.png)
skin/images/play-hdpi.png (images/play-hdpi.png)
skin/images/pause-hdpi.png (images/pause-hdpi.png)
skin/images/cast-ready-hdpi.png (images/cast-ready-hdpi.png)
skin/images/cast-active-hdpi.png (images/cast-active-hdpi.png)
skin/images/mute-hdpi.png (images/mute-hdpi.png)
skin/images/unmute-hdpi.png (images/unmute-hdpi.png)
skin/images/scrubber-hdpi.png (images/scrubber-hdpi.png)

View File

@ -28,6 +28,7 @@
}
.playButton,
.castingButton,
.muteButton {
-moz-appearance: none;
min-height: 42px;
@ -44,6 +45,14 @@
background: url("chrome://browser/skin/images/play-hdpi.png") no-repeat center;
}
.castingButton {
background: url("chrome://browser/skin/images/cast-ready-hdpi.png") no-repeat center;
}
.castingButton[active="true"] {
background: url("chrome://browser/skin/images/cast-active-hdpi.png") no-repeat center;
}
.muteButton {
background: url("chrome://browser/skin/images/mute-hdpi.png") no-repeat center;
}

View File

@ -1527,6 +1527,8 @@
</box>
<vbox class="controlBar" hidden="true">
<hbox class="buttonsBar">
<button class="castingButton" hidden="true"
aria-label="&castingButton.castingLabel;"/>
<button class="fullscreenButton"
enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
@ -1566,9 +1568,11 @@
this.isTouchControl = true;
this.TouchUtils = {
videocontrols: null,
controlsTimer : null,
controlsTimeout : 5000,
video: null,
controlsTimer: null,
controlsTimeout: 5000,
positionLabel: null,
castingButton: null,
get Utils() {
return this.videocontrols.Utils;
@ -1640,13 +1644,59 @@
this.Utils.video.removeEventListener(event, this, false);
},
isVideoCasting : function () {
let unwrappedVideo = XPCNativeWrapper.unwrap(this.video);
if (unwrappedVideo.mozIsCasting)
return true;
return false;
},
updateCasting : function (eventDetail) {
let unwrappedVideo = XPCNativeWrapper.unwrap(this.video);
let castingData = JSON.parse(eventDetail);
if ("allow" in castingData) {
if (castingData.allow)
unwrappedVideo.mozAllowCasting = true;
else
delete unwrappedVideo.mozAllowCasting;
}
if ("active" in castingData) {
if (castingData.active)
unwrappedVideo.mozIsCasting = true;
else
delete unwrappedVideo.mozIsCasting;
}
this.setCastButtonState();
},
startCasting : function () {
this.videocontrols.dispatchEvent(new CustomEvent("VideoBindingCast"));
},
setCastButtonState : function () {
let unwrappedVideo = XPCNativeWrapper.unwrap(this.video);
if (this.isAudioOnly || !unwrappedVideo.mozAllowCasting) {
this.castingButton.hidden = true;
return;
}
if (unwrappedVideo.mozIsCasting) {
this.castingButton.setAttribute("active", "true");
} else {
this.castingButton.removeAttribute("active");
}
this.castingButton.hidden = false;
},
init : function (binding) {
this.videocontrols = binding;
var video = binding.parentNode;
this.video = binding.parentNode;
let self = this;
this.Utils.playButton.addEventListener("command", function() {
if (!self.Utils.video.paused)
if (!self.video.paused)
self.delayHideControls(0);
else
self.showControls();
@ -1659,26 +1709,38 @@
}, false);
this.Utils.muteButton.addEventListener("click", function() { self.delayHideControls(self.controlsTimeout); }, false);
this.castingButton = document.getAnonymousElementByAttribute(binding, "class", "castingButton");
this.castingButton.addEventListener("command", function() {
self.startCasting();
}, false);
this.video.addEventListener("media-videoCasting", function (e) {
if (!e.isTrusted)
return;
self.updateCasting(e.detail);
}, false, true);
// The first time the controls appear we want to just display
// a play button that does not fade away. The firstShow property
// makes that happen. But because of bug 718107 this init() method
// may be called again when we switch in or out of fullscreen
// mode. So we only set firstShow if we're not autoplaying and
// if we are at the beginning of the video and not already playing
if (!video.autoplay && this.Utils.dynamicControls && video.paused &&
video.currentTime === 0)
if (!this.video.autoplay && this.Utils.dynamicControls && this.video.paused &&
this.video.currentTime === 0)
this.firstShow = true;
// If the video is not at the start, then we probably just
// 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 (video.currentTime !== 0) {
if (this.video.currentTime !== 0) {
this.delayHideControls(this.Utils.HIDE_CONTROLS_TIMEOUT_MS);
}
}
};
this.TouchUtils.init(this);
this.dispatchEvent(new CustomEvent("VideoBindingAttached"));
]]>
</constructor>
<destructor>

View File

@ -8,6 +8,7 @@
<!ENTITY muteButton.unmuteLabel "Unmute">
<!ENTITY fullscreenButton.enterfullscreenlabel "Full Screen">
<!ENTITY fullscreenButton.exitfullscreenlabel "Exit Full Screen">
<!ENTITY castingButton.castingLabel "Cast to Screen">
<!ENTITY stats.media "Media">
<!ENTITY stats.size "Size">