Bug 1368152: Part 2 - Move extension policy registration out of ExtensionManagement.jsm. r?aswan,mixedpuppy draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 26 May 2017 12:04:18 -0700
changeset 585258 924e88fc5a61a52babefed74e66c1bcaa8c89fee
parent 585257 5bf094cfa9a5b20d3661434d1a1352a5c98635b2
child 585259 0450f429817e5a8a8a8ecd11c768f04878dae929
push id61083
push usermaglione.k@gmail.com
push dateFri, 26 May 2017 19:19:13 +0000
reviewersaswan, mixedpuppy
bugs1368152
milestone55.0a1
Bug 1368152: Part 2 - Move extension policy registration out of ExtensionManagement.jsm. r?aswan,mixedpuppy MozReview-Commit-ID: Ls1ZvJLcjlR
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionManagement.jsm
toolkit/components/extensions/extension-process-script.js
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -72,24 +72,31 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
                                   "resource://gre/modules/Preferences.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "require",
                                   "resource://devtools/shared/Loader.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
                                   "resource://gre/modules/Schemas.jsm");
 
-Cu.import("resource://gre/modules/ExtensionManagement.jsm");
+XPCOMUtils.defineLazyGetter(
+  this, "processScript",
+  () => Cc["@mozilla.org/webextensions/extension-process-script;1"]
+          .getService().wrappedJSObject);
+
 Cu.import("resource://gre/modules/ExtensionParent.jsm");
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidGen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
+                                      "extensions.webextensions.remote", false);
+
 var {
   GlobalManager,
   ParentAPIManager,
   apiManager: Management,
 } = ExtensionParent;
 
 const {
   classifyPermission,
@@ -701,17 +708,17 @@ this.Extension = class extends Extension
 
     this.addonData = addonData;
     this.startupReason = startupReason;
 
     if (["ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(startupReason)) {
       StartupCache.clearAddonData(addonData.id);
     }
 
-    this.remote = ExtensionManagement.useRemoteWebExtensions;
+    this.remote = useRemoteWebExtensions;
 
     if (this.remote && processCount !== 1) {
       throw new Error("Out-of-process WebExtensions are not supported with multiple child processes");
     }
     // This is filled in the first time an extension child is created.
     this.parentMessageManager = null;
 
     this.id = addonData.id;
@@ -905,23 +912,16 @@ this.Extension = class extends Extension
       Services.obs.addObserver(observer, "message-manager-close");
       Services.obs.addObserver(observer, "message-manager-disconnect");
 
       ppmm.broadcastAsyncMessage(msg, data);
     });
   }
 
   runManifest(manifest) {
-    let resources = [];
-    if (manifest.web_accessible_resources) {
-      resources = manifest.web_accessible_resources.map(path => path.replace(/^\/*/, "/"));
-    }
-
-    this.webAccessibleResources = resources.map(res => new MatchGlob(res));
-
     let promises = [];
     for (let directive in manifest) {
       if (manifest[directive] !== null) {
         promises.push(Management.emit(`manifest_${directive}`, directive, this, manifest));
 
         promises.push(Management.asyncEmitManifestEntry(this, directive));
       }
     }
@@ -971,24 +971,19 @@ this.Extension = class extends Extension
   }
 
   startup() {
     this.startupPromise = this._startup();
     return this.startupPromise;
   }
 
   async _startup() {
-    this.started = false;
-
     try {
       let [, perms] = await Promise.all([this.loadManifest(), ExtensionPermissions.get(this)]);
 
-      ExtensionManagement.startupExtension(this.uuid, this.addonData.resourceURI, this);
-      this.started = true;
-
       if (!this.hasShutdown) {
         await this.initLocale();
       }
 
       if (this.errors.length) {
         return Promise.reject({errors: this.errors});
       }
 
@@ -1004,34 +999,41 @@ this.Extension = class extends Extension
       }
       if (perms.origins.length > 0) {
         let patterns = this.whiteListedHosts.patterns.map(host => host.pattern);
 
         this.whiteListedHosts = new MatchPatternSet([...patterns, ...perms.origins],
                                                     {ignorePath: true});
       }
 
+      let resources = (this.manifest.web_accessible_resources || [])
+          .map(path => path.replace(/^\/*/, "/"));
+
+      this.webAccessibleResources = resources.map(res => new MatchGlob(res));
+
+
+      this.policy = processScript.initExtension(this.serialize(), this);
+
       // The "startup" Management event sent on the extension instance itself
       // is emitted just before the Management "startup" event,
       // and it is used to run code that needs to be executed before
       // any of the "startup" listeners.
       this.emit("startup", this);
       Management.emit("startup", this);
 
       await this.runManifest(this.manifest);
 
       Management.emit("ready", this);
       this.emit("ready");
     } catch (e) {
       dump(`Extension error: ${e.message} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`);
       Cu.reportError(e);
 
-      if (this.started) {
-        this.started = false;
-        ExtensionManagement.shutdownExtension(this);
+      if (this.policy) {
+        this.policy.active = false;
       }
 
       this.cleanupGeneratedFile();
 
       throw e;
     }
 
     this.startupPromise = null;
@@ -1063,32 +1065,32 @@ this.Extension = class extends Extension
       }
     } catch (e) {
       Cu.reportError(e);
     }
 
     this.shutdownReason = reason;
     this.hasShutdown = true;
 
