browser_closed_tabs_closed_windows.js (10285B)
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 const { TelemetryTestUtils } = ChromeUtils.importESModule( 5 "resource://testing-common/TelemetryTestUtils.sys.mjs" 6 ); 7 const TEST_URLS = [ 8 "http://mochi.test:8888/browser/", 9 "https://www.example.com/", 10 "https://example.net/", 11 "https://example.org/", 12 "about:logo", 13 "about:robots", 14 ]; 15 const TOPIC_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed"; 16 var preparedClosedIds; 17 18 add_setup(async function testSetup() { 19 registerCleanupFunction(async () => { 20 await promiseAllButPrimaryWindowClosed(); 21 Services.obs.notifyObservers(null, "browser:purge-session-history"); 22 }); 23 preparedClosedIds = await prepareClosedData(); 24 SimpleTest.promiseFocus(window); 25 }); 26 27 async function prepareClosedData() { 28 // prepare some closed state 29 let closedIds = {}; 30 await openAndCloseTab(window, TEST_URLS[0]); 31 closedIds.tab0 = SessionStore.getClosedTabDataForWindow(window)[0].closedId; 32 33 const testWindow1 = await BrowserTestUtils.openNewBrowserWindow(); 34 // open a non-transitory, worth-keeping tab to ensure window data is saved on close 35 await BrowserTestUtils.openNewForegroundTab( 36 testWindow1.gBrowser, 37 "about:mozilla" 38 ); 39 await openAndCloseTab(testWindow1, TEST_URLS[1]); 40 closedIds.tab1 = 41 SessionStore.getClosedTabDataForWindow(testWindow1)[0].closedId; 42 43 const testWindow2 = await BrowserTestUtils.openNewBrowserWindow(); 44 // open a non-transitory, worth-keeping tab to ensure window data is saved on close 45 await BrowserTestUtils.openNewForegroundTab( 46 testWindow2.gBrowser, 47 "about:mozilla" 48 ); 49 await openAndCloseTab(testWindow2, TEST_URLS[2]); 50 closedIds.tab2 = 51 SessionStore.getClosedTabDataForWindow(testWindow2)[0].closedId; 52 await openAndCloseTab(testWindow2, TEST_URLS[3]); 53 closedIds.tab3 = 54 SessionStore.getClosedTabDataForWindow(testWindow2)[0].closedId; 55 56 const testWindow3 = await BrowserTestUtils.openNewBrowserWindow(); 57 // open a non-transitory, worth-keeping tab to ensure window data is saved on close 58 await BrowserTestUtils.openNewForegroundTab( 59 testWindow3.gBrowser, 60 "about:mozilla" 61 ); 62 await openAndCloseTab(testWindow3, TEST_URLS[4]); 63 closedIds.tab4 = 64 SessionStore.getClosedTabDataForWindow(testWindow3)[0].closedId; 65 66 // open a private window 67 const privateWin = await BrowserTestUtils.openNewBrowserWindow({ 68 private: true, 69 }); 70 // open a non-transitory, worth-keeping tab to ensure window data is saved on close 71 await BrowserTestUtils.openNewForegroundTab( 72 privateWin.gBrowser, 73 "about:mozilla" 74 ); 75 await openAndCloseTab(privateWin, TEST_URLS[5]); 76 closedIds.tab5 = 77 SessionStore.getClosedTabDataForWindow(privateWin)[0].closedId; 78 79 const testWindow6 = await BrowserTestUtils.openNewBrowserWindow(); 80 81 const testWindow7 = await BrowserTestUtils.openNewBrowserWindow(); 82 await openAndCloseTab(testWindow7, TEST_URLS[4]); 83 84 await BrowserTestUtils.closeWindow(testWindow1); 85 closedIds.testWindow1 = SessionStore.getClosedWindowData()[0].closedId; 86 await BrowserTestUtils.closeWindow(testWindow2); 87 88 closedIds.testWindow2 = SessionStore.getClosedWindowData()[0].closedId; 89 await BrowserTestUtils.closeWindow(testWindow3); 90 91 closedIds.testWindow3 = SessionStore.getClosedWindowData()[0].closedId; 92 await BrowserTestUtils.closeWindow(privateWin); 93 Assert.greater( 94 closedIds.testWindow2, 95 closedIds.testWindow1, 96 "We got the closedIds in the expected order" 97 ); 98 99 await BrowserTestUtils.closeWindow(testWindow6); 100 await BrowserTestUtils.closeWindow(testWindow7); 101 return closedIds; 102 } 103 104 // The tasks need to be run sequentially. They are broken up so the different functions 105 // under test and any stack traces are easier to find 106 107 add_task(async function get_closed_tabs_from_closed_windows() { 108 Assert.equal( 109 SessionStore.getClosedTabCount({ closedTabsFromClosedWindows: false }), 110 1, 111 "Expected closed tab count (in currently-open windows only)" 112 ); 113 Assert.equal( 114 SessionStore.getClosedTabCount(), 115 5, 116 "Expected closed tab count (in currently-open and closed windows)" 117 ); 118 // We dont keep the closed tab from the private window 119 Assert.equal( 120 SessionStore.getClosedTabCountFromClosedWindows(), 121 4, 122 "Expected closed tab count (from closed windows)" 123 ); 124 125 // Closed tabs from closed windows aren't included in the list from getClosedTabData() 126 Assert.deepEqual( 127 SessionStore.getClosedTabData().map(td => td.state.entries[0].url), 128 ["http://mochi.test:8888/browser/"], 129 "Matched closed tab data from currently open windows" 130 ); 131 132 // Closed tabs from open windows or closed private windows aren't included in the list 133 // from getClosedTabDataFromClosedWindows() 134 Assert.deepEqual( 135 SessionStore.getClosedTabDataFromClosedWindows().map( 136 td => td.state.entries[0].url 137 ), 138 [ 139 "about:logo", 140 "https://example.org/", 141 "https://example.net/", 142 "https://www.example.com/", 143 ], 144 "Matched closed tab data from currently closed windows" 145 ); 146 }); 147 148 add_task(async function forget_closed_tabs_from_closed_windows() { 149 // forget the most recent tab we closed in testWindow1 150 let closedObjectsChanged = TestUtils.topicObserved( 151 TOPIC_CLOSED_OBJECTS_CHANGED 152 ); 153 info( 154 "Calling forgetClosedTab with window sourceClosedId: " + 155 preparedClosedIds.testWindow1 156 ); 157 SessionStore.forgetClosedTab( 158 { sourceClosedId: preparedClosedIds.testWindow1 }, 159 0 160 ); 161 await closedObjectsChanged; 162 Assert.deepEqual( 163 SessionStore.getClosedTabDataFromClosedWindows().map( 164 td => td.state.entries[0].url 165 ), 166 [ 167 "about:logo", 168 "https://example.org/", 169 "https://example.net/", 170 // "https://www.example.com/" from testWindow1 was forgotten and should be removed 171 ], 172 "Closed tab forgotten" 173 ); 174 175 // forget a closed window that had closed tabs 176 let closedTabCount = SessionStore.getClosedTabCountFromClosedWindows(); 177 closedObjectsChanged = TestUtils.topicObserved(TOPIC_CLOSED_OBJECTS_CHANGED); 178 SessionStore.forgetClosedWindowById(preparedClosedIds.testWindow3); 179 await closedObjectsChanged; 180 Assert.equal( 181 SessionStore.getClosedTabCountFromClosedWindows(), 182 closedTabCount - 1, 183 "The closed tab is gone when we forget the closed window it was in" 184 ); 185 }); 186 187 add_task(async function expected_exceptions_closed_tabs_from_closed_windows() { 188 // testWindow3 was forgotten, attempting to undoClose its tabs should throw 189 Assert.throws( 190 () => 191 SessionStore.undoClosedTabFromClosedWindow( 192 { sourceClosedId: preparedClosedIds.testWindow3 }, 193 preparedClosedIds.tab4 194 ), 195 /NS_ERROR_ILLEGAL_VALUE/, 196 "Invalid window or source for undoClosedTabFromClosedWindow throws" 197 ); 198 // tab1 was forgotten, attempting to undoClose it should throw 199 Assert.throws( 200 () => 201 SessionStore.undoClosedTabFromClosedWindow( 202 { sourceClosedId: preparedClosedIds.testWindow1 }, 203 preparedClosedIds.tab1 204 ), 205 /NS_ERROR_ILLEGAL_VALUE/, 206 "Invalid tab closedId for undoClosedTabFromClosedWindow throws" 207 ); 208 209 // testWindow3 was forgotten, attempting to forget its tabs should throw 210 Assert.throws( 211 () => 212 SessionStore.forgetClosedTab( 213 { sourceClosedId: preparedClosedIds.testWindow3 }, 214 preparedClosedIds.tab4 215 ), 216 /NS_ERROR_ILLEGAL_VALUE/, 217 "Invalid window or source for forgetClosedTab throws" 218 ); 219 220 // tab1 was forgotten, attempting to forget it again should throw 221 Assert.throws( 222 () => 223 SessionStore.undoClosedTabFromClosedWindow( 224 { sourceClosedId: preparedClosedIds.testWindow3 }, 225 preparedClosedIds.tab4 226 ), 227 /NS_ERROR_ILLEGAL_VALUE/, 228 "Invalid window for undoClosedTabFromClosedWindow throws" 229 ); 230 }); 231 232 add_task(async function undo_closed_tabs_from_closed_windows() { 233 const openTabsCount = gBrowser.tabs.length; 234 235 // undo most recently closed tab from a closed window using a window closedId 236 let closedObjectsChanged = TestUtils.topicObserved( 237 TOPIC_CLOSED_OBJECTS_CHANGED 238 ); 239 let restoredTab1 = SessionStore.undoCloseTab( 240 { sourceClosedId: preparedClosedIds.testWindow2 }, 241 0, 242 window 243 ); 244 await BrowserTestUtils.waitForEvent( 245 window.gBrowser.tabContainer, 246 "SSTabRestored" 247 ); 248 await closedObjectsChanged; 249 Assert.equal( 250 window.gBrowser.tabs.length, 251 openTabsCount + 1, 252 "The closed tab was restored" 253 ); 254 Assert.equal( 255 restoredTab1.linkedBrowser.currentURI.spec, 256 "https://example.org/", 257 "The re-opened tab had the expected URL" 258 ); 259 Assert.deepEqual( 260 SessionStore.getClosedTabDataFromClosedWindows().map( 261 td => td.state.entries[0].url 262 ), 263 [ 264 "https://example.net/", 265 // "https://example.org/" from testWindow2 got reopened and should be removed 266 ], 267 "Re-opened tabs not listed in closed tabs" 268 ); 269 270 // use undoClosedTabFromClosedWindows to reference the closed tab by closedId 271 closedObjectsChanged = TestUtils.topicObserved(TOPIC_CLOSED_OBJECTS_CHANGED); 272 let restoredTab2 = SessionStore.undoClosedTabFromClosedWindow( 273 { sourceClosedId: preparedClosedIds.testWindow2 }, 274 preparedClosedIds.tab2, 275 window 276 ); 277 await BrowserTestUtils.waitForEvent( 278 window.gBrowser.tabContainer, 279 "SSTabRestored" 280 ); 281 await closedObjectsChanged; 282 Assert.equal( 283 window.gBrowser.tabs.length, 284 openTabsCount + 2, 285 "The closed tab was restored" 286 ); 287 Assert.equal( 288 restoredTab2.linkedBrowser.currentURI.spec, 289 "https://example.net/", 290 "The re-opened tab had the expected URL" 291 ); 292 293 Assert.deepEqual( 294 SessionStore.getClosedTabDataFromClosedWindows().map( 295 td => td.state.entries[0].url 296 ), 297 [ 298 // "https://example.org/" from testWindow2 got reopened and should be removed 299 ], 300 "Re-opened tabs not listed in closed tabs" 301 ); 302 303 // clean up the re-opened tabs 304 for (let tab of [restoredTab1, restoredTab2]) { 305 await TabStateFlusher.flush(tab.linkedBrowser); 306 let sessionUpdatePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab); 307 BrowserTestUtils.removeTab(tab); 308 await sessionUpdatePromise; 309 } 310 });