--- a/browser/components/extensions/parent/ext-urlbar.js
+++ b/browser/components/extensions/parent/ext-urlbar.js
@@ -220,27 +220,27 @@ this.urlbar = class extends ExtensionAPI
return fire.async(resultPayload).catch(error => {
throw context.normalizeError(error);
});
});
return () => provider.setEventListener("resultPicked", null);
},
}).api(),
- openViewOnFocus: getSettingsAPI({
- context,
- name: "openViewOnFocus",
- callback: () => UrlbarPrefs.get("openViewOnFocus"),
- }),
+ openViewOnFocus: getSettingsAPI(
+ context.extension.id,
+ "openViewOnFocus",
+ () => UrlbarPrefs.get("openViewOnFocus")
+ ),
- engagementTelemetry: getSettingsAPI({
- context,
- name: "engagementTelemetry",
- callback: () => UrlbarPrefs.get("eventTelemetry.enabled"),
- }),
+ engagementTelemetry: getSettingsAPI(
+ context.extension.id,
+ "engagementTelemetry",
+ () => UrlbarPrefs.get("eventTelemetry.enabled")
+ ),
contextualTip: {
/**
* Sets the contextual tip's icon,
* title, button title, and link title.
*
* @param {object} details
* If null, then the contextual tip will be hidden.
--- a/toolkit/components/extensions/ExtensionPreferencesManager.jsm
+++ b/toolkit/components/extensions/ExtensionPreferencesManager.jsm
@@ -17,18 +17,16 @@
* prefNames: An array of strings, each of which is a preference on
* which the setting depends.
* setCallback: A function that returns an object containing properties and
* values that correspond to the prefs to be set.
*/
var EXPORTED_SYMBOLS = ["ExtensionPreferencesManager"];
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
const { Management } = ChromeUtils.import(
"resource://gre/modules/Extension.jsm",
null
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
@@ -38,44 +36,33 @@ ChromeUtils.defineModuleGetter(
"ExtensionSettingsStore",
"resource://gre/modules/ExtensionSettingsStore.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Preferences",
"resource://gre/modules/Preferences.jsm"
);
-ChromeUtils.defineModuleGetter(
- this,
- "ExtensionCommon",
- "resource://gre/modules/ExtensionCommon.jsm"
-);
-
-const { ExtensionUtils } = ChromeUtils.import(
- "resource://gre/modules/ExtensionUtils.jsm"
-);
-
-const { ExtensionError } = ExtensionUtils;
XPCOMUtils.defineLazyGetter(this, "defaultPreferences", function() {
return new Preferences({ defaultBranch: true });
});
/* eslint-disable mozilla/balanced-listeners */
Management.on("uninstall", (type, { id }) => {
ExtensionPreferencesManager.removeAll(id);
});
Management.on("disable", (type, id) => {
- ExtensionPreferencesManager.disableAll(id);
+ this.ExtensionPreferencesManager.disableAll(id);
});
Management.on("startup", async (type, extension) => {
if (extension.startupReason == "ADDON_ENABLE") {
- ExtensionPreferencesManager.enableAll(extension.id);
+ this.ExtensionPreferencesManager.enableAll(extension.id);
}
});
/* eslint-enable mozilla/balanced-listeners */
const STORE_TYPE = "prefs";
// Definitions of settings, each of which correspond to a different API.
let settingsMap = new Map();
@@ -123,44 +110,41 @@ function settingsUpdate(initialValue) {
}
}
return initialValue;
}
/**
* Loops through a set of prefs, either setting or resetting them.
*
- * @param {string} name
- * The api name of the setting.
* @param {Object} setting
* An object that represents a setting, which will have a setCallback
* property. If a onPrefsChanged function is provided it will be called
* with item when the preferences change.
* @param {Object} item
* An object that represents an item handed back from the setting store
* from which the new pref values can be calculated.
*/
-function setPrefs(name, setting, item) {
+function setPrefs(setting, item) {
let prefs = item.initialValue || setting.setCallback(item.value);
let changed = false;
for (let pref of setting.prefNames) {
if (prefs[pref] === undefined) {
if (Preferences.isSet(pref)) {
changed = true;
Preferences.reset(pref);
}
} else if (Preferences.get(pref) != prefs[pref]) {
Preferences.set(pref, prefs[pref]);
changed = true;
}
}
if (changed && typeof setting.onPrefsChanged == "function") {
setting.onPrefsChanged(item);
}
- Management.emit(`extension-setting-changed:${name}`);
}
/**
* Commits a change to a setting and conditionally sets preferences.
*
* If the change to the setting causes a different extension to gain
* control of the pref (or removes all extensions with control over the pref)
* then the prefs should be updated, otherwise they should not be.
@@ -192,17 +176,17 @@ async function processSetting(id, name,
if (
Object.keys(expectedPrefs).some(
pref =>
expectedPrefs[pref] && Preferences.get(pref) != expectedPrefs[pref]
)
) {
return false;
}
- setPrefs(name, setting, item);
+ setPrefs(setting, item);
return true;
}
return false;
}
this.ExtensionPreferencesManager = {
/**
* Adds a setting to the settingsMap. This is how an API tells the
@@ -254,17 +238,17 @@ this.ExtensionPreferencesManager = {
STORE_TYPE,
name,
value,
initialValueCallback.bind(setting),
name,
settingsUpdate.bind(setting)
);
if (item) {
- setPrefs(name, setting, item);
+ setPrefs(setting, item);
return true;
}
return false;
},
/**
* Indicates that this extension wants to temporarily cede control over the
* given setting.
@@ -410,102 +394,40 @@ this.ExtensionPreferencesManager = {
}
await ExtensionSettingsStore.initialize();
return ExtensionSettingsStore.getLevelOfControl(id, storeType, name);
},
/**
* Returns an API object with get/set/clear used for a setting.
*
- * @param {string|object} extensionId or params object
+ * @param {string} extensionId
* @param {string} name
- * The unique id of the setting.
+ * The unique id of the setting.
* @param {Function} callback
- * The function that retreives the current setting from prefs.
+ * The function that retreives the current setting from prefs.
* @param {string} storeType
- * The name of the store in ExtensionSettingsStore.
- * Defaults to STORE_TYPE.
+ * The name of the store in ExtensionSettingsStore.
+ * Defaults to STORE_TYPE.
* @param {boolean} readOnly
* @param {Function} validate
- * Utility function for any specific validation, such as checking
- * for supported platform. Function should throw an error if necessary.
+ * Utility function for any specific validation, such as checking
+ * for supported platform. Function should throw an error if necessary.
*
* @returns {object} API object with get/set/clear methods
*/
getSettingsAPI(
extensionId,
name,
callback,
storeType,
readOnly = false,
validate = () => {}
) {
- if (arguments.length > 1) {
- Services.console.logStringMessage(
- `ExtensionPreferencesManager.getSettingsAPI for ${name} should be updated to use a single paramater object.`
- );
- }
- return ExtensionPreferencesManager._getSettingsAPI(
- arguments.length === 1
- ? extensionId
- : {
- extensionId,
- name,
- storeType,
- readOnly,
- validate,
- }
- );
- },
-
- /**
- * Returns an API object with get/set/clear used for a setting.
- *
- * @param {object} params The params object contains the following:
- * {BaseContext} context
- * {string} extensionId, optional to support old API
- * {string} name
- * The unique id of the setting.
- * {Function} callback
- * The function that retreives the current setting from prefs.
- * {string} storeType
- * The name of the store in ExtensionSettingsStore.
- * Defaults to STORE_TYPE.
- * {boolean} readOnly
- * {Function} validate
- * Utility function for any specific validation, such as checking
- * for supported platform. Function should throw an error if necessary.
- *
- * @returns {object} API object with get/set/clear methods
- */
- _getSettingsAPI(params) {
- let {
- extensionId,
- context,
- name,
- callback,
- storeType,
- readOnly = false,
- onChange,
- validate = () => {},
- } = params;
- if (!extensionId) {
- extensionId = context.extension.id;
- }
-
- const checkScope = details => {
- let { scope } = details;
- if (scope && scope !== "regular") {
- throw new ExtensionError(
- `Firefox does not support the ${scope} settings scope.`
- );
- }
- };
-
- let settingsAPI = {
+ return {
async get(details) {
validate();
let levelOfControl = details.incognito
? "not_controllable"
: await ExtensionPreferencesManager.getLevelOfControl(
extensionId,
name,
storeType
@@ -516,61 +438,27 @@ this.ExtensionPreferencesManager = {
: levelOfControl;
return {
levelOfControl,
value: await callback(),
};
},
set(details) {
validate();
- checkScope(details);
if (!readOnly) {
return ExtensionPreferencesManager.setSetting(
extensionId,
name,
details.value
);
}
return false;
},
clear(details) {
validate();
- checkScope(details);
if (!readOnly) {
return ExtensionPreferencesManager.removeSetting(extensionId, name);
}
return false;
},
- onChange,
};
- // Any caller using the old call signature will not have passed
- // context to us. This should only be experimental addons in the
- // wild.
- if (onChange === undefined && context) {
- // Some settings that are read-only may not have called addSetting, in
- // which case we have no way to listen on the pref changes.
- let setting = settingsMap.get(name);
- if (!setting) {
- Services.console.logStringMessage(
- `ExtensionPreferencesManager API ${name} created but addSetting was not called.`
- );
- return settingsAPI;
- }
-
- settingsAPI.onChange = new ExtensionCommon.EventManager({
- context,
- name: `${name}.onChange`,
- register: fire => {
- let listener = async () => {
- fire.async({
- details: await settingsAPI.get({}),
- });
- };
- Management.on(`extension-setting-changed:${name}`, listener);
- return () => {
- Management.off(`extension-setting-changed:${name}`, listener);
- };
- },
- }).api();
- }
- return settingsAPI;
},
};
--- a/toolkit/components/extensions/parent/ext-browserSettings.js
+++ b/toolkit/components/extensions/parent/ext-browserSettings.js
@@ -73,24 +73,16 @@ ExtensionPreferencesManager.addSetting("
ExtensionPreferencesManager.addSetting("contextMenuShowEvent", {
prefNames: ["ui.context_menus.after_mouseup"],
setCallback(value) {
return { [this.prefNames[0]]: value === "mouseup" };
},
});
-ExtensionPreferencesManager.addSetting(HOMEPAGE_OVERRIDE_SETTING, {
- prefNames: [HOMEPAGE_URL_PREF],
-
- setCallback() {
- throw new Error("Unable to set read-only setting");
- },
-});
-
ExtensionPreferencesManager.addSetting("ftpProtocolEnabled", {
prefNames: ["network.ftp.enabled"],
setCallback(value) {
return { [this.prefNames[0]]: value };
},
});
@@ -164,63 +156,57 @@ ExtensionPreferencesManager.addSetting("
},
});
this.browserSettings = class extends ExtensionAPI {
getAPI(context) {
let { extension } = context;
return {
browserSettings: {
- allowPopupsForUserEvents: getSettingsAPI({
- context,
- name: "allowPopupsForUserEvents",
- callback() {
+ allowPopupsForUserEvents: getSettingsAPI(
+ extension.id,
+ "allowPopupsForUserEvents",
+ () => {
return Services.prefs.getCharPref("dom.popup_allowed_events") != "";
- },
+ }
+ ),
+ cacheEnabled: getSettingsAPI(extension.id, "cacheEnabled", () => {
+ return (
+ Services.prefs.getBoolPref("browser.cache.disk.enable") &&
+ Services.prefs.getBoolPref("browser.cache.memory.enable")
+ );
}),
- cacheEnabled: getSettingsAPI({
- context,
- name: "cacheEnabled",
- callback() {
- return (
- Services.prefs.getBoolPref("browser.cache.disk.enable") &&
- Services.prefs.getBoolPref("browser.cache.memory.enable")
- );
- },
- }),
- closeTabsByDoubleClick: getSettingsAPI({
- context,
- name: "closeTabsByDoubleClick",
- callback() {
+ closeTabsByDoubleClick: getSettingsAPI(
+ extension.id,
+ "closeTabsByDoubleClick",
+ () => {
return Services.prefs.getBoolPref(
"browser.tabs.closeTabByDblclick"
);
},
- validate() {
+ undefined,
+ false,
+ () => {
if (AppConstants.platform == "android") {
throw new ExtensionError(
`android is not a supported platform for the closeTabsByDoubleClick setting.`
);
}
- },
- }),
+ }
+ ),
contextMenuShowEvent: Object.assign(
- getSettingsAPI({
- context,
- name: "contextMenuShowEvent",
- callback() {
- if (AppConstants.platform === "win") {
- return "mouseup";
- }
- let prefValue = Services.prefs.getBoolPref(
- "ui.context_menus.after_mouseup",
- null
- );
- return prefValue ? "mouseup" : "mousedown";
- },
+ getSettingsAPI(extension.id, "contextMenuShowEvent", () => {
+ if (AppConstants.platform === "win") {
+ return "mouseup";
+ }
+ let prefValue = Services.prefs.getBoolPref(
+ "ui.context_menus.after_mouseup",
+ null
+ );
+ return prefValue ? "mouseup" : "mousedown";
}),
{
set: details => {
if (!["mouseup", "mousedown"].includes(details.value)) {
throw new ExtensionError(
`${
details.value
} is not a valid value for contextMenuShowEvent.`
@@ -236,131 +222,104 @@ this.browserSettings = class extends Ext
return ExtensionPreferencesManager.setSetting(
extension.id,
"contextMenuShowEvent",
details.value
);
},
}
),
- ftpProtocolEnabled: getSettingsAPI({
- context,
- name: "ftpProtocolEnabled",
- callback() {
+ ftpProtocolEnabled: getSettingsAPI(
+ extension.id,
+ "ftpProtocolEnabled",
+ () => {
return Services.prefs.getBoolPref("network.ftp.enabled");
- },
- }),
- homepageOverride: getSettingsAPI({
- context,
- name: HOMEPAGE_OVERRIDE_SETTING,
- callback() {
+ }
+ ),
+ homepageOverride: getSettingsAPI(
+ extension.id,
+ HOMEPAGE_OVERRIDE_SETTING,
+ () => {
return Services.prefs.getStringPref(HOMEPAGE_URL_PREF);
},
- readOnly: true,
- }),
- imageAnimationBehavior: getSettingsAPI({
- context,
- name: "imageAnimationBehavior",
- callback() {
+ undefined,
+ true
+ ),
+ imageAnimationBehavior: getSettingsAPI(
+ extension.id,
+ "imageAnimationBehavior",
+ () => {
return Services.prefs.getCharPref("image.animation_mode");
- },
+ }
+ ),
+ newTabPosition: getSettingsAPI(extension.id, "newTabPosition", () => {
+ if (Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")) {
+ return "afterCurrent";
+ }
+ if (
+ Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")
+ ) {
+ return "relatedAfterCurrent";
+ }
+ return "atEnd";
}),
- newTabPosition: getSettingsAPI({
- context,
- name: "newTabPosition",
- callback() {
- if (Services.prefs.getBoolPref("browser.tabs.insertAfterCurrent")) {
- return "afterCurrent";
- }
- if (
- Services.prefs.getBoolPref(
- "browser.tabs.insertRelatedAfterCurrent"
- )
- ) {
- return "relatedAfterCurrent";
- }
- return "atEnd";
- },
- }),
- newTabPageOverride: getSettingsAPI({
- context,
- name: NEW_TAB_OVERRIDE_SETTING,
- callback() {
+ newTabPageOverride: getSettingsAPI(
+ extension.id,
+ NEW_TAB_OVERRIDE_SETTING,
+ () => {
return aboutNewTabService.newTabURL;
},
- storeType: URL_STORE_TYPE,
- readOnly: true,
- onChange: new ExtensionCommon.EventManager({
- context,
- name: `${NEW_TAB_OVERRIDE_SETTING}.onChange`,
- register: fire => {
- let listener = (text, id) => {
- fire.async({
- details: {
- levelOfControl: "not_controllable",
- value: aboutNewTabService.newTabURL,
- },
- });
- };
- Services.obs.addObserver(listener, "newtab-url-changed");
- return () => {
- Services.obs.removeObserver(listener, "newtab-url-changed");
- };
- },
- }).api(),
- }),
- openBookmarksInNewTabs: getSettingsAPI({
- context,
- name: "openBookmarksInNewTabs",
- callback() {
+ URL_STORE_TYPE,
+ true
+ ),
+ openBookmarksInNewTabs: getSettingsAPI(
+ extension.id,
+ "openBookmarksInNewTabs",
+ () => {
return Services.prefs.getBoolPref(
"browser.tabs.loadBookmarksInTabs"
);
- },
- }),
- openSearchResultsInNewTabs: getSettingsAPI({
- context,
- name: "openSearchResultsInNewTabs",
- callback() {
+ }
+ ),
+ openSearchResultsInNewTabs: getSettingsAPI(
+ extension.id,
+ "openSearchResultsInNewTabs",
+ () => {
return Services.prefs.getBoolPref("browser.search.openintab");
- },
- }),
- openUrlbarResultsInNewTabs: getSettingsAPI({
- context,
- name: "openUrlbarResultsInNewTabs",
- callback() {
+ }
+ ),
+ openUrlbarResultsInNewTabs: getSettingsAPI(
+ extension.id,
+ "openUrlbarResultsInNewTabs",
+ () => {
return Services.prefs.getBoolPref("browser.urlbar.openintab");
- },
- }),
- webNotificationsDisabled: getSettingsAPI({
- context,
- name: "webNotificationsDisabled",
- callback() {
+ }
+ ),
+ webNotificationsDisabled: getSettingsAPI(
+ extension.id,
+ "webNotificationsDisabled",
+ () => {
let prefValue = Services.prefs.getIntPref(
"permissions.default.desktop-notification",
null
);
return prefValue === PERM_DENY_ACTION;
- },
- }),
+ }
+ ),
overrideDocumentColors: Object.assign(
- getSettingsAPI({
- context,
- name: "overrideDocumentColors",
- callback() {
- let prefValue = Services.prefs.getIntPref(
- "browser.display.document_color_use"
- );
- if (prefValue === 1) {
- return "never";
- } else if (prefValue === 2) {
- return "always";
- }
- return "high-contrast-only";
- },
+ getSettingsAPI(extension.id, "overrideDocumentColors", () => {
+ let prefValue = Services.prefs.getIntPref(
+ "browser.display.document_color_use"
+ );
+ if (prefValue === 1) {
+ return "never";
+ } else if (prefValue === 2) {
+ return "always";
+ }
+ return "high-contrast-only";
}),
{
set: details => {
if (
!["never", "always", "high-contrast-only"].includes(
details.value
)
) {
@@ -380,26 +339,22 @@ this.browserSettings = class extends Ext
extension.id,
"overrideDocumentColors",
prefValue
);
},
}
),
useDocumentFonts: Object.assign(
- getSettingsAPI({
- context,
- name: "useDocumentFonts",
- callback() {
- return (
- Services.prefs.getIntPref(
- "browser.display.use_document_fonts"
- ) !== 0
- );
- },
+ getSettingsAPI(extension.id, "useDocumentFonts", () => {
+ return (
+ Services.prefs.getIntPref(
+ "browser.display.use_document_fonts"
+ ) !== 0
+ );
}),
{
set: details => {
if (typeof details.value !== "boolean") {
throw new ExtensionError(
`${details.value} is not a valid value for useDocumentFonts.`
);
}
--- a/toolkit/components/extensions/parent/ext-privacy.js
+++ b/toolkit/components/extensions/parent/ext-privacy.js
@@ -10,28 +10,65 @@ ChromeUtils.defineModuleGetter(
this,
"Preferences",
"resource://gre/modules/Preferences.jsm"
);
var { ExtensionPreferencesManager } = ChromeUtils.import(
"resource://gre/modules/ExtensionPreferencesManager.jsm"
);
-var { getSettingsAPI } = ExtensionPreferencesManager;
+
+var { ExtensionError } = ExtensionUtils;
const cookieSvc = Ci.nsICookieService;
const cookieBehaviorValues = new Map([
["allow_all", cookieSvc.BEHAVIOR_ACCEPT],
["reject_third_party", cookieSvc.BEHAVIOR_REJECT_FOREIGN],
["reject_all", cookieSvc.BEHAVIOR_REJECT],
["allow_visited", cookieSvc.BEHAVIOR_LIMIT_FOREIGN],
["reject_trackers", cookieSvc.BEHAVIOR_REJECT_TRACKER],
]);
+const checkScope = scope => {
+ if (scope && scope !== "regular") {
+ throw new ExtensionError(
+ `Firefox does not support the ${scope} settings scope.`
+ );
+ }
+};
+
+const getPrivacyAPI = (extension, name, callback) => {
+ return {
+ async get(details) {
+ return {
+ levelOfControl: details.incognito
+ ? "not_controllable"
+ : await ExtensionPreferencesManager.getLevelOfControl(
+ extension.id,
+ name
+ ),
+ value: await callback(),
+ };
+ },
+ set(details) {
+ checkScope(details.scope);
+ return ExtensionPreferencesManager.setSetting(
+ extension.id,
+ name,
+ details.value
+ );
+ },
+ clear(details) {
+ checkScope(details.scope);
+ return ExtensionPreferencesManager.removeSetting(extension.id, name);
+ },
+ };
+};
+
// Add settings objects for supported APIs to the preferences manager.
ExtensionPreferencesManager.addSetting("network.networkPredictionEnabled", {
prefNames: [
"network.predictor.enabled",
"network.prefetch-next",
"network.http.speculative-parallel-limit",
"network.dns.disablePrefetch",
],
@@ -175,43 +212,44 @@ ExtensionPreferencesManager.addSetting("
}
return prefs;
},
});
this.privacy = class extends ExtensionAPI {
getAPI(context) {
+ let { extension } = context;
return {
privacy: {
network: {
- networkPredictionEnabled: getSettingsAPI({
- context,
- name: "network.networkPredictionEnabled",
- callback() {
+ networkPredictionEnabled: getPrivacyAPI(
+ 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")
);
- },
- }),
- peerConnectionEnabled: getSettingsAPI({
- context,
- name: "network.peerConnectionEnabled",
- callback() {
+ }
+ ),
+ peerConnectionEnabled: getPrivacyAPI(
+ extension,
+ "network.peerConnectionEnabled",
+ () => {
return Preferences.get("media.peerconnection.enabled");
- },
- }),
- webRTCIPHandlingPolicy: getSettingsAPI({
- context,
- name: "network.webRTCIPHandlingPolicy",
- callback() {
+ }
+ ),
+ webRTCIPHandlingPolicy: getPrivacyAPI(
+ extension,
+ "network.webRTCIPHandlingPolicy",
+ () => {
if (Preferences.get("media.peerconnection.ice.proxy_only")) {
return "proxy_only";
}
let default_address_only = Preferences.get(
"media.peerconnection.ice.default_address_only"
);
if (default_address_only) {
@@ -227,85 +265,85 @@ this.privacy = class extends ExtensionAP
return "disable_non_proxied_udp";
}
return "default_public_interface_only";
}
return "default_public_and_private_interfaces";
}
return "default";
- },
- }),
+ }
+ ),
},
services: {
- passwordSavingEnabled: getSettingsAPI({
- context,
- name: "services.passwordSavingEnabled",
- callback() {
+ passwordSavingEnabled: getPrivacyAPI(
+ extension,
+ "services.passwordSavingEnabled",
+ () => {
return Preferences.get("signon.rememberSignons");
- },
- }),
+ }
+ ),
},
websites: {
- cookieConfig: getSettingsAPI({
- context,
- name: "websites.cookieConfig",
- callback() {
+ cookieConfig: getPrivacyAPI(
+ extension,
+ "websites.cookieConfig",
+ () => {
let prefValue = Preferences.get("network.cookie.cookieBehavior");
return {
behavior: Array.from(cookieBehaviorValues.entries()).find(
entry => entry[1] === prefValue
)[0],
nonPersistentCookies:
Preferences.get("network.cookie.lifetimePolicy") ===
cookieSvc.ACCEPT_SESSION,
};
- },
- }),
- firstPartyIsolate: getSettingsAPI({
- context,
- name: "websites.firstPartyIsolate",
- callback() {
+ }
+ ),
+ firstPartyIsolate: getPrivacyAPI(
+ extension,
+ "websites.firstPartyIsolate",
+ () => {
return Preferences.get("privacy.firstparty.isolate");
- },
- }),
- hyperlinkAuditingEnabled: getSettingsAPI({
- context,
- name: "websites.hyperlinkAuditingEnabled",
- callback() {
+ }
+ ),
+ hyperlinkAuditingEnabled: getPrivacyAPI(
+ extension,
+ "websites.hyperlinkAuditingEnabled",
+ () => {
return Preferences.get("browser.send_pings");
- },
- }),
- referrersEnabled: getSettingsAPI({
- context,
- name: "websites.referrersEnabled",
- callback() {
+ }
+ ),
+ referrersEnabled: getPrivacyAPI(
+ extension,
+ "websites.referrersEnabled",
+ () => {
return Preferences.get("network.http.sendRefererHeader") !== 0;
- },
- }),
- resistFingerprinting: getSettingsAPI({
- context,
- name: "websites.resistFingerprinting",
- callback() {
+ }
+ ),
+ resistFingerprinting: getPrivacyAPI(
+ extension,
+ "websites.resistFingerprinting",
+ () => {
return Preferences.get("privacy.resistFingerprinting");
- },
- }),
- trackingProtectionMode: getSettingsAPI({
- context,
- name: "websites.trackingProtectionMode",
- callback() {
+ }
+ ),
+ trackingProtectionMode: getPrivacyAPI(
+ extension,
+ "websites.trackingProtectionMode",
+ () => {
if (Preferences.get("privacy.trackingprotection.enabled")) {
return "always";
} else if (
Preferences.get("privacy.trackingprotection.pbmode.enabled")
) {
return "private_browsing";
}
return "never";
- },
- }),
+ }
+ ),
},
},
};
}
};
--- a/toolkit/components/extensions/parent/ext-proxy.js
+++ b/toolkit/components/extensions/parent/ext-proxy.js
@@ -168,20 +168,20 @@ this.proxy = class extends ExtensionAPI
extension.on("proxy-error", listener);
return () => {
extension.off("proxy-error", listener);
};
},
}).api(),
settings: Object.assign(
- getSettingsAPI({
- context,
- name: "proxy.settings",
- callback() {
+ getSettingsAPI(
+ extension.id,
+ "proxy.settings",
+ () => {
let prefValue = Services.prefs.getIntPref("network.proxy.type");
let proxyConfig = {
proxyType: Array.from(PROXY_TYPES_MAP.entries()).find(
entry => entry[1] === prefValue
)[0],
autoConfigUrl: Services.prefs.getCharPref(
"network.proxy.autoconfig_url"
),
@@ -209,24 +209,26 @@ this.proxy = class extends ExtensionAPI
`network.proxy.${prop}_port`
);
proxyConfig[prop] = port ? `${host}:${port}` : host;
}
return proxyConfig;
},
// proxy.settings is unsupported on android.
- validate() {
+ undefined,
+ false,
+ () => {
if (AppConstants.platform == "android") {
throw new ExtensionError(
`proxy.settings is not supported on android.`
);
}
- },
- }),
+ }
+ ),
{
set: details => {
if (AppConstants.platform === "android") {
throw new ExtensionError(
"proxy.settings is not supported on android."
);
}
--- a/toolkit/components/extensions/schemas/types.json
+++ b/toolkit/components/extensions/schemas/types.json
@@ -128,16 +128,17 @@
]
}
],
"events": [
{
"name": "onChange",
"type": "function",
"description": "Fired after the setting changes.",
+ "unsupported": true,
"parameters": [
{
"type": "object",
"name": "details",
"properties": {
"value": {
"description": "The value of the setting after the change.",
"type": "any"
--- a/toolkit/components/extensions/test/xpcshell/test_ext_browserSettings.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_browserSettings.js
@@ -35,30 +35,18 @@ add_task(async function test_browser_set
"browser.search.openintab": false,
"browser.tabs.insertRelatedAfterCurrent": true,
"browser.tabs.insertAfterCurrent": false,
"browser.display.document_color_use": 1,
"browser.display.use_document_fonts": 1,
};
async function background() {
- let listeners = new Set([]);
browser.test.onMessage.addListener(async (msg, apiName, value) => {
let apiObj = browser.browserSettings[apiName];
- // Don't add more than one listner per apiName. We leave the
- // listener to ensure we do not get more calls than we expect.
- if (!listeners.has(apiName)) {
- apiObj.onChange.addListener(details => {
- browser.test.sendMessage("onChange", {
- details: details.details,
- setting: apiName,
- });
- });
- listeners.add(apiName);
- }
let result = await apiObj.set({ value });
if (msg === "set") {
browser.test.assertTrue(result, "set returns true.");
browser.test.sendMessage("settingData", await apiObj.get({}));
} else {
browser.test.assertFalse(result, "set returns false for a no-op.");
browser.test.sendMessage("no-op set");
}
@@ -86,23 +74,16 @@ add_task(async function test_browser_set
});
await promiseStartupManager();
await extension.startup();
async function testSetting(setting, value, expected, expectedValue = value) {
extension.sendMessage("set", setting, value);
let data = await extension.awaitMessage("settingData");
- let dataChange = await extension.awaitMessage("onChange");
- equal(setting, dataChange.setting, "onChange fired");
- equal(
- data.value,
- dataChange.details.value,
- "onChange fired with correct value"
- );
deepEqual(
data.value,
expectedValue,
`The ${setting} setting has the expected value.`
);
equal(
data.levelOfControl,
"controlled_by_this_extension",
@@ -188,22 +169,22 @@ add_task(async function test_browser_set
await testSetting("closeTabsByDoubleClick", true, {
"browser.tabs.closeTabByDblclick": true,
});
await testSetting("closeTabsByDoubleClick", false, {
"browser.tabs.closeTabByDblclick": false,
});
}
+ await testSetting("ftpProtocolEnabled", true, {
+ "network.ftp.enabled": true,
+ });
await testSetting("ftpProtocolEnabled", false, {
"network.ftp.enabled": false,
});
- await testSetting("ftpProtocolEnabled", true, {
- "network.ftp.enabled": true,
- });
await testSetting("newTabPosition", "afterCurrent", {
"browser.tabs.insertRelatedAfterCurrent": false,
"browser.tabs.insertAfterCurrent": true,
});
await testSetting("newTabPosition", "atEnd", {
"browser.tabs.insertRelatedAfterCurrent": false,
"browser.tabs.insertAfterCurrent": false,