Bug 1341277 - Part 2: Update ExtensionPreferencesManager to support disabled settings. r=aswan
authorBob Silverberg <bsilverberg@mozilla.com>
Wed, 22 Feb 2017 14:27:33 -0500
changeset 394638 02d52f2c1d5534b3ac784ffe3dcf1c27ad6d7281
parent 394637 68d9a7c54c002b69afc30fa7e2bb133c63be7c6a
child 394639 ce4a71e11833f4ade0976842576e158224876ede
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1341277
milestone54.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 1341277 - Part 2: Update ExtensionPreferencesManager to support disabled settings. r=aswan MozReview-Commit-ID: FG7u2KbWLdG
toolkit/components/extensions/ExtensionPreferencesManager.jsm
toolkit/components/extensions/test/xpcshell/test_ext_extensionPreferencesManager.js
--- a/toolkit/components/extensions/ExtensionPreferencesManager.jsm
+++ b/toolkit/components/extensions/ExtensionPreferencesManager.jsm
@@ -52,32 +52,42 @@ function initialValueCallback() {
   let initialValue = {};
   for (let pref of this.prefNames) {
     initialValue[pref] = Preferences.get(pref);
   }
   return initialValue;
 }
 
 /**
- * Takes an object of preferenceName:value pairs and either sets or resets the
- * preference to the value.
+ * Takes an item returned by the ExtensionSettingsStore and conditionally sets
+ * preferences based on the item's contents.
  *
- * @param {Object} prefsObject
- *        An object with one property per preference, which holds the value to
- *        store in that preference. If the value is undefined then the
- *        preference is reset.
- */
-function setPrefs(prefsObject) {
-  for (let pref in prefsObject) {
-    if (prefsObject[pref] === undefined) {
-      Preferences.reset(pref);
-    } else {
-      Preferences.set(pref, prefsObject[pref]);
+ * @param {string} name
+ *        The name of the setting being processed.
+ * @param {Object|null} item
+ *        Either null, or an object with a value property which indicates the
+ *        value stored for the setting in the settings store.
+
+ * @returns {Promise}
+ *          Resolves to true if the preferences were changed and to false if
+ *          the preferences were not changed.
+*/
+async function processItem(name, item) {
+  if (item) {
+    let prefs = item.initialValue || await settingsMap.get(name).setCallback(item.value);
+    for (let pref in prefs) {
+      if (prefs[pref] === undefined) {
+        Preferences.reset(pref);
+      } else {
+        Preferences.set(pref, prefs[pref]);
+      }
     }
+    return true;
   }
+  return false;
 }
 
 this.ExtensionPreferencesManager = {
   /**
    * Adds a setting to the settingsMap. This is how an API tells the
    * preferences manager what its setting object is. The preferences
    * manager needs to know this when settings need to be removed
    * automatically.
@@ -117,52 +127,119 @@ this.ExtensionPreferencesManager = {
    * @returns {Promise}
    *          Resolves to true if the preferences were changed and to false if
    *          the preferences were not changed.
    */
   async setSetting(extension, name, value) {
     let setting = settingsMap.get(name);
     let item = await ExtensionSettingsStore.addSetting(
       extension, STORE_TYPE, name, value, initialValueCallback.bind(setting));
-    if (item) {
-      let prefs = await setting.setCallback(item.value);
-      setPrefs(prefs);
-      return true;
-    }
-    return false;
+    return await processItem(name, item);
   },
 
   /**
-   * Indicates that this extension no longer wants to set the given preference.
+   * Indicates that this extension wants to temporarily cede control over the
+   * given setting.
+   *
+   * @param {Extension} extension
+   *        The extension for which a preference setting is being removed.
+   * @param {string} name
+   *        The unique id of the setting.
+   *
+   * @returns {Promise}
+   *          Resolves to true if the preferences were changed and to false if
+   *          the preferences were not changed.
+   */
+  async disableSetting(extension, name) {
+    let item = await ExtensionSettingsStore.disable(
+      extension, STORE_TYPE, name);
+    return await processItem(name, item);
+  },
+
+  /**
+   * Enable a setting that has been disabled.
+   *
+   * @param {Extension} extension
+   *        The extension for which a setting is being enabled.
+   * @param {string} name
+   *        The unique id of the setting.
+   *
+   * @returns {Promise}
+   *          Resolves to true if the preferences were changed and to false if
+   *          the preferences were not changed.
+   */
+  async enableSetting(extension, name) {
+    let item = await ExtensionSettingsStore.enable(extension, STORE_TYPE, name);
+    return await processItem(name, item);
+  },
+
+  /**
+   * Indicates that this extension no longer wants to set the given setting.
    *
    * @param {Extension} extension
    *        The extension for which a preference setting is being removed.
    * @param {string} name
    *        The unique id of the setting.
+   *
+   * @returns {Promise}
+   *          Resolves to true if the preferences were changed and to false if
+   *          the preferences were not changed.
    */
-  async unsetSetting(extension, name) {
+  async removeSetting(extension, name) {
     let item = await ExtensionSettingsStore.removeSetting(
       extension, STORE_TYPE, name);
-    if (item) {
-      let prefs = item.initialValue || await settingsMap.get(name).setCallback(item.value);
-      setPrefs(prefs);
+    return await processItem(name, item);
+  },
+
+  /**
+   * Disables all previously set settings for an extension. This can be called when
+   * an extension is being disabled, for example.
+   *
+   * @param {Extension} extension
+   *        The extension for which all settings are being unset.
+   */
+  async disableAll(extension) {
+    let settings = await ExtensionSettingsStore.getAllForExtension(extension, STORE_TYPE);
+    let disablePromises = [];
+    for (let name of settings) {
+      disablePromises.push(this.disableSetting(extension, name));
     }
+    await Promise.all(disablePromises);
   },
 
   /**
-   * Unsets all previously set settings for an extension. This can be called when
-   * an extension is being uninstalled or disabled, for example.
+   * Enables all disabled settings for an extension. This can be called when
+   * an extension has finsihed updating or is being re-enabled, for example.
    *
-   * @param {Extension} extension The extension for which all settings are being unset.
+   * @param {Extension} extension
+   *        The extension for which all settings are being enabled.
    */
-  async unsetAll(extension) {
+  async enableAll(extension) {
     let settings = await ExtensionSettingsStore.getAllForExtension(extension, STORE_TYPE);
+    let enablePromises = [];
     for (let name of settings) {
-      await this.unsetSetting(extension, name);
+      enablePromises.push(this.enableSetting(extension, name));
     }
+    await Promise.all(enablePromises);
+  },
+
+  /**
+   * Removes all previously set settings for an extension. This can be called when
+   * an extension is being uninstalled, for example.
+   *
+   * @param {Extension} extension
+   *        The extension for which all settings are being unset.
+   */
+  async removeAll(extension) {
+    let settings = await ExtensionSettingsStore.getAllForExtension(extension, STORE_TYPE);
+    let removePromises = [];
+    for (let name of settings) {
+      removePromises.push(this.removeSetting(extension, name));
+    }
+    await Promise.all(removePromises);
   },
 
   /**
    * Return the levelOfControl for a setting / extension combo.
    * This queries the levelOfControl from the ExtensionSettingsStore and also
    * takes into account whether any of the setting's preferences are locked.
    *
    * @param {Extension} extension
--- a/toolkit/components/extensions/test/xpcshell/test_ext_extensionPreferencesManager.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_extensionPreferencesManager.js
@@ -66,16 +66,22 @@ ExtensionPreferencesManager.addSetting("
 // Set initial values for prefs.
 for (let setting in SETTINGS) {
   setting = SETTINGS[setting];
   for (let i = 0; i < setting.prefNames.length; i++) {
     Preferences.set(setting.prefNames[i], setting.initalValues[i]);
   }
 }
 
+function checkPrefs(settingObj, value, msg) {
+  for (let pref of settingObj.prefNames) {
+    equal(Preferences.get(pref), settingObj.valueFn(pref, value), msg);
+  }
+}
+
 add_task(async function test_preference_manager() {
   // Create an array of test framework extension wrappers to install.
   let testExtensions = [
     ExtensionTestUtils.loadExtension({
       useAddonManager: "temporary",
       manifest: {},
     }),
     ExtensionTestUtils.loadExtension({
@@ -92,100 +98,134 @@ add_task(async function test_preference_
 
   // Create an array actual Extension objects which correspond to the
   // test framework extension wrappers.
   let extensions = testExtensions.map(extension => extension.extension._extension);
 
   for (let setting in SETTINGS) {
     let settingObj = SETTINGS[setting];
     let newValue1 = "newValue1";
-    let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(extensions[1], setting);
+    let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(
+      extensions[1], setting);
     equal(levelOfControl, "controllable_by_this_extension",
       "getLevelOfControl returns correct levelOfControl with no settings set.");
-    let settingSet = await ExtensionPreferencesManager.setSetting(extensions[1], setting, newValue1);
-    ok(settingSet, "setSetting returns true when the pref(s) have been set.");
-    for (let pref of settingObj.prefNames) {
-      equal(Preferences.get(pref), settingObj.valueFn(pref, newValue1),
-        "setSetting sets the prefs for the first extension.");
-    }
+
+    let prefsChanged = await ExtensionPreferencesManager.setSetting(
+      extensions[1], setting, newValue1);
+    ok(prefsChanged, "setSetting returns true when the pref(s) have been set.");
+    checkPrefs(settingObj, newValue1,
+      "setSetting sets the prefs for the first extension.");
     levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(extensions[1], setting);
     equal(
       levelOfControl,
       "controlled_by_this_extension",
       "getLevelOfControl returns correct levelOfControl when a pref has been set.");
 
     let newValue2 = "newValue2";
-    settingSet = await ExtensionPreferencesManager.setSetting(extensions[0], setting, newValue2);
-    ok(!settingSet, "setSetting returns false when the pref(s) have not been set.");
-    for (let pref of settingObj.prefNames) {
-      equal(Preferences.get(pref), settingObj.valueFn(pref, newValue1),
-        "setSetting does not set the pref(s) for an earlier extension.");
-    }
+    prefsChanged = await ExtensionPreferencesManager.setSetting(extensions[0], setting, newValue2);
+    ok(!prefsChanged, "setSetting returns false when the pref(s) have not been set.");
+    checkPrefs(settingObj, newValue1,
+      "setSetting does not set the pref(s) for an earlier extension.");
+
+    prefsChanged = await ExtensionPreferencesManager.disableSetting(extensions[0], setting);
+    ok(!prefsChanged, "disableSetting returns false when the pref(s) have not been set.");
+    checkPrefs(settingObj, newValue1,
+      "disableSetting does not change the pref(s) for the non-top extension.");
 
-    await ExtensionPreferencesManager.unsetSetting(extensions[0], setting);
-    for (let pref of settingObj.prefNames) {
-      equal(Preferences.get(pref), settingObj.valueFn(pref, newValue1),
-        "unsetSetting does not change the pref(s) for the non-top extension.");
-    }
+    prefsChanged = await ExtensionPreferencesManager.enableSetting(extensions[0], setting);
+    ok(!prefsChanged, "enableSetting returns false when the pref(s) have not been set.");
+    checkPrefs(settingObj, newValue1,
+      "enableSetting does not change the pref(s) for the non-top extension.");
+
+    prefsChanged = await ExtensionPreferencesManager.removeSetting(extensions[0], setting);
+    ok(!prefsChanged, "removeSetting returns false when the pref(s) have not been set.");
+    checkPrefs(settingObj, newValue1,
+      "removeSetting does not change the pref(s) for the non-top extension.");
 
-    await ExtensionPreferencesManager.setSetting(extensions[0], setting, newValue2);
-    for (let pref of settingObj.prefNames) {
-      equal(Preferences.get(pref), settingObj.valueFn(pref, newValue1),
-        "setSetting does not set the pref(s) for an earlier extension.");
-    }
+    prefsChanged = await ExtensionPreferencesManager.setSetting(extensions[0], setting, newValue2);
+    ok(!prefsChanged, "setSetting returns false when the pref(s) have not been set.");
+    checkPrefs(settingObj, newValue1,
+      "setSetting does not set the pref(s) for an earlier extension.");
+
+    prefsChanged = await ExtensionPreferencesManager.disableSetting(extensions[1], setting);
+    ok(prefsChanged, "disableSetting returns true when the pref(s) have been set.");
+    checkPrefs(settingObj, newValue2,
+      "disableSetting sets the pref(s) to the next value when disabling the top extension.");
 
-    await ExtensionPreferencesManager.unsetSetting(extensions[1], setting);
-    for (let pref of settingObj.prefNames) {
-      equal(Preferences.get(pref), settingObj.valueFn(pref, newValue2),
-        "unsetSetting sets the pref(s) to the next value when removing the top extension.");
-    }
+    prefsChanged = await ExtensionPreferencesManager.enableSetting(extensions[1], setting);
+    ok(prefsChanged, "enableSetting returns true when the pref(s) have been set.");
+    checkPrefs(settingObj, newValue1,
+      "enableSetting sets the pref(s) to the previous value(s).");
 
-    await ExtensionPreferencesManager.unsetSetting(extensions[0], setting);
+    prefsChanged = await ExtensionPreferencesManager.removeSetting(extensions[1], setting);
+    ok(prefsChanged, "removeSetting returns true when the pref(s) have been set.");
+    checkPrefs(settingObj, newValue2,
+      "removeSetting sets the pref(s) to the next value when removing the top extension.");
+
+    prefsChanged = await ExtensionPreferencesManager.removeSetting(extensions[0], setting);
+    ok(prefsChanged, "removeSetting returns true when the pref(s) have been set.");
     for (let i = 0; i < settingObj.prefNames.length; i++) {
       equal(Preferences.get(settingObj.prefNames[i]), settingObj.initalValues[i],
-        "unsetSetting sets the pref(s) to the initial value(s) when removing the last extension.");
+        "removeSetting sets the pref(s) to the initial value(s) when removing the last extension.");
     }
   }
 
   // Tests for unsetAll.
   let newValue3 = "newValue3";
   for (let setting in SETTINGS) {
     let settingObj = SETTINGS[setting];
     await ExtensionPreferencesManager.setSetting(extensions[0], setting, newValue3);
-    for (let pref of settingObj.prefNames) {
-      equal(Preferences.get(pref), settingObj.valueFn(pref, newValue3), `setSetting set the pref for ${pref}.`);
-    }
+    checkPrefs(settingObj, newValue3, "setSetting set the pref.");
   }
 
   let setSettings = await ExtensionSettingsStore.getAllForExtension(extensions[0], STORE_TYPE);
   deepEqual(setSettings, Object.keys(SETTINGS), "Expected settings were set for extension.");
-  await ExtensionPreferencesManager.unsetAll(extensions[0]);
+  await ExtensionPreferencesManager.disableAll(extensions[0]);
 
   for (let setting in SETTINGS) {
     let settingObj = SETTINGS[setting];
     for (let i = 0; i < settingObj.prefNames.length; i++) {
       equal(Preferences.get(settingObj.prefNames[i]), settingObj.initalValues[i],
-        "unsetAll unset the pref.");
+        "disableAll unset the pref.");
     }
   }
 
   setSettings = await ExtensionSettingsStore.getAllForExtension(extensions[0], STORE_TYPE);
-  deepEqual(setSettings, [], "unsetAll removed all settings.");
+  deepEqual(setSettings, Object.keys(SETTINGS), "disableAll retains the settings.");
+
+  await ExtensionPreferencesManager.enableAll(extensions[0]);
+  for (let setting in SETTINGS) {
+    let settingObj = SETTINGS[setting];
+    checkPrefs(settingObj, newValue3, "enableAll re-set the pref.");
+  }
+
+  await ExtensionPreferencesManager.removeAll(extensions[0]);
+
+  for (let setting in SETTINGS) {
+    let settingObj = SETTINGS[setting];
+    for (let i = 0; i < settingObj.prefNames.length; i++) {
+      equal(Preferences.get(settingObj.prefNames[i]), settingObj.initalValues[i],
+        "removeAll unset the pref.");
+    }
+  }
+
+  setSettings = await ExtensionSettingsStore.getAllForExtension(extensions[0], STORE_TYPE);
+  deepEqual(setSettings, [], "removeAll removed all settings.");
 
   // Test with an uninitialized pref.
   let setting = "singlePref";
   let settingObj = SETTINGS[setting];
   let pref = settingObj.prefNames[0];
   let newValue = "newValue";
   Preferences.reset(pref);
   await ExtensionPreferencesManager.setSetting(extensions[1], setting, newValue);
   equal(Preferences.get(pref), settingObj.valueFn(pref, newValue),
     "Uninitialized pref is set.");
-  await ExtensionPreferencesManager.unsetSetting(extensions[1], setting);
-  ok(!Preferences.has(pref), "unsetSetting removed the pref.");
+  await ExtensionPreferencesManager.removeSetting(extensions[1], setting);
+  ok(!Preferences.has(pref), "removeSetting removed the pref.");
 
   // Test levelOfControl with a locked pref.
   setting = "multiple_prefs";
   let prefToLock = SETTINGS[setting].prefNames[0];
   Preferences.lock(prefToLock, 1);
   ok(Preferences.locked(prefToLock), `Preference ${prefToLock} is locked.`);
   let levelOfControl = await ExtensionPreferencesManager.getLevelOfControl(extensions[1], setting);
   equal(