browser_restoreLastClosedTabOrWindowOrSession.js (10403B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { _LastSession, _lastClosedActions } = ChromeUtils.importESModule( 7 "resource:///modules/sessionstore/SessionStore.sys.mjs" 8 ); 9 10 async function testLastClosedActionsEntries() { 11 SessionStore.resetLastClosedActions(); 12 13 let win2 = await BrowserTestUtils.openNewBrowserWindow(); 14 BrowserTestUtils.startLoadingURIString( 15 win2.gBrowser.selectedBrowser, 16 "https://www.mozilla.org/" 17 ); 18 19 await BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser); 20 await openAndCloseTab(win2, "https://example.org/"); 21 22 Assert.equal( 23 SessionStore.lastClosedActions.length, 24 1, 25 `1 closed action has been recorded` 26 ); 27 28 await BrowserTestUtils.closeWindow(win2); 29 30 Assert.equal( 31 SessionStore.lastClosedActions.length, 32 2, 33 `2 closed actions have been recorded` 34 ); 35 } 36 37 add_setup(() => { 38 forgetClosedTabs(window); 39 registerCleanupFunction(() => { 40 forgetClosedTabs(window); 41 }); 42 43 // needed for verify tests so that forgetting tabs isn't recorded 44 SessionStore.resetLastClosedActions(); 45 }); 46 47 /** 48 * Tests that if the user invokes restoreLastClosedTabOrWindowOrSession it will 49 * result in either the last session will be restored, if possible, the last 50 * tab (or multiple selected tabs) that was closed is reopened, or the last 51 * window that is closed is reopened. 52 */ 53 add_task(async function test_undo_last_action() { 54 const state = { 55 windows: [ 56 { 57 tabs: [ 58 { 59 entries: [ 60 { 61 title: "example.org", 62 url: "https://example.org/", 63 triggeringPrincipal_base64, 64 }, 65 ], 66 }, 67 { 68 entries: [ 69 { 70 title: "example.com", 71 url: "https://example.com/", 72 triggeringPrincipal_base64, 73 }, 74 ], 75 }, 76 { 77 entries: [ 78 { 79 title: "mozilla", 80 url: "https://www.mozilla.org/", 81 triggeringPrincipal_base64, 82 }, 83 ], 84 }, 85 ], 86 selected: 3, 87 }, 88 ], 89 }; 90 91 _LastSession.setState(state); 92 93 let sessionRestored = promiseSessionStoreLoads(3 /* total restored tabs */); 94 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 95 await sessionRestored; 96 Assert.equal( 97 window.gBrowser.tabs.length, 98 3, 99 "Window has three tabs after session is restored" 100 ); 101 102 // open and close a window, then reopen it 103 let win2 = await BrowserTestUtils.openNewBrowserWindow(); 104 Assert.equal(win2.gBrowser.tabs.length, 1, "Second window has one open tab"); 105 BrowserTestUtils.startLoadingURIString( 106 win2.gBrowser.selectedBrowser, 107 "https://example.com/" 108 ); 109 await BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser); 110 await BrowserTestUtils.closeWindow(win2); 111 let restoredWinPromise = BrowserTestUtils.waitForNewWindow({ 112 url: "https://example.com/", 113 }); 114 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 115 let restoredWin = await restoredWinPromise; 116 Assert.equal( 117 restoredWin.gBrowser.tabs.length, 118 1, 119 "First tab in the second window has been reopened" 120 ); 121 await BrowserTestUtils.closeWindow(restoredWin); 122 SessionStore.forgetClosedWindow(restoredWin.index); 123 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 124 125 // close one tab and reopen it 126 BrowserTestUtils.removeTab(window.gBrowser.tabs[2]); 127 Assert.equal(window.gBrowser.tabs.length, 2, "Window has two open tabs"); 128 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 129 Assert.equal( 130 window.gBrowser.tabs.length, 131 3, 132 "Window now has three open tabs" 133 ); 134 135 // select 2 tabs and close both via the 'close 2 tabs' context menu option 136 let tab2 = window.gBrowser.tabs[1]; 137 let tab3 = window.gBrowser.tabs[2]; 138 await triggerClickOn(tab2, { ctrlKey: true }); 139 Assert.equal(tab2.multiselected, true); 140 Assert.equal(tab3.multiselected, true); 141 142 let menu = await openTabMenuFor(tab3); 143 let menuItemCloseTab = document.getElementById("context_closeTab"); 144 let tab2Closing = BrowserTestUtils.waitForTabClosing(tab2); 145 let tab3Closing = BrowserTestUtils.waitForTabClosing(tab3); 146 menu.activateItem(menuItemCloseTab); 147 await tab2Closing; 148 await tab3Closing; 149 Assert.equal(window.gBrowser.tabs[0].selected, true); 150 await TestUtils.waitForCondition(() => window.gBrowser.tabs.length == 1); 151 Assert.equal( 152 window.gBrowser.tabs.length, 153 1, 154 "Window now has one open tab after closing two multi-selected tabs" 155 ); 156 157 // ensure both tabs are reopened with a single click 158 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 159 Assert.equal( 160 window.gBrowser.tabs.length, 161 3, 162 "Window now has three open tabs after reopening closed tabs" 163 ); 164 165 // close one tab and forget it - it should not be reopened 166 BrowserTestUtils.removeTab(window.gBrowser.tabs[2]); 167 Assert.equal(window.gBrowser.tabs.length, 2, "Window has two open tabs"); 168 SessionStore.forgetClosedTab(window, 0); 169 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 170 Assert.equal( 171 window.gBrowser.tabs.length, 172 2, 173 "Window still has two open tabs" 174 ); 175 176 // create a tab group, delete it, and re-open it 177 const groupedTab = BrowserTestUtils.addTab( 178 window.gBrowser, 179 "https://example.com" 180 ); 181 await BrowserTestUtils.browserLoaded(groupedTab.linkedBrowser); 182 const tabGroup = window.gBrowser.addTabGroup([groupedTab]); 183 Assert.equal( 184 window.gBrowser.tabs.length, 185 3, 186 "Window has three tabs after creating tab group" 187 ); 188 189 await TabStateFlusher.flushWindow(window); 190 let removePromise = BrowserTestUtils.waitForEvent( 191 tabGroup, 192 "TabGroupRemoved" 193 ); 194 window.gBrowser.removeTabGroup(tabGroup); 195 await removePromise; 196 197 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 198 Assert.equal( 199 window.gBrowser.tabs.length, 200 3, 201 "Window has three tabs after restoring tab group" 202 ); 203 Assert.equal( 204 window.gBrowser.tabGroups.length, 205 1, 206 "Tab group exists on the tab strip after restore" 207 ); 208 209 // Save and close the tab group, and then re-open it 210 const groupedTabToSave = BrowserTestUtils.addTab( 211 window.gBrowser, 212 "https://example.com" 213 ); 214 await BrowserTestUtils.browserLoaded(groupedTabToSave.linkedBrowser); 215 const tabGroupToSave = window.gBrowser.addTabGroup([groupedTabToSave]); 216 Assert.equal( 217 window.gBrowser.tabs.length, 218 4, 219 "Window has four tabs after creating tab group" 220 ); 221 222 await TabStateFlusher.flushWindow(window); 223 tabGroupToSave.save(); 224 removePromise = BrowserTestUtils.waitForEvent( 225 tabGroupToSave, 226 "TabGroupRemoved" 227 ); 228 window.gBrowser.removeTabGroup(tabGroupToSave); 229 await removePromise; 230 231 await TabStateFlusher.flushWindow(window); 232 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 233 Assert.equal( 234 window.gBrowser.tabs.length, 235 4, 236 "Window has four tabs after restoring tab group" 237 ); 238 Assert.equal( 239 window.gBrowser.tabGroups.length, 240 2, 241 "Tab group exists on the tab strip after restore" 242 ); 243 244 gBrowser.removeAllTabsBut(gBrowser.tabs[0]); 245 }); 246 247 add_task(async function test_forget_closed_window() { 248 await testLastClosedActionsEntries(); 249 SessionStore.forgetClosedWindow(); 250 251 // both the forgotten window and its closed tab has been removed from the list 252 Assert.ok( 253 !SessionStore.lastClosedActions.length, 254 `0 closed actions have been recorded` 255 ); 256 }); 257 258 add_task(async function test_user_clears_history() { 259 await testLastClosedActionsEntries(); 260 Services.obs.notifyObservers(null, "browser:purge-session-history"); 261 262 // both the forgotten window and its closed tab has been removed from the list 263 Assert.ok( 264 !SessionStore.lastClosedActions.length, 265 `0 closed actions have been recorded` 266 ); 267 }); 268 269 /** 270 * It the browser has restarted and the closed actions list is empty, we 271 * should fallback to re-opening the last closed tab if one exists. 272 */ 273 add_task(async function test_reopen_last_tab_if_no_closed_actions() { 274 await BrowserTestUtils.withNewTab( 275 { 276 gBrowser, 277 url: "about:blank", 278 }, 279 async () => { 280 const TEST_URL = "https://example.com/"; 281 let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL); 282 let update = BrowserTestUtils.waitForSessionStoreUpdate(tab); 283 BrowserTestUtils.removeTab(tab); 284 await update; 285 286 SessionStore.resetLastClosedActions(); 287 288 let promiseTab = BrowserTestUtils.waitForNewTab(gBrowser, TEST_URL); 289 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 290 let newTab = await promiseTab; 291 Assert.equal(newTab.linkedBrowser.currentURI.spec, TEST_URL); 292 } 293 ); 294 }); 295 296 /** 297 * It the browser has restarted and the closed actions list is empty, and 298 * no closed tabs exist for the window, we should fallback to re-opening 299 * the last session if one exists. 300 */ 301 add_task(async function test_reopen_last_session_if_no_closed_actions() { 302 gBrowser.removeAllTabsBut(gBrowser.tabs[0]); 303 await TabStateFlusher.flushWindow(window); 304 305 forgetClosedTabs(window); 306 307 const state = { 308 windows: [ 309 { 310 tabs: [ 311 { 312 entries: [ 313 { 314 title: "example.org", 315 url: "https://example.org/", 316 triggeringPrincipal_base64, 317 }, 318 ], 319 }, 320 { 321 entries: [ 322 { 323 title: "example.com", 324 url: "https://example.com/", 325 triggeringPrincipal_base64, 326 }, 327 ], 328 }, 329 { 330 entries: [ 331 { 332 title: "mozilla", 333 url: "https://www.mozilla.org/", 334 triggeringPrincipal_base64, 335 }, 336 ], 337 }, 338 ], 339 selected: 3, 340 }, 341 ], 342 }; 343 344 _LastSession.setState(state); 345 SessionStore.resetLastClosedActions(); 346 347 let sessionRestored = promiseSessionStoreLoads(3 /* total restored tabs */); 348 SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window); 349 await sessionRestored; 350 Assert.equal(gBrowser.tabs.length, 4, "Got the expected number of tabs"); 351 gBrowser.removeAllTabsBut(gBrowser.tabs[0]); 352 });