Bug 961362 - DOM Fullscreen API support for e10s. r=smaug,billm.

This commit is contained in:
Mike Conley 2014-08-19 16:58:00 -04:00
parent caaf18a600
commit 72420563c2
6 changed files with 185 additions and 43 deletions

View File

@ -9,6 +9,21 @@ var FullScreen = {
delete this._fullScrToggler;
return this._fullScrToggler = document.getElementById("fullscr-toggler");
},
init: function() {
// called when we go into full screen, even if initiated by a web page script
window.addEventListener("fullscreen", this, true);
window.messageManager.addMessageListener("MozEnteredDomFullscreen", this);
if (window.fullScreen)
this.toggle();
},
uninit: function() {
window.messageManager.removeMessageListener("MozEnteredDomFullscreen", this);
this.cleanup();
},
toggle: function (event) {
var enterFS = window.fullScreen;
@ -95,9 +110,12 @@ var FullScreen = {
switch (event.type) {
case "activate":
if (document.mozFullScreen) {
this.showWarning(this.fullscreenDoc);
this.showWarning(this.fullscreenOrigin);
}
break;
case "fullscreen":
this.toggle(event);
break;
case "transitionend":
if (event.propertyName == "opacity")
this.cancelWarning();
@ -105,18 +123,33 @@ var FullScreen = {
}
},
enterDomFullscreen : function(event) {
receiveMessage: function(aMessage) {
if (aMessage.name == "MozEnteredDomFullscreen") {
// If we're a multiprocess browser, then the request to enter fullscreen
// did not bubble up to the root browser document - it stopped at the root
// of the content document. That means we have to kick off the switch to
// fullscreen here at the operating system level in the parent process
// ourselves.
let data = aMessage.data;
let browser = aMessage.target;
if (gMultiProcessBrowser && browser.getAttribute("remote") == "true") {
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.remoteFrameFullscreenChanged(browser, data.origin);
}
this.enterDomFullscreen(browser, data.origin);
}
},
enterDomFullscreen : function(aBrowser, aOrigin) {
if (!document.mozFullScreen)
return;
// However, if we receive a "MozEnteredDomFullScreen" event for a document
// which is not a subdocument of a currently active (ie. visible) browser
// or iframe, we know that we've switched to a different frame since the
// request to enter full-screen was made, so we should exit full-screen
// since the "full-screen document" isn't acutally visible.
if (!event.target.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell).isActive) {
// If we've received a fullscreen notification, we have to ensure that the
// element that's requesting fullscreen belongs to the browser that's currently
// active. If not, we exit fullscreen since the "full-screen document" isn't
// actually visible now.
if (gBrowser.selectedBrowser != aBrowser) {
document.mozCancelFullScreen();
return;
}
@ -136,7 +169,7 @@ var FullScreen = {
if (gFindBarInitialized)
gFindBar.close();
this.showWarning(event.target);
this.showWarning(aOrigin);
// Exit DOM full-screen mode upon open, close, or change tab.
gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
@ -178,7 +211,9 @@ var FullScreen = {
gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
if (!this.useLionFullScreen)
window.removeEventListener("activate", this);
this.fullscreenDoc = null;
window.messageManager
.broadcastAsyncMessage("DOMFullscreen:Cleanup");
}
},
@ -337,7 +372,7 @@ var FullScreen = {
// the permission manager can't handle (documents with URIs without a host).
// We simply require those to be approved every time instead.
let rememberCheckbox = document.getElementById("full-screen-remember-decision");
let uri = this.fullscreenDoc.nodePrincipal.URI;
let uri = BrowserUtils.makeURI(this.fullscreenOrigin);
if (!rememberCheckbox.hidden) {
if (rememberCheckbox.checked)
Services.perms.add(uri,
@ -370,27 +405,29 @@ var FullScreen = {
// If the document has been granted fullscreen, notify Gecko so it can resume
// any pending pointer lock requests, otherwise exit fullscreen; the user denied
// the fullscreen request.
if (isApproved)
Services.obs.notifyObservers(this.fullscreenDoc, "fullscreen-approved", "");
else
if (isApproved) {
gBrowser.selectedBrowser
.messageManager
.sendAsyncMessage("DOMFullscreen:Approved");
} else {
document.mozCancelFullScreen();
}
},
warningBox: null,
warningFadeOutTimeout: null,
fullscreenDoc: null,
// Shows the fullscreen approval UI, or if the domain has already been approved
// for fullscreen, shows a warning that the site has entered fullscreen for a short
// duration.
showWarning: function(targetDoc) {
showWarning: function(aOrigin) {
if (!document.mozFullScreen ||
!gPrefService.getBoolPref("full-screen-api.approval-required"))
return;
// Set the strings on the fullscreen approval UI.
this.fullscreenDoc = targetDoc;
let uri = this.fullscreenDoc.nodePrincipal.URI;
this.fullscreenOrigin = aOrigin;
let uri = BrowserUtils.makeURI(aOrigin);
let host = null;
try {
host = uri.host;

View File

@ -1287,17 +1287,7 @@ var gBrowserInit = {
if (Win7Features)
Win7Features.onOpenWindow();
// called when we go into full screen, even if initiated by a web page script
window.addEventListener("fullscreen", onFullScreen, true);
// Called when we enter DOM full-screen mode. Note we can already be in browser
// full-screen mode when we enter DOM full-screen mode.
window.addEventListener("MozEnteredDomFullscreen", onMozEnteredDomFullscreen, true);
if (window.fullScreen)
onFullScreen();
if (document.mozFullScreen)
onMozEnteredDomFullscreen();
FullScreen.init();
#ifdef MOZ_SERVICES_SYNC
// initialize the sync UI
@ -1428,7 +1418,7 @@ var gBrowserInit = {
gHistorySwipeAnimation.uninit();
FullScreen.cleanup();
FullScreen.uninit();
#ifdef MOZ_SERVICES_SYNC
gFxAccounts.uninit();
@ -2755,14 +2745,6 @@ function SwitchToMetro() {
#endif
}
function onFullScreen(event) {
FullScreen.toggle(event);
}
function onMozEnteredDomFullscreen(event) {
FullScreen.enterDomFullscreen(event);
}
function getWebNavigation()
{
return gBrowser.webNavigation;

View File

@ -578,3 +578,40 @@ if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
Cu.import("resource:///modules/translation/TranslationContentHandler.jsm");
trHandler = new TranslationContentHandler(global, docShell);
}
let DOMFullscreenHandler = {
_fullscreenDoc: null,
init: function() {
addMessageListener("DOMFullscreen:Approved", this);
addMessageListener("DOMFullscreen:CleanUp", this);
addEventListener("MozEnteredDomFullscreen", this);
},
receiveMessage: function(aMessage) {
switch(aMessage.name) {
case "DOMFullscreen:Approved": {
if (this._fullscreenDoc) {
Services.obs.notifyObservers(this._fullscreenDoc,
"fullscreen-approved",
"");
}
break;
}
case "DOMFullscreen:CleanUp": {
this._fullscreenDoc = null;
break;
}
}
},
handleEvent: function(aEvent) {
if (aEvent.type == "MozEnteredDomFullscreen") {
this._fullscreenDoc = aEvent.target;
sendAsyncMessage("MozEnteredDomFullscreen", {
origin: this._fullscreenDoc.nodePrincipal.origin,
});
}
}
};
DOMFullscreenHandler.init();

View File

@ -198,6 +198,7 @@
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/NodeFilterBinding.h"
#include "mozilla/dom/OwningNonNull.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/UndoManager.h"
#include "mozilla/dom/WebComponentsBinding.h"
#include "nsFrame.h"
@ -10543,8 +10544,9 @@ nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
}
// Returns true if the document is a direct child of a cross process parent
// mozbrowser iframe. This is the case when the document has a null parent,
// and its DocShell reports that it is a browser frame.
// mozbrowser iframe or TabParent. This is the case when the document has
// a null parent and its DocShell reports that it is a browser frame, or
// we can get a TabChild from it.
static bool
HasCrossProcessParent(nsIDocument* aDocument)
{
@ -10562,7 +10564,12 @@ HasCrossProcessParent(nsIDocument* aDocument)
if (!docShell) {
return false;
}
return docShell->GetIsBrowserOrApp();
TabChild* tabChild(TabChild::GetFrom(docShell));
if (!tabChild) {
return false;
}
return true;
}
static bool

View File

@ -375,6 +375,48 @@ addMessageListener("NetworkPrioritizer:AdjustPriority", (msg) => {
loadGroup.adjustPriority(msg.data.adjustment);
});
let DOMFullscreenManager = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
init: function() {
Services.obs.addObserver(this, "ask-parent-to-exit-fullscreen", false);
Services.obs.addObserver(this, "ask-parent-to-rollback-fullscreen", false);
addMessageListener("DOMFullscreen:ChildrenMustExit", () => {
let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
utils.exitFullscreen();
});
addEventListener("unload", () => {
Services.obs.removeObserver(this, "ask-parent-to-exit-fullscreen");
Services.obs.removeObserver(this, "ask-parent-to-rollback-fullscreen");
});
},
observe: function(aSubject, aTopic, aData) {
// Observer notifications are global, which means that these notifications
// might be coming from elements that are not actually children within this
// windows' content. We should ignore those. This will not be necessary once
// we fix bug 1053413 and stop using observer notifications for this stuff.
if (aSubject.defaultView.top !== content) {
return;
}
switch (aTopic) {
case "ask-parent-to-exit-fullscreen": {
sendAsyncMessage("DOMFullscreen:RequestExit");
break;
}
case "ask-parent-to-rollback-fullscreen": {
sendAsyncMessage("DOMFullscreen:RequestRollback");
break;
}
}
},
};
DOMFullscreenManager.init();
let AutoCompletePopup = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup]),

View File

@ -209,6 +209,8 @@
this.messageManager.addMessageListener("DocumentInserted", this);
this.messageManager.addMessageListener("FullZoomChange", this);
this.messageManager.addMessageListener("TextZoomChange", this);
this.messageManager.addMessageListener("DOMFullscreen:RequestExit", this);
this.messageManager.addMessageListener("DOMFullscreen:RequestRollback", this);
this.messageManager.loadFrameScript("chrome://global/content/browser-child.js", true);
if (this.hasAttribute("selectpopup")) {
@ -221,9 +223,17 @@
let RemoteController = Components.utils.import(jsm, {}).RemoteController;
this._controller = new RemoteController(this);
this.controllers.appendController(this._controller);
Services.obs.addObserver(this, "ask-children-to-exit-fullscreen", false);
]]>
</constructor>
<destructor>
<![CDATA[
Services.obs.removeObserver(this, "ask-children-to-exit-fullscreen");
]]>
</destructor>
<method name="receiveMessage">
<parameter name="aMessage"/>
<body><![CDATA[
@ -276,6 +286,20 @@
break;
}
case "DOMFullscreen:RequestExit": {
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.exitFullscreen();
break;
}
case "DOMFullscreen:RequestRollback": {
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
windowUtils.remoteFrameFullscreenReverted();
break;
}
default:
// Delegate to browser.xml.
return this._receiveMessage(aMessage);
@ -284,6 +308,19 @@
]]></body>
</method>
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body><![CDATA[
if (aTopic == "ask-children-to-exit-fullscreen") {
if (aSubject == window.document) {
this.messageManager.sendAsyncMessage("DOMFullscreen:ChildrenMustExit");
}
}
]]></body>
</method>
<!--
For out-of-process code, event.screen[XY] is relative to the
left/top of the content view. For in-process code,