--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -90,18 +90,19 @@ function promiseBrowserLoaded(browser, u
// use one. But we also need to make sure it stays alive until we're
// done with it, so thunk away a strong reference to keep it alive.
kungFuDeathGrip.add(listener);
browser.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
});
}
class ContentPage {
- constructor(remote = REMOTE_CONTENT_SCRIPTS) {
+ constructor(remote = REMOTE_CONTENT_SCRIPTS, extension = null) {
this.remote = remote;
+ this.extension = extension;
this.browserReady = this._initBrowser();
}
async _initBrowser() {
this.windowlessBrowser = Services.appShell.createWindowlessBrowser(true);
let system = Services.scriptSecurityManager.getSystemPrincipal();
@@ -118,16 +119,22 @@ class ContentPage {
win => win.document == chromeShell.document);
let chromeDoc = await promiseDocumentLoaded(chromeShell.document);
let browser = chromeDoc.createElement("browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
+ if (this.extension && this.extension.remote) {
+ browser.setAttribute("remote", "true");
+ browser.setAttribute("remoteType", "extension");
+ browser.sameProcessAsFrameLoader = this.extension.groupFrameLoader;
+ }
+
let awaitFrameLoader = Promise.resolve();
if (this.remote) {
awaitFrameLoader = promiseEvent(browser, "XULFrameLoaderCreated");
browser.setAttribute("remote", "true");
}
chromeDoc.documentElement.appendChild(browser);
@@ -683,16 +690,16 @@ var ExtensionTestUtils = {
get remoteContentScripts() {
return REMOTE_CONTENT_SCRIPTS;
},
set remoteContentScripts(val) {
REMOTE_CONTENT_SCRIPTS = !!val;
},
- loadContentPage(url, remote = undefined, redirectUrl = undefined) {
- let contentPage = new ContentPage(remote);
+ loadContentPage(url, {extension = undefined, remote = undefined, redirectUrl = undefined} = {}) {
+ let contentPage = new ContentPage(remote, extension && extension.extension);
return contentPage.loadURL(url, redirectUrl).then(() => {
return contentPage;
});
},
};
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
@@ -1,36 +1,52 @@
"use strict";
-XPCOMUtils.defineLazyGetter(this, "ExtensionManager", () => {
- const {ExtensionManager}
- = Cu.import("resource://gre/modules/ExtensionChild.jsm", {});
- return ExtensionManager;
-});
Cu.import("resource://gre/modules/ExtensionPermissions.jsm");
+Cu.import("resource://gre/modules/MessageChannel.jsm");
const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
-// Find the DOMWindowUtils for the background page for the given
-// extension (wrapper)
-function findWinUtils(extension) {
- let extensionChild = ExtensionManager.extensions.get(extension.extension.id);
- let bgwin = null;
- for (let view of extensionChild.views) {
- if (view.viewType == "background") {
- bgwin = view.contentWindow;
- }
+let extensionHandlers = new WeakSet();
+
+function frameScript() {
+ /* globals content */
+ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+ Cu.import("resource://gre/modules/MessageChannel.jsm");
+
+ let handle;
+ MessageChannel.addListener(this, "ExtensionTest:HandleUserInput", {
+ receiveMessage({name, data}) {
+ if (data) {
+ handle = content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .setHandlingUserInput(true);
+ } else if (handle) {
+ handle.destruct();
+ handle = null;
+ }
+ },
+ });
+}
+
+async function withHandlingUserInput(extension, fn) {
+ let {messageManager} = extension.extension.groupFrameLoader;
+
+ if (!extensionHandlers.has(extension)) {
+ messageManager.loadFrameScript(`data:,(${frameScript})(this)`, false);
+ extensionHandlers.add(extension);
}
- notEqual(bgwin, null, "Found background window for the test extension");
- return bgwin.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
+
+ await MessageChannel.sendMessage(messageManager, "ExtensionTest:HandleUserInput", true);
+ await fn();
+ await MessageChannel.sendMessage(messageManager, "ExtensionTest:HandleUserInput", false);
}
let sawPrompt = false;
let acceptPrompt = false;
const observer = {
observe(subject, topic, data) {
if (topic == "webextension-optional-permission-prompt") {
sawPrompt = true;
@@ -89,17 +105,16 @@ add_task(async function test_permissions
manifest: {
permissions: [...REQUIRED_PERMISSIONS, ...REQUIRED_ORIGINS],
optional_permissions: [...OPTIONAL_PERMISSIONS, ...OPTIONAL_ORIGINS],
},
useAddonManager: "permanent",
});
await extension.startup();
- let winUtils = findWinUtils(extension);
function call(method, arg) {
extension.sendMessage(method, arg);
return extension.awaitMessage(`${method}.result`);
}
let result = await call("getAll");
deepEqual(result.permissions, REQUIRED_PERMISSIONS);
@@ -132,42 +147,41 @@ add_task(async function test_permissions
let perm = OPTIONAL_PERMISSIONS[0];
result = await call("request", {permissions: [perm]});
equal(result.status, "error", "request() fails if not called from an event handler");
ok(/request may only be called from a user input handler/.test(result.message),
"error message for calling request() outside an event handler is reasonable");
result = await call("contains", {permissions: [perm]});
equal(result, false, "Permission requested outside an event handler was not granted");
- let userInputHandle = winUtils.setHandlingUserInput(true);
+ await withHandlingUserInput(extension, async () => {
+ result = await call("request", {permissions: ["notifications"]});
+ equal(result.status, "error", "request() for permission not in optional_permissions should fail");
+ ok(/since it was not declared in optional_permissions/.test(result.message),
+ "error message for undeclared optional_permission is reasonable");
- result = await call("request", {permissions: ["notifications"]});
- equal(result.status, "error", "request() for permission not in optional_permissions should fail");
- ok(/since it was not declared in optional_permissions/.test(result.message),
- "error message for undeclared optional_permission is reasonable");
-
- // Check request() when the prompt is canceled.
- acceptPrompt = false;
- result = await call("request", {permissions: [perm]});
- equal(result.status, "success", "request() returned cleanly");
- equal(result.result, false, "request() returned false for rejected permission");
+ // Check request() when the prompt is canceled.
+ acceptPrompt = false;
+ result = await call("request", {permissions: [perm]});
+ equal(result.status, "success", "request() returned cleanly");
+ equal(result.result, false, "request() returned false for rejected permission");
- result = await call("contains", {permissions: [perm]});
- equal(result, false, "Rejected permission was not granted");
+ result = await call("contains", {permissions: [perm]});
+ equal(result, false, "Rejected permission was not granted");
- // Call request() and accept the prompt
- acceptPrompt = true;
- let allOptional = {
- permissions: OPTIONAL_PERMISSIONS,
- origins: OPTIONAL_ORIGINS,
- };
- result = await call("request", allOptional);
- equal(result.status, "success", "request() returned cleanly");
- equal(result.result, true, "request() returned true for accepted permissions");
- userInputHandle.destruct();
+ // Call request() and accept the prompt
+ acceptPrompt = true;
+ let allOptional = {
+ permissions: OPTIONAL_PERMISSIONS,
+ origins: OPTIONAL_ORIGINS,
+ };
+ result = await call("request", allOptional);
+ equal(result.status, "success", "request() returned cleanly");
+ equal(result.result, true, "request() returned true for accepted permissions");
+ });
let allPermissions = {
permissions: [...REQUIRED_PERMISSIONS, ...OPTIONAL_PERMISSIONS],
origins: [...REQUIRED_ORIGINS_NORMALIZED, ...OPTIONAL_ORIGINS_NORMALIZED],
};
result = await call("getAll");
deepEqual(result, allPermissions, "getAll() returns required and runtime requested permissions");
@@ -235,27 +249,25 @@ add_task(async function test_startup() {
});
await extension1.startup();
await extension2.startup();
let perms = await extension1.awaitMessage("perms");
perms = await extension2.awaitMessage("perms");
- let winUtils = findWinUtils(extension1);
- let handle = winUtils.setHandlingUserInput(true);
- extension1.sendMessage(PERMS1);
- await extension1.awaitMessage("requested");
- handle.destruct();
+ await withHandlingUserInput(extension1, async () => {
+ extension1.sendMessage(PERMS1);
+ await extension1.awaitMessage("requested");
+ });
- winUtils = findWinUtils(extension2);
- handle = winUtils.setHandlingUserInput(true);
- extension2.sendMessage(PERMS2);
- await extension2.awaitMessage("requested");
- handle.destruct();
+ await withHandlingUserInput(extension2, async () => {
+ extension2.sendMessage(PERMS2);
+ await extension2.awaitMessage("requested");
+ });
// Restart everything, and force the permissions store to be
// re-read on startup
ExtensionPermissions._uninit();
await AddonTestUtils.promiseRestartManager();
await extension1.awaitStartup();
await extension2.awaitStartup();
@@ -318,57 +330,55 @@ add_task(async function test_alreadyGran
</head></html>`,
"page.js": pageScript,
},
});
await extension.startup();
- let winUtils = findWinUtils(extension);
- let handle = winUtils.setHandlingUserInput(true);
-
- let url = await extension.awaitMessage("ready");
- await ExtensionTestUtils.loadContentPage(url);
- await extension.awaitMessage("page-ready");
+ await withHandlingUserInput(extension, async () => {
+ let url = await extension.awaitMessage("ready");
+ await ExtensionTestUtils.loadContentPage(url, {extension});
+ await extension.awaitMessage("page-ready");
- async function checkRequest(arg, expectPrompt, msg) {
- sawPrompt = false;
- extension.sendMessage("request", arg);
- let result = await extension.awaitMessage("request.result");
- ok(result, "request() call succeeded");
- equal(sawPrompt, expectPrompt,
- `Got ${expectPrompt ? "" : "no "}permission prompt for ${msg}`);
- }
+ async function checkRequest(arg, expectPrompt, msg) {
+ sawPrompt = false;
+ extension.sendMessage("request", arg);
+ let result = await extension.awaitMessage("request.result");
+ ok(result, "request() call succeeded");
+ equal(sawPrompt, expectPrompt,
+ `Got ${expectPrompt ? "" : "no "}permission prompt for ${msg}`);
+ }
- await checkRequest({permissions: ["geolocation"]}, false,
- "required permission from manifest");
- await checkRequest({origins: ["http://required-host.com/"]}, false,
- "origin permission from manifest");
- await checkRequest({origins: ["http://host.required-domain.com/"]}, false,
- "wildcard origin permission from manifest");
+ await checkRequest({permissions: ["geolocation"]}, false,
+ "required permission from manifest");
+ await checkRequest({origins: ["http://required-host.com/"]}, false,
+ "origin permission from manifest");
+ await checkRequest({origins: ["http://host.required-domain.com/"]}, false,
+ "wildcard origin permission from manifest");
- await checkRequest({permissions: ["clipboardRead"]}, true,
- "optional permission");
- await checkRequest({permissions: ["clipboardRead"]}, false,
- "already granted optional permission");
+ await checkRequest({permissions: ["clipboardRead"]}, true,
+ "optional permission");
+ await checkRequest({permissions: ["clipboardRead"]}, false,
+ "already granted optional permission");
- await checkRequest({origins: ["http://optional-host.com/"]}, true,
- "optional origin");
- await checkRequest({origins: ["http://optional-host.com/"]}, false,
- "already granted origin permission");
+ await checkRequest({origins: ["http://optional-host.com/"]}, true,
+ "optional origin");
+ await checkRequest({origins: ["http://optional-host.com/"]}, false,
+ "already granted origin permission");
- await checkRequest({origins: ["http://*.optional-domain.com/"]}, true,
- "optional wildcard origin");
- await checkRequest({origins: ["http://*.optional-domain.com/"]}, false,
- "already granted optional wildcard origin");
- await checkRequest({origins: ["http://host.optional-domain.com/"]}, false,
- "host matching optional wildcard origin");
+ await checkRequest({origins: ["http://*.optional-domain.com/"]}, true,
+ "optional wildcard origin");
+ await checkRequest({origins: ["http://*.optional-domain.com/"]}, false,
+ "already granted optional wildcard origin");
+ await checkRequest({origins: ["http://host.optional-domain.com/"]}, false,
+ "host matching optional wildcard origin");
+ });
- handle.destruct();
await extension.unload();
});
// IMPORTANT: Do not change this list without review from a Web Extensions peer!
const GRANTED_WITHOUT_USER_PROMPT = [
"activeTab",
"alarms",
--- a/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_redirects.js
@@ -143,44 +143,44 @@ add_task(async function test_channel_red
add_task(async function test_content_redirect_to_non_accessible_resource() {
let extension = getExtension();
await extension.startup();
let redirectUrl = await extension.awaitMessage("redirectURI");
let url = `${gServerUrl}/redirect?redirect_uri=${redirectUrl}`;
let watcher = onModifyListener(url).then(channel => {
return onStopListener(channel);
});
- let contentPage = await ExtensionTestUtils.loadContentPage(url, undefined, "about:blank");
+ let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectURL: "about:blank"});
equal(contentPage.browser.documentURI.spec, "about:blank", `expected no redirect`);
equal(await watcher, url, "expected no redirect");
await contentPage.close();
await extension.unload();
});
// This test makes a request against a server that redirects with a 302.
add_task(async function test_content_302_redirect_to_extension() {
let extension = getExtension(true);
await extension.startup();
let redirectUrl = await extension.awaitMessage("redirectURI");
let url = `${gServerUrl}/redirect?redirect_uri=${redirectUrl}`;
- let contentPage = await ExtensionTestUtils.loadContentPage(url, undefined, redirectUrl);
+ let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`);
await contentPage.close();
await extension.unload();
});
// This test uses channel.redirectTo during http-on-modify to redirect to the
// moz-extension url.
add_task(async function test_content_channel_redirect_to_extension() {
let extension = getExtension(true);
await extension.startup();
let redirectUrl = await extension.awaitMessage("redirectURI");
let url = `${gServerUrl}/dummy?r=${Math.random()}`;
onModifyListener(url, redirectUrl);
- let contentPage = await ExtensionTestUtils.loadContentPage(url, undefined, redirectUrl);
+ let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`);
await contentPage.close();
await extension.unload();
});
// This test makes a request against a server and tests webrequest. Currently
// disabled due to NS_BINDING_ABORTED happening.
add_task(async function test_extension_302_redirect() {
@@ -200,17 +200,17 @@ add_task(async function test_extension_3
}, {urls: ["<all_urls>", myuri]});
// send the extensions public uri to the test.
browser.test.sendMessage("redirectURI", exturi);
});
await extension.startup();
let redirectUrl = await extension.awaitMessage("redirectURI");
let completed = extension.awaitFinish("requestCompleted");
let url = `${gServerUrl}/redirect?r=${Math.random()}&redirect_uri=${redirectUrl}`;
- let contentPage = await ExtensionTestUtils.loadContentPage(url, undefined, redirectUrl);
+ let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
equal(contentPage.browser.documentURI.spec, redirectUrl, `expected content redirect`);
await completed;
await contentPage.close();
await extension.unload();
}).skip();
// This test makes a request and uses onBeforeRequet to redirect to moz-ext.
// Currently disabled due to NS_BINDING_ABORTED happening.
@@ -234,14 +234,14 @@ add_task(async function test_extension_r
}, {urls: ["<all_urls>", myuri]});
// send the extensions public uri to the test.
browser.test.sendMessage("redirectURI", exturi);
});
await extension.startup();
let redirectUrl = await extension.awaitMessage("redirectURI");
let completed = extension.awaitFinish("requestCompleted");
let url = `${gServerUrl}/dummy?r=${Math.random()}`;
- let contentPage = await ExtensionTestUtils.loadContentPage(url, undefined, redirectUrl);
+ let contentPage = await ExtensionTestUtils.loadContentPage(url, {redirectUrl});
equal(contentPage.browser.documentURI.spec, redirectUrl, `expected redirect`);
await completed;
await contentPage.close();
await extension.unload();
}).skip();