Bug 886794 - Add new crash report ui. r=fryn
authorJim Mathies <jmathies@mozilla.com>
Mon, 01 Jul 2013 17:09:26 -0500
changeset 137068 c4b623117894c17a8afbf0586eda9d4ee1939ace
parent 137067 73923e44fb3fd345294634f3aea2801f9219d17b
child 137069 eb1f30cd3b3db748e91cc02678e6c311cf6844ad
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersfryn
bugs886794
milestone25.0a1
Bug 886794 - Add new crash report ui. r=fryn
browser/metro/base/content/browser-ui.js
browser/metro/base/content/browser.xul
browser/metro/base/content/prompt/crash.xul
browser/metro/base/content/prompt/prompt.js
browser/metro/base/jar.mn
browser/metro/locales/en-US/chrome/crashprompt.dtd
browser/metro/locales/en-US/chrome/crashprompt.properties
browser/metro/locales/jar.mn
browser/metro/profile/metro.js
browser/metro/theme/platform.css
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -151,19 +151,18 @@ var BrowserUI = {
 #endif
       } catch(ex) {
         Util.dumpLn("Exception in delay load module:", ex.message);
       }
 
       BrowserUI._pullDesktopControlledPrefs();
 
       // check for left over crash reports and submit them if found.
-      if (BrowserUI.startupCrashCheck()) {
-        Browser.selectedTab = BrowserUI.newOrSelectTab("about:crash");
-      }
+      BrowserUI.startupCrashCheck();
+
       Util.dumpLn("* delay load complete.");
     }, false);
 
 #ifndef MOZ_OFFICIAL_BRANDING
     setTimeout(function() {
       let startup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).getStartupInfo();
       for (let name in startup) {
         if (name != "process")
@@ -207,34 +206,53 @@ var BrowserUI = {
    */
 
   get CrashSubmit() {
     delete this.CrashSubmit;
     Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
     return this.CrashSubmit;
   },
 
+  get lastCrashID() {
+    return Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime).lastRunCrashID;
+  },
+
   startupCrashCheck: function startupCrashCheck() {
 #ifdef MOZ_CRASHREPORTER
-    if (!Services.prefs.getBoolPref("app.reportCrashes"))
-      return false;
-    if (CrashReporter.enabled) {
-      var lastCrashID = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).lastRunCrashID;
-      if (lastCrashID && lastCrashID.length) {
-        Util.dumpLn("Submitting last crash id:", lastCrashID);
-        try {
-          this.CrashSubmit.submit(lastCrashID);
-        } catch (ex) {
-          Util.dumpLn(ex);
-        }
-        return true;
-      }
+    if (!CrashReporter.enabled) {
+      return;
+    }
+    let lastCrashID = this.lastCrashID;
+    if (!lastCrashID || !lastCrashID.length) {
+      return;
+    }
+    let shouldReport = Services.prefs.getBoolPref("app.crashreporter.autosubmit");
+    let didPrompt = Services.prefs.getBoolPref("app.crashreporter.prompted");
+
+    if (!shouldReport && !didPrompt) {
+      // We have a crash to submit, we haven't prompted for approval yet,
+      // and the auto-submit pref is false, prompt. The dialog will call
+      // startupCrashCheck again if the user approves.
+      Services.prefs.setBoolPref("app.crashreporter.prompted", true);
+      DialogUI.importModal(document, "chrome://browser/content/prompt/crash.xul");
+      return;
+    }
+
+    // We've already prompted, return if the user doesn't want to report.
+    if (!shouldReport && didPrompt) {
+      return;
+    }
+
+    Util.dumpLn("Submitting last crash id:", lastCrashID);
+    try {
+      this.CrashSubmit.submit(lastCrashID);
+    } catch (ex) {
+      Util.dumpLn(ex);
     }
 #endif
-    return false;
   },
 
 
   /*********************************
    * Navigation
    */
 
   update: function(aState) {
@@ -1634,19 +1652,28 @@ var DialogUI = {
     let parentNode = contentMenuContainer.parentNode;
 
     // emit DOMWillOpenModalDialog event
     let event = document.createEvent("Events");
     event.initEvent("DOMWillOpenModalDialog", true, false);
     let dispatcher = aParent || getBrowser();
     dispatcher.dispatchEvent(event);
 
-    // create a full-screen semi-opaque box as a background
-    let back = document.createElement("box");
+    // create a full-screen semi-opaque box as a background or reuse
+    // the existing one.
+    let back = document.getElementById("dialog-modal-block");
+    if (!back) {
+      back = document.createElement("box");
+    } else {
+      while (back.hasChildNodes()) {
+        back.removeChild(back.firstChild);
+      }
+    }
     back.setAttribute("class", "modal-block");
+    back.setAttribute("id", "dialog-modal-block");
     dialog = back.appendChild(document.importNode(doc, true));
     parentNode.insertBefore(back, contentMenuContainer);
 
     dialog.arguments = aArguments;
     dialog.parent = aParent;
     return dialog;
   },
 
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -496,17 +496,17 @@
               <image src="chrome://browser/skin/images/throbber.png" />
             </hbox>
             <description id="clear-notification-done">&clearPrivateData.done;</description>
           </deck>
         </hbox>
       </settings>
       <setting pref="signon.rememberSignons" title="&optionsHeader.privacy.passwords.label;" type="bool"/>
       <settings id="prefs-reporting" label="&optionsHeader.reporting.title;">
-        <setting pref="app.reportCrashes" type="bool" title="&optionsHeader.reporting.crashes.label;" oncommand="BrowserUI.crashReportingPrefChanged(this.value);"/>
+        <setting pref="app.crashreporter.autosubmit" type="bool" title="&optionsHeader.reporting.crashes.label;" oncommand="BrowserUI.crashReportingPrefChanged(this.value);"/>
       </settings>
       <settings id="prefs-dnt" label="&doNotTrack.title;">
         <description>&doNotTrack.desc;</description>
         <setting id="prefs-dnt-value" pref="privacy.donottrackheader.value" onpreferencechanged="PreferencesPanelView.onDNTPreferenceChanged()" type="radio" >
           <radiogroup id="prefs-dnt-options">
             <radio id="prefs-dnt-notrack" label="&doNotTrack.options.trackingNotOkay;" value="1"/>
             <radio id="prefs-dnt-nopref" label="&doNotTrack.options.noPreference;" value="-1"/>
             <radio id="prefs-dnt-oktrack" label="&doNotTrack.options.trackingOkay;" value="0"/>
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/content/prompt/crash.xul
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<!DOCTYPE prompt [
+  <!ENTITY % promptDTD SYSTEM "chrome://browser/locale/prompt.dtd">
+  <!ENTITY % commonDialogDTD SYSTEM "chrome://global/locale/commonDialog.dtd">
+  <!ENTITY % crashpromptDTD SYSTEM "chrome://browser/locale/crashprompt.dtd">
+  %crashpromptDTD;
+  %promptDTD;
+  %commonDialogDTD;
+]>
+
+<dialog id="crash-prompt-dialog"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="this.CrashPrompt.load();"
+        script="chrome://browser/content/prompt/prompt.js">
+
+  <keyset>
+    <key keycode="VK_RETURN" command="cmd_ok"/>
+    <key keycode="VK_ESCAPE" command="cmd_cancel"/>
+  </keyset>
+
+  <commandset>
+    <command id="cmd_ok" oncommand="CrashPrompt.accept();"/>
+    <command id="cmd_cancel" oncommand="CrashPrompt.refuse();"/>
+  </commandset>
+
+  <!-- user query and options -->
+  <vbox id="crash-privacy-options" class="prompt-inner">
+    <vbox class="prompt-header" flex="1">
+      <hbox class="prompt-title">
+        <description>&crashprompt.dialog.title;</description>
+      </hbox>
+      <description class="prompt-message" id="privacy-crash-blurb"/>
+      <hbox flex="1">
+        <description class="text-link crash-link" flex="1"
+          onclick="document.getElementById('crash-prompt-dialog').CrashPrompt.privacy();">&crashprompt.dialog.privacyLink;</description>
+      </hbox>
+    </vbox>
+    <hbox class="prompt-buttons">
+      <button id="crash-button-accept" class="button-default" default="true" command="cmd_ok">&crashprompt.dialog.acceptbutton;</button>
+      <separator/>
+      <button id="crash-button-refuse" command="cmd_cancel">&crashprompt.dialog.refusebutton;</button>
+    </hbox>
+  </vbox>
+
+  <!-- long winded privacy statement, hidden by default -->
+  <vbox id="crash-privacy-statement" class="prompt-inner-statement" hidden="true">
+    <hbox flex="1">
+      <vbox class="crash-privacy-back-button" onclick="document.getElementById('crash-prompt-dialog').CrashPrompt.privacyBack();"/>
+      <scrollbox orient="vertical" class="crash-privacy-statement-scroller" flex="1">
+        <hbox class="prompt-title">
+          <description>&crashprompt.dialog.title2;</description>
+        </hbox>
+        <description class="crash-message">&crashprompt.dialog.statement1;</description>
+        <separator/>
+        <description class="crash-message">&crashprompt.dialog.statement2;</description>
+      </scrollbox>
+    </hbox>
+  </vbox>
+</dialog>
--- a/browser/metro/base/content/prompt/prompt.js
+++ b/browser/metro/base/content/prompt/prompt.js
@@ -65,8 +65,57 @@ const PromptHelper = {
     this.closeDialog(confirm, "prompt-select-dialog");
   },
 
   onCloseSelect: function(dialog) {
     if (dialog.arguments)
       dialog.arguments.selection.value = document.getElementById("prompt-select-list").selectedIndex;
   }
 };
+
+var CrashPrompt = {
+  load: function () {
+    if (this._preventRecurse) {
+      throw new Exception("CrashPrompt recursion error!!");
+      return;
+    }
+    // populate the text blurb with localized text
+    let brandName = Services.strings.createBundle("chrome://branding/locale/brand.properties")
+                                    .GetStringFromName("brandShortName");
+    let vendorName = Services.strings.createBundle("chrome://branding/locale/brand.properties")
+                                     .GetStringFromName("vendorShortName");
+    let crashBundle = Services.strings.createBundle("chrome://browser/locale/crashprompt.properties");
+    let message = crashBundle.formatStringFromName("crashprompt.messagebody", [brandName, vendorName, brandName], 3);
+    let descElement = document.getElementById("privacy-crash-blurb");
+    descElement.textContent = message;
+
+    // focus the send button
+    document.getElementById('crash-button-accept').focus();
+  },
+
+  accept: function() {
+    document.getElementById("crash-prompt-dialog").close();
+    Services.prefs.setBoolPref('app.crashreporter.autosubmit', true);
+    Services.prefs.setBoolPref('app.crashreporter.prompted', true);
+    BrowserUI.crashReportingPrefChanged(true);
+
+    this._preventRecurse = true;
+    BrowserUI.startupCrashCheck();
+    this._preventRecurse = false;
+  },
+
+  refuse: function() {
+    document.getElementById("crash-prompt-dialog").close();
+    Services.prefs.setBoolPref('app.crashreporter.autosubmit', false);
+    Services.prefs.setBoolPref('app.crashreporter.prompted', true);
+    BrowserUI.crashReportingPrefChanged(false);
+  },
+
+  privacy: function() {
+    document.getElementById("crash-privacy-options").setAttribute("hidden", true);
+    document.getElementById("crash-privacy-statement").setAttribute("hidden", false);
+  },
+
+  privacyBack: function() {
+    document.getElementById("crash-privacy-options").setAttribute("hidden", false);
+    document.getElementById("crash-privacy-statement").setAttribute("hidden", true);
+  },
+};
--- a/browser/metro/base/jar.mn
+++ b/browser/metro/base/jar.mn
@@ -29,16 +29,17 @@ chrome.jar:
   content/bindings/popup.xml                   (content/bindings/popup.xml)
 
   content/prompt/alert.xul                     (content/prompt/alert.xul)
   content/prompt/confirm.xul                   (content/prompt/confirm.xul)
   content/prompt/prompt.xul                    (content/prompt/prompt.xul)
   content/prompt/promptPassword.xul            (content/prompt/promptPassword.xul)
   content/prompt/select.xul                    (content/prompt/select.xul)
   content/prompt/prompt.js                     (content/prompt/prompt.js)
+  content/prompt/crash.xul                     (content/prompt/crash.xul)
 
   content/helperui/AlertsHelper.js             (content/helperui/AlertsHelper.js)
   content/helperui/IndexedDB.js                (content/helperui/IndexedDB.js)
   content/helperui/MenuUI.js                   (content/helperui/MenuUI.js)
   content/helperui/OfflineApps.js              (content/helperui/OfflineApps.js)
   content/helperui/SelectHelperUI.js           (content/helperui/SelectHelperUI.js)
   content/helperui/SelectionHelperUI.js        (content/helperui/SelectionHelperUI.js)
   content/helperui/ChromeSelectionHandler.js   (content/helperui/ChromeSelectionHandler.js)
new file mode 100644
--- /dev/null
+++ b/browser/metro/locales/en-US/chrome/crashprompt.dtd
@@ -0,0 +1,12 @@
+<!-- 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 crashprompt.dialog.title           "Would you like to send Mozilla crash reports?">
+<!ENTITY crashprompt.dialog.privacyLink     "Privacy statement of crash-reporting feature">
+<!ENTITY crashprompt.dialog.acceptbutton    "Send reports">
+<!ENTITY crashprompt.dialog.refusebutton    "Don't send">
+<!ENTITY crashprompt.dialog.title2          "Privacy statement of crash-reporting feature">
+<!ENTITY crashprompt.dialog.statement1      "Firefox has a crash-reporting feature that sends a report to Mozilla when Firefox crashes. Mozilla uses information in crash reports to diagnose and correct problems in Firefox that cause crashes. Though this feature starts automatically after Firefox crashes, it does not send information to Mozilla until you explicitly authorize it to do so. By default, this feature sends a variety of Non-Personal Information to Mozilla, including the stack trace (a detailed description of which parts of the Firefox code were active at the time of the crash) and the type of computer you are using. Additional information is collected by the crash reporting feature. Which crash reporting feature is used and what additional information collected by Firefox depends on which version of Firefox you are using.">
+<!ENTITY crashprompt.dialog.statement2      "For current versions of Firefox, “Firefox Crash Reporter” is Firefox’s crash reporting feature. With this feature, you may have the option to include Personal Information (including your email address), Potentially Personal Information (including your IP address and the URL of the site you were visiting when Firefox crashed), and a comment. Firefox Crash Reporter also sends a list of all add-ons that you were using at the time of the crash, the time since (i) the last crash, (ii) the last install, and (iii) the start-up of the program. Mozilla only makes Non-Personal Information (i.e., generic information about your computer, the stack trace, and any comment given by the user) available in the public reports available online at http://crash-stats.mozilla.com/.">
new file mode 100644
--- /dev/null
+++ b/browser/metro/locales/en-US/chrome/crashprompt.properties
@@ -0,0 +1,6 @@
+# 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/.
+
+# LOCALIZATION NOTE: '%S' is short brand name
+crashprompt.messagebody=We are sorry, %S just recovered from a crash. Sending crash reports will help %S make %S more stable and secure. You can always change your preference in Settings/Options.
--- a/browser/metro/locales/jar.mn
+++ b/browser/metro/locales/jar.mn
@@ -16,16 +16,18 @@
   locale/browser/preferences.dtd          (%chrome/preferences.dtd)
   locale/browser/aboutPanel.dtd           (%chrome/aboutPanel.dtd)
   locale/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/browser/sync.dtd                 (%chrome/sync.dtd)
   locale/browser/sync.properties          (%chrome/sync.properties)
   locale/browser/passwordmgr.properties   (%chrome/passwordmgr.properties)
   locale/browser/prompt.dtd               (%chrome/prompt.dtd)
   locale/browser/phishing.dtd             (%chrome/phishing.dtd)
+  locale/browser/crashprompt.dtd          (%chrome/crashprompt.dtd)
+  locale/browser/crashprompt.properties   (%chrome/crashprompt.properties)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
   locale/browser/bookmarks.json           (bookmarks.json)
 
 #
 # Browser jar resources
 #
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -6,18 +6,20 @@
 
 #ifdef DEBUG
 // disable content and content script caching
 pref("nglayout.debug.disable_xul_cache", true);
 pref("nglayout.debug.disable_xul_fastload", true);
 pref("devtools.errorconsole.enabled", true);
 #endif
 
-// Enable headless crash reporting by default
-pref("app.reportCrashes", true);
+// Automatically submit crash reports
+pref("app.crashreporter.autosubmit", false);
+// Has the user been prompted about crash reporting?
+pref("app.crashreporter.prompted", false);
 
 // Debug prefs, see input.js
 pref("metro.debug.treatmouseastouch", false);
 pref("metro.debug.colorizeInputOverlay", false);
 pref("metro.debug.selection.displayRanges", false);
 pref("metro.debug.selection.dumpRanges", false);
 pref("metro.debug.selection.dumpEvents", false);
 
--- a/browser/metro/theme/platform.css
+++ b/browser/metro/theme/platform.css
@@ -487,32 +487,69 @@ dialog > .prompt-inner {
 }
 
 /* Authentication dialogs do not have a title */
 .prompt-title:empty {
   display: none;
 }
 
 .prompt-message {
+  padding-top: @metro_spacing_normal@;
   text-align: start;
   font-size: @metro_font_normal@;
 }
 
 .prompt-buttons {
   -moz-box-pack: end;
   text-align: end;
   padding: @metro_spacing_normal@ 0;
 }
 
 .prompt-edit {
   margin: @margin_xnormal@;
   font-size: @font_normal@;
   text-align: start;
 }
 
+/* additional styles for crash prompt dialog */
+
+/* specific height and wider for the long privacy statement */
+.prompt-inner-statement {
+  margin-top: @metro_spacing_xnormal@;
+  margin-bottom: @metro_spacing_xnormal@;
+  height: 450px;
+  width: 800px;
+}
+
+.crash-message {
+  text-align: justify;
+  margin-top: 5px;
+  margin-bottom: @metro_spacing_small@;
+  font-size: @metro_font_normal@;
+}
+
+.crash-link {
+  margin-top: @metro_spacing_small@;
+}
+
+/* temp - bug 887176 */
+.crash-privacy-back-button {
+  width: 26px;
+  margin-top: 22px; /* align with title text */
+  -moz-margin-end: 15px;
+  background-image: url(chrome://browser/skin/images/appbar-stop.png);
+  background-origin: border-box;
+  background-position: right 0 top 0;
+  background-repeat: no-repeat;
+}
+
+.crash-privacy-statement-scroller {
+  overflow: auto;
+}
+
 /* Arrowbox ---------------------------------------------------------------- */
 
 arrowbox {
   -moz-appearance: none;
   background: transparent;
   border: none;
 }