Bug 1309316 - Expose preference for auto-submitting backlogged crash reports to about:tabcrashed. r?felipe
MozReview-Commit-ID: JvhK0MO1IK
--- a/browser/base/content/aboutTabCrashed.css
+++ b/browser/base/content/aboutTabCrashed.css
@@ -1,8 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
html:not(.crashDumpSubmitted) #reportSent,
-html:not(.crashDumpAvailable) #report-box {
+html:not(.crashDumpAvailable) #reportBox,
+.container[multiple="true"] > .offers > #offerHelpMessageSingle,
+.container[multiple="false"] > .offers > #offerHelpMessageMultiple,
+.container[multiple="false"] > .button-container > #restoreAll {
display: none;
-}
+}
\ No newline at end of file
--- a/browser/base/content/aboutTabCrashed.js
+++ b/browser/base/content/aboutTabCrashed.js
@@ -57,17 +57,17 @@ var AboutTabCrashed = {
addEventListener("DOMContentLoaded", this);
document.title = this.pageData.title;
},
receiveMessage(message) {
switch (message.name) {
case "UpdateCount": {
- this.showRestoreAll(message.data.count > 1);
+ this.setMultiple(message.data.count > 1);
break;
}
case "SetCrashReportAvailable": {
this.onSetCrashReportAvailable(message);
break;
}
case "CrashReportSent": {
this.onCrashReportSent();
@@ -146,48 +146,59 @@ var AboutTabCrashed = {
* will respond with whether or not a crash report is available. This
* method handles that message.
*
* @param message
* The message from the parent, which should contain a data
* Object property with the following properties:
*
* hasReport (bool):
- * Whether or not there is a crash report
+ * Whether or not there is a crash report.
*
* sendReport (bool):
* Whether or not the the user prefers to send the report
- * by default
+ * by default.
*
* includeURL (bool):
* Whether or not the user prefers to send the URL of
* the tab that crashed.
*
* emailMe (bool):
* Whether or not to send the email address of the user
* in the report.
*
* email (String):
- * The email address of the user (empty if emailMe is false)
+ * The email address of the user (empty if emailMe is false).
+ *
+ * requestAutoSubmit (bool):
+ * Whether or not we should ask the user to automatically
+ * submit backlogged crash reports.
*
*/
onSetCrashReportAvailable(message) {
- if (message.data.hasReport) {
+ let data = message.data;
+
+ if (data.hasReport) {
this.hasReport = true;
document.documentElement.classList.add("crashDumpAvailable");
- let data = message.data;
document.getElementById("sendReport").checked = data.sendReport;
document.getElementById("includeURL").checked = data.includeURL;
document.getElementById("emailMe").checked = data.emailMe;
if (data.emailMe) {
document.getElementById("email").value = data.email;
}
this.showCrashReportUI(data.sendReport);
+ } else {
+ this.showCrashReportUI(false);
+ }
+
+ if (data.requestAutoSubmit) {
+ document.getElementById("requestAutoSubmit").hidden = false;
}
let event = new CustomEvent("AboutTabCrashedReady", {bubbles:true});
document.dispatchEvent(event);
},
/**
* Handler for when the parent reports that the crash report associated
@@ -200,34 +211,39 @@ var AboutTabCrashed = {
/**
* Toggles the display of the crash report form.
*
* @param shouldShow (bool)
* True if the crash report form should be shown
*/
showCrashReportUI(shouldShow) {
- let container = document.getElementById("crash-reporter-container");
- container.hidden = !shouldShow;
+ let options = document.getElementById("options");
+ options.hidden = !shouldShow;
},
/**
- * Toggles the display of the "Restore All" button.
+ * Toggles whether or not the page is one of several visible pages
+ * showing the crash reporter. This controls some of the language
+ * on the page, along with what the "primary" button is.
*
- * @param shouldShow (bool)
- * True if the "Restore All" button should be shown
+ * @param hasMultiple (bool)
+ * True if there are multiple crash report pages being shown.
*/
- showRestoreAll(shouldShow) {
- let restoreAll = document.getElementById("restoreAll");
+ setMultiple(hasMultiple) {
+ let main = document.getElementById("main");
+ main.setAttribute("multiple", hasMultiple);
+
let restoreTab = document.getElementById("restoreTab");
- if (shouldShow) {
- restoreAll.removeAttribute("hidden");
+
+ // The "Restore All" button has the "primary" class by default, so
+ // we only need to modify the "Restore Tab" button.
+ if (hasMultiple) {
restoreTab.classList.remove("primary");
} else {
- restoreAll.setAttribute("hidden", true);
restoreTab.classList.add("primary");
}
},
/**
* Sends a message to the parent in response to the user choosing
* one of the actions available on the page. This might also send up
* crash report information if the user has chosen to submit a crash
@@ -238,16 +254,17 @@ var AboutTabCrashed = {
*/
sendMessage(messageName) {
let comments = "";
let email = "";
let URL = "";
let sendReport = false;
let emailMe = false;
let includeURL = false;
+ let autoSubmit = false;
if (this.hasReport) {
sendReport = document.getElementById("sendReport").checked;
if (sendReport) {
comments = document.getElementById("comments").value.trim();
includeURL = document.getElementById("includeURL").checked;
if (includeURL) {
@@ -256,20 +273,30 @@ var AboutTabCrashed = {
emailMe = document.getElementById("emailMe").checked;
if (emailMe) {
email = document.getElementById("email").value.trim();
}
}
}
+ let requestAutoSubmit = document.getElementById("requestAutoSubmit");
+ if (requestAutoSubmit.hidden) {
+ // The checkbox is hidden if the user has already opted in to sending
+ // backlogged crash reports.
+ autoSubmit = true;
+ } else {
+ autoSubmit = document.getElementById("autoSubmit").checked;
+ }
+
sendAsyncMessage(messageName, {
sendReport,
comments,
email,
emailMe,
includeURL,
URL,
+ autoSubmit,
});
},
};
AboutTabCrashed.init();
--- a/browser/base/content/aboutTabCrashed.xhtml
+++ b/browser/base/content/aboutTabCrashed.xhtml
@@ -25,47 +25,73 @@
href="chrome://global/skin/in-content/info-pages.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/content/aboutTabCrashed.css"/>
<link rel="stylesheet" type="text/css" media="all"
href="chrome://browser/skin/aboutTabCrashed.css"/>
</head>
<body dir="&locale.dir;">
- <div class="container">
+ <div id="main" class="container" multiple="false">
+
<div class="title">
- <h1 class="title-text">&tabCrashed.header;</h1>
+ <h1 class="title-text">&tabCrashed.header2;</h1>
+ </div>
+
+ <div class="offers">
+ <h2>&tabCrashed.offerHelp;</h2>
+ <p id="offerHelpMessageSingle">&tabCrashed.single.offerHelpMessage;</p>
+ <p id="offerHelpMessageMultiple">&tabCrashed.multiple.offerHelpMessage;</p>
</div>
- <p>&tabCrashed.message;</p>
+
+ <div id="reportBox">
+ <h2>&tabCrashed.requestHelp;</h2>
+ <p>&tabCrashed.requestHelpMessage;</p>
+
+ <h2>&tabCrashed.requestReport;</h2>
+
+ <div class="checkbox-with-label">
+ <input type="checkbox" id="sendReport"/>
+ <label for="sendReport">&tabCrashed.sendReport2;</label>
+ </div>
- <div id="report-box">
- <input type="checkbox" id="sendReport"/>
- <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>
+ <textarea id="comments" placeholder="&tabCrashed.commentPlaceholder2;" rows="4"></textarea>
+ </li>
+
+ <li class="checkbox-with-label">
+ <input type="checkbox" id="includeURL"/>
+ <label for="includeURL">&tabCrashed.includeURL2;</label>
+ </li>
- <ul id="options">
- <li><input type="checkbox" id="includeURL"/>
- <label for="includeURL">&tabCrashed.includeURL;</label></li>
+ <li>
+ <div class="checkbox-with-label">
+ <input type="checkbox" id="emailMe"/>
+ <label for="emailMe">&tabCrashed.emailMe;</label>
+ </div>
+ <input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
+ </li>
+ </ul>
- <li><input type="checkbox" id="emailMe"/>
- <label for="emailMe">&tabCrashed.emailMe;</label></li>
- </ul>
-
- <input type="text" id="email" placeholder="&tabCrashed.emailPlaceholder;"/>
+ <div id="requestAutoSubmit" hidden="true">
+ <h2>&tabCrashed.requestAutoSubmit;</h2>
+ <div class="checkbox-with-label">
+ <input type="checkbox" id="autoSubmit"/>
+ <label for="autoSubmit">&tabCrashed.autoSubmit;</label>
+ </div>
</div>
</div>
<p id="reportSent">&tabCrashed.reportSent;</p>
<div class="button-container">
<button id="closeTab">
&tabCrashed.closeTab;</button>
- <button id="restoreTab">
+ <button id="restoreTab" class="primary">
&tabCrashed.restoreTab;</button>
<button id="restoreAll" autofocus="true" class="primary">
&tabCrashed.restoreAll;</button>
</div>
</div>
</body>
<script type="text/javascript;version=1.8" src="chrome://browser/content/aboutTabCrashed.js"/>
</html>
--- a/browser/base/content/test/general/browser_aboutTabCrashed_showForm.js
+++ b/browser/base/content/test/general/browser_aboutTabCrashed_showForm.js
@@ -26,13 +26,13 @@ add_task(function* test_show_form() {
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 form is displayed.
- let container = doc.getElementById("crash-reporter-container");
- ok(!container.hidden, "Showing the crash report detail form.");
+ // Ensure the options form is displayed.
+ let options = doc.getElementById("options");
+ ok(!options.hidden, "Showing the crash report options form.");
});
});
--- a/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js
+++ b/browser/base/content/test/general/browser_aboutTabCrashed_withoutDump.js
@@ -27,18 +27,18 @@ add_task(function* test_without_dump() {
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 container = doc.getElementById("crash-reporter-container");
- Assert.ok(container, "has crash-reporter-container");
- Assert.ok(container.hidden, "crash-reporter-container is hidden");
+ 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;
});
});
--- a/browser/components/sessionstore/test/browser_crashedTabs.js
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -387,17 +387,18 @@ add_task(function* test_hide_restore_all
// Crash the tab
yield BrowserTestUtils.crashBrowser(browser);
let doc = browser.contentDocument;
let restoreAllButton = doc.getElementById("restoreAll");
let restoreOneButton = doc.getElementById("restoreTab");
- is(restoreAllButton.getAttribute("hidden"), "true", "Restore All button should be hidden");
+ let restoreAllStyles = window.getComputedStyle(restoreAllButton);
+ is(restoreAllStyles.display, "none", "Restore All button should be hidden");
ok(restoreOneButton.classList.contains("primary"), "Restore Tab button should have the primary class");
let newTab2 = gBrowser.addTab();
gBrowser.selectedTab = newTab;
browser.loadURI(PAGE_2);
yield promiseBrowserLoaded(browser);
@@ -416,17 +417,18 @@ add_task(function* test_hide_restore_all
// Crash the first tab.
yield BrowserTestUtils.crashBrowser(browser);
yield otherBrowserReady;
doc = browser.contentDocument;
restoreAllButton = doc.getElementById("restoreAll");
restoreOneButton = doc.getElementById("restoreTab");
- ok(!restoreAllButton.hasAttribute("hidden"), "Restore All button should not be hidden");
+ restoreAllStyles = window.getComputedStyle(restoreAllButton);
+ isnot(restoreAllStyles.display, "none", "Restore All button should not be hidden");
ok(!(restoreOneButton.classList.contains("primary")), "Restore Tab button should not have the primary class");
yield BrowserTestUtils.closeWindow(win2);
gBrowser.removeTab(newTab);
gBrowser.removeTab(newTab2);
});
add_task(function* test_aboutcrashedtabzoom() {
--- a/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutTabCrashed.dtd
@@ -1,16 +1,30 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!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">
<!ENTITY tabCrashed.restoreAll "Restore All Crashed Tabs">
+
+<!-- LOCALIZATION NOTE (tabCrashed.header2): "Gah" is an English slang word
+ used to express surprise or frustration (or both at the same time). We
+ are using it to communicate in an informal way that it is both
+ frustrating that your tab crashed and a surprise that we didn't want to
+ happen. If you have a similar word or short phrase that is not profane or
+ vulgar, use it. If not, feel free to skip the word in your
+ translation. -->
+<!ENTITY tabCrashed.header2 "Gah. Your tab just crashed.">
+<!ENTITY tabCrashed.offerHelp "We can help you!">
+<!ENTITY tabCrashed.single.offerHelpMessage "Choose &tabCrashed.restoreTab; to reload page content.">
+<!ENTITY tabCrashed.multiple.offerHelpMessage "Choose &tabCrashed.restoreTab; or &tabCrashed.restoreAll; to reload page content.">
+<!ENTITY tabCrashed.requestHelp "Will you help us?">
+<!ENTITY tabCrashed.requestHelpMessage "Crash reports help us diagnose problems and make &brandShortName; better.">
+<!ENTITY tabCrashed.requestReport "Report this tab">
+<!ENTITY tabCrashed.sendReport2 "Send a crash report for the tab you are viewing">
+<!ENTITY tabCrashed.commentPlaceholder2 "Optional comments (comments are publicly visible)">
+<!ENTITY tabCrashed.includeURL2 "Include page URL with this crash report">
+<!ENTITY tabCrashed.emailPlaceholder "Enter your email address here">
+<!ENTITY tabCrashed.emailMe "Email me when more information is available">
+<!ENTITY tabCrashed.reportSent "Crash report already submitted; thank you for helping make &brandShortName; better!">
+<!ENTITY tabCrashed.requestAutoSubmit "Request background tabs">
+<!ENTITY tabCrashed.autoSubmit "Update preferences to automatically submit backlogged crash reports (and get fewer messages like this from us in the future)">
\ No newline at end of file
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -330,16 +330,22 @@ this.TabCrashHandler = {
* even if they are empty.
*/
maybeSendCrashReport(message) {
if (!AppConstants.MOZ_CRASHREPORTER)
return;
let browser = message.target.browser;
+ if (message.data.autoSubmit) {
+ // The user has opted in to autosubmitted backlogged
+ // crash reports in the future.
+ UnsubmittedCrashHandler.autoSubmit = true;
+ }
+
let childID = this.browserMap.get(browser.permanentKey);
let dumpID = this.childMap.get(childID);
if (!dumpID)
return
if (!message.data.sendReport) {
Services.telemetry.getHistogramById("FX_CONTENT_CRASH_NOT_SUBMITTED").add(1);
this.prefs.setBoolPref("sendReport", false);
@@ -444,21 +450,29 @@ this.TabCrashHandler = {
let dumpID = this.getDumpID(browser);
if (!dumpID) {
message.target.sendAsyncMessage("SetCrashReportAvailable", {
hasReport: false,
});
return;
}
+ let requestAutoSubmit = !UnsubmittedCrashHandler.autoSubmit;
let sendReport = this.prefs.getBoolPref("sendReport");
let includeURL = this.prefs.getBoolPref("includeURL");
let emailMe = this.prefs.getBoolPref("emailMe");
- let data = { hasReport: true, sendReport, includeURL, emailMe };
+ let data = {
+ hasReport: true,
+ sendReport,
+ includeURL,
+ emailMe,
+ requestAutoSubmit,
+ };
+
if (emailMe) {
data.email = this.prefs.getCharPref("email", "");
}
// Make sure to only count once even if there are multiple windows
// that will all show about:tabcrashed.
if (this._crashedTabCount == 1) {
Services.telemetry.getHistogramById("FX_CONTENT_CRASH_PRESENTED").add(1);
--- a/browser/themes/shared/aboutTabCrashed.css
+++ b/browser/themes/shared/aboutTabCrashed.css
@@ -1,42 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+body {
+ font-size: 1.25rem;
+}
+
.title {
background-image: url("chrome://browser/skin/tab-crashed.svg");
}
+.title > h1,
+.offers {
+ margin-left: 14px;
+}
+
+.title > h1 {
+ /**
+ * Add commentary?
+ */
+ padding-right: 14px;
+}
+
+.container {
+ width: 45%;
+}
+
#reportSent {
font-weight: bold;
}
-#crash-reporter-container {
- width: 80%;
+#reportBox {
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;
}
+#reportBox > h2:first-child {
+ margin-top: 0;
+}
+
#crash-reporter-title {
font-weight: bold;
margin: 0 0 14px 0;
}
input[type="text"],
textarea {
width: 100%;
box-sizing: border-box;
resize: none;
}
+input[type="text"],
+input[type="checkbox"] {
+ -moz-margin-start: 0px;
+}
+
#options {
list-style: none;
margin-inline-start: 0;
}
-input[type="text"],
-#options > li {
- margin: 14px 0 0 0;
+#options > li,
+#email {
+ margin-top: 14px;
}
+
+.checkbox-with-label {
+ display: flex;
+}
+
+.checkbox-with-label > label {
+ margin-top: auto;
+ margin-bottom: auto;
+}
\ No newline at end of file
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -69,21 +69,26 @@ html|hr {
border-color: var(--in-content-border-color);
}
xul|caption {
-moz-appearance: none;
margin: 0;
}
+html|h2,
xul|caption > xul|checkbox,
xul|caption > xul|label {
font-size: 1.3rem;
font-weight: bold;
line-height: 22px;
+}
+
+xul|caption > xul|checkbox,
+xul|caption > xul|label {
margin: 0 !important;
}
*|*.main-content {
padding-top: 40px;
padding-inline-end: 44px; /* compensate the 4px margin of child elements */
padding-bottom: 48px;
padding-inline-start: 48px;
@@ -497,70 +502,51 @@ html|a:visited {
html|a:hover:active,
.text-link:hover:active {
color: var(--in-content-link-color-active);
text-decoration: none;
}
/* Checkboxes and radio buttons */
-/* Hide the actual checkbox */
-html|input[type="checkbox"] {
- opacity: 0;
- width: 0;
- pointer-events: none;
- position: absolute;
-}
-
-/* Create a box to style as the checkbox */
-html|input[type="checkbox"] + html|label:before {
- display: inline-block;
- content: "";
- vertical-align: middle;
-}
-
-html|input[type="checkbox"] + html|label {
- line-height: 0px;
-}
-
xul|checkbox {
margin-inline-start: 0;
}
xul|*.checkbox-check,
-html|input[type="checkbox"] + html|label:before {
+html|input[type="checkbox"] {
-moz-appearance: none;
width: 23px;
height: 23px;
border-radius: 2px;
border: 1px solid var(--in-content-box-border-color);
margin-inline-end: 10px;
background-color: #f1f1f1;
/* !important needed to override toolkit checked !important rule */
background-image: linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
background-position: center center;
background-repeat: no-repeat;
box-shadow: 0 1px 1px 0 #fff, inset 0 2px 0 0 rgba(0,0,0,0.03);
}
xul|checkbox:not([disabled="true"]):hover > xul|*.checkbox-check,
-html|input[type="checkbox"]:not(:disabled) + html|label:hover:before {
+html|input[type="checkbox"]:not(:disabled):hover {
border-color: var(--in-content-border-focus);
}
xul|*.checkbox-check[checked] {
list-style-image: url("chrome://global/skin/in-content/check.svg#check");
}
-html|input[type="checkbox"]:checked + html|label:before {
+html|input[type="checkbox"]:checked {
background-image: url("chrome://global/skin/in-content/check.svg#check"), linear-gradient(#fff, rgba(255,255,255,0.8)) !important;
}
xul|checkbox[disabled="true"] > xul|*.checkbox-check,
-html|input[type="checkbox"]:disabled + html|label {
+html|input[type="checkbox"]:disabled {
opacity: 0.5;
}
xul|*.checkbox-label-box {
margin-inline-start: -1px; /* negative margin for the transparent border */
padding-inline-start: 0;
}