Backed out 6 changesets (bug 1110511) for automation timeouts in mochitest-bc suites

Backed out changeset eddfd7f32a1c (bug 1110511)
Backed out changeset 0eb0dda094b3 (bug 1110511)
Backed out changeset 8e20cd68ca78 (bug 1110511)
Backed out changeset c7b983f65568 (bug 1110511)
Backed out changeset 219d86f78b6b (bug 1110511)
Backed out changeset 0e055b648e10 (bug 1110511)
This commit is contained in:
Wes Kocher 2015-10-05 16:38:03 -07:00
parent 715fadd945
commit 4826252eee
15 changed files with 212 additions and 705 deletions

View File

@ -1882,12 +1882,6 @@ pref("browser.tabs.remote.autostart.1", false);
pref("browser.tabs.remote.autostart.2", true);
#endif
// For the about:tabcrashed page
pref("browser.tabs.crashReporting.sendReport", true);
pref("browser.tabs.crashReporting.includeURL", false);
pref("browser.tabs.crashReporting.emailMe", false);
pref("browser.tabs.crashReporting.email", "");
#ifdef NIGHTLY_BUILD
#ifndef MOZ_MULET
pref("layers.async-pan-zoom.enabled", true);

View File

@ -3,66 +3,27 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function parseQueryString() {
let URL = document.documentURI;
let queryString = URL.replace(/^about:tabcrashed?e=tabcrashed/, "");
let url = document.documentURI;
let queryString = url.replace(/^about:tabcrashed?e=tabcrashed/, "");
let titleMatch = queryString.match(/d=([^&]*)/);
let URLMatch = queryString.match(/u=([^&]*)/);
return {
title: titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "",
URL: URLMatch && URLMatch[1] ? decodeURIComponent(URLMatch[1]) : "",
};
return titleMatch && titleMatch[1] ? decodeURIComponent(titleMatch[1]) : "";
}
function displayUI() {
if (!hasReport()) {
return;
}
document.title = parseQueryString();
let sendCrashReport = document.getElementById("sendReport").checked;
let container = document.getElementById("crash-reporter-container");
container.hidden = !sendCrashReport;
}
function hasReport() {
return document.documentElement.classList.contains("crashDumpAvailable");
function shouldSendReport() {
if (!document.documentElement.classList.contains("crashDumpAvailable"))
return false;
return document.getElementById("sendReport").checked;
}
function sendEvent(message) {
let comments = "";
let email = "";
let URL = "";
let sendCrashReport = false;
let emailMe = false;
let includeURL = false;
if (hasReport()) {
sendCrashReport = document.getElementById("sendReport").checked;
if (sendCrashReport) {
comments = document.getElementById("comments").value.trim();
includeURL = document.getElementById("includeURL").checked;
if (includeURL) {
URL = parseQueryString().URL.trim();
}
emailMe = document.getElementById("emailMe").checked;
if (emailMe) {
email = document.getElementById("email").value.trim();
}
}
}
let event = new CustomEvent("AboutTabCrashedMessage", {
bubbles: true,
detail: {
message,
sendCrashReport,
comments,
email,
emailMe,
includeURL,
URL,
sendCrashReport: shouldSendReport(),
},
});
@ -81,17 +42,6 @@ function restoreAll() {
sendEvent("restoreAll");
}
document.title = parseQueryString().title;
// Error pages are loaded as LOAD_BACKGROUND, so they don't get load events.
var event = new CustomEvent("AboutTabCrashedLoad", {bubbles:true});
document.dispatchEvent(event);
addEventListener("DOMContentLoaded", function() {
let sendReport = document.getElementById("sendReport");
sendReport.addEventListener("click", function() {
displayUI();
});
displayUI();
});

View File

@ -37,22 +37,8 @@
<p>&tabCrashed.message;</p>
<div id="report-box">
<input type="checkbox" id="sendReport"/>
<input type="checkbox" id="sendReport" checked="checked"/>
<label for="sendReport">&tabCrashed.sendReport;</label>
<div id="crash-reporter-container" hidden="true">
<p id="crash-reporter-title">&tabCrashed.crashReporter;</p>
<textarea id="comments" placeholder="&tabCrashed.commentPlaceholder;" rows="4"></textarea>
<ul id="options">
<li><input type="checkbox" id="includeURL"/>
<label for="includeURL">&tabCrashed.includeURL;</label></li>
<li><input type="checkbox" id="emailMe"/>
<label for="emailMe">&tabCrashed.emailMe;</label></li>
</ul>
<input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
</div>
</div>
<p id="reportSent">&tabCrashed.reportSent;</p>

View File

@ -1166,15 +1166,7 @@ var gBrowserInit = {
let browser = gBrowser.getBrowserForDocument(ownerDoc);
#ifdef MOZ_CRASHREPORTER
if (event.detail.sendCrashReport) {
TabCrashReporter.submitCrashReport(browser, {
comments: event.detail.comments,
email: event.detail.email,
emailMe: event.detail.emailMe,
includeURL: event.detail.includeURL,
URL: event.detail.URL,
});
} else {
TabCrashReporter.dontSubmitCrashReport();
TabCrashReporter.submitCrashReport(browser);
}
#endif

View File

@ -525,5 +525,3 @@ support-files =
readerModeArticle.html
[browser_domFullscreen_fullscreenMode.js]
[browser_menuButtonBadgeManager.js]
[browser_aboutTabCrashed.js]
skip-if = !e10s || !crashreporter

View File

@ -1,335 +0,0 @@
"use strict";
let { TabCrashReporter } =
Cu.import("resource:///modules/ContentCrashReporters.jsm");
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";
/**
* 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) {}
return null;
}
/**
* 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
* 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.
*
* Note that this will only check the values that exist
* 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, data] =
yield TestUtils.topicObserved("crash-report-status", (subject, 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) {
is(value, expectedExtra[key],
`Crash report had the right extra value for ${key}`);
}
}
});
}
/**
* 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);
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 = TabCrashReporter.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": "",
"URL": "",
"Email": "",
});
});
/**
* Test just sending a comment.
*/
add_task(function* test_just_a_comment() {
yield crashTabTestHelper({
comments: COMMENTS,
}, {
"Comments": COMMENTS,
"URL": "",
"Email": "",
});
});
/**
* Test that we don't send email if emailMe is unchecked
*/
add_task(function* test_no_email() {
yield crashTabTestHelper({
email: EMAIL,
emailMe: false,
}, {
"Comments": "",
"URL": "",
"Email": "",
});
});
/**
* 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": "",
"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": "",
"URL": PAGE,
"Email": "",
});
});
/**
* 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,
});
});
/**
* 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 = TabCrashReporter.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);
});
});

View File

@ -69,7 +69,7 @@ add_task(function* test_crash() {
// the content process. The "crash" message makes it first so that we don't
// get a chance to process the flush. The TabStateFlusher however should be
// notified so that the flush still completes.
let promise1 = BrowserTestUtils.crashBrowser(browser);
let promise1 = crashBrowser(browser);
let promise2 = TabStateFlusher.flush(browser);
yield Promise.all([promise1, promise2]);

View File

@ -117,7 +117,7 @@ add_task(function test_crash_page_not_in_history() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
// Check the tab state and make sure the tab crashed page isn't
// mentioned.
@ -146,7 +146,7 @@ add_task(function test_revived_history_from_remote() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
// Browse to a new site that will cause the browser to
// become remote again.
@ -185,7 +185,7 @@ add_task(function test_revived_history_from_non_remote() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
// Browse to a new site that will not cause the browser to
// become remote again.
@ -235,7 +235,7 @@ add_task(function test_revive_tab_from_session_store() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
// Use SessionStore to revive the tab
@ -286,7 +286,7 @@ add_task(function test_revive_all_tabs_from_session_store() {
yield TabStateFlusher.flush(browser2);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
is(newTab2.getAttribute("crashed"), "true", "Second tab should be crashed too.");
// Use SessionStore to revive all the tabs
@ -331,7 +331,7 @@ add_task(function test_close_tab_after_crash() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
let promise = promiseEvent(gBrowser.tabContainer, "TabClose");
@ -359,7 +359,7 @@ add_task(function* test_hide_restore_all_button() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
let doc = browser.contentDocument;
let restoreAllButton = doc.getElementById("restoreAll");
@ -375,7 +375,7 @@ add_task(function* test_hide_restore_all_button() {
yield promiseBrowserLoaded(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
doc = browser.contentDocument;
restoreAllButton = doc.getElementById("restoreAll");
@ -405,7 +405,7 @@ add_task(function* test_aboutcrashedtabzoom() {
yield TabStateFlusher.flush(browser);
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
yield crashBrowser(browser);
ok(ZoomManager.getZoomForBrowser(browser) === 1, "zoom should have reset on crash");

View File

@ -538,6 +538,112 @@ function promiseRemoveTab(tab) {
return BrowserTestUtils.removeTab(tab);
}
/**
* Returns a Promise that resolves once a remote <xul:browser> has experienced
* a crash. Also does the job of cleaning up the minidump of the crash.
*
* @param browser
* The <xul:browser> that will crash
* @return Promise
*/
function crashBrowser(browser) {
/**
* Returns the directory where crash dumps are stored.
*
* @return nsIFile
*/
function getMinidumpDirectory() {
let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
dir.append("minidumps");
return dir;
}
/**
* Removes a file from a directory. This is a no-op if the file does not
* exist.
*
* @param directory
* The nsIFile representing the directory to remove from.
* @param filename
* A string for the file to remove from the directory.
*/
function removeFile(directory, filename) {
let file = directory.clone();
file.append(filename);
if (file.exists()) {
file.remove(false);
}
}
// This frame script is injected into the remote browser, and used to
// intentionally crash the tab. We crash by using js-ctypes and dereferencing
// a bad pointer. The crash should happen immediately upon loading this
// frame script.
let frame_script = () => {
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
let dies = function() {
privateNoteIntentionalCrash();
let zero = new ctypes.intptr_t(8);
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
badptr.contents
};
dump("Et tu, Brute?");
dies();
}
let crashCleanupPromise = new Promise((resolve, reject) => {
let observer = (subject, topic, data) => {
is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
ok(subject instanceof Ci.nsIPropertyBag2,
'Subject implements nsIPropertyBag2.');
// we might see this called as the process terminates due to previous tests.
// We are only looking for "abnormal" exits...
if (!subject.hasKey("abnormal")) {
info("This is a normal termination and isn't the one we are looking for...");
return;
}
let dumpID;
if ('nsICrashReporter' in Ci) {
dumpID = subject.getPropertyAsAString('dumpID');
ok(dumpID, "dumpID is present and not an empty string");
}
if (dumpID) {
let minidumpDirectory = getMinidumpDirectory();
removeFile(minidumpDirectory, dumpID + '.dmp');
removeFile(minidumpDirectory, dumpID + '.extra');
}
Services.obs.removeObserver(observer, 'ipc:content-shutdown');
info("Crash cleaned up");
resolve();
};
Services.obs.addObserver(observer, 'ipc:content-shutdown');
});
let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
info("about:tabcrashed loaded");
resolve();
}, false, true);
});
// This frame script will crash the remote browser as soon as it is
// evaluated.
let mm = browser.messageManager;
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]).then(() => {
let tab = gBrowser.getTabForBrowser(browser);
is(tab.getAttribute("crashed"), "true", "Tab should be marked as crashed");
});
}
// Write DOMSessionStorage data to the given browser.
function modifySessionStorage(browser, data, options = {}) {
return ContentTask.spawn(browser, [data, options], function* ([data, options]) {

View File

@ -5,11 +5,6 @@
<!ENTITY tabCrashed.header "Bad news first: This tab has crashed">
<!ENTITY tabCrashed.message "Now for the good news: You can just close this tab, restore it or restore all your crashed tabs.">
<!ENTITY tabCrashed.sendReport "Submit a crash report to help prevent more bad news">
<!ENTITY tabCrashed.includeURL "Include the address of the page I was on">
<!ENTITY tabCrashed.commentPlaceholder "Add a comment (comments are publicly visible)">
<!ENTITY tabCrashed.emailPlaceholder "Enter your email address here">
<!ENTITY tabCrashed.emailMe "Email me when more information is available">
<!ENTITY tabCrashed.crashReporter "Mozilla Crash Reporter">
<!ENTITY tabCrashed.reportSent "Crash report already submitted; thank you for helping make &brandShortName; better!">
<!ENTITY tabCrashed.closeTab "Close This Tab">
<!ENTITY tabCrashed.restoreTab "Restore This Tab">

View File

@ -17,11 +17,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
"resource://gre/modules/CrashSubmit.jsm");
this.TabCrashReporter = {
get prefs() {
delete this.prefs;
return this.prefs = Services.prefs.getBranch("browser.tabs.crashReporting.");
},
init: function () {
if (this.initialized)
return;
@ -52,66 +47,21 @@ this.TabCrashReporter = {
if (!browser)
return;
this.browserMap.set(browser.permanentKey, aSubject.childID);
this.browserMap.set(browser, aSubject.childID);
break;
}
},
/**
* Submits a crash report from about:tabcrashed
*
* @param aBrowser
* The <xul:browser> that the report was sent from.
* @param aFormData
* An Object with the following properties:
*
* includeURL (bool):
* Whether to include the URL that the user was on
* in the crashed tab before the crash occurred.
* URL (String)
* The URL that the user was on in the crashed tab
* before the crash occurred.
* emailMe (bool):
* Whether or not to include the user's email address
* in the crash report.
* email (String):
* The email address of the user.
* comments (String):
* Any additional comments from the user.
*
* Note that it is expected that all properties are set,
* even if they are empty.
*/
submitCrashReport: function (aBrowser, aFormData) {
let childID = this.browserMap.get(aBrowser.permanentKey);
submitCrashReport: function (aBrowser) {
let childID = this.browserMap.get(aBrowser);
let dumpID = this.childMap.get(childID);
if (!dumpID)
return
CrashSubmit.submit(dumpID, {
recordSubmission: true,
extraExtraKeyVals: {
Comments: aFormData.comments,
Email: aFormData.email,
URL: aFormData.URL,
},
}).then(null, Cu.reportError);
this.prefs.setBoolPref("sendReport", true);
this.prefs.setBoolPref("includeURL", aFormData.includeURL);
this.prefs.setBoolPref("emailMe", aFormData.emailMe);
if (aFormData.emailMe) {
this.prefs.setCharPref("email", aFormData.email);
} else {
this.prefs.setCharPref("email", "");
if (CrashSubmit.submit(dumpID, { recordSubmission: true })) {
this.childMap.set(childID, null); // Avoid resubmission.
this.removeSubmitCheckboxesForSameCrash(childID);
}
this.childMap.set(childID, null); // Avoid resubmission.
this.removeSubmitCheckboxesForSameCrash(childID);
},
dontSubmitCrashReport: function() {
this.prefs.setBoolPref("sendReport", false);
},
removeSubmitCheckboxesForSameCrash: function(childID) {
@ -129,8 +79,8 @@ this.TabCrashReporter = {
if (!doc.documentURI.startsWith("about:tabcrashed"))
continue;
if (this.browserMap.get(browser.permanentKey) == childID) {
this.browserMap.delete(browser.permanentKey);
if (this.browserMap.get(browser) == childID) {
this.browserMap.delete(browser);
browser.contentDocument.documentElement.classList.remove("crashDumpAvailable");
browser.contentDocument.documentElement.classList.add("crashDumpSubmitted");
}
@ -147,27 +97,11 @@ this.TabCrashReporter = {
if (!this.childMap)
return;
let dumpID = this.childMap.get(this.browserMap.get(aBrowser.permanentKey));
let dumpID = this.childMap.get(this.browserMap.get(aBrowser));
if (!dumpID)
return;
let doc = aBrowser.contentDocument;
doc.documentElement.classList.add("crashDumpAvailable");
let sendReport = this.prefs.getBoolPref("sendReport");
doc.getElementById("sendReport").checked = sendReport;
let includeURL = this.prefs.getBoolPref("includeURL");
doc.getElementById("includeURL").checked = includeURL;
let emailMe = this.prefs.getBoolPref("emailMe");
doc.getElementById("emailMe").checked = emailMe;
if (emailMe) {
let email = this.prefs.getCharPref("email", "");
doc.getElementById("email").value = email;
}
aBrowser.contentDocument.documentElement.classList.add("crashDumpAvailable");
},
hideRestoreAllButton: function (aBrowser) {

View File

@ -9,34 +9,3 @@
#reportSent {
font-weight: bold;
}
#crash-reporter-container {
width: 80%;
background-color: var(--in-content-box-background-hover);
margin: 24px 0;
padding: 14px;
border: 1px solid var(--in-content-box-border-color);
border-radius: 2px;
}
#crash-reporter-title {
font-weight: bold;
margin: 0 0 14px 0;
}
input[type="text"],
textarea {
width: 100%;
box-sizing: border-box;
resize: none;
}
#options {
list-style: none;
margin-inline-start: 0;
}
input[type="text"],
#options > li {
margin: 14px 0 0 0;
}

View File

@ -22,7 +22,6 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");
Cu.import("resource://gre/modules/KeyValueParser.jsm");
Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager)
@ -459,139 +458,5 @@ this.BrowserTestUtils = {
tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
}
});
},
/**
* Crashes a remote browser tab and cleans up the generated minidumps.
* Resolves with the data from the .extra file (the crash annotations).
*
* @param (Browser) browser
* A remote <xul:browser> element. Must not be null.
*
* @returns (Promise)
* @resolves An Object with key-value pairs representing the data from the
* crash report's extra file (if applicable).
*/
crashBrowser: Task.async(function*(browser) {
let extra = {};
if (!browser.isRemoteBrowser) {
throw new Error("<xul:browser> needs to be remote in order to crash");
}
/**
* Returns the directory where crash dumps are stored.
*
* @return nsIFile
*/
function getMinidumpDirectory() {
let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
dir.append("minidumps");
return dir;
}
/**
* Removes a file from a directory. This is a no-op if the file does not
* exist.
*
* @param directory
* The nsIFile representing the directory to remove from.
* @param filename
* A string for the file to remove from the directory.
*/
function removeFile(directory, filename) {
let file = directory.clone();
file.append(filename);
if (file.exists()) {
file.remove(false);
}
}
// This frame script is injected into the remote browser, and used to
// intentionally crash the tab. We crash by using js-ctypes and dereferencing
// a bad pointer. The crash should happen immediately upon loading this
// frame script.
let frame_script = () => {
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
let dies = function() {
privateNoteIntentionalCrash();
let zero = new ctypes.intptr_t(8);
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
badptr.contents
};
dump("\nEt tu, Brute?\n");
dies();
}
let crashCleanupPromise = new Promise((resolve, reject) => {
let observer = (subject, topic, data) => {
if (topic != "ipc:content-shutdown") {
return reject("Received incorrect observer topic: " + topic);
}
if (!(subject instanceof Ci.nsIPropertyBag2)) {
return reject("Subject did not implement nsIPropertyBag2");
}
// we might see this called as the process terminates due to previous tests.
// We are only looking for "abnormal" exits...
if (!subject.hasKey("abnormal")) {
dump("\nThis is a normal termination and isn't the one we are looking for...\n");
return;
}
let dumpID;
if ('nsICrashReporter' in Ci) {
dumpID = subject.getPropertyAsAString('dumpID');
if (!dumpID) {
return reject("dumpID was not present despite crash reporting " +
"being enabled");
}
}
if (dumpID) {
let minidumpDirectory = getMinidumpDirectory();
let extrafile = minidumpDirectory.clone();
extrafile.append(dumpID + '.extra');
if (extrafile.exists()) {
dump(`\nNo .extra file for dumpID: ${dumpID}\n`);
extra = parseKeyValuePairsFromFile(extrafile);
}
removeFile(minidumpDirectory, dumpID + '.dmp');
removeFile(minidumpDirectory, dumpID + '.extra');
}
Services.obs.removeObserver(observer, 'ipc:content-shutdown');
dump("\nCrash cleaned up\n");
resolve();
};
Services.obs.addObserver(observer, 'ipc:content-shutdown', false);
});
let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
dump("\nabout:tabcrashed loaded\n");
resolve();
}, false, true);
});
// This frame script will crash the remote browser as soon as it is
// evaluated.
let mm = browser.messageManager;
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
yield Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]);
let gBrowser = browser.ownerDocument.defaultView.gBrowser;
let tab = gBrowser.getTabForBrowser(browser);
if (tab.getAttribute("crashed") != "true") {
throw new Error("Tab should be marked as crashed");
}
return extra;
}),
}
};

