Bug 1450388 Part 2: Expose startupData to webextensions r=kmag
authorAndrew Swan <aswan@mozilla.com>
Wed, 21 Mar 2018 10:22:13 -0700
changeset 412437 d5e58b02d335729e2e2097c67cc7ad802d565fbd
parent 412436 bcacd467f8671ceebc903dc8018256e178393f58
child 412438 b40f4cad613beb0d18abc47748d481a64fcd596e
push id62396
push useraswan@mozilla.com
push dateMon, 09 Apr 2018 19:28:41 +0000
treeherderautoland@b40f4cad613b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1450388
milestone61.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 1450388 Part 2: Expose startupData to webextensions r=kmag MozReview-Commit-ID: AbqR3hECLZe
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/test/xpcshell/test_ext_startupData.js
toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1158,16 +1158,17 @@ class Extension extends ExtensionData {
 
     if (addonData.cleanupFile) {
       Services.obs.addObserver(this, "xpcom-shutdown");
       this.cleanupFile = addonData.cleanupFile || null;
       delete addonData.cleanupFile;
     }
 
     this.addonData = addonData;
+    this.startupData = addonData.startupData || {};
     this.startupReason = startupReason;
 
     if (["ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(startupReason)) {
       StartupCache.clearAddonData(addonData.id);
     }
 
     this.remote = !WebExtensionPolicy.isExtensionProcess;
 
@@ -1352,16 +1353,20 @@ class Extension extends ExtensionData {
              this.addonData.temporarilyInstalled));
   }
 
   get experimentsAllowed() {
     return (AddonSettings.ALLOW_LEGACY_EXTENSIONS ||
             this.isPrivileged);
   }
 
+  saveStartupData() {
+    AddonManagerPrivate.setStartupData(this.id, this.startupData);
+  }
+
   async _parseManifest() {
     let manifest = await super.parseManifest();
     if (manifest && manifest.permissions.has("mozillaAddons") &&
         !this.isPrivileged) {
       Cu.reportError(`Stripping mozillaAddons permission from ${this.id}`);
       manifest.permissions.delete("mozillaAddons");
       let i = manifest.manifest.permissions.indexOf("mozillaAddons");
       if (i >= 0) {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_startupData.js
@@ -0,0 +1,40 @@
+"use strict";
+
+AddonTestUtils.init(this);
+AddonTestUtils.overrideCertDB();
+AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+// Tests that startupData is persisted and is available at startup
+add_task(async function test_startupData() {
+  await AddonTestUtils.promiseStartupManager();
+
+  let wrapper = ExtensionTestUtils.loadExtension({useAddonManager: "permanent"});
+  await wrapper.startup();
+
+  let {extension} = wrapper;
+
+  deepEqual(extension.startupData, {}, "startupData for a new extension defaults to empty object");
+
+  const DATA = {test: "i am some startup data"};
+  extension.startupData = DATA;
+  extension.saveStartupData();
+
+  await AddonTestUtils.promiseRestartManager();
+  await wrapper.startupPromise;
+
+  ({extension} = wrapper);
+  deepEqual(extension.startupData, DATA, "startupData is present on restart");
+
+  const DATA2 = {other: "this is different data"};
+  extension.startupData = DATA2;
+  extension.saveStartupData();
+
+  await AddonTestUtils.promiseRestartManager();
+  await wrapper.startupPromise;
+
+  ({extension} = wrapper);
+  deepEqual(extension.startupData, DATA2, "updated startupData is present on restart");
+
+  await wrapper.unload();
+  await AddonTestUtils.promiseShutdownManager();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -78,16 +78,17 @@ skip-if = true # bug 1315829
 [test_ext_runtime_sendMessage.js]
 [test_ext_runtime_sendMessage_errors.js]
 [test_ext_runtime_sendMessage_no_receiver.js]
 [test_ext_runtime_sendMessage_self.js]
 [test_ext_shutdown_cleanup.js]
 [test_ext_sandbox_var.js]
 [test_ext_schema.js]
 [test_ext_simple.js]
+[test_ext_startupData.js]
 [test_ext_startup_cache.js]
 skip-if = os == "android"
 [test_ext_startup_perf.js]
 [test_ext_storage.js]
 [test_ext_storage_content.js]
 [test_ext_storage_managed.js]
 skip-if = os == "android"
 [test_ext_storage_sync.js]
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -3116,16 +3116,36 @@ var AddonManagerPrivate = {
     return AddonManagerInternal._getProviderByName("XPIProvider")
                                .isTemporaryInstallID(extensionId);
   },
 
   isDBLoaded() {
     let provider = AddonManagerInternal._getProviderByName("XPIProvider");
     return provider ? provider.isDBLoaded : false;
   },
+
+  /**
+   * Sets startupData for the given addon.  The provided data will be stored
+   * in addonsStartup.json so it is available early during browser startup.
+   * Note that this file is read synchronously at startup, so startupData
+   * should be used with care.
+   *
+   * @param {string} aID
+   *         The id of the addon to save startup data for.
+   * @param {any} aData
+   *        The data to store.  Must be JSON serializable.
+   */
+  setStartupData(aID, aData) {
+    if (!gStarted)
+      throw Components.Exception("AddonManager is not initialized",
+                                 Cr.NS_ERROR_NOT_INITIALIZED);
+
+    AddonManagerInternal._getProviderByName("XPIProvider")
+                        .setStartupData(aID, aData);
+  },
 };
 
 /**
  * This is the public API that UI and developers should be calling. All methods
  * just forward to AddonManagerInternal.
  * @class
  */
 var AddonManager = {
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1292,17 +1292,19 @@ class XPIState {
     if (aDBAddon.type == "theme") {
       this.enabled = aDBAddon.internalName == DEFAULT_SKIN;
     } else {
       this.enabled = aDBAddon.visible && !aDBAddon.disabled;
     }
 
     this.version = aDBAddon.version;
     this.type = aDBAddon.type;
-    this.startupData = aDBAddon.startupData;
+    if (aDBAddon.startupData) {
+      this.startupData = aDBAddon.startupData;
+    }
 
     this.bootstrapped = !!aDBAddon.bootstrap;
     if (this.bootstrapped) {
       this.hasEmbeddedWebExtension = aDBAddon.hasEmbeddedWebExtension;
       this.dependencies = aDBAddon.dependencies;
       this.runInSafeMode = canRunInSafeMode(aDBAddon);
     }
 
@@ -3446,16 +3448,33 @@ var XPIProvider = {
     // Notify providers that a new theme has been enabled.
     if (isTheme(addon.type))
       AddonManagerPrivate.notifyAddonChanged(addon.id, addon.type, false);
 
     return addon.wrapper;
   },
 
   /**
+   * Sets startupData for the given addon.  The provided data will be stored
+   * in addonsStartup.json so it is available early during browser startup.
+   * Note that this file is read synchronously at startup, so startupData
+   * should be used with care.
+   *
+   * @param {string} aID
+   *         The id of the addon to save startup data for.
+   * @param {any} aData
+   *        The data to store.  Must be JSON serializable.
+   */
+  setStartupData(aID, aData) {
+    let state = XPIStates.findAddon(aID);
+    state.startupData = aData;
+    XPIStates.save();
+  },
+
+  /**
    * Returns an Addon corresponding to an instance ID.
    * @param aInstanceID
    *        An Addon Instance ID
    * @return {Promise}
    * @resolves The found Addon or null if no such add-on exists.
    * @rejects  Never
    * @throws if the aInstanceID argument is not specified
    */