diff options
Diffstat (limited to 'browser/base/content/test/tabcrashed')
7 files changed, 639 insertions, 0 deletions
diff --git a/browser/base/content/test/tabcrashed/browser.ini b/browser/base/content/test/tabcrashed/browser.ini new file mode 100644 index 000000000..051b40d9f --- /dev/null +++ b/browser/base/content/test/tabcrashed/browser.ini @@ -0,0 +1,13 @@ +[DEFAULT] +support-files = + head.js +[browser_shown.js] +skip-if = !e10s || !crashreporter +[browser_clearEmail.js] +skip-if = !e10s || !crashreporter +[browser_showForm.js] +skip-if = !e10s || !crashreporter +[browser_withoutDump.js] +skip-if = !e10s +[browser_autoSubmitRequest.js] +skip-if = !e10s || !crashreporter diff --git a/browser/base/content/test/tabcrashed/browser_autoSubmitRequest.js b/browser/base/content/test/tabcrashed/browser_autoSubmitRequest.js new file mode 100644 index 000000000..778331814 --- /dev/null +++ b/browser/base/content/test/tabcrashed/browser_autoSubmitRequest.js @@ -0,0 +1,152 @@ +"use strict"; + +const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; +const AUTOSUBMIT_PREF = "browser.crashReports.unsubmittedCheck.autoSubmit2"; + +const {TabStateFlusher} = + Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {}); + +// 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(2); + +/** + * Tests that if the user is not configured to autosubmit + * backlogged crash reports, that we offer to do that, and + * that the user can accept that offer. + */ +add_task(function* test_show_form() { + yield SpecialPowers.pushPrefEnv({ + set: [[AUTOSUBMIT_PREF, false]], + }) + + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + // Make sure we've flushed the browser messages so that + // we can restore it. + yield TabStateFlusher.flush(browser); + + // Now crash the browser. + yield BrowserTestUtils.crashBrowser(browser); + + let doc = browser.contentDocument; + + // Ensure the request is visible. We can safely reach into + // the content since about:tabcrashed is an in-process URL. + let requestAutoSubmit = doc.getElementById("requestAutoSubmit"); + Assert.ok(!requestAutoSubmit.hidden, + "Request for autosubmission is visible."); + + // Since the pref is set to false, the checkbox should be + // unchecked. + let autoSubmit = doc.getElementById("autoSubmit"); + Assert.ok(!autoSubmit.checked, + "Checkbox for autosubmission is not checked.") + + // Check the checkbox, and then restore the tab. + autoSubmit.checked = true; + let restoreButton = doc.getElementById("restoreTab"); + restoreButton.click(); + + yield BrowserTestUtils.browserLoaded(browser, false, PAGE); + + // The autosubmission pref should now be set. + Assert.ok(Services.prefs.getBoolPref(AUTOSUBMIT_PREF), + "Autosubmission pref should have been set."); + }); +}); + +/** + * Tests that if the user is autosubmitting backlogged crash reports + * that we don't make the offer again. + */ +add_task(function* test_show_form() { + yield SpecialPowers.pushPrefEnv({ + set: [[AUTOSUBMIT_PREF, true]], + }) + + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + yield TabStateFlusher.flush(browser); + // Now crash the browser. + yield BrowserTestUtils.crashBrowser(browser); + + let doc = browser.contentDocument; + + // Ensure the request is NOT visible. We can safely reach into + // the content since about:tabcrashed is an in-process URL. + let requestAutoSubmit = doc.getElementById("requestAutoSubmit"); + Assert.ok(requestAutoSubmit.hidden, + "Request for autosubmission is not visible."); + + // Restore the tab. + let restoreButton = doc.getElementById("restoreTab"); + restoreButton.click(); + + yield BrowserTestUtils.browserLoaded(browser, false, PAGE); + + // The autosubmission pref should still be set to true. + Assert.ok(Services.prefs.getBoolPref(AUTOSUBMIT_PREF), + "Autosubmission pref should have been set."); + }); +}); + +/** + * Tests that we properly set the autoSubmit preference if the user is + * presented with a tabcrashed page without a crash report. + */ +add_task(function* test_no_offer() { + // We should default to sending the report. + Assert.ok(TabCrashHandler.prefs.getBoolPref("sendReport")); + + yield SpecialPowers.pushPrefEnv({ + set: [[AUTOSUBMIT_PREF, false]], + }); + + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + yield TabStateFlusher.flush(browser); + + // Make it so that it seems like no dump is available for the next crash. + prepareNoDump(); + + // Now crash the browser. + yield BrowserTestUtils.crashBrowser(browser); + + // eslint-disable-next-line mozilla/no-cpows-in-tests + let doc = browser.contentDocument; + + // Ensure the request to autosubmit is invisible, since there's no report. + let requestRect = doc.getElementById("requestAutoSubmit") + .getBoundingClientRect(); + Assert.equal(0, requestRect.height, + "Request for autosubmission has no height"); + Assert.equal(0, requestRect.width, + "Request for autosubmission has no width"); + + // Since the pref is set to false, the checkbox should be + // unchecked. + let autoSubmit = doc.getElementById("autoSubmit"); + Assert.ok(!autoSubmit.checked, + "Checkbox for autosubmission is not checked."); + + let restoreButton = doc.getElementById("restoreTab"); + restoreButton.click(); + + yield BrowserTestUtils.browserLoaded(browser, false, PAGE); + + // The autosubmission pref should now be set. + Assert.ok(!Services.prefs.getBoolPref(AUTOSUBMIT_PREF), + "Autosubmission pref should not have changed."); + }); + + // We should not have changed the default value for sending the report. + Assert.ok(TabCrashHandler.prefs.getBoolPref("sendReport")); +}); diff --git a/browser/base/content/test/tabcrashed/browser_clearEmail.js b/browser/base/content/test/tabcrashed/browser_clearEmail.js new file mode 100644 index 000000000..9ec04944f --- /dev/null +++ b/browser/base/content/test/tabcrashed/browser_clearEmail.js @@ -0,0 +1,85 @@ +"use strict"; + +const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; +const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; +const EMAIL = "foo@privacy.com"; + +/** + * Sets up the browser to send crash reports to the local crash report + * testing server. + */ +add_task(function* setup() { + // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash + // reports. This test needs them enabled. The test also needs a mock + // report server, and fortunately one is already set up by toolkit/ + // crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL, + // which CrashSubmit.jsm uses as a server override. + let env = Cc["@mozilla.org/process/environment;1"] + .getService(Components.interfaces.nsIEnvironment); + let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT"); + let serverUrl = env.get("MOZ_CRASHREPORTER_URL"); + env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); + env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); + + // By default, requesting the email address of the user is disabled. + // For the purposes of this test, we turn it back on. + yield SpecialPowers.pushPrefEnv({ + set: [["browser.tabs.crashReporting.requestEmail", true]], + }); + + registerCleanupFunction(function() { + env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); + env.set("MOZ_CRASHREPORTER_URL", serverUrl); + }); +}); + +/** + * Test that if we have an email address stored in prefs, and we decide + * not to submit the email address in the next crash report, that we + * clear the email address. + */ +add_task(function* test_clear_email() { + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + let prefs = TabCrashHandler.prefs; + let originalSendReport = prefs.getBoolPref("sendReport"); + let originalEmailMe = prefs.getBoolPref("emailMe"); + let originalIncludeURL = prefs.getBoolPref("includeURL"); + let originalEmail = prefs.getCharPref("email"); + + // Pretend that we stored an email address from the previous + // crash + prefs.setCharPref("email", EMAIL); + prefs.setBoolPref("emailMe", true); + + let tab = gBrowser.getTabForBrowser(browser); + yield BrowserTestUtils.crashBrowser(browser); + let doc = browser.contentDocument; + + // Since about:tabcrashed will run in the parent process, we can safely + // manipulate its DOM nodes directly + let emailMe = doc.getElementById("emailMe"); + emailMe.checked = false; + + let crashReport = promiseCrashReport({ + Email: "", + }); + + let restoreTab = browser.contentDocument.getElementById("restoreTab"); + restoreTab.click(); + yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored"); + yield crashReport; + + is(prefs.getCharPref("email"), "", "No email address should be stored"); + + // Submitting the crash report may have set some prefs regarding how to + // send tab crash reports. Let's reset them for the next test. + prefs.setBoolPref("sendReport", originalSendReport); + prefs.setBoolPref("emailMe", originalEmailMe); + prefs.setBoolPref("includeURL", originalIncludeURL); + prefs.setCharPref("email", originalEmail); + }); +}); + diff --git a/browser/base/content/test/tabcrashed/browser_showForm.js b/browser/base/content/test/tabcrashed/browser_showForm.js new file mode 100644 index 000000000..780af93fb --- /dev/null +++ b/browser/base/content/test/tabcrashed/browser_showForm.js @@ -0,0 +1,40 @@ +"use strict"; + +const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; + +// 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(2); + +/** + * Tests that we show the about:tabcrashed additional details form + * if the "submit a crash report" checkbox was checked by default. + */ +add_task(function* test_show_form() { + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + // Flip the pref so that the checkbox should be checked + // by default. + let pref = TabCrashHandler.prefs.root + "sendReport"; + yield SpecialPowers.pushPrefEnv({ + set: [[pref, true]] + }); + + // Now crash the browser. + yield BrowserTestUtils.crashBrowser(browser); + + let doc = browser.contentDocument; + + // Ensure the checkbox is checked. We can safely reach into + // the content since about:tabcrashed is an in-process URL. + let checkbox = doc.getElementById("sendReport"); + ok(checkbox.checked, "Send report checkbox is checked."); + + // Ensure the options form is displayed. + let options = doc.getElementById("options"); + ok(!options.hidden, "Showing the crash report options form."); + }); +}); diff --git a/browser/base/content/test/tabcrashed/browser_shown.js b/browser/base/content/test/tabcrashed/browser_shown.js new file mode 100644 index 000000000..d09d9438f --- /dev/null +++ b/browser/base/content/test/tabcrashed/browser_shown.js @@ -0,0 +1,203 @@ +"use strict"; + +const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; +const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; +const COMMENTS = "Here's my test comment!"; +const EMAIL = "foo@privacy.com"; + +/** + * Sets up the browser to send crash reports to the local crash report + * testing server. + */ +add_task(function* setup() { + // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables crash + // reports. This test needs them enabled. The test also needs a mock + // report server, and fortunately one is already set up by toolkit/ + // crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL, + // which CrashSubmit.jsm uses as a server override. + let env = Cc["@mozilla.org/process/environment;1"] + .getService(Components.interfaces.nsIEnvironment); + let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT"); + let serverUrl = env.get("MOZ_CRASHREPORTER_URL"); + env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); + env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); + + // 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(2); + + registerCleanupFunction(function() { + env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); + env.set("MOZ_CRASHREPORTER_URL", serverUrl); + }); +}); + +/** + * This function returns a Promise that resolves once the following + * actions have taken place: + * + * 1) A new tab is opened up at PAGE + * 2) The tab is crashed + * 3) The about:tabcrashed page's fields are set in accordance with + * fieldValues + * 4) The tab is restored + * 5) A crash report is received from the testing server + * 6) Any tab crash prefs that were overwritten are reset + * + * @param fieldValues + * An Object describing how to set the about:tabcrashed + * fields. The following properties are accepted: + * + * comments (String) + * The comments to put in the comment textarea + * email (String) + * The email address to put in the email address input + * emailMe (bool) + * The checked value of the "Email me" checkbox + * includeURL (bool) + * The checked value of the "Include URL" checkbox + * + * If any of these fields are missing, the defaults from + * the user preferences are used. + * @param expectedExtra + * An Object describing the expected values that the submitted + * crash report's extra data should contain. + * @returns Promise + */ +function crashTabTestHelper(fieldValues, expectedExtra) { + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + let prefs = TabCrashHandler.prefs; + let originalSendReport = prefs.getBoolPref("sendReport"); + let originalEmailMe = prefs.getBoolPref("emailMe"); + let originalIncludeURL = prefs.getBoolPref("includeURL"); + let originalEmail = prefs.getCharPref("email"); + + let tab = gBrowser.getTabForBrowser(browser); + yield BrowserTestUtils.crashBrowser(browser); + let doc = browser.contentDocument; + + // Since about:tabcrashed will run in the parent process, we can safely + // manipulate its DOM nodes directly + let comments = doc.getElementById("comments"); + let email = doc.getElementById("email"); + let emailMe = doc.getElementById("emailMe"); + let includeURL = doc.getElementById("includeURL"); + + if (fieldValues.hasOwnProperty("comments")) { + comments.value = fieldValues.comments; + } + + if (fieldValues.hasOwnProperty("email")) { + email.value = fieldValues.email; + } + + if (fieldValues.hasOwnProperty("emailMe")) { + emailMe.checked = fieldValues.emailMe; + } + + if (fieldValues.hasOwnProperty("includeURL")) { + includeURL.checked = fieldValues.includeURL; + } + + let crashReport = promiseCrashReport(expectedExtra); + let restoreTab = browser.contentDocument.getElementById("restoreTab"); + restoreTab.click(); + yield BrowserTestUtils.waitForEvent(tab, "SSTabRestored"); + yield crashReport; + + // Submitting the crash report may have set some prefs regarding how to + // send tab crash reports. Let's reset them for the next test. + prefs.setBoolPref("sendReport", originalSendReport); + prefs.setBoolPref("emailMe", originalEmailMe); + prefs.setBoolPref("includeURL", originalIncludeURL); + prefs.setCharPref("email", originalEmail); + }); +} + +/** + * Tests what we send with the crash report by default. By default, we do not + * send any comments, the URL of the crashing page, or the email address of + * the user. + */ +add_task(function* test_default() { + yield crashTabTestHelper({}, { + "Comments": null, + "URL": "", + "Email": null, + }); +}); + +/** + * Test just sending a comment. + */ +add_task(function* test_just_a_comment() { + yield crashTabTestHelper({ + comments: COMMENTS, + }, { + "Comments": COMMENTS, + "URL": "", + "Email": null, + }); +}); + +/** + * Test that we don't send email if emailMe is unchecked + */ +add_task(function* test_no_email() { + yield crashTabTestHelper({ + email: EMAIL, + emailMe: false, + }, { + "Comments": null, + "URL": "", + "Email": null, + }); +}); + +/** + * Test that we can send an email address if emailMe is checked + */ +add_task(function* test_yes_email() { + yield crashTabTestHelper({ + email: EMAIL, + emailMe: true, + }, { + "Comments": null, + "URL": "", + "Email": EMAIL, + }); +}); + +/** + * Test that we will send the URL of the page if includeURL is checked. + */ +add_task(function* test_send_URL() { + yield crashTabTestHelper({ + includeURL: true, + }, { + "Comments": null, + "URL": PAGE, + "Email": null, + }); +}); + +/** + * Test that we can send comments, the email address, and the URL + */ +add_task(function* test_send_all() { + yield crashTabTestHelper({ + includeURL: true, + emailMe: true, + email: EMAIL, + comments: COMMENTS, + }, { + "Comments": COMMENTS, + "URL": PAGE, + "Email": EMAIL, + }); +}); + diff --git a/browser/base/content/test/tabcrashed/browser_withoutDump.js b/browser/base/content/test/tabcrashed/browser_withoutDump.js new file mode 100644 index 000000000..62557f443 --- /dev/null +++ b/browser/base/content/test/tabcrashed/browser_withoutDump.js @@ -0,0 +1,36 @@ +"use strict"; + +const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page."; + +add_task(function* setup() { + prepareNoDump(); +}); + +/** + * Tests tab crash page when a dump is not available. + */ +add_task(function* test_without_dump() { + return BrowserTestUtils.withNewTab({ + gBrowser, + url: PAGE, + }, function*(browser) { + let tab = gBrowser.getTabForBrowser(browser); + yield BrowserTestUtils.crashBrowser(browser); + + let tabRemovedPromise = BrowserTestUtils.removeTab(tab, { dontRemove: true }); + + yield ContentTask.spawn(browser, null, function*() { + let doc = content.document; + Assert.ok(!doc.documentElement.classList.contains("crashDumpAvailable"), + "doesn't have crash dump"); + + let options = doc.getElementById("options"); + Assert.ok(options, "has crash report options"); + Assert.ok(options.hidden, "crash report options are hidden"); + + doc.getElementById("closeTab").click(); + }); + + yield tabRemovedPromise; + }); +}); diff --git a/browser/base/content/test/tabcrashed/head.js b/browser/base/content/test/tabcrashed/head.js new file mode 100644 index 000000000..6eee08f13 --- /dev/null +++ b/browser/base/content/test/tabcrashed/head.js @@ -0,0 +1,110 @@ +/** + * Returns a Promise that resolves once a crash report has + * been submitted. This function will also test the crash + * reports extra data to see if it matches expectedExtra. + * + * @param expectedExtra (object) + * An Object whose key-value pairs will be compared + * against the key-value pairs in the extra data of the + * crash report. A test failure will occur if there is + * a mismatch. + * + * If the value of the key-value pair is "null", this will + * be interpreted as "this key should not be included in the + * extra data", and will cause a test failure if it is detected + * in the crash report. + * + * Note that this will ignore any keys that are not included + * in expectedExtra. It's possible that the crash report + * will contain other extra information that is not + * compared against. + * @returns Promise + */ +function promiseCrashReport(expectedExtra={}) { + return Task.spawn(function*() { + info("Starting wait on crash-report-status"); + let [subject, ] = + yield TestUtils.topicObserved("crash-report-status", (unused, data) => { + return data == "success"; + }); + info("Topic observed!"); + + if (!(subject instanceof Ci.nsIPropertyBag2)) { + throw new Error("Subject was not a Ci.nsIPropertyBag2"); + } + + let remoteID = getPropertyBagValue(subject, "serverCrashID"); + if (!remoteID) { + throw new Error("Report should have a server ID"); + } + + let file = Cc["@mozilla.org/file/local;1"] + .createInstance(Ci.nsILocalFile); + file.initWithPath(Services.crashmanager._submittedDumpsDir); + file.append(remoteID + ".txt"); + if (!file.exists()) { + throw new Error("Report should have been received by the server"); + } + + file.remove(false); + + let extra = getPropertyBagValue(subject, "extra"); + if (!(extra instanceof Ci.nsIPropertyBag2)) { + throw new Error("extra was not a Ci.nsIPropertyBag2"); + } + + info("Iterating crash report extra keys"); + let enumerator = extra.enumerator; + while (enumerator.hasMoreElements()) { + let key = enumerator.getNext().QueryInterface(Ci.nsIProperty).name; + let value = extra.getPropertyAsAString(key); + if (key in expectedExtra) { + if (expectedExtra[key] == null) { + ok(false, `Got unexpected key ${key} with value ${value}`); + } else { + is(value, expectedExtra[key], + `Crash report had the right extra value for ${key}`); + } + } + } + }); +} + + +/** + * For an nsIPropertyBag, returns the value for a given + * key. + * + * @param bag + * The nsIPropertyBag to retrieve the value from + * @param key + * The key that we want to get the value for from the + * bag + * @returns The value corresponding to the key from the bag, + * or null if the value could not be retrieved (for + * example, if no value is set at that key). +*/ +function getPropertyBagValue(bag, key) { + try { + let val = bag.getProperty(key); + return val; + } catch (e) { + if (e.result != Cr.NS_ERROR_FAILURE) { + throw e; + } + } + + return null; +} + +/** + * Monkey patches TabCrashHandler.getDumpID to return null in order to test + * about:tabcrashed when a dump is not available. + */ +function prepareNoDump() { + let originalGetDumpID = TabCrashHandler.getDumpID; + TabCrashHandler.getDumpID = function(browser) { return null; }; + registerCleanupFunction(() => { + TabCrashHandler.getDumpID = originalGetDumpID; + }); +} |