tor-browser

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

MockRegistry.sys.mjs (9745B)


      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 
      5 import { MockRegistrar } from "resource://testing-common/MockRegistrar.sys.mjs";
      6 
      7 class MockWindowsRegKey {
      8  key = null;
      9 
     10  // --- Overridden nsISupports interface functions ---
     11  QueryInterface = ChromeUtils.generateQI(["nsIWindowsRegKey"]);
     12 
     13  #assertKey() {
     14    if (this.key) {
     15      return;
     16    }
     17    throw Components.Exception("invalid registry path", Cr.NS_ERROR_FAILURE);
     18  }
     19 
     20  #findOrMaybeCreateKey(root, path, mode, maybeCreateCallback) {
     21    let rootKey = MockRegistry.getRoot(root);
     22    let parts = path.split("\\");
     23    if (parts.some(part => !part.length)) {
     24      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
     25    }
     26 
     27    let key = rootKey;
     28    for (let part of parts) {
     29      if (!key.subkeys.has(part)) {
     30        maybeCreateCallback(key.subkeys, part);
     31      }
     32      key = key.subkeys.get(part);
     33    }
     34    this.key = key;
     35  }
     36 
     37  // --- Overridden nsIWindowsRegKey interface functions ---
     38  open(root, path, mode) {
     39    // eslint-disable-next-line no-unused-vars
     40    this.#findOrMaybeCreateKey(root, path, mode, (subkeys, part) => {
     41      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
     42    });
     43  }
     44 
     45  create(root, path, mode) {
     46    this.#findOrMaybeCreateKey(root, path, mode, (subkeys, part) =>
     47      subkeys.set(part, { subkeys: new Map(), values: new Map() })
     48    );
     49  }
     50 
     51  close() {
     52    this.key = null;
     53  }
     54 
     55  get valueCount() {
     56    this.#assertKey();
     57    return this.key.values.size;
     58  }
     59 
     60  hasValue(name) {
     61    this.#assertKey();
     62    return this.key.values.has(name);
     63  }
     64 
     65  #getValuePair(name, expectedType = null) {
     66    this.#assertKey();
     67    if (!this.key.values.has(name)) {
     68      throw Components.Exception("invalid value name", Cr.NS_ERROR_FAILURE);
     69    }
     70    let [value, type] = this.key.values.get(name);
     71    if (expectedType && type !== expectedType) {
     72      throw Components.Exception("unexpected value type", Cr.NS_ERROR_FAILURE);
     73    }
     74    return [value, type];
     75  }
     76 
     77  getValueType(name) {
     78    let [, type] = this.#getValuePair(name);
     79    return type;
     80  }
     81 
     82  getValueName(index) {
     83    if (!this.key || index >= this.key.values.size) {
     84      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
     85    }
     86    let names = Array.from(this.key.values.keys());
     87    return names[index];
     88  }
     89 
     90  readStringValue(name) {
     91    let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_STRING);
     92    return value;
     93  }
     94 
     95  readIntValue(name) {
     96    let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_INT);
     97    return value;
     98  }
     99 
    100  readInt64Value(name) {
    101    let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_INT64);
    102    return value;
    103  }
    104 
    105  readBinaryValue(name) {
    106    let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_BINARY);
    107    return value;
    108  }
    109 
    110  #writeValuePair(name, value, type) {
    111    this.#assertKey();
    112    this.key.values.set(name, [value, type]);
    113  }
    114 
    115  writeStringValue(name, value) {
    116    this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_STRING);
    117  }
    118 
    119  writeIntValue(name, value) {
    120    this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_INT);
    121  }
    122 
    123  writeInt64Value(name, value) {
    124    this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_INT64);
    125  }
    126 
    127  writeBinaryValue(name, value) {
    128    this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_BINARY);
    129  }
    130 
    131  removeValue(name) {
    132    this.#assertKey();
    133    this.key.values.delete(name);
    134  }
    135 
    136  get childCount() {
    137    this.#assertKey();
    138    return this.key.subkeys.size;
    139  }
    140 
    141  getChildName(index) {
    142    if (!this.key || index >= this.key.values.size) {
    143      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
    144    }
    145    let names = Array.from(this.key.subkeys.keys());
    146    return names[index];
    147  }
    148 
    149  hasChild(name) {
    150    this.#assertKey();
    151    return this.key.subkeys.has(name);
    152  }
    153 
    154  removeChild(name) {
    155    this.#assertKey();
    156    let child = this.key.subkeys.get(name);
    157 
    158    if (!child) {
    159      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
    160    }
    161    if (child.subkeys.size > 0) {
    162      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
    163    }
    164 
    165    this.key.subkeys.delete(name);
    166  }
    167 
    168  #findOrMaybeCreateChild(name, mode, maybeCreateCallback) {
    169    this.#assertKey();
    170    if (name.split("\\").length > 1) {
    171      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
    172    }
    173    if (!this.key.subkeys.has(name)) {
    174      maybeCreateCallback(this.key.subkeys, name);
    175    }
    176    // This won't wrap in the same way as `Cc["@mozilla.org/windows-registry-key;1"].createInstance(nsIWindowsRegKey);`.
    177    let subKey = new MockWindowsRegKey();
    178    subKey.key = this.key.subkeys.get(name);
    179    return subKey;
    180  }
    181 
    182  openChild(name, mode) {
    183    // eslint-disable-next-line no-unused-vars
    184    return this.#findOrMaybeCreateChild(name, mode, (subkeys, part) => {
    185      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
    186    });
    187  }
    188 
    189  createChild(name, mode) {
    190    return this.#findOrMaybeCreateChild(name, mode, (subkeys, part) =>
    191      subkeys.set(part, { subkeys: new Map(), values: new Map() })
    192    );
    193  }
    194 }
    195 
    196 export class MockRegistry {
    197  // All instances of `MockRegistry` share a single data-store; this is
    198  // conceptually parallel to the Windows registry, which is a shared global
    199  // resource.  It would be possible to have separate data-stores for separate
    200  // instances with a little adjustment to `MockWindowsRegKey`.
    201  //
    202  // Top-level map is indexed by roots.  A "key" is an object that has
    203  // `subkeys` and `values`, both maps indexed by strings.  Subkey items are
    204  // again "key" objects.  Value items are `[value, type]` pairs.
    205  //
    206  // In pseudo-code:
    207  //
    208  // {Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
    209  //  {subkeys:
    210  //   {child: {subkeys: {}, values: {key: ["string_value", Ci.nsIWindowsRegKey.TYPE_STRING]}}},
    211  //  values: {}
    212  //  },
    213  //  ...
    214  // }
    215  static roots;
    216 
    217  constructor() {
    218    MockRegistry.roots = new Map([
    219      [
    220        Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
    221        { subkeys: new Map(), values: new Map() },
    222      ],
    223      [
    224        Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
    225        { subkeys: new Map(), values: new Map() },
    226      ],
    227      [
    228        Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
    229        { subkeys: new Map(), values: new Map() },
    230      ],
    231    ]);
    232 
    233    // See bug 1688838 - nsNotifyAddrListener::CheckAdaptersAddresses might
    234    // attempt to use the registry off the main thread, so we disable that
    235    // feature while the mock registry is active.
    236    this.oldSuffixListPref = Services.prefs.getBoolPref(
    237      "network.notify.dnsSuffixList"
    238    );
    239    Services.prefs.setBoolPref("network.notify.dnsSuffixList", false);
    240 
    241    this.oldCheckForProxiesPref = Services.prefs.getBoolPref(
    242      "network.notify.checkForProxies"
    243    );
    244    Services.prefs.setBoolPref("network.notify.checkForProxies", false);
    245 
    246    this.oldCheckForNRPTPref = Services.prefs.getBoolPref(
    247      "network.notify.checkForNRPT"
    248    );
    249    Services.prefs.setBoolPref("network.notify.checkForNRPT", false);
    250 
    251    this.cid = MockRegistrar.register(
    252      "@mozilla.org/windows-registry-key;1",
    253      () => new MockWindowsRegKey()
    254    );
    255  }
    256 
    257  shutdown() {
    258    MockRegistrar.unregister(this.cid);
    259    Services.prefs.setBoolPref(
    260      "network.notify.dnsSuffixList",
    261      this.oldSuffixListPref
    262    );
    263    Services.prefs.setBoolPref(
    264      "network.notify.checkForProxies",
    265      this.oldCheckForProxiesPref
    266    );
    267    Services.prefs.setBoolPref(
    268      "network.notify.checkForNRPT",
    269      this.oldCheckForNRPTPref
    270    );
    271    this.cid = null;
    272  }
    273 
    274  static getRoot(root) {
    275    if (!this.roots.has(root)) {
    276      throw new Error(`No such root ${root}`);
    277    }
    278    return this.roots.get(root);
    279  }
    280 
    281  setValue(root, path, name, value, type = Ci.nsIWindowsRegKey.TYPE_STRING) {
    282    let key = new MockWindowsRegKey();
    283    key.create(root, path, Ci.nsIWindowsRegKey.ACCESS_ALL);
    284    if (value == null) {
    285      try {
    286        key.removeValue(name);
    287      } catch (e) {
    288        if (
    289          !(e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)
    290        ) {
    291          throw e;
    292        }
    293      }
    294    } else {
    295      switch (type) {
    296        case Ci.nsIWindowsRegKey.TYPE_STRING:
    297          key.writeStringValue(name, value);
    298          break;
    299        case Ci.nsIWindowsRegKey.TYPE_BINARY:
    300          key.writeBinaryValue(name, value);
    301          break;
    302        case Ci.nsIWindowsRegKey.TYPE_INT:
    303          key.writeIntValue(name, value);
    304          break;
    305        case Ci.nsIWindowsRegKey.TYPE_INT64:
    306          key.writeInt64Value(name, value);
    307          break;
    308      }
    309    }
    310  }
    311 
    312  /**
    313   * Dump given `key` (or, if not given, all roots), and all its value and its
    314   * subkeys recursively, using the given function to `printOneLine`.
    315   */
    316  static dump(key = null, indent = "", printOneLine = console.log) {
    317    let types = new Map([
    318      [1, "REG_SZ"],
    319      [3, "REG_BINARY"],
    320      [4, "REG_DWORD"],
    321      [11, "REG_QWORD"],
    322    ]);
    323 
    324    if (!key) {
    325      let roots = [
    326        "ROOT_KEY_LOCAL_MACHINE",
    327        "ROOT_KEY_CURRENT_USER",
    328        "ROOT_KEY_CLASSES_ROOT",
    329      ];
    330      for (let root of roots) {
    331        printOneLine(indent + root);
    332        this.dump(
    333          this.roots.get(Ci.nsIWindowsRegKey[root]),
    334          "  " + indent,
    335          printOneLine
    336        );
    337      }
    338    } else {
    339      for (let [k, v] of key.values.entries()) {
    340        let [value, type] = v;
    341        printOneLine(`${indent}${k}: ${value} (${types.get(type)})`);
    342      }
    343      for (let [k, child] of key.subkeys.entries()) {
    344        printOneLine(indent + k);
    345        this.dump(child, "  " + indent, printOneLine);
    346      }
    347    }
    348  }
    349 }