forked from ZenBrowserMirrors/zen-desktop
chore: Added more checks for restoring previous sessions, b=(no-bug), c=workspaces, tests
This commit is contained in:
parent
d788ac4ad6
commit
5c30c83341
4 changed files with 685 additions and 4 deletions
|
@ -1 +1,7 @@
|
|||
[DEFAULT]
|
||||
support-files = [
|
||||
"head.js",
|
||||
]
|
||||
|
||||
["browser_basic_workspaces.js"]
|
||||
["browser_restore_workspaces.js"]
|
||||
|
|
58
src/zen/tests/workspaces/browser_restore_workspaces.js
Normal file
58
src/zen/tests/workspaces/browser_restore_workspaces.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that sessionrestore handles cycles in the shentry graph properly.
|
||||
//
|
||||
// These cycles shouldn't be there in the first place, but they cause hangs
|
||||
// when they mysteriously appear (bug 687710). Docshell code assumes this
|
||||
// graph is a tree and tires to walk to the root. But if there's a cycle,
|
||||
// there is no root, and we loop forever.
|
||||
|
||||
var stateBackup = ss.getBrowserState();
|
||||
|
||||
var state = {
|
||||
windows: [
|
||||
{
|
||||
tabs: [
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
docIdentifier: 1,
|
||||
url: 'http://example.com',
|
||||
triggeringPrincipal_base64,
|
||||
children: [
|
||||
{
|
||||
docIdentifier: 2,
|
||||
url: 'http://example.com',
|
||||
triggeringPrincipal_base64,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
docIdentifier: 2,
|
||||
url: 'http://example.com',
|
||||
triggeringPrincipal_base64,
|
||||
children: [
|
||||
{
|
||||
docIdentifier: 1,
|
||||
url: 'http://example.com',
|
||||
triggeringPrincipal_base64,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
add_task(async function test() {
|
||||
registerCleanupFunction(function () {
|
||||
ss.setBrowserState(stateBackup);
|
||||
});
|
||||
|
||||
/* This test fails by hanging. */
|
||||
await setBrowserState(state);
|
||||
ok(true, "Didn't hang!");
|
||||
});
|
614
src/zen/tests/workspaces/head.js
Normal file
614
src/zen/tests/workspaces/head.js
Normal file
|
@ -0,0 +1,614 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
|
||||
|
||||
const TAB_STATE_NEEDS_RESTORE = 1;
|
||||
const TAB_STATE_RESTORING = 2;
|
||||
|
||||
const ROOT = getRootDirectory(gTestPath);
|
||||
const HTTPROOT = ROOT.replace('chrome://mochitests/content/', 'http://example.com/');
|
||||
const HTTPSROOT = ROOT.replace('chrome://mochitests/content/', 'https://example.com/');
|
||||
|
||||
const { SessionSaver } = ChromeUtils.importESModule('resource:///modules/sessionstore/SessionSaver.sys.mjs');
|
||||
const { SessionFile } = ChromeUtils.importESModule('resource:///modules/sessionstore/SessionFile.sys.mjs');
|
||||
const { TabState } = ChromeUtils.importESModule('resource:///modules/sessionstore/TabState.sys.mjs');
|
||||
const { TabStateFlusher } = ChromeUtils.importESModule('resource:///modules/sessionstore/TabStateFlusher.sys.mjs');
|
||||
const { SessionStoreTestUtils } = ChromeUtils.importESModule('resource://testing-common/SessionStoreTestUtils.sys.mjs');
|
||||
|
||||
const { PageWireframes } = ChromeUtils.importESModule('resource:///modules/sessionstore/PageWireframes.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
|
||||
// related preference (see the "firefox.js" defaults file for details).
|
||||
Services.prefs.setBoolPref('browser.sessionstore.restore_on_demand', false);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref('browser.sessionstore.restore_on_demand');
|
||||
});
|
||||
|
||||
// Obtain access to internals
|
||||
Services.prefs.setBoolPref('browser.sessionstore.debug', true);
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref('browser.sessionstore.debug');
|
||||
});
|
||||
|
||||
// This kicks off the search service used on about:home and allows the
|
||||
// session restore tests to be run standalone without triggering errors.
|
||||
Cc['@mozilla.org/browser/clh;1'].getService(Ci.nsIBrowserHandler).defaultArgs;
|
||||
|
||||
function provideWindow(aCallback, aURL, aFeatures) {
|
||||
function callbackSoon(aWindow) {
|
||||
executeSoon(function executeCallbackSoon() {
|
||||
aCallback(aWindow);
|
||||
});
|
||||
}
|
||||
|
||||
let win = openDialog(AppConstants.BROWSER_CHROME_URL, '', aFeatures || 'chrome,all,dialog=no', aURL || 'about:blank');
|
||||
whenWindowLoaded(win, function onWindowLoaded(aWin) {
|
||||
if (!aURL) {
|
||||
info('Loaded a blank window.');
|
||||
callbackSoon(aWin);
|
||||
return;
|
||||
}
|
||||
|
||||
aWin.gBrowser.selectedBrowser.addEventListener(
|
||||
'load',
|
||||
function () {
|
||||
callbackSoon(aWin);
|
||||
},
|
||||
{ capture: true, once: true }
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// This assumes that tests will at least have some state/entries
|
||||
function waitForBrowserState(aState, aSetStateCallback) {
|
||||
return SessionStoreTestUtils.waitForBrowserState(aState, aSetStateCallback);
|
||||
}
|
||||
|
||||
function promiseBrowserState(aState) {
|
||||
return SessionStoreTestUtils.promiseBrowserState(aState);
|
||||
}
|
||||
|
||||
function promiseTabState(tab, state) {
|
||||
if (typeof state != 'string') {
|
||||
state = JSON.stringify(state);
|
||||
}
|
||||
|
||||
let promise = promiseTabRestored(tab);
|
||||
ss.setTabState(tab, state);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function promiseWindowRestoring(win) {
|
||||
return new Promise((resolve) => win.addEventListener('SSWindowRestoring', resolve, { once: true }));
|
||||
}
|
||||
|
||||
function promiseWindowRestored(win) {
|
||||
return new Promise((resolve) => win.addEventListener('SSWindowRestored', resolve, { once: true }));
|
||||
}
|
||||
|
||||
async function setBrowserState(state, win = window) {
|
||||
ss.setBrowserState(typeof state != 'string' ? JSON.stringify(state) : state);
|
||||
await promiseWindowRestored(win);
|
||||
}
|
||||
|
||||
async function setWindowState(win, state, overwrite = false) {
|
||||
ss.setWindowState(win, typeof state != 'string' ? JSON.stringify(state) : state, overwrite);
|
||||
await promiseWindowRestored(win);
|
||||
}
|
||||
|
||||
function waitForTopic(aTopic, aTimeout, aCallback) {
|
||||
let observing = false;
|
||||
function removeObserver() {
|
||||
if (!observing) {
|
||||
return;
|
||||
}
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
observing = false;
|
||||
}
|
||||
|
||||
let timeout = setTimeout(function () {
|
||||
removeObserver();
|
||||
aCallback(false);
|
||||
}, aTimeout);
|
||||
|
||||
function observer() {
|
||||
removeObserver();
|
||||
timeout = clearTimeout(timeout);
|
||||
executeSoon(() => aCallback(true));
|
||||
}
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
removeObserver();
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
});
|
||||
|
||||
observing = true;
|
||||
Services.obs.addObserver(observer, aTopic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until session restore has finished collecting its data and is
|
||||
* has written that data ("sessionstore-state-write-complete").
|
||||
*
|
||||
* @param {function} aCallback If sessionstore-state-write-complete is sent
|
||||
* within buffering interval + 100 ms, the callback is passed |true|,
|
||||
* otherwise, it is passed |false|.
|
||||
*/
|
||||
function waitForSaveState(aCallback) {
|
||||
let timeout = 100 + Services.prefs.getIntPref('browser.sessionstore.interval');
|
||||
return waitForTopic('sessionstore-state-write-complete', timeout, aCallback);
|
||||
}
|
||||
function promiseSaveState() {
|
||||
return new Promise((resolve, reject) => {
|
||||
waitForSaveState((isSuccessful) => {
|
||||
if (!isSuccessful) {
|
||||
reject(new Error('Save state timeout'));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function forceSaveState() {
|
||||
return SessionSaver.run();
|
||||
}
|
||||
|
||||
function promiseRecoveryFileContents() {
|
||||
let promise = forceSaveState();
|
||||
return promise.then(function () {
|
||||
return IOUtils.readUTF8(SessionFile.Paths.recovery, {
|
||||
decompress: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var promiseForEachSessionRestoreFile = async function (cb) {
|
||||
for (let key of SessionFile.Paths.loadOrder) {
|
||||
let data = '';
|
||||
try {
|
||||
data = await IOUtils.readUTF8(SessionFile.Paths[key], {
|
||||
decompress: true,
|
||||
});
|
||||
} catch (ex) {
|
||||
// Ignore missing files
|
||||
if (!(DOMException.isInstance(ex) && ex.name == 'NotFoundError')) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
cb(data, key);
|
||||
}
|
||||
};
|
||||
|
||||
function promiseBrowserLoaded(aBrowser, ignoreSubFrames = true, wantLoad = null) {
|
||||
return BrowserTestUtils.browserLoaded(aBrowser, !ignoreSubFrames, wantLoad);
|
||||
}
|
||||
|
||||
function whenWindowLoaded(aWindow, aCallback) {
|
||||
aWindow.addEventListener(
|
||||
'load',
|
||||
function () {
|
||||
executeSoon(function executeWhenWindowLoaded() {
|
||||
aCallback(aWindow);
|
||||
});
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
function promiseWindowLoaded(aWindow) {
|
||||
return new Promise((resolve) => whenWindowLoaded(aWindow, resolve));
|
||||
}
|
||||
|
||||
var gUniqueCounter = 0;
|
||||
function r() {
|
||||
return Date.now() + '-' + ++gUniqueCounter;
|
||||
}
|
||||
|
||||
function* BrowserWindowIterator() {
|
||||
for (let currentWindow of Services.wm.getEnumerator('navigator:browser')) {
|
||||
if (!currentWindow.closed) {
|
||||
yield currentWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var gWebProgressListener = {
|
||||
_callback: null,
|
||||
|
||||
setCallback(aCallback) {
|
||||
if (!this._callback) {
|
||||
window.gBrowser.addTabsProgressListener(this);
|
||||
}
|
||||
this._callback = aCallback;
|
||||
},
|
||||
|
||||
unsetCallback() {
|
||||
if (this._callback) {
|
||||
this._callback = null;
|
||||
window.gBrowser.removeTabsProgressListener(this);
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, _aStatus) {
|
||||
if (
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW
|
||||
) {
|
||||
this._callback(aBrowser);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
gWebProgressListener.unsetCallback();
|
||||
});
|
||||
|
||||
var gProgressListener = {
|
||||
_callback: null,
|
||||
|
||||
setCallback(callback) {
|
||||
Services.obs.addObserver(this, 'sessionstore-debug-tab-restored');
|
||||
this._callback = callback;
|
||||
},
|
||||
|
||||
unsetCallback() {
|
||||
if (this._callback) {
|
||||
this._callback = null;
|
||||
Services.obs.removeObserver(this, 'sessionstore-debug-tab-restored');
|
||||
}
|
||||
},
|
||||
|
||||
observe(browser) {
|
||||
gProgressListener.onRestored(browser);
|
||||
},
|
||||
|
||||
onRestored(browser) {
|
||||
if (ss.getInternalObjectState(browser) == TAB_STATE_RESTORING) {
|
||||
let args = [browser].concat(gProgressListener._countTabs());
|
||||
gProgressListener._callback.apply(gProgressListener, args);
|
||||
}
|
||||
},
|
||||
|
||||
_countTabs() {
|
||||
let needsRestore = 0,
|
||||
isRestoring = 0,
|
||||
wasRestored = 0;
|
||||
|
||||
for (let win of BrowserWindowIterator()) {
|
||||
for (let i = 0; i < win.gBrowser.tabs.length; i++) {
|
||||
let browser = win.gBrowser.tabs[i].linkedBrowser;
|
||||
let state = ss.getInternalObjectState(browser);
|
||||
if (browser.isConnected && !state) {
|
||||
wasRestored++;
|
||||
} else if (state == TAB_STATE_RESTORING) {
|
||||
isRestoring++;
|
||||
} else if (state == TAB_STATE_NEEDS_RESTORE || !browser.isConnected) {
|
||||
needsRestore++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [needsRestore, isRestoring, wasRestored];
|
||||
},
|
||||
};
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
gProgressListener.unsetCallback();
|
||||
});
|
||||
|
||||
// Close all but our primary window.
|
||||
function promiseAllButPrimaryWindowClosed() {
|
||||
let windows = [];
|
||||
for (let win of BrowserWindowIterator()) {
|
||||
if (win != window) {
|
||||
windows.push(win);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(windows.map(BrowserTestUtils.closeWindow));
|
||||
}
|
||||
|
||||
// Forget all closed windows.
|
||||
function forgetClosedWindows() {
|
||||
while (ss.getClosedWindowCount() > 0) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Forget all closed tabs for a window
|
||||
function forgetClosedTabs(win) {
|
||||
const closedTabCount = ss.getClosedTabCountForWindow(win);
|
||||
for (let i = 0; i < closedTabCount; i++) {
|
||||
try {
|
||||
ss.forgetClosedTab(win, 0);
|
||||
} catch (err) {
|
||||
// This will fail if there are tab groups in here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function forgetSavedTabGroups() {
|
||||
const tabGroups = ss.getSavedTabGroups();
|
||||
tabGroups.forEach((tabGroup) => ss.forgetSavedTabGroup(tabGroup.id));
|
||||
}
|
||||
|
||||
function forgetClosedTabGroups(win) {
|
||||
const tabGroups = ss.getClosedTabGroups(win);
|
||||
tabGroups.forEach((tabGroup) => ss.forgetClosedTabGroup(win, tabGroup.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* When opening a new window it is not sufficient to wait for its load event.
|
||||
* We need to use whenDelayedStartupFinshed() here as the browser window's
|
||||
* delayedStartup() routine is executed one tick after the window's load event
|
||||
* has been dispatched. browser-delayed-startup-finished might be deferred even
|
||||
* further if parts of the window's initialization process take more time than
|
||||
* expected (e.g. reading a big session state from disk).
|
||||
*/
|
||||
function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
let features = '';
|
||||
let url = 'about:blank';
|
||||
|
||||
if ((aOptions && aOptions.private) || false) {
|
||||
features = ',private';
|
||||
url = 'about:privatebrowsing';
|
||||
}
|
||||
|
||||
let win = openDialog(AppConstants.BROWSER_CHROME_URL, '', 'chrome,all,dialog=no' + features, url);
|
||||
let delayedStartup = promiseDelayedStartupFinished(win);
|
||||
|
||||
let browserLoaded = new Promise((resolve) => {
|
||||
if (url == 'about:blank') {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
win.addEventListener(
|
||||
'load',
|
||||
function () {
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
promiseBrowserLoaded(browser).then(resolve);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
});
|
||||
|
||||
Promise.all([delayedStartup, browserLoaded]).then(() => aCallback(win));
|
||||
}
|
||||
function promiseNewWindowLoaded(aOptions) {
|
||||
return new Promise((resolve) => whenNewWindowLoaded(aOptions, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* This waits for the browser-delayed-startup-finished notification of a given
|
||||
* window. It indicates that the windows has loaded completely and is ready to
|
||||
* be used for testing.
|
||||
*/
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
}, 'browser-delayed-startup-finished');
|
||||
}
|
||||
function promiseDelayedStartupFinished(aWindow) {
|
||||
return new Promise((resolve) => whenDelayedStartupFinished(aWindow, resolve));
|
||||
}
|
||||
|
||||
function promiseTabRestored(tab) {
|
||||
return BrowserTestUtils.waitForEvent(tab, 'SSTabRestored');
|
||||
}
|
||||
|
||||
function promiseTabRestoring(tab) {
|
||||
return BrowserTestUtils.waitForEvent(tab, 'SSTabRestoring');
|
||||
}
|
||||
|
||||
// Removes the given tab immediately and returns a promise that resolves when
|
||||
// all pending status updates (messages) of the closing tab have been received.
|
||||
function promiseRemoveTabAndSessionState(tab) {
|
||||
let sessionUpdatePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
return sessionUpdatePromise;
|
||||
}
|
||||
|
||||
// Write DOMSessionStorage data to the given browser.
|
||||
function modifySessionStorage(browser, storageData, storageOptions = {}) {
|
||||
let browsingContext = browser.browsingContext;
|
||||
if (storageOptions && 'frameIndex' in storageOptions) {
|
||||
browsingContext = browsingContext.children[storageOptions.frameIndex];
|
||||
}
|
||||
|
||||
return SpecialPowers.spawn(browsingContext, [[storageData, storageOptions]], async function ([data]) {
|
||||
let frame = content;
|
||||
let keys = new Set(Object.keys(data));
|
||||
let isClearing = !keys.size;
|
||||
let storage = frame.sessionStorage;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
docShell.chromeEventHandler.addEventListener(
|
||||
'MozSessionStorageChanged',
|
||||
function onStorageChanged(event) {
|
||||
if (event.storageArea == storage) {
|
||||
keys.delete(event.key);
|
||||
}
|
||||
|
||||
if (keys.size == 0) {
|
||||
docShell.chromeEventHandler.removeEventListener('MozSessionStorageChanged', onStorageChanged, true);
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
if (isClearing) {
|
||||
storage.clear();
|
||||
} else {
|
||||
for (let key of keys) {
|
||||
frame.sessionStorage[key] = data[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pushPrefs(...aPrefs) {
|
||||
return SpecialPowers.pushPrefEnv({ set: aPrefs });
|
||||
}
|
||||
|
||||
function popPrefs() {
|
||||
return SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
function setScrollPosition(bc, x, y) {
|
||||
return SpecialPowers.spawn(bc, [x, y], (childX, childY) => {
|
||||
return new Promise((resolve) => {
|
||||
content.addEventListener(
|
||||
'mozvisualscroll',
|
||||
function onScroll(event) {
|
||||
if (content.document.ownerGlobal.visualViewport == event.target) {
|
||||
content.removeEventListener('mozvisualscroll', onScroll, {
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
{ mozSystemGroup: true }
|
||||
);
|
||||
content.scrollTo(childX, childY);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function checkScroll(tab, expected, msg) {
|
||||
let browser = tab.linkedBrowser;
|
||||
await TabStateFlusher.flush(browser);
|
||||
|
||||
let scroll = JSON.parse(ss.getTabState(tab)).scroll || null;
|
||||
is(JSON.stringify(scroll), JSON.stringify(expected), msg);
|
||||
}
|
||||
|
||||
function whenDomWindowClosedHandled(aCallback) {
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
aCallback();
|
||||
}, 'sessionstore-debug-domwindowclosed-handled');
|
||||
}
|
||||
|
||||
function getPropertyOfFormField(browserContext, selector, propName) {
|
||||
return SpecialPowers.spawn(browserContext, [selector, propName], (selectorChild, propNameChild) => {
|
||||
return content.document.querySelector(selectorChild)[propNameChild];
|
||||
});
|
||||
}
|
||||
|
||||
function setPropertyOfFormField(browserContext, selector, propName, newValue) {
|
||||
return SpecialPowers.spawn(browserContext, [selector, propName, newValue], (selectorChild, propNameChild, newValueChild) => {
|
||||
let node = content.document.querySelector(selectorChild);
|
||||
node[propNameChild] = newValueChild;
|
||||
|
||||
let event = node.ownerDocument.createEvent('UIEvents');
|
||||
event.initUIEvent('input', true, true, node.ownerGlobal, 0);
|
||||
node.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseOnHistoryReplaceEntry(browser) {
|
||||
return new Promise((resolve) => {
|
||||
let sessionHistory = browser.browsingContext?.sessionHistory;
|
||||
if (sessionHistory) {
|
||||
var historyListener = {
|
||||
OnHistoryNewEntry() {},
|
||||
OnHistoryGotoIndex() {},
|
||||
OnHistoryPurge() {},
|
||||
OnHistoryReload() {
|
||||
return true;
|
||||
},
|
||||
|
||||
OnHistoryReplaceEntry() {
|
||||
resolve();
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI(['nsISHistoryListener', 'nsISupportsWeakReference']),
|
||||
};
|
||||
|
||||
sessionHistory.addSHistoryListener(historyListener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadTestSubscript(filePath) {
|
||||
Services.scriptloader.loadSubScript(new URL(filePath, gTestPath).href, this);
|
||||
}
|
||||
|
||||
function addCoopTask(aFile, aTest, aUrlRoot) {
|
||||
async function taskToBeAdded() {
|
||||
info(`File ${aFile} has COOP headers enabled`);
|
||||
let filePath = `browser/browser/components/sessionstore/test/${aFile}`;
|
||||
let url = aUrlRoot + `coopHeaderCommon.sjs?fileRoot=${filePath}`;
|
||||
await aTest(url);
|
||||
}
|
||||
Object.defineProperty(taskToBeAdded, 'name', { value: aTest.name });
|
||||
add_task(taskToBeAdded);
|
||||
}
|
||||
|
||||
function addNonCoopTask(aFile, aTest, aUrlRoot) {
|
||||
async function taskToBeAdded() {
|
||||
await aTest(aUrlRoot + aFile);
|
||||
}
|
||||
Object.defineProperty(taskToBeAdded, 'name', { value: aTest.name });
|
||||
add_task(taskToBeAdded);
|
||||
}
|
||||
|
||||
function openAndCloseTab(window, url) {
|
||||
return SessionStoreTestUtils.openAndCloseTab(window, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is regrettable, but when `promiseBrowserState` resolves, we're still
|
||||
* midway through loading the tabs. To avoid race conditions in URLs for tabs
|
||||
* being available, wait for all the loads to finish:
|
||||
*/
|
||||
function promiseSessionStoreLoads(numberOfLoads) {
|
||||
let loadsSeen = 0;
|
||||
return new Promise((resolve) => {
|
||||
Services.obs.addObserver(function obs(browser) {
|
||||
loadsSeen++;
|
||||
if (loadsSeen == numberOfLoads) {
|
||||
resolve();
|
||||
}
|
||||
// The typeof check is here to avoid one test messing with everything else by
|
||||
// keeping the observer indefinitely.
|
||||
if (typeof info == 'undefined' || loadsSeen >= numberOfLoads) {
|
||||
Services.obs.removeObserver(obs, 'sessionstore-debug-tab-restored');
|
||||
}
|
||||
info('Saw load for ' + browser.currentURI.spec);
|
||||
}, 'sessionstore-debug-tab-restored');
|
||||
});
|
||||
}
|
||||
|
||||
function triggerClickOn(target, options) {
|
||||
let promise = BrowserTestUtils.waitForEvent(target, 'click');
|
||||
if (AppConstants.platform == 'macosx') {
|
||||
options.metaKey = options.ctrlKey;
|
||||
delete options.ctrlKey;
|
||||
}
|
||||
EventUtils.synthesizeMouseAtCenter(target, options);
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function openTabMenuFor(tab) {
|
||||
let tabMenu = tab.ownerDocument.getElementById('tabContextMenu');
|
||||
|
||||
let tabMenuShown = BrowserTestUtils.waitForEvent(tabMenu, 'popupshown');
|
||||
EventUtils.synthesizeMouseAtCenter(tab, { type: 'contextmenu' }, tab.ownerGlobal);
|
||||
await tabMenuShown;
|
||||
|
||||
return tabMenu;
|
||||
}
|
|
@ -815,7 +815,6 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
this._removedByStartupPage = true;
|
||||
gBrowser.removeTab(this._initialTab, {
|
||||
skipSessionStore: true,
|
||||
animate: false,
|
||||
});
|
||||
} else {
|
||||
this.moveTabToWorkspace(this._initialTab, this.activeWorkspace);
|
||||
|
@ -826,14 +825,19 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
}
|
||||
if (this._tabToRemoveForEmpty) {
|
||||
const tabs = gBrowser.tabs.filter((tab) => !tab.collapsed && !tab.hasAttribute('zen-empty-tab'));
|
||||
if (typeof this._tabToSelect === 'number' && this._tabToSelect >= 0 && tabs[this._tabToSelect]) {
|
||||
if (
|
||||
typeof this._tabToSelect === 'number' &&
|
||||
this._tabToSelect >= 0 &&
|
||||
tabs[this._tabToSelect] &&
|
||||
this._shouldShowTab(tabs[this._tabToSelect]) &&
|
||||
tabs[this._tabToSelect] !== this._tabToRemoveForEmpty
|
||||
) {
|
||||
setTimeout(() => {
|
||||
this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`);
|
||||
gBrowser.selectedTab = tabs[this._tabToSelect];
|
||||
this._removedByStartupPage = true;
|
||||
gBrowser.removeTab(this._tabToRemoveForEmpty, {
|
||||
skipSessionStore: true,
|
||||
animate: false,
|
||||
});
|
||||
cleanup();
|
||||
}, 0);
|
||||
|
@ -843,7 +847,6 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
this._removedByStartupPage = true;
|
||||
gBrowser.removeTab(this._tabToRemoveForEmpty, {
|
||||
skipSessionStore: true,
|
||||
animate: false,
|
||||
});
|
||||
cleanup();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue