Bug 1450388 Part 2: Expose startupData to webextensions
MozReview-Commit-ID: AbqR3hECLZe
--- 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
@@ -1290,17 +1290,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);
}
@@ -3444,16 +3446,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
*/