mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
b59b137167
MozReview-Commit-ID: 8EILFeFjvnd --HG-- extra : rebase_source : e63dbff3bea99feba8b217f515c48dfb702c1f49
285 lines
9.4 KiB
JavaScript
285 lines
9.4 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* @fileOverview
|
|
* This module is used for managing preferences from WebExtension APIs.
|
|
* It takes care of the precedence chain and decides whether a preference
|
|
* needs to be updated when a change is requested by an API.
|
|
*
|
|
* It deals with preferences via settings objects, which are objects with
|
|
* the following properties:
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
"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");
|
|
|
|
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});
|
|
});
|
|
|
|
/* eslint-disable mozilla/balanced-listeners */
|
|
Management.on("shutdown", (type, extension) => {
|
|
switch (extension.shutdownReason) {
|
|
case "ADDON_DISABLE":
|
|
case "ADDON_DOWNGRADE":
|
|
case "ADDON_UPGRADE":
|
|
this.ExtensionPreferencesManager.disableAll(extension);
|
|
break;
|
|
|
|
case "ADDON_UNINSTALL":
|
|
this.ExtensionPreferencesManager.removeAll(extension);
|
|
break;
|
|
}
|
|
});
|
|
|
|
Management.on("startup", (type, extension) => {
|
|
if (["ADDON_ENABLE", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(extension.startupReason)) {
|
|
this.ExtensionPreferencesManager.enableAll(extension);
|
|
}
|
|
});
|
|
/* eslint-enable mozilla/balanced-listeners */
|
|
|
|
const STORE_TYPE = "prefs";
|
|
|
|
// Definitions of settings, each of which correspond to a different API.
|
|
let settingsMap = new Map();
|
|
|
|
/**
|
|
* This function is passed into the ExtensionSettingsStore to determine the
|
|
* initial value of the setting. It reads an array of preference names from
|
|
* the this scope, which gets bound to a settings object.
|
|
*
|
|
* @returns {Object}
|
|
* An object with one property per preference, which holds the current
|
|
* value of that preference.
|
|
*/
|
|
function initialValueCallback() {
|
|
let initialValue = {};
|
|
for (let pref of this.prefNames) {
|
|
initialValue[pref] = Preferences.get(pref);
|
|
}
|
|
return initialValue;
|
|
}
|
|
|
|
/**
|
|
* Takes an item returned by the ExtensionSettingsStore and conditionally sets
|
|
* preferences based on the item's contents.
|
|
*
|
|
* @param {string} name
|
|
* The name of the setting being processed.
|
|
* @param {Object|null} item
|
|
* Either null, or an object with a value property which indicates the
|
|
* value stored for the setting in the settings store.
|
|
|
|
* @returns {Promise}
|
|
* Resolves to true if the preferences were changed and to false if
|
|
* the preferences were not changed.
|
|
*/
|
|
async function processItem(name, item) {
|
|
if (item) {
|
|
let prefs = item.initialValue || await settingsMap.get(name).setCallback(item.value);
|
|
for (let pref in prefs) {
|
|
if (prefs[pref] === undefined) {
|
|
Preferences.reset(pref);
|
|
} else {
|
|
Preferences.set(pref, prefs[pref]);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
this.ExtensionPreferencesManager = {
|
|
/**
|
|
* Adds a setting to the settingsMap. This is how an API tells the
|
|
* preferences manager what its setting object is. The preferences
|
|
* manager needs to know this when settings need to be removed
|
|
* automatically.
|
|
*
|
|
* @param {string} name The unique id of the setting.
|
|
* @param {Object} setting
|
|
* A setting object that should have properties for
|
|
* prefNames, getCallback and setCallback.
|
|
*/
|
|
addSetting(name, setting) {
|
|
settingsMap.set(name, setting);
|
|
},
|
|
|
|
/**
|
|
* Gets the default value for a preference.
|
|
*
|
|
* @param {string} prefName The name of the preference.
|
|
*
|
|
* @returns {string|number|boolean} The default value of the preference.
|
|
*/
|
|
getDefaultValue(prefName) {
|
|
return defaultPreferences.get(prefName);
|
|
},
|
|
|
|
/**
|
|
* Indicates that an extension would like to change the value of a previously
|
|
* defined setting.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which a setting is being set.
|
|
* @param {string} name
|
|
* The unique id of the setting.
|
|
* @param {any} value
|
|
* The value to be stored in the settings store for this
|
|
* group of preferences.
|
|
*
|
|
* @returns {Promise}
|
|
* Resolves to true if the preferences were changed and to false if
|
|
* the preferences were not changed.
|
|
*/
|
|
async setSetting(extension, name, value) {
|
|
let setting = settingsMap.get(name);
|
|
let item = await ExtensionSettingsStore.addSetting(
|
|
extension, STORE_TYPE, name, value, initialValueCallback.bind(setting));
|
|
return await processItem(name, item);
|
|
},
|
|
|
|
/**
|
|
* Indicates that this extension wants to temporarily cede control over the
|
|
* given setting.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which a preference setting is being removed.
|
|
* @param {string} name
|
|
* The unique id of the setting.
|
|
*
|
|
* @returns {Promise}
|
|
* Resolves to true if the preferences were changed and to false if
|
|
* the preferences were not changed.
|
|
*/
|
|
async disableSetting(extension, name) {
|
|
let item = await ExtensionSettingsStore.disable(
|
|
extension, STORE_TYPE, name);
|
|
return await processItem(name, item);
|
|
},
|
|
|
|
/**
|
|
* Enable a setting that has been disabled.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which a setting is being enabled.
|
|
* @param {string} name
|
|
* The unique id of the setting.
|
|
*
|
|
* @returns {Promise}
|
|
* Resolves to true if the preferences were changed and to false if
|
|
* the preferences were not changed.
|
|
*/
|
|
async enableSetting(extension, name) {
|
|
let item = await ExtensionSettingsStore.enable(extension, STORE_TYPE, name);
|
|
return await processItem(name, item);
|
|
},
|
|
|
|
/**
|
|
* Indicates that this extension no longer wants to set the given setting.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which a preference setting is being removed.
|
|
* @param {string} name
|
|
* The unique id of the setting.
|
|
*
|
|
* @returns {Promise}
|
|
* Resolves to true if the preferences were changed and to false if
|
|
* the preferences were not changed.
|
|
*/
|
|
async removeSetting(extension, name) {
|
|
let item = await ExtensionSettingsStore.removeSetting(
|
|
extension, STORE_TYPE, name);
|
|
return await processItem(name, item);
|
|
},
|
|
|
|
/**
|
|
* Disables all previously set settings for an extension. This can be called when
|
|
* an extension is being disabled, for example.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which all settings are being unset.
|
|
*/
|
|
async disableAll(extension) {
|
|
let settings = await ExtensionSettingsStore.getAllForExtension(extension, STORE_TYPE);
|
|
let disablePromises = [];
|
|
for (let name of settings) {
|
|
disablePromises.push(this.disableSetting(extension, name));
|
|
}
|
|
await Promise.all(disablePromises);
|
|
},
|
|
|
|
/**
|
|
* Enables all disabled settings for an extension. This can be called when
|
|
* an extension has finsihed updating or is being re-enabled, for example.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which all settings are being enabled.
|
|
*/
|
|
async enableAll(extension) {
|
|
let settings = await ExtensionSettingsStore.getAllForExtension(extension, STORE_TYPE);
|
|
let enablePromises = [];
|
|
for (let name of settings) {
|
|
enablePromises.push(this.enableSetting(extension, name));
|
|
}
|
|
await Promise.all(enablePromises);
|
|
},
|
|
|
|
/**
|
|
* Removes all previously set settings for an extension. This can be called when
|
|
* an extension is being uninstalled, for example.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which all settings are being unset.
|
|
*/
|
|
async removeAll(extension) {
|
|
let settings = await ExtensionSettingsStore.getAllForExtension(extension, STORE_TYPE);
|
|
let removePromises = [];
|
|
for (let name of settings) {
|
|
removePromises.push(this.removeSetting(extension, name));
|
|
}
|
|
await Promise.all(removePromises);
|
|
},
|
|
|
|
/**
|
|
* Return the levelOfControl for a setting / extension combo.
|
|
* This queries the levelOfControl from the ExtensionSettingsStore and also
|
|
* takes into account whether any of the setting's preferences are locked.
|
|
*
|
|
* @param {Extension} extension
|
|
* The extension for which levelOfControl is being requested.
|
|
* @param {string} name
|
|
* The unique id of the setting.
|
|
*
|
|
* @returns {Promise}
|
|
* Resolves to the level of control of the extension over the setting.
|
|
*/
|
|
async getLevelOfControl(extension, name) {
|
|
for (let prefName of settingsMap.get(name).prefNames) {
|
|
if (Preferences.locked(prefName)) {
|
|
return "not_controllable";
|
|
}
|
|
}
|
|
return await ExtensionSettingsStore.getLevelOfControl(extension, STORE_TYPE, name);
|
|
},
|
|
};
|