Bug 1407209 - Add observer for preference changes whilst extensions are being updated. r?bsilverberg r?baku r?aswan
MozReview-Commit-ID: 5CqpYDc4tCg
--- 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);
+});