test_FirefoxBridgeExtensionUtils.js (11507B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { FirefoxBridgeExtensionUtils } = ChromeUtils.importESModule( 7 "resource:///modules/FirefoxBridgeExtensionUtils.sys.mjs" 8 ); 9 10 const OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL}\\shell\\open\\command`; 11 const OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL}\\shell\\open\\command`; 12 const FIREFOX_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL}\\shell\\open\\command`; 13 const FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH = `${FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL}\\shell\\open\\command`; 14 15 class StubbedRegistryKey { 16 #children; 17 #originalChildren; 18 #closeCalled; 19 #deletedChildren; 20 #openedForRead; 21 #values; 22 23 constructor(children, values) { 24 this.#children = children; 25 this.#values = values; 26 this.#originalChildren = new Map(children); 27 28 this.#closeCalled = false; 29 this.#openedForRead = false; 30 this.#deletedChildren = new Set([]); 31 } 32 33 get ACCESS_READ() { 34 return 0; 35 } 36 37 reset() { 38 this.#closeCalled = false; 39 this.#deletedChildren = new Set([]); 40 this.#children = new Map(this.#originalChildren); 41 } 42 43 open(_accessLevel) { 44 this.#openedForRead = true; 45 } 46 47 get wasOpenedForRead() { 48 return this.#openedForRead; 49 } 50 51 openChild(path, accessLevel) { 52 const result = this.#children.get(path); 53 result?.open(accessLevel); 54 return result; 55 } 56 57 hasChild(path) { 58 return this.#children.has(path); 59 } 60 61 close() { 62 this.#closeCalled = true; 63 } 64 65 removeChild(path) { 66 this.#deletedChildren.add(path); 67 68 // delete the actual child if it's in there 69 this.#children.delete(path); 70 } 71 72 isChildDeleted(path) { 73 return this.#deletedChildren.has(path); 74 } 75 76 getChildName(index) { 77 let i = 0; 78 for (const [key] of this.#children) { 79 if (i == index) { 80 return key; 81 } 82 i++; 83 } 84 85 return undefined; 86 } 87 88 readStringValue(name) { 89 return this.#values.get(name); 90 } 91 92 get childCount() { 93 return this.#children.size; 94 } 95 96 getValueType(entryName) { 97 if (typeof this.readStringValue(entryName) == "string") { 98 return Ci.nsIWindowsRegKey.TYPE_STRING; 99 } 100 101 throw new Error(`${entryName} not found in registry`); 102 } 103 104 get wasCloseCalled() { 105 return this.#closeCalled; 106 } 107 108 getValueName(index) { 109 let i = 0; 110 for (const [key] of this.#values) { 111 if (i == index) { 112 return key; 113 } 114 i++; 115 } 116 117 return undefined; 118 } 119 120 get valueCount() { 121 return this.#values.size; 122 } 123 } 124 125 class StubbedDeleteBridgeProtocolRegistryEntryHelper { 126 #applicationPath; 127 #registryRootKey; 128 129 constructor({ applicationPath, registryRootKey }) { 130 this.#applicationPath = applicationPath; 131 this.#registryRootKey = registryRootKey; 132 } 133 134 getApplicationPath() { 135 return this.#applicationPath; 136 } 137 138 openRegistryRoot() { 139 return this.#registryRootKey; 140 } 141 142 deleteRegistryTree(root, toDeletePath) { 143 // simplify this for tests 144 root.removeChild(toDeletePath); 145 } 146 } 147 148 add_task(async function test_DeleteWhenSameFirefoxInstall() { 149 for (let protocols of [ 150 [ 151 FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, 152 FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, 153 OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH, 154 OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH, 155 ], 156 [ 157 FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL, 158 FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL, 159 FIREFOX_SHELL_OPEN_COMMAND_PATH, 160 FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH, 161 ], 162 ]) { 163 let [publicProtocol, privateProtocol, publicPath, privatePath] = protocols; 164 const applicationPath = "testPath"; 165 166 const firefoxEntries = new Map(); 167 firefoxEntries.set("", `\"${applicationPath}\" -osint -url \"%1\"`); 168 169 const firefoxProtocolRegKey = new StubbedRegistryKey( 170 new Map(), 171 firefoxEntries 172 ); 173 174 const firefoxPrivateEntries = new Map(); 175 firefoxPrivateEntries.set( 176 "", 177 `\"${applicationPath}\" -osint -private-window \"%1\"` 178 ); 179 const firefoxPrivateProtocolRegKey = new StubbedRegistryKey( 180 new Map(), 181 firefoxPrivateEntries 182 ); 183 184 const children = new Map(); 185 children.set(publicPath, firefoxProtocolRegKey); 186 children.set(privatePath, firefoxPrivateProtocolRegKey); 187 188 const registryRootKey = new StubbedRegistryKey(children, new Map()); 189 190 const stubbedDeleteBridgeProtocolRegistryHelper = 191 new StubbedDeleteBridgeProtocolRegistryEntryHelper({ 192 applicationPath, 193 registryRootKey, 194 }); 195 196 FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( 197 publicProtocol, 198 privateProtocol, 199 stubbedDeleteBridgeProtocolRegistryHelper 200 ); 201 202 ok(registryRootKey.wasCloseCalled, "Root key closed"); 203 204 ok(firefoxProtocolRegKey.wasOpenedForRead, "Firefox key opened"); 205 ok(firefoxProtocolRegKey.wasCloseCalled, "Firefox key closed"); 206 ok( 207 registryRootKey.isChildDeleted(publicProtocol), 208 "Firefox protocol registry entry deleted" 209 ); 210 211 ok( 212 firefoxPrivateProtocolRegKey.wasOpenedForRead, 213 "Firefox private key opened" 214 ); 215 ok( 216 firefoxPrivateProtocolRegKey.wasCloseCalled, 217 "Firefox private key closed" 218 ); 219 ok( 220 registryRootKey.isChildDeleted(privateProtocol), 221 "Firefox private protocol registry entry deleted" 222 ); 223 } 224 }); 225 226 add_task(async function test_DeleteWhenDifferentFirefoxInstall() { 227 for (let protocols of [ 228 [ 229 FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, 230 FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, 231 OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH, 232 OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH, 233 ], 234 [ 235 FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL, 236 FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL, 237 FIREFOX_SHELL_OPEN_COMMAND_PATH, 238 FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH, 239 ], 240 ]) { 241 let [publicProtocol, privateProtocol, publicPath, privatePath] = protocols; 242 const applicationPath = "testPath"; 243 const badApplicationPath = "testPath2"; 244 245 const firefoxEntries = new Map(); 246 firefoxEntries.set("", `\"${badApplicationPath}\" -osint -url \"%1\"`); 247 248 const firefoxProtocolRegKey = new StubbedRegistryKey( 249 new Map(), 250 firefoxEntries 251 ); 252 253 const firefoxPrivateEntries = new Map(); 254 firefoxPrivateEntries.set( 255 "", 256 `\"${badApplicationPath}\" -osint -private-window \"%1\"` 257 ); 258 const firefoxPrivateProtocolRegKey = new StubbedRegistryKey( 259 new Map(), 260 firefoxPrivateEntries 261 ); 262 263 const children = new Map(); 264 children.set(publicPath, firefoxProtocolRegKey); 265 children.set(privatePath, firefoxPrivateProtocolRegKey); 266 267 const registryRootKey = new StubbedRegistryKey(children, new Map()); 268 269 const stubbedDeleteBridgeProtocolRegistryHelper = 270 new StubbedDeleteBridgeProtocolRegistryEntryHelper({ 271 applicationPath, 272 registryRootKey, 273 }); 274 275 FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( 276 publicProtocol, 277 privateProtocol, 278 stubbedDeleteBridgeProtocolRegistryHelper 279 ); 280 281 ok(registryRootKey.wasCloseCalled, "Root key closed"); 282 283 ok(firefoxProtocolRegKey.wasOpenedForRead, "Firefox key opened"); 284 ok(firefoxProtocolRegKey.wasCloseCalled, "Firefox key closed"); 285 ok( 286 !registryRootKey.isChildDeleted(publicProtocol), 287 "Firefox protocol registry entry not deleted" 288 ); 289 290 ok( 291 firefoxPrivateProtocolRegKey.wasOpenedForRead, 292 "Firefox private key opened" 293 ); 294 ok( 295 firefoxPrivateProtocolRegKey.wasCloseCalled, 296 "Firefox private key closed" 297 ); 298 ok( 299 !registryRootKey.isChildDeleted(privateProtocol), 300 "Firefox private protocol registry entry not deleted" 301 ); 302 } 303 }); 304 305 add_task(async function test_DeleteWhenNoRegistryEntries() { 306 for (let protocols of [ 307 [ 308 FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, 309 FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, 310 OLD_FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH, 311 ], 312 [ 313 FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL, 314 FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL, 315 FIREFOX_PRIVATE_SHELL_OPEN_COMMAND_PATH, 316 ], 317 ]) { 318 let [publicProtocol, privateProtocol, privatePath] = protocols; 319 const applicationPath = "testPath"; 320 321 const firefoxPrivateEntries = new Map(); 322 const firefoxPrivateProtocolRegKey = new StubbedRegistryKey( 323 new Map(), 324 firefoxPrivateEntries 325 ); 326 327 const children = new Map(); 328 children.set(privatePath, firefoxPrivateProtocolRegKey); 329 330 const registryRootKey = new StubbedRegistryKey(children, new Map()); 331 332 const stubbedDeleteBridgeProtocolRegistryHelper = 333 new StubbedDeleteBridgeProtocolRegistryEntryHelper({ 334 applicationPath, 335 registryRootKey, 336 }); 337 338 FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( 339 publicProtocol, 340 privateProtocol, 341 stubbedDeleteBridgeProtocolRegistryHelper 342 ); 343 344 ok(registryRootKey.wasCloseCalled, "Root key closed"); 345 346 ok( 347 firefoxPrivateProtocolRegKey.wasOpenedForRead, 348 "Firefox private key opened" 349 ); 350 ok( 351 firefoxPrivateProtocolRegKey.wasCloseCalled, 352 "Firefox private key closed" 353 ); 354 ok( 355 !registryRootKey.isChildDeleted(publicProtocol), 356 "Firefox protocol registry entry deleted when it shouldn't be" 357 ); 358 ok( 359 !registryRootKey.isChildDeleted(privateProtocol), 360 "Firefox private protocol registry deleted when it shouldn't be" 361 ); 362 } 363 }); 364 365 add_task(async function test_DeleteWhenUnexpectedRegistryEntries() { 366 for (let protocols of [ 367 [ 368 FirefoxBridgeExtensionUtils.OLD_PUBLIC_PROTOCOL, 369 FirefoxBridgeExtensionUtils.OLD_PRIVATE_PROTOCOL, 370 OLD_FIREFOX_SHELL_OPEN_COMMAND_PATH, 371 ], 372 [ 373 FirefoxBridgeExtensionUtils.PUBLIC_PROTOCOL, 374 FirefoxBridgeExtensionUtils.PRIVATE_PROTOCOL, 375 FIREFOX_SHELL_OPEN_COMMAND_PATH, 376 ], 377 ]) { 378 let [publicProtocol, privateProtocol, publicPath] = protocols; 379 const applicationPath = "testPath"; 380 381 const firefoxEntries = new Map(); 382 firefoxEntries.set("", `\"${applicationPath}\" -osint -url \"%1\"`); 383 firefoxEntries.set("extraEntry", "extraValue"); 384 const firefoxProtocolRegKey = new StubbedRegistryKey( 385 new Map(), 386 firefoxEntries 387 ); 388 389 const children = new Map(); 390 children.set(publicPath, firefoxProtocolRegKey); 391 392 const registryRootKey = new StubbedRegistryKey(children, new Map()); 393 394 const stubbedDeleteBridgeProtocolRegistryHelper = 395 new StubbedDeleteBridgeProtocolRegistryEntryHelper({ 396 applicationPath, 397 registryRootKey, 398 }); 399 400 FirefoxBridgeExtensionUtils.maybeDeleteBridgeProtocolRegistryEntries( 401 publicProtocol, 402 privateProtocol, 403 stubbedDeleteBridgeProtocolRegistryHelper 404 ); 405 406 ok(registryRootKey.wasCloseCalled, "Root key closed"); 407 408 ok(firefoxProtocolRegKey.wasOpenedForRead, "Firefox key opened"); 409 ok(firefoxProtocolRegKey.wasCloseCalled, "Firefox key closed"); 410 ok( 411 !registryRootKey.isChildDeleted(publicProtocol), 412 "Firefox protocol registry entry deleted when it shouldn't be" 413 ); 414 ok( 415 !registryRootKey.isChildDeleted(privateProtocol), 416 "Firefox private protocol registry deleted when it shouldn't be" 417 ); 418 } 419 });