Bug 1833522 - Share some of sessionstore's test helpers for use in recently-closed tab tests. r=sclements,fxview-reviewers,sessionstore-reviewers

* Move closeTab and openAndCloseTab to the helpers module and replace a couple of uses
* Move waitForBrowserState/promiseBrowserState and replace a couple of uses

Differential Revision: https://phabricator.services.mozilla.com/D187636
This commit is contained in:
Sam Foster 2023-09-08 00:36:32 +00:00
parent 758b64ad63
commit 7878082e5c
7 changed files with 392 additions and 366 deletions

View File

@ -9,23 +9,11 @@
* the current window.
*/
const openedTabs = [];
async function closeTab(tab) {
const sessionStoreChanged = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
BrowserTestUtils.removeTab(tab);
await sessionStoreChanged;
const idx = openedTabs.indexOf(tab);
ok(idx >= 0, "Tab was found in the openedTabs array");
openedTabs.splice(idx, 1);
}
async function openTab(win = window, url) {
const tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
openedTabs.push(tab);
}
const { SessionStoreTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/SessionStoreTestUtils.sys.mjs"
);
const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
SessionStoreTestUtils.init(this, window);
async function checkMenu(window, expected) {
await SimpleTest.promiseFocus(window);
@ -52,24 +40,11 @@ async function checkMenu(window, expected) {
info("checkMenu:, menuHidden, returning");
}
async function resetHistory() {
function resetClosedTabsAndWindows() {
// Clear the lists of closed windows and tabs.
let sessionStoreChanged;
while (SessionStore.getClosedWindowCount() > 0) {
sessionStoreChanged = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
SessionStore.forgetClosedWindow(0);
await sessionStoreChanged;
}
Services.obs.notifyObservers(null, "browser:purge-session-history");
is(SessionStore.getClosedWindowCount(), 0, "Expect 0 closed windows");
for (const win of BrowserWindowTracker.orderedWindows) {
while (SessionStore.getClosedTabCountForWindow(win)) {
sessionStoreChanged = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
SessionStore.forgetClosedTab(win, 0);
await sessionStoreChanged;
}
is(
SessionStore.getClosedTabCountForWindow(win),
0,
@ -79,16 +54,20 @@ async function resetHistory() {
}
add_task(async function test_recently_closed_tabs_nonprivate() {
await resetHistory();
is(openedTabs.length, 0, "Got expected openedTabs length");
await resetClosedTabsAndWindows();
const win1 = window;
const win2 = await BrowserTestUtils.openNewBrowserWindow();
await openTab(win1, "https://example.com");
await BrowserTestUtils.openNewForegroundTab(
win1.gBrowser,
"https://example.com"
);
// we're going to close a tab and don't want to accidentally close the window when it has 0 tabs
await openTab(win2, "about:about");
await openTab(win2, "https://example.org");
is(openedTabs.length, 3, "Got expected openedTabs length");
await BrowserTestUtils.openNewForegroundTab(win2.gBrowser, "about:about");
await BrowserTestUtils.openNewForegroundTab(
win2.gBrowser,
"https://example.org"
);
info("Checking the menuitem is initially disabled in both windows");
for (let win of [win1, win2]) {
@ -97,8 +76,7 @@ add_task(async function test_recently_closed_tabs_nonprivate() {
});
}
await closeTab(win2.gBrowser.selectedTab);
is(openedTabs.length, 2, "Got expected openedTabs length");
await SessionStoreTestUtils.closeTab(win2.gBrowser.selectedTab);
is(
SessionStore.getClosedTabCount(),
1,
@ -121,26 +99,31 @@ add_task(async function test_recently_closed_tabs_nonprivate() {
info("starting tab cleanup");
while (gBrowser.tabs.length > 1) {
await closeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
await SessionStoreTestUtils.closeTab(
gBrowser.tabs[gBrowser.tabs.length - 1]
);
}
info("finished tab cleanup");
openedTabs.length = 0;
});
add_task(async function test_recently_closed_tabs_nonprivate_pref_off() {
await SpecialPowers.pushPrefEnv({
set: [["browser.sessionstore.closedTabsFromAllWindows", false]],
});
await resetHistory();
is(openedTabs.length, 0, "Got expected openedTabs length");
await resetClosedTabsAndWindows();
const win1 = window;
const win2 = await BrowserTestUtils.openNewBrowserWindow();
await openTab(win1, "https://example.com");
await BrowserTestUtils.openNewForegroundTab(
win1.gBrowser,
"https://example.com"
);
// we're going to close a tab and don't want to accidentally close the window when it has 0 tabs
await openTab(win2, "about:about");
await openTab(win2, "https://example.org");
is(openedTabs.length, 3, "Got expected openedTabs length");
await BrowserTestUtils.openNewForegroundTab(win2.gBrowser, "about:about");
await BrowserTestUtils.openNewForegroundTab(
win2.gBrowser,
"https://example.org"
);
info("Checking the menuitem is initially disabled in both windows");
for (let win of [win1, win2]) {
@ -148,10 +131,8 @@ add_task(async function test_recently_closed_tabs_nonprivate_pref_off() {
menuItemDisabled: true,
});
}
is(win2, BrowserWindowTracker.getTopWindow(), "Check topWindow");
await closeTab(win2.gBrowser.selectedTab);
is(openedTabs.length, 2, "Got expected openedTabs length");
await SimpleTest.promiseFocus(win2);
await SessionStoreTestUtils.closeTab(win2.gBrowser.selectedTab);
is(
SessionStore.getClosedTabCount(),
1,
@ -175,31 +156,39 @@ add_task(async function test_recently_closed_tabs_nonprivate_pref_off() {
info("starting tab cleanup");
while (gBrowser.tabs.length > 1) {
await closeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
await SessionStoreTestUtils.closeTab(
gBrowser.tabs[gBrowser.tabs.length - 1]
);
}
info("finished tab cleanup");
openedTabs.length = 0;
SpecialPowers.popPrefEnv();
});
add_task(async function test_recently_closed_tabs_mixed_private() {
await resetHistory();
is(openedTabs.length, 0, "Got expected openedTabs length");
await resetClosedTabsAndWindows();
is(
SessionStore.getClosedTabCount(),
0,
"Expect closed tab count of 0 after reset"
);
await openTab(window, "about:robots");
await openTab(window, "https://example.com");
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "about:robots");
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com"
);
const privateWin = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
await openTab(privateWin, "about:about");
await openTab(privateWin, "https://example.org");
is(openedTabs.length, 4, "Got expected openedTabs length");
await BrowserTestUtils.openNewForegroundTab(
privateWin.gBrowser,
"about:about"
);
await BrowserTestUtils.openNewForegroundTab(
privateWin.gBrowser,
"https://example.org"
);
for (let win of [window, privateWin]) {
await checkMenu(win, {
@ -207,8 +196,7 @@ add_task(async function test_recently_closed_tabs_mixed_private() {
});
}
await closeTab(privateWin.gBrowser.selectedTab);
is(openedTabs.length, 3, "Got expected openedTabs length");
await SessionStoreTestUtils.closeTab(privateWin.gBrowser.selectedTab);
is(
SessionStore.getClosedTabCount(privateWin),
1,
@ -228,21 +216,16 @@ add_task(async function test_recently_closed_tabs_mixed_private() {
menuItemDisabled: false,
});
await resetHistory();
is(
SessionStore.getClosedTabCount(privateWin),
0,
"Expect 0 closed tab count after reset"
);
is(
SessionStore.getClosedTabCount(window),
0,
"Expect 0 closed tab count after reset"
);
await resetClosedTabsAndWindows();
await SimpleTest.promiseFocus(window);
info("closing tab in non-private window");
await closeTab(window.gBrowser.selectedTab);
is(openedTabs.length, 2, "Got expected openedTabs length");
await SessionStoreTestUtils.closeTab(window.gBrowser.selectedTab);
is(
SessionStore.getClosedTabCount(window),
1,
"Expect 1 closed tab count after closing the a tab in the non-private window"
);
// the menu should be enabled only for the non-private window
await checkMenu(window, {
@ -259,33 +242,36 @@ add_task(async function test_recently_closed_tabs_mixed_private() {
info("starting tab cleanup");
while (gBrowser.tabs.length > 1) {
await closeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
await SessionStoreTestUtils.closeTab(
gBrowser.tabs[gBrowser.tabs.length - 1]
);
}
info("finished tab cleanup");
openedTabs.length = 0;
});
add_task(async function test_recently_closed_tabs_mixed_private_pref_off() {
await SpecialPowers.pushPrefEnv({
set: [["browser.sessionstore.closedTabsFromAllWindows", false]],
});
await resetHistory();
is(openedTabs.length, 0, "Got expected openedTabs length");
is(
SessionStore.getClosedTabCount(),
0,
"Expect closed tab count of 0 after reset"
);
await resetClosedTabsAndWindows();
await openTab(window, "about:robots");
await openTab(window, "https://example.com");
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "about:robots");
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com"
);
const privateWin = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
await openTab(privateWin, "about:about");
await openTab(privateWin, "https://example.org");
is(openedTabs.length, 4, "Got expected openedTabs length");
await BrowserTestUtils.openNewForegroundTab(
privateWin.gBrowser,
"about:about"
);
await BrowserTestUtils.openNewForegroundTab(
privateWin.gBrowser,
"https://example.org"
);
for (let win of [window, privateWin]) {
await checkMenu(win, {
@ -293,8 +279,8 @@ add_task(async function test_recently_closed_tabs_mixed_private_pref_off() {
});
}
await closeTab(privateWin.gBrowser.selectedTab);
is(openedTabs.length, 3, "Got expected openedTabs length");
await SimpleTest.promiseFocus(privateWin);
await SessionStoreTestUtils.closeTab(privateWin.gBrowser.selectedTab);
is(
SessionStore.getClosedTabCount(privateWin),
1,
@ -314,7 +300,7 @@ add_task(async function test_recently_closed_tabs_mixed_private_pref_off() {
menuItemDisabled: false,
});
await resetHistory();
await resetClosedTabsAndWindows();
is(
SessionStore.getClosedTabCount(privateWin),
0,
@ -327,8 +313,8 @@ add_task(async function test_recently_closed_tabs_mixed_private_pref_off() {
);
info("closing tab in non-private window");
await closeTab(window.gBrowser.selectedTab);
is(openedTabs.length, 2, "Got expected openedTabs length");
await SimpleTest.promiseFocus(window);
await SessionStoreTestUtils.closeTab(window.gBrowser.selectedTab);
// the menu should be enabled only for the non-private window
await checkMenu(window, {
@ -345,9 +331,10 @@ add_task(async function test_recently_closed_tabs_mixed_private_pref_off() {
info("starting tab cleanup");
while (gBrowser.tabs.length > 1) {
await closeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
await SessionStoreTestUtils.closeTab(
gBrowser.tabs[gBrowser.tabs.length - 1]
);
}
info("finished tab cleanup");
openedTabs.length = 0;
SpecialPowers.popPrefEnv();
});

View File

@ -4,23 +4,18 @@
"use strict";
const { SessionStoreTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/SessionStoreTestUtils.sys.mjs"
);
const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
SessionStoreTestUtils.init(this, window);
const TEST_PATH = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"http://example.com"
);
async function openTab(URL) {
let tab = BrowserTestUtils.addTab(gBrowser, URL);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
return tab;
}
async function closeTab(tab) {
const sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
BrowserTestUtils.removeTab(tab);
await sessionStorePromise;
}
let panelMenuWidgetAdded = false;
function prepareHistoryPanel() {
if (panelMenuWidgetAdded) {
@ -108,8 +103,10 @@ add_task(async function testRecentlyClosedDisabled() {
await hideHistoryPanel();
gBrowser.selectedTab.focus();
let tab = await openTab(TEST_PATH + "dummy_history_item.html");
await closeTab(tab);
await SessionStoreTestUtils.openAndCloseTab(
window,
TEST_PATH + "dummy_history_item.html"
);
await openHistoryPanel();
@ -207,69 +204,85 @@ add_task(async function testRecentlyClosedRestoreAllTabs() {
// We need to make sure the history is cleared before starting the test
await Sanitizer.sanitize(["history"]);
await resetClosedTabs();
const initialTabCount = gBrowser.visibleTabs.length;
await BrowserTestUtils.withNewTab("about:mozilla", async browser => {
const initialTabCount = gBrowser.visibleTabs.length;
// Open and close a few of tabs
const closedTabUrls = [
"about:robots",
"https://example.com/",
"https://example.org/",
];
for (let url of closedTabUrls) {
let tab = await openTab(url);
await closeTab(tab);
}
is(
gBrowser.visibleTabs.length,
initialTabCount,
"All the new tabs were closed"
);
// Open the "Recently closed tabs" panel.
let closeTabsPanel = await openRecentlyClosedTabsMenu();
// Click the first toolbar button in the panel.
let toolbarButton = closeTabsPanel.querySelector(
".panel-subview-body toolbarbutton"
);
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window);
info("waiting for reopenedTab");
let reopenedTab = await newTabPromise;
is(
reopenedTab.linkedBrowser.currentURI.spec,
closedTabUrls[closedTabUrls.length - 1],
"Opened correct URL"
);
info(`restored tab, total open tabs: ${gBrowser.tabs.length}`);
await closeTab(reopenedTab);
await openRecentlyClosedTabsMenu();
let restoreAllItem = closeTabsPanel.querySelector(".restoreallitem");
ok(
restoreAllItem && !restoreAllItem.hidden,
"Restore all menu item is not hidden"
);
// Click the restore-all toolbar button in the panel.
EventUtils.sendMouseEvent({ type: "click" }, restoreAllItem, window);
info("waiting for restored tabs");
await BrowserTestUtils.waitForCondition(
() => SessionStore.getClosedTabCount() === 0,
"Waiting for all the closed tabs to be opened"
);
is(
gBrowser.tabs.length,
initialTabCount + closedTabUrls.length,
"The expected number of closed tabs were restored"
);
info(
`restored ${closedTabUrls.length} tabs, total open tabs: ${gBrowser.tabs.length}`
);
const closedTabUrls = [
"about:robots",
"https://example.com/",
"https://example.org/",
];
const windowState = {
tabs: [
{
entries: [{ url: "about:mozilla", triggeringPrincipal_base64 }],
},
],
_closedTabs: closedTabUrls.map(url => {
return {
title: url,
state: {
entries: [
{
url,
triggeringPrincipal_base64,
},
],
},
};
}),
};
await SessionStoreTestUtils.promiseBrowserState({
windows: [windowState],
});
is(gBrowser.visibleTabs.length, 1, "We start with one tab open");
// Open the "Recently closed tabs" panel.
let closeTabsPanel = await openRecentlyClosedTabsMenu();
// Click the first toolbar button in the panel.
let toolbarButton = closeTabsPanel.querySelector(
".panel-subview-body toolbarbutton"
);
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window);
info(
"We should reopen the first of closedTabUrls: " +
JSON.stringify(closedTabUrls)
);
let reopenedTab = await newTabPromise;
is(
reopenedTab.linkedBrowser.currentURI.spec,
closedTabUrls[0],
"Opened the first URL"
);
info(`restored tab, total open tabs: ${gBrowser.tabs.length}`);
info("waiting for closeTab");
await SessionStoreTestUtils.closeTab(reopenedTab);
await openRecentlyClosedTabsMenu();
let restoreAllItem = closeTabsPanel.querySelector(".restoreallitem");
ok(
restoreAllItem && !restoreAllItem.hidden,
"Restore all menu item is not hidden"
);
// Click the restore-all toolbar button in the panel.
EventUtils.sendMouseEvent({ type: "click" }, restoreAllItem, window);
info("waiting for restored tabs");
await BrowserTestUtils.waitForCondition(
() => SessionStore.getClosedTabCount() === 0,
"Waiting for all the closed tabs to be opened"
);
is(
gBrowser.tabs.length,
initialTabCount + closedTabUrls.length,
"The expected number of closed tabs were restored"
);
// clean up extra tabs
while (gBrowser.tabs.length > 1) {
BrowserTestUtils.removeTab(gBrowser.tabs.at(-1));
@ -279,6 +292,7 @@ add_task(async function testRecentlyClosedRestoreAllTabs() {
add_task(async function testRecentlyClosedWindows() {
// We need to make sure the history is cleared before starting the test
await Sanitizer.sanitize(["history"]);
await resetClosedTabs();
// Open and close a new window.
let newWin = await BrowserTestUtils.openNewBrowserWindow();
@ -290,7 +304,11 @@ add_task(async function testRecentlyClosedWindows() {
"https://example.com"
);
await loadedPromise;
let closedObjectsChangePromise = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
await BrowserTestUtils.closeWindow(newWin);
await closedObjectsChangePromise;
prepareHistoryPanel();
await openHistoryPanel();
@ -307,15 +325,16 @@ add_task(async function testRecentlyClosedWindows() {
// Click the first toolbar button in the panel.
let panelBody = winPanel.querySelector(".panel-subview-body");
let toolbarButton = panelBody.querySelector("toolbarbutton");
let newWindowPromise = BrowserTestUtils.waitForNewWindow();
let newWindowPromise = BrowserTestUtils.waitForNewWindow({
url: "https://example.com/",
});
closedObjectsChangePromise = TestUtils.topicObserved(
"sessionstore-closed-objects-changed"
);
EventUtils.sendMouseEvent({ type: "click" }, toolbarButton, window);
newWin = await newWindowPromise;
is(
newWin.gBrowser.currentURI.spec,
"https://example.com/",
"Opened correct URL"
);
await closedObjectsChangePromise;
is(gBrowser.tabs.length, 1, "Did not open new tabs");
await BrowserTestUtils.closeWindow(newWin);

View File

@ -30,6 +30,12 @@ const { TelemetryTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/TelemetryTestUtils.sys.mjs"
);
const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
const { SessionStoreTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/SessionStoreTestUtils.sys.mjs"
);
SessionStoreTestUtils.init(this, window);
ChromeUtils.defineESModuleGetters(this, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
SyncedTabs: "resource://services-sync/SyncedTabs.sys.mjs",
@ -537,18 +543,7 @@ class TelemetrySpy {
* has been updated after closing the tab.
*/
async function open_then_close(url, win = window) {
let { updatePromise } = await BrowserTestUtils.withNewTab(
{ url, gBrowser: win.gBrowser },
async browser => {
return {
updatePromise: BrowserTestUtils.waitForSessionStoreUpdate({
linkedBrowser: browser,
}),
};
}
);
await updatePromise;
return TestUtils.topicObserved("sessionstore-closed-objects-changed");
return SessionStoreTestUtils.openAndCloseTab(win, url);
}
/**

View File

@ -29,5 +29,9 @@ EXTRA_JS_MODULES.sessionstore = [
"TabStateFlusher.sys.mjs",
]
TESTING_JS_MODULES += [
"test/SessionStoreTestUtils.sys.mjs",
]
with Files("**"):
BUG_COMPONENT = ("Firefox", "Session Restore")

View File

@ -0,0 +1,186 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserTestUtils: "resource://testing-common/BrowserTestUtils.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
TestUtils: "resource://testing-common/TestUtils.sys.mjs",
});
export var SessionStoreTestUtils = {
/**
* Running this init allows helpers to access test scope helpers, like Assert
* and SimpleTest.
* Tests should call this init() before using the helpers which rely on properties assign here.
*
* @param {object} scope The global scope where tests are being run.
* @param {DOmWindow} scope The global window object, for acessing gBrowser etc.
*/
init(scope, windowGlobal) {
if (!scope) {
throw new Error(
"Must initialize SessionStoreTestUtils with a test scope"
);
}
if (!windowGlobal) {
throw new Error("this.windowGlobal must be defined when we init");
}
this.info = scope.info;
this.registerCleanupFunction = scope.registerCleanupFunction;
this.windowGlobal = windowGlobal;
},
async closeTab(tab) {
await lazy.TabStateFlusher.flush(tab.linkedBrowser);
let sessionUpdatePromise =
lazy.BrowserTestUtils.waitForSessionStoreUpdate(tab);
lazy.BrowserTestUtils.removeTab(tab);
await sessionUpdatePromise;
},
async openAndCloseTab(window, url) {
let { updatePromise } = await lazy.BrowserTestUtils.withNewTab(
{ url, gBrowser: window.gBrowser },
async browser => {
return {
updatePromise: lazy.BrowserTestUtils.waitForSessionStoreUpdate({
linkedBrowser: browser,
}),
};
}
);
await updatePromise;
return lazy.TestUtils.topicObserved("sessionstore-closed-objects-changed");
},
// This assumes that tests will at least have some state/entries
waitForBrowserState(aState, aSetStateCallback) {
if (typeof aState == "string") {
aState = JSON.parse(aState);
}
if (typeof aState != "object") {
throw new TypeError(
"Argument must be an object or a JSON representation of an object"
);
}
if (!this.windowGlobal) {
throw new Error(
"no windowGlobal defined, please call init() first with the scope and window object"
);
}
let windows = [this.windowGlobal];
let tabsRestored = 0;
let expectedTabsRestored = 0;
let expectedWindows = aState.windows.length;
let windowsOpen = 1;
let listening = false;
let windowObserving = false;
let restoreHiddenTabs = Services.prefs.getBoolPref(
"browser.sessionstore.restore_hidden_tabs"
);
// This should match the |restoreTabsLazily| value that
// SessionStore.restoreWindow() uses.
let restoreTabsLazily =
Services.prefs.getBoolPref("browser.sessionstore.restore_on_demand") &&
Services.prefs.getBoolPref("browser.sessionstore.restore_tabs_lazily");
aState.windows.forEach(function (winState) {
winState.tabs.forEach(function (tabState) {
if (!restoreTabsLazily && (restoreHiddenTabs || !tabState.hidden)) {
expectedTabsRestored++;
}
});
});
// If there are only hidden tabs and restoreHiddenTabs = false, we still
// expect one of them to be restored because it gets shown automatically.
// Otherwise if lazy tab restore there will only be one tab restored per window.
if (!expectedTabsRestored) {
expectedTabsRestored = 1;
} else if (restoreTabsLazily) {
expectedTabsRestored = aState.windows.length;
}
function onSSTabRestored(aEvent) {
if (++tabsRestored == expectedTabsRestored) {
// Remove the event listener from each window
windows.forEach(function (win) {
win.gBrowser.tabContainer.removeEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
});
listening = false;
SessionStoreTestUtils.info("running " + aSetStateCallback.name);
lazy.TestUtils.executeSoon(aSetStateCallback);
}
}
// Used to add our listener to further windows so we can catch SSTabRestored
// coming from them when creating a multi-window state.
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let newWindow = aSubject;
newWindow.addEventListener(
"load",
function () {
if (++windowsOpen == expectedWindows) {
Services.ww.unregisterNotification(windowObserver);
windowObserving = false;
}
// Track this window so we can remove the progress listener later
windows.push(newWindow);
// Add the progress listener
newWindow.gBrowser.tabContainer.addEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
},
{ once: true }
);
}
}
// We only want to register the notification if we expect more than 1 window
if (expectedWindows > 1) {
this.registerCleanupFunction(function () {
if (windowObserving) {
Services.ww.unregisterNotification(windowObserver);
}
});
windowObserving = true;
Services.ww.registerNotification(windowObserver);
}
this.registerCleanupFunction(function () {
if (listening) {
windows.forEach(function (win) {
win.gBrowser.tabContainer.removeEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
});
}
});
// Add the event listener for this window as well.
listening = true;
this.windowGlobal.gBrowser.tabContainer.addEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
// Ensure setBrowserState() doesn't remove the initial tab.
this.windowGlobal.gBrowser.selectedTab = this.windowGlobal.gBrowser.tabs[0];
// Finally, call setBrowserState
lazy.SessionStore.setBrowserState(JSON.stringify(aState));
},
promiseBrowserState(aState) {
return new Promise(resolve => this.waitForBrowserState(aState, resolve));
},
};

View File

@ -64,57 +64,8 @@ const multiWindowState = {
],
};
async function setupWithBrowserState(browserState) {
let numTabs = browserState.windows.reduce((count, winData) => {
return count + winData.tabs.length;
}, 0);
let loadCount = 0;
let windowOpenedCount = 1; // pre-count the first window
let promiseRestoringTabs = new Promise(resolve => {
gProgressListener.setCallback(function (
aBrowser,
aNeedRestore,
aRestoring,
aRestored
) {
if (++loadCount == numTabs) {
// We don't actually care about load order in this test, just that they all
// do load.
is(loadCount, numTabs, "all tabs were restored");
is(aNeedRestore, 0, "there are no tabs left needing restore");
gProgressListener.unsetCallback();
resolve();
}
});
});
// We also want to catch the 2nd window, so we need to observe domwindowopened
Services.ww.registerNotification(function observer(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let win = aSubject;
win.addEventListener(
"load",
function () {
if (++windowOpenedCount == browserState.windows.length) {
Services.ww.unregisterNotification(observer);
win.gBrowser.addTabsProgressListener(gProgressListener);
}
},
{ once: true }
);
}
});
const stateRestored = TestUtils.topicObserved(
"sessionstore-browser-state-restored"
);
await ss.setBrowserState(JSON.stringify(browserState));
await stateRestored;
await promiseRestoringTabs;
}
add_setup(async function testSetup() {
await setupWithBrowserState(multiWindowState);
await SessionStoreTestUtils.promiseBrowserState(multiWindowState);
});
add_task(async function test_ClosedTabMethods() {

View File

@ -29,7 +29,12 @@ const { TabState } = ChromeUtils.importESModule(
const { TabStateFlusher } = ChromeUtils.importESModule(
"resource:///modules/sessionstore/TabStateFlusher.sys.mjs"
);
const { SessionStoreTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/SessionStoreTestUtils.sys.mjs"
);
const ss = SessionStore;
SessionStoreTestUtils.init(this, window);
// Some tests here assume that all restored tabs are loaded without waiting for
// the user to bring them to the foreground. We ensure this by resetting the
@ -81,129 +86,11 @@ function provideWindow(aCallback, aURL, aFeatures) {
// This assumes that tests will at least have some state/entries
function waitForBrowserState(aState, aSetStateCallback) {
if (typeof aState == "string") {
aState = JSON.parse(aState);
}
if (typeof aState != "object") {
throw new TypeError(
"Argument must be an object or a JSON representation of an object"
);
}
let windows = [window];
let tabsRestored = 0;
let expectedTabsRestored = 0;
let expectedWindows = aState.windows.length;
let windowsOpen = 1;
let listening = false;
let windowObserving = false;
let restoreHiddenTabs = Services.prefs.getBoolPref(
"browser.sessionstore.restore_hidden_tabs"
);
// This should match the |restoreTabsLazily| value that
// SessionStore.restoreWindow() uses.
let restoreTabsLazily =
Services.prefs.getBoolPref("browser.sessionstore.restore_on_demand") &&
Services.prefs.getBoolPref("browser.sessionstore.restore_tabs_lazily");
aState.windows.forEach(function (winState) {
winState.tabs.forEach(function (tabState) {
if (!restoreTabsLazily && (restoreHiddenTabs || !tabState.hidden)) {
expectedTabsRestored++;
}
});
});
// If there are only hidden tabs and restoreHiddenTabs = false, we still
// expect one of them to be restored because it gets shown automatically.
// Otherwise if lazy tab restore there will only be one tab restored per window.
if (!expectedTabsRestored) {
expectedTabsRestored = 1;
} else if (restoreTabsLazily) {
expectedTabsRestored = aState.windows.length;
}
function onSSTabRestored(aEvent) {
if (++tabsRestored == expectedTabsRestored) {
// Remove the event listener from each window
windows.forEach(function (win) {
win.gBrowser.tabContainer.removeEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
});
listening = false;
info("running " + aSetStateCallback.name);
executeSoon(aSetStateCallback);
}
}
// Used to add our listener to further windows so we can catch SSTabRestored
// coming from them when creating a multi-window state.
function windowObserver(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let newWindow = aSubject;
newWindow.addEventListener(
"load",
function () {
if (++windowsOpen == expectedWindows) {
Services.ww.unregisterNotification(windowObserver);
windowObserving = false;
}
// Track this window so we can remove the progress listener later
windows.push(newWindow);
// Add the progress listener
newWindow.gBrowser.tabContainer.addEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
},
{ once: true }
);
}
}
// We only want to register the notification if we expect more than 1 window
if (expectedWindows > 1) {
registerCleanupFunction(function () {
if (windowObserving) {
Services.ww.unregisterNotification(windowObserver);
}
});
windowObserving = true;
Services.ww.registerNotification(windowObserver);
}
registerCleanupFunction(function () {
if (listening) {
windows.forEach(function (win) {
win.gBrowser.tabContainer.removeEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
});
}
});
// Add the event listener for this window as well.
listening = true;
gBrowser.tabContainer.addEventListener(
"SSTabRestored",
onSSTabRestored,
true
);
// Ensure setBrowserState() doesn't remove the initial tab.
gBrowser.selectedTab = gBrowser.tabs[0];
// Finally, call setBrowserState
ss.setBrowserState(JSON.stringify(aState));
return SessionStoreTestUtils.waitForBrowserState(aState, aSetStateCallback);
}
function promiseBrowserState(aState) {
return new Promise(resolve => waitForBrowserState(aState, resolve));
return SessionStoreTestUtils.promiseBrowserState(aState);
}
function promiseTabState(tab, state) {
@ -751,11 +638,8 @@ function addNonCoopTask(aFile, aTest, aUrlRoot) {
add_task(taskToBeAdded);
}
async function openAndCloseTab(window, url) {
let tab = BrowserTestUtils.addTab(window.gBrowser, url);
await promiseBrowserLoaded(tab.linkedBrowser, true, url);
await TabStateFlusher.flush(tab.linkedBrowser);
await promiseRemoveTabAndSessionState(tab);
function openAndCloseTab(window, url) {
return SessionStoreTestUtils.openAndCloseTab(window, url);
}
/**