Bug 1341277 - Part 3: Update ext-privacy.js to support disabling and re-enabling settings. r?aswan
MozReview-Commit-ID: 4Yf0uxsoXHP
--- a/toolkit/components/extensions/ext-privacy.js
+++ b/toolkit/components/extensions/ext-privacy.js
@@ -8,54 +8,64 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/ExtensionPreferencesManager.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
ExtensionError,
} = ExtensionUtils;
+/* eslint-disable mozilla/balanced-listeners */
+extensions.on("startup", async (type, extension) => {
+ if (["ADDON_ENABLE", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(extension.startupReason)) {
+ await ExtensionPreferencesManager.enableAll(extension);
+ }
+});
+
+extensions.on("shutdown", async (type, extension) => {
+ switch (extension.shutdownReason) {
+ case "ADDON_DISABLE":
+ case "ADDON_DOWNGRADE":
+ case "ADDON_UPGRADE":
+ await ExtensionPreferencesManager.disableAll(extension);
+ break;
+
+ case "ADDON_UNINSTALL":
+ await ExtensionPreferencesManager.removeAll(extension);
+ break;
+ }
+});
+/* eslint-enable mozilla/balanced-listeners */
+
function checkScope(scope) {
if (scope && scope !== "regular") {
throw new ExtensionError(
`Firefox does not support the ${scope} settings scope.`);
}
}
-function getAPI(extension, context, name, callback) {
- let anythingSet = false;
+function getAPI(extension, name, callback) {
return {
async get(details) {
return {
levelOfControl: details.incognito ?
"not_controllable" :
await ExtensionPreferencesManager.getLevelOfControl(
extension, name),
value: await callback(),
};
},
async set(details) {
checkScope(details.scope);
- if (!anythingSet) {
- anythingSet = true;
- context.callOnClose({
- close: async () => {
- if (["ADDON_DISABLE", "ADDON_UNINSTALL"].includes(extension.shutdownReason)) {
- await ExtensionPreferencesManager.unsetAll(extension);
- anythingSet = false;
- }
- },
- });
- }
return await ExtensionPreferencesManager.setSetting(
extension, name, details.value);
},
async clear(details) {
checkScope(details.scope);
- return await ExtensionPreferencesManager.unsetSetting(
+ return await ExtensionPreferencesManager.removeSetting(
extension, name);
},
};
}
// Add settings objects for supported APIs to the preferences manager.
ExtensionPreferencesManager.addSetting("network.networkPredictionEnabled", {
prefNames: [
@@ -120,25 +130,25 @@ ExtensionPreferencesManager.addSetting("
},
});
extensions.registerSchemaAPI("privacy.network", "addon_parent", context => {
let {extension} = context;
return {
privacy: {
network: {
- networkPredictionEnabled: getAPI(extension, context,
+ networkPredictionEnabled: getAPI(extension,
"network.networkPredictionEnabled",
() => {
return Preferences.get("network.predictor.enabled") &&
Preferences.get("network.prefetch-next") &&
Preferences.get("network.http.speculative-parallel-limit") > 0 &&
!Preferences.get("network.dns.disablePrefetch");
}),
- webRTCIPHandlingPolicy: getAPI(extension, context,
+ webRTCIPHandlingPolicy: getAPI(extension,
"network.webRTCIPHandlingPolicy",
() => {
if (Preferences.get("media.peerconnection.ice.proxy_only")) {
return "disable_non_proxied_udp";
}
let default_address_only =
Preferences.get("media.peerconnection.ice.default_address_only");
@@ -148,17 +158,17 @@ extensions.registerSchemaAPI("privacy.ne
}
return "default_public_and_private_interfaces";
}
return "default";
}),
},
websites: {
- hyperlinkAuditingEnabled: getAPI(extension, context,
+ hyperlinkAuditingEnabled: getAPI(extension,
"websites.hyperlinkAuditingEnabled",
() => {
return Preferences.get("browser.send_pings");
}),
},
},
};
});
--- a/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_privacy.js
@@ -15,17 +15,17 @@ const {
promiseStartupManager,
} = AddonTestUtils;
AddonTestUtils.init(this);
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
add_task(async function test_privacy() {
- // Create a object to hold the values to which we will initialize the prefs.
+ // Create an object to hold the values to which we will initialize the prefs.
const SETTINGS = {
"network.networkPredictionEnabled": {
"network.predictor.enabled": true,
"network.prefetch-next": true,
"network.http.speculative-parallel-limit": 10,
"network.dns.disablePrefetch": false,
},
"websites.hyperlinkAuditingEnabled": {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_privacy_disable.js
@@ -0,0 +1,185 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyGetter(this, "Management", () => {
+ const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
+ return Management;
+});
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
+ "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPreferencesManager",
+ "resource://gre/modules/ExtensionPreferencesManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+ "resource://gre/modules/Preferences.jsm");
+
+const {
+ createAppInfo,
+ promiseShutdownManager,
+ promiseStartupManager,
+} = AddonTestUtils;
+
+AddonTestUtils.init(this);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+function awaitEvent(eventName) {
+ return new Promise(resolve => {
+ let listener = (_eventName, ...args) => {
+ if (_eventName === eventName) {
+ Management.off(eventName, listener);
+ resolve(...args);
+ }
+ };
+
+ Management.on(eventName, listener);
+ });
+}
+
+function awaitPrefChange(prefName) {
+ return new Promise(resolve => {
+ let listener = (args) => {
+ Preferences.ignore(prefName, listener);
+ resolve();
+ };
+
+ Preferences.observe(prefName, listener);
+ });
+}
+
+add_task(async function test_disable() {
+ const OLD_ID = "old_id@tests.mozilla.org";
+ const NEW_ID = "new_id@tests.mozilla.org";
+
+ const PREF_TO_WATCH = "network.http.speculative-parallel-limit";
+
+ // Create an object to hold the values to which we will initialize the prefs.
+ const PREFS = {
+ "network.predictor.enabled": true,
+ "network.prefetch-next": true,
+ "network.http.speculative-parallel-limit": 10,
+ "network.dns.disablePrefetch": false,
+ };
+
+ // Set prefs to our initial values.
+ for (let pref in PREFS) {
+ Preferences.set(pref, PREFS[pref]);
+ }
+
+ do_register_cleanup(() => {
+ // Reset the prefs.
+ for (let pref in PREFS) {
+ Preferences.reset(pref);
+ }
+ });
+
+ function checkPrefs(expected) {
+ for (let pref in PREFS) {
+ let msg = `${pref} set correctly.`;
+ let expectedValue = expected ? PREFS[pref] : !PREFS[pref];
+ if (pref === "network.http.speculative-parallel-limit") {
+ expectedValue = expected ? ExtensionPreferencesManager.getDefaultValue(pref) : 0;
+ }
+ equal(Preferences.get(pref), expectedValue, msg);
+ }
+ }
+
+ async function background() {
+ browser.test.onMessage.addListener(async (msg, data) => {
+ await browser.privacy.network.networkPredictionEnabled.set(data);
+ let settingData = await browser.privacy.network.networkPredictionEnabled.get({});
+ browser.test.sendMessage("privacyData", settingData);
+ });
+ }
+
+ // Create an array of extensions to install.
+ let testExtensions = [
+ ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ applications: {
+ gecko: {
+ id: OLD_ID,
+ },
+ },
+ permissions: ["privacy"],
+ },
+ useAddonManager: "temporary",
+ }),
+
+ ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ applications: {
+ gecko: {
+ id: NEW_ID,
+ },
+ },
+ permissions: ["privacy"],
+ },
+ useAddonManager: "temporary",
+ }),
+ ];
+
+ await promiseStartupManager();
+
+ for (let extension of testExtensions) {
+ await extension.startup();
+ }
+
+ // Set the value to true for the older extension.
+ testExtensions[0].sendMessage("set", {value: true});
+ let data = await testExtensions[0].awaitMessage("privacyData");
+ ok(data.value, "Value set to true for the older extension.");
+
+ // Set the value to false for the newest extension.
+ testExtensions[1].sendMessage("set", {value: false});
+ data = await testExtensions[1].awaitMessage("privacyData");
+ ok(!data.value, "Value set to false for the newest extension.");
+
+ // Verify the prefs have been set to match the "false" setting.
+ checkPrefs(false);
+
+ // Disable the newest extension.
+ let disabledPromise = awaitPrefChange(PREF_TO_WATCH);
+ let newAddon = await AddonManager.getAddonByID(NEW_ID);
+ newAddon.userDisabled = true;
+ await disabledPromise;
+
+ // Verify the prefs have been set to match the "true" setting.
+ checkPrefs(true);
+
+ // Disable the older extension.
+ disabledPromise = awaitPrefChange(PREF_TO_WATCH);
+ let oldAddon = await AddonManager.getAddonByID(OLD_ID);
+ oldAddon.userDisabled = true;
+ await disabledPromise;
+
+ // Verify the prefs have reverted back to their initial values.
+ for (let pref in PREFS) {
+ equal(Preferences.get(pref), PREFS[pref], `${pref} reset correctly.`);
+ }
+
+ // Re-enable the newest extension.
+ let enabledPromise = awaitEvent("ready");
+ newAddon.userDisabled = false;
+ await enabledPromise;
+
+ // Verify the prefs have been set to match the "false" setting.
+ checkPrefs(false);
+
+ // Re-enable the older extension.
+ enabledPromise = awaitEvent("ready");
+ oldAddon.userDisabled = false;
+ await enabledPromise;
+
+ // Verify the prefs have remained set to match the "false" setting.
+ checkPrefs(false);
+
+ for (let extension of testExtensions) {
+ await extension.unload();
+ }
+
+ await promiseShutdownManager();
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_privacy_update.js
@@ -0,0 +1,180 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+XPCOMUtils.defineLazyGetter(this, "Management", () => {
+ const {Management} = Cu.import("resource://gre/modules/Extension.jsm", {});
+ return Management;
+});
+
+XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
+ "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+ "resource://gre/modules/Preferences.jsm");
+
+const {
+ createAppInfo,
+ createTempWebExtensionFile,
+ promiseAddonEvent,
+ promiseCompleteAllInstalls,
+ promiseFindAddonUpdates,
+ promiseShutdownManager,
+ promiseStartupManager,
+} = AddonTestUtils;
+
+AddonTestUtils.init(this);
+
+// Allow for unsigned addons.
+AddonTestUtils.overrideCertDB();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+function awaitEvent(eventName) {
+ return new Promise(resolve => {
+ let listener = (_eventName, ...args) => {
+ if (_eventName === eventName) {
+ Management.off(eventName, listener);
+ resolve(...args);
+ }
+ };
+
+ Management.on(eventName, listener);
+ });
+}
+
+add_task(async function test_privacy_update() {
+ // Create a object to hold the values to which we will initialize the prefs.
+ const PREFS = {
+ "network.predictor.enabled": true,
+ "network.prefetch-next": true,
+ "network.http.speculative-parallel-limit": 10,
+ "network.dns.disablePrefetch": false,
+ };
+
+ const EXTENSION_ID = "test_privacy_addon_update@tests.mozilla.org";
+ const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
+
+ // Set prefs to our initial values.
+ for (let pref in PREFS) {
+ Preferences.set(pref, PREFS[pref]);
+ }
+
+ do_register_cleanup(() => {
+ // Reset the prefs.
+ for (let pref in PREFS) {
+ Preferences.reset(pref);
+ }
+ });
+
+ async function background() {
+ browser.test.onMessage.addListener(async (msg, data) => {
+ let settingData;
+ switch (msg) {
+ case "get":
+ settingData = await browser.privacy.network.networkPredictionEnabled.get({});
+ browser.test.sendMessage("privacyData", settingData);
+ break;
+
+ case "set":
+ await browser.privacy.network.networkPredictionEnabled.set(data);
+ settingData = await browser.privacy.network.networkPredictionEnabled.get({});
+ browser.test.sendMessage("privacyData", settingData);
+ break;
+ }
+ });
+ }
+
+ const testServer = createHttpServer();
+ const port = testServer.identity.primaryPort;
+
+ // The test extension uses an insecure update url.
+ Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+ testServer.registerPathHandler("/test_update.json", (request, response) => {
+ response.write(`{
+ "addons": {
+ "${EXTENSION_ID}": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:${port}/addons/test_privacy-2.0.xpi"
+ }
+ ]
+ }
+ }
+ }`);
+ });
+
+ let webExtensionFile = createTempWebExtensionFile({
+ manifest: {
+ version: "2.0",
+ applications: {
+ gecko: {
+ id: EXTENSION_ID,
+ },
+ },
+ permissions: ["privacy"],
+ },
+ background,
+ });
+
+ testServer.registerFile("/addons/test_privacy-2.0.xpi", webExtensionFile);
+
+ await promiseStartupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": EXTENSION_ID,
+ "update_url": `http://localhost:${port}/test_update.json`,
+ },
+ },
+ permissions: ["privacy"],
+ },
+ background,
+ });
+
+ await extension.startup();
+
+ // Change the value to false.
+ extension.sendMessage("set", {value: false});
+ let data = await extension.awaitMessage("privacyData");
+ ok(!data.value, "get returns expected value after setting.");
+
+ let addon = await AddonManager.getAddonByID(EXTENSION_ID);
+ equal(addon.version, "1.0", "The installed addon has the expected version.");
+
+ let update = await promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ let promiseInstalled = promiseAddonEvent("onInstalled");
+ await promiseCompleteAllInstalls([install]);
+
+ let startupPromise = awaitEvent("ready");
+
+ let [updated_addon] = await promiseInstalled;
+ equal(updated_addon.version, "2.0", "The updated addon has the expected version.");
+
+ extension.extension = await startupPromise;
+ extension.attachListeners();
+
+ extension.sendMessage("get");
+ data = await extension.awaitMessage("privacyData");
+ ok(!data.value, "get returns expected value after updating.");
+
+ // Verify the prefs are still set to match the "false" setting.
+ for (let pref in PREFS) {
+ let msg = `${pref} set correctly.`;
+ let expectedValue = pref === "network.http.speculative-parallel-limit" ? 0 : !PREFS[pref];
+ equal(Preferences.get(pref), expectedValue, msg);
+ }
+
+ await extension.unload();
+
+ await updated_addon.uninstall();
+
+ await promiseShutdownManager();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -44,16 +44,18 @@ skip-if = release_or_beta
[test_ext_management.js]
[test_ext_management_uninstall_self.js]
[test_ext_manifest_content_security_policy.js]
[test_ext_manifest_incognito.js]
[test_ext_manifest_minimum_chrome_version.js]
[test_ext_onmessage_removelistener.js]
skip-if = true # This test no longer tests what it is meant to test.
[test_ext_privacy.js]
+[test_ext_privacy_disable.js]
+[test_ext_privacy_update.js]
[test_ext_runtime_connect_no_receiver.js]
[test_ext_runtime_getBrowserInfo.js]
[test_ext_runtime_getPlatformInfo.js]
[test_ext_runtime_onInstalled_and_onStartup.js]
[test_ext_runtime_sendMessage.js]
[test_ext_runtime_sendMessage_errors.js]
[test_ext_runtime_sendMessage_no_receiver.js]
[test_ext_runtime_sendMessage_self.js]