summaryrefslogtreecommitdiffstats
path: root/dom/ipc/tests/browser_crash_oopiframe.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc/tests/browser_crash_oopiframe.js')
-rw-r--r--dom/ipc/tests/browser_crash_oopiframe.js236
1 files changed, 236 insertions, 0 deletions
diff --git a/dom/ipc/tests/browser_crash_oopiframe.js b/dom/ipc/tests/browser_crash_oopiframe.js
new file mode 100644
index 0000000000..d37320a4b6
--- /dev/null
+++ b/dom/ipc/tests/browser_crash_oopiframe.js
@@ -0,0 +1,236 @@
+"use strict";
+
+/**
+ * Opens a number of tabs containing an out-of-process iframe.
+ *
+ * @param numTabs the number of tabs to open.
+ * @returns the browsing context of the iframe in the last tab opened.
+ */
+async function openTestTabs(numTabs) {
+ let iframeBC = null;
+
+ for (let count = 0; count < numTabs; count++) {
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: "about:blank",
+ });
+
+ // If we load example.com in an injected subframe, we assume that this
+ // will load in its own subprocess, which we can then crash.
+ iframeBC = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ let iframe = content.document.createElement("iframe");
+ iframe.setAttribute("src", "http://example.com");
+
+ content.document.body.appendChild(iframe);
+ await ContentTaskUtils.waitForEvent(iframe, "load");
+ return iframe.frameLoader.browsingContext;
+ });
+ }
+
+ return iframeBC;
+}
+
+/**
+ * Helper function for testing frame crashing. Some tabs are opened
+ * containing frames from example.com and then the process for
+ * example.com is crashed. Notifications should apply to each tab
+ * and all should close when one of the notifications is closed.
+ *
+ * @param numTabs the number of tabs to open.
+ */
+async function testFrameCrash(numTabs) {
+ let iframeBC = await openTestTabs(numTabs);
+ let browser = gBrowser.selectedBrowser;
+ let rootBC = browser.browsingContext;
+
+ is(iframeBC.parent, rootBC, "oop frame has root as parent");
+
+ let eventFiredPromise = BrowserTestUtils.waitForEvent(
+ browser,
+ "oop-browser-crashed"
+ );
+
+ BrowserTestUtils.crashFrame(
+ browser,
+ true /* shouldShowTabCrashPage */,
+ true /* shouldClearMinidumps */,
+ iframeBC
+ );
+
+ let notificationPromise = BrowserTestUtils.waitForNotificationBar(
+ gBrowser,
+ browser,
+ "subframe-crashed"
+ );
+
+ info("Waiting for oop-browser-crashed event.");
+ await eventFiredPromise.then(event => {
+ ok(!event.isTopFrame, "should not be reporting top-level frame crash");
+ ok(event.childID != 0, "childID is non-zero");
+
+ isnot(
+ event.browsingContextId,
+ rootBC,
+ "top frame browsing context id not expected."
+ );
+
+ is(
+ event.browsingContextId,
+ iframeBC.id,
+ "oop frame browsing context id expected."
+ );
+ });
+
+ if (numTabs == 1) {
+ // The BrowsingContext is re-used, but the window global might still be
+ // getting set up at this point, so wait until it's been initialized.
+ let {
+ subject: windowGlobal,
+ } = await BrowserUtils.promiseObserved("window-global-created", wgp =>
+ wgp.documentURI.spec.startsWith("about:framecrashed")
+ );
+
+ is(
+ windowGlobal,
+ iframeBC.currentWindowGlobal,
+ "Resolved on expected window global"
+ );
+
+ let newIframeURI = await SpecialPowers.spawn(iframeBC, [], async () => {
+ return content.document.documentURI;
+ });
+
+ ok(
+ newIframeURI.startsWith("about:framecrashed"),
+ "The iframe is now pointing at about:framecrashed"
+ );
+
+ let title = await SpecialPowers.spawn(iframeBC, [], async () => {
+ await content.document.l10n.ready;
+ return content.document.documentElement.getAttribute("title");
+ });
+ ok(title, "The iframe has a non-empty tooltip.");
+ }
+
+ // Next, check that the crash notification bar has appeared.
+ await notificationPromise;
+
+ for (let count = 1; count <= numTabs; count++) {
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.browsers[count]);
+ let notification = notificationBox.currentNotification;
+ ok(notification, "Notification " + count + " should be visible");
+ is(
+ notification.getAttribute("value"),
+ "subframe-crashed",
+ "Should be showing the right notification" + count
+ );
+
+ let buttons = notification.buttonContainer.querySelectorAll(
+ ".notification-button"
+ );
+ is(
+ buttons.length,
+ 1,
+ "Notification " + count + " should have only one button."
+ );
+ let links = notification.messageText.querySelectorAll(".text-link");
+ is(
+ links.length,
+ 1,
+ "Notification " + count + " should have only one link."
+ );
+ let msgs = notification.messageText.querySelectorAll(
+ "span[data-l10n-id='crashed-subframe-message']"
+ );
+ is(msgs.length, 1, "Notification " + count + " should have one crash msg.");
+ }
+
+ // Press the ignore button on the visible notification.
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+ let notification = notificationBox.currentNotification;
+
+ // Make sure all of the notifications were closed when one of them was closed.
+ let closedPromises = [];
+ for (let count = 1; count <= numTabs; count++) {
+ let nb = gBrowser.getNotificationBox(gBrowser.browsers[count]);
+ closedPromises.push(
+ BrowserTestUtils.waitForMutationCondition(
+ nb.stack,
+ { childList: true },
+ () => !nb.currentNotification
+ )
+ );
+ }
+
+ notification.dismiss();
+ await Promise.all(closedPromises);
+
+ for (let count = 1; count <= numTabs; count++) {
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+}
+
+/**
+ * In this test, we crash an out-of-process iframe and
+ * verify that :
+ * 1. the "oop-browser-crashed" event is dispatched with
+ * the browsing context of the crashed oop subframe.
+ * 2. the crashed subframe is now pointing at "about:framecrashed"
+ * page.
+ */
+add_task(async function test_crashframe() {
+ // Open a new window with fission enabled.
+ ok(
+ SpecialPowers.useRemoteSubframes,
+ "This test only makes sense of we can use OOP iframes."
+ );
+
+ // Create the crash reporting directory if it doesn't yet exist, otherwise, a failure
+ // sometimes occurs. See bug 1687855 for fixing this.
+ const uAppDataPath = Services.dirsvc.get("UAppData", Ci.nsIFile).path;
+ let path = PathUtils.join(uAppDataPath, "Crash Reports", "pending");
+ await IOUtils.makeDirectory(path, { ignoreExisting: true });
+
+ // Test both one tab and when four tabs are opened.
+ await testFrameCrash(1);
+ await testFrameCrash(4);
+});
+
+// This test checks that no notification shows when there is no minidump available. It
+// simulates the steps that occur during a crash, once with a dumpID and once without.
+add_task(async function test_nominidump() {
+ for (let dumpID of [null, "8888"]) {
+ let iframeBC = await openTestTabs(1);
+
+ let childID = iframeBC.currentWindowGlobal.domProcess.childID;
+
+ gBrowser.selectedBrowser.dispatchEvent(
+ new FrameCrashedEvent("oop-browser-crashed", {
+ browsingContextID: iframeBC,
+ childID,
+ isTopFrame: false,
+ bubbles: true,
+ })
+ );
+
+ let bag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag
+ );
+ bag.setProperty("abnormal", "true");
+ bag.setProperty("childID", iframeBC.currentWindowGlobal.domProcess.childID);
+ if (dumpID) {
+ bag.setProperty("dumpID", dumpID);
+ }
+
+ Services.obs.notifyObservers(bag, "ipc:content-shutdown");
+
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+ let notification = notificationBox.currentNotification;
+ ok(
+ dumpID ? notification : !notification,
+ "notification shown for browser with no minidump"
+ );
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+});