Bug 1287178 - Move unsubmitted crash report handling into ContentCrashHandlers.jsm. r?felipe
MozReview-Commit-ID: 8lsv6zxLc9x
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1500,8 +1500,17 @@ pref("extensions.pocket.enabled", true);
pref("signon.schemeUpgrades", true);
// Enable the "Simplify Page" feature in Print Preview
pref("print.use_simplify_page", true);
// Space separated list of URLS that are allowed to send objects (instead of
// only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
+
+// Whether or not the browser should scan for unsubmitted
+// crash reports, and then show a notification for submitting
+// those reports.
+#ifdef RELEASE_BUILD
+pref("browser.crashReports.unsubmittedCheck.enabled", false);
+#else
+pref("browser.crashReports.unsubmittedCheck.enabled", true);
+#endif
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -66,16 +66,18 @@ XPCOMUtils.defineLazyServiceGetter(this,
["WebChannel", "resource://gre/modules/WebChannel.jsm"],
["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
["webrtcUI", "resource:///modules/webrtcUI.jsm"],
].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
if (AppConstants.MOZ_CRASHREPORTER) {
XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
"resource:///modules/ContentCrashHandlers.jsm");
+ XPCOMUtils.defineLazyModuleGetter(this, "UnsubmittedCrashHandler",
+ "resource:///modules/ContentCrashHandlers.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
"resource://gre/modules/CrashSubmit.jsm");
}
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
return Services.strings.createBundle('chrome://branding/locale/brand.properties');
});
@@ -709,16 +711,17 @@ BrowserGlue.prototype = {
iconURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.icon.png",
author: vendorShortName,
});
}
TabCrashHandler.init();
if (AppConstants.MOZ_CRASHREPORTER) {
PluginCrashReporter.init();
+ UnsubmittedCrashHandler.init();
}
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
},
_checkForOldBuildUpdates: function () {
// check for update if our build is old
if (AppConstants.MOZ_UPDATER &&
@@ -739,74 +742,16 @@ BrowserGlue.prototype = {
let acceptableAge = Services.prefs.getIntPref("app.update.checkInstallTime.days") * millisecondsIn24Hours;
if (buildDate + acceptableAge < today) {
Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService).checkForBackgroundUpdates();
}
}
},
- checkForPendingCrashReports: function() {
- // We don't process crash reports older than 28 days, so don't bother submitting them
- const PENDING_CRASH_REPORT_DAYS = 28;
- if (AppConstants.MOZ_CRASHREPORTER) {
- let dateLimit = new Date();
- dateLimit.setDate(dateLimit.getDate() - PENDING_CRASH_REPORT_DAYS);
- CrashSubmit.pendingIDsAsync(dateLimit).then(
- function onSuccess(ids) {
- let count = ids.length;
- if (count) {
- let win = RecentWindow.getMostRecentBrowserWindow();
- if (!win) {
- return;
- }
- let nb = win.document.getElementById("global-notificationbox");
- let notification = nb.getNotificationWithValue("pending-crash-reports");
- if (notification) {
- return;
- }
- let buttons = [
- {
- label: win.gNavigatorBundle.getString("pendingCrashReports.submitAll"),
- callback: function() {
- ids.forEach(function(id) {
- CrashSubmit.submit(id, {extraExtraKeyVals: {"SubmittedFromInfobar": true}});
- });
- }
- },
- {
- label: win.gNavigatorBundle.getString("pendingCrashReports.ignoreAll"),
- callback: function() {
- ids.forEach(function(id) {
- CrashSubmit.ignore(id);
- });
- }
- },
- {
- label: win.gNavigatorBundle.getString("pendingCrashReports.viewAll"),
- callback: function() {
- win.openUILinkIn("about:crashes", "tab");
- return true;
- }
- }
- ];
- nb.appendNotification(PluralForm.get(count,
- win.gNavigatorBundle.getString("pendingCrashReports.label")).replace("#1", count),
- "pending-crash-reports",
- "chrome://browser/skin/tab-crashed.svg",
- nb.PRIORITY_INFO_HIGH, buttons);
- }
- },
- function onError(err) {
- Cu.reportError(err);
- }
- );
- }
- },
-
_onSafeModeRestart: function BG_onSafeModeRestart() {
// prompt the user to confirm
let strings = gBrowserBundle;
let promptTitle = strings.GetStringFromName("safeModeRestartPromptTitle");
let promptMessage = strings.GetStringFromName("safeModeRestartPromptMessage");
let restartText = strings.GetStringFromName("safeModeRestartButton");
let buttonFlags = (Services.prompt.BUTTON_POS_0 *
Services.prompt.BUTTON_TITLE_IS_STRING) +
@@ -1065,20 +1010,16 @@ BrowserGlue.prototype = {
if (removalSuccessful && uninstalledValue == "True") {
this._resetProfileNotification("uninstall");
}
}
}
this._checkForOldBuildUpdates();
- if (!AppConstants.RELEASE_BUILD) {
- this.checkForPendingCrashReports();
- }
-
CaptivePortalWatcher.init();
AutoCompletePopup.init();
this._firstWindowTelemetry(aWindow);
this._firstWindowLoaded();
},
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -718,17 +718,16 @@ tabgroups.migration.anonGroup = Group %S
tabgroups.migration.tabGroupBookmarkFolderName = Bookmarked Tab Groups
# LOCALIZATION NOTE (pendingCrashReports.label): Semi-colon list of plural forms
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 is the number of pending crash reports
pendingCrashReports.label = You have an unsubmitted crash report;You have #1 unsubmitted crash reports
pendingCrashReports.viewAll = View
pendingCrashReports.submitAll = Submit
-pendingCrashReports.ignoreAll = Ignore
decoder.noCodecs.button = Learn how
decoder.noCodecs.accesskey = L
decoder.noCodecs.message = To play video, you may need to install Microsoft’s Media Feature Pack.
decoder.noCodecsVista.message = To play video, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
decoder.noCodecsXP.message = To play video, you may need to enable Adobe’s Primetime Content Decryption Module.
decoder.noCodecsLinux.message = To play video, you may need to install the required video codecs.
decoder.noHWAcceleration.message = To improve video quality, you may need to install Microsoft’s Media Feature Pack.
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -3,29 +3,46 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
-this.EXPORTED_SYMBOLS = [ "TabCrashHandler", "PluginCrashReporter" ];
+this.EXPORTED_SYMBOLS = [ "TabCrashHandler",
+ "PluginCrashReporter",
+ "UnsubmittedCrashHandler" ];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
"resource://gre/modules/CrashSubmit.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RemotePages",
"resource://gre/modules/RemotePageManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+ "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
+ "resource:///modules/RecentWindow.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+ "resource://gre/modules/PluralForm.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
+ const url = "chrome://browser/locale/browser.properties";
+ return Services.strings.createBundle(url);
+});
+
+// We don't process crash reports older than 28 days, so don't bother
+// submitting them
+const PENDING_CRASH_REPORT_DAYS = 28;
this.TabCrashHandler = {
_crashedTabCount: 0,
get prefs() {
delete this.prefs;
return this.prefs = Services.prefs.getBranch("browser.tabs.crashReporting.");
},
@@ -314,16 +331,191 @@ this.TabCrashHandler = {
if (!this.childMap) {
return null;
}
return this.childMap.get(this.browserMap.get(browser.permanentKey));
},
}
+/**
+ * This component is responsible for scanning the pending
+ * crash report directory for reports, and (if enabled), to
+ * prompt the user to submit those reports.
+ */
+this.UnsubmittedCrashHandler = {
+ init() {
+ if (this.initialized) {
+ return;
+ }
+
+ this.initialized = true;
+
+ let pref = "browser.crashReports.unsubmittedCheck.enabled";
+ let shouldCheck = Services.prefs.getBoolPref(pref);
+
+ if (shouldCheck) {
+ Services.obs.addObserver(this, "browser-delayed-startup-finished",
+ false);
+ }
+ },
+
+ observe(subject, topic, data) {
+ if (topic != "browser-delayed-startup-finished") {
+ return;
+ }
+
+ Services.obs.removeObserver(this, topic);
+ this.checkForUnsubmittedCrashReports();
+ },
+
+ /**
+ * Scans the profile directory for unsubmitted crash reports
+ * within the past PENDING_CRASH_REPORT_DAYS days. If it
+ * finds any, it will, if necessary, attempt to open a notification
+ * bar to prompt the user to submit them.
+ *
+ * @returns Promise
+ * Resolves after it tries to append a notification on
+ * the most recent browser window. If a notification
+ * cannot be shown, will resolve anyways.
+ */
+ checkForUnsubmittedCrashReports: Task.async(function*() {
+ let dateLimit = new Date();
+ dateLimit.setDate(dateLimit.getDate() - PENDING_CRASH_REPORT_DAYS);
+
+ let reportIDs = [];
+ try {
+ reportIDs = yield CrashSubmit.pendingIDsAsync(dateLimit);
+ } catch (e) {
+ Cu.reportError(e);
+ return;
+ }
+
+ if (reportIDs.length) {
+ this.showPendingSubmissionsNotification(reportIDs);
+ }
+ }),
+
+ /**
+ * Given an array of unsubmitted crash report IDs, try to open
+ * up a notification asking the user to submit them.
+ *
+ * @param reportIDs (Array<string>)
+ * The Array of report IDs to offer the user to send.
+ */
+ showPendingSubmissionsNotification(reportIDs) {
+ let count = reportIDs.length;
+ if (!count) {
+ return;
+ }
+
+ let messageTemplate =
+ gNavigatorBundle.GetStringFromName("pendingCrashReports.label");
+
+ let message = PluralForm.get(count, messageTemplate).replace("#1", count);
+
+ CrashNotificationBar.show({
+ notificationID: "pending-crash-reports",
+ message,
+ reportIDs,
+ });
+ },
+};
+
+this.CrashNotificationBar = {
+ /**
+ * Attempts to show a notification bar to the user in the most
+ * recent browser window asking them to submit some crash report
+ * IDs. If a notification cannot be shown (for example, there
+ * is no browser window), this method exits silently.
+ *
+ * The notification will allow the user to submit their crash
+ * reports. If the user dismissed the notification, the crash
+ * reports will be marked to be ignored (though they can
+ * still be manually submitted via about:crashes).
+ *
+ * @param JS Object
+ * An Object with the following properties:
+ *
+ * notificationID (string)
+ * The ID for the notification to be opened.
+ *
+ * message (string)
+ * The message to be displayed in the notification.
+ *
+ * reportIDs (Array<string>)
+ * The array of report IDs to offer to the user.
+ */
+ show({ notificationID, message, reportIDs }) {
+ let chromeWin = RecentWindow.getMostRecentBrowserWindow();
+ if (!chromeWin) {
+ // Can't show a notification in this case. We'll hopefully
+ // get another opportunity to have the user submit their
+ // crash reports later.
+ return;
+ }
+
+ let nb = chromeWin.document.getElementById("global-notificationbox");
+ let notification = nb.getNotificationWithValue(notificationID);
+ if (notification) {
+ return;
+ }
+
+ let buttons = [{
+ label: gNavigatorBundle.GetStringFromName("pendingCrashReports.submitAll"),
+ callback: () => {
+ this.submitReports(reportIDs);
+ },
+ },
+ {
+ label: gNavigatorBundle.GetStringFromName("pendingCrashReports.viewAll"),
+ callback: function() {
+ chromeWin.openUILinkIn("about:crashes", "tab");
+ return true;
+ },
+ }];
+
+ let eventCallback = (eventType) => {
+ if (eventType == "dismissed") {
+ // The user intentionally dismissed the notification,
+ // which we interpret as meaning that they don't care
+ // to submit the reports. We'll ignore these particular
+ // reports going forward.
+ reportIDs.forEach(function(reportID) {
+ CrashSubmit.ignore(reportID);
+ });
+ }
+ };
+
+ nb.appendNotification(message, notificationID,
+ "chrome://browser/skin/tab-crashed.svg",
+ nb.PRIORITY_INFO_HIGH, buttons,
+ eventCallback);
+ },
+
+ /**
+ * Attempt to submit reports to the crash report server. Each
+ * report will have the "SubmittedFromInfobar" extra key set
+ * to true.
+ *
+ * @param reportIDs (Array<string>)
+ * The array of reportIDs to submit.
+ */
+ submitReports(reportIDs) {
+ for (let reportID of reportIDs) {
+ CrashSubmit.submit(reportID, {
+ extraExtraKeyVals: {
+ "SubmittedFromInfobar": true,
+ },
+ });
+ }
+ },
+};
+
this.PluginCrashReporter = {
/**
* Makes the PluginCrashReporter ready to hear about and
* submit crash reports.
*/
init() {
if (this.initialized) {
return;