Bug 1484373: Part 9 - Move more extension page matching logic into policy service. r=mixedpuppy
authorKris Maglione <maglione.k@gmail.com>
Fri, 17 Aug 2018 22:30:17 -0700
changeset 488323 63c64335cec03809578607164b12ab6b067a52d2
parent 488322 1b774e058f1d93b737c4d37f4061e7e2f0449ff8
child 488324 091835164b689bd673182068ab7c896cba203449
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1484373
milestone63.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 1484373: Part 9 - Move more extension page matching logic into policy service. r=mixedpuppy Differential Revision: https://phabricator.services.mozilla.com/D3699
toolkit/components/extensions/ExtensionPolicyService.cpp
toolkit/components/extensions/extension-process-script.js
toolkit/components/extensions/mozIExtensionProcessScript.idl
--- a/toolkit/components/extensions/ExtensionPolicyService.cpp
+++ b/toolkit/components/extensions/ExtensionPolicyService.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/Promise-inl.h"
 #include "mozIExtensionProcessScript.h"
 #include "nsEscape.h"
 #include "nsGkAtoms.h"
 #include "nsIChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
+#include "nsGlobalWindowOuter.h"
 #include "nsILoadInfo.h"
 #include "nsIXULRuntime.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsXULAppAPI.h"
 #include "nsQueryObject.h"
 
 namespace mozilla {
@@ -442,16 +443,47 @@ ExtensionPolicyService::CheckRequest(nsI
   nsCOMPtr<nsIURI> uri;
   if (NS_FAILED(aChannel->GetURI(getter_AddRefs(uri)))) {
     return;
   }
 
   CheckContentScripts({uri.get(), loadInfo}, true);
 }
 
+static bool
+CheckParentFrames(nsPIDOMWindowOuter* aWindow, WebExtensionPolicy& aPolicy)
+{
+  nsCOMPtr<nsIURI> aboutAddons;
+  if (NS_FAILED(NS_NewURI(getter_AddRefs(aboutAddons), "about:addons"))) {
+    return false;
+  }
+
+  auto* piWin = aWindow;
+  while ((piWin = piWin->GetScriptableParentOrNull())) {
+    auto* win = nsGlobalWindowOuter::Cast(piWin);
+
+    auto* principal = BasePrincipal::Cast(win->GetPrincipal());
+    if (nsContentUtils::IsSystemPrincipal(principal)) {
+      // The add-on manager is a special case, since it contains extension
+      // options pages in same-type <browser> frames.
+      bool equals;
+      if (NS_SUCCEEDED(win->GetDocumentURI()->Equals(aboutAddons, &equals)) &&
+          equals) {
+        return true;
+      }
+    }
+
+    if (principal->AddonPolicy() != &aPolicy) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 // Checks a document, just after the document element has been inserted, for
 // matching content scripts or extension principals, and loads them if
 // necessary.
 void
 ExtensionPolicyService::CheckDocument(nsIDocument* aDocument)
 {
   nsCOMPtr<nsPIDOMWindowOuter> win = aDocument->GetWindow();
   if (win) {
@@ -464,17 +496,19 @@ ExtensionPolicyService::CheckDocument(ns
     if (win->GetDocumentURI()) {
       CheckContentScripts(win.get(), false);
     }
 
     nsIPrincipal* principal = aDocument->NodePrincipal();
 
     RefPtr<WebExtensionPolicy> policy = BasePrincipal::Cast(principal)->AddonPolicy();
     if (policy) {
-      ProcessScript().InitExtensionDocument(policy, aDocument);
+      bool privileged = IsExtensionProcess() && CheckParentFrames(win, *policy);
+
+      ProcessScript().InitExtensionDocument(policy, aDocument, privileged);
     }
   }
 }
 
 // Checks for loads of about:blank into new window globals, and loads any
 // matching content scripts. about:blank loads do not trigger document element
 // inserted events, so they're the only load type that are special cased this
 // way.
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -154,64 +154,16 @@ DocumentManager = {
   // into.
   initGlobal(global) {
     this.globals.set(global, new ExtensionGlobal(global));
     // eslint-disable-next-line mozilla/balanced-listeners
     global.addEventListener("unload", () => {
       this.globals.delete(global);
     });
   },
-
-  // Script loading
-
-  /**
-   * Checks that all parent frames for the given withdow either have the
-   * same add-on ID, or are special chrome-privileged documents such as
-   * about:addons or developer tools panels.
-   *
-   * @param {Window} window
-   *        The window to check.
-   * @param {string} addonId
-   *        The add-on ID to check.
-   * @returns {boolean}
-   */
-  checkParentFrames(window, addonId) {
-    while (window.parent !== window) {
-      window = window.parent;
-
-      let principal = window.document.nodePrincipal;
-
-      if (Services.scriptSecurityManager.isSystemPrincipal(principal)) {
-        // The add-on manager is a special case, since it contains extension
-        // options pages in same-type <browser> frames.
-        if (window.location.href === "about:addons") {
-          return true;
-        }
-      }
-
-      if (principal.addonId !== addonId) {
-        return false;
-      }
-    }
-
-    return true;
-  },
-
-  loadInto(policy, window) {
-    let extension = extensions.get(policy);
-    if (WebExtensionPolicy.isExtensionProcess && this.checkParentFrames(window, policy.id)) {
-      // We're in a top-level extension frame, or a sub-frame thereof,
-      // in the extension process. Inject the full extension page API.
-      ExtensionPageChild.initExtensionContext(extension, window);
-    } else {
-      // We're in a content sub-frame or not in the extension process.
-      // Only inject a minimal content script API.
-      ExtensionContent.initExtensionContext(extension, window);
-    }
-  },
 };
 
 ExtensionManager = {
   // WeakMap<WebExtensionPolicy, Map<string, WebExtensionContentScript>>
   registeredContentScripts: new DefaultWeakMap((policy) => new Map()),
 
   init() {
     MessageChannel.setupMessageManagers([Services.cpmm]);
@@ -373,19 +325,22 @@ ExtensionProcessScript.prototype = {
     let extGlobal = DocumentManager.globals.get(global);
     return extGlobal && extGlobal.getFrameData(force);
   },
 
   initExtension(extension) {
     return ExtensionManager.initExtensionPolicy(extension);
   },
 
-  initExtensionDocument(policy, doc) {
-    if (DocumentManager.globals.has(doc.defaultView.docShell.messageManager)) {
-      DocumentManager.loadInto(policy, doc.defaultView);
+  initExtensionDocument(policy, doc, privileged) {
+    let extension = extensions.get(policy);
+    if (privileged) {
+      ExtensionPageChild.initExtensionContext(extension, doc.defaultView);
+    } else {
+      ExtensionContent.initExtensionContext(extension, doc.defaultView);
     }
   },
 
   getExtensionChild(id) {
     let policy = WebExtensionPolicy.getByID(id);
     if (policy) {
       return extensions.get(policy);
     }
--- a/toolkit/components/extensions/mozIExtensionProcessScript.idl
+++ b/toolkit/components/extensions/mozIExtensionProcessScript.idl
@@ -11,10 +11,11 @@ webidl WebExtensionContentScript;
 [scriptable,uuid(6b09dc51-6caa-4ca7-9d6d-30c87258a630)]
 interface mozIExtensionProcessScript : nsISupports
 {
   void preloadContentScript(in nsISupports contentScript);
 
   Promise loadContentScript(in WebExtensionContentScript contentScript,
                             in mozIDOMWindow window);
 
-  void initExtensionDocument(in nsISupports extension, in Document doc);
+  void initExtensionDocument(in nsISupports extension, in Document doc,
+                             in bool privileged);
 };