Bug 1585776 - Fix XPInstall content handler under Fission. r=nika
authorKris Maglione <maglione.k@gmail.com>
Fri, 18 Oct 2019 01:35:33 +0000
changeset 498080 93c2588d2799d630caeb38f1c834267e278972a9
parent 498079 382cd1dcf838c2cefe523daf61d817ac9bb5cf9e
child 498081 4dbfb65c5991c35b9539b3e121a91e69657a4f05
push id98267
push usermaglione.k@gmail.com
push dateFri, 18 Oct 2019 01:36:53 +0000
treeherderautoland@93c2588d2799 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1585776
milestone71.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1585776 - Fix XPInstall content handler under Fission. r=nika The XPInstall content handlers currently make all sorts of assumptions about the process they're being run in and the docShell their request is tied to. These assumptions don't hold under Fission, either now, when they may be called just after a process switch, or in the future, when they'll be called in the parent process. This patch updates them to operate on the BrowsingContext of the request, and to do their messaging via the process message manager, rather than via frame message managers. As a bonus, it also removes the existing hacks they had in place to handle the cases where frame message managers didn't work. Differential Revision: https://phabricator.services.mozilla.com/D48224
browser/components/enterprisepolicies/tests/browser/browser.ini
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/addonManager.js
toolkit/mozapps/extensions/amContentHandler.jsm
toolkit/mozapps/extensions/amInstallTrigger.jsm
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -35,17 +35,16 @@ skip-if = (verify && debug && (os == 'ma
 [browser_policy_disable_safemode.js]
 [browser_policy_disable_shield.js]
 [browser_policy_disable_telemetry.js]
 [browser_policy_display_bookmarks.js]
 [browser_policy_display_menu.js]
 [browser_policy_extensions.js]
 [browser_policy_downloads.js]
 [browser_policy_extensionsettings.js]
-skip-if = fission
 [browser_policy_firefoxhome.js]
 [browser_policy_override_postupdatepage.js]
 [browser_policy_passwordmanager.js]
 [browser_policy_search_engine.js]
 [browser_policy_searchbar.js]
 [browser_policy_set_homepage.js]
 [browser_policy_support_menu.js]
 [browser_policy_websitefilter.js]
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2328,17 +2328,28 @@ var AddonManagerInternal = {
           topBrowser,
           aInstallingPrincipal.URI,
           aInstall
         );
         return;
       } else if (
         aInstallingPrincipal.isNullPrincipal ||
         !aBrowser.contentPrincipal ||
-        !aInstallingPrincipal.subsumes(aBrowser.contentPrincipal) ||
+        // When we attempt to handle an XPI load immediately after a
+        // process switch, the DocShell it's being loaded into will have
+        // a null principal, since it won't have been initialized yet.
+        // Allowing installs in this case is relatively safe, since
+        // there isn't much to gain by spoofing an install request from
+        // a null principal in any case. This exception can be removed
+        // once content handlers are triggered by DocumentChannel in the
+        // parent process.
+        !(
+          aBrowser.contentPrincipal.isNullPrincipal ||
+          aInstallingPrincipal.subsumes(aBrowser.contentPrincipal)
+        ) ||
         !this.isInstallAllowedByPolicy(
           aInstallingPrincipal,
           aInstall,
           false /* explicit */
         )
       ) {
         aInstall.cancel();
 
--- a/toolkit/mozapps/extensions/addonManager.js
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -60,21 +60,22 @@ var gSingleton = null;
 var AddonManager, AddonManagerPrivate;
 function amManager() {
   ({ AddonManager, AddonManagerPrivate } = ChromeUtils.import(
     "resource://gre/modules/AddonManager.jsm"
   ));
 
   Services.mm.loadFrameScript(CHILD_SCRIPT, true, true);
   Services.mm.addMessageListener(MSG_INSTALL_ENABLED, this);
-  Services.mm.addMessageListener(MSG_INSTALL_ADDON, this);
   Services.mm.addMessageListener(MSG_PROMISE_REQUEST, this);
   Services.mm.addMessageListener(MSG_INSTALL_CLEANUP, this);
   Services.mm.addMessageListener(MSG_ADDON_EVENT_REQ, this);
 
+  Services.ppmm.addMessageListener(MSG_INSTALL_ADDON, this);
+
   Services.obs.addObserver(this, "message-manager-close");
   Services.obs.addObserver(this, "message-manager-disconnect");
 
   AddonManager.webAPI.setEventHandler(this.sendEvent);
 
   // Needed so receiveMessage can be called directly by JS callers
   this.wrappedJSObject = this;
 }
@@ -214,31 +215,33 @@ amManager.prototype = {
   receiveMessage(aMessage) {
     let payload = aMessage.data;
 
     switch (aMessage.name) {
       case MSG_INSTALL_ENABLED:
         return AddonManager.isInstallEnabled(payload.mimetype);
 
       case MSG_INSTALL_ADDON: {
+        let browser = payload.browsingContext.top.embedderElement;
+
         let callback = null;
         if (payload.callbackID != -1) {
-          let mm = aMessage.target.messageManager;
+          let mm = browser.messageManager;
           callback = {
             onInstallEnded(url, status) {
               mm.sendAsyncMessage(MSG_INSTALL_CALLBACK, {
                 callbackID: payload.callbackID,
                 url,
                 status,
               });
             },
           };
         }
 
-        return this.installAddonFromWebpage(payload, aMessage.target, callback);
+        return this.installAddonFromWebpage(payload, browser, callback);
       }
 
       case MSG_PROMISE_REQUEST: {
         if (
           !extensionsWebAPITesting &&
           separatePrivilegedMozillaWebContentProcess &&
           aMessage.target &&
           aMessage.target.remoteType != null &&
--- a/toolkit/mozapps/extensions/amContentHandler.jsm
+++ b/toolkit/mozapps/extensions/amContentHandler.jsm
@@ -28,30 +28,24 @@ amContentHandler.prototype = {
     }
 
     if (!(aRequest instanceof Ci.nsIChannel)) {
       throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
     }
 
     let uri = aRequest.URI;
 
-    let window = null;
-    let callbacks = aRequest.notificationCallbacks
-      ? aRequest.notificationCallbacks
-      : aRequest.loadGroup.notificationCallbacks;
-    if (callbacks) {
-      window = callbacks.getInterface(Ci.nsIDOMWindow);
-    }
-
     aRequest.cancel(Cr.NS_BINDING_ABORTED);
 
-    const { triggeringPrincipal } = aRequest.loadInfo;
+    let { loadInfo } = aRequest;
+    const { triggeringPrincipal } = loadInfo;
+    let browsingContext =
+      loadInfo.frameBrowsingContext || loadInfo.browsingContext;
 
     let sourceHost;
-
     try {
       sourceHost = triggeringPrincipal.URI.host;
     } catch (err) {
       // Ignore errors when retrieving the host for the principal (e.g. null principals raise
       // an NS_ERROR_FAILURE when principal.URI.host is accessed).
     }
 
     let install = {
@@ -59,45 +53,20 @@ amContentHandler.prototype = {
       hash: null,
       name: null,
       icon: null,
       mimetype: XPI_CONTENT_TYPE,
       triggeringPrincipal,
       callbackID: -1,
       method: "link",
       sourceHost,
+      browsingContext,
     };
 
-    if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
-      // When running in the main process this might be a frame inside an
-      // in-content UI page, walk up to find the first frame element in a chrome
-      // privileged document
-      let element = window.frameElement;
-      while (
-        element &&
-        !element.ownerDocument.nodePrincipal.isSystemPrincipal
-      ) {
-        element = element.ownerGlobal.frameElement;
-      }
-
-      if (element) {
-        let listener = Cc["@mozilla.org/addons/integration;1"].getService();
-        listener.wrappedJSObject.receiveMessage({
-          name: MSG_INSTALL_ADDON,
-          target: element,
-          data: install,
-        });
-        return;
-      }
-    }
-
-    // Fall back to sending through the message manager
-    let messageManager = window.docShell.messageManager;
-
-    messageManager.sendAsyncMessage(MSG_INSTALL_ADDON, install);
+    Services.cpmm.sendAsyncMessage(MSG_INSTALL_ADDON, install);
   },
 
   classID: Components.ID("{7beb3ba8-6ec3-41b4-b67c-da89b8518922}"),
   QueryInterface: ChromeUtils.generateQI([Ci.nsIContentHandler]),
 
   log(aMsg) {
     let msg = "amContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg);
     Services.console.logStringMessage(msg);
--- a/toolkit/mozapps/extensions/amInstallTrigger.jsm
+++ b/toolkit/mozapps/extensions/amInstallTrigger.jsm
@@ -66,43 +66,19 @@ RemoteMediator.prototype = {
   },
 
   install(install, principal, callback, window) {
     let callbackID = this._addCallback(callback);
 
     install.mimetype = XPINSTALL_MIMETYPE;
     install.triggeringPrincipal = principal;
     install.callbackID = callbackID;
-
-    if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
-      // When running in the main process this might be a frame inside an
-      // in-content UI page, walk up to find the first frame element in a chrome
-      // privileged document
-      let element = window.frameElement;
-      while (
-        element &&
-        !element.ownerDocument.nodePrincipal.isSystemPrincipal
-      ) {
-        element = element.ownerGlobal.frameElement;
-      }
+    install.browsingContext = BrowsingContext.getFromWindow(window);
 
-      if (element) {
-        let listener = Cc["@mozilla.org/addons/integration;1"].getService();
-        return listener.wrappedJSObject.receiveMessage({
-          name: MSG_INSTALL_ADDON,
-          target: element,
-          data: install,
-        });
-      }
-    }
-
-    // Fall back to sending through the message manager
-    let messageManager = window.docShell.messageManager;
-
-    return messageManager.sendSyncMessage(MSG_INSTALL_ADDON, install)[0];
+    return Services.cpmm.sendSyncMessage(MSG_INSTALL_ADDON, install)[0];
   },
 
   _addCallback(callback) {
     if (!callback || typeof callback != "function") {
       return -1;
     }
 
     let callbackID = this._windowID + "-" + ++this._lastCallbackID;