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 374688 02d52f2c1d5534b3ac784ffe3dcf1c27ad6d7281
parent 374687 68d9a7c54c002b69afc30fa7e2bb133c63be7c6a
child 374689 ce4a71e11833f4ade0976842576e158224876ede
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1341277
milestone54.0a1
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(