-    if (!this.started) {
+    if (!this.policy) {
       return;
     }
 
     if (this.cleanupFile ||
         ["ADDON_INSTALL", "ADDON_UNINSTALL", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(reason)) {
       StartupCache.clearAddonData(this.id);
     }
 
     let data = Services.ppmm.initialProcessData;
     data["Extension:Extensions"] = data["Extension:Extensions"].filter(e => e.id !== this.id);
 
     Services.ppmm.removeMessageListener(this.MESSAGE_EMIT_EVENT, this);
 
     if (!this.manifest) {
-      ExtensionManagement.shutdownExtension(this);
+      this.policy.active = false;
 
       this.cleanupGeneratedFile();
       return;
     }
 
     GlobalManager.uninit(this);
 
     for (let obj of this.onShutdown) {
@@ -1103,17 +1105,17 @@ this.Extension = class extends Extension
 
     Management.emit("shutdown", this);
     this.emit("shutdown");
 
     Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id});
 
     MessageChannel.abortResponses({extensionId: this.id});
 
-    ExtensionManagement.shutdownExtension(this);
+    this.policy.active = false;
 
     return this.cleanupGeneratedFile();
   }
 
   observe(subject, topic, data) {
     if (topic === "xpcom-shutdown") {
       this.cleanupGeneratedFile();
     }
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -1,105 +1,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+/* exported ExtensionManagement */
+
 this.EXPORTED_SYMBOLS = ["ExtensionManagement"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
-                                  "resource:///modules/E10SUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
-  let {UUIDMap} = Cu.import("resource://gre/modules/Extension.jsm", {});
-  return UUIDMap;
-});
-
 /*
  * This file should be kept short and simple since it's loaded even
  * when no extensions are running.
  */
 
