diff options
Diffstat (limited to 'dom/ipc/tests/browser_crash_oopiframe.js')
-rw-r--r-- | dom/ipc/tests/browser_crash_oopiframe.js | 235 |
1 files changed, 235 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..3066d7bf88 --- /dev/null +++ b/dom/ipc/tests/browser_crash_oopiframe.js @@ -0,0 +1,235 @@ +"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); + } +}); |