browser_history_recently_closed.js (28101B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { SessionStoreTestUtils } = ChromeUtils.importESModule( 8 "resource://testing-common/SessionStoreTestUtils.sys.mjs" 9 ); 10 const { TabStateFlusher } = ChromeUtils.importESModule( 11 "resource:///modules/sessionstore/TabStateFlusher.sys.mjs" 12 ); 13 const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL; 14 15 SessionStoreTestUtils.init(this, window); 16 17 const TEST_PATH = getRootDirectory(gTestPath).replace( 18 "chrome://mochitests/content", 19 "https://example.com" 20 ); 21 22 let panelMenuWidgetAdded = false; 23 function prepareHistoryPanel() { 24 if (panelMenuWidgetAdded) { 25 return; 26 } 27 CustomizableUI.addWidgetToArea( 28 "history-panelmenu", 29 CustomizableUI.AREA_FIXED_OVERFLOW_PANEL 30 ); 31 registerCleanupFunction(() => CustomizableUI.reset()); 32 } 33 34 async function openRecentlyClosedTabsMenu(doc = window.document) { 35 prepareHistoryPanel(); 36 await openHistoryPanel(doc); 37 38 let recentlyClosedTabs = doc.getElementById("appMenuRecentlyClosedTabs"); 39 Assert.ok( 40 !recentlyClosedTabs.getAttribute("disabled"), 41 "Recently closed tabs button enabled" 42 ); 43 let closeTabsPanel = doc.getElementById("appMenu-library-recentlyClosedTabs"); 44 let panelView = closeTabsPanel && PanelView.forNode(closeTabsPanel); 45 if (!panelView?.active) { 46 recentlyClosedTabs.click(); 47 closeTabsPanel = doc.getElementById("appMenu-library-recentlyClosedTabs"); 48 await BrowserTestUtils.waitForEvent(closeTabsPanel, "ViewShown"); 49 ok( 50 PanelView.forNode(closeTabsPanel)?.active, 51 "Opened 'Recently closed tabs' panel" 52 ); 53 } 54 55 return closeTabsPanel; 56 } 57 58 function makeTabState(url) { 59 return { 60 entries: [{ url, triggeringPrincipal_base64 }], 61 }; 62 } 63 64 function makeClosedTabState(url, { groupId, closedInTabGroup = false } = {}) { 65 return { 66 title: url, 67 closedInTabGroupId: closedInTabGroup ? groupId : null, 68 state: { 69 entries: [ 70 { 71 url, 72 triggeringPrincipal_base64, 73 }, 74 ], 75 groupId, 76 }, 77 }; 78 } 79 80 function resetClosedTabsAndWindows() { 81 // Clear the lists of closed windows and tabs. 82 Services.obs.notifyObservers(null, "browser:purge-session-history"); 83 is(SessionStore.getClosedWindowCount(), 0, "Expect 0 closed windows"); 84 for (const win of BrowserWindowTracker.orderedWindows) { 85 is( 86 SessionStore.getClosedTabCountForWindow(win), 87 0, 88 "Expect 0 closed tabs for this window" 89 ); 90 } 91 } 92 93 registerCleanupFunction(async () => { 94 await resetClosedTabsAndWindows(); 95 }); 96 97 add_task(async function testRecentlyClosedDisabled() { 98 info("Check history recently closed tabs/windows section"); 99 100 prepareHistoryPanel(); 101 // We need to make sure the history is cleared before starting the test 102 await Sanitizer.sanitize(["history"]); 103 104 await openHistoryPanel(); 105 106 let recentlyClosedTabs = document.getElementById("appMenuRecentlyClosedTabs"); 107 let recentlyClosedWindows = document.getElementById( 108 "appMenuRecentlyClosedWindows" 109 ); 110 111 // Wait for the disabled attribute to change, as we receive 112 // the "viewshown" event before this changes 113 await BrowserTestUtils.waitForCondition( 114 () => recentlyClosedTabs.hasAttribute("disabled"), 115 "Waiting for button to become disabled" 116 ); 117 Assert.ok( 118 recentlyClosedTabs.hasAttribute("disabled"), 119 "Recently closed tabs button disabled" 120 ); 121 Assert.ok( 122 recentlyClosedWindows.hasAttribute("disabled"), 123 "Recently closed windows button disabled" 124 ); 125 126 await hideHistoryPanel(); 127 128 gBrowser.selectedTab.focus(); 129 await SessionStoreTestUtils.openAndCloseTab( 130 window, 131 TEST_PATH + "dummy_history_item.html" 132 ); 133 134 await openHistoryPanel(); 135 136 await BrowserTestUtils.waitForCondition( 137 () => !recentlyClosedTabs.hasAttribute("disabled"), 138 "Waiting for button to be enabled" 139 ); 140 Assert.ok( 141 !recentlyClosedTabs.hasAttribute("disabled"), 142 "Recently closed tabs is available" 143 ); 144 Assert.ok( 145 recentlyClosedWindows.hasAttribute("disabled"), 146 "Recently closed windows button disabled" 147 ); 148 149 await hideHistoryPanel(); 150 151 let newWin = await BrowserTestUtils.openNewBrowserWindow(); 152 let loadedPromise = BrowserTestUtils.browserLoaded( 153 newWin.gBrowser.selectedBrowser 154 ); 155 BrowserTestUtils.startLoadingURIString( 156 newWin.gBrowser.selectedBrowser, 157 "about:mozilla" 158 ); 159 await loadedPromise; 160 await BrowserTestUtils.closeWindow(newWin); 161 162 await openHistoryPanel(); 163 164 await BrowserTestUtils.waitForCondition( 165 () => !recentlyClosedWindows.hasAttribute("disabled"), 166 "Waiting for button to be enabled" 167 ); 168 Assert.ok( 169 !recentlyClosedTabs.hasAttribute("disabled"), 170 "Recently closed tabs is available" 171 ); 172 Assert.ok( 173 !recentlyClosedWindows.hasAttribute("disabled"), 174 "Recently closed windows is available" 175 ); 176 177 await hideHistoryPanel(); 178 }); 179 180 add_task(async function testRecentlyClosedTabsDisabledPersists() { 181 info("Check history recently closed tabs/windows section"); 182 183 prepareHistoryPanel(); 184 185 // We need to make sure the history is cleared before starting the test 186 await Sanitizer.sanitize(["history"]); 187 188 await openHistoryPanel(); 189 190 let recentlyClosedTabs = document.getElementById("appMenuRecentlyClosedTabs"); 191 Assert.ok( 192 recentlyClosedTabs.hasAttribute("disabled"), 193 "Recently closed tabs button disabled" 194 ); 195 196 await hideHistoryPanel(); 197 198 let newWin = await BrowserTestUtils.openNewBrowserWindow(); 199 200 await openHistoryPanel(newWin.document); 201 recentlyClosedTabs = newWin.document.getElementById( 202 "appMenuRecentlyClosedTabs" 203 ); 204 Assert.ok( 205 recentlyClosedTabs.hasAttribute("disabled"), 206 "Recently closed tabs is disabled" 207 ); 208 209 // We close the window without hiding the panel first, which used to interfere 210 // with populating the view subsequently. 211 await BrowserTestUtils.closeWindow(newWin); 212 213 newWin = await BrowserTestUtils.openNewBrowserWindow(); 214 await openHistoryPanel(newWin.document); 215 recentlyClosedTabs = newWin.document.getElementById( 216 "appMenuRecentlyClosedTabs" 217 ); 218 Assert.ok( 219 recentlyClosedTabs.hasAttribute("disabled"), 220 "Recently closed tabs is disabled" 221 ); 222 await hideHistoryPanel(newWin.document); 223 await BrowserTestUtils.closeWindow(newWin); 224 }); 225 226 add_task(async function testRecentlyClosedRestoreAllTabs() { 227 // We need to make sure the history is cleared before starting the test 228 await Sanitizer.sanitize(["history"]); 229 await resetClosedTabsAndWindows(); 230 const initialTabCount = gBrowser.visibleTabs.length; 231 232 const closedTabUrls = [ 233 "about:robots", 234 "https://example.com/", 235 "https://example.org/", 236 ]; 237 238 const closedTabGroupInOpenWindowUrls = ["about:logo", "about:logo"]; 239 const closedTabGroupInOpenWindowId = "1234567890-1"; 240 241 const closedTabGroupInClosedWindowUrls = ["about:robots", "about:robots"]; 242 const closedTabGroupInClosedWindowId = "1234567890-2"; 243 244 await SessionStoreTestUtils.promiseBrowserState({ 245 windows: [ 246 { 247 tabs: [makeTabState("about:mozilla")], 248 _closedTabs: closedTabUrls.map(makeClosedTabState), 249 closedGroups: [ 250 { 251 collapsed: false, 252 color: "blue", 253 id: closedTabGroupInOpenWindowId, 254 name: "closed-in-open-window", 255 tabs: closedTabGroupInOpenWindowUrls.map(url => 256 makeClosedTabState(url, { groupId: closedTabGroupInOpenWindowId }) 257 ), 258 }, 259 ], 260 }, 261 ], 262 _closedWindows: [ 263 { 264 tabs: [makeTabState("about:mozilla")], 265 _closedTabs: [], 266 closedGroups: [ 267 { 268 collapsed: false, 269 color: "red", 270 id: closedTabGroupInClosedWindowId, 271 name: "closed-in-closed-window", 272 tabs: closedTabGroupInClosedWindowUrls.map(url => 273 makeClosedTabState(url, { 274 groupId: closedTabGroupInClosedWindowId, 275 }) 276 ), 277 }, 278 ], 279 }, 280 ], 281 }); 282 283 is(gBrowser.visibleTabs.length, 1, "We start with one tab open"); 284 // Open the "Recently closed tabs" panel. 285 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 286 287 // Click the first toolbar button in the panel. 288 let toolbarButton = closeTabsPanel.querySelector( 289 ".panel-subview-body toolbarbutton" 290 ); 291 let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 292 EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window); 293 294 info( 295 "We should reopen the first of closedTabUrls: " + 296 JSON.stringify(closedTabUrls) 297 ); 298 let reopenedTab = await newTabPromise; 299 is( 300 reopenedTab.linkedBrowser.currentURI.spec, 301 closedTabUrls[0], 302 "Opened the first URL" 303 ); 304 info(`restored tab, total open tabs: ${gBrowser.tabs.length}`); 305 306 info("waiting for closeTab"); 307 await SessionStoreTestUtils.closeTab(reopenedTab); 308 309 await openRecentlyClosedTabsMenu(); 310 let restoreAllItem = closeTabsPanel.querySelector(".restoreallitem"); 311 ok( 312 restoreAllItem && !restoreAllItem.hidden, 313 "Restore all menu item is not hidden" 314 ); 315 316 // Click the restore-all toolbar button in the panel. 317 EventUtils.sendMouseEvent({ type: "click" }, restoreAllItem, window); 318 319 info("waiting for restored tabs"); 320 await BrowserTestUtils.waitForCondition( 321 () => SessionStore.getClosedTabCount() === 0, 322 "Waiting for all the closed tabs to be opened" 323 ); 324 325 is( 326 gBrowser.tabs.length, 327 initialTabCount + 328 closedTabUrls.length + 329 closedTabGroupInOpenWindowUrls.length + 330 closedTabGroupInClosedWindowUrls.length, 331 "The expected number of closed tabs were restored" 332 ); 333 is( 334 gBrowser.tabGroups[0].id, 335 closedTabGroupInOpenWindowId, 336 "Closed tab group in open window was restored" 337 ); 338 closedTabGroupInOpenWindowUrls.forEach((expectedUrl, index) => { 339 is( 340 gBrowser.tabGroups[0].tabs[index].linkedBrowser.currentURI.spec, 341 expectedUrl, 342 `Closed tab group in open window tab #${index} has correct URL` 343 ); 344 }); 345 is( 346 gBrowser.tabGroups[1].id, 347 closedTabGroupInClosedWindowId, 348 "Closed tab group in closed window was restored" 349 ); 350 closedTabGroupInClosedWindowUrls.forEach((expectedUrl, index) => { 351 is( 352 gBrowser.tabGroups[1].tabs[index].linkedBrowser.currentURI.spec, 353 expectedUrl, 354 `Closed tab group in closed window tab #${index} has correct URL` 355 ); 356 }); 357 358 // clean up extra tabs 359 while (gBrowser.tabs.length > 1) { 360 BrowserTestUtils.removeTab(gBrowser.tabs.at(-1)); 361 } 362 }); 363 364 add_task(async function testRecentlyClosedWindows() { 365 // We need to make sure the history is cleared before starting the test 366 await Sanitizer.sanitize(["history"]); 367 await resetClosedTabsAndWindows(); 368 369 // Open and close a new window. 370 let newWin = await BrowserTestUtils.openNewBrowserWindow(); 371 let loadedPromise = BrowserTestUtils.browserLoaded( 372 newWin.gBrowser.selectedBrowser 373 ); 374 BrowserTestUtils.startLoadingURIString( 375 newWin.gBrowser.selectedBrowser, 376 "https://example.com" 377 ); 378 await loadedPromise; 379 let closedObjectsChangePromise = TestUtils.topicObserved( 380 "sessionstore-closed-objects-changed" 381 ); 382 await BrowserTestUtils.closeWindow(newWin); 383 await closedObjectsChangePromise; 384 385 prepareHistoryPanel(); 386 await openHistoryPanel(); 387 388 // Open the "Recently closed windows" panel. 389 document.getElementById("appMenuRecentlyClosedWindows").click(); 390 391 let winPanel = document.getElementById( 392 "appMenu-library-recentlyClosedWindows" 393 ); 394 await BrowserTestUtils.waitForEvent(winPanel, "ViewShown"); 395 ok(true, "Opened 'Recently closed windows' panel"); 396 397 // Click the first toolbar button in the panel. 398 let panelBody = winPanel.querySelector(".panel-subview-body"); 399 let toolbarButton = panelBody.querySelector("toolbarbutton"); 400 let newWindowPromise = BrowserTestUtils.waitForNewWindow({ 401 url: "https://example.com/", 402 }); 403 closedObjectsChangePromise = TestUtils.topicObserved( 404 "sessionstore-closed-objects-changed" 405 ); 406 EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window); 407 408 newWin = await newWindowPromise; 409 await closedObjectsChangePromise; 410 is(gBrowser.tabs.length, 1, "Did not open new tabs"); 411 412 await BrowserTestUtils.closeWindow(newWin); 413 }); 414 415 add_task(async function testRecentlyClosedTabsFromClosedWindows() { 416 await resetClosedTabsAndWindows(); 417 const closedTabUrls = [ 418 "about:robots", 419 "https://example.com/", 420 "https://example.org/", 421 ]; 422 const closedWindowState = { 423 tabs: [ 424 { 425 entries: [{ url: "about:mozilla", triggeringPrincipal_base64 }], 426 }, 427 ], 428 closedGroups: [], 429 _closedTabs: closedTabUrls.map(url => { 430 return { 431 title: url, 432 state: { 433 entries: [ 434 { 435 url, 436 triggeringPrincipal_base64, 437 }, 438 ], 439 }, 440 }; 441 }), 442 }; 443 await SessionStoreTestUtils.promiseBrowserState({ 444 windows: [ 445 { 446 tabs: [ 447 { 448 entries: [{ url: "about:mozilla", triggeringPrincipal_base64 }], 449 }, 450 ], 451 }, 452 ], 453 _closedWindows: [closedWindowState], 454 }); 455 Assert.equal( 456 SessionStore.getClosedTabCountFromClosedWindows(), 457 closedTabUrls.length, 458 "Sanity check number of closed tabs from closed windows" 459 ); 460 461 prepareHistoryPanel(); 462 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 463 // make sure we can actually restore one of these closed tabs 464 const closedTabItems = closeTabsPanel.querySelectorAll( 465 "toolbarbutton[targetURI]" 466 ); 467 Assert.equal( 468 closedTabItems.length, 469 closedTabUrls.length, 470 "We have expected number of closed tab items" 471 ); 472 473 const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 474 const closedObjectsChangePromise = TestUtils.topicObserved( 475 "sessionstore-closed-objects-changed" 476 ); 477 EventUtils.sendMouseEvent({ type: "click" }, closedTabItems[0], window); 478 await newTabPromise; 479 await closedObjectsChangePromise; 480 481 // flip the pref so none of the closed tabs from closed window are included 482 await SpecialPowers.pushPrefEnv({ 483 set: [["browser.sessionstore.closedTabsFromClosedWindows", false]], 484 }); 485 await openHistoryPanel(); 486 487 // verify the recently-closed-tabs menu item is disabled 488 let recentlyClosedTabsItem = document.getElementById( 489 "appMenuRecentlyClosedTabs" 490 ); 491 Assert.ok( 492 recentlyClosedTabsItem.hasAttribute("disabled"), 493 "Recently closed tabs button is now disabled" 494 ); 495 await hideHistoryPanel(); 496 497 SpecialPowers.popPrefEnv(); 498 while (gBrowser.tabs.length > 1) { 499 await SessionStoreTestUtils.closeTab( 500 gBrowser.tabs[gBrowser.tabs.length - 1] 501 ); 502 } 503 }); 504 505 add_task(async function testRecentlyClosedTabGroupsSingleTab() { 506 // We need to make sure the history is cleared before starting the test 507 await Sanitizer.sanitize(["history"]); 508 await resetClosedTabsAndWindows(); 509 prepareHistoryPanel(); 510 511 is(gBrowser.visibleTabs.length, 1, "We start with one tab already open"); 512 513 let aboutMozillaTab = BrowserTestUtils.addTab(gBrowser, "about:mozilla"); 514 let aboutLogoTab = BrowserTestUtils.addTab(gBrowser, "about:logo"); 515 let mozillaTabGroup = gBrowser.addTabGroup([aboutMozillaTab, aboutLogoTab], { 516 color: "red", 517 label: "mozilla stuff", 518 }); 519 const mozillaTabGroupId = mozillaTabGroup.id; 520 const mozillaTabGroupName = mozillaTabGroup.label; 521 let aboutRobotsTab = BrowserTestUtils.addTab(gBrowser, "about:robots"); 522 523 info("load all of the tabs"); 524 await Promise.all( 525 [aboutMozillaTab, aboutLogoTab, aboutRobotsTab].map(async t => { 526 await BrowserTestUtils.browserLoaded(t.linkedBrowser); 527 await TabStateFlusher.flush(t.linkedBrowser); 528 }) 529 ); 530 531 info("close the tab group and wait for it to be removed"); 532 let removePromise = BrowserTestUtils.waitForEvent( 533 mozillaTabGroup, 534 "TabGroupRemoved" 535 ); 536 gBrowser.removeTabGroup(mozillaTabGroup); 537 await removePromise; 538 539 is( 540 gBrowser.visibleTabs.length, 541 2, 542 "The tab from before the test plus about:robots should still be open" 543 ); 544 info("Open the 'Recently closed tabs' panel."); 545 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 546 547 // Click the tab group button in the panel. 548 let tabGroupToolbarButton = closeTabsPanel.querySelector( 549 `.panel-subview-body toolbarbutton[label="${mozillaTabGroupName}"]` 550 ); 551 ok(tabGroupToolbarButton, "should find the tab group toolbar button"); 552 553 let tabGroupPanelview = document.getElementById( 554 `closed-tabs-tab-group-${mozillaTabGroupId}` 555 ); 556 ok(tabGroupPanelview, "should find the tab group panelview"); 557 558 EventUtils.sendMouseEvent({ type: "click" }, tabGroupToolbarButton, window); 559 await BrowserTestUtils.waitForEvent(tabGroupPanelview, "ViewShown"); 560 561 info("restore the first tab from the closed tab group"); 562 let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 563 let tabToolbarButton = tabGroupPanelview.querySelector( 564 ".panel-subview-body toolbarbutton" 565 ); 566 ok(tabToolbarButton, "should find at least one tab to restore"); 567 let tabTitle = tabToolbarButton.label; 568 EventUtils.sendMouseEvent({ type: "click" }, tabToolbarButton, window); 569 570 let reopenedTab = await newTabPromise; 571 is(reopenedTab.label, tabTitle, "Opened the first URL"); 572 info(`restored tab, total open tabs: ${gBrowser.tabs.length}`); 573 574 // clean up extra tabs 575 while (gBrowser.tabs.length > 1) { 576 BrowserTestUtils.removeTab(gBrowser.tabs.at(-1)); 577 } 578 }); 579 580 add_task(async function testRecentlyClosedTabGroupOpensFromAnyWindow() { 581 // We need to make sure the history is cleared before starting the test 582 await Sanitizer.sanitize(["history"]); 583 await resetClosedTabsAndWindows(); 584 prepareHistoryPanel(); 585 586 is(gBrowser.visibleTabs.length, 1, "We start with one tab already open"); 587 588 let groupedTab = BrowserTestUtils.addTab(gBrowser, "https://example.com"); 589 await BrowserTestUtils.browserLoaded(groupedTab.linkedBrowser); 590 await TabStateFlusher.flush(groupedTab.linkedBrowser); 591 let tabGroup = gBrowser.addTabGroup([groupedTab], { label: "Some group" }); 592 const tabGroupId = tabGroup.id; 593 const tabGroupLabel = tabGroup.label; 594 595 info("close the tab group and wait for it to be removed"); 596 let removePromise = BrowserTestUtils.waitForEvent( 597 tabGroup, 598 "TabGroupRemoved" 599 ); 600 gBrowser.removeTabGroup(tabGroup); 601 await removePromise; 602 603 let newWin = await BrowserTestUtils.openNewBrowserWindow(); 604 let closeTabsPanel = await openRecentlyClosedTabsMenu(newWin.document); 605 606 // Click the tab group button in the panel. 607 let tabGroupToolbarButton = closeTabsPanel.querySelector( 608 `.panel-subview-body toolbarbutton[label="${tabGroupLabel}"]` 609 ); 610 ok(tabGroupToolbarButton, "should find the tab group toolbar button"); 611 612 let tabGroupPanelview = newWin.document.getElementById( 613 `closed-tabs-tab-group-${tabGroupId}` 614 ); 615 ok(tabGroupPanelview, "should find the tab group panelview"); 616 617 EventUtils.sendMouseEvent({ type: "click" }, tabGroupToolbarButton, window); 618 await BrowserTestUtils.waitForEvent(tabGroupPanelview, "ViewShown"); 619 620 let tabToolbarButton = tabGroupPanelview.querySelector( 621 "toolbarbutton.reopentabgroupitem" 622 ); 623 let tabGroupRestored = BrowserTestUtils.waitForEvent( 624 newWin.gBrowser.tabContainer, 625 "SSTabRestored" 626 ); 627 EventUtils.sendMouseEvent({ type: "click" }, tabToolbarButton, window); 628 await tabGroupRestored; 629 630 is( 631 newWin.gBrowser.tabGroups.length, 632 1, 633 "Tab group added to new window tab strip" 634 ); 635 is( 636 newWin.gBrowser.tabGroups[0].label, 637 tabGroupLabel, 638 "Tab group label is the same as it was before restore" 639 ); 640 641 await BrowserTestUtils.closeWindow(newWin); 642 }); 643 644 add_task(async function testRecentlyClosedTabsFromManyWindows() { 645 // Ensures that bug1943850 has a proper resolution. 646 info( 647 "Tabs must be indexed by individual window, even when multiple windows are open" 648 ); 649 650 await resetClosedTabsAndWindows(); 651 const ORIG_STATE = SessionStore.getBrowserState(); 652 653 await SessionStoreTestUtils.promiseBrowserState({ 654 windows: [ 655 { 656 tabs: [makeTabState("about:mozilla")], 657 _closedTabs: [ 658 makeClosedTabState("about:mozilla"), 659 makeClosedTabState("about:mozilla"), 660 ], 661 }, 662 { 663 tabs: [makeTabState("about:mozilla")], 664 _closedTabs: [makeClosedTabState("about:robots")], 665 }, 666 ], 667 }); 668 669 Assert.equal( 670 SessionStore.getClosedTabCount(), 671 3, 672 "Sanity check number of closed tabs from closed windows" 673 ); 674 675 prepareHistoryPanel(); 676 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 677 678 info("make sure we can actually restore one of these closed tabs"); 679 const closedTabItems = closeTabsPanel.querySelectorAll( 680 "toolbarbutton[targetURI]" 681 ); 682 Assert.equal( 683 closedTabItems.length, 684 3, 685 "We have expected number of closed tab items" 686 ); 687 688 const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 689 const closedObjectsChangePromise = TestUtils.topicObserved( 690 "sessionstore-closed-objects-changed" 691 ); 692 EventUtils.sendMouseEvent({ type: "click" }, closedTabItems[2], window); 693 await newTabPromise; 694 await closedObjectsChangePromise; 695 696 Assert.equal( 697 gBrowser.tabs[1].linkedBrowser.currentURI.spec, 698 "about:robots", 699 "Closed tab from the second window is opened" 700 ); 701 702 await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE); 703 }); 704 705 add_task(async function testTabsFromGroupClosedBeforeGroupDeleted() { 706 // Ensures that bug1945111 has a proper resolution. 707 info( 708 "Tabs that were closed from within a tab group should appear in history menus as standalone tabs, even if the tab group is later deleted" 709 ); 710 711 await resetClosedTabsAndWindows(); 712 const ORIG_STATE = SessionStore.getBrowserState(); 713 const GROUP_ID = "1234567890-1"; 714 715 await SessionStoreTestUtils.promiseBrowserState({ 716 windows: [ 717 { 718 tabs: [makeTabState("about:blank")], 719 _closedTabs: [ 720 makeClosedTabState("about:mozilla", { groupId: GROUP_ID }), 721 ], 722 closedGroups: [ 723 { 724 id: GROUP_ID, 725 color: "blue", 726 name: "tab-group", 727 tabs: [ 728 makeClosedTabState("about:mozilla", { 729 groupId: GROUP_ID, 730 closedInTabGroup: true, 731 }), 732 makeClosedTabState("about:mozilla", { 733 groupId: GROUP_ID, 734 closedInTabGroup: true, 735 }), 736 makeClosedTabState("about:mozilla", { 737 groupId: GROUP_ID, 738 closedInTabGroup: true, 739 }), 740 ], 741 }, 742 ], 743 }, 744 ], 745 }); 746 747 Assert.equal( 748 SessionStore.getClosedTabCount(), 749 4, 750 "Sanity check number of closed tabs from closed windows" 751 ); 752 753 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 754 755 const topLevelClosedTabItems = closeTabsPanel 756 .querySelector(".panel-subview-body") 757 .querySelectorAll(":scope > toolbarbutton[targetURI]"); 758 Assert.equal( 759 topLevelClosedTabItems.length, 760 1, 761 "We have the expected number of top-level closed tab items" 762 ); 763 764 const tabGroupClosedTabItems = closeTabsPanel.querySelectorAll( 765 `panelview#closed-tabs-tab-group-${GROUP_ID} toolbarbutton[targetURI]` 766 ); 767 Assert.equal( 768 tabGroupClosedTabItems.length, 769 3, 770 "We have the expected number of closed tab items within the tab group" 771 ); 772 773 await hideHistoryPanel(); 774 775 await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE); 776 }); 777 778 add_task(async function testOpenTabFromClosedGroupInClosedWindow() { 779 // Asserts fix for bug1944416 780 info( 781 "Open a tab from a closed tab group in a closed window as a standalone tab" 782 ); 783 await Sanitizer.sanitize(["history"]); 784 await resetClosedTabsAndWindows(); 785 const ORIG_STATE = SessionStore.getBrowserState(); 786 787 const closedTabUrl = "about:robots"; 788 const closedTabGroupUrls = ["about:logo", "https://example.com"]; 789 const closedTabGroupId = "1234567890-1"; 790 791 await SessionStoreTestUtils.promiseBrowserState({ 792 windows: [ 793 { 794 tabs: [makeTabState("about:blank")], 795 _closedTabs: [], 796 closedGroups: [], 797 }, 798 ], 799 _closedWindows: [ 800 { 801 tabs: [makeTabState("about:blank")], 802 _closedTabs: [makeClosedTabState(closedTabUrl)], 803 closedGroups: [ 804 { 805 id: closedTabGroupId, 806 color: "red", 807 name: "tab-group", 808 tabs: closedTabGroupUrls.map(url => 809 makeClosedTabState(url, { 810 groupId: closedTabGroupId, 811 closedInTabGroup: true, 812 }) 813 ), 814 }, 815 ], 816 }, 817 ], 818 }); 819 820 Assert.equal( 821 SessionStore.getClosedTabCountFromClosedWindows(), 822 closedTabGroupUrls.length + 1, // Add the lone ungrouped closed tab 823 "Sanity check number of closed tabs from closed windows" 824 ); 825 826 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 827 const closedTabItems = closeTabsPanel.querySelectorAll( 828 "toolbarbutton[targetURI]" 829 ); 830 831 let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 832 let closedObjectsChangePromise = TestUtils.topicObserved( 833 "sessionstore-closed-objects-changed" 834 ); 835 EventUtils.sendMouseEvent({ type: "click" }, closedTabItems[0], window); 836 await newTabPromise; 837 await closedObjectsChangePromise; 838 839 Assert.equal( 840 gBrowser.tabs.at(-1).linkedBrowser.currentURI.spec, 841 closedTabUrl, 842 "Ungrouped closed tab from closed window is opened correctly in the presence of closed tab groups" 843 ); 844 845 closeTabsPanel = await openRecentlyClosedTabsMenu(); 846 const tabGroupClosedTabItems = closeTabsPanel.querySelectorAll( 847 `panelview#closed-tabs-tab-group-${closedTabGroupId} toolbarbutton[targetURI]` 848 ); 849 850 newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 851 closedObjectsChangePromise = TestUtils.topicObserved( 852 "sessionstore-closed-objects-changed" 853 ); 854 EventUtils.sendMouseEvent( 855 { type: "click" }, 856 tabGroupClosedTabItems[0], 857 window 858 ); 859 await newTabPromise; 860 await closedObjectsChangePromise; 861 862 Assert.equal( 863 gBrowser.tabs.at(-1).linkedBrowser.currentURI.spec, 864 closedTabGroupUrls[0], 865 "Grouped closed tab from closed window is opened correctly" 866 ); 867 868 await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE); 869 }); 870 871 add_task(async function testHistoryMenusWorkWithOldPreTabGroupsState() { 872 info( 873 "Ensure that a session file created before the release of tab groups does not prevent closed tabs in closed windows from opening" 874 ); 875 // Fixes bug1947503 876 877 // We need to make sure the history is cleared before starting the test 878 await Sanitizer.sanitize(["history"]); 879 await resetClosedTabsAndWindows(); 880 const ORIG_STATE = SessionStore.getBrowserState(); 881 882 const closedTabUrls = [ 883 "about:robots", 884 "https://example.com/", 885 "https://example.org/", 886 ]; 887 888 await SessionStoreTestUtils.promiseBrowserState({ 889 windows: [ 890 { 891 tabs: [makeTabState("about:mozilla")], 892 _closedTabs: [], 893 // No closedGroups element 894 }, 895 ], 896 _closedWindows: [ 897 { 898 tabs: [makeTabState("about:mozilla")], 899 _closedTabs: closedTabUrls.map(makeClosedTabState), 900 // No closedGroups element 901 }, 902 ], 903 }); 904 905 is(gBrowser.visibleTabs.length, 1, "We start with one tab open"); 906 // Open the "Recently closed tabs" panel. 907 let closeTabsPanel = await openRecentlyClosedTabsMenu(); 908 909 // Click the first toolbar button in the panel. 910 let toolbarButton = closeTabsPanel.querySelector( 911 ".panel-subview-body toolbarbutton" 912 ); 913 let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true); 914 EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window); 915 let reopenedTab = await newTabPromise; 916 917 is(gBrowser.tabs.length, 2, "Closed tab was restored"); 918 919 await SessionStoreTestUtils.closeTab(reopenedTab); 920 await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE); 921 });