Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"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"
);
}
});