summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/prefs.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/prefs.js')
-rw-r--r--devtools/client/shared/prefs.js178
1 files changed, 178 insertions, 0 deletions
diff --git a/devtools/client/shared/prefs.js b/devtools/client/shared/prefs.js
new file mode 100644
index 000000000..9b44d4d58
--- /dev/null
+++ b/devtools/client/shared/prefs.js
@@ -0,0 +1,178 @@
+/* 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 Services = require("Services");
+const EventEmitter = require("devtools/shared/event-emitter");
+
+/**
+ * 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", (prefName, 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);
+
+ let cache = new Map();
+
+ for (let accessorName in prefsBlueprint) {
+ let [prefType, prefName] = prefsBlueprint[accessorName];
+ map(this, cache, accessorName, prefType, prefsRoot, prefName);
+ }
+
+ let 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
+ * @return any
+ */
+function get(cache, prefType, prefsRoot, prefName) {
+ let cachedPref = cache.get(prefName);
+ if (cachedPref !== undefined) {
+ return cachedPref;
+ }
+ let value = Services.prefs["get" + prefType + "Pref"](
+ [prefsRoot, prefName].join(".")
+ );
+ 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 array serializer [optional]
+ */
+function map(self, cache, accessorName, prefType, prefsRoot, prefName,
+ 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, "Char", prefsRoot, prefName, {
+ in: JSON.parse,
+ out: JSON.stringify
+ });
+ return;
+ }
+ if (prefType == "Float") {
+ map(self, cache, accessorName, "Char", prefsRoot, prefName, {
+ in: Number.parseFloat,
+ out: (n) => n + ""
+ });
+ return;
+ }
+
+ Object.defineProperty(self, accessorName, {
+ get: () => serializer.in(get(cache, prefType, prefsRoot, prefName)),
+ 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 (let accessorName in prefsBlueprint) {
+ let [, 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: function () {
+ this._branch = Services.prefs.getBranch(prefsRoot + ".");
+ this._branch.addObserver("", this, false);
+ },
+ unregister: function () {
+ this._branch.removeObserver("", this);
+ },
+ observe: function (subject, topic, prefName) {
+ // If this particular pref isn't handled by the blueprint object,
+ // even though it's in the specified branch, ignore it.
+ let accessorName = accessorNameForPref(prefName, prefsBlueprint);
+ if (!(accessorName in self)) {
+ return;
+ }
+ cache.delete(prefName);
+ self.emit("pref-changed", accessorName, self[accessorName]);
+ }
+ };
+}
+
+exports.PrefsHelper = PrefsHelper;