about:crashes: updated user interface (bug 1476062); r=mconley,flod

The about:crashes page's user interface is being updated (bug 1463515).

- changed crash submission to be done through button press instead of link clicking
    - this allows users to know that they are submitting the crash
- updated visuals to match new mock-up
    - mock-up image: https://bug1463515.bmoattachments.org/attachment.cgi?id=8990380
- added new Fluent strings that are needed for the new user interface
- modernized the surrounding code

Differential Revision: https://phabricator.services.mozilla.com/D2792

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Michael Kelly 2018-08-30 19:04:03 +00:00
Родитель 157b6e767a
Коммит dc84fa05b8
8 изменённых файлов: 390 добавлений и 308 удалений

Просмотреть файл

@ -8,59 +8,68 @@
min-width: 30em;
max-width: 60em;
}
table {
clear: both;
width: 90%;
margin: 0 auto;
width: 100%;
padding-bottom: 2em;
}
th {
font-size: 130%;
text-align: left;
white-space: nowrap;
}
th:-moz-locale-dir(rtl) {
text-align: right;
}
/* name */
th:first-child {
padding-inline-end: 2em;
}
/* submitted */
th:last-child {
text-align: center;
}
:link, :visited {
display: block;
min-height: 17px;
}
/* date */
td:first-child + td {
width: 0;
padding-inline-start: 1em;
padding-inline-end: .5em;
white-space: nowrap;
}
/* time */
td:last-child {
width: 0;
padding-inline-start: .5em;
white-space: nowrap;
}
#clear-reports {
.float-right {
float: right;
}
#clear-reports:-moz-locale-dir(rtl) {
float: left;
.hidden {
display: none;
}
.table-title-container {
align-items: center;
display: flex;
justify-content: space-between;
}
.wide-button {
display: block;
min-height: 32px;
padding-left: 30px;
padding-right: 30px;
}
.submitting {
background-image: url(chrome://global/skin/icons/loading.png);
background-position: center;
background-repeat: no-repeat;
background-position: right;
background-size: 16px;
}
.submitting .submit-crash-button-label {
display: none;
}
.failed-to-submit {
color: #ca8695;
}
a.button-as-link {
-moz-appearance: none;
min-height: 30px;
color: var(--in-content-text-color) !important;
border: 1px solid var(--in-content-box-border-color) !important;
border-radius: 2px;
background-color: var(--in-content-page-background);
line-height: 30px;
margin: 4px 8px;
/* Ensure font-size isn't overridden by widget styling (e.g. in forms.css) */
font-size: 1em;
}
a.button-as-link:hover {
background-color: var(--in-content-box-background-hover) !important;
text-decoration: none;
}
h2.lighter-font-weight {
font-weight: lighter;
}
html[dir=ltr] th {
text-align: left;
}
html[dir=rtl] th {
text-align: right;
}
@media (min-resolution: 1.1dppx) {
.submitting {

Просмотреть файл

@ -2,148 +2,211 @@
* 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/. */
var reportURL;
let reportURL;
ChromeUtils.import("resource://gre/modules/CrashReports.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(this, "CrashSubmit",
"resource://gre/modules/CrashSubmit.jsm");
ChromeUtils.defineModuleGetter(this, "CrashSubmit", "resource://gre/modules/CrashSubmit.jsm");
document.addEventListener("DOMContentLoaded", function() {
populateReportList();
document.getElementById("clear-reports").addEventListener("click", function() {
clearReports().then(null, Cu.reportError);
document.addEventListener("DOMContentLoaded", () => {
populateReportLists();
document.getElementById("clearUnsubmittedReports").addEventListener("click", () => {
clearUnsubmittedReports().catch(Cu.reportError);
});
document.getElementById("clearSubmittedReports").addEventListener("click", () => {
clearSubmittedReports().catch(Cu.reportError);
});
});
const buildID = Services.appinfo.appBuildID;
function submitPendingReport(event) {
let link = event.target;
let id = link.firstChild.textContent;
link.className = "submitting";
CrashSubmit.submit(id, { noThrottle: true }).then(
(remoteCrashID) => {
link.className = "";
// Reset the link to point at our new crash report. This way, if the
// user clicks "Back", the link will be correct.
link.firstChild.textContent = remoteCrashID;
link.setAttribute("id", remoteCrashID);
link.removeEventListener("click", submitPendingReport, true);
if (reportURL) {
link.setAttribute("href", reportURL + remoteCrashID);
// redirect the user to their brand new crash report
window.location.href = reportURL + remoteCrashID;
}
},
() => {
// XXX: do something more useful here
link.className = "";
// Dispatch an event, useful for testing
let event = document.createEvent("Events");
event.initEvent("CrashSubmitFailed", true, false);
document.dispatchEvent(event);
});
event.preventDefault();
return false;
}
function populateReportList() {
/**
* Adds the crash reports with submission buttons and links
* to the unsubmitted and submitted crash report lists.
* If breakpad.reportURL is not set, displays a misconfiguration message
* instead.
*/
function populateReportLists() {
try {
reportURL = Services.prefs.getCharPref("breakpad.reportURL");
// Ignore any non http/https urls
if (!/^https?:/i.test(reportURL))
if (!/^https?:/i.test(reportURL)) {
reportURL = null;
} catch (e) { }
if (!reportURL) {
document.getElementById("clear-reports").style.display = "none";
document.getElementById("reportList").style.display = "none";
document.getElementById("noConfig").style.display = "block";
return;
}
let reports = CrashReports.getReports();
if (reports.length == 0) {
document.getElementById("clear-reports").style.display = "none";
document.getElementById("reportList").style.display = "none";
document.getElementById("noReports").style.display = "block";
return;
}
var dateFormatter;
var timeFormatter;
try {
dateFormatter = new Services.intl.DateTimeFormat(undefined, { dateStyle: "short" });
timeFormatter = new Services.intl.DateTimeFormat(undefined, { timeStyle: "short" });
} catch (e) {
// XXX Fallback to be removed once bug 1215247 is complete
// and the Intl API is available on all platforms.
dateFormatter = {
format(date) {
return date.toLocaleDateString();
reportURL = null;
}
};
timeFormatter = {
format(date) {
return date.toLocaleTimeString();
if (!reportURL) {
document.getElementById("noConfig").classList.remove("hidden");
return;
}
};
}
var reportURI = Services.io.newURI(reportURL);
// resolving this URI relative to /report/index
var aboutThrottling = Services.io.newURI("../../about/throttling", null, reportURI);
const reports = CrashReports.getReports();
const dateFormatter = new Services.intl.DateTimeFormat(undefined, {
timeStyle: "short",
dateStyle: "short"
});
reports.forEach(report => addReportRow(report.pending, report.id, report.date, dateFormatter));
showAppropriateSections();
}
for (var i = 0; i < reports.length; i++) {
var row = document.createElement("tr");
var cell = document.createElement("td");
row.appendChild(cell);
var link = document.createElement("a");
if (reports[i].pending) {
link.setAttribute("href", aboutThrottling.spec);
link.addEventListener("click", submitPendingReport, true);
} else {
link.setAttribute("href", reportURL + reports[i].id);
}
link.setAttribute("id", reports[i].id);
link.classList.add("crashReport");
link.appendChild(document.createTextNode(reports[i].id));
cell.appendChild(link);
/**
* Adds a crash report with the appropriate submission button
* or viewing link to the unsubmitted or submitted report list
* based on isPending.
*
* @param {Boolean} isPending whether the crash is up for submission
* @param {String} id the unique id of the crash report
* @param {Date} date either the date of crash or date of submission
* @param {Object} dateFormatter formatter for presenting dates to users
*/
function addReportRow(isPending, id, date, dateFormatter) {
const rowTemplate = document.getElementById("crashReportRow");
const row = document.importNode(rowTemplate.content, true).querySelector("tr");
row.id = id;
var date = new Date(reports[i].date);
cell = document.createElement("td");
cell.appendChild(document.createTextNode(dateFormatter.format(date)));
row.appendChild(cell);
cell = document.createElement("td");
cell.appendChild(document.createTextNode(timeFormatter.format(date)));
row.appendChild(cell);
if (reports[i].pending) {
const cells = row.querySelectorAll("td");
cells[0].appendChild(document.createTextNode(id));
cells[1].appendChild(document.createTextNode(dateFormatter.format(date)));
if (isPending) {
const buttonTemplate = document.getElementById("crashSubmitButton");
const button = document.importNode(buttonTemplate.content, true).querySelector("button");
const buttonText = button.querySelector("span");
button.addEventListener("click",
() => submitPendingReport(id, row, button, buttonText, dateFormatter));
cells[2].appendChild(button);
document.getElementById("unsubmitted").appendChild(row);
} else {
const linkTemplate = document.getElementById("viewCrashLink");
const link = document.importNode(linkTemplate.content, true).querySelector("a");
link.href = `${reportURL}${id}`;
cells[2].appendChild(link);
document.getElementById("submitted").appendChild(row);
}
}
}
var clearReports = async function() {
/**
* Shows or hides each of the unsubmitted and submitted report list
* based on whether they contain at least one crash report.
* If hidden, the submitted report list is replaced by a message
* indicating that no crash reports have been submitted.
*/
function showAppropriateSections() {
let hasUnsubmitted = document.getElementById("unsubmitted").childElementCount > 0;
document.getElementById("reportListUnsubmitted").classList.toggle("hidden", !hasUnsubmitted);
let hasSubmitted = document.getElementById("submitted").childElementCount > 0;
document.getElementById("reportListSubmitted").classList.toggle("hidden", !hasSubmitted);
document.getElementById("noSubmittedReports").classList.toggle("hidden", hasSubmitted);
}
/**
* Changes the provided button to display a spinner. Then, tries to submit the
* crash report for the provided id. On success, removes the crash report from
* the list of unsubmitted crash reports and adds a new crash report to the list
* of submitted crash reports. On failure, changes the provided button to display
* a red error message.
*
* @param {String} reportId the unique id of the crash report
* @param {HTMLTableRowElement} row the table row of the crash report
* @param {HTMLButtonElement} button the button pressed to start the submission
* @param {HTMLSpanElement} buttonText the text inside the pressed button
* @param {Object} dateFormatter formatter for presenting dates to users
*/
function submitPendingReport(reportId, row, button, buttonText, dateFormatter) {
button.classList.add("submitting");
CrashSubmit.submit(reportId, { noThrottle: true }).then(remoteCrashID => {
document.getElementById("unsubmitted").removeChild(row);
const report = CrashReports.getReports().filter(report => report.id === remoteCrashID);
addReportRow(false, remoteCrashID, report.date, dateFormatter);
showAppropriateSections();
dispatchEvent("CrashSubmitSucceeded");
},
() => {
button.classList.remove("submitting");
button.classList.add("failed-to-submit");
document.l10n.setAttributes(buttonText, "submit-crash-button-failure-label");
dispatchEvent("CrashSubmitFailed");
},
);
}
/**
* Deletes unsubmitted and old crash reports from the user's device.
* Then, hides the list of unsubmitted crash reports.
*/
async function clearUnsubmittedReports() {
const [title, description] = await document.l10n.formatValues([
{id: "delete-confirm-title"},
{id: "delete-confirm-description"},
{id: "delete-unsubmitted-description"},
]);
if (!Services.prompt.confirm(window, title, description)) {
return;
}
let cleanupFolder = async function(path, filter) {
let iterator = new OS.File.DirectoryIterator(path);
await cleanupFolder(CrashReports.pendingDir.path);
await clearOldReports();
document.getElementById("reportListUnsubmitted").classList.add("hidden");
}
/**
* Deletes submitted and old crash reports from the user's device.
* Then, hides the list of submitted crash reports.
*/
async function clearSubmittedReports() {
const [title, description] = await document.l10n.formatValues([
{id: "delete-confirm-title"},
{id: "delete-submitted-description"},
]);
if (!Services.prompt.confirm(window, title, description)) {
return;
}
await cleanupFolder(
CrashReports.submittedDir.path,
async entry => entry.name.startsWith("bp-") && entry.name.endsWith(".txt"),
);
await clearOldReports();
document.getElementById("reportListSubmitted").classList.add("hidden");
document.getElementById("noSubmittedReports").classList.remove("hidden");
}
/**
* Deletes old crash reports from the user's device.
*/
async function clearOldReports() {
const oneYearAgo = Date.now() - 31586000000;
await cleanupFolder(CrashReports.reportsDir.path, async entry => {
if (!entry.name.startsWith("InstallTime") || entry.name == "InstallTime" + buildID) {
return false;
}
let date = entry.winLastWriteDate;
if (!date) {
const stat = await OS.File.stat(entry.path);
date = stat.lastModificationDate;
}
return date < oneYearAgo;
});
}
/**
* Deletes files from the user's device at the specified path
* that match the provided filter.
*
* @param {String} path the directory location to delete form
* @param {Function} filter function taking in a file entry and
* returning whether to delete the file
*/
async function cleanupFolder(path, filter) {
const iterator = new OS.File.DirectoryIterator(path);
try {
await iterator.forEach(async function(aEntry) {
if (!filter || (await filter(aEntry))) {
await OS.File.remove(aEntry.path);
await iterator.forEach(async entry => {
if (!filter || (await filter(entry))) {
await OS.File.remove(entry.path);
}
});
} catch (e) {
@ -153,31 +216,15 @@ var clearReports = async function() {
} finally {
iterator.close();
}
};
}
await cleanupFolder(CrashReports.submittedDir.path, function(aEntry) {
return aEntry.name.startsWith("bp-") && aEntry.name.endsWith(".txt");
});
let oneYearAgo = Date.now() - 31586000000;
await cleanupFolder(CrashReports.reportsDir.path, async function(aEntry) {
if (!aEntry.name.startsWith("InstallTime") ||
aEntry.name == "InstallTime" + buildID) {
return false;
}
let date = aEntry.winLastWriteDate;
if (!date) {
let stat = await OS.File.stat(aEntry.path);
date = stat.lastModificationDate;
}
return (date < oneYearAgo);
});
await cleanupFolder(CrashReports.pendingDir.path);
document.getElementById("clear-reports").style.display = "none";
document.getElementById("reportList").style.display = "none";
document.getElementById("noReports").style.display = "block";
};
/**
* Dispatches an event with the specified name.
*
* @param {String} name the name of the event
*/
function dispatchEvent(name) {
const event = document.createEvent("Events");
event.initEvent(name, true, false);
document.dispatchEvent(event);
}

Просмотреть файл

@ -4,48 +4,69 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
<link rel="stylesheet" type="text/css" href="chrome://global/content/crashes.css"/>
<link rel="stylesheet" media="screen, projection" type="text/css"
<head>
<link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
<link rel="stylesheet" type="text/css" href="chrome://global/content/crashes.css"/>
<link rel="stylesheet" media="screen, projection" type="text/css"
href="chrome://global/skin/in-content/common.css"/>
<script type="application/javascript" src="chrome://global/content/crashes.js"/>
<script type="text/javascript" src="chrome://global/content/l10n.js"/>
<script type="application/javascript" src="chrome://global/content/crashes.js"/>
<script type="text/javascript" src="chrome://global/content/l10n.js"/>
<title data-l10n-id="crash-reports-title"></title>
</head>
<title data-l10n-id="crash-reports-title"></title>
</head><body>
<button id="clear-reports" data-l10n-id="clear-all-reports-label"></button>
<div id="reportList">
<div id="reportListUnsubmitted">
<h1 data-l10n-id="crashes-unsubmitted-label"></h1>
<body>
<p id="noConfig" class="hidden" data-l10n-id="no-config-label"></p>
<p id="noSubmittedReports" class="hidden" data-l10n-id="no-reports-label"></p>
<div id="reportListSubmitted" class="hidden">
<div class="table-title-container">
<h2 class="lighter-font-weight" data-l10n-id="crashes-submitted-label"></h2>
<button id="clearSubmittedReports" class="wide-button" data-l10n-id="delete-button-label"></button>
</div>
<table>
<thead>
<tr>
<th data-l10n-id="id-heading"></th>
<th colspan="2" data-l10n-id="date-crashed-heading"></th>
<th data-l10n-id="date-submitted-heading"></th>
</tr>
</thead>
<tbody id="unsubmitted">
</tbody>
<tbody id="submitted"></tbody>
</table>
</div>
<div id="reportListSubmitted">
<h1 data-l10n-id="crashes-submitted-label"></h1>
<div id="reportListUnsubmitted" class="hidden">
<div class="table-title-container">
<h2 class="lighter-font-weight" data-l10n-id="crashes-unsubmitted-label"></h2>
<button id="clearUnsubmittedReports" class="wide-button" data-l10n-id="delete-button-label"></button>
</div>
<table>
<thead>
<tr>
<th data-l10n-id="id-heading"></th>
<th colspan="2" data-l10n-id="date-submitted-heading"></th>
<th data-l10n-id="date-crashed-heading"></th>
</tr>
</thead>
<tbody id="submitted">
</tbody>
<tbody id="unsubmitted"></tbody>
</table>
</div>
</div>
<p id="noReports" style="display: none" data-l10n-id="no-reports-label"></p>
<p id="noConfig" style="display: none" data-l10n-id="no-config-label"></p>
</body>
</body>
<template id="crashReportRow">
<tr>
<td class="crash-id"></td>
<td></td>
<td class="float-right"></td>
</tr>
</template>
<template id="crashSubmitButton">
<button class="submit-button wide-button">
<span class="submit-crash-button-label" data-l10n-id="submit-crash-button-label"></span>
</button>
</template>
<template id="viewCrashLink">
<a class="crash-link button-as-link wide-button" data-l10n-id="view-crash-button-label"></a>
</template>
</html>

Просмотреть файл

@ -1,22 +1,28 @@
add_task(async function test() {
let appD = make_fake_appdir();
let crD = appD.clone();
const appD = make_fake_appdir();
const crD = appD.clone();
crD.append("Crash Reports");
let crashes = add_fake_crashes(crD, 5);
const crashes = add_fake_crashes(crD, 5);
// sanity check
let appDtest = Services.dirsvc.get("UAppData", Ci.nsIFile);
const appDtest = Services.dirsvc.get("UAppData", Ci.nsIFile);
ok(appD.equals(appDtest), "directory service provider registered ok");
await BrowserTestUtils.withNewTab({ gBrowser, url: "about:crashes" }, function(browser) {
await BrowserTestUtils.withNewTab({ gBrowser, url: "about:crashes" }, browser => {
info("about:crashes loaded");
return ContentTask.spawn(browser, crashes, function(crashes) {
let doc = content.document;
let crashlinks = doc.getElementById("submitted").querySelectorAll(".crashReport");
Assert.equal(crashlinks.length, crashes.length,
"about:crashes lists correct number of crash reports");
return ContentTask.spawn(browser, crashes, crashes => {
const doc = content.document;
const crashIds = doc.getElementsByClassName("crash-id");
Assert.equal(
crashIds.length,
crashes.length,
"about:crashes lists correct number of crash reports",
);
for (let i = 0; i < crashes.length; i++) {
Assert.equal(crashlinks[i].firstChild.textContent, crashes[i].id,
i + ": crash ID is correct");
Assert.equal(
crashIds[i].textContent,
crashes[i].id,
i + ": crash ID is correct",
);
}
});
});

Просмотреть файл

@ -16,21 +16,20 @@ function cleanup_and_finish() {
* NB: This function is run in the child process via ContentTask.spawn.
*/
function check_crash_list(crashes) {
let doc = content.document;
let crashlinks = doc.getElementsByClassName("crashReport");
Assert.equal(crashlinks.length, crashes.length,
"about:crashes lists correct number of crash reports");
// no point in checking this if the lists aren't the same length
if (crashlinks.length == crashes.length) {
const doc = content.document;
const crashIds = doc.getElementsByClassName("crash-id");
Assert.equal(
crashIds.length,
crashes.length,
"about:crashes lists correct number of crash reports",
);
for (let i = 0; i < crashes.length; i++) {
Assert.equal(crashlinks[i].id, crashes[i].id, i + ": crash ID is correct");
if (crashes[i].pending) {
// we set the breakpad.reportURL pref in test()
Assert.equal(crashlinks[i].getAttribute("href"),
"http://example.com/browser/toolkit/crashreporter/about/throttling",
"pending URL links to the correct static page");
}
}
Assert.equal(
crashIds[i].textContent,
crashes[i].id,
i + ": crash ID is correct",
);
}
}
@ -43,10 +42,16 @@ function check_crash_list(crashes) {
* Additionally, click "back" and verify that the link now points to our new
*/
function check_submit_pending(tab, crashes) {
let browser = gBrowser.getBrowserForTab(tab);
const browser = gBrowser.getBrowserForTab(tab);
let SubmittedCrash = null;
let CrashID = null;
let CrashURL = null;
function csp_onsuccess() {
const crashLinks = content.document.getElementsByClassName("crash-link");
// Get the last link since it is appended to the end of the list
const link = crashLinks[crashLinks.length - 1];
link.click();
}
function csp_onload() {
// loaded the crash report page
ok(true, "got submission onload");
@ -99,47 +104,50 @@ function check_submit_pending(tab, crashes) {
ok(false, "failed to submit crash report!");
cleanup_and_finish();
}
browser.addEventListener("CrashSubmitSucceeded", csp_onsuccess, true);
browser.addEventListener("CrashSubmitFailed", csp_fail, true);
BrowserTestUtils.browserLoaded(browser, false, (url) => url !== "about:crashes").then(csp_onload);
function csp_pageshow() {
ContentTask.spawn(browser, { CrashID, CrashURL }, function({ CrashID, CrashURL }) {
Assert.equal(content.location.href, "about:crashes", "navigated back successfully");
let link = content.document.getElementById(CrashID);
const link = content.document.getElementById(CrashID).getElementsByClassName("crash-link")[0];
Assert.notEqual(link, null, "crash report link changed correctly");
if (link)
if (link) {
Assert.equal(link.href, CrashURL, "crash report link points to correct href");
}
}).then(cleanup_and_finish);
}
// try submitting the pending report
for (let crash of crashes) {
for (const crash of crashes) {
if (crash.pending) {
SubmittedCrash = crash;
break;
}
}
ContentTask.spawn(browser, SubmittedCrash.id, function(id) {
let link = content.document.getElementById(id);
link.click();
ContentTask.spawn(browser, SubmittedCrash.id, id => {
const submitButton = content.document.getElementById(id).getElementsByClassName("submit-button")[0];
submitButton.click();
});
}
function test() {
waitForExplicitFinish();
let appD = make_fake_appdir();
let crD = appD.clone();
const appD = make_fake_appdir();
const crD = appD.clone();
crD.append("Crash Reports");
let crashes = add_fake_crashes(crD, 1);
const crashes = add_fake_crashes(crD, 1);
// we don't need much data here, it's not going to a real Socorro
crashes.push(addPendingCrashreport(crD,
crashes.push(addPendingCrashreport(
crD,
crashes[crashes.length - 1].date + 60000,
{"ServerURL": "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs",
{
"ServerURL": "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs",
"ProductName": "Test App",
// test that we don't truncate
// at = (bug 512853)
"Foo": "ABC=XYZ"
}));
"Foo": "ABC=XYZ", // test that we don't truncat eat = (bug 512853)
}
));
crashes.sort((a, b) => b.date - a.date);
// set this pref so we can link to our test server

Просмотреть файл

@ -1,16 +1,16 @@
function check_clear_visible(browser, aVisible) {
return ContentTask.spawn(browser, aVisible, function(aVisible) {
let doc = content.document;
const doc = content.document;
let visible = false;
let button = doc.getElementById("clear-reports");
if (button) {
let style = doc.defaultView.getComputedStyle(button);
if (style.display != "none" &&
style.visibility == "visible")
const reportListSubmitted = doc.getElementById("reportListSubmitted");
if (reportListSubmitted) {
const style = doc.defaultView.getComputedStyle(reportListSubmitted);
if (style.display !== "none" && style.visibility === "visible") {
visible = true;
}
}
Assert.equal(visible, aVisible,
"clear reports button is " + (aVisible ? "visible" : "hidden"));
"clear submitted reports button is " + (aVisible ? "visible" : "hidden"));
});
}

Просмотреть файл

@ -2,41 +2,23 @@
* 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/. */
function clickClearReports(browser) {
let doc = content.document;
let button = doc.getElementById("clear-reports");
if (!button) {
Assert.ok(false, "Button not found");
return Promise.resolve();
function clickClearReports() {
const doc = content.document;
const reportListUnsubmitted = doc.getElementById("reportListUnsubmitted");
const reportListSubmitted = doc.getElementById("reportListSubmitted");
if (!reportListUnsubmitted || !reportListSubmitted) {
Assert.ok(false, "Report list not found");
}
let style = doc.defaultView.getComputedStyle(button);
const unsubmittedStyle = doc.defaultView.getComputedStyle(reportListUnsubmitted);
const submittedStyle = doc.defaultView.getComputedStyle(reportListSubmitted);
Assert.notEqual(unsubmittedStyle.display, "none", "Unsubmitted report list is visible");
Assert.notEqual(submittedStyle.display, "none", "Submitted report list is visible");
Assert.notEqual(style.display, "none", "Clear reports button visible");
let deferred = {};
deferred.promise = new Promise(resolve => deferred.resolve = resolve);
var observer = new content.MutationObserver(function(mutations) {
for (let mutation of mutations) {
if (mutation.type == "attributes" &&
mutation.attributeName == "style") {
observer.disconnect();
Assert.equal(style.display, "none", "Clear reports button hidden");
deferred.resolve();
}
}
});
observer.observe(button, {
attributes: true,
childList: true,
characterData: true,
attributeFilter: ["style"],
});
button.click();
return deferred.promise;
const clearUnsubmittedButton = doc.getElementById("clearUnsubmittedReports");
const clearSubmittedButton = doc.getElementById("clearSubmittedReports");
clearUnsubmittedButton.click();
clearSubmittedButton.click();
}
var promptShown = false;
@ -103,7 +85,10 @@ add_task(async function test() {
let existing = [ file1.path, file2.path, report1.path, report2.path,
report3.path, submitdir.path, pendingdir.path ];
await ContentTask.spawn(browser, null, clickClearReports);
ContentTask.spawn(browser, null, clickClearReports);
await BrowserTestUtils.waitForCondition(() =>
content.document.getElementById("reportListUnsubmitted").classList.contains("hidden")
&& content.document.getElementById("reportListSubmitted").classList.contains("hidden"));
for (let dir of dirs) {
let entries = dir.directoryEntries;

Просмотреть файл

@ -4,16 +4,22 @@
crash-reports-title = Crash Reports
clear-all-reports-label = Remove All Reports
delete-button-label = Clear All
delete-confirm-title = Are you sure?
delete-confirm-description = This will delete all reports and cannot be undone.
delete-unsubmitted-description = This will delete all unsubmitted crash reports and cannot be undone.
delete-submitted-description = This will remove the list of submitted crash reports but will not delete the submitted data. This cannot be undone.
crashes-unsubmitted-label = Unsubmitted Crash Reports
id-heading = Report ID
date-crashed-heading = Date Crashed
submit-crash-button-label = Submit
# This text is used to replace the label of the crash submit button
# if the crash submission fails.
submit-crash-button-failure-label = Failed
crashes-submitted-label = Submitted Crash Reports
date-submitted-heading = Date Submitted
view-crash-button-label = View
no-reports-label = No crash reports have been submitted.
no-config-label = This application has not been configured to display crash reports. The preference <code>breakpad.reportURL</code> must be set.