Bug 1405264 Part 2: Use startupData for new langpacks draft
authorAndrew Swan <aswan@mozilla.com>
Wed, 04 Oct 2017 13:29:20 -0700
changeset 676229 9cbbf9306f40636155db4ba4a5ecc84324c26769
parent 675110 30a0c62889e051dcd524665f90d00ae572bdf4bb
child 734885 66181d60141412ed60a5cef2e850ac5df53dc03e
push id83436
push useraswan@mozilla.com
push dateFri, 06 Oct 2017 21:07:01 +0000
bugs1405264
milestone58.0a1
Bug 1405264 Part 2: Use startupData for new langpacks Webextension-formatted langpacks now store their list of chrome registry resources in startupData so that those resources can be registered early in startup. MozReview-Commit-ID: 80eOiPKLlWu
toolkit/components/extensions/Extension.jsm
toolkit/mozapps/extensions/internal/XPIInstall.jsm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -308,16 +308,18 @@ this.ExtensionData = class {
     this.uuid = null;
     this.localeData = null;
     this._promiseLocales = null;
 
     this.apiNames = new Set();
     this.dependencies = new Set();
     this.permissions = new Set();
 
+    this.startupData = null;
+
     this.errors = [];
     this.warnings = [];
   }
 
   get builtinMessages() {
     return null;
   }
 
@@ -615,16 +617,34 @@ this.ExtensionData = class {
         dependencies.add(`${api}@experiments.addons.mozilla.org`);
       }
 
       // Normalize all patterns to contain a single leading /
       if (manifest.web_accessible_resources) {
         webAccessibleResources = manifest.web_accessible_resources
           .map(path => path.replace(/^\/*/, "/"));
       }
+    } else if (this.type == "langpack") {
+      // Compute the chrome resources to be registered for this langpack
+      // and stash them in startupData
+      const platform = AppConstants.platform;
+      const chromeEntries = [];
+      for (const [language, entry] of Object.entries(manifest.languages)) {
+        for (const [alias, path] of Object.entries(entry.chrome_resources || {})) {
+          if (typeof path === "string") {
+            chromeEntries.push(["locale", alias, language, path]);
+          } else if (platform in path) {
+            // If the path is not a string, it's an object with path per
+            // platform where the keys are taken from AppConstants.platform
+            chromeEntries.push(["locale", alias, language, path[platform]]);
+          }
+        }
+      }
+
+      this.startupData = {chromeEntries};
     }
 
     return {apiNames, dependencies, originPermissions, id, manifest, permissions,
             webAccessibleResources, type: this.type};
   }
 
   // Reads the extension's |manifest.json| file, and stores its
   // parsed contents in |this.manifest|.
@@ -1603,16 +1623,17 @@ this.Extension = class extends Extension
     }
     return this._optionalOrigins;
   }
 };
 
 this.Langpack = class extends ExtensionData {
   constructor(addonData, startupReason) {
     super(addonData.resourceURI);
+    this.startupData = addonData.startupData;
   }
 
   static getBootstrapScope(id, file) {
     return new LangpackBootstrapScope();
   }
 
   async promiseLocales(locale) {
     let locales = await StartupCache.locales
@@ -1656,40 +1677,38 @@ this.Langpack = class extends ExtensionD
     // Add any additional sources listed in the manifest
     if (data.manifest.sources) {
       for (const [sourceName, {base_path}] of Object.entries(data.manifest.sources)) {
         l10nRegistrySources[sourceName] = base_path;
       }
     }
 
     data.l10nRegistrySources = l10nRegistrySources;
-    data.chromeResources = this.getChromeResources(data.manifest);
 
     return data;
   }
 
   parseManifest() {
     return StartupCache.manifests.get(this.manifestCacheKey,
                                       () => this._parseManifest());
   }
 
   async startup(reason) {
+    this.chromeRegistryHandle = null;
+    if (this.startupData.chromeEntries.length > 0) {
+      const manifestURI = Services.io.newURI("manifest.json", null, this.rootURI);
+      this.chromeRegistryHandle =
+        aomStartup.registerChrome(manifestURI, this.startupData.chromeEntries);
+    }
+
     const data = await this.parseManifest();
     this.langpackId = data.langpackId;
     this.l10nRegistrySources = data.l10nRegistrySources;
 
     const languages = Object.keys(data.manifest.languages);
-    const manifestURI = Services.io.newURI("manifest.json", null, this.rootURI);
-
-    this.chromeRegistryHandle = null;
-    if (data.chromeResources.length > 0) {
-      this.chromeRegistryHandle =
-        aomStartup.registerChrome(manifestURI, data.chromeResources);
-    }
-
     resourceProtocol.setSubstitution(this.langpackId, this.rootURI);
 
     for (const [sourceName, basePath] of Object.entries(this.l10nRegistrySources)) {
       L10nRegistry.registerSource(new FileSource(
         `${sourceName}-${this.langpackId}`,
         languages,
         `resource://${this.langpackId}/${basePath}localization/{locale}/`
       ));
@@ -1702,28 +1721,9 @@ this.Langpack = class extends ExtensionD
     }
     if (this.chromeRegistryHandle) {
       this.chromeRegistryHandle.destruct();
       this.chromeRegistryHandle = null;
     }
 
     resourceProtocol.setSubstitution(this.langpackId, null);
   }
-
-  getChromeResources(manifest) {
-    const chromeEntries = [];
-    for (const [language, entry] of Object.entries(manifest.languages)) {
-      for (const [alias, path] of Object.entries(entry.chrome_resources || {})) {
-        if (typeof path === "string") {
-          chromeEntries.push(["locale", alias, language, path]);
-        } else {
-          // If the path is not a string, it's an object with path per platform
-          // where the keys are taken from AppConstants.platform
-          const platform = AppConstants.platform;
-          if (platform in path) {
-            chromeEntries.push(["locale", alias, language, path[platform]]);
-          }
-        }
-      }
-    }
-    return chromeEntries;
-  }
 };
--- a/toolkit/mozapps/extensions/internal/XPIInstall.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIInstall.jsm
@@ -358,16 +358,17 @@ async function loadManifestFromWebManife
   addon.internalName = null;
   addon.updateURL = bss.update_url;
   addon.updateKey = null;
   addon.optionsBrowserStyle = true;
   addon.optionsURL = null;
   addon.optionsType = null;
   addon.aboutURL = null;
   addon.dependencies = Object.freeze(Array.from(extension.dependencies));
+  addon.startupData = extension.startupData;
 
   if (manifest.options_ui) {
     // Store just the relative path here, the AddonWrapper getURL
     // wrapper maps this to a full URL.
     addon.optionsURL = manifest.options_ui.page;
     if (manifest.options_ui.open_in_tab)
       addon.optionsType = AddonManager.OPTIONS_TYPE_TAB;
     else