/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { IceCatBridgeExtensionUtils } = ChromeUtils.importESModule( "resource:///modules/IceCatBridgeExtensionUtils.sys.mjs" ); const OLD_ICECAT_SHELL_OPEN_COMMAND_PATH = `${IceCatBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL}\\shell\\open\\command`; const OLD_ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH = `${IceCatBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL}\\shell\\open\\command`; const ICECAT_SHELL_OPEN_COMMAND_PATH = `${IceCatBridgeExtensionUtils.PUBLIC_PROTOCOL}\\shell\\open\\command`; const ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH = `${IceCatBridgeExtensionUtils.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_DeleteWhenSameIceCatInstall() { for (let protocols of [ [ IceCatBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, OLD_ICECAT_SHELL_OPEN_COMMAND_PATH, OLD_ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH, ], [ IceCatBridgeExtensionUtils.PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.PRIVATE_PROTOCOL, ICECAT_SHELL_OPEN_COMMAND_PATH, ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH, ], ]) { let [publicProtocol, privateProtocol, publicPath, privatePath] = protocols; const applicationPath = "testPath"; const icecatEntries = new Map(); icecatEntries.set("", `\"${applicationPath}\" -osint -url \"%1\"`); const icecatProtocolRegKey = new StubbedRegistryKey( new Map(), icecatEntries ); const icecatPrivateEntries = new Map(); icecatPrivateEntries.set( "", `\"${applicationPath}\" -osint -private-window \"%1\"` ); const icecatPrivateProtocolRegKey = new StubbedRegistryKey( new Map(), icecatPrivateEntries ); const children = new Map(); children.set(publicPath, icecatProtocolRegKey); children.set(privatePath, icecatPrivateProtocolRegKey); const registryRootKey = new StubbedRegistryKey(children, new Map()); const stubbedDeleteBridgeProtocolRegistryHelper = new StubbedDeleteBridgeProtocolRegistryEntryHelper({ applicationPath, registryRootKey, }); IceCatBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( publicProtocol, privateProtocol, stubbedDeleteBridgeProtocolRegistryHelper ); ok(registryRootKey.wasCloseCalled, "Root key closed"); ok(icecatProtocolRegKey.wasOpenedForRead, "IceCat key opened"); ok(icecatProtocolRegKey.wasCloseCalled, "IceCat key closed"); ok( registryRootKey.isChildDeleted(publicProtocol), "IceCat protocol registry entry deleted" ); ok( icecatPrivateProtocolRegKey.wasOpenedForRead, "IceCat private key opened" ); ok( icecatPrivateProtocolRegKey.wasCloseCalled, "IceCat private key closed" ); ok( registryRootKey.isChildDeleted(privateProtocol), "IceCat private protocol registry entry deleted" ); } }); add_task(async function test_DeleteWhenDifferentIceCatInstall() { for (let protocols of [ [ IceCatBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, OLD_ICECAT_SHELL_OPEN_COMMAND_PATH, OLD_ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH, ], [ IceCatBridgeExtensionUtils.PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.PRIVATE_PROTOCOL, ICECAT_SHELL_OPEN_COMMAND_PATH, ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH, ], ]) { let [publicProtocol, privateProtocol, publicPath, privatePath] = protocols; const applicationPath = "testPath"; const badApplicationPath = "testPath2"; const icecatEntries = new Map(); icecatEntries.set("", `\"${badApplicationPath}\" -osint -url \"%1\"`); const icecatProtocolRegKey = new StubbedRegistryKey( new Map(), icecatEntries ); const icecatPrivateEntries = new Map(); icecatPrivateEntries.set( "", `\"${badApplicationPath}\" -osint -private-window \"%1\"` ); const icecatPrivateProtocolRegKey = new StubbedRegistryKey( new Map(), icecatPrivateEntries ); const children = new Map(); children.set(publicPath, icecatProtocolRegKey); children.set(privatePath, icecatPrivateProtocolRegKey); const registryRootKey = new StubbedRegistryKey(children, new Map()); const stubbedDeleteBridgeProtocolRegistryHelper = new StubbedDeleteBridgeProtocolRegistryEntryHelper({ applicationPath, registryRootKey, }); IceCatBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( publicProtocol, privateProtocol, stubbedDeleteBridgeProtocolRegistryHelper ); ok(registryRootKey.wasCloseCalled, "Root key closed"); ok(icecatProtocolRegKey.wasOpenedForRead, "IceCat key opened"); ok(icecatProtocolRegKey.wasCloseCalled, "IceCat key closed"); ok( !registryRootKey.isChildDeleted(publicProtocol), "IceCat protocol registry entry not deleted" ); ok( icecatPrivateProtocolRegKey.wasOpenedForRead, "IceCat private key opened" ); ok( icecatPrivateProtocolRegKey.wasCloseCalled, "IceCat private key closed" ); ok( !registryRootKey.isChildDeleted(privateProtocol), "IceCat private protocol registry entry not deleted" ); } }); add_task(async function test_DeleteWhenNoRegistryEntries() { for (let protocols of [ [ IceCatBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, OLD_ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH, ], [ IceCatBridgeExtensionUtils.PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.PRIVATE_PROTOCOL, ICECAT_PRIVATE_SHELL_OPEN_COMMAND_PATH, ], ]) { let [publicProtocol, privateProtocol, privatePath] = protocols; const applicationPath = "testPath"; const icecatPrivateEntries = new Map(); const icecatPrivateProtocolRegKey = new StubbedRegistryKey( new Map(), icecatPrivateEntries ); const children = new Map(); children.set(privatePath, icecatPrivateProtocolRegKey); const registryRootKey = new StubbedRegistryKey(children, new Map()); const stubbedDeleteBridgeProtocolRegistryHelper = new StubbedDeleteBridgeProtocolRegistryEntryHelper({ applicationPath, registryRootKey, }); IceCatBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( publicProtocol, privateProtocol, stubbedDeleteBridgeProtocolRegistryHelper ); ok(registryRootKey.wasCloseCalled, "Root key closed"); ok( icecatPrivateProtocolRegKey.wasOpenedForRead, "IceCat private key opened" ); ok( icecatPrivateProtocolRegKey.wasCloseCalled, "IceCat private key closed" ); ok( !registryRootKey.isChildDeleted(publicProtocol), "IceCat protocol registry entry deleted when it shouldn't be" ); ok( !registryRootKey.isChildDeleted(privateProtocol), "IceCat private protocol registry deleted when it shouldn't be" ); } }); add_task(async function test_DeleteWhenUnexpectedRegistryEntries() { for (let protocols of [ [ IceCatBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, OLD_ICECAT_SHELL_OPEN_COMMAND_PATH, ], [ IceCatBridgeExtensionUtils.PUBLIC_PROTOCOL, IceCatBridgeExtensionUtils.PRIVATE_PROTOCOL, ICECAT_SHELL_OPEN_COMMAND_PATH, ], ]) { let [publicProtocol, privateProtocol, publicPath] = protocols; const applicationPath = "testPath"; const icecatEntries = new Map(); icecatEntries.set("", `\"${applicationPath}\" -osint -url \"%1\"`); icecatEntries.set("extraEntry", "extraValue"); const icecatProtocolRegKey = new StubbedRegistryKey( new Map(), icecatEntries ); const children = new Map(); children.set(publicPath, icecatProtocolRegKey); const registryRootKey = new StubbedRegistryKey(children, new Map()); const stubbedDeleteBridgeProtocolRegistryHelper = new StubbedDeleteBridgeProtocolRegistryEntryHelper({ applicationPath, registryRootKey, }); IceCatBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( publicProtocol, privateProtocol, stubbedDeleteBridgeProtocolRegistryHelper ); ok(registryRootKey.wasCloseCalled, "Root key closed"); ok(icecatProtocolRegKey.wasOpenedForRead, "IceCat key opened"); ok(icecatProtocolRegKey.wasCloseCalled, "IceCat key closed"); ok( !registryRootKey.isChildDeleted(publicProtocol), "IceCat protocol registry entry deleted when it shouldn't be" ); ok( !registryRootKey.isChildDeleted(privateProtocol), "IceCat private protocol registry deleted when it shouldn't be" ); } });