Source code

Revision control

Copy as Markdown

Other Tools

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.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() {
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) {
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));
},
};