bug 1088758 - Add the ability to mirror tabs from desktop to a second screen r=mconley, mfinkle

This commit is contained in:
Brad Lassey 2014-12-15 18:52:55 -05:00
parent f6f6e4df7d
commit 551703ddcf
8 changed files with 73 additions and 10 deletions

View File

@ -451,7 +451,8 @@
<menu id="tools-menu"
label="&toolsMenu.label;"
accesskey="&toolsMenu.accesskey;">
accesskey="&toolsMenu.accesskey;"
onpopupshowing="mirrorShow(this)">
<menupopup id="menu_ToolsPopup"
#ifdef MOZ_SERVICES_SYNC
# We have to use setTimeout() here to avoid a flickering menu bar when opening
@ -548,6 +549,12 @@
key="key_viewInfo"
#endif
command="View:PageInfo"/>
<menu id="menu_mirrorTabCmd"
accesskey="&mirrorTabCmd.accesskey;"
label="&mirrorTabCmd.label;">
<menupopup id="menu_mirrorTab-popup"
onpopupshowing="populateMirrorTabMenu(this)"/>
</menu>
#ifndef XP_UNIX
<menuseparator id="prefSep"/>
<menuitem id="menu_preferences"

View File

@ -2968,6 +2968,30 @@ function BrowserFullScreen()
window.fullScreen = !window.fullScreen;
}
function mirrorShow(popup) {
let services = CastingApps.getServicesForMirroring();
popup.ownerDocument.getElementById("menu_mirrorTabCmd").disabled = !services.length;
}
function mirrorMenuItemClicked(event) {
gBrowser.selectedBrowser.messageManager.sendAsyncMessage("SecondScreen:tab-mirror",
{service: event.originalTarget._service});
}
function populateMirrorTabMenu(popup) {
let videoEl = this.target;
popup.innerHTML = null;
let doc = popup.ownerDocument;
let services = CastingApps.getServicesForMirroring();
services.forEach(service => {
let item = doc.createElement("menuitem");
item.setAttribute("label", service.friendlyName);
item._service = service;
item.addEventListener("command", mirrorMenuItemClicked);
popup.appendChild(item);
});
};
function _checkDefaultAndSwitchToMetro() {
#ifdef HAVE_SHELL_SERVICE
#ifdef XP_WIN

View File

@ -27,6 +27,22 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
"resource:///modules/FormSubmitObserver.jsm");
XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() {
let ssdp = Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm", {}).SimpleServiceDiscovery;
// Register targets
ssdp.registerDevice({
id: "roku:ecp",
target: "roku:ecp",
factory: function(aService) {
Cu.import("resource://gre/modules/RokuApp.jsm");
return new RokuApp(aService);
},
mirror: true,
types: ["video/mp4"],
extensions: ["mp4"]
});
return ssdp;
});
// TabChildGlobal
var global = this;
@ -76,6 +92,16 @@ addMessageListener("MixedContent:ReenableProtection", function() {
docShell.mixedContentChannel = null;
});
addMessageListener("SecondScreen:tab-mirror", function(message) {
let app = SimpleServiceDiscovery.findAppForService(message.data.service);
if (app) {
let width = content.scrollWidth;
let height = content.scrollHeight;
let viewport = {cssWidth: width, cssHeight: height, width: width, height: height};
app.mirror(function() {}, content, viewport, function() {}, content);
}
});
addEventListener("DOMFormHasPassword", function(event) {
InsecurePasswordUtils.checkForInsecurePasswords(event.target);
LoginManagerContent.onFormPassword(event);

View File

@ -807,7 +807,7 @@ BrowserGlue.prototype = {
Cu.import("resource://gre/modules/RokuApp.jsm");
return new RokuApp(aService);
},
mirror: false,
mirror: true,
types: ["video/mp4"],
extensions: ["mp4"]
};

View File

@ -95,6 +95,8 @@ when there are no windows but Firefox is still running. -->
<!ENTITY pageInfoCmd.label "Page Info">
<!ENTITY pageInfoCmd.accesskey "I">
<!ENTITY pageInfoCmd.commandkey "i">
<!ENTITY mirrorTabCmd.label "Mirror Tab">
<!ENTITY mirrorTabCmd.accesskey "m">
<!-- LOCALIZATION NOTE (enterFullScreenCmd.label, exitFullScreenCmd.label):
These should match what Safari and other Apple applications use on OS X Lion. -->
<!ENTITY enterFullScreenCmd.label "Enter Full Screen">

View File

@ -130,6 +130,10 @@ var CastingApps = {
return filteredServices;
},
getServicesForMirroring: function () {
return SimpleServiceDiscovery.services.filter(service => service.mirror);
},
// RemoteMedia callback API methods
onRemoteMediaStart: function (remoteMedia) {
if (!this.session) {

View File

@ -98,7 +98,7 @@ var CastingApps = {
let callbackFunc = function(aService) {
let app = SimpleServiceDiscovery.findAppForService(aService);
if (app) {
app.mirror(function() {}, window, BrowserApp.selectedTab.getViewport(), this._mirrorStarted.bind(this));
app.mirror(function() {}, window, BrowserApp.selectedTab.getViewport(), this._mirrorStarted.bind(this), window.BrowserApp.selectedBrowser.contentWindow);
}
}.bind(this);

View File

@ -147,16 +147,16 @@ RokuApp.prototype = {
}
},
mirror: function(callback, win, viewport, mirrorStartedCallback) {
mirror: function(callback, win, viewport, mirrorStartedCallback, contentWindow) {
if (this.mirrorAppID == -1) {
// The status function may not have been called yet if mirrorAppID is -1
this.status(this._createRemoteMirror.bind(this, callback, win, viewport, mirrorStartedCallback));
this.status(this._createRemoteMirror.bind(this, callback, win, viewport, mirrorStartedCallback, contentWindow));
} else {
this._createRemoteMirror(callback, win, viewport, mirrorStartedCallback);
this._createRemoteMirror(callback, win, viewport, mirrorStartedCallback, contentWindow);
}
},
_createRemoteMirror: function(callback, win, viewport, mirrorStartedCallback) {
_createRemoteMirror: function(callback, win, viewport, mirrorStartedCallback, contentWindow) {
if (this.mirrorAppID == -1) {
// TODO: Inform user to install Roku WebRTC Player Channel.
log("RokuApp: Failed to find Mirror App ID.");
@ -169,7 +169,7 @@ RokuApp.prototype = {
xhr.addEventListener("load", (function() {
// 204 seems to be returned if the channel is already running
if ((xhr.status == 200) || (xhr.status == 204)) {
this.remoteMirror = new RemoteMirror(this.resourceURL, win, viewport, mirrorStartedCallback);
this.remoteMirror = new RemoteMirror(this.resourceURL, win, viewport, mirrorStartedCallback, contentWindow);
}
}).bind(this), false);
@ -279,7 +279,7 @@ RemoteMedia.prototype = {
}
}
function RemoteMirror(url, win, viewport, mirrorStartedCallback) {
function RemoteMirror(url, win, viewport, mirrorStartedCallback, contentWindow) {
this._serverURI = Services.io.newURI(url , null, null);
this._window = win;
this._iceCandidates = [];
@ -287,7 +287,7 @@ function RemoteMirror(url, win, viewport, mirrorStartedCallback) {
// This code insures the generated tab mirror is not wider than 800 nor taller than 600
// Better dimensions should be chosen after the Roku Channel is working.
let windowId = win.BrowserApp.selectedBrowser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
let windowId = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
let cWidth = Math.max(viewport.cssWidth, viewport.width);
let cHeight = Math.max(viewport.cssHeight, viewport.height);