browser_NavigableManager.js (16746B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { NavigableManager } = ChromeUtils.importESModule( 7 "chrome://remote/content/shared/NavigableManager.sys.mjs" 8 ); 9 10 const BUILDER_URL = "https://example.com/document-builder.sjs?html="; 11 const FRAME_URL = "https://example.com/document-builder.sjs?html=frame"; 12 const FRAME_MARKUP = ` 13 <iframe src="${encodeURI(FRAME_URL)}"></iframe> 14 <iframe src="${encodeURI(FRAME_URL)}"></iframe> 15 `; 16 const TEST_URL = BUILDER_URL + encodeURI(FRAME_MARKUP); 17 18 describe("NavigableManager", function () { 19 let testData; 20 21 beforeEach(async () => { 22 NavigableManager.startTracking(); 23 24 const initialBrowser = gBrowser.selectedBrowser; 25 const initialContext = initialBrowser.browsingContext; 26 27 info(`Open a new tab and navigate to ${TEST_URL}`); 28 const newTab = await addTabAndWaitForNavigated(gBrowser, TEST_URL); 29 30 const newBrowser = newTab.linkedBrowser; 31 const newContext = newBrowser.browsingContext; 32 const newFrameContexts = newContext 33 .getAllBrowsingContextsInSubtree() 34 .filter(context => context.parent); 35 36 is(newFrameContexts.length, 2, "Top context has 2 child contexts"); 37 38 testData = { 39 initialBrowser, 40 initialContext, 41 newBrowser, 42 newContext, 43 newFrameContexts, 44 newTab, 45 }; 46 }); 47 48 afterEach(() => { 49 NavigableManager.stopTracking(); 50 51 gBrowser.removeAllTabsBut(gBrowser.tabs[0]); 52 }); 53 54 it("Get the browser by its Navigable id", async function test_getBrowserById() { 55 const { initialBrowser, newBrowser } = testData; 56 57 const invalidValues = [undefined, null, 1, "foo", {}, []]; 58 invalidValues.forEach(value => 59 is(NavigableManager.getBrowserById(value), null) 60 ); 61 62 const initialBrowserId = NavigableManager.getIdForBrowser(initialBrowser); 63 const newBrowserId = NavigableManager.getIdForBrowser(newBrowser); 64 65 Assert.stringMatches( 66 initialBrowserId, 67 uuidRegex, 68 "Initial browser is a valid uuid" 69 ); 70 Assert.stringMatches( 71 newBrowserId, 72 uuidRegex, 73 "New tab's browser is a valid uuid" 74 ); 75 isnot(initialBrowserId, newBrowserId, "Both browsers have different ids"); 76 77 is(NavigableManager.getBrowserById(initialBrowserId), initialBrowser); 78 is(NavigableManager.getBrowserById(newBrowserId), newBrowser); 79 }); 80 81 it("Get the BrowsingContext by its Navigable id", async function test_getBrowsingContextById() { 82 const { newContext, newFrameContexts } = testData; 83 84 const invalidValues = [undefined, null, "foo", {}, []]; 85 invalidValues.forEach(value => 86 is(NavigableManager.getBrowsingContextById(value), null) 87 ); 88 89 const newContextId = NavigableManager.getIdForBrowsingContext(newContext); 90 const newFrameContextIds = newFrameContexts.map(context => 91 NavigableManager.getIdForBrowsingContext(context) 92 ); 93 94 Assert.stringMatches( 95 newContextId, 96 uuidRegex, 97 "Top context is a valid uuid" 98 ); 99 Assert.stringMatches( 100 newFrameContextIds[0], 101 numberRegex, 102 "First child context has a valid id" 103 ); 104 Assert.stringMatches( 105 newFrameContextIds[1], 106 numberRegex, 107 "Second child context has a valid id" 108 ); 109 isnot( 110 newContextId, 111 newFrameContextIds[0], 112 "Id of top-level context is different from first child context" 113 ); 114 isnot( 115 newContextId, 116 newFrameContextIds[0], 117 "Id of top-level context is different from second child context" 118 ); 119 is( 120 NavigableManager.getBrowsingContextById(newFrameContextIds[0]), 121 newFrameContexts[0], 122 "Context of first child can be retrieved by id" 123 ); 124 is( 125 NavigableManager.getBrowsingContextById(newFrameContextIds[1]), 126 newFrameContexts[1], 127 "Context of second child can be retrieved by id" 128 ); 129 }); 130 131 it("Get the Navigable id for a browser", async function test_getIdForBrowser() { 132 const { initialBrowser, newBrowser, newContext } = testData; 133 134 const invalidValues = [undefined, null, 1, "foo", {}, []]; 135 invalidValues.forEach(value => 136 is(NavigableManager.getBrowserById(value), null) 137 ); 138 139 is( 140 NavigableManager.getIdForBrowser(newContext), 141 null, 142 "Requires a browser instance as argument" 143 ); 144 145 const newBrowserId = NavigableManager.getIdForBrowser(newBrowser); 146 Assert.stringMatches( 147 NavigableManager.getIdForBrowser(newBrowser), 148 uuidRegex, 149 "Got a valid uuid for the browser" 150 ); 151 is( 152 NavigableManager.getIdForBrowser(newBrowser), 153 newBrowserId, 154 "For the same browser the identical id is returned" 155 ); 156 isnot( 157 NavigableManager.getIdForBrowser(initialBrowser), 158 newBrowserId, 159 "For a different browser the id is not the same" 160 ); 161 }); 162 163 it("Get the Navigable id for a BrowsingContext", async function test_getIdForBrowsingContext() { 164 const { newBrowser, newContext, newFrameContexts } = testData; 165 166 const invalidValues = [undefined, null, 1, "foo", {}, []]; 167 invalidValues.forEach(value => 168 is(NavigableManager.getIdForBrowsingContext(value), null) 169 ); 170 171 const newContextId = NavigableManager.getIdForBrowsingContext(newContext); 172 const newFrameContextIds = newFrameContexts.map(context => 173 NavigableManager.getIdForBrowsingContext(context) 174 ); 175 176 Assert.stringMatches( 177 newContextId, 178 uuidRegex, 179 "Got a valid uuid for top-level context" 180 ); 181 is( 182 NavigableManager.getIdForBrowsingContext(newContext), 183 newContextId, 184 "Id is always the same for a top-level context" 185 ); 186 is( 187 NavigableManager.getIdForBrowsingContext(newContext), 188 NavigableManager.getIdForBrowser(newBrowser), 189 "Id of a top-level context is equal to the browser id" 190 ); 191 192 Assert.stringMatches( 193 newFrameContextIds[0], 194 numberRegex, 195 "Got a valid id for a child context" 196 ); 197 is( 198 NavigableManager.getIdForBrowsingContext(newFrameContexts[0]), 199 newFrameContextIds[0], 200 "Id is always the same for a child context" 201 ); 202 }); 203 204 it("Get the Navigable for a BrowsingContext", async function test_getNavigableForBrowsingContext() { 205 const { newBrowser, newContext, newFrameContexts, newTab } = testData; 206 207 const invalidValues = [undefined, null, 1, "test", {}, [], newBrowser]; 208 invalidValues.forEach(invalidValue => 209 Assert.throws( 210 () => NavigableManager.getNavigableForBrowsingContext(invalidValue), 211 /Expected browsingContext to be a CanonicalBrowsingContext/ 212 ) 213 ); 214 215 is( 216 NavigableManager.getNavigableForBrowsingContext(newContext), 217 newBrowser, 218 "Top-Level context has the content browser as navigable" 219 ); 220 is( 221 NavigableManager.getNavigableForBrowsingContext(newFrameContexts[0]), 222 newFrameContexts[0], 223 "Child context has itself as navigable" 224 ); 225 226 gBrowser.removeTab(newTab); 227 }); 228 229 it("Get discarded BrowsingContext by id", async function test_getDiscardedBrowsingContextById() { 230 const { newBrowser, newContext, newFrameContexts, newTab } = testData; 231 232 const newContextId = NavigableManager.getIdForBrowsingContext(newContext); 233 const newFrameContextIds = newFrameContexts.map(context => 234 NavigableManager.getIdForBrowsingContext(context) 235 ); 236 237 is( 238 NavigableManager.getBrowsingContextById(newContextId), 239 newContext, 240 "Top context can be retrieved by its id" 241 ); 242 is( 243 NavigableManager.getBrowsingContextById(newFrameContextIds[0]), 244 newFrameContexts[0], 245 "Child context can be retrieved by its id" 246 ); 247 248 // Remove all the iframes 249 await SpecialPowers.spawn(newBrowser, [], async () => { 250 const frames = content.document.querySelectorAll("iframe"); 251 frames.forEach(frame => frame.remove()); 252 }); 253 254 is( 255 NavigableManager.getBrowsingContextById(newContextId), 256 newContext, 257 "Top context can still be retrieved after removing all the frames" 258 ); 259 is( 260 NavigableManager.getBrowsingContextById(newFrameContextIds[0]), 261 null, 262 "Child context can no longer be retrieved by its id after removing all the frames" 263 ); 264 265 gBrowser.removeTab(newTab); 266 267 is( 268 NavigableManager.getBrowsingContextById(newContextId), 269 null, 270 "Top context can no longer be retrieved by its id after the tab is closed" 271 ); 272 }); 273 274 it("Support unloaded browsers", async function test_unloadedBrowser() { 275 const { newBrowser, newContext, newTab } = testData; 276 277 const newBrowserId = NavigableManager.getIdForBrowser(newBrowser); 278 const newContextId = NavigableManager.getIdForBrowsingContext(newContext); 279 280 await gBrowser.discardBrowser(newTab); 281 282 is( 283 NavigableManager.getIdForBrowser(newBrowser), 284 newBrowserId, 285 "Id for the browser is still available after unloading the tab" 286 ); 287 is( 288 NavigableManager.getBrowserById(newBrowserId), 289 newBrowser, 290 "Unloaded browser can still be retrieved by id" 291 ); 292 293 is( 294 NavigableManager.getIdForBrowsingContext(newContext), 295 newContextId, 296 "Id for the browsing context is still available after unloading the tab" 297 ); 298 is( 299 NavigableManager.getBrowsingContextById(newContextId), 300 null, 301 "Browsing context can no longer be retrieved after unloading the tab" 302 ); 303 Assert.throws( 304 () => NavigableManager.getNavigableForBrowsingContext(newContext), 305 /Expected browsingContext to be a CanonicalBrowsingContext/ 306 ); 307 308 // Loading a new page for new browsing contexts 309 await loadURL(newBrowser, TEST_URL); 310 311 const newBrowsingContext = newBrowser.browsingContext; 312 313 is( 314 NavigableManager.getIdForBrowser(newBrowser), 315 newBrowserId, 316 "Id for the browser is still the same when navigating after unloading the tab" 317 ); 318 is( 319 NavigableManager.getBrowserById(newBrowserId), 320 newBrowser, 321 "New browser can be retrieved by its id" 322 ); 323 324 is( 325 NavigableManager.getIdForBrowsingContext(newBrowsingContext), 326 newContextId, 327 "Id for the new top-level context is still the same" 328 ); 329 is( 330 NavigableManager.getBrowsingContextById(newContextId), 331 newBrowsingContext, 332 "Top-level context can be retrieved again" 333 ); 334 is( 335 NavigableManager.getNavigableForBrowsingContext(newBrowsingContext), 336 newBrowser, 337 "The navigable can be retrieved again" 338 ); 339 }); 340 341 it("Retrieve id for cross-group opener", async function test_crossGroupOpener() { 342 const { newContext, newBrowser, newTab } = testData; 343 344 const newContextId = NavigableManager.getIdForBrowsingContext(newContext); 345 346 await SpecialPowers.spawn(newBrowser, [], async () => { 347 content.open("", "_blank", ""); 348 }); 349 350 const browser = gBrowser.selectedBrowser; 351 const openerContext = browser.browsingContext.crossGroupOpener; 352 353 isnot( 354 browser.browsingContext.crossGroupOpener, 355 null, 356 "Opened popup window has a cross-group opener" 357 ); 358 is( 359 NavigableManager.getIdForBrowsingContext(openerContext), 360 newContextId, 361 "Id of cross-group opener context is correct" 362 ); 363 364 // Remove the tab which opened the popup window 365 gBrowser.removeTab(newTab); 366 367 is( 368 NavigableManager.getIdForBrowsingContext(openerContext), 369 newContextId, 370 "Id of cross-group opener context is still correct after closing the opener tab" 371 ); 372 }); 373 374 it("Start and stop tracking of browsing contexts", async function test_startStopTracking() { 375 async function addUnloadedTab(tabBrowser) { 376 info(`Open a new tab, navigate, and unload it immediately`); 377 const newTab = await addTabAndWaitForNavigated(tabBrowser, TEST_URL); 378 379 const newBrowser = newTab.linkedBrowser; 380 const newContext = newBrowser.browsingContext; 381 382 const newFrameContexts = newContext 383 .getAllBrowsingContextsInSubtree() 384 .filter(context => context.parent); 385 386 info(`Unload the newly opened tab`); 387 await tabBrowser.discardBrowser(newTab); 388 389 return { 390 newBrowser, 391 newContext, 392 newFrameContexts, 393 newTab, 394 }; 395 } 396 397 // Calling start tracking multiple times doesn't cause failures. 398 NavigableManager.startTracking(); 399 NavigableManager.startTracking(); 400 401 { 402 // Stop tracking of new browsing contexts 403 NavigableManager.stopTracking(); 404 405 let { newBrowser, newContext } = await addUnloadedTab(gBrowser); 406 407 Assert.stringMatches( 408 NavigableManager.getIdForBrowser(newBrowser), 409 uuidRegex, 410 "There is always a valid uuid for the browser" 411 ); 412 is( 413 NavigableManager.getIdForBrowsingContext(newContext), 414 null, 415 "There is no id of a temporarily open top-level context" 416 ); 417 } 418 419 // Calling stop tracking multiple times doesn't cause failures. 420 NavigableManager.stopTracking(); 421 422 // Re-enable tracking 423 NavigableManager.startTracking(); 424 425 let { newBrowser, newContext } = await addUnloadedTab(gBrowser); 426 427 Assert.stringMatches( 428 NavigableManager.getIdForBrowser(newBrowser), 429 uuidRegex, 430 "Got a valid uuid for the browser" 431 ); 432 Assert.stringMatches( 433 NavigableManager.getIdForBrowsingContext(newContext), 434 uuidRegex, 435 "Got a valid uuid for the top-level context" 436 ); 437 }); 438 439 it("Get the Navigable id for a chrome browsing context", async function test_getIdForChromeBrowsingContext() { 440 // Get the parent process browsing context (chrome scope) 441 const chromeContext = window.browsingContext; 442 443 ok(!chromeContext.isContent, "Chrome context is not a content context"); 444 445 const chromeContextId = 446 NavigableManager.getIdForBrowsingContext(chromeContext); 447 448 Assert.stringMatches( 449 chromeContextId, 450 uuidRegex, 451 "Got a valid uuid for chrome browsing context" 452 ); 453 454 is( 455 NavigableManager.getIdForBrowsingContext(chromeContext), 456 chromeContextId, 457 "Id is always the same for the same chrome browsing context" 458 ); 459 460 const chromeContext2 = BrowsingContext.getFromWindow( 461 Services.wm.getMostRecentWindow("navigator:browser") 462 ); 463 is( 464 NavigableManager.getIdForBrowsingContext(chromeContext2), 465 chromeContextId, 466 "Same chrome context returns same id when retrieved differently" 467 ); 468 }); 469 470 it("Get chrome browsing context by its Navigable id", async function test_getChromeBrowsingContextById() { 471 const chromeContext = BrowsingContext.getFromWindow(window); 472 473 ok(!chromeContext.isContent, "Chrome context is not a content context"); 474 475 const chromeContextId = 476 NavigableManager.getIdForBrowsingContext(chromeContext); 477 478 is( 479 NavigableManager.getBrowsingContextById(chromeContextId), 480 chromeContext, 481 "Chrome browsing context can be retrieved by its id" 482 ); 483 }); 484 485 it("Chrome browsing contexts have different ids than content contexts", async function test_chromeVsContentIds() { 486 const { newContext } = testData; 487 const chromeContext = BrowsingContext.getFromWindow(window); 488 489 const chromeContextId = 490 NavigableManager.getIdForBrowsingContext(chromeContext); 491 const contentContextId = 492 NavigableManager.getIdForBrowsingContext(newContext); 493 494 Assert.stringMatches( 495 chromeContextId, 496 uuidRegex, 497 "Chrome context has valid uuid" 498 ); 499 Assert.stringMatches( 500 contentContextId, 501 uuidRegex, 502 "Content context has valid uuid" 503 ); 504 505 isnot( 506 chromeContextId, 507 contentContextId, 508 "Chrome and content contexts have different ids" 509 ); 510 }); 511 512 it("Chrome browsing context cleanup on discard", async function test_chromeBrowsingContextDiscard() { 513 // Open a new window to get a chrome browsing context we can close 514 const newWindow = await BrowserTestUtils.openNewBrowserWindow(); 515 const newChromeContext = BrowsingContext.getFromWindow(newWindow); 516 517 ok( 518 !newChromeContext.isContent, 519 "New window's chrome context is not a content context" 520 ); 521 522 const chromeContextId = 523 NavigableManager.getIdForBrowsingContext(newChromeContext); 524 525 Assert.stringMatches( 526 chromeContextId, 527 uuidRegex, 528 "Got a valid uuid for new window's chrome context" 529 ); 530 531 is( 532 NavigableManager.getBrowsingContextById(chromeContextId), 533 newChromeContext, 534 "Chrome browsing context can be retrieved by id" 535 ); 536 537 // Close the new window 538 await BrowserTestUtils.closeWindow(newWindow); 539 540 is( 541 NavigableManager.getBrowsingContextById(chromeContextId), 542 null, 543 "Discarded chrome browsing context has no navigable id" 544 ); 545 }); 546 });