Bug 1484373: Part 12 - Use policy object rather than keeping serialized extension data alive. r=mixedpuppy
authorKris Maglione <maglione.k@gmail.com>
Sat, 18 Aug 2018 00:10:53 -0700
changeset 481496 b88f5fa7dca4ba5cb92d9c905f3e52d043977498
parent 481495 812ae71cea4f9c4971a6ef2b40b64eb26ba1feeb
child 481497 10d2e81f3c8a157151bf5cca7133e65458a859eb
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmixedpuppy
bugs1484373
milestone63.0a1
Bug 1484373: Part 12 - Use policy object rather than keeping serialized extension data alive. r=mixedpuppy Differential Revision: https://phabricator.services.mozilla.com/D3702
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionChild.jsm
toolkit/components/extensions/extension-process-script.js
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1547,32 +1547,36 @@ class Extension extends ExtensionData {
     return this.manifest.content_security_policy;
   }
 
   get backgroundScripts() {
     return (this.manifest.background &&
             this.manifest.background.scripts);
   }
 
+  get optionalPermissions() {
+    return this.manifest.optional_permissions;
+  }
+
   // Representation of the extension to send to content
   // processes. This should include anything the content process might
   // need.
   serialize() {
     return {
       id: this.id,
       uuid: this.uuid,
       name: this.name,
       contentSecurityPolicy: this.contentSecurityPolicy,
       instanceId: this.instanceId,
       resourceURL: this.resourceURL,
       contentScripts: this.contentScripts,
       webAccessibleResources: this.webAccessibleResources.map(res => res.glob),
       whiteListedHosts: this.whiteListedHosts.patterns.map(pat => pat.pattern),
       permissions: this.permissions,
-      optionalPermissions: this.manifest.optional_permissions,
+      optionalPermissions: this.optionalPermissions,
     };
   }
 
   // Extended serialized data which is only needed in the extensions process,
   // and is never deserialized in web content processes.
   serializeExtended() {
     return {
       backgroundScripts: this.backgroundScripts,
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -589,39 +589,32 @@ class Messenger {
 
 // For test use only.
 var ExtensionManager = {
   extensions: new Map(),
 };
 
 // Represents a browser extension in the content process.
 class BrowserExtensionContent extends EventEmitter {
-  constructor(data) {
+  constructor(policy) {
     super();
 
-    this.data = data;
-    this.id = data.id;
-    this.uuid = data.uuid;
-    this.instanceId = data.instanceId;
+    this.policy = policy;
+    this.instanceId = policy.instanceId;
+    this.optionalPermissions = policy.optionalPermissions;
 
     if (WebExtensionPolicy.isExtensionProcess) {
       Object.assign(this, this.getSharedData("extendedData"));
     }
 
     this.MESSAGE_EMIT_EVENT = `Extension:EmitEvent:${this.instanceId}`;
     Services.cpmm.addMessageListener(this.MESSAGE_EMIT_EVENT, this);
 
-    this.webAccessibleResources = data.webAccessibleResources.map(res => new MatchGlob(res));
-    this.permissions = data.permissions;
-    this.optionalPermissions = data.optionalPermissions;
-
     let restrictSchemes = !this.hasPermission("mozillaAddons");
 
-    this.whiteListedHosts = new MatchPatternSet(data.whiteListedHosts, {restrictSchemes, ignorePath: true});
-
     this.apiManager = this.getAPIManager();
 
     this._manifest = null;
     this._localeData = null;
 
     this.baseURI = Services.io.newURI(`moz-extension://${this.uuid}/`);
     this.baseURL = this.baseURI.spec;
 
@@ -632,60 +625,75 @@ class BrowserExtensionContent extends Ev
     this.views = new Set();
 
     // Only used for devtools views.
     this.devtoolsViews = new Set();
 
     /* eslint-disable mozilla/balanced-listeners */
     this.on("add-permissions", (ignoreEvent, permissions) => {
       if (permissions.permissions.length > 0) {
+        let perms = new Set(this.policy.permissions);
         for (let perm of permissions.permissions) {
-          this.permissions.add(perm);
+          perms.add(perm);
         }
+        this.policy.permissions = perms;
       }
 
       if (permissions.origins.length > 0) {
         let patterns = this.whiteListedHosts.patterns.map(host => host.pattern);
 
-        this.whiteListedHosts = new MatchPatternSet([...patterns, ...permissions.origins],
-                                                    {restrictSchemes, ignorePath: true});
-      }
-
-      if (this.policy) {
-        this.policy.permissions = Array.from(this.permissions);
-        this.policy.allowedOrigins = this.whiteListedHosts;
+        this.policy.allowedOrigins =
+          new MatchPatternSet([...patterns, ...permissions.origins],
+                              {restrictSchemes, ignorePath: true});
       }
     });
 
     this.on("remove-permissions", (ignoreEvent, permissions) => {
       if (permissions.permissions.length > 0) {
+        let perms = new Set(this.policy.permissions);
         for (let perm of permissions.permissions) {
-          this.permissions.delete(perm);
+          perms.delete(perm);
         }
+        this.policy.permissions = perms;
       }
 
       if (permissions.origins.length > 0) {
         let origins = permissions.origins.map(
           origin => new MatchPattern(origin, {ignorePath: true}).pattern);
 
-        this.whiteListedHosts = new MatchPatternSet(
+        this.policy.allowedOrigins = new MatchPatternSet(
           this.whiteListedHosts.patterns
               .filter(host => !origins.includes(host.pattern)));
       }
-
-      if (this.policy) {
-        this.policy.permissions = Array.from(this.permissions);
-        this.policy.allowedOrigins = this.whiteListedHosts;
-      }
     });
     /* eslint-enable mozilla/balanced-listeners */
 
     ExtensionManager.extensions.set(this.id, this);
   }
 
+  get id() {
+    return this.policy.id;
+  }
+
+  get uuid() {
+    return this.policy.mozExtensionHostname;
+  }
+
+  get permissions() {
+    return new Set(this.policy.permissions);
+  }
+
+  get whiteListedHosts() {
+    return this.policy.allowedOrigins;
+  }
+
+  get webAccessibleResources() {
+    return this.policy.webAccessibleResources;
+  }
+
   getSharedData(key, value) {
     return sharedData.get(`extension/${this.id}/${key}`);
   }
 
   get localeData() {
     if (!this._localeData) {
       this._localeData = new LocaleData(this.getSharedData("locales"));
     }
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -38,27 +38,17 @@ function getData(extension, key = "") {
 
 // We need to avoid touching Services.appinfo here in order to prevent
 // the wrong version from being cached during xpcshell test startup.
 // eslint-disable-next-line mozilla/use-services
 const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
 const isContentProcess = appinfo.processType == appinfo.PROCESS_TYPE_CONTENT;
 
 var extensions = new DefaultWeakMap(policy => {
-  let data = policy.initData;
-  if (data.serialize) {
-    // We have an actual Extension rather than serialized extension
-    // data, so serialize it now to make sure we have consistent inputs
-    // between parent and child processes.
-    data = data.serialize();
-  }
-
-  let extension = new ExtensionChild.BrowserExtensionContent(data);
-  extension.policy = policy;
-  return extension;
+  return new ExtensionChild.BrowserExtensionContent(policy);
 });
 
 var ExtensionManager;
 
 class ExtensionGlobal {
   constructor(global) {
     this.global = global;
     this.global.addMessageListener("Extension:SetFrameData", this);
@@ -173,17 +163,18 @@ ExtensionManager = {
 
       for (let [scriptId, options] of getData(extension, "contentScripts") || []) {
         const script = new WebExtensionContentScript(policy, options);
         policy.registerContentScript(script);
         registeredContentScripts.set(scriptId, script);
       }
 
       policy.active = true;
-      policy.initData = extension;
+      policy.instanceId = extension.instanceId;
+      policy.optionalPermissions = extension.optionalPermissions;
     }
     return policy;
   },
 
   initExtension(data) {
     if (typeof data === "string") {
       data = getData({id: data});
     }