summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/browser_background_tab_crash.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/sessionstore/test/browser_background_tab_crash.js')
-rw-r--r--browser/components/sessionstore/test/browser_background_tab_crash.js221
1 files changed, 221 insertions, 0 deletions
diff --git a/browser/components/sessionstore/test/browser_background_tab_crash.js b/browser/components/sessionstore/test/browser_background_tab_crash.js
new file mode 100644
index 000000000..e804b177e
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_background_tab_crash.js
@@ -0,0 +1,221 @@
+"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.
+ */
+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);
+ yield BrowserTestUtils.browserLoaded(initialBrowser);
+
+ // Open some tabs that should be running in the content process.
+ let tab1 =
+ yield BrowserTestUtils.openNewForegroundTab(gBrowser, REMOTE_PAGE);
+ let remoteBrowser1 = tab1.linkedBrowser;
+ yield TabStateFlusher.flush(remoteBrowser1);
+
+ let tab2 =
+ yield BrowserTestUtils.openNewForegroundTab(gBrowser, REMOTE_PAGE);
+ let remoteBrowser2 = tab2.linkedBrowser;
+ yield 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...
+ yield BrowserTestUtils.switchTab(gBrowser, initialTab);
+
+ yield testFn([tab1, tab2]);
+
+ yield BrowserTestUtils.removeTab(tab1);
+ yield 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.
+ */
+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);
+ });
+
+ yield BrowserTestUtils.crashBrowser(tabs[0].linkedBrowser, false);
+ yield Promise.all(remotenessChangePromises);
+ yield 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.linkedBrowser.hasAttribute("pending"), "tab is pending");
+ }
+}
+
+add_task(function* setup() {
+ // We'll simplify by making sure we only ever one content process for this
+ // test.
+ yield 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(function* test_background_crash_simple() {
+ yield setupBackgroundTabs(function*([tab1, tab2]) {
+ // Let's crash one of those background tabs now...
+ yield 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);
+ yield BrowserTestUtils.switchTab(gBrowser, tab1);
+ yield tabCrashedPagePromise;
+
+ // Selecting the second tab should restore it.
+ let tabRestored = promiseTabRestored(tab2);
+ yield BrowserTestUtils.switchTab(gBrowser, tab2);
+ yield 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(function* test_background_crash_autosubmit_backlogged() {
+ yield SpecialPowers.pushPrefEnv({
+ set: [["browser.crashReports.unsubmittedCheck.autoSubmit2", true]],
+ });
+
+ yield setupBackgroundTabs(function*([tab1, tab2]) {
+ // Let's crash one of those background tabs now...
+ yield crashBackgroundTabs([tab1, tab2]);
+
+ // Selecting the first tab should restore it.
+ let tabRestored = promiseTabRestored(tab1);
+ yield BrowserTestUtils.switchTab(gBrowser, tab1);
+ yield tabRestored;
+
+ // Selecting the second tab should restore it.
+ tabRestored = promiseTabRestored(tab2);
+ yield BrowserTestUtils.switchTab(gBrowser, tab2);
+ yield tabRestored;
+ });
+
+ yield 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(function* test_background_crash_multiple() {
+ let initialTab = gBrowser.selectedTab;
+
+ yield setupBackgroundTabs(function*([tab1, tab2]) {
+ // Let's crash one of those background tabs now...
+ yield 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);
+ yield BrowserTestUtils.switchTab(gBrowser, tab1);
+ yield tabCrashedPagePromise;
+
+ // Now switch back to the original non-remote tab...
+ yield BrowserTestUtils.switchTab(gBrowser, initialTab);
+
+ yield setupBackgroundTabs(function*([tab3, tab4]) {
+ yield crashBackgroundTabs([tab3, tab4]);
+
+ // Selecting the second tab should restore it.
+ let tabRestored = promiseTabRestored(tab2);
+ yield BrowserTestUtils.switchTab(gBrowser, tab2);
+ yield tabRestored;
+
+ // Selecting the fourth tab should now send it to the tab crashed page.
+ let tabCrashedPagePromise =
+ BrowserTestUtils.waitForContentEvent(tab4.linkedBrowser,
+ "AboutTabCrashedReady",
+ false, null, true);
+ yield BrowserTestUtils.switchTab(gBrowser, tab4);
+ yield tabCrashedPagePromise;
+
+ // Selecting the third tab should restore it.
+ tabRestored = promiseTabRestored(tab3);
+ yield BrowserTestUtils.switchTab(gBrowser, tab3);
+ yield tabRestored;
+ });
+ });
+});