tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prefs.js (5977B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 "use strict";
      5 
      6 const EventEmitter = require("resource://devtools/shared/event-emitter.js");
      7 
      8 /**
      9 * Shortcuts for lazily accessing and setting various preferences.
     10 * Usage:
     11 *   let prefs = new Prefs("root.path.to.branch", {
     12 *     myIntPref: ["Int", "leaf.path.to.my-int-pref"],
     13 *     myCharPref: ["Char", "leaf.path.to.my-char-pref"],
     14 *     myJsonPref: ["Json", "leaf.path.to.my-json-pref"],
     15 *     myFloatPref: ["Float", "leaf.path.to.my-float-pref"]
     16 *     ...
     17 *   });
     18 *
     19 * Get/set:
     20 *   prefs.myCharPref = "foo";
     21 *   let aux = prefs.myCharPref;
     22 *
     23 * Observe:
     24 *   prefs.registerObserver();
     25 *   prefs.on("pref-changed", (prefValue) => {
     26 *     ...
     27 *   });
     28 *
     29 * @param string prefsRoot
     30 *        The root path to the required preferences branch.
     31 * @param object prefsBlueprint
     32 *        An object containing { accessorName: [prefType, prefName] } keys.
     33 */
     34 function PrefsHelper(prefsRoot = "", prefsBlueprint = {}) {
     35  EventEmitter.decorate(this);
     36 
     37  const cache = new Map();
     38 
     39  for (const accessorName in prefsBlueprint) {
     40    const [prefType, prefName, fallbackValue] = prefsBlueprint[accessorName];
     41    map(
     42      this,
     43      cache,
     44      accessorName,
     45      prefType,
     46      prefsRoot,
     47      prefName,
     48      fallbackValue
     49    );
     50  }
     51 
     52  const observer = makeObserver(this, cache, prefsRoot, prefsBlueprint);
     53  this.registerObserver = () => observer.register();
     54  this.unregisterObserver = () => observer.unregister();
     55 }
     56 
     57 /**
     58 * Helper method for getting a pref value.
     59 *
     60 * @param Map cache
     61 * @param string prefType
     62 * @param string prefsRoot
     63 * @param string prefName
     64 * @param string|int|boolean fallbackValue
     65 * @return any
     66 */
     67 function get(cache, prefType, prefsRoot, prefName, fallbackValue) {
     68  const cachedPref = cache.get(prefName);
     69  if (cachedPref !== undefined) {
     70    return cachedPref;
     71  }
     72  const value = Services.prefs["get" + prefType + "Pref"](
     73    [prefsRoot, prefName].join("."),
     74    fallbackValue
     75  );
     76  cache.set(prefName, value);
     77  return value;
     78 }
     79 
     80 /**
     81 * Helper method for setting a pref value.
     82 *
     83 * @param Map cache
     84 * @param string prefType
     85 * @param string prefsRoot
     86 * @param string prefName
     87 * @param any value
     88 */
     89 function set(cache, prefType, prefsRoot, prefName, value) {
     90  Services.prefs["set" + prefType + "Pref"](
     91    [prefsRoot, prefName].join("."),
     92    value
     93  );
     94  cache.set(prefName, value);
     95 }
     96 
     97 /**
     98 * Maps a property name to a pref, defining lazy getters and setters.
     99 * Supported types are "Bool", "Char", "Int", "Float" (sugar around "Char"
    100 * type and casting), and "Json" (which is basically just sugar for "Char"
    101 * using the standard JSON serializer).
    102 *
    103 * @param PrefsHelper self
    104 * @param Map cache
    105 * @param string accessorName
    106 * @param string prefType
    107 * @param string prefsRoot
    108 * @param string prefName
    109 * @param string|int|boolean fallbackValue
    110 * @param array serializer [optional]
    111 */
    112 function map(
    113  self,
    114  cache,
    115  accessorName,
    116  prefType,
    117  prefsRoot,
    118  prefName,
    119  fallbackValue,
    120  serializer = { in: e => e, out: e => e }
    121 ) {
    122  if (prefName in self) {
    123    throw new Error(
    124      `Can't use ${prefName} because it overrides a property` +
    125        "on the instance."
    126    );
    127  }
    128  if (prefType == "Json") {
    129    map(
    130      self,
    131      cache,
    132      accessorName,
    133      "String",
    134      prefsRoot,
    135      prefName,
    136      fallbackValue,
    137      {
    138        in: JSON.parse,
    139        out: JSON.stringify,
    140      }
    141    );
    142    return;
    143  }
    144  if (prefType == "Float") {
    145    map(self, cache, accessorName, "Char", prefsRoot, prefName, fallbackValue, {
    146      in: Number.parseFloat,
    147      out: n => n + "",
    148    });
    149    return;
    150  }
    151 
    152  Object.defineProperty(self, accessorName, {
    153    get: () =>
    154      serializer.in(get(cache, prefType, prefsRoot, prefName, fallbackValue)),
    155    set: e => {
    156      set(cache, prefType, prefsRoot, prefName, serializer.out(e));
    157    },
    158  });
    159 }
    160 
    161 /**
    162 * Finds the accessor for the provided pref, based on the blueprint object
    163 * used in the constructor.
    164 *
    165 * @param PrefsHelper self
    166 * @param object prefsBlueprint
    167 * @return string
    168 */
    169 function accessorNameForPref(somePrefName, prefsBlueprint) {
    170  for (const accessorName in prefsBlueprint) {
    171    const [, prefName] = prefsBlueprint[accessorName];
    172    if (somePrefName == prefName) {
    173      return accessorName;
    174    }
    175  }
    176  return "";
    177 }
    178 
    179 /**
    180 * Creates a pref observer for `self`.
    181 *
    182 * @param PrefsHelper self
    183 * @param Map cache
    184 * @param string prefsRoot
    185 * @param object prefsBlueprint
    186 * @return object
    187 */
    188 function makeObserver(self, cache, prefsRoot, prefsBlueprint) {
    189  return {
    190    register() {
    191      this._branch = Services.prefs.getBranch(prefsRoot + ".");
    192      this._branch.addObserver("", this);
    193    },
    194    unregister() {
    195      this._branch.removeObserver("", this);
    196    },
    197    observe(subject, topic, prefName) {
    198      // If this particular pref isn't handled by the blueprint object,
    199      // even though it's in the specified branch, ignore it.
    200      const accessorName = accessorNameForPref(prefName, prefsBlueprint);
    201      if (!(accessorName in self)) {
    202        return;
    203      }
    204      cache.delete(prefName);
    205      self.emit("pref-changed", accessorName, self[accessorName]);
    206    },
    207  };
    208 }
    209 
    210 exports.PrefsHelper = PrefsHelper;
    211 
    212 /**
    213 * A PreferenceObserver observes a pref branch for pref changes.
    214 * It emits an event for each preference change.
    215 */
    216 class PrefObserver extends EventEmitter {
    217  constructor(branchName) {
    218    super();
    219 
    220    this.#branchName = branchName;
    221    this.#branch = Services.prefs.getBranch(branchName);
    222    this.#branch.addObserver("", this);
    223  }
    224 
    225  #branchName;
    226  #branch;
    227 
    228  observe(subject, topic, data) {
    229    if (topic == "nsPref:changed") {
    230      this.emit(this.#branchName + data);
    231    }
    232  }
    233 
    234  destroy() {
    235    this.#branch.removeObserver("", this);
    236  }
    237 }
    238 
    239 exports.PrefObserver = PrefObserver;