Bug 1258309 - Pull ViewHelpers.Prefs into its own module, r=jsantell
authorVictor Porof <vporof@mozilla.com>
Tue, 22 Mar 2016 10:17:21 +0100
changeset 289876 41188a469dc65ed0433868811a400c5f0a4842d9
parent 289875 b8b21810b2b3b42a90dfda07c3ff0ecf1a030727
child 289877 572b756efdf644b8cf68685dcf215fa257bb716e
push id74027
push userkwierso@gmail.com
push dateTue, 22 Mar 2016 23:57:42 +0000
treeherdermozilla-inbound@eb528d042c85 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjsantell
bugs1258309
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1258309 - Pull ViewHelpers.Prefs into its own module, r=jsantell
devtools/client/shared/moz.build
devtools/client/shared/prefs.js
devtools/client/shared/widgets/ViewHelpers.jsm
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -32,16 +32,17 @@ DevToolsModules(
     'getjson.js',
     'inplace-editor.js',
     'Jsbeautify.jsm',
     'l10n.js',
     'node-attribute-parser.js',
     'options-view.js',
     'output-parser.js',
     'poller.js',
+    'prefs.js',
     'source-utils.js',
     'SplitView.jsm',
     'telemetry.js',
     'theme-switching.js',
     'theme.js',
     'undo.js',
     'view-source.js',
     'webgl-utils.js',
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/prefs.js
@@ -0,0 +1,165 @@
+/* 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;
--- a/devtools/client/shared/widgets/ViewHelpers.jsm
+++ b/devtools/client/shared/widgets/ViewHelpers.jsm
@@ -301,163 +301,16 @@ this.ViewHelpers = {
     if (aFlags.delayed) {
       aPane.ownerDocument.defaultView.setTimeout(doToggle, PANE_APPEARANCE_DELAY);
     } else {
       doToggle();
     }
   }
 };
 
-
-
-/**
- * Shortcuts for lazily accessing and setting various preferences.
- * Usage:
- *   let prefs = new ViewHelpers.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 aPrefsRoot
- *        The root path to the required preferences branch.
- * @param object aPrefsBlueprint
- *        An object containing { accessorName: [prefType, prefName] } keys.
- * @param object aOptions
- *        Additional options for this constructor. Currently supported:
- *          - monitorChanges: true to update the stored values if they changed
- *                            when somebody edits about:config or the prefs
- *                            change somewhere else.
- */
-ViewHelpers.Prefs = function(aPrefsRoot = "", aPrefsBlueprint = {}, aOptions = {}) {
-  EventEmitter.decorate(this);
-
-  this._cache = new Map();
-  let self = this;
-
-  for (let [accessorName, [prefType, prefName]] of Iterator(aPrefsBlueprint)) {
-    this._map(accessorName, prefType, aPrefsRoot, prefName);
-  }
-
-  let observer = {
-    register: function() {
-      this.branch = Services.prefs.getBranch(aPrefsRoot + ".");
-      this.branch.addObserver("", this, false);
-    },
-    unregister: function() {
-      this.branch.removeObserver("", this);
-    },
-    observe: function(_, __, aPrefName) {
-      // If this particular pref isn't handled by the blueprint object,
-      // even though it's in the specified branch, ignore it.
-      let accessor = self._accessor(aPrefsBlueprint, aPrefName);
-      if (!(accessor in self)) {
-        return;
-      }
-      self._cache.delete(aPrefName);
-      self.emit("pref-changed", accessor, self[accessor]);
-    }
-  };
-
-  this.registerObserver = () => observer.register();
-  this.unregisterObserver = () => observer.unregister();
-
-  if (aOptions.monitorChanges) {
-    this.registerObserver();
-  }
-};
-
-ViewHelpers.Prefs.prototype = {
-  /**
-   * Helper method for getting a pref value.
-   *
-   * @param string aType
-   * @param string aPrefsRoot
-   * @param string aPrefName
-   * @return any
-   */
-  _get: function(aType, aPrefsRoot, aPrefName) {
-    let cachedPref = this._cache.get(aPrefName);
-    if (cachedPref !== undefined) {
-      return cachedPref;
-    }
-    let value = Services.prefs["get" + aType + "Pref"]([aPrefsRoot, aPrefName].join("."));
-    this._cache.set(aPrefName, value);
-    return value;
-  },
-
-  /**
-   * Helper method for setting a pref value.
-   *
-   * @param string aType
-   * @param string aPrefsRoot
-   * @param string aPrefName
-   * @param any aValue
-   */
-  _set: function(aType, aPrefsRoot, aPrefName, aValue) {
-    Services.prefs["set" + aType + "Pref"]([aPrefsRoot, aPrefName].join("."), aValue);
-    this._cache.set(aPrefName, aValue);
-  },
-
-  /**
-   * 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 string aAccessorName
-   * @param string aType
-   * @param string aPrefsRoot
-   * @param string aPrefName
-   * @param array aSerializer
-   */
-  _map: function(aAccessorName, aType, aPrefsRoot, aPrefName, aSerializer = { in: e => e, out: e => e }) {
-    if (aPrefName in this) {
-      throw new Error(`Can't use ${aPrefName} because it's already a property.`);
-    }
-    if (aType == "Json") {
-      this._map(aAccessorName, "Char", aPrefsRoot, aPrefName, { in: JSON.parse, out: JSON.stringify });
-      return;
-    }
-    if (aType == "Float") {
-      this._map(aAccessorName, "Char", aPrefsRoot, aPrefName, { in: Number.parseFloat, out: (n) => n + ""});
-      return;
-    }
-
-    Object.defineProperty(this, aAccessorName, {
-      get: () => aSerializer.in(this._get(aType, aPrefsRoot, aPrefName)),
-      set: (e) => this._set(aType, aPrefsRoot, aPrefName, aSerializer.out(e))
-    });
-  },
-
-  /**
-   * Finds the accessor in this object for the provided property name,
-   * based on the blueprint object used in the constructor.
-   */
-  _accessor: function(aPrefsBlueprint, aPrefName) {
-    for (let [accessorName, [, prefName]] of Iterator(aPrefsBlueprint)) {
-      if (prefName == aPrefName) {
-        return accessorName;
-      }
-    }
-    return null;
-  }
-};
-
 /**
  * A generic Item is used to describe children present in a Widget.
  *
  * This is basically a very thin wrapper around an nsIDOMNode, with a few
  * characteristics, like a `value` and an `attachment`.
  *
  * The characteristics are optional, and their meaning is entirely up to you.
  * - The `value` should be a string, passed as an argument.