forked from mirrors/gecko-dev
239 lines
8.6 KiB
JavaScript
239 lines
8.6 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* These tests the behaviour of the browser when background tabs crash,
|
|
* while the foreground tab remains.
|
|
*
|
|
* The current behavioural rule is this: if only background tabs crash,
|
|
* then only the first tab shown of that group should show the tab crash
|
|
* page, and subsequent ones should restore on demand.
|
|
*/
|
|
|
|
/**
|
|
* Makes the current browser tab non-remote, and then sets up two remote
|
|
* background tabs, ensuring that both belong to the same content process.
|
|
* Callers should pass in a testing function that will execute (and possibly
|
|
* yield Promises) taking the created background tabs as arguments. Once
|
|
* the testing function completes, this function will take care of closing
|
|
* the opened tabs.
|
|
*
|
|
* @param testFn (function)
|
|
* A Promise-generating function that will be called once the tabs
|
|
* are opened and ready.
|
|
* @return Promise
|
|
* Resolves once the testing function completes and the opened tabs
|
|
* have been completely closed.
|
|
*/
|
|
async function setupBackgroundTabs(testFn) {
|
|
const REMOTE_PAGE = "http://www.example.com";
|
|
const NON_REMOTE_PAGE = "about:robots";
|
|
|
|
// Browse the initial tab to a non-remote page, which we'll have in the
|
|
// foreground.
|
|
let initialTab = gBrowser.selectedTab;
|
|
let initialBrowser = initialTab.linkedBrowser;
|
|
initialBrowser.loadURI(NON_REMOTE_PAGE);
|
|
await BrowserTestUtils.browserLoaded(initialBrowser);
|
|
|
|
// Open some tabs that should be running in the content process.
|
|
let tab1 =
|
|
await BrowserTestUtils.openNewForegroundTab(gBrowser, REMOTE_PAGE);
|
|
let remoteBrowser1 = tab1.linkedBrowser;
|
|
await TabStateFlusher.flush(remoteBrowser1);
|
|
|
|
let tab2 =
|
|
await BrowserTestUtils.openNewForegroundTab(gBrowser, REMOTE_PAGE);
|
|
let remoteBrowser2 = tab2.linkedBrowser;
|
|
await TabStateFlusher.flush(remoteBrowser2);
|
|
|
|
// Quick sanity check - the two browsers should be remote and share the
|
|
// same childID, or else this test is not going to work.
|
|
Assert.ok(remoteBrowser1.isRemoteBrowser,
|
|
"Browser should be remote in order to crash.");
|
|
Assert.ok(remoteBrowser2.isRemoteBrowser,
|
|
"Browser should be remote in order to crash.");
|
|
Assert.equal(remoteBrowser1.frameLoader.childID,
|
|
remoteBrowser2.frameLoader.childID,
|
|
"Both remote browsers should share the same content process.");
|
|
|
|
// Now switch back to the non-remote browser...
|
|
await BrowserTestUtils.switchTab(gBrowser, initialTab);
|
|
|
|
await testFn([tab1, tab2]);
|
|
|
|
BrowserTestUtils.removeTab(tab1);
|
|
BrowserTestUtils.removeTab(tab2);
|
|
}
|
|
|
|
/**
|
|
* Takes some set of background tabs that are assumed to all belong to
|
|
* the same content process, and crashes them.
|
|
*
|
|
* @param tabs (Array(<xul:tab>))
|
|
* The tabs to crash.
|
|
* @return Promise
|
|
* Resolves once the tabs have crashed and entered the pending
|
|
* background state.
|
|
*/
|
|
async function crashBackgroundTabs(tabs) {
|
|
Assert.ok(tabs.length > 0, "Need to crash at least one tab.");
|
|
for (let tab of tabs) {
|
|
Assert.ok(tab.linkedBrowser.isRemoteBrowser, "tab is remote");
|
|
}
|
|
|
|
let remotenessChangePromises = tabs.map((t) => {
|
|
return BrowserTestUtils.waitForEvent(t, "TabRemotenessChange");
|
|
});
|
|
|
|
let tabsRevived = tabs.map((t) => {
|
|
return promiseTabRestoring(t);
|
|
});
|
|
|
|
await BrowserTestUtils.crashBrowser(tabs[0].linkedBrowser, false);
|
|
await Promise.all(remotenessChangePromises);
|
|
await Promise.all(tabsRevived);
|
|
|
|
// Both background tabs should now be in the pending restore
|
|
// state.
|
|
for (let tab of tabs) {
|
|
Assert.ok(!tab.linkedBrowser.isRemoteBrowser, "tab is not remote");
|
|
Assert.ok(!tab.linkedBrowser.hasAttribute("crashed"), "tab is not crashed");
|
|
Assert.ok(tab.hasAttribute("pending"), "tab is pending");
|
|
}
|
|
}
|
|
|
|
add_task(async function setup() {
|
|
// We'll simplify by making sure we only ever one content process for this
|
|
// test.
|
|
await SpecialPowers.pushPrefEnv({ set: [[ "dom.ipc.processCount", 1 ]] });
|
|
|
|
// On debug builds, crashing tabs results in much thinking, which
|
|
// slows down the test and results in intermittent test timeouts,
|
|
// so we'll pump up the expected timeout for this test.
|
|
requestLongerTimeout(5);
|
|
});
|
|
|
|
/**
|
|
* Tests that if a content process crashes taking down only
|
|
* background tabs, then the first of those tabs that the user
|
|
* selects will show the tab crash page, but the rest will restore
|
|
* on demand.
|
|
*/
|
|
add_task(async function test_background_crash_simple() {
|
|
await setupBackgroundTabs(async function([tab1, tab2]) {
|
|
// Let's crash one of those background tabs now...
|
|
await crashBackgroundTabs([tab1, tab2]);
|
|
|
|
// Selecting the first tab should now send it to the tab crashed page.
|
|
let tabCrashedPagePromise =
|
|
BrowserTestUtils.waitForContentEvent(tab1.linkedBrowser,
|
|
"AboutTabCrashedReady",
|
|
false, null, true);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab1);
|
|
await tabCrashedPagePromise;
|
|
|
|
// Selecting the second tab should restore it.
|
|
let tabRestored = promiseTabRestored(tab2);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab2);
|
|
await tabRestored;
|
|
});
|
|
});
|
|
|
|
/**
|
|
* Tests that if a content process crashes taking down only
|
|
* background tabs, and the user is configured to send backlogged
|
|
* crash reports automatically, that the tab crashed page is not
|
|
* shown.
|
|
*/
|
|
add_task(async function test_background_crash_autosubmit_backlogged() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["browser.crashReports.unsubmittedCheck.autoSubmit2", true]],
|
|
});
|
|
|
|
await setupBackgroundTabs(async function([tab1, tab2]) {
|
|
// Let's crash one of those background tabs now...
|
|
await crashBackgroundTabs([tab1, tab2]);
|
|
|
|
// Selecting the first tab should restore it.
|
|
let tabRestored = promiseTabRestored(tab1);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab1);
|
|
await tabRestored;
|
|
|
|
// Selecting the second tab should restore it.
|
|
tabRestored = promiseTabRestored(tab2);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab2);
|
|
await tabRestored;
|
|
});
|
|
|
|
await SpecialPowers.popPrefEnv();
|
|
});
|
|
|
|
/**
|
|
* Tests that if there are two background tab crashes in a row, that
|
|
* the two sets of background crashes don't interfere with one another.
|
|
*
|
|
* Specifically, if we start with two background tabs (1, 2) which crash,
|
|
* and we visit 1, 1 should go to the tab crashed page. If we then have
|
|
* two new background tabs (3, 4) crash, visiting 2 should still restore.
|
|
* Visiting 4 should show us the tab crashed page, and then visiting 3
|
|
* should restore.
|
|
*/
|
|
add_task(async function test_background_crash_multiple() {
|
|
let initialTab = gBrowser.selectedTab;
|
|
|
|
await setupBackgroundTabs(async function([tab1, tab2]) {
|
|
// Let's crash one of those background tabs now...
|
|
await crashBackgroundTabs([tab1, tab2]);
|
|
|
|
// Selecting the first tab should now send it to the tab crashed page.
|
|
let tabCrashedPagePromise =
|
|
BrowserTestUtils.waitForContentEvent(tab1.linkedBrowser,
|
|
"AboutTabCrashedReady",
|
|
false, null, true);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab1);
|
|
await tabCrashedPagePromise;
|
|
|
|
// Now switch back to the original non-remote tab...
|
|
await BrowserTestUtils.switchTab(gBrowser, initialTab);
|
|
|
|
await setupBackgroundTabs(async function([tab3, tab4]) {
|
|
await crashBackgroundTabs([tab3, tab4]);
|
|
|
|
// Selecting the second tab should restore it.
|
|
let tabRestored = promiseTabRestored(tab2);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab2);
|
|
await tabRestored;
|
|
|
|
// Selecting the fourth tab should now send it to the tab crashed page.
|
|
tabCrashedPagePromise =
|
|
BrowserTestUtils.waitForContentEvent(tab4.linkedBrowser,
|
|
"AboutTabCrashedReady",
|
|
false, null, true);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab4);
|
|
await tabCrashedPagePromise;
|
|
|
|
// Selecting the third tab should restore it.
|
|
tabRestored = promiseTabRestored(tab3);
|
|
await BrowserTestUtils.switchTab(gBrowser, tab3);
|
|
await tabRestored;
|
|
});
|
|
});
|
|
});
|
|
|
|
// Tests that crashed preloaded tabs are removed and no unexpected errors are
|
|
// thrown.
|
|
add_task(async function test_preload_crash() {
|
|
if (!Services.prefs.getBoolPref("browser.newtab.preload")) {
|
|
return;
|
|
}
|
|
|
|
// Release any existing preloaded browser
|
|
gBrowser.removePreloadedBrowser();
|
|
|
|
// Create a fresh preloaded browser
|
|
gBrowser._createPreloadBrowser();
|
|
|
|
await BrowserTestUtils.crashBrowser(gBrowser._preloadedBrowser, false);
|
|
|
|
Assert.ok(!gBrowser._preloadedBrowser);
|
|
});
|