summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js')
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js b/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
new file mode 100644
index 0000000000..720c7f539a
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_extensionSettingsStore.js
@@ -0,0 +1,1085 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ ExtensionSettingsStore:
+ "resource://gre/modules/ExtensionSettingsStore.sys.mjs",
+});
+
+const { createAppInfo, promiseShutdownManager, promiseStartupManager } =
+ AddonTestUtils;
+
+AddonTestUtils.init(this);
+
+// Allow for unsigned addons.
+AddonTestUtils.overrideCertDB();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+const ITEMS = {
+ key1: [
+ { key: "key1", value: "val1", id: "@first" },
+ { key: "key1", value: "val2", id: "@second" },
+ { key: "key1", value: "val3", id: "@third" },
+ ],
+ key2: [
+ { key: "key2", value: "val1-2", id: "@first" },
+ { key: "key2", value: "val2-2", id: "@second" },
+ { key: "key2", value: "val3-2", id: "@third" },
+ ],
+};
+const KEY_LIST = Object.keys(ITEMS);
+const TEST_TYPE = "myType";
+
+let callbackCount = 0;
+
+function initialValue(key) {
+ callbackCount++;
+ return `key:${key}`;
+}
+
+add_task(async function test_settings_store() {
+ await promiseStartupManager();
+
+ // Create an array of test framework extension wrappers to install.
+ let testExtensions = [
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@first" } },
+ },
+ }),
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@second" } },
+ },
+ }),
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@third" } },
+ },
+ }),
+ ];
+
+ for (let extension of testExtensions) {
+ await extension.startup();
+ }
+
+ // Create an array actual Extension objects which correspond to the
+ // test framework extension wrappers.
+ let extensions = testExtensions.map(extension => extension.extension);
+
+ let expectedCallbackCount = 0;
+
+ await Assert.rejects(
+ ExtensionSettingsStore.getLevelOfControl(1, TEST_TYPE, "key"),
+ /The ExtensionSettingsStore was accessed before the initialize promise resolved/,
+ "Accessing the SettingsStore before it is initialized throws an error."
+ );
+
+ // Initialize the SettingsStore.
+ await ExtensionSettingsStore.initialize();
+
+ // Add a setting for the second oldest extension, where it is the only setting for a key.
+ for (let key of KEY_LIST) {
+ let extensionIndex = 1;
+ let itemToAdd = ITEMS[key][extensionIndex];
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl with no settings set for a key."
+ );
+ let item = await ExtensionSettingsStore.addSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ itemToAdd.key,
+ itemToAdd.value,
+ initialValue
+ );
+ expectedCallbackCount++;
+ equal(
+ callbackCount,
+ expectedCallbackCount,
+ "initialValueCallback called the expected number of times."
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "Adding initial item for a key returns that item."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting returns correct item with only one item in the list."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl with only one item in the list."
+ );
+ ok(
+ ExtensionSettingsStore.hasSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ ),
+ "hasSetting returns the correct value when an extension has a setting set."
+ );
+ item = await ExtensionSettingsStore.getSetting(
+ TEST_TYPE,
+ key,
+ extensions[extensionIndex].id
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting with id returns correct item with only one item in the list."
+ );
+ }
+
+ // Add a setting for the oldest extension.
+ for (let key of KEY_LIST) {
+ let extensionIndex = 0;
+ let itemToAdd = ITEMS[key][extensionIndex];
+ let item = await ExtensionSettingsStore.addSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ itemToAdd.key,
+ itemToAdd.value,
+ initialValue
+ );
+ equal(
+ callbackCount,
+ expectedCallbackCount,
+ "initialValueCallback called the expected number of times."
+ );
+ equal(
+ item,
+ null,
+ "An older extension adding a setting for a key returns null"
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][1],
+ "getSetting returns correct item with more than one item in the list."
+ );
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_other_extensions",
+ "getLevelOfControl returns correct levelOfControl when another extension is in control."
+ );
+ item = await ExtensionSettingsStore.getSetting(
+ TEST_TYPE,
+ key,
+ extensions[extensionIndex].id
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting with id returns correct item with more than one item in the list."
+ );
+ }
+
+ // Reload the settings store to emulate a browser restart.
+ await ExtensionSettingsStore._reloadFile();
+
+ // Add a setting for the newest extension.
+ for (let key of KEY_LIST) {
+ let extensionIndex = 2;
+ let itemToAdd = ITEMS[key][extensionIndex];
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl for a more recent extension."
+ );
+ let item = await ExtensionSettingsStore.addSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ itemToAdd.key,
+ itemToAdd.value,
+ initialValue
+ );
+ equal(
+ callbackCount,
+ expectedCallbackCount,
+ "initialValueCallback called the expected number of times."
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "Adding item for most recent extension returns that item."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting returns correct item with more than one item in the list."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl when this extension is in control."
+ );
+ item = await ExtensionSettingsStore.getSetting(
+ TEST_TYPE,
+ key,
+ extensions[extensionIndex].id
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting with id returns correct item with more than one item in the list."
+ );
+ }
+
+ for (let extension of extensions) {
+ let items = await ExtensionSettingsStore.getAllForExtension(
+ extension.id,
+ TEST_TYPE
+ );
+ deepEqual(items, KEY_LIST, "getAllForExtension returns expected keys.");
+ }
+
+ // Attempting to remove a setting that has not been set should *not* throw an exception.
+ let removeResult = await ExtensionSettingsStore.removeSetting(
+ extensions[0].id,
+ "myType",
+ "unset_key"
+ );
+ equal(
+ removeResult,
+ null,
+ "Removing a setting that was not previously set returns null."
+ );
+
+ // Attempting to disable a setting that has not been set should throw an exception.
+ Assert.throws(
+ () =>
+ ExtensionSettingsStore.disable(extensions[0].id, "myType", "unset_key"),
+ /Cannot alter the setting for myType:unset_key as it does not exist/,
+ "disable rejects with an unset key."
+ );
+
+ // Attempting to enable a setting that has not been set should throw an exception.
+ Assert.throws(
+ () =>
+ ExtensionSettingsStore.enable(extensions[0].id, "myType", "unset_key"),
+ /Cannot alter the setting for myType:unset_key as it does not exist/,
+ "enable rejects with an unset key."
+ );
+
+ let expectedKeys = KEY_LIST;
+ // Disable the non-top item for a key.
+ for (let key of KEY_LIST) {
+ let extensionIndex = 0;
+ let item = await ExtensionSettingsStore.addSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key,
+ "new value",
+ initialValue
+ );
+ equal(
+ callbackCount,
+ expectedCallbackCount,
+ "initialValueCallback called the expected number of times."
+ );
+ equal(item, null, "Updating non-top item for a key returns null");
+ item = await ExtensionSettingsStore.disable(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(item, null, "Disabling non-top item for a key returns null.");
+ let allForExtension = await ExtensionSettingsStore.getAllForExtension(
+ extensions[extensionIndex].id,
+ TEST_TYPE
+ );
+ deepEqual(
+ allForExtension,
+ expectedKeys,
+ "getAllForExtension returns expected keys after a disable."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][2],
+ "getSetting returns correct item after a disable."
+ );
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_other_extensions",
+ "getLevelOfControl returns correct levelOfControl after disabling of non-top item."
+ );
+ }
+
+ // Re-enable the non-top item for a key.
+ for (let key of KEY_LIST) {
+ let extensionIndex = 0;
+ let item = await ExtensionSettingsStore.enable(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(item, null, "Enabling non-top item for a key returns null.");
+ let allForExtension = await ExtensionSettingsStore.getAllForExtension(
+ extensions[extensionIndex].id,
+ TEST_TYPE
+ );
+ deepEqual(
+ allForExtension,
+ expectedKeys,
+ "getAllForExtension returns expected keys after an enable."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][2],
+ "getSetting returns correct item after an enable."
+ );
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_other_extensions",
+ "getLevelOfControl returns correct levelOfControl after enabling of non-top item."
+ );
+ }
+
+ // Remove the non-top item for a key.
+ for (let key of KEY_LIST) {
+ let extensionIndex = 0;
+ let item = await ExtensionSettingsStore.removeSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(item, null, "Removing non-top item for a key returns null.");
+ expectedKeys = expectedKeys.filter(expectedKey => expectedKey != key);
+ let allForExtension = await ExtensionSettingsStore.getAllForExtension(
+ extensions[extensionIndex].id,
+ TEST_TYPE
+ );
+ deepEqual(
+ allForExtension,
+ expectedKeys,
+ "getAllForExtension returns expected keys after a removal."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][2],
+ "getSetting returns correct item after a removal."
+ );
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_other_extensions",
+ "getLevelOfControl returns correct levelOfControl after removal of non-top item."
+ );
+ ok(
+ !ExtensionSettingsStore.hasSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ key
+ ),
+ "hasSetting returns the correct value when an extension does not have a setting set."
+ );
+ }
+
+ for (let key of KEY_LIST) {
+ // Disable the top item for a key.
+ let item = await ExtensionSettingsStore.disable(
+ extensions[2].id,
+ TEST_TYPE,
+ key
+ );
+ deepEqual(
+ item,
+ ITEMS[key][1],
+ "Disabling top item for a key returns the new top item."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][1],
+ "getSetting returns correct item after a disable."
+ );
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[2].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after disabling of top item."
+ );
+
+ // Re-enable the top item for a key.
+ item = await ExtensionSettingsStore.enable(
+ extensions[2].id,
+ TEST_TYPE,
+ key
+ );
+ deepEqual(
+ item,
+ ITEMS[key][2],
+ "Re-enabling top item for a key returns the old top item."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][2],
+ "getSetting returns correct item after an enable."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[2].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after re-enabling top item."
+ );
+
+ // Remove the top item for a key.
+ item = await ExtensionSettingsStore.removeSetting(
+ extensions[2].id,
+ TEST_TYPE,
+ key
+ );
+ deepEqual(
+ item,
+ ITEMS[key][1],
+ "Removing top item for a key returns the new top item."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ ITEMS[key][1],
+ "getSetting returns correct item after a removal."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[2].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after removal of top item."
+ );
+
+ // Add a setting for the current top item.
+ let itemToAdd = { key, value: `new-${key}`, id: "@second" };
+ item = await ExtensionSettingsStore.addSetting(
+ extensions[1].id,
+ TEST_TYPE,
+ itemToAdd.key,
+ itemToAdd.value,
+ initialValue
+ );
+ equal(
+ callbackCount,
+ expectedCallbackCount,
+ "initialValueCallback called the expected number of times."
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "Updating top item for a key returns that item."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting returns correct item after updating."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after updating."
+ );
+
+ // Disable the last remaining item for a key.
+ let expectedItem = { key, initialValue: initialValue(key) };
+ // We're using the callback to set the expected value, so we need to increment the
+ // expectedCallbackCount.
+ expectedCallbackCount++;
+ item = await ExtensionSettingsStore.disable(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ deepEqual(
+ item,
+ expectedItem,
+ "Disabling last item for a key returns the initial value."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ expectedItem,
+ "getSetting returns the initial value after all are disabled."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after all are disabled."
+ );
+
+ // Re-enable the last remaining item for a key.
+ item = await ExtensionSettingsStore.enable(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ deepEqual(
+ item,
+ itemToAdd,
+ "Re-enabling last item for a key returns the old value."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(
+ item,
+ itemToAdd,
+ "getSetting returns expected value after re-enabling."
+ );
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controlled_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after re-enabling."
+ );
+
+ // Remove the last remaining item for a key.
+ item = await ExtensionSettingsStore.removeSetting(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ deepEqual(
+ item,
+ expectedItem,
+ "Removing last item for a key returns the initial value."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, key);
+ deepEqual(item, null, "getSetting returns null after all are removed.");
+ levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl after all are removed."
+ );
+
+ // Attempting to remove a setting that has had all extensions removed should *not* throw an exception.
+ removeResult = await ExtensionSettingsStore.removeSetting(
+ extensions[1].id,
+ TEST_TYPE,
+ key
+ );
+ equal(
+ removeResult,
+ null,
+ "Removing a setting that has had all extensions removed returns null."
+ );
+ }
+
+ // Test adding a setting with a value in callbackArgument.
+ let extensionIndex = 0;
+ let testKey = "callbackArgumentKey";
+ let callbackArgumentValue = Date.now();
+ // Add the setting.
+ let item = await ExtensionSettingsStore.addSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ testKey,
+ 1,
+ initialValue,
+ callbackArgumentValue
+ );
+ expectedCallbackCount++;
+ equal(
+ callbackCount,
+ expectedCallbackCount,
+ "initialValueCallback called the expected number of times."
+ );
+ // Remove the setting which should return the initial value.
+ let expectedItem = {
+ key: testKey,
+ initialValue: initialValue(callbackArgumentValue),
+ };
+ // We're using the callback to set the expected value, so we need to increment the
+ // expectedCallbackCount.
+ expectedCallbackCount++;
+ item = await ExtensionSettingsStore.removeSetting(
+ extensions[extensionIndex].id,
+ TEST_TYPE,
+ testKey
+ );
+ deepEqual(
+ item,
+ expectedItem,
+ "Removing last item for a key returns the initial value."
+ );
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, testKey);
+ deepEqual(item, null, "getSetting returns null after all are removed.");
+
+ item = await ExtensionSettingsStore.getSetting(TEST_TYPE, "not a key");
+ equal(
+ item,
+ null,
+ "getSetting returns a null item if the setting does not have any records."
+ );
+ let levelOfControl = await ExtensionSettingsStore.getLevelOfControl(
+ extensions[1].id,
+ TEST_TYPE,
+ "not a key"
+ );
+ equal(
+ levelOfControl,
+ "controllable_by_this_extension",
+ "getLevelOfControl returns correct levelOfControl if the setting does not have any records."
+ );
+
+ for (let extension of testExtensions) {
+ await extension.unload();
+ }
+
+ await promiseShutdownManager();
+});
+
+add_task(async function test_settings_store_setByUser() {
+ await promiseStartupManager();
+
+ // Create an array of test framework extension wrappers to install.
+ let testExtensions = [
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@first" } },
+ },
+ }),
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@second" } },
+ },
+ }),
+ ];
+
+ let type = "some_type";
+ let key = "some_key";
+
+ for (let extension of testExtensions) {
+ await extension.startup();
+ }
+
+ // Create an array actual Extension objects which correspond to the
+ // test framework extension wrappers.
+ let [one, two] = testExtensions.map(extension => extension.extension);
+ let initialCallback = () => "initial";
+
+ // Initialize the SettingsStore.
+ await ExtensionSettingsStore.initialize();
+
+ equal(
+ null,
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting is initially null"
+ );
+
+ let item = await ExtensionSettingsStore.addSetting(
+ one.id,
+ type,
+ key,
+ "one",
+ initialCallback
+ );
+ deepEqual(
+ { key, value: "one", id: one.id },
+ item,
+ "addSetting returns the first set item"
+ );
+
+ item = await ExtensionSettingsStore.addSetting(
+ two.id,
+ type,
+ key,
+ "two",
+ initialCallback
+ );
+ deepEqual(
+ { key, value: "two", id: two.id },
+ item,
+ "addSetting returns the second set item"
+ );
+
+ // a user-set selection reverts to precedence order when new
+ // extension sets the setting.
+ ExtensionSettingsStore.select(
+ ExtensionSettingsStore.SETTING_USER_SET,
+ type,
+ key
+ );
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after being set by user"
+ );
+
+ let three = ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@third" } },
+ },
+ });
+ await three.startup();
+
+ item = await ExtensionSettingsStore.addSetting(
+ three.id,
+ type,
+ key,
+ "three",
+ initialCallback
+ );
+ deepEqual(
+ { key, value: "three", id: three.id },
+ item,
+ "addSetting returns the third set item"
+ );
+ deepEqual(
+ item,
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the third set item"
+ );
+
+ ExtensionSettingsStore.select(
+ ExtensionSettingsStore.SETTING_USER_SET,
+ type,
+ key
+ );
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after being set by user"
+ );
+
+ item = ExtensionSettingsStore.select(one.id, type, key);
+ deepEqual(
+ { key, value: "one", id: one.id },
+ item,
+ "selecting an extension returns the first set item after enable"
+ );
+
+ // Disabling a selected item returns to precedence order
+ ExtensionSettingsStore.disable(one.id, type, key);
+ deepEqual(
+ { key, value: "three", id: three.id },
+ ExtensionSettingsStore.getSetting(type, key),
+ "returning to precedence order sets the third set item"
+ );
+
+ // Test that disabling all then enabling one does not take over a user-set setting.
+ ExtensionSettingsStore.select(
+ ExtensionSettingsStore.SETTING_USER_SET,
+ type,
+ key
+ );
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after being set by user"
+ );
+
+ ExtensionSettingsStore.disable(three.id, type, key);
+ ExtensionSettingsStore.disable(two.id, type, key);
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after disabling all extensions"
+ );
+
+ ExtensionSettingsStore.enable(three.id, type, key);
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after enabling one extension"
+ );
+
+ // Ensure that calling addSetting again will not reset a user-set value when
+ // the extension install date is older than the user-set date.
+ item = await ExtensionSettingsStore.addSetting(
+ three.id,
+ type,
+ key,
+ "three",
+ initialCallback
+ );
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after calling addSetting for old addon"
+ );
+
+ item = ExtensionSettingsStore.enable(three.id, type, key);
+ equal(undefined, item, "enabling the active item does not return an item");
+ deepEqual(
+ { key, initialValue: "initial" },
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns the initial value after enabling one extension"
+ );
+
+ ExtensionSettingsStore.removeSetting(three.id, type, key);
+ ExtensionSettingsStore.removeSetting(two.id, type, key);
+ ExtensionSettingsStore.removeSetting(one.id, type, key);
+
+ equal(
+ null,
+ ExtensionSettingsStore.getSetting(type, key),
+ "getSetting returns null after removing all settings"
+ );
+
+ for (let extension of testExtensions) {
+ await extension.unload();
+ }
+
+ await promiseShutdownManager();
+});
+
+add_task(async function test_settings_store_add_disabled() {
+ await promiseStartupManager();
+
+ let id = "@add-on-disable";
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id } },
+ },
+ });
+
+ await extension.startup();
+ await ExtensionSettingsStore.initialize();
+
+ await ExtensionSettingsStore.addSetting(
+ id,
+ "foo",
+ "bar",
+ "set",
+ () => "not set"
+ );
+
+ let item = ExtensionSettingsStore.getSetting("foo", "bar");
+ equal(item.id, id, "The add-on is in control");
+ equal(item.value, "set", "The value is set");
+
+ ExtensionSettingsStore.disable(id, "foo", "bar");
+ item = ExtensionSettingsStore.getSetting("foo", "bar");
+ equal(item.id, undefined, "The add-on is not in control");
+ equal(item.initialValue, "not set", "The value is not set");
+
+ await ExtensionSettingsStore.addSetting(
+ id,
+ "foo",
+ "bar",
+ "set",
+ () => "not set"
+ );
+ item = ExtensionSettingsStore.getSetting("foo", "bar");
+ equal(item.id, id, "The add-on is in control");
+ equal(item.value, "set", "The value is set");
+
+ await extension.unload();
+
+ await promiseShutdownManager();
+});
+
+add_task(async function test_settings_uninstall_remove() {
+ await promiseStartupManager();
+
+ let id = "@add-on-uninstall";
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id } },
+ },
+ });
+
+ await extension.startup();
+ await ExtensionSettingsStore.initialize();
+
+ await ExtensionSettingsStore.addSetting(
+ id,
+ "foo",
+ "bar",
+ "set",
+ () => "not set"
+ );
+
+ let item = ExtensionSettingsStore.getSetting("foo", "bar");
+ equal(item.id, id, "The add-on is in control");
+ equal(item.value, "set", "The value is set");
+
+ await extension.unload();
+
+ await promiseShutdownManager();
+
+ item = ExtensionSettingsStore.getSetting("foo", "bar");
+ equal(item, null, "The add-on setting was removed");
+});
+
+add_task(async function test_exceptions() {
+ await ExtensionSettingsStore.initialize();
+
+ await Assert.rejects(
+ ExtensionSettingsStore.addSetting(
+ 1,
+ TEST_TYPE,
+ "key_not_a_function",
+ "val1",
+ "not a function"
+ ),
+ /initialValueCallback must be a function/,
+ "addSetting rejects with a callback that is not a function."
+ );
+});
+
+add_task(async function test_get_all_settings() {
+ await promiseStartupManager();
+
+ let testExtensions = [
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@first" } },
+ },
+ }),
+ ExtensionTestUtils.loadExtension({
+ useAddonManager: "temporary",
+ manifest: {
+ browser_specific_settings: { gecko: { id: "@second" } },
+ },
+ }),
+ ];
+
+ for (let extension of testExtensions) {
+ await extension.startup();
+ }
+
+ await ExtensionSettingsStore.initialize();
+
+ let items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+ equal(items.length, 0, "There are no addons controlling this setting yet");
+
+ await ExtensionSettingsStore.addSetting(
+ "@first",
+ "foo",
+ "bar",
+ "set",
+ () => "not set"
+ );
+
+ items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+ equal(items.length, 1, "The add-on setting has 1 addon trying to control it");
+
+ await ExtensionSettingsStore.addSetting(
+ "@second",
+ "foo",
+ "bar",
+ "setting",
+ () => "not set"
+ );
+
+ let item = ExtensionSettingsStore.getSetting("foo", "bar");
+ equal(item.id, "@second", "The second add-on is in control");
+ equal(item.value, "setting", "The second value is set");
+
+ items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+ equal(
+ items.length,
+ 2,
+ "The add-on setting has 2 addons trying to control it"
+ );
+
+ await ExtensionSettingsStore.removeSetting("@first", "foo", "bar");
+
+ items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+ equal(items.length, 1, "There is only 1 addon controlling this setting");
+
+ await ExtensionSettingsStore.removeSetting("@second", "foo", "bar");
+
+ items = ExtensionSettingsStore.getAllSettings("foo", "bar");
+ equal(
+ items.length,
+ 0,
+ "There is no longer any addon controlling this setting"
+ );
+
+ for (let extension of testExtensions) {
+ await extension.unload();
+ }
+
+ await promiseShutdownManager();
+});