зеркало из https://github.com/mozilla/gecko-dev.git
240 строки
5.8 KiB
JavaScript
240 строки
5.8 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/. */
|
|
"use strict";
|
|
|
|
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
|
|
|
|
/**
|
|
* Shortcuts for lazily accessing and setting various preferences.
|
|
* Usage:
|
|
* let prefs = new Prefs("root.path.to.branch", {
|
|
* myIntPref: ["Int", "leaf.path.to.my-int-pref"],
|
|
* myCharPref: ["Char", "leaf.path.to.my-char-pref"],
|
|
* myJsonPref: ["Json", "leaf.path.to.my-json-pref"],
|
|
* myFloatPref: ["Float", "leaf.path.to.my-float-pref"]
|
|
* ...
|
|
* });
|
|
*
|
|
* Get/set:
|
|
* prefs.myCharPref = "foo";
|
|
* let aux = prefs.myCharPref;
|
|
*
|
|
* Observe:
|
|
* prefs.registerObserver();
|
|
* prefs.on("pref-changed", (prefValue) => {
|
|
* ...
|
|
* });
|
|
*
|
|
* @param string prefsRoot
|
|
* The root path to the required preferences branch.
|
|
* @param object prefsBlueprint
|
|
* An object containing { accessorName: [prefType, prefName] } keys.
|
|
*/
|
|
function PrefsHelper(prefsRoot = "", prefsBlueprint = {}) {
|
|
EventEmitter.decorate(this);
|
|
|
|
const cache = new Map();
|
|
|
|
for (const accessorName in prefsBlueprint) {
|
|
const [prefType, prefName, fallbackValue] = prefsBlueprint[accessorName];
|
|
map(
|
|
this,
|
|
cache,
|
|
accessorName,
|
|
prefType,
|
|
prefsRoot,
|
|
prefName,
|
|
fallbackValue
|
|
);
|
|
}
|
|
|
|
const observer = makeObserver(this, cache, prefsRoot, prefsBlueprint);
|
|
this.registerObserver = () => observer.register();
|
|
this.unregisterObserver = () => observer.unregister();
|
|
}
|
|
|
|
/**
|
|
* Helper method for getting a pref value.
|
|
*
|
|
* @param Map cache
|
|
* @param string prefType
|
|
* @param string prefsRoot
|
|
* @param string prefName
|
|
* @param string|int|boolean fallbackValue
|
|
* @return any
|
|
*/
|
|
function get(cache, prefType, prefsRoot, prefName, fallbackValue) {
|
|
const cachedPref = cache.get(prefName);
|
|
if (cachedPref !== undefined) {
|
|
return cachedPref;
|
|
}
|
|
const value = Services.prefs["get" + prefType + "Pref"](
|
|
[prefsRoot, prefName].join("."),
|
|
fallbackValue
|
|
);
|
|
cache.set(prefName, value);
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Helper method for setting a pref value.
|
|
*
|
|
* @param Map cache
|
|
* @param string prefType
|
|
* @param string prefsRoot
|
|
* @param string prefName
|
|
* @param any value
|
|
*/
|
|
function set(cache, prefType, prefsRoot, prefName, value) {
|
|
Services.prefs["set" + prefType + "Pref"](
|
|
[prefsRoot, prefName].join("."),
|
|
value
|
|
);
|
|
cache.set(prefName, value);
|
|
}
|
|
|
|
/**
|
|
* Maps a property name to a pref, defining lazy getters and setters.
|
|
* Supported types are "Bool", "Char", "Int", "Float" (sugar around "Char"
|
|
* type and casting), and "Json" (which is basically just sugar for "Char"
|
|
* using the standard JSON serializer).
|
|
*
|
|
* @param PrefsHelper self
|
|
* @param Map cache
|
|
* @param string accessorName
|
|
* @param string prefType
|
|
* @param string prefsRoot
|
|
* @param string prefName
|
|
* @param string|int|boolean fallbackValue
|
|
* @param array serializer [optional]
|
|
*/
|
|
function map(
|
|
self,
|
|
cache,
|
|
accessorName,
|
|
prefType,
|
|
prefsRoot,
|
|
prefName,
|
|
fallbackValue,
|
|
serializer = { in: e => e, out: e => e }
|
|
) {
|
|
if (prefName in self) {
|
|
throw new Error(
|
|
`Can't use ${prefName} because it overrides a property` +
|
|
"on the instance."
|
|
);
|
|
}
|
|
if (prefType == "Json") {
|
|
map(
|
|
self,
|
|
cache,
|
|
accessorName,
|
|
"String",
|
|
prefsRoot,
|
|
prefName,
|
|
fallbackValue,
|
|
{
|
|
in: JSON.parse,
|
|
out: JSON.stringify,
|
|
}
|
|
);
|
|
return;
|
|
}
|
|
if (prefType == "Float") {
|
|
map(self, cache, accessorName, "Char", prefsRoot, prefName, fallbackValue, {
|
|
in: Number.parseFloat,
|
|
out: n => n + "",
|
|
});
|
|
return;
|
|
}
|
|
|
|
Object.defineProperty(self, accessorName, {
|
|
get: () =>
|
|
serializer.in(get(cache, prefType, prefsRoot, prefName, fallbackValue)),
|
|
set: e => {
|
|
set(cache, prefType, prefsRoot, prefName, serializer.out(e));
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Finds the accessor for the provided pref, based on the blueprint object
|
|
* used in the constructor.
|
|
*
|
|
* @param PrefsHelper self
|
|
* @param object prefsBlueprint
|
|
* @return string
|
|
*/
|
|
function accessorNameForPref(somePrefName, prefsBlueprint) {
|
|
for (const accessorName in prefsBlueprint) {
|
|
const [, prefName] = prefsBlueprint[accessorName];
|
|
if (somePrefName == prefName) {
|
|
return accessorName;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Creates a pref observer for `self`.
|
|
*
|
|
* @param PrefsHelper self
|
|
* @param Map cache
|
|
* @param string prefsRoot
|
|
* @param object prefsBlueprint
|
|
* @return object
|
|
*/
|
|
function makeObserver(self, cache, prefsRoot, prefsBlueprint) {
|
|
return {
|
|
register() {
|
|
this._branch = Services.prefs.getBranch(prefsRoot + ".");
|
|
this._branch.addObserver("", this);
|
|
},
|
|
unregister() {
|
|
this._branch.removeObserver("", this);
|
|
},
|
|
observe(subject, topic, prefName) {
|
|
// If this particular pref isn't handled by the blueprint object,
|
|
// even though it's in the specified branch, ignore it.
|
|
const accessorName = accessorNameForPref(prefName, prefsBlueprint);
|
|
if (!(accessorName in self)) {
|
|
return;
|
|
}
|
|
cache.delete(prefName);
|
|
self.emit("pref-changed", accessorName, self[accessorName]);
|
|
},
|
|
};
|
|
}
|
|
|
|
exports.PrefsHelper = PrefsHelper;
|
|
|
|
/**
|
|
* A PreferenceObserver observes a pref branch for pref changes.
|
|
* It emits an event for each preference change.
|
|
*/
|
|
class PrefObserver extends EventEmitter {
|
|
constructor(branchName) {
|
|
super();
|
|
|
|
this.#branchName = branchName;
|
|
this.#branch = Services.prefs.getBranch(branchName);
|
|
this.#branch.addObserver("", this);
|
|
}
|
|
|
|
#branchName;
|
|
#branch;
|
|
|
|
observe(subject, topic, data) {
|
|
if (topic == "nsPref:changed") {
|
|
this.emit(this.#branchName + data);
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.#branch.removeObserver("", this);
|
|
}
|
|
}
|
|
|
|
exports.PrefObserver = PrefObserver;
|