Bug 1323845: Part 2c - Lazy init API global only when APIs are first loaded. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Tue, 09 Jan 2018 16:07:45 -0800
changeset 718299 545a27f5130be2e6b2fb1db470a169c2bf76cac5
parent 718298 3036170b873d6edbd4d270f5b2c75279688fb4b1
child 718300 2af921afb6bb3cdee920c7093d58caf09f8f4c4c
push id94869
push usermaglione.k@gmail.com
push dateWed, 10 Jan 2018 01:49:31 +0000
reviewersaswan
bugs1323845
milestone59.0a1
Bug 1323845: Part 2c - Lazy init API global only when APIs are first loaded. r?aswan MozReview-Commit-ID: B1bbiM0j5gT
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionCommon.jsm
toolkit/components/extensions/ExtensionPageChild.jsm
toolkit/components/extensions/ExtensionParent.jsm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -655,16 +655,17 @@ this.ExtensionData = class {
     }
 
     this.manifest = manifestData.manifest;
     this.apiNames = manifestData.apiNames;
     this.dependencies = manifestData.dependencies;
     this.permissions = manifestData.permissions;
     this.type = manifestData.type;
 
+    await this.apiManager.lazyInit();
     this.webAccessibleResources = manifestData.webAccessibleResources.map(res => new MatchGlob(res));
     this.whiteListedHosts = new MatchPatternSet(manifestData.originPermissions);
 
     return this.manifest;
   }
 
   localizeMessage(...args) {
     return this.localeData.localizeMessage(...args);
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -940,17 +940,16 @@ class SchemaAPIManager extends EventEmit
    *     "content" - A content process.
    *     "devtools" - A devtools process.
    *     "proxy" - A proxy script process.
    * @param {SchemaRoot} schema
    */
   constructor(processType, schema) {
     super();
     this.processType = processType;
-    this.global = this._createExtGlobal();
     this.schema = schema;
 
     this.modules = new Map();
     this.modulePaths = {children: new Map(), modules: new Set()};
     this.manifestKeys = new Map();
     this.eventModules = new DefaultMap(() => new Set());
 
     this._modulesJSONLoaded = false;
@@ -1178,16 +1177,18 @@ class SchemaAPIManager extends EventEmit
   loadModule(name) {
     let module = this.modules.get(name);
     if (module.loaded) {
       return this.global[name];
     }
 
     this._checkLoadModule(module, name);
 
+    this.initGlobal();
+
     Services.scriptloader.loadSubScript(module.url, this.global, "UTF-8");
 
     module.loaded = true;
 
     return this.global[name];
   }
   /**
    * aSynchronously loads an API module, if not already loaded, and
@@ -1205,16 +1206,17 @@ class SchemaAPIManager extends EventEmit
     }
     if (module.asyncLoaded) {
       return module.asyncLoaded;
     }
 
     this._checkLoadModule(module, name);
 
     module.asyncLoaded = ChromeUtils.compileScript(module.url).then(script => {
+      this.initGlobal();
       script.executeInGlobal(this.global);
 
       module.loaded = true;
 
       return this.global[name];
     });
 
     return module.asyncLoaded;
@@ -1259,17 +1261,17 @@ class SchemaAPIManager extends EventEmit
 
   _checkLoadModule(module, name) {
     if (!module) {
       throw new Error(`Module '${name}' does not exist`);
     }
     if (module.asyncLoaded) {
       throw new Error(`Module '${name}' currently being lazily loaded`);
     }
-    if (this.global[name]) {
+    if (this.global && this.global[name]) {
       throw new Error(`Module '${name}' conflicts with existing global property`);
     }
   }
 
 
   /**
    * Create a global object that is used as the shared global for all ext-*.js
    * scripts that are loaded via `loadScript`.
@@ -1307,16 +1309,22 @@ class SchemaAPIManager extends EventEmit
     XPCOMUtils.defineLazyModuleGetters(global, {
       ExtensionUtils: "resource://gre/modules/ExtensionUtils.jsm",
       XPCOMUtils: "resource://gre/modules/XPCOMUtils.jsm",
     });
 
     return global;
   }
 
+  initGlobal() {
+    if (!this.global) {
+      this.global = this._createExtGlobal();
+    }
+  }
+
   /**
    * Load an ext-*.js script. The script runs in its own scope, if it wishes to
    * share state with another script it can assign to the `global` variable. If
    * it wishes to communicate with this API manager, use `extensions`.
    *
    * @param {string} scriptUrl The URL of the ext-*.js script.
    */
   loadScript(scriptUrl) {
--- a/toolkit/components/extensions/ExtensionPageChild.jsm
+++ b/toolkit/components/extensions/ExtensionPageChild.jsm
@@ -68,32 +68,34 @@ var apiManager = new class extends Schem
   constructor() {
     super("addon", Schemas);
     this.initialized = false;
   }
 
   lazyInit() {
     if (!this.initialized) {
       this.initialized = true;
+      this.initGlobal();
       for (let [/* name */, value] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCRIPTS_ADDON)) {
         this.loadScript(value);
       }
     }
   }
 }();
 
 var devtoolsAPIManager = new class extends SchemaAPIManager {
   constructor() {
     super("devtools", Schemas);
     this.initialized = false;
   }
 
   lazyInit() {
     if (!this.initialized) {
       this.initialized = true;
+      this.initGlobal();
       for (let [/* name */, value] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_EXTENSION_SCRIPTS_DEVTOOLS)) {
         this.loadScript(value);
       }
     }
   }
 }();
 
 class ExtensionBaseContextChild extends BaseContext {
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -130,16 +130,17 @@ let apiManager = new class extends Schem
       scriptURLs.push(value);
     }
 
     let promise = (async () => {
       let scripts = await Promise.all(scriptURLs.map(url => ChromeUtils.compileScript(url)));
 
       this.initModuleData(await modulesPromise);
 
+      this.initGlobal();
       for (let script of scripts) {
         script.executeInGlobal(this.global);
       }
 
       // Load order matters here. The base manifest defines types which are
       // extended by other schemas, so needs to be loaded first.
       return Schemas.load(BASE_SCHEMA, AppConstants.DEBUG).then(() => {
         let promises = [];