View File

@ -9,6 +9,72 @@
// Running this test in ASAN is slow.
requestLongerTimeout(2);
/**
* Returns a Promise that resolves once a remote <xul:browser> has experienced
* a crash. Resolves with the data from the .extra file (the crash annotations).
*
* @param browser
* The <xul:browser> that will crash
* @return Promise
*/
function crashBrowser(browser) {
let kv = {};
Cu.import("resource://gre/modules/KeyValueParser.jsm", kv);
// This frame script is injected into the remote browser, and used to
// intentionally crash the tab. We crash by using js-ctypes and dereferencing
// a bad pointer. The crash should happen immediately upon loading this
// frame script.
let frame_script = () => {
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
let dies = function() {
privateNoteIntentionalCrash();
let zero = new ctypes.intptr_t(8);
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
let crash = badptr.contents;
};
dump("Et tu, Brute?");
dies();
};
function checkSubject(subject, data) {
return subject instanceof Ci.nsIPropertyBag2 &&
subject.hasKey("abnormal");
};
let crashPromise = TestUtils.topicObserved('ipc:content-shutdown',
checkSubject);
let crashDataPromise = crashPromise.then(([subject, data]) => {
ok(subject instanceof Ci.nsIPropertyBag2);
let dumpID;
if ('nsICrashReporter' in Ci) {
dumpID = subject.getPropertyAsAString('dumpID');
ok(dumpID, "dumpID is present and not an empty string");
}
let extra = null;
if (dumpID) {
let minidumpDirectory = getMinidumpDirectory();
let extrafile = minidumpDirectory.clone();
extrafile.append(dumpID + '.extra');
ok(extrafile.exists(), 'found .extra file');
extra = kv.parseKeyValuePairsFromFile(extrafile);
removeFile(minidumpDirectory, dumpID + '.dmp');
removeFile(minidumpDirectory, dumpID + '.extra');
}
return extra;
});
// This frame script will crash the remote browser as soon as it is
// evaluated.
let mm = browser.messageManager;
mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
return crashDataPromise;
}
/**
* Removes a file from a directory. This is a no-op if the file does not
* exist.
@ -64,7 +130,7 @@ add_task(function* test_content_url_annotation() {
yield promise;
// Crash the tab
let annotations = yield BrowserTestUtils.crashBrowser(browser);
let annotations = yield crashBrowser(browser);
ok("URL" in annotations, "annotated a URL");
is(annotations.URL, redirect_url,

View File

@ -406,11 +406,13 @@ xul|button[type="menu"] > xul|menupopup xul|menuseparator {
/* textboxes */
html|input[type="text"],
html|textarea,
xul|textbox {
*|textbox {
-moz-appearance: none;
height: 30px;
color: var(--in-content-text-color);
line-height: 20px;
padding-right: 10px;
padding-left: 10px;
border: 1px solid var(--in-content-box-border-color);
-moz-border-top-colors: none !important;
-moz-border-right-colors: none !important;
@ -420,27 +422,12 @@ xul|textbox {
background-color: var(--in-content-box-background);
}
xul|textbox {
min-height: 30px;
padding-right: 10px;
padding-left: 10px;
}
html|input[type="text"],
html|textarea {
font-family: inherit;
font-size: inherit;
padding: 5px 10px;
}
html|input[type="text"]:focus,
html|textarea:focus,
html|textbox:focus,
xul|textbox[focused] {
border-color: var(--in-content-border-focus);
}
html|input[type="text"]:disabled,
html|textarea:disabled,
html|textbox:disabled,
xul|textbox[disabled="true"] {
opacity: 0.5;
}