зеркало из https://github.com/mozilla/gecko-dev.git
337 строки
9.1 KiB
JavaScript
337 строки
9.1 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/. */
|
|
|
|
import { MockRegistrar } from "resource://testing-common/MockRegistrar.sys.mjs";
|
|
|
|
class MockWindowsRegKey {
|
|
key = null;
|
|
|
|
// --- Overridden nsISupports interface functions ---
|
|
QueryInterface = ChromeUtils.generateQI(["nsIWindowsRegKey"]);
|
|
|
|
#assertKey() {
|
|
if (this.key) {
|
|
return;
|
|
}
|
|
throw Components.Exception("invalid registry path", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
|
|
#findOrMaybeCreateKey(root, path, mode, maybeCreateCallback) {
|
|
let rootKey = MockRegistry.getRoot(root);
|
|
let parts = path.split("\\");
|
|
if (parts.some(part => !part.length)) {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
|
|
let key = rootKey;
|
|
for (let part of parts) {
|
|
if (!key.subkeys.has(part)) {
|
|
maybeCreateCallback(key.subkeys, part);
|
|
}
|
|
key = key.subkeys.get(part);
|
|
}
|
|
this.key = key;
|
|
}
|
|
|
|
// --- Overridden nsIWindowsRegKey interface functions ---
|
|
open(root, path, mode) {
|
|
// eslint-disable-next-line no-unused-vars
|
|
this.#findOrMaybeCreateKey(root, path, mode, (subkeys, part) => {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
});
|
|
}
|
|
|
|
create(root, path, mode) {
|
|
this.#findOrMaybeCreateKey(root, path, mode, (subkeys, part) =>
|
|
subkeys.set(part, { subkeys: new Map(), values: new Map() })
|
|
);
|
|
}
|
|
|
|
close() {
|
|
this.key = null;
|
|
}
|
|
|
|
get valueCount() {
|
|
this.#assertKey();
|
|
return this.key.values.size;
|
|
}
|
|
|
|
hasValue(name) {
|
|
this.#assertKey();
|
|
return this.key.values.has(name);
|
|
}
|
|
|
|
#getValuePair(name, expectedType = null) {
|
|
this.#assertKey();
|
|
if (!this.key.values.has(name)) {
|
|
throw Components.Exception("invalid value name", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
let [value, type] = this.key.values.get(name);
|
|
if (expectedType && type !== expectedType) {
|
|
throw Components.Exception("unexpected value type", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
return [value, type];
|
|
}
|
|
|
|
getValueType(name) {
|
|
let [, type] = this.#getValuePair(name);
|
|
return type;
|
|
}
|
|
|
|
getValueName(index) {
|
|
if (!this.key || index >= this.key.values.size) {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
let names = Array.from(this.key.values.keys());
|
|
return names[index];
|
|
}
|
|
|
|
readStringValue(name) {
|
|
let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_STRING);
|
|
return value;
|
|
}
|
|
|
|
readIntValue(name) {
|
|
let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_INT);
|
|
return value;
|
|
}
|
|
|
|
readInt64Value(name) {
|
|
let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_INT64);
|
|
return value;
|
|
}
|
|
|
|
readBinaryValue(name) {
|
|
let [value] = this.#getValuePair(name, Ci.nsIWindowsRegKey.TYPE_BINARY);
|
|
return value;
|
|
}
|
|
|
|
#writeValuePair(name, value, type) {
|
|
this.#assertKey();
|
|
this.key.values.set(name, [value, type]);
|
|
}
|
|
|
|
writeStringValue(name, value) {
|
|
this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_STRING);
|
|
}
|
|
|
|
writeIntValue(name, value) {
|
|
this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_INT);
|
|
}
|
|
|
|
writeInt64Value(name, value) {
|
|
this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_INT64);
|
|
}
|
|
|
|
writeBinaryValue(name, value) {
|
|
this.#writeValuePair(name, value, Ci.nsIWindowsRegKey.TYPE_BINARY);
|
|
}
|
|
|
|
removeValue(name) {
|
|
this.#assertKey();
|
|
this.key.values.delete(name);
|
|
}
|
|
|
|
get childCount() {
|
|
this.#assertKey();
|
|
return this.key.subkeys.size;
|
|
}
|
|
|
|
getChildName(index) {
|
|
if (!this.key || index >= this.key.values.size) {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
let names = Array.from(this.key.subkeys.keys());
|
|
return names[index];
|
|
}
|
|
|
|
hasChild(name) {
|
|
this.#assertKey();
|
|
return this.key.subkeys.has(name);
|
|
}
|
|
|
|
removeChild(name) {
|
|
this.#assertKey();
|
|
let child = this.key.subkeys.get(name);
|
|
|
|
if (!child) {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
if (child.subkeys.size > 0) {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
|
|
this.key.subkeys.delete(name);
|
|
}
|
|
|
|
#findOrMaybeCreateChild(name, mode, maybeCreateCallback) {
|
|
this.#assertKey();
|
|
if (name.split("\\").length > 1) {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
}
|
|
if (!this.key.subkeys.has(name)) {
|
|
maybeCreateCallback(this.key.subkeys, name);
|
|
}
|
|
// This won't wrap in the same way as `Cc["@mozilla.org/windows-registry-key;1"].createInstance(nsIWindowsRegKey);`.
|
|
let subKey = new MockWindowsRegKey();
|
|
subKey.key = this.key.subkeys.get(name);
|
|
return subKey;
|
|
}
|
|
|
|
openChild(name, mode) {
|
|
// eslint-disable-next-line no-unused-vars
|
|
return this.#findOrMaybeCreateChild(name, mode, (subkeys, part) => {
|
|
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
|
|
});
|
|
}
|
|
|
|
createChild(name, mode) {
|
|
return this.#findOrMaybeCreateChild(name, mode, (subkeys, part) =>
|
|
subkeys.set(part, { subkeys: new Map(), values: new Map() })
|
|
);
|
|
}
|
|
}
|
|
|
|
export class MockRegistry {
|
|
// All instances of `MockRegistry` share a single data-store; this is
|
|
// conceptually parallel to the Windows registry, which is a shared global
|
|
// resource. It would be possible to have separate data-stores for separate
|
|
// instances with a little adjustment to `MockWindowsRegKey`.
|
|
//
|
|
// Top-level map is indexed by roots. A "key" is an object that has
|
|
// `subkeys` and `values`, both maps indexed by strings. Subkey items are
|
|
// again "key" objects. Value items are `[value, type]` pairs.
|
|
//
|
|
// In pseudo-code:
|
|
//
|
|
// {Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
|
|
// {subkeys:
|
|
// {child: {subkeys: {}, values: {key: ["string_value", Ci.nsIWindowsRegKey.TYPE_STRING]}}},
|
|
// values: {}
|
|
// },
|
|
// ...
|
|
// }
|
|
static roots;
|
|
|
|
constructor() {
|
|
MockRegistry.roots = new Map([
|
|
[
|
|
Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
|
|
{ subkeys: new Map(), values: new Map() },
|
|
],
|
|
[
|
|
Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
|
|
{ subkeys: new Map(), values: new Map() },
|
|
],
|
|
[
|
|
Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
|
|
{ subkeys: new Map(), values: new Map() },
|
|
],
|
|
]);
|
|
|
|
// See bug 1688838 - nsNotifyAddrListener::CheckAdaptersAddresses might
|
|
// attempt to use the registry off the main thread, so we disable that
|
|
// feature while the mock registry is active.
|
|
this.oldSuffixListPref = Services.prefs.getBoolPref(
|
|
"network.notify.dnsSuffixList"
|
|
);
|
|
Services.prefs.setBoolPref("network.notify.dnsSuffixList", false);
|
|
|
|
this.oldCheckForProxiesPref = Services.prefs.getBoolPref(
|
|
"network.notify.checkForProxies"
|
|
);
|
|
Services.prefs.setBoolPref("network.notify.checkForProxies", false);
|
|
|
|
this.oldCheckForNRPTPref = Services.prefs.getBoolPref(
|
|
"network.notify.checkForNRPT"
|
|
);
|
|
Services.prefs.setBoolPref("network.notify.checkForNRPT", false);
|
|
|
|
this.cid = MockRegistrar.register(
|
|
"@mozilla.org/windows-registry-key;1",
|
|
() => new MockWindowsRegKey()
|
|
);
|
|
}
|
|
|
|
shutdown() {
|
|
MockRegistrar.unregister(this.cid);
|
|
Services.prefs.setBoolPref(
|
|
"network.notify.dnsSuffixList",
|
|
this.oldSuffixListPref
|
|
);
|
|
Services.prefs.setBoolPref(
|
|
"network.notify.checkForProxies",
|
|
this.oldCheckForProxiesPref
|
|
);
|
|
Services.prefs.setBoolPref(
|
|
"network.notify.checkForNRPT",
|
|
this.oldCheckForNRPTPref
|
|
);
|
|
this.cid = null;
|
|
}
|
|
|
|
static getRoot(root) {
|
|
if (!this.roots.has(root)) {
|
|
throw new Error(`No such root ${root}`);
|
|
}
|
|
return this.roots.get(root);
|
|
}
|
|
|
|
setValue(root, path, name, value) {
|
|
let key = new MockWindowsRegKey();
|
|
key.create(root, path, Ci.nsIWindowsRegKey.ACCESS_ALL);
|
|
if (value == null) {
|
|
try {
|
|
key.removeValue(name);
|
|
} catch (e) {
|
|
if (
|
|
!(e instanceof Ci.nsIException && e.result == Cr.NS_ERROR_FAILURE)
|
|
) {
|
|
throw e;
|
|
}
|
|
}
|
|
} else {
|
|
key.writeStringValue(name, value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dump given `key` (or, if not given, all roots), and all its value and its
|
|
* subkeys recursively, using the given function to `printOneLine`.
|
|
*/
|
|
static dump(key = null, indent = "", printOneLine = console.log) {
|
|
let types = new Map([
|
|
[1, "REG_SZ"],
|
|
[3, "REG_BINARY"],
|
|
[4, "REG_DWORD"],
|
|
[11, "REG_QWORD"],
|
|
]);
|
|
|
|
if (!key) {
|
|
let roots = [
|
|
"ROOT_KEY_LOCAL_MACHINE",
|
|
"ROOT_KEY_CURRENT_USER",
|
|
"ROOT_KEY_CLASSES_ROOT",
|
|
];
|
|
for (let root of roots) {
|
|
printOneLine(indent + root);
|
|
this.dump(
|
|
this.roots.get(Ci.nsIWindowsRegKey[root]),
|
|
" " + indent,
|
|
printOneLine
|
|
);
|
|
}
|
|
} else {
|
|
for (let [k, v] of key.values.entries()) {
|
|
let [value, type] = v;
|
|
printOneLine(`${indent}${k}: ${value} (${types.get(type)})`);
|
|
}
|
|
for (let [k, child] of key.subkeys.entries()) {
|
|
printOneLine(indent + k);
|
|
this.dump(child, " " + indent, printOneLine);
|
|
}
|
|
}
|
|
}
|
|
}
|