Bug 1407209 - Add observer for preference changes whilst extensions are being updated. r=aswan,baku,bsilverberg
authorJonathan Kingston <jkt@mozilla.com>
Mon, 16 Oct 2017 18:44:50 +0100
changeset 386809 398dfb7487ae01de5db3b8a3207472edaedf56e9
parent 386808 73c16ac042912a583fb927b9682b2771a95f6bbf
child 386810 c09ea1671fc337f30941d52e64588f76af7096ef
push id96311
push userarchaeopteryx@coole-files.de
push dateWed, 18 Oct 2017 09:52:02 +0000
treeherdermozilla-inbound@a8a1e8cc1980 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan, baku, bsilverberg
bugs1407209
milestone58.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 1407209 - Add observer for preference changes whilst extensions are being updated. r=aswan,baku,bsilverberg MozReview-Commit-ID: 5CqpYDc4tCg
toolkit/components/contextualidentity/ContextualIdentityService.jsm
toolkit/components/extensions/ExtensionPreferencesManager.jsm
toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
--- a/toolkit/components/contextualidentity/ContextualIdentityService.jsm
+++ b/toolkit/components/contextualidentity/ContextualIdentityService.jsm
@@ -110,27 +110,39 @@ function _ContextualIdentityService(path
 
   _path: null,
   _dataReady: false,
 
   _saver: null,
 
   init(path) {
     this._path = path;
+    this._webExtensionUpdating = false;
 
     Services.prefs.addObserver(CONTEXTUAL_IDENTITY_ENABLED_PREF, this);
+    Services.obs.addObserver(this, "web-extension-preferences-replacing");
+    Services.obs.addObserver(this, "web-extension-preferences-replaced");
   },
 
-  // observe() is only used to listen to container enabling pref
-  async observe() {
-    const contextualIdentitiesEnabled = Services.prefs.getBoolPref(CONTEXTUAL_IDENTITY_ENABLED_PREF);
-    if (!contextualIdentitiesEnabled) {
-      await this.closeContainerTabs();
-      this.notifyAllContainersCleared();
-      this.resetDefault();
+  async observe(aSubject, aTopic) {
+    switch (aTopic) {
+      case "web-extension-preferences-replacing":
+        this._webExtensionUpdating = true;
+        break;
+      case "web-extension-preferences-replaced":
+        this._webExtensionUpdating = false;
+        // We want to check the pref when the extension has been replaced too
+      case "nsPref:changed":
+        const contextualIdentitiesEnabled = Services.prefs.getBoolPref(CONTEXTUAL_IDENTITY_ENABLED_PREF);
+        if (!contextualIdentitiesEnabled && !this._webExtensionUpdating) {
+          await this.closeContainerTabs();
+          this.notifyAllContainersCleared();
+          this.resetDefault();
+        }
+        break;
     }
   },
 
   load() {
     OS.File.read(this._path).then(bytes => {
       // If synchronous loading happened in the meantime, exit now.
       if (this._dataReady) {
         return;
--- a/toolkit/components/extensions/ExtensionPreferencesManager.jsm
+++ b/toolkit/components/extensions/ExtensionPreferencesManager.jsm
@@ -20,44 +20,57 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["ExtensionPreferencesManager"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
                                   "resource://gre/modules/ExtensionSettingsStore.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
                                   "resource://gre/modules/Preferences.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "defaultPreferences", function() {
   return new Preferences({defaultBranch: true});
 });
 
+const ADDON_REPLACE_REASONS = new Set([
+  "ADDON_DOWNGRADE",
+  "ADDON_UPGRADE",
+]);
+
 /* eslint-disable mozilla/balanced-listeners */
 Management.on("shutdown", (type, extension) => {
   switch (extension.shutdownReason) {
     case "ADDON_DISABLE":
     case "ADDON_DOWNGRADE":
     case "ADDON_UPGRADE":
+      if (ADDON_REPLACE_REASONS.has(extension.shutdownReason)) {
+        Services.obs.notifyObservers(null, "web-extension-preferences-replacing");
+      }
       this.ExtensionPreferencesManager.disableAll(extension);
       break;
 
     case "ADDON_UNINSTALL":
       this.ExtensionPreferencesManager.removeAll(extension);
       break;
   }
 });
 
-Management.on("startup", (type, extension) => {
+Management.on("startup", async (type, extension) => {
   if (["ADDON_ENABLE", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(extension.startupReason)) {
-    this.ExtensionPreferencesManager.enableAll(extension);
+    const enablePromise = this.ExtensionPreferencesManager.enableAll(extension);
+    if (ADDON_REPLACE_REASONS.has(extension.startupReason)) {
+      await enablePromise;
+      Services.obs.notifyObservers(null, "web-extension-preferences-replaced");
+    }
   }
 });
 /* eslint-enable mozilla/balanced-listeners */
 
 const STORE_TYPE = "prefs";
 
 // Definitions of settings, each of which correspond to a different API.
 let settingsMap = new Map();
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
@@ -120,17 +120,17 @@ add_task(async function test_contextualI
 
   Services.prefs.clearUserPref(CONTAINERS_PREF);
 });
 
 add_task(async function test_contextualIdentity_with_permissions() {
   const CONTAINERS_PREF = "privacy.userContext.enabled";
   const initial = Services.prefs.getBoolPref(CONTAINERS_PREF);
 
-  async function background(ver) {
+  async function background() {
     let ci;
     await browser.test.assertRejects(browser.contextualIdentities.get("foobar"), "Invalid contextual identity: foobar", "API should reject here");
     await browser.test.assertRejects(browser.contextualIdentities.update("foobar", {name: "testing"}), "Invalid contextual identity: foobar", "API should reject for unknown updates");
     await browser.test.assertRejects(browser.contextualIdentities.remove("foobar"), "Invalid contextual identity: foobar", "API should reject for removing unknown containers");
 
     ci = await browser.contextualIdentities.get("firefox-container-1");
     browser.test.assertTrue(!!ci, "We have an identity");
     browser.test.assertTrue("name" in ci, "We have an identity.name");
@@ -260,17 +260,17 @@ add_task(async function test_contextualI
   equal(Services.prefs.getBoolPref(CONTAINERS_PREF), initial, "Pref should now be initial state");
 
   Services.prefs.clearUserPref(CONTAINERS_PREF);
 });
 
 add_task(async function test_contextualIdentity_extensions_enable_containers() {
   const CONTAINERS_PREF = "privacy.userContext.enabled";
   const initial = Services.prefs.getBoolPref(CONTAINERS_PREF);
-  async function background(ver) {
+  async function background() {
     let ci = await browser.contextualIdentities.get("firefox-container-1");
     browser.test.assertTrue(!!ci, "We have an identity");
 
     browser.test.notifyPass("contextualIdentities");
   }
   function makeExtension(id) {
     return ExtensionTestUtils.loadExtension({
       useAddonManager: "temporary",
@@ -323,8 +323,59 @@ add_task(async function test_contextualI
   equal(Services.prefs.getBoolPref(CONTAINERS_PREF), true, "Pref should now be enabled 1");
   await extension3.unload();
   equal(Services.prefs.getBoolPref(CONTAINERS_PREF), true, "Pref should now be enabled 2");
   await extension2.unload();
   equal(Services.prefs.getBoolPref(CONTAINERS_PREF), true, "Pref should now be enabled 3");
 
   Services.prefs.clearUserPref(CONTAINERS_PREF);
 });
+
+add_task(async function test_contextualIdentity_preference_change() {
+  const CONTAINERS_PREF = "privacy.userContext.enabled";
+  async function background() {
+    let extensionInfo = await browser.management.getSelf();
+    if (extensionInfo.version == "1.0.0") {
+      const containers = await browser.contextualIdentities.query({});
+      browser.test.assertEq(containers.length, 4, "We still have the original containers");
+      await browser.contextualIdentities.create({
+        name: "foobar",
+        color: "red",
+        icon: "circle",
+      });
+    }
+    const containers = await browser.contextualIdentities.query({});
+    browser.test.assertEq(containers.length, 5, "We have a new container");
+    if (extensionInfo.version == "1.1.0") {
+      await browser.contextualIdentities.remove(containers[4].cookieStoreId);
+    }
+    browser.test.notifyPass("contextualIdentities");
+  }
+  function makeExtension(id, version) {
+    return ExtensionTestUtils.loadExtension({
+      useAddonManager: "temporary",
+      background,
+      manifest: {
+        version,
+        applications: {
+          gecko: {id},
+        },
+        permissions: ["contextualIdentities"],
+      },
+    });
+  }
+
+  Services.prefs.setBoolPref(CONTAINERS_PREF, false);
+  let extension = makeExtension("containers-pref-test@mozilla.org", "1.0.0");
+  await extension.startup();
+  await extension.awaitFinish("contextualIdentities");
+  equal(Services.prefs.getBoolPref(CONTAINERS_PREF), true, "Pref should now be enabled, whatever it's initial state");
+
+  let extension2 = makeExtension("containers-pref-test@mozilla.org", "1.1.0");
+  await extension2.startup();
+  await extension2.awaitFinish("contextualIdentities");
+
+  const prefChange = waitForPrefChange(CONTAINERS_PREF);
+  await extension.unload();
+  await prefChange;
+
+  Services.prefs.clearUserPref(CONTAINERS_PREF);
+});