-function parseScriptOptions(options) {
-  return {
-    allFrames: options.all_frames,
-    matchAboutBlank: options.match_about_blank,
-    frameID: options.frame_id,
-    runAt: options.run_at,
-
-    matches: new MatchPatternSet(options.matches),
-    excludeMatches: new MatchPatternSet(options.exclude_matches || []),
-    includeGlobs: options.include_globs && options.include_globs.map(glob => new MatchGlob(glob)),
-    excludeGlobs: options.include_globs && options.exclude_globs.map(glob => new MatchGlob(glob)),
-
-    jsPaths: options.js || [],
-    cssPaths: options.css || [],
-  };
-}
-
-function getURLForExtension(id, path = "") {
-  let uuid = UUIDMap.get(id, false);
-  if (!uuid) {
-    Cu.reportError(`Called getURLForExtension on unmapped extension ${id}`);
-    return null;
-  }
-  return `moz-extension://${uuid}/${path}`;
-}
-
 let cacheInvalidated = 0;
 function onCacheInvalidate() {
   cacheInvalidated++;
 }
 Services.obs.addObserver(onCacheInvalidate, "startupcache-invalidate");
 
 var ExtensionManagement = {
   get cacheInvalidated() {
     return cacheInvalidated;
   },
 
   get isExtensionProcess() {
     return WebExtensionPolicy.isExtensionProcess;
   },
 
-  // Called when a new extension is loaded.
-  startupExtension(uuid, uri, extension) {
-    let policy = new WebExtensionPolicy({
-      id: extension.id,
-      mozExtensionHostname: uuid,
-      baseURL: uri.spec,
-
-      permissions: Array.from(extension.permissions),
-      allowedOrigins: extension.whiteListedHosts,
-      webAccessibleResources: extension.webAccessibleResources || [],
-
-      contentSecurityPolicy: extension.manifest.content_security_policy,
-
-      localizeCallback: extension.localize.bind(extension),
-
-      backgroundScripts: (extension.manifest.background &&
-                          extension.manifest.background.scripts),
-
-      contentScripts: (extension.manifest.content_scripts || []).map(parseScriptOptions),
-    });
-
-    extension.policy = policy;
-    policy.active = true;
+  getURLForExtension(id, path = "") {
+    let policy = WebExtensionPolicy.getByID(id);
+    if (!policy) {
+      Cu.reportError(`Called getURLForExtension on unmapped extension ${id}`);
+      return null;
+    }
+    return policy.getURL(path);
   },
-
-  // Called when an extension is unloaded.
-  shutdownExtension(extension) {
-    extension.policy.active = false;
-  },
-
-  getURLForExtension,
 };
-
-XPCOMUtils.defineLazyPreferenceGetter(ExtensionManagement, "useRemoteWebExtensions",
-                                      "extensions.webextensions.remote", false);
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -289,44 +289,50 @@ ExtensionManager = {
         procData["Extension:Schemas"] = new Map();
       }
       this.schemaJSON = procData["Extension:Schemas"];
 
       Services.cpmm.addMessageListener("Schema:Add", this);
     }
   },
 
-  initExtension(data) {
-    let policy;
-    if (isContentProcess) {
+  initExtensionPolicy(data, extension) {
+    let policy = WebExtensionPolicy.getByID(data.id);
+    if (!policy) {
+      let localizeCallback = (
+        extension ? extension.localize.bind(extension)
+                  : str => extensions.get(policy).localize(str));
+
       policy = new WebExtensionPolicy({
         id: data.id,
         mozExtensionHostname: data.uuid,
         baseURL: data.resourceURL,
 
         permissions: Array.from(data.permissions),
         allowedOrigins: new MatchPatternSet(data.whiteListedHosts),
         webAccessibleResources: data.webAccessibleResources.map(host => new MatchGlob(host)),
 
         contentSecurityPolicy: data.manifest.content_security_policy,
 
-        localizeCallback: str => extensions.get(policy).localize(str),
+        localizeCallback,
 
         backgroundScripts: (data.manifest.background &&
                             data.manifest.background.scripts),
 
         contentScripts: (data.manifest.content_scripts || []).map(parseScriptOptions),
       });
 
       policy.active = true;
-    } else {
-      policy = WebExtensionPolicy.getByID(data.id);
+      policy.initData = data;
     }
+    return policy;
+  },
 
-    policy.initData = data;
+  initExtension(data) {
+    let policy = this.initExtensionPolicy(data);
 
     DocumentManager.initExtension(policy);
   },
 
   receiveMessage({name, data}) {
     switch (name) {
       case "Extension:Startup": {
         this.initExtension(data);
@@ -370,16 +376,22 @@ function ExtensionProcessScript() {
 }
 
 ExtensionProcessScript.singleton = null;
 
 ExtensionProcessScript.prototype = {
   classID: Components.ID("{21f9819e-4cdf-49f9-85a0-850af91a5058}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIExtensionProcessScript]),
 
+  get wrappedJSObject() { return this; },
+
+  initExtension(data) {
+    return ExtensionManager.initExtensionPolicy(data);
+  },
+
   initExtensionDocument(policy, doc) {
     if (DocumentManager.globals.has(getMessageManager(doc.defaultView))) {
       DocumentManager.loadInto(policy, doc.defaultView);
     }
   },
 
   preloadContentScript(contentScript) {
     contentScripts.get(contentScript).preload();