Bug 1402066: Part 2 - Enable permissions tests in OOP mode. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 22 Sep 2017 15:50:12 -0700
changeset 669352 f9ab74ad8833f5287df8873637be8eb9539e67da
parent 669351 5613613bfb211ce814d76b0c730f6c33600ae619
child 670049 d84ca43d8972784c639b9f05b5bc830e51e76225
child 670064 f4a08a2871737f92880d659f0bbafb54a78bbc5d
child 670800 d20236f9b72b40166a0861bf9ed364eb4ad83ef7
push id81306
push usermaglione.k@gmail.com
push dateFri, 22 Sep 2017 22:51:05 +0000
reviewersaswan
bugs1402066
milestone58.0a1
Bug 1402066: Part 2 - Enable permissions tests in OOP mode. r?aswan MozReview-Commit-ID: 4Tv2HAaSV19
toolkit/components/extensions/ExtensionXPCShellUtils.jsm
toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
toolkit/components/extensions/test/xpcshell/test_ext_redirects.js
toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
toolkit/components/extensions/test/xpcshell/xpcshell.ini
--- 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();
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -69,11 +69,13 @@ skip-if = os == "android"
 [test_ext_storage_sync_crypto.js]
 skip-if = os == "android"
 [test_ext_storage_telemetry.js]
 skip-if = os == "android" # checking for telemetry needs to be updated: 1384923
 [test_ext_topSites.js]
 skip-if = os == "android"
 [test_native_manifests.js]
 skip-if = os == "android"
+[test_ext_permissions.js]
+skip-if = os == "android" # Bug 1350559
 [test_proxy_scripts.js]
 skip-if = os == "linux" # bug 1393940
 [test_proxy_scripts_results.js]
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -43,14 +43,12 @@ tags = webextensions in-process-webexten
 [test_ext_schemas_allowed_contexts.js]
 [test_ext_schemas_interactive.js]
 [test_ext_schemas_revoke.js]
 [test_ext_themes_supported_properties.js]
 [test_ext_unknown_permissions.js]
 [test_locale_converter.js]
 [test_locale_data.js]
 
-[test_ext_permissions.js]
-skip-if = os == "android" # Bug 1350559
 [test_ext_runtime_sendMessage_args.js]
 
 [include:xpcshell-common.ini]
 [include:xpcshell-content.ini]