Bug 1410412 implement browser setting onChange event r=zombie

Differential Revision: https://phabricator.services.mozilla.com/D51324

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Shane Caraveo 2019-11-19 17:26:13 +00:00
Родитель f17dad37f7
Коммит 4c534e5697
8 изменённых файлов: 383 добавлений и 248 удалений

Просмотреть файл

@ -225,17 +225,17 @@ this.urlbar = class extends ExtensionAPI {
}, },
}).api(), }).api(),
openViewOnFocus: getSettingsAPI( openViewOnFocus: getSettingsAPI({
context.extension.id, context,
"openViewOnFocus", name: "openViewOnFocus",
() => UrlbarPrefs.get("openViewOnFocus") callback: () => UrlbarPrefs.get("openViewOnFocus"),
), }),
engagementTelemetry: getSettingsAPI( engagementTelemetry: getSettingsAPI({
context.extension.id, context,
"engagementTelemetry", name: "engagementTelemetry",
() => UrlbarPrefs.get("eventTelemetry.enabled") callback: () => UrlbarPrefs.get("eventTelemetry.enabled"),
), }),
contextualTip: { contextualTip: {
/** /**

Просмотреть файл

@ -22,6 +22,8 @@
var EXPORTED_SYMBOLS = ["ExtensionPreferencesManager"]; var EXPORTED_SYMBOLS = ["ExtensionPreferencesManager"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Management } = ChromeUtils.import( const { Management } = ChromeUtils.import(
"resource://gre/modules/Extension.jsm", "resource://gre/modules/Extension.jsm",
null null
@ -41,6 +43,17 @@ ChromeUtils.defineModuleGetter(
"Preferences", "Preferences",
"resource://gre/modules/Preferences.jsm" "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() { XPCOMUtils.defineLazyGetter(this, "defaultPreferences", function() {
return new Preferences({ defaultBranch: true }); return new Preferences({ defaultBranch: true });
@ -52,12 +65,12 @@ Management.on("uninstall", (type, { id }) => {
}); });
Management.on("disable", (type, id) => { Management.on("disable", (type, id) => {
this.ExtensionPreferencesManager.disableAll(id); ExtensionPreferencesManager.disableAll(id);
}); });
Management.on("startup", async (type, extension) => { Management.on("startup", async (type, extension) => {
if (extension.startupReason == "ADDON_ENABLE") { if (extension.startupReason == "ADDON_ENABLE") {
this.ExtensionPreferencesManager.enableAll(extension.id); ExtensionPreferencesManager.enableAll(extension.id);
} }
}); });
/* eslint-enable mozilla/balanced-listeners */ /* eslint-enable mozilla/balanced-listeners */
@ -115,6 +128,8 @@ function settingsUpdate(initialValue) {
/** /**
* Loops through a set of prefs, either setting or resetting them. * Loops through a set of prefs, either setting or resetting them.
* *
* @param {string} name
* The api name of the setting.
* @param {Object} setting * @param {Object} setting
* An object that represents a setting, which will have a setCallback * An object that represents a setting, which will have a setCallback
* property. If a onPrefsChanged function is provided it will be called * property. If a onPrefsChanged function is provided it will be called
@ -123,7 +138,7 @@ function settingsUpdate(initialValue) {
* An object that represents an item handed back from the setting store * An object that represents an item handed back from the setting store
* from which the new pref values can be calculated. * from which the new pref values can be calculated.
*/ */
function setPrefs(setting, item) { function setPrefs(name, setting, item) {
let prefs = item.initialValue || setting.setCallback(item.value); let prefs = item.initialValue || setting.setCallback(item.value);
let changed = false; let changed = false;
for (let pref of setting.prefNames) { for (let pref of setting.prefNames) {
@ -140,6 +155,7 @@ function setPrefs(setting, item) {
if (changed && typeof setting.onPrefsChanged == "function") { if (changed && typeof setting.onPrefsChanged == "function") {
setting.onPrefsChanged(item); setting.onPrefsChanged(item);
} }
Management.emit(`extension-setting-changed:${name}`);
} }
/** /**
@ -181,7 +197,7 @@ async function processSetting(id, name, action) {
) { ) {
return false; return false;
} }
setPrefs(setting, item); setPrefs(name, setting, item);
return true; return true;
} }
return false; return false;
@ -243,7 +259,7 @@ this.ExtensionPreferencesManager = {
settingsUpdate.bind(setting) settingsUpdate.bind(setting)
); );
if (item) { if (item) {
setPrefs(setting, item); setPrefs(name, setting, item);
return true; return true;
} }
return false; return false;
@ -399,18 +415,18 @@ this.ExtensionPreferencesManager = {
/** /**
* Returns an API object with get/set/clear used for a setting. * Returns an API object with get/set/clear used for a setting.
* *
* @param {string} extensionId * @param {string|object} extensionId or params object
* @param {string} name * @param {string} name
* The unique id of the setting. * The unique id of the setting.
* @param {Function} callback * @param {Function} callback
* The function that retreives the current setting from prefs. * The function that retreives the current setting from prefs.
* @param {string} storeType * @param {string} storeType
* The name of the store in ExtensionSettingsStore. * The name of the store in ExtensionSettingsStore.
* Defaults to STORE_TYPE. * Defaults to STORE_TYPE.
* @param {boolean} readOnly * @param {boolean} readOnly
* @param {Function} validate * @param {Function} validate
* Utility function for any specific validation, such as checking * Utility function for any specific validation, such as checking
* for supported platform. Function should throw an error if necessary. * for supported platform. Function should throw an error if necessary.
* *
* @returns {object} API object with get/set/clear methods * @returns {object} API object with get/set/clear methods
*/ */
@ -422,7 +438,70 @@ this.ExtensionPreferencesManager = {
readOnly = false, readOnly = false,
validate = () => {} validate = () => {}
) { ) {
return { 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,
callback,
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 = {
async get(details) { async get(details) {
validate(); validate();
let levelOfControl = details.incognito let levelOfControl = details.incognito
@ -443,6 +522,7 @@ this.ExtensionPreferencesManager = {
}, },
set(details) { set(details) {
validate(); validate();
checkScope(details);
if (!readOnly) { if (!readOnly) {
return ExtensionPreferencesManager.setSetting( return ExtensionPreferencesManager.setSetting(
extensionId, extensionId,
@ -454,11 +534,44 @@ this.ExtensionPreferencesManager = {
}, },
clear(details) { clear(details) {
validate(); validate();
checkScope(details);
if (!readOnly) { if (!readOnly) {
return ExtensionPreferencesManager.removeSetting(extensionId, name); return ExtensionPreferencesManager.removeSetting(extensionId, name);
} }
return false; 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;
}, },
}; };

Просмотреть файл

@ -78,6 +78,14 @@ ExtensionPreferencesManager.addSetting("contextMenuShowEvent", {
}, },
}); });
ExtensionPreferencesManager.addSetting(HOMEPAGE_OVERRIDE_SETTING, {
prefNames: [HOMEPAGE_URL_PREF],
setCallback() {
throw new Error("Unable to set read-only setting");
},
});
ExtensionPreferencesManager.addSetting("ftpProtocolEnabled", { ExtensionPreferencesManager.addSetting("ftpProtocolEnabled", {
prefNames: ["network.ftp.enabled"], prefNames: ["network.ftp.enabled"],
@ -161,47 +169,53 @@ this.browserSettings = class extends ExtensionAPI {
let { extension } = context; let { extension } = context;
return { return {
browserSettings: { browserSettings: {
allowPopupsForUserEvents: getSettingsAPI( allowPopupsForUserEvents: getSettingsAPI({
extension.id, context,
"allowPopupsForUserEvents", name: "allowPopupsForUserEvents",
() => { callback() {
return Services.prefs.getCharPref("dom.popup_allowed_events") != ""; 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")
);
}), }),
closeTabsByDoubleClick: getSettingsAPI( cacheEnabled: getSettingsAPI({
extension.id, context,
"closeTabsByDoubleClick", name: "cacheEnabled",
() => { callback() {
return (
Services.prefs.getBoolPref("browser.cache.disk.enable") &&
Services.prefs.getBoolPref("browser.cache.memory.enable")
);
},
}),
closeTabsByDoubleClick: getSettingsAPI({
context,
name: "closeTabsByDoubleClick",
callback() {
return Services.prefs.getBoolPref( return Services.prefs.getBoolPref(
"browser.tabs.closeTabByDblclick" "browser.tabs.closeTabByDblclick"
); );
}, },
undefined, validate() {
false,
() => {
if (AppConstants.platform == "android") { if (AppConstants.platform == "android") {
throw new ExtensionError( throw new ExtensionError(
`android is not a supported platform for the closeTabsByDoubleClick setting.` `android is not a supported platform for the closeTabsByDoubleClick setting.`
); );
} }
} },
), }),
contextMenuShowEvent: Object.assign( contextMenuShowEvent: Object.assign(
getSettingsAPI(extension.id, "contextMenuShowEvent", () => { getSettingsAPI({
if (AppConstants.platform === "win") { context,
return "mouseup"; name: "contextMenuShowEvent",
} callback() {
let prefValue = Services.prefs.getBoolPref( if (AppConstants.platform === "win") {
"ui.context_menus.after_mouseup", return "mouseup";
null }
); let prefValue = Services.prefs.getBoolPref(
return prefValue ? "mouseup" : "mousedown"; "ui.context_menus.after_mouseup",
null
);
return prefValue ? "mouseup" : "mousedown";
},
}), }),
{ {
set: details => { set: details => {
@ -227,94 +241,121 @@ this.browserSettings = class extends ExtensionAPI {
}, },
} }
), ),
ftpProtocolEnabled: getSettingsAPI( ftpProtocolEnabled: getSettingsAPI({
extension.id, context,
"ftpProtocolEnabled", name: "ftpProtocolEnabled",
() => { callback() {
return Services.prefs.getBoolPref("network.ftp.enabled"); return Services.prefs.getBoolPref("network.ftp.enabled");
} },
), }),
homepageOverride: getSettingsAPI( homepageOverride: getSettingsAPI({
extension.id, context,
HOMEPAGE_OVERRIDE_SETTING, name: HOMEPAGE_OVERRIDE_SETTING,
() => { callback() {
return Services.prefs.getStringPref(HOMEPAGE_URL_PREF); return Services.prefs.getStringPref(HOMEPAGE_URL_PREF);
}, },
undefined, readOnly: true,
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";
}), }),
newTabPageOverride: getSettingsAPI( imageAnimationBehavior: getSettingsAPI({
extension.id, context,
NEW_TAB_OVERRIDE_SETTING, name: "imageAnimationBehavior",
() => { callback() {
return Services.prefs.getCharPref("image.animation_mode");
},
}),
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() {
return aboutNewTabService.newTabURL; return aboutNewTabService.newTabURL;
}, },
URL_STORE_TYPE, storeType: URL_STORE_TYPE,
true readOnly: true,
), onChange: new ExtensionCommon.EventManager({
openBookmarksInNewTabs: getSettingsAPI( context,
extension.id, name: `${NEW_TAB_OVERRIDE_SETTING}.onChange`,
"openBookmarksInNewTabs", 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() {
return Services.prefs.getBoolPref( return Services.prefs.getBoolPref(
"browser.tabs.loadBookmarksInTabs" "browser.tabs.loadBookmarksInTabs"
); );
} },
), }),
openSearchResultsInNewTabs: getSettingsAPI( openSearchResultsInNewTabs: getSettingsAPI({
extension.id, context,
"openSearchResultsInNewTabs", name: "openSearchResultsInNewTabs",
() => { callback() {
return Services.prefs.getBoolPref("browser.search.openintab"); return Services.prefs.getBoolPref("browser.search.openintab");
} },
), }),
openUrlbarResultsInNewTabs: getSettingsAPI( openUrlbarResultsInNewTabs: getSettingsAPI({
extension.id, context,
"openUrlbarResultsInNewTabs", name: "openUrlbarResultsInNewTabs",
() => { callback() {
return Services.prefs.getBoolPref("browser.urlbar.openintab"); return Services.prefs.getBoolPref("browser.urlbar.openintab");
} },
), }),
webNotificationsDisabled: getSettingsAPI( webNotificationsDisabled: getSettingsAPI({
extension.id, context,
"webNotificationsDisabled", name: "webNotificationsDisabled",
() => { callback() {
let prefValue = Services.prefs.getIntPref( let prefValue = Services.prefs.getIntPref(
"permissions.default.desktop-notification", "permissions.default.desktop-notification",
null null
); );
return prefValue === PERM_DENY_ACTION; return prefValue === PERM_DENY_ACTION;
} },
), }),
overrideDocumentColors: Object.assign( overrideDocumentColors: Object.assign(
getSettingsAPI(extension.id, "overrideDocumentColors", () => { getSettingsAPI({
let prefValue = Services.prefs.getIntPref( context,
"browser.display.document_color_use" name: "overrideDocumentColors",
); callback() {
if (prefValue === 1) { let prefValue = Services.prefs.getIntPref(
return "never"; "browser.display.document_color_use"
} else if (prefValue === 2) { );
return "always"; if (prefValue === 1) {
} return "never";
return "high-contrast-only"; } else if (prefValue === 2) {
return "always";
}
return "high-contrast-only";
},
}), }),
{ {
set: details => { set: details => {
@ -344,12 +385,16 @@ this.browserSettings = class extends ExtensionAPI {
} }
), ),
useDocumentFonts: Object.assign( useDocumentFonts: Object.assign(
getSettingsAPI(extension.id, "useDocumentFonts", () => { getSettingsAPI({
return ( context,
Services.prefs.getIntPref( name: "useDocumentFonts",
"browser.display.use_document_fonts" callback() {
) !== 0 return (
); Services.prefs.getIntPref(
"browser.display.use_document_fonts"
) !== 0
);
},
}), }),
{ {
set: details => { set: details => {

Просмотреть файл

@ -104,15 +104,14 @@ this.captivePortal = class extends ExtensionAPI {
}; };
}, },
}).api(), }).api(),
canonicalURL: getSettingsAPI( canonicalURL: getSettingsAPI({
context.extension.id, context,
"captiveURL", name: "captiveURL",
() => { callback() {
return Services.prefs.getStringPref("captivedetect.canonicalURL"); return Services.prefs.getStringPref("captivedetect.canonicalURL");
}, },
undefined, readOnly: true,
true }),
),
}, },
}; };
} }

Просмотреть файл

@ -15,8 +15,7 @@ ChromeUtils.defineModuleGetter(
var { ExtensionPreferencesManager } = ChromeUtils.import( var { ExtensionPreferencesManager } = ChromeUtils.import(
"resource://gre/modules/ExtensionPreferencesManager.jsm" "resource://gre/modules/ExtensionPreferencesManager.jsm"
); );
var { getSettingsAPI } = ExtensionPreferencesManager;
var { ExtensionError } = ExtensionUtils;
const cookieSvc = Ci.nsICookieService; const cookieSvc = Ci.nsICookieService;
@ -28,42 +27,6 @@ const cookieBehaviorValues = new Map([
["reject_trackers", cookieSvc.BEHAVIOR_REJECT_TRACKER], ["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. // Add settings objects for supported APIs to the preferences manager.
ExtensionPreferencesManager.addSetting("network.networkPredictionEnabled", { ExtensionPreferencesManager.addSetting("network.networkPredictionEnabled", {
prefNames: [ prefNames: [
@ -217,14 +180,13 @@ ExtensionPreferencesManager.addSetting("websites.trackingProtectionMode", {
this.privacy = class extends ExtensionAPI { this.privacy = class extends ExtensionAPI {
getAPI(context) { getAPI(context) {
let { extension } = context;
return { return {
privacy: { privacy: {
network: { network: {
networkPredictionEnabled: getPrivacyAPI( networkPredictionEnabled: getSettingsAPI({
extension, context,
"network.networkPredictionEnabled", name: "network.networkPredictionEnabled",
() => { callback() {
return ( return (
Preferences.get("network.predictor.enabled") && Preferences.get("network.predictor.enabled") &&
Preferences.get("network.prefetch-next") && Preferences.get("network.prefetch-next") &&
@ -232,19 +194,19 @@ this.privacy = class extends ExtensionAPI {
0 && 0 &&
!Preferences.get("network.dns.disablePrefetch") !Preferences.get("network.dns.disablePrefetch")
); );
} },
), }),
peerConnectionEnabled: getPrivacyAPI( peerConnectionEnabled: getSettingsAPI({
extension, context,
"network.peerConnectionEnabled", name: "network.peerConnectionEnabled",
() => { callback() {
return Preferences.get("media.peerconnection.enabled"); return Preferences.get("media.peerconnection.enabled");
} },
), }),
webRTCIPHandlingPolicy: getPrivacyAPI( webRTCIPHandlingPolicy: getSettingsAPI({
extension, context,
"network.webRTCIPHandlingPolicy", name: "network.webRTCIPHandlingPolicy",
() => { callback() {
if (Preferences.get("media.peerconnection.ice.proxy_only")) { if (Preferences.get("media.peerconnection.ice.proxy_only")) {
return "proxy_only"; return "proxy_only";
} }
@ -270,25 +232,25 @@ this.privacy = class extends ExtensionAPI {
} }
return "default"; return "default";
} },
), }),
}, },
services: { services: {
passwordSavingEnabled: getPrivacyAPI( passwordSavingEnabled: getSettingsAPI({
extension, context,
"services.passwordSavingEnabled", name: "services.passwordSavingEnabled",
() => { callback() {
return Preferences.get("signon.rememberSignons"); return Preferences.get("signon.rememberSignons");
} },
), }),
}, },
websites: { websites: {
cookieConfig: getPrivacyAPI( cookieConfig: getSettingsAPI({
extension, context,
"websites.cookieConfig", name: "websites.cookieConfig",
() => { callback() {
let prefValue = Preferences.get("network.cookie.cookieBehavior"); let prefValue = Preferences.get("network.cookie.cookieBehavior");
return { return {
behavior: Array.from(cookieBehaviorValues.entries()).find( behavior: Array.from(cookieBehaviorValues.entries()).find(
@ -298,40 +260,40 @@ this.privacy = class extends ExtensionAPI {
Preferences.get("network.cookie.lifetimePolicy") === Preferences.get("network.cookie.lifetimePolicy") ===
cookieSvc.ACCEPT_SESSION, cookieSvc.ACCEPT_SESSION,
}; };
} },
), }),
firstPartyIsolate: getPrivacyAPI( firstPartyIsolate: getSettingsAPI({
extension, context,
"websites.firstPartyIsolate", name: "websites.firstPartyIsolate",
() => { callback() {
return Preferences.get("privacy.firstparty.isolate"); return Preferences.get("privacy.firstparty.isolate");
} },
), }),
hyperlinkAuditingEnabled: getPrivacyAPI( hyperlinkAuditingEnabled: getSettingsAPI({
extension, context,
"websites.hyperlinkAuditingEnabled", name: "websites.hyperlinkAuditingEnabled",
() => { callback() {
return Preferences.get("browser.send_pings"); return Preferences.get("browser.send_pings");
} },
), }),
referrersEnabled: getPrivacyAPI( referrersEnabled: getSettingsAPI({
extension, context,
"websites.referrersEnabled", name: "websites.referrersEnabled",
() => { callback() {
return Preferences.get("network.http.sendRefererHeader") !== 0; return Preferences.get("network.http.sendRefererHeader") !== 0;
} },
), }),
resistFingerprinting: getPrivacyAPI( resistFingerprinting: getSettingsAPI({
extension, context,
"websites.resistFingerprinting", name: "websites.resistFingerprinting",
() => { callback() {
return Preferences.get("privacy.resistFingerprinting"); return Preferences.get("privacy.resistFingerprinting");
} },
), }),
trackingProtectionMode: getPrivacyAPI( trackingProtectionMode: getSettingsAPI({
extension, context,
"websites.trackingProtectionMode", name: "websites.trackingProtectionMode",
() => { callback() {
if (Preferences.get("privacy.trackingprotection.enabled")) { if (Preferences.get("privacy.trackingprotection.enabled")) {
return "always"; return "always";
} else if ( } else if (
@ -340,8 +302,8 @@ this.privacy = class extends ExtensionAPI {
return "private_browsing"; return "private_browsing";
} }
return "never"; return "never";
} },
), }),
}, },
}, },
}; };

Просмотреть файл

@ -173,10 +173,10 @@ this.proxy = class extends ExtensionAPI {
}).api(), }).api(),
settings: Object.assign( settings: Object.assign(
getSettingsAPI( getSettingsAPI({
extension.id, context,
"proxy.settings", name: "proxy.settings",
() => { callback() {
let prefValue = Services.prefs.getIntPref("network.proxy.type"); let prefValue = Services.prefs.getIntPref("network.proxy.type");
let proxyConfig = { let proxyConfig = {
proxyType: Array.from(PROXY_TYPES_MAP.entries()).find( proxyType: Array.from(PROXY_TYPES_MAP.entries()).find(
@ -214,16 +214,14 @@ this.proxy = class extends ExtensionAPI {
return proxyConfig; return proxyConfig;
}, },
// proxy.settings is unsupported on android. // proxy.settings is unsupported on android.
undefined, validate() {
false,
() => {
if (AppConstants.platform == "android") { if (AppConstants.platform == "android") {
throw new ExtensionError( throw new ExtensionError(
`proxy.settings is not supported on android.` `proxy.settings is not supported on android.`
); );
} }
} },
), }),
{ {
set: details => { set: details => {
if (AppConstants.platform === "android") { if (AppConstants.platform === "android") {

Просмотреть файл

@ -133,7 +133,6 @@
"name": "onChange", "name": "onChange",
"type": "function", "type": "function",
"description": "Fired after the setting changes.", "description": "Fired after the setting changes.",
"unsupported": true,
"parameters": [ "parameters": [
{ {
"type": "object", "type": "object",

Просмотреть файл

@ -40,8 +40,20 @@ add_task(async function test_browser_settings() {
}; };
async function background() { async function background() {
let listeners = new Set([]);
browser.test.onMessage.addListener(async (msg, apiName, value) => { browser.test.onMessage.addListener(async (msg, apiName, value) => {
let apiObj = browser.browserSettings[apiName]; 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 }); let result = await apiObj.set({ value });
if (msg === "set") { if (msg === "set") {
browser.test.assertTrue(result, "set returns true."); browser.test.assertTrue(result, "set returns true.");
@ -79,6 +91,13 @@ add_task(async function test_browser_settings() {
async function testSetting(setting, value, expected, expectedValue = value) { async function testSetting(setting, value, expected, expectedValue = value) {
extension.sendMessage("set", setting, value); extension.sendMessage("set", setting, value);
let data = await extension.awaitMessage("settingData"); 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( deepEqual(
data.value, data.value,
expectedValue, expectedValue,
@ -174,12 +193,12 @@ add_task(async function test_browser_settings() {
}); });
} }
await testSetting("ftpProtocolEnabled", true, {
"network.ftp.enabled": true,
});
await testSetting("ftpProtocolEnabled", false, { await testSetting("ftpProtocolEnabled", false, {
"network.ftp.enabled": false, "network.ftp.enabled": false,
}); });
await testSetting("ftpProtocolEnabled", true, {
"network.ftp.enabled": true,
});
await testSetting("newTabPosition", "afterCurrent", { await testSetting("newTabPosition", "afterCurrent", {
"browser.tabs.insertRelatedAfterCurrent": false, "browser.tabs.insertRelatedAfterCurrent": false,