419 lines
11 KiB
JavaScript
419 lines
11 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const { FirefoxBridgeExtensionUtils } = ChromeUtils.importESModule(
|
|
"resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs"
|
|
);
|
|
|
|
const OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL}\\shell\\open\\command`;
|
|
const OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL}\\shell\\open\\command`;
|
|
const FIREFOX_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL}\\shell\\open\\command`;
|
|
const FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL}\\shell\\open\\command`;
|
|
|
|
class StubbedRegistryKey {
|
|
#children;
|
|
#originalChildren;
|
|
#closeCalled;
|
|
#deletedChildren;
|
|
#openedForRead;
|
|
#values;
|
|
|
|
constructor(children, values) {
|
|
this.#children = children;
|
|
this.#values = values;
|
|
this.#originalChildren = new Map(children);
|
|
|
|
this.#closeCalled = false;
|
|
this.#openedForRead = false;
|
|
this.#deletedChildren = new Set([]);
|
|
}
|
|
|
|
get ACCESS_READ() {
|
|
return 0;
|
|
}
|
|
|
|
reset() {
|
|
this.#closeCalled = false;
|
|
this.#deletedChildren = new Set([]);
|
|
this.#children = new Map(this.#originalChildren);
|
|
}
|
|
|
|
open(_accessLevel) {
|
|
this.#openedForRead = true;
|
|
}
|
|
|
|
get wasOpenedForRead() {
|
|
return this.#openedForRead;
|
|
}
|
|
|
|
openChild(path, accessLevel) {
|
|
const result = this.#children.get(path);
|
|
result?.open(accessLevel);
|
|
return result;
|
|
}
|
|
|
|
hasChild(path) {
|
|
return this.#children.has(path);
|
|
}
|
|
|
|
close() {
|
|
this.#closeCalled = true;
|
|
}
|
|
|
|
removeChild(path) {
|
|
this.#deletedChildren.add(path);
|
|
|
|
// delete the actual child if it's in there
|
|
this.#children.delete(path);
|
|
}
|
|
|
|
isChildDeleted(path) {
|
|
return this.#deletedChildren.has(path);
|
|
}
|
|
|
|
getChildName(index) {
|
|
let i = 0;
|
|
for (const [key] of this.#children) {
|
|
if (i == index) {
|
|
return key;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
readStringValue(name) {
|
|
return this.#values.get(name);
|
|
}
|
|
|
|
get childCount() {
|
|
return this.#children.size;
|
|
}
|
|
|
|
getValueType(entryName) {
|
|
if (typeof this.readStringValue(entryName) == "string") {
|
|
return Ci.nsIWindowsRegKey.TYPE_STRING;
|
|
}
|
|
|
|
throw new Error(`${entryName} not found in registry`);
|
|
}
|
|
|
|
get wasCloseCalled() {
|
|
return this.#closeCalled;
|
|
}
|
|
|
|
getValueName(index) {
|
|
let i = 0;
|
|
for (const [key] of this.#values) {
|
|
if (i == index) {
|
|
return key;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
get valueCount() {
|
|
return this.#values.size;
|
|
}
|
|
}
|
|
|
|
class StubbedDeleteBridgeProtocolRegistryEntryHelper {
|
|
#applicationPath;
|
|
#registryRootKey;
|
|
|
|
constructor({ applicationPath, registryRootKey }) {
|
|
this.#applicationPath = applicationPath;
|
|
this.#registryRootKey = registryRootKey;
|
|
}
|
|
|
|
getApplicationPath() {
|
|
return this.#applicationPath;
|
|
}
|
|
|
|
openRegistryRoot() {
|
|
return this.#registryRootKey;
|
|
}
|
|
|
|
deleteRegistryTree(root, toDeletePath) {
|
|
// simplify this for tests
|
|
root.removeChild(toDeletePath);
|
|
}
|
|
}
|
|
|
|
add_task(async function test_DeleteWhenSameFirefoxInstall() {
|
|
for (let protocols of [
|
|
[
|
|
FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL,
|
|
OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH,
|
|
OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
[
|
|
FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL,
|
|
FIREFOX_SHELL_OPEN_COMMAND_PATH,
|
|
FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
]) {
|
|
let [publicProtocol, privateProtocol, publicPath, privatePath] = protocols;
|
|
const applicationPath = "testPath";
|
|
|
|
const firefoxEntries = new Map();
|
|
firefoxEntries.set("", `\"${applicationPath}\" -osint -url \"%1\"`);
|
|
|
|
const firefoxProtocolRegKey = new StubbedRegistryKey(
|
|
new Map(),
|
|
firefoxEntries
|
|
);
|
|
|
|
const firefoxPrivateEntries = new Map();
|
|
firefoxPrivateEntries.set(
|
|
"",
|
|
`\"${applicationPath}\" -osint -private-window \"%1\"`
|
|
);
|
|
const firefoxPrivateProtocolRegKey = new StubbedRegistryKey(
|
|
new Map(),
|
|
firefoxPrivateEntries
|
|
);
|
|
|
|
const children = new Map();
|
|
children.set(publicPath, firefoxProtocolRegKey);
|
|
children.set(privatePath, firefoxPrivateProtocolRegKey);
|
|
|
|
const registryRootKey = new StubbedRegistryKey(children, new Map());
|
|
|
|
const stubbedDeleteBridgeProtocolRegistryHelper =
|
|
new StubbedDeleteBridgeProtocolRegistryEntryHelper({
|
|
applicationPath,
|
|
registryRootKey,
|
|
});
|
|
|
|
FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries(
|
|
publicProtocol,
|
|
privateProtocol,
|
|
stubbedDeleteBridgeProtocolRegistryHelper
|
|
);
|
|
|
|
ok(registryRootKey.wasCloseCalled, "Root key closed");
|
|
|
|
ok(firefoxProtocolRegKey.wasOpenedForRead, "Firefox key opened");
|
|
ok(firefoxProtocolRegKey.wasCloseCalled, "Firefox key closed");
|
|
ok(
|
|
registryRootKey.isChildDeleted(publicProtocol),
|
|
"Firefox protocol registry entry deleted"
|
|
);
|
|
|
|
ok(
|
|
firefoxPrivateProtocolRegKey.wasOpenedForRead,
|
|
"Firefox private key opened"
|
|
);
|
|
ok(
|
|
firefoxPrivateProtocolRegKey.wasCloseCalled,
|
|
"Firefox private key closed"
|
|
);
|
|
ok(
|
|
registryRootKey.isChildDeleted(privateProtocol),
|
|
"Firefox private protocol registry entry deleted"
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_DeleteWhenDifferentFirefoxInstall() {
|
|
for (let protocols of [
|
|
[
|
|
FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL,
|
|
OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH,
|
|
OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
[
|
|
FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL,
|
|
FIREFOX_SHELL_OPEN_COMMAND_PATH,
|
|
FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
]) {
|
|
let [publicProtocol, privateProtocol, publicPath, privatePath] = protocols;
|
|
const applicationPath = "testPath";
|
|
const badApplicationPath = "testPath2";
|
|
|
|
const firefoxEntries = new Map();
|
|
firefoxEntries.set("", `\"${badApplicationPath}\" -osint -url \"%1\"`);
|
|
|
|
const firefoxProtocolRegKey = new StubbedRegistryKey(
|
|
new Map(),
|
|
firefoxEntries
|
|
);
|
|
|
|
const firefoxPrivateEntries = new Map();
|
|
firefoxPrivateEntries.set(
|
|
"",
|
|
`\"${badApplicationPath}\" -osint -private-window \"%1\"`
|
|
);
|
|
const firefoxPrivateProtocolRegKey = new StubbedRegistryKey(
|
|
new Map(),
|
|
firefoxPrivateEntries
|
|
);
|
|
|
|
const children = new Map();
|
|
children.set(publicPath, firefoxProtocolRegKey);
|
|
children.set(privatePath, firefoxPrivateProtocolRegKey);
|
|
|
|
const registryRootKey = new StubbedRegistryKey(children, new Map());
|
|
|
|
const stubbedDeleteBridgeProtocolRegistryHelper =
|
|
new StubbedDeleteBridgeProtocolRegistryEntryHelper({
|
|
applicationPath,
|
|
registryRootKey,
|
|
});
|
|
|
|
FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries(
|
|
publicProtocol,
|
|
privateProtocol,
|
|
stubbedDeleteBridgeProtocolRegistryHelper
|
|
);
|
|
|
|
ok(registryRootKey.wasCloseCalled, "Root key closed");
|
|
|
|
ok(firefoxProtocolRegKey.wasOpenedForRead, "Firefox key opened");
|
|
ok(firefoxProtocolRegKey.wasCloseCalled, "Firefox key closed");
|
|
ok(
|
|
!registryRootKey.isChildDeleted(publicProtocol),
|
|
"Firefox protocol registry entry not deleted"
|
|
);
|
|
|
|
ok(
|
|
firefoxPrivateProtocolRegKey.wasOpenedForRead,
|
|
"Firefox private key opened"
|
|
);
|
|
ok(
|
|
firefoxPrivateProtocolRegKey.wasCloseCalled,
|
|
"Firefox private key closed"
|
|
);
|
|
ok(
|
|
!registryRootKey.isChildDeleted(privateProtocol),
|
|
"Firefox private protocol registry entry not deleted"
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_DeleteWhenNoRegistryEntries() {
|
|
for (let protocols of [
|
|
[
|
|
FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL,
|
|
OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
[
|
|
FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL,
|
|
FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
]) {
|
|
let [publicProtocol, privateProtocol, privatePath] = protocols;
|
|
const applicationPath = "testPath";
|
|
|
|
const firefoxPrivateEntries = new Map();
|
|
const firefoxPrivateProtocolRegKey = new StubbedRegistryKey(
|
|
new Map(),
|
|
firefoxPrivateEntries
|
|
);
|
|
|
|
const children = new Map();
|
|
children.set(privatePath, firefoxPrivateProtocolRegKey);
|
|
|
|
const registryRootKey = new StubbedRegistryKey(children, new Map());
|
|
|
|
const stubbedDeleteBridgeProtocolRegistryHelper =
|
|
new StubbedDeleteBridgeProtocolRegistryEntryHelper({
|
|
applicationPath,
|
|
registryRootKey,
|
|
});
|
|
|
|
FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries(
|
|
publicProtocol,
|
|
privateProtocol,
|
|
stubbedDeleteBridgeProtocolRegistryHelper
|
|
);
|
|
|
|
ok(registryRootKey.wasCloseCalled, "Root key closed");
|
|
|
|
ok(
|
|
firefoxPrivateProtocolRegKey.wasOpenedForRead,
|
|
"Firefox private key opened"
|
|
);
|
|
ok(
|
|
firefoxPrivateProtocolRegKey.wasCloseCalled,
|
|
"Firefox private key closed"
|
|
);
|
|
ok(
|
|
!registryRootKey.isChildDeleted(publicProtocol),
|
|
"Firefox protocol registry entry deleted when it shouldn't be"
|
|
);
|
|
ok(
|
|
!registryRootKey.isChildDeleted(privateProtocol),
|
|
"Firefox private protocol registry deleted when it shouldn't be"
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_DeleteWhenUnexpectedRegistryEntries() {
|
|
for (let protocols of [
|
|
[
|
|
FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL,
|
|
OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
[
|
|
FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL,
|
|
FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL,
|
|
FIREFOX_SHELL_OPEN_COMMAND_PATH,
|
|
],
|
|
]) {
|
|
let [publicProtocol, privateProtocol, publicPath] = protocols;
|
|
const applicationPath = "testPath";
|
|
|
|
const firefoxEntries = new Map();
|
|
firefoxEntries.set("", `\"${applicationPath}\" -osint -url \"%1\"`);
|
|
firefoxEntries.set("extraEntry", "extraValue");
|
|
const firefoxProtocolRegKey = new StubbedRegistryKey(
|
|
new Map(),
|
|
firefoxEntries
|
|
);
|
|
|
|
const children = new Map();
|
|
children.set(publicPath, firefoxProtocolRegKey);
|
|
|
|
const registryRootKey = new StubbedRegistryKey(children, new Map());
|
|
|
|
const stubbedDeleteBridgeProtocolRegistryHelper =
|
|
new StubbedDeleteBridgeProtocolRegistryEntryHelper({
|
|
applicationPath,
|
|
registryRootKey,
|
|
});
|
|
|
|
FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries(
|
|
publicProtocol,
|
|
privateProtocol,
|
|
stubbedDeleteBridgeProtocolRegistryHelper
|
|
);
|
|
|
|
ok(registryRootKey.wasCloseCalled, "Root key closed");
|
|
|
|
ok(firefoxProtocolRegKey.wasOpenedForRead, "Firefox key opened");
|
|
ok(firefoxProtocolRegKey.wasCloseCalled, "Firefox key closed");
|
|
ok(
|
|
!registryRootKey.isChildDeleted(publicProtocol),
|
|
"Firefox protocol registry entry deleted when it shouldn't be"
|
|
);
|
|
ok(
|
|
!registryRootKey.isChildDeleted(privateProtocol),
|
|
"Firefox private protocol registry deleted when it shouldn't be"
|
|
);
|
|
}
|
|
});
|