summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/tabcrashed/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/tabcrashed/head.js')
-rw-r--r--browser/base/content/test/tabcrashed/head.js238
1 files changed, 238 insertions, 0 deletions
diff --git a/browser/base/content/test/tabcrashed/head.js b/browser/base/content/test/tabcrashed/head.js
new file mode 100644
index 0000000000..bc6185a283
--- /dev/null
+++ b/browser/base/content/test/tabcrashed/head.js
@@ -0,0 +1,238 @@
+"use strict";
+
+const { TelemetryTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TelemetryTestUtils.sys.mjs"
+);
+
+/**
+ * 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 (async function () {
+ info("Starting wait on crash-report-status");
+ let [subject] = await 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.nsIFile);
+ 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");
+ for (let { name: key } of extra.enumerator) {
+ 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;
+}
+
+/**
+ * Sets up the browser to send crash reports to the local crash report
+ * testing server.
+ */
+async function setupLocalCrashReportServer() {
+ const SERVER_URL =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
+
+ // 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 noReport = Services.env.get("MOZ_CRASHREPORTER_NO_REPORT");
+ let serverUrl = Services.env.get("MOZ_CRASHREPORTER_URL");
+ Services.env.set("MOZ_CRASHREPORTER_NO_REPORT", "");
+ Services.env.set("MOZ_CRASHREPORTER_URL", SERVER_URL);
+
+ registerCleanupFunction(function () {
+ Services.env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport);
+ Services.env.set("MOZ_CRASHREPORTER_URL", serverUrl);
+ });
+}
+
+/**
+ * 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;
+ });
+}
+
+const kBuildidMatchEnv = "MOZ_BUILDID_MATCH_DONTSEND";
+
+function setBuildidMatchDontSendEnv() {
+ info("Setting " + kBuildidMatchEnv + "=1");
+ Services.env.set(kBuildidMatchEnv, "1");
+}
+
+function unsetBuildidMatchDontSendEnv() {
+ info("Setting " + kBuildidMatchEnv + "=0");
+ Services.env.set(kBuildidMatchEnv, "0");
+}
+
+function getEventPromise(eventName, eventKind) {
+ return new Promise(function (resolve, reject) {
+ info("Installing event listener (" + eventKind + ")");
+ window.addEventListener(
+ eventName,
+ event => {
+ ok(true, "Received " + eventName + " (" + eventKind + ") event");
+ info("Call resolve() for " + eventKind + " event");
+ resolve();
+ },
+ { once: true }
+ );
+ info("Installed event listener (" + eventKind + ")");
+ });
+}
+
+async function ensureBuildID() {
+ let profD = Services.dirsvc.get("GreD", Ci.nsIFile);
+ let platformIniOrig = await IOUtils.readUTF8(
+ PathUtils.join(profD.path, "platform.ini")
+ );
+ let buildID = Services.appinfo.platformBuildID;
+ return platformIniOrig.indexOf(buildID) > 0;
+}
+
+async function openNewTab(forceCrash) {
+ const PAGE =
+ "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
+
+ let options = {
+ gBrowser,
+ PAGE,
+ waitForLoad: false,
+ waitForStateStop: false,
+ forceNewProcess: true,
+ };
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(options);
+ if (forceCrash === true) {
+ let browser = tab.linkedBrowser;
+ await BrowserTestUtils.crashFrame(
+ browser,
+ /* shouldShowTabCrashPage */ false,
+ /* shouldClearMinidumps */ true,
+ /* BrowsingContext */ null
+ );
+ }
+
+ return tab;
+}
+
+async function closeTab(tab) {
+ await TestUtils.waitForTick();
+ BrowserTestUtils.removeTab(tab);
+}
+
+function getFalsePositiveTelemetry() {
+ const scalars = TelemetryTestUtils.getProcessScalars("parent");
+ return scalars["dom.contentprocess.buildID_mismatch_false_positive"];
+}
+
+// The logic bound to dom.ipc.processPrelaunch.enabled will react to value
+// changes: https://searchfox.org/mozilla-central/rev/ecd91b104714a8b2584a4c03175be50ccb3a7c67/dom/ipc/PreallocatedProcessManager.cpp#171-195
+// So we force flip to ensure we have no dangling process.
+async function forceCleanProcesses() {
+ const origPrefValue = SpecialPowers.getBoolPref(
+ "dom.ipc.processPrelaunch.enabled"
+ );
+ await SpecialPowers.setBoolPref(
+ "dom.ipc.processPrelaunch.enabled",
+ !origPrefValue
+ );
+ await SpecialPowers.setBoolPref(
+ "dom.ipc.processPrelaunch.enabled",
+ origPrefValue
+ );
+ const currPrefValue = SpecialPowers.getBoolPref(
+ "dom.ipc.processPrelaunch.enabled"
+ );
+ ok(currPrefValue === origPrefValue, "processPrelaunch properly re-enabled");
+}