Bug 1498293 - Ensure the webextension fallback window has a TabChild actor in content. r=ochameau

This patch remove the usage of `Services.appShell.createWindowlessBrowser` from the
webextension target actor (that runs in a child process when the extension is in oop-mode).

As a fallback window (needed when an extension doesn't have an extension page yet, e.g. while
the extension is being reloaded, or when the extension doesn't have a background page), the actor
is going to search for the window related to the XUL browser element created to connect into
the extension process.

If the extension runs in the child process (e.g. as it currently happens on all platforms supported
by Firefox Desktop), the TabParent/TabChild's tabId is used to identify the fallback window.

On the contrary, when the extension runs in the parent process (e.g. as it currently happens on
Firefox for Android), the XUL browser's ownerGlobal innerWindowID is used to identify the
fallback window.

Differential Revision: https://phabricator.services.mozilla.com/D8573

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Luca Greco 2018-11-15 19:00:44 +00:00
Родитель 7ea4ed8008
Коммит 7153f497a9
2 изменённых файлов: 34 добавлений и 47 удалений

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

@ -45,20 +45,28 @@ add_task(async function testWebExtensionsToolboxNoBackgroundPage() {
.getService(Ci.nsIEnvironment); .getService(Ci.nsIEnvironment);
const testScript = function() { const testScript = function() {
/* eslint-disable no-undef */ /* eslint-disable no-undef */
// This is webextension toolbox process. So we can't access mochitest framework.
const waitUntil = async function(predicate, interval = 10) {
if (await predicate()) {
return true;
}
return new Promise(resolve => {
toolbox.win.setTimeout(function() {
waitUntil(predicate, interval).then(() => resolve(true));
}, interval);
});
};
toolbox.selectTool("inspector").then(async inspector => { toolbox.selectTool("inspector").then(async inspector => {
const nodeActor = await inspector.walker.querySelector( let nodeActor;
inspector.walker.rootNode, "body");
if (!nodeActor) { dump(`Wait the fallback window to be fully loaded\n`);
throw new Error("nodeActor not found"); await waitUntil(async () => {
} nodeActor = await inspector.walker.querySelector(inspector.walker.rootNode, "h1");
return nodeActor && nodeActor.inlineTextChild;
if (!(nodeActor.inlineTextChild)) { });
throw new Error("inlineTextChild not found");
}
dump("Got a nodeActor with an inline text child\n"); dump("Got a nodeActor with an inline text child\n");
const expectedValue = "Your addon does not have any document opened yet."; const expectedValue = "Your addon does not have any document opened yet.";
const actualValue = nodeActor.inlineTextChild._form.nodeValue; const actualValue = nodeActor.inlineTextChild._form.nodeValue;

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

@ -76,14 +76,11 @@ const webExtensionTargetPrototype = extend({}, parentProcessTargetPrototype);
*/ */
webExtensionTargetPrototype.initialize = function(conn, chromeGlobal, prefix, addonId) { webExtensionTargetPrototype.initialize = function(conn, chromeGlobal, prefix, addonId) {
this.addonId = addonId; this.addonId = addonId;
this.chromeGlobal = chromeGlobal;
// Try to discovery an existent extension page to attach (which will provide the initial // Try to discovery an existent extension page to attach (which will provide the initial
// URL shown in the window tittle when the addon debugger is opened). // URL shown in the window tittle when the addon debugger is opened).
let extensionWindow = this._searchForExtensionWindow(); const extensionWindow = this._searchForExtensionWindow();
if (!extensionWindow) {
this._createFallbackWindow();
extensionWindow = this.fallbackWindow;
}
parentProcessTargetPrototype.initialize.call(this, conn, extensionWindow); parentProcessTargetPrototype.initialize.call(this, conn, extensionWindow);
this._chromeGlobal = chromeGlobal; this._chromeGlobal = chromeGlobal;
@ -154,33 +151,24 @@ webExtensionTargetPrototype.exit = function() {
// Private helpers. // Private helpers.
webExtensionTargetPrototype._createFallbackWindow = function() { webExtensionTargetPrototype._searchFallbackWindow = function() {
if (this.fallbackWindow) { if (this.fallbackWindow) {
// Skip if there is already an existent fallback window. // Skip if there is already an existent fallback window.
return; return this.fallbackWindow;
} }
// Create an empty hidden window as a fallback (e.g. the background page could be // Set and initialized the fallbackWindow (which initially is a empty
// not defined for the target add-on or not yet when the actor instance has been // about:blank browser), this window is related to a XUL browser element
// created). // specifically created for the devtools server and it is never used
this.fallbackWebNav = Services.appShell.createWindowlessBrowser(true); // or navigated anywhere else.
this.fallbackWindow = this.chromeGlobal.content;
this.fallbackWindow.location = "data:text/html,<h1>" + FALLBACK_DOC_MESSAGE;
// Save the reference to the fallback DOMWindow. return this.fallbackWindow;
this.fallbackWindow = this.fallbackWebNav.document.defaultView;
// Insert the fallback doc message.
this.fallbackWindow.document.body.innerText = FALLBACK_DOC_MESSAGE;
}; };
webExtensionTargetPrototype._destroyFallbackWindow = function() { webExtensionTargetPrototype._destroyFallbackWindow = function() {
if (this.fallbackWebNav) { if (this.fallbackWindow) {
const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
// Explicitly close the fallback windowless browser to prevent it to leak
// (and to prevent it to freeze devtools xpcshell tests).
this.fallbackWebNav.loadURI("about:blank", 0, null, null, null, systemPrincipal);
this.fallbackWebNav.close();
this.fallbackWebNav = null;
this.fallbackWindow = null; this.fallbackWindow = null;
} }
}; };
@ -196,7 +184,7 @@ webExtensionTargetPrototype._searchForExtensionWindow = function() {
} }
} }
return undefined; return this._searchFallbackWindow();
}; };
// Customized ParentProcessTargetActor/BrowsingContextTargetActor hooks. // Customized ParentProcessTargetActor/BrowsingContextTargetActor hooks.
@ -214,9 +202,7 @@ webExtensionTargetPrototype._onDocShellDestroy = function(docShell) {
// If the destroyed docShell was the current docShell and the actor is // If the destroyed docShell was the current docShell and the actor is
// currently attached, switch to the fallback window // currently attached, switch to the fallback window
if (this.attached && docShell == this.docShell) { if (this.attached && docShell == this.docShell) {
// Creates a fallback window if it doesn't exist yet. this._changeTopLevelDocument(this._searchForExtensionWindow());
this._createFallbackWindow();
this._changeTopLevelDocument(this.fallbackWindow);
} }
}; };
@ -232,15 +218,8 @@ webExtensionTargetPrototype._attach = function() {
// subscribed to the child doc shell updates. // subscribed to the child doc shell updates.
if (!this.window || this.window.document.nodePrincipal.addonId !== this.addonId) { if (!this.window || this.window.document.nodePrincipal.addonId !== this.addonId) {
// Discovery an existent extension page to attach. // Discovery an existent extension page (or fallback window) to attach.
const extensionWindow = this._searchForExtensionWindow(); this._setWindow(this._searchForExtensionWindow());
if (!extensionWindow) {
this._createFallbackWindow();
this._setWindow(this.fallbackWindow);
} else {
this._setWindow(extensionWindow);
}
} }
// Call ParentProcessTargetActor's _attach to listen for any new/destroyed chrome // Call ParentProcessTargetActor's _attach to listen for any new/destroyed chrome