Bug 358082 - Improve Mac Crashreporter UI, patch by dcamp, r=luser+me
authorbenjamin@smedbergs.us
Tue, 24 Jul 2007 18:06:05 -0700
changeset 3900 773e59d93d24fa890fd08a84d3d091fe43f651b8
parent 3899 4f1c8636e4bec663256d7ea3aeacf0e8677ea35c
child 3901 609f929ab0c3d4f617bbe25afaa2847a875cc113
push idunknown
push userunknown
push dateunknown
reviewersluser
bugs358082
milestone1.9a7pre
Bug 358082 - Improve Mac Crashreporter UI, patch by dcamp, r=luser+me
toolkit/crashreporter/client/crashreporter.cpp
toolkit/crashreporter/client/crashreporter.h
toolkit/crashreporter/client/crashreporter.ini
toolkit/crashreporter/client/crashreporter.rc
toolkit/crashreporter/client/crashreporter_osx.h
toolkit/crashreporter/client/crashreporter_osx.mm
toolkit/crashreporter/client/crashreporter_win.cpp
toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib
toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
toolkit/crashreporter/client/resource.h
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
--- a/toolkit/crashreporter/client/crashreporter.cpp
+++ b/toolkit/crashreporter/client/crashreporter.cpp
@@ -34,72 +34,103 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "crashreporter.h"
 
+#ifdef _MSC_VER
 // Disable exception handler warnings.
-#pragma warning( disable : 4530 )
+# pragma warning( disable : 4530 )
+#endif
+
 #include <fstream>
 #include <sstream>
 
 using std::string;
 using std::istream;
 using std::ifstream;
 using std::istringstream;
+using std::ostringstream;
 using std::ostream;
 using std::ofstream;
+using std::vector;
 
 StringTable  gStrings;
 int          gArgc;
 const char** gArgv;
 
 static string       gDumpFile;
 static string       gExtraFile;
 static string       gSettingsPath;
 
 static string kExtraDataExtension = ".extra";
 
-static bool ReadStrings(istream &in, StringTable &strings)
+static string Unescape(const string& str)
+{
+  string ret;
+  for (string::const_iterator iter = str.begin();
+       iter != str.end();
+       iter++) {
+    if (*iter == '\\') {
+      iter++;
+      if (*iter == '\\'){
+        ret.push_back('\\');
+      } else if (*iter == 'n') {
+        ret.push_back('\n');
+      } else if (*iter == 't') {
+        ret.push_back('\t');
+      }
+    } else {
+      ret.push_back(*iter);
+    }
+  }
+
+  return ret;
+}
+
+static bool ReadStrings(istream& in, StringTable& strings, bool unescape)
 {
   string currentSection;
   while (!in.eof()) {
     string line;
     std::getline(in, line);
     int sep = line.find('=');
     if (sep >= 0) {
       string key, value;
       key = line.substr(0, sep);
       value = line.substr(sep + 1);
+      if (unescape)
+        value = Unescape(value);
       strings[key] = value;
     }
   }
 
   return true;
 }
 
 static bool ReadStringsFromFile(const string& path,
-                                StringTable& strings)
+                                StringTable& strings,
+                                bool unescape)
 {
   ifstream f(path.c_str(), std::ios::in);
   if (!f.is_open()) return false;
 
-  return ReadStrings(f, strings);
+  return ReadStrings(f, strings, unescape);
 }
 
 static bool ReadConfig()
 {
   string iniPath;
   if (!UIGetIniPath(iniPath))
     return false;
 
-  if (!ReadStringsFromFile(iniPath, gStrings))
+  if (!ReadStringsFromFile(iniPath, gStrings, true))
     return false;
 
   return true;
 }
 
 static string GetExtraDataFilename(const string& dumpfile)
 {
   string filename(dumpfile);
@@ -120,41 +151,38 @@ static string Basename(const string& fil
     return file;
 }
 
 static bool MoveCrashData(const string& toDir,
                           string& dumpfile,
                           string& extrafile)
 {
   if (!UIEnsurePathExists(toDir)) {
-    UIError(toDir.c_str());
     return false;
   }
 
   string newDump = toDir + UI_DIR_SEPARATOR + Basename(dumpfile);
   string newExtra = toDir + UI_DIR_SEPARATOR + Basename(extrafile);
 
   if (!UIMoveFile(dumpfile, newDump) ||
       !UIMoveFile(extrafile, newExtra)) {
-    UIError(dumpfile.c_str());
-    UIError(newDump.c_str());
     return false;
   }
 
   dumpfile = newDump;
   extrafile = newExtra;
 
   return true;
 }
 
 static bool AddSubmittedReport(const string& serverResponse)
 {
   StringTable responseItems;
   istringstream in(serverResponse);
-  ReadStrings(in, responseItems);
+  ReadStrings(in, responseItems, false);
 
   if (responseItems.find("CrashID") == responseItems.end())
     return false;
 
   string submittedDir =
     gSettingsPath + UI_DIR_SEPARATOR + "submitted";
   if (!UIEnsurePathExists(submittedDir)) {
     return false;
@@ -185,20 +213,23 @@ static bool AddSubmittedReport(const str
   return true;
 
 }
 
 bool CrashReporterSendCompleted(bool success,
                                 const string& serverResponse)
 {
   if (success) {
-    if (!gDumpFile.empty())
-      UIDeleteFile(gDumpFile);
-    if (!gExtraFile.empty())
-      UIDeleteFile(gExtraFile);
+    const char* noDelete = getenv("MOZ_CRASHREPORTER_NO_DELETE_DUMP");
+    if (!noDelete || *noDelete == '\0') {
+      if (!gDumpFile.empty())
+        UIDeleteFile(gDumpFile);
+      if (!gExtraFile.empty())
+        UIDeleteFile(gExtraFile);
+    }
 
     return AddSubmittedReport(serverResponse);
   }
   return true;
 }
 
 int main(int argc, const char** argv)
 {
@@ -223,17 +254,17 @@ int main(int argc, const char** argv)
   } else {
     gExtraFile = GetExtraDataFilename(gDumpFile);
     if (gExtraFile.empty()) {
       UIError("Couldn't get extra data filename");
       return 0;
     }
 
     StringTable queryParameters;
-    if (!ReadStringsFromFile(gExtraFile, queryParameters)) {
+    if (!ReadStringsFromFile(gExtraFile, queryParameters, true)) {
       UIError("Couldn't read extra data");
       return 0;
     }
 
     if (queryParameters.find("ProductName") == queryParameters.end()) {
       UIError("No product name specified");
       return 0;
     }
@@ -255,25 +286,60 @@ int main(int argc, const char** argv)
       UIError("Couldn't move crash data");
       return 0;
     }
 
     string sendURL = queryParameters["ServerURL"];
     // we don't need to actually send this
     queryParameters.erase("ServerURL");
 
+    vector<string> restartArgs;
+
+    ostringstream paramName;
+    int i = 0;
+    paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
+    const char *param = getenv(paramName.str().c_str());
+    while (param && *param) {
+      restartArgs.push_back(param);
+
+      paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
+      param = getenv(paramName.str().c_str());
+    };
+
     // allow override of the server url via environment variable
     //XXX: remove this in the far future when our robot
     // masters force everyone to use XULRunner
     char* urlEnv = getenv("MOZ_CRASHREPORTER_URL");
     if (urlEnv && *urlEnv) {
       sendURL = urlEnv;
     }
 
-    UIShowCrashUI(gDumpFile, queryParameters, sendURL);
+    // rewrite some UI strings with the values from the query parameters
+    char buf[4096];
+    UI_SNPRINTF(buf, sizeof(buf),
+                gStrings[ST_RESTART].c_str(),
+                product.c_str());
+    gStrings[ST_RESTART] = buf;
+
+    UI_SNPRINTF(buf, sizeof(buf),
+                gStrings[ST_CRASHREPORTERDESCRIPTION].c_str(),
+                product.c_str());
+    gStrings[ST_CRASHREPORTERDESCRIPTION] = buf;
+
+    UI_SNPRINTF(buf, sizeof(buf),
+                gStrings[ST_CHECKSUBMIT].c_str(),
+                vendor.empty() ? "Mozilla" : vendor.c_str());
+    gStrings[ST_CHECKSUBMIT] = buf;
+
+    UI_SNPRINTF(buf, sizeof(buf),
+                gStrings[ST_CRASHREPORTERTITLE].c_str(),
+                vendor.empty() ? "Mozilla" : vendor.c_str());
+    gStrings[ST_CRASHREPORTERTITLE] = buf;
+
+    UIShowCrashUI(gDumpFile, queryParameters, sendURL, restartArgs);
   }
 
   UIShutdown();
 
   return 0;
 }
 
 #if defined(XP_WIN) && !defined(__GNUC__)
--- a/toolkit/crashreporter/client/crashreporter.h
+++ b/toolkit/crashreporter/client/crashreporter.h
@@ -1,16 +1,20 @@
 #ifndef CRASHREPORTER_H__
 #define CRASHREPORTER_H__
 
-#pragma warning( push )
+#ifdef _MSC_VER
+# pragma warning( push )
 // Disable exception handler warnings.
-#pragma warning( disable : 4530 )
+# pragma warning( disable : 4530 )
+#endif
+
 #include <string>
 #include <map>
+#include <vector>
 #include <stdlib.h>
 #include <stdio.h>
 
 #if defined(XP_WIN32)
 
 #include <windows.h>
 #define UI_SNPRINTF _snprintf
 #define UI_DIR_SEPARATOR "\\"
@@ -19,27 +23,26 @@
 
 #define UI_SNPRINTF snprintf
 #define UI_DIR_SEPARATOR "/"
 
 #endif
 
 typedef std::map<std::string, std::string> StringTable;
 
-#define ST_OK                       "Ok"
-#define ST_CANCEL                   "Cancel"
-#define ST_SEND                     "Send"
-#define ST_DONTSEND                 "DontSend"
+#define ST_CRASHREPORTERTITLE       "CrashReporterTitle"
+#define ST_CRASHREPORTERHEADER      "CrashReporterHeader"
+#define ST_CRASHREPORTERDESCRIPTION "CrashReporterDescription"
+#define ST_CRASHREPORTERDEFAULT     "CrashReporterDefault"
+#define ST_VIEWREPORT               "ViewReport"
+#define ST_EXTRAREPORTINFO          "ExtraReportInfo"
+#define ST_CHECKSUBMIT              "CheckSubmit"
+#define ST_CHECKEMAIL               "CheckEmail"
 #define ST_CLOSE                    "Close"
-#define ST_CRASHREPORTERTITLE       "CrashReporterTitle"
-#define ST_CRASHREPORTERDESCRIPTION "CrashReporterDescription"
-#define ST_RADIOENABLE              "RadioEnable"
-#define ST_RADIODISABLE             "RadioDisable"
-#define ST_SENDTITLE                "SendTitle"
-#define ST_SUBMITSUCCESS            "SubmitSuccess"
+#define ST_RESTART                  "Restart"
 #define ST_SUBMITFAILED             "SubmitFailed"
 
 //=============================================================================
 // implemented in crashreporter.cpp
 //=============================================================================
 
 extern StringTable  gStrings;
 extern int          gArgc;
@@ -57,22 +60,26 @@ bool UIInit();
 void UIShutdown();
 
 // Run the UI for when the app was launched without a dump file
 void UIShowDefaultUI();
 
 // Run the UI for when the app was launched with a dump file
 void UIShowCrashUI(const std::string& dumpfile,
                    const StringTable& queryParameters,
-                   const std::string& sendURL);
+                   const std::string& sendURL,
+                   const std::vector<std::string>& restartArgs);
 
 void UIError(const std::string& message);
 
 bool UIGetIniPath(std::string& path);
 bool UIGetSettingsPath(const std::string& vendor,
                        const std::string& product,
                        std::string& settingsPath);
 bool UIEnsurePathExists(const std::string& path);
 bool UIMoveFile(const std::string& oldfile, const std::string& newfile);
 bool UIDeleteFile(const std::string& oldfile);
 
-#pragma warning( pop )
+#ifdef _MSC_VER
+# pragma warning( pop )
 #endif
+
+#endif
--- a/toolkit/crashreporter/client/crashreporter.ini
+++ b/toolkit/crashreporter/client/crashreporter.ini
@@ -1,16 +1,15 @@
 ; This file is in the UTF-8 encoding
 [Strings]
-Ok=Ok
-Cancel=Cancel
-Send=Send
-DontSend=Don't Send
+CrashReporterTitle=%s Crash Reporter
+CrashReporterHeader=Crash! Bang! Boom!
+CrashReporterDescription=We're sorry, but %s hit an unexpected problem and crashed.  We'll try to restore your tabs and windows when it restarts.\n\nTo help us diagnose and repair this problem, you can send us a crash report.
+CrashReporterDefault=This application is run after a crash to report the problem to the application vendor.  It should not be run directly.
+ViewReport=View Report
+ExtraReportInfo=This report also contains information about the state of the application when it crashed.
+CheckSubmit=Submit crash report to %s
+CheckEmail=Email me when the problem is fixed
 Close=Close
-CrashReporterTitle=Mozilla Crash Reporter
-CrashReporterDescription=Crash reporting blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah
-RadioEnable=Enable Crash Reporting
-RadioDisable=Disable Crash Reporting
-SendTitle=Sending Crash Report...
-SubmitSuccess=Crash report submitted successfully
+Restart=Restart %s
 SubmitFailed=Failed to submit crash report
 CrashID=Crash ID: %s
 CrashDetailsURL=You can view details of this crash at %s
--- a/toolkit/crashreporter/client/crashreporter.rc
+++ b/toolkit/crashreporter/client/crashreporter.rc
@@ -1,124 +1,114 @@
-// Microsoft Visual C++ generated resource script.
-//
-#include "resource.h"
-
-#define APSTUDIO_READONLY_SYMBOLS
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 2 resource.
-//
-#include "winresrc.h"
-
-/////////////////////////////////////////////////////////////////////////////
-#undef APSTUDIO_READONLY_SYMBOLS
-
-/////////////////////////////////////////////////////////////////////////////
-// English (U.S.) resources
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif //_WIN32
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-
-1 TEXTINCLUDE 
-BEGIN
-    "resource.h\0"
-END
-
-2 TEXTINCLUDE 
-BEGIN
-    "#include ""winresrc.h""\r\n"
-    "\0"
-END
-
-3 TEXTINCLUDE 
-BEGIN
-    "\r\n"
-    "\0"
-END
-
-#endif    // APSTUDIO_INVOKED
-
-
-IDI_DIALOG ICON "crashreporter.ico"
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Dialog
-//
-
-IDD_ENABLEDIALOG DIALOGEX 0, 0, 322, 140
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | 
-    WS_VISIBLE | WS_CAPTION | WS_SYSMENU
-CAPTION "Crash Reporter"
-FONT 8, "MS Shell Dlg", 400, 0, 0x1
-BEGIN
-    CONTROL         "",IDC_DESCRIPTIONTEXT,"RichEdit20W",
-                    ES_AUTOHSCROLL | ES_READONLY | WS_BORDER | WS_TABSTOP | ES_MULTILINE,
-                    62,7,179,47
-    CONTROL         "Enable Crash Reporting",IDC_RADIOENABLE,"Button",
-                    BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,73,72,158,13
-    CONTROL         "Disable Crash Reporting",IDC_RADIODISABLE,"Button",
-                    BS_AUTORADIOBUTTON | WS_TABSTOP,73,86,158,13
-    DEFPUSHBUTTON   "OK",IDOK,125,119,50,14,WS_GROUP
-END
-
-IDD_SENDDIALOG DIALOGEX 0, 0, 186, 46
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | 
-    WS_VISIBLE | WS_CAPTION | WS_SYSMENU
-CAPTION "Sending Crash Report..."
-FONT 8, "MS Shell Dlg", 400, 0, 0x1
-BEGIN
-    PUSHBUTTON      "Cancel",IDCANCEL,67,25,50,14
-    CONTROL         "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7,7,172,14
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// DESIGNINFO
-//
-
-#ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO 
-BEGIN
-    IDD_ENABLEDIALOG, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 315
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 133
-    END
-
-    IDD_SENDDIALOG, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 179
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 39
-    END
-END
-#endif    // APSTUDIO_INVOKED
-
-#endif    // English (U.S.) resources
-/////////////////////////////////////////////////////////////////////////////
-
-
-
-#ifndef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// Generated from the TEXTINCLUDE 3 resource.
-//
-
-
-/////////////////////////////////////////////////////////////////////////////
-#endif    // not APSTUDIO_INVOKED
-
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""winresrc.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_DIALOG              ICON                    "crashreporter.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_SENDDIALOG DIALOGEX 0, 0, 239, 105
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Sending Crash Report..."
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    CONTROL         "",IDC_DESCRIPTIONTEXT,"RichEdit20A",ES_MULTILINE | ES_READONLY,7,7,226,12,WS_EX_TRANSPARENT
+    CONTROL         "view report",IDC_VIEWREPORTCHECK,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,12,24,54,12
+    CONTROL         "",IDC_VIEWREPORTTEXT,"RichEdit20A",ES_MULTILINE | ES_READONLY | NOT WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP,12,42,222,56
+    CONTROL         "submit a crash report to mozilla",IDC_SUBMITREPORTCHECK,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,42,222,10
+    CONTROL         "email me when the problem is fixed",IDC_EMAILMECHECK,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,54,222,10
+    EDITTEXT        IDC_EMAILTEXT,24,66,208,14,ES_AUTOHSCROLL
+    PUSHBUTTON      "close",IDC_CLOSEBUTTON,111,84,50,14
+    DEFPUSHBUTTON   "restart firefox",IDC_RESTARTBUTTON,165,84,68,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO 
+BEGIN
+    IDD_SENDDIALOG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 232
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 98
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
--- a/toolkit/crashreporter/client/crashreporter_osx.h
+++ b/toolkit/crashreporter/client/crashreporter_osx.h
@@ -42,48 +42,57 @@
 #include <Cocoa/Cocoa.h>
 #include "HTTPMultipartUpload.h"
 #include "crashreporter.h"
 
 @interface CrashReporterUI : NSObject
 {
     IBOutlet NSWindow* window;
 
-    /* Enabled view */
-    IBOutlet NSView* enableView;
-
+    /* Crash reporter view */
+    IBOutlet NSTextField* headerLabel;
     IBOutlet NSTextField* descriptionLabel;
-    IBOutlet NSButton* disableReportingButton;
-    IBOutlet NSButton* dontSendButton;
-    IBOutlet NSButton* sendButton;
-
-    /* Upload progress view */
-    IBOutlet NSView* uploadingView;
-
-    IBOutlet NSTextField* progressLabel;
-    IBOutlet NSProgressIndicator* progressBar;
+    IBOutlet NSButton* viewReportButton;
+    IBOutlet NSTextField* viewReportLabel;
+    IBOutlet NSScrollView* viewReportScrollView;
+    IBOutlet NSTextView* viewReportTextView;
+    IBOutlet NSButton* submitReportButton;
+    IBOutlet NSButton* emailMeButton;
+    IBOutlet NSTextField* emailText;
     IBOutlet NSButton* closeButton;
+    IBOutlet NSButton* restartButton;
 
     /* Error view */
     IBOutlet NSView* errorView;
+    IBOutlet NSTextField* errorHeaderLabel;
     IBOutlet NSTextField* errorLabel;
     IBOutlet NSButton* errorCloseButton;
 
     HTTPMultipartUpload *mPost;
 }
 
-- (void)showDefaultUI;
 - (void)showCrashUI:(const std::string&)dumpfile
     queryParameters:(const StringTable&)queryParameters
             sendURL:(const std::string&)sendURL;
 - (void)showErrorUI:(const std::string&)dumpfile;
+- (void)showReportInfo;
 
+- (IBAction)viewReportClicked:(id)sender;
 - (IBAction)closeClicked:(id)sender;
-- (IBAction)sendClicked:(id)sender;
+- (IBAction)closeAndSendClicked:(id)sender;
+- (IBAction)restartClicked:(id)sender;
+- (IBAction)emailMeClicked:(id)sender;
+
+- (void)controlTextDidChange:(NSNotification *)note;
 
-- (void)setView:(NSWindow *)w newView: (NSView *)v animate: (BOOL) animate;
+- (float)setStringFitVertically:(NSControl*)control
+                         string:(NSString*)str
+                   resizeWindow:(BOOL)resizeWindow;
+- (void)setView:(NSView*)v animate: (BOOL) animate;
+- (void)updateEmail;
+- (void)sendReport;
 - (bool)setupPost;
 - (void)uploadThread:(id)post;
 - (void)uploadComplete:(id)data;
 
 @end
 
 #endif
--- a/toolkit/crashreporter/client/crashreporter_osx.mm
+++ b/toolkit/crashreporter/client/crashreporter_osx.mm
@@ -40,117 +40,300 @@
 #import <CoreFoundation/CoreFoundation.h>
 #include "crashreporter.h"
 #include "crashreporter_osx.h"
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <fcntl.h>
 
 using std::string;
+using std::vector;
 
 static NSAutoreleasePool* gMainPool;
 static CrashReporterUI* gUI = 0;
 static string gDumpFile;
 static StringTable gQueryParameters;
 static string gSendURL;
-
+static vector<string> gRestartArgs;
 
 #define NSSTR(s) [NSString stringWithUTF8String:(s).c_str()]
 
-static NSString *
-Str(const char* aName)
+static NSString* Str(const char* aName)
 {
   string str = gStrings[aName];
   if (str.empty()) str = "?";
   return NSSTR(str);
 }
 
+static bool RestartApplication()
+{
+  char** argv = reinterpret_cast<char**>(
+    malloc(sizeof(char*) * (gRestartArgs.size() + 1)));
+
+  if (!argv) return false;
+
+  unsigned int i;
+  for (i = 0; i < gRestartArgs.size(); i++) {
+    argv[i] = (char*)gRestartArgs[i].c_str();
+  }
+  argv[i] = 0;
+
+  pid_t pid = fork();
+  if (pid == -1)
+    return false;
+  else if (pid == 0) {
+    (void)execv(argv[0], argv);
+    _exit(1);
+  }
+
+  free(argv);
+
+  return true;
+}
+
 @implementation CrashReporterUI
 
 -(void)awakeFromNib
 {
   gUI = self;
   [window center];
 
   [window setTitle:[[NSBundle mainBundle]
-                objectForInfoDictionaryKey:@"CFBundleName"]];
-  [descriptionLabel setStringValue:Str(ST_CRASHREPORTERDESCRIPTION)];
-  [disableReportingButton setTitle:Str(ST_RADIODISABLE)];
-
-  [closeButton setTitle:Str(ST_CLOSE)];
-  [errorCloseButton setTitle:Str(ST_CLOSE)];
-}
-
--(void)showDefaultUI
-{
-  [dontSendButton setFrame:[sendButton frame]];
-  [dontSendButton setTitle:Str(ST_CLOSE)];
-  [dontSendButton setKeyEquivalent:@"\r"];
-  [sendButton removeFromSuperview];
-
-  [window makeKeyAndOrderFront:nil];
+                      objectForInfoDictionaryKey:@"CFBundleName"]];
 }
 
 -(void)showCrashUI:(const string&)dumpfile
    queryParameters:(const StringTable&)queryParameters
            sendURL:(const string&)sendURL
 {
   gDumpFile = dumpfile;
   gQueryParameters = queryParameters;
   gSendURL = sendURL;
 
-  [sendButton setTitle:Str(ST_SEND)];
-  [sendButton setKeyEquivalent:@"\r"];
-  [dontSendButton setTitle:Str(ST_DONTSEND)];
+  [headerLabel setStringValue:Str(ST_CRASHREPORTERHEADER)];
+
+  [self setStringFitVertically:descriptionLabel
+                        string:Str(ST_CRASHREPORTERDESCRIPTION)
+                  resizeWindow:YES];
+  [viewReportLabel setStringValue:Str(ST_VIEWREPORT)];
+  [submitReportButton setTitle:Str(ST_CHECKSUBMIT)];
+  [emailMeButton setTitle:Str(ST_CHECKEMAIL)];
+  [closeButton setTitle:Str(ST_CLOSE)];
+
+  [viewReportScrollView retain];
+  [viewReportScrollView removeFromSuperview];
+
+  if (gRestartArgs.size() == 0) {
+    NSRect restartFrame = [restartButton frame];
+    [restartButton removeFromSuperview];
+    NSRect closeFrame = [closeButton frame];
+    closeFrame.origin.x = restartFrame.origin.x +
+      (restartFrame.size.width - closeFrame.size.width);
+    [closeButton setFrame: closeFrame];
+    [closeButton setKeyEquivalent:@"\r"];
+  } else {
+    [restartButton setTitle:Str(ST_RESTART)];
+    [restartButton setKeyEquivalent:@"\r"];
+  }
+
+  [self updateEmail];
+  [self showReportInfo];
 
   [window makeKeyAndOrderFront:nil];
 }
 
 -(void)showErrorUI:(const string&)message
 {
-  [errorLabel setStringValue: NSSTR(message)];
+  [self setView: errorView animate: NO];
+
+  [errorHeaderLabel setStringValue:Str(ST_CRASHREPORTERHEADER)];
+  [self setStringFitVertically:errorLabel
+                        string:NSSTR(message)
+                  resizeWindow:YES];
+  [errorCloseButton setTitle:Str(ST_CLOSE)];
+
+  [window makeKeyAndOrderFront:nil];
+}
+
+-(void)showReportInfo
+{
+  NSDictionary* boldAttr = [NSDictionary
+                            dictionaryWithObject:[NSFont boldSystemFontOfSize:0]
+                                          forKey:NSFontAttributeName];
+  NSDictionary* normalAttr = [NSDictionary
+                              dictionaryWithObject:[NSFont systemFontOfSize:0]
+                                            forKey:NSFontAttributeName];
+
+  [viewReportTextView setString:@""];
+  for (StringTable::iterator iter = gQueryParameters.begin();
+       iter != gQueryParameters.end();
+       iter++) {
+    [[viewReportTextView textStorage]
+     appendAttributedString: [[NSAttributedString alloc]
+                              initWithString:NSSTR(iter->first + ": ")
+                                  attributes:boldAttr]];
+    [[viewReportTextView textStorage]
+     appendAttributedString: [[NSAttributedString alloc]
+                              initWithString:NSSTR(iter->second + "\n")
+                                  attributes:normalAttr]];
+  }
 
-  [self setView: window newView: errorView animate: NO];
-  [window makeKeyAndOrderFront:nil];
+  [[viewReportTextView textStorage]
+   appendAttributedString: [[NSAttributedString alloc]
+                            initWithString:NSSTR("\n" + gStrings[ST_EXTRAREPORTINFO])
+                            attributes:normalAttr]];
+}
+
+-(IBAction)viewReportClicked:(id)sender
+{
+  NSRect frame = [window frame];
+  NSRect scrolledFrame = [viewReportScrollView frame];
+
+  float delta = scrolledFrame.size.height + 5; // FIXME
+
+  if ([viewReportButton state] == NSOnState) {
+    [[window contentView] addSubview:viewReportScrollView];
+  } else {
+    delta = 0 - delta;
+  }
+
+  frame.origin.y -= delta;
+  frame.size.height += delta;
+
+  int buttonMask = [viewReportButton autoresizingMask];
+  int textMask = [viewReportLabel autoresizingMask];
+  int reportMask = [viewReportScrollView autoresizingMask];
+
+  [viewReportButton setAutoresizingMask:NSViewMinYMargin];
+  [viewReportLabel setAutoresizingMask:NSViewMinYMargin];
+  [viewReportScrollView setAutoresizingMask:NSViewMinYMargin];
+
+  [window setFrame: frame display: true animate: NO];
+
+  if ([viewReportButton state] == NSOffState) {
+    [viewReportScrollView removeFromSuperview];
+  }
+
+  [viewReportButton setAutoresizingMask:buttonMask];
+  [viewReportLabel setAutoresizingMask:textMask];
+  [viewReportScrollView setAutoresizingMask:reportMask];
 }
 
 -(IBAction)closeClicked:(id)sender
 {
   [NSApp terminate: self];
 }
 
--(IBAction)sendClicked:(id)sender
+-(IBAction)closeAndSendClicked:(id)sender
 {
-  [self setView: window newView: uploadingView animate: YES];
-  [progressBar startAnimation: self];
-  [progressLabel setStringValue:Str(ST_SENDTITLE)];
+  if ([submitReportButton state] == NSOnState) {
+    // Hide the dialog after "closing", but leave it around to coordinate
+    // with the upload thread
+    [window orderOut:nil];
+    [self sendReport];
+  } else {
+    [NSApp terminate:self];
+  }
+}
 
-  if (![self setupPost])
-    [NSApp terminate];
+-(IBAction)restartClicked:(id)sender
+{
+  RestartApplication();
+  if ([submitReportButton state] == NSOnState) {
+    // Hide the dialog after "closing", but leave it around to coordinate
+    // with the upload thread
+    [window orderOut:nil];
+    [self sendReport];
+  } else {
+    [NSApp terminate:self];
+  }
+}
 
-  [NSThread detachNewThreadSelector:@selector(uploadThread:)
-            toTarget:self
-            withObject:mPost];
+-(IBAction)emailMeClicked:(id)sender
+{
+  [self updateEmail];
+  [self showReportInfo];
 }
 
--(void)setView:(NSWindow*)w newView: (NSView*)v animate: (BOOL)animate
+-(void)controlTextDidChange:(NSNotification *)note
+{
+  // Email text changed, assume they want the "Email me" checkbox
+  // updated appropriately
+  if ([[emailText stringValue] length] > 0)
+    [emailMeButton setState:NSOnState];
+  else
+    [emailMeButton setState:NSOffState];
+
+  [self updateEmail];
+  [self showReportInfo];
+}
+
+-(float)setStringFitVertically:(NSControl*)control
+                        string:(NSString*)str
+                  resizeWindow:(BOOL)resizeWindow
 {
-  NSRect frame = [w frame];
+  // hack to make the text field grow vertically
+  NSRect frame = [control frame];
+  float oldHeight = frame.size.height;
+
+  frame.size.height = 10000;
+  NSSize oldCellSize = [[control cell] cellSizeForBounds: frame];
+  [control setStringValue: str];
+  NSSize newCellSize = [[control cell] cellSizeForBounds: frame];
+
+  float delta = newCellSize.height - oldCellSize.height;
+  frame.origin.y -= delta;
+  frame.size.height = oldHeight + delta;
+  [control setFrame: frame];
 
-  NSRect oldViewFrame = [[w contentView] frame];
+  if (resizeWindow) {
+    NSRect frame = [window frame];
+    frame.origin.y -= delta;
+    frame.size.height += delta;
+    [window setFrame:frame display: true animate: NO];
+  }
+
+  return delta;
+}
+
+-(void)setView: (NSView*)v animate: (BOOL)animate
+{
+  NSRect frame = [window frame];
+
+  NSRect oldViewFrame = [[window contentView] frame];
   NSRect newViewFrame = [v frame];
 
   frame.origin.y += oldViewFrame.size.height - newViewFrame.size.height;
   frame.size.height -= oldViewFrame.size.height - newViewFrame.size.height;
 
   frame.origin.x += oldViewFrame.size.width - newViewFrame.size.width;
   frame.size.width -= oldViewFrame.size.width - newViewFrame.size.width;
 
-  [w setContentView: v];
-  [w setFrame: frame display: true animate: animate];
+  [window setContentView:v];
+  [window setFrame:frame display:true animate:animate];
+}
+
+-(void)updateEmail
+{
+  if ([emailMeButton state] == NSOnState) {
+    NSString* email = [emailText stringValue];
+    gQueryParameters["Email"] = [email UTF8String];
+  } else {
+    gQueryParameters.erase("Email");
+  }
+}
+
+-(void)sendReport
+{
+  if (![self setupPost])
+    [NSApp terminate:self];
+
+  [NSThread detachNewThreadSelector:@selector(uploadThread:)
+            toTarget:self
+            withObject:mPost];
 }
 
 -(bool)setupPost
 {
   NSURL* url = [NSURL URLWithString:NSSTR(gSendURL)];
   if (!url) return false;
 
   mPost = [[HTTPMultipartUpload alloc] initWithURL: url];
@@ -172,48 +355,45 @@ Str(const char* aName)
   [mPost addFileAtPath: NSSTR(gDumpFile) name: @"upload_file_minidump"];
   [mPost setParameters: parameters];
 
   return true;
 }
 
 -(void)uploadComplete:(id)data
 {
-  [progressBar stopAnimation: self];
-
   NSHTTPURLResponse* response = [mPost response];
 
-  NSString* status;
   bool success;
   string reply;
   if (!data || !response || [response statusCode] != 200) {
-    status = Str(ST_SUBMITFAILED);
     success = false;
     reply = "";
   } else {
-    status = Str(ST_SUBMITSUCCESS);
     success = true;
 
     NSString* encodingName = [response textEncodingName];
     NSStringEncoding encoding;
     if (encodingName) {
       encoding = CFStringConvertEncodingToNSStringEncoding(
         CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName));
     } else {
       encoding = NSISOLatin1StringEncoding;
     }
     NSString* r = [[NSString alloc] initWithData: data encoding: encoding];
     reply = [r UTF8String];
   }
 
-  [progressLabel setStringValue: status];
-  [closeButton setEnabled: true];
-  [closeButton setKeyEquivalent:@"\r"];
+  CrashReporterSendCompleted(success, reply);
 
-  CrashReporterSendCompleted(success, reply);
+  if (success) {
+    [NSApp terminate:self];
+  } else {
+    [self showErrorUI:gStrings[ST_SUBMITFAILED]];
+  }
 }
 
 -(void)uploadThread:(id)post
 {
   NSAutoreleasePool* autoreleasepool = [[NSAutoreleasePool alloc] init];
   NSError* error = nil;
   NSData* data = [post send: &error];
   if (error)
@@ -241,24 +421,26 @@ bool UIInit()
 
 void UIShutdown()
 {
   [gMainPool release];
 }
 
 void UIShowDefaultUI()
 {
-  [gUI showDefaultUI];
-  [NSApp run];
+  UIError(gStrings[ST_CRASHREPORTERDEFAULT]);
 }
 
 void UIShowCrashUI(const string& dumpfile,
                    const StringTable& queryParameters,
-                   const string& sendURL)
+                   const string& sendURL,
+                   const vector<string>& restartArgs)
 {
+  gRestartArgs = restartArgs;
+
   [gUI showCrashUI: dumpfile
        queryParameters: queryParameters
        sendURL: sendURL];
   [NSApp run];
 }
 
 void UIError(const string& message)
 {
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ b/toolkit/crashreporter/client/crashreporter_win.cpp
@@ -15,16 +15,18 @@
  * The Original Code is Mozilla Toolkit Crash Reporter
  *
  * The Initial Developer of the Original Code is
  * Ted Mielczarek <ted.mielczarek@gmail.com>
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *   Ted Mielczarek <ted.mielczarek@gmail.com>
+ *   Dave Camp <dcamp@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -42,234 +44,561 @@
 #include "crashreporter.h"
 
 #include <windows.h>
 #include <commctrl.h>
 #include <richedit.h>
 #include <shellapi.h>
 #include <shlobj.h>
 #include <shlwapi.h>
+#include <set>
 #include "resource.h"
 #include "client/windows/sender/crash_report_sender.h"
 #include "common/windows/string_utils-inl.h"
 
-#define CRASH_REPORTER_KEY L"Software\\Mozilla\\Crash Reporter"
 #define CRASH_REPORTER_VALUE L"Enabled"
+#define SUBMIT_REPORT_VALUE  L"SubmitReport"
+#define EMAIL_ME_VALUE       L"EmailMe"
+#define EMAIL_VALUE          L"Email"
+#define MAX_EMAIL_LENGTH     1024
 
 #define WM_UPLOADCOMPLETE WM_APP
 
 using std::string;
 using std::wstring;
 using std::map;
+using std::vector;
+using std::set;
+
+typedef struct {
+  HWND hDlg;
+  wstring dumpFile;
+  map<wstring,wstring> queryParameters;
+  wstring sendURL;
+
+  wstring serverResponse;
+} SendThreadData;
+
+static HANDLE               gThreadHandle;
+static SendThreadData       gSendData = { 0, };
+static vector<string>       gRestartArgs;
+static map<wstring,wstring> gQueryParameters;
+static wstring              gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
+
+// When vertically resizing the dialog, these items should move down
+static set<UINT> gAttachedBottom;
+
+// Default set of items for gAttachedBottom
+static const UINT kDefaultAttachedBottom[] = {
+  IDC_VIEWREPORTCHECK,
+  IDC_VIEWREPORTTEXT,
+  IDC_SUBMITCRASHCHECK,
+  IDC_EMAILMECHECK,
+  IDC_EMAILTEXT,
+  IDC_CLOSEBUTTON,
+  IDC_RESTARTBUTTON,
+};
 
 static wstring UTF8ToWide(const string& utf8, bool *success = 0);
 static string WideToUTF8(const wstring& wide, bool *success = 0);
 static DWORD WINAPI SendThreadProc(LPVOID param);
 
-typedef struct {
-  HWND hDlg;
-  wstring dumpFile;
-  const StringTable* query_parameters;
-  wstring send_url;
-  wstring* server_response;
-} SENDTHREADDATA;
-
 static wstring Str(const char* key)
 {
   return UTF8ToWide(gStrings[key]);
 }
 
+/* === win32 helper functions === */
+
 static void DoInitCommonControls()
 {
-	INITCOMMONCONTROLSEX ic;
-	ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
-	ic.dwICC = ICC_PROGRESS_CLASS;
-	InitCommonControlsEx(&ic);
+  INITCOMMONCONTROLSEX ic;
+  ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
+  ic.dwICC = ICC_PROGRESS_CLASS;
+  InitCommonControlsEx(&ic);
   // also get the rich edit control
   LoadLibrary(L"riched20.dll");
 }
 
-static BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) 
-{ 
-  switch (message) {
-  case WM_INITDIALOG:
-    SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
-    SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
-    SetDlgItemText(hwndDlg, IDC_RADIOENABLE, Str(ST_RADIOENABLE).c_str());
-    SetDlgItemText(hwndDlg, IDC_RADIODISABLE, Str(ST_RADIODISABLE).c_str());
-    SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, Str(ST_CRASHREPORTERDESCRIPTION).c_str());
-    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
-    SetFocus(GetDlgItem(hwndDlg, IDC_RADIOENABLE));
-    CheckRadioButton(hwndDlg, IDC_RADIOENABLE, IDC_RADIODISABLE, lParam ? IDC_RADIOENABLE : IDC_RADIODISABLE);
-    return FALSE;
+static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
+{
+  DWORD type, dataSize;
+  dataSize = sizeof(DWORD);
+  if (RegQueryValueEx(hRegKey, valueName, NULL, &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS
+    && type == REG_DWORD)
+    return true;
 
-  case WM_COMMAND:
-    if (LOWORD(wParam) == IDOK && HIWORD(wParam) == BN_CLICKED)
-      {
-        UINT enableChecked = IsDlgButtonChecked(hwndDlg, IDC_RADIOENABLE);
-        EndDialog(hwndDlg, (enableChecked > 0) ? 1 : 0);
-      }
-    return FALSE;
-
-  default: 
-    return FALSE; 
-  } 
+  return false;
 }
 
-static bool GetRegValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
-{
-	DWORD type, dataSize;
-	dataSize = sizeof(DWORD);
-	if (RegQueryValueEx(hRegKey, valueName, NULL, &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS
-		&& type == REG_DWORD)
-		return true;
-
-	return false;
-}
-
-static bool CheckCrashReporterEnabled(bool* enabled)
+static bool CheckBoolKey(const wchar_t* key,
+                         const wchar_t* valueName,
+                         bool* enabled)
 {
   *enabled = false;
   bool found = false;
   HKEY hRegKey;
   DWORD val;
   // see if our reg key is set globally
-  if (RegOpenKey(HKEY_LOCAL_MACHINE, CRASH_REPORTER_KEY, &hRegKey) == ERROR_SUCCESS)
-    {
-      if (GetRegValue(hRegKey, CRASH_REPORTER_VALUE, &val))
-        {
-          *enabled = (val == 1);
-          found = true;
-        }
+  if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
+    if (GetBoolValue(hRegKey, valueName, &val)) {
+      *enabled = (val == 1);
+      found = true;
+    }
+    RegCloseKey(hRegKey);
+  } else {
+    // look for it in user settings
+    if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
+      if (GetBoolValue(hRegKey, valueName, &val)) {
+        *enabled = (val == 1);
+        found = true;
+      }
       RegCloseKey(hRegKey);
     }
-  else
-    {
-      // look for it in user settings
-      if (RegOpenKey(HKEY_CURRENT_USER, CRASH_REPORTER_KEY, &hRegKey) == ERROR_SUCCESS)	
-        {
-          if (GetRegValue(hRegKey, CRASH_REPORTER_VALUE, &val))
-            {
-              *enabled = (val == 1);
-              found = true;
-            }
-          RegCloseKey(hRegKey);
-        }
-    }
+  }
 
-  // didn't find our reg key, ask user
   return found;
 }
 
-static void SetCrashReporterEnabled(bool enabled)
+static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
 {
   HKEY hRegKey;
-  if (RegCreateKey(HKEY_CURRENT_USER, CRASH_REPORTER_KEY, &hRegKey) == ERROR_SUCCESS) {
+  if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
     DWORD data = (enabled ? 1 : 0);
-    RegSetValueEx(hRegKey, CRASH_REPORTER_VALUE, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
+    RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
+    RegCloseKey(hRegKey);
+  }
+}
+
+static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value)
+{
+  DWORD type, dataSize;
+  wchar_t buf[2048];
+  dataSize = sizeof(buf);
+  if (RegQueryValueEx(hRegKey, valueName, NULL, &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS
+      && type == REG_SZ) {
+    value = buf;
+    return true;
+  }
+
+  return false;
+}
+
+static bool GetStringKey(const wchar_t* key,
+                         const wchar_t* valueName,
+                         wstring& value)
+{
+  value = L"";
+  bool found = false;
+  HKEY hRegKey;
+  // see if our reg key is set globally
+  if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
+    if (GetStringValue(hRegKey, valueName, value)) {
+      found = true;
+    }
+    RegCloseKey(hRegKey);
+  } else {
+    // look for it in user settings
+    if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
+      if (GetStringValue(hRegKey, valueName, value)) {
+        found = true;
+      }
+      RegCloseKey(hRegKey);
+    }
+  }
+
+  return found;
+}
+
+static void SetStringKey(const wchar_t* key,
+                         const wchar_t* valueName,
+                         const wstring& value)
+{
+  HKEY hRegKey;
+  if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
+    RegSetValueEx(hRegKey, valueName, 0, REG_SZ,
+                  (LPBYTE)value.c_str(),
+                  (value.length() + 1) * sizeof(wchar_t));
     RegCloseKey(hRegKey);
   }
 }
 
-static BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
+// Gets the position of a window relative to another window's client area
+static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r)
+{
+  GetWindowRect(hwnd, r);
+  ScreenToClient(hwndParent, (POINT*)&(r->left));
+  ScreenToClient(hwndParent, (POINT*)&(r->right));
+}
+
+static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
+{
+  HWND hwnd = GetDlgItem(hwndDlg, item);
+  LONG style = GetWindowLong(hwnd, GWL_STYLE);
+  if (visible)
+    style |= WS_VISIBLE;
+  else
+    style &= ~WS_VISIBLE;
+
+  SetWindowLong(hwnd, GWL_STYLE, style);
+}
+
+static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled)
+{
+  HWND hwnd = GetDlgItem(hwndDlg, item);
+  LONG style = GetWindowLong(hwnd, GWL_STYLE);
+  if (!disabled)
+    style |= WS_DISABLED;
+  else
+    style &= ~WS_DISABLED;
+
+  SetWindowLong(hwnd, GWL_STYLE, style);
+}
+
+/* === Crash Reporting Dialog === */
+
+static void StretchDialog(HWND hwndDlg, int ydiff)
+{
+  RECT r;
+  GetWindowRect(hwndDlg, &r);
+  r.bottom += ydiff;
+  MoveWindow(hwndDlg, r.left, r.top,
+             r.right - r.left, r.bottom - r.top, TRUE);
+}
+
+static void ReflowDialog(HWND hwndDlg, int ydiff)
+{
+  // Move items attached to the bottom down/up by as much as
+  // the window resize
+  for (set<UINT>::const_iterator item = gAttachedBottom.begin();
+       item != gAttachedBottom.end();
+       item++) {
+    RECT r;
+    HWND hwnd = GetDlgItem(hwndDlg, *item);
+    GetRelativeRect(hwnd, hwndDlg, &r);
+    r.top += ydiff;
+    r.bottom += ydiff;
+    MoveWindow(hwnd, r.left, r.top,
+               r.right - r.left, r.bottom - r.top, TRUE);
+  }
+}
+
+static DWORD WINAPI SendThreadProc(LPVOID param)
+{
+  bool finishedOk;
+  SendThreadData* td = (SendThreadData*)param;
+
+  if (td->sendURL.empty()) {
+    finishedOk = false;
+  } else {
+    google_breakpad::CrashReportSender sender(L"");
+    finishedOk = (sender.SendCrashReport(td->sendURL,
+                                         td->queryParameters,
+                                         td->dumpFile,
+                                         &td->serverResponse)
+                  == google_breakpad::RESULT_SUCCEEDED);
+  }
+
+  PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
+
+  return 0;
+}
+
+static void EndCrashReporterDialog(HWND hwndDlg, int code)
+{
+  // Save the current values to the registry
+  wchar_t email[MAX_EMAIL_LENGTH];
+  GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email));
+  SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email);
+
+  SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE,
+             IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0);
+  SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE,
+             IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
+
+  EndDialog(hwndDlg, code);
+}
+
+static void MaybeSendReport(HWND hwndDlg)
 {
-  static bool finishedOk = false;
-  static HANDLE hThread = NULL;
+  if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
+    EndCrashReporterDialog(hwndDlg, 0);
+    return;
+  }
+
+  gThreadHandle = NULL;
+  gSendData.hDlg = hwndDlg;
+  gSendData.queryParameters = gQueryParameters;
+
+  gThreadHandle = CreateThread(NULL, 0, SendThreadProc, &gSendData, 0, NULL);
+}
+
+static void RestartApplication()
+{
+  wstring cmdLine;
+
+  for (unsigned int i = 0; i < gRestartArgs.size(); i++) {
+    cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" ";
+  }
+
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  si.dwFlags = STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_SHOWNORMAL;
+  ZeroMemory(&pi, sizeof(pi));
+
+  if (CreateProcess(NULL, (LPWSTR)cmdLine.c_str(), NULL, NULL, FALSE, 0,
+                    NULL, NULL, &si, &pi)) {
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+  }
+}
+
+static void ShowReportInfo(HWND hwndDlg)
+{
+  wstring description;
+
+  for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin();
+       i != gQueryParameters.end();
+       i++) {
+    description += i->first;
+    description += L": ";
+    description += i->second;
+    description += L"\n";
+  }
+
+  description += L"\n";
+  description += Str(ST_EXTRAREPORTINFO);
+
+  SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str());
+}
+
+static void ShowHideReport(HWND hwndDlg)
+{
+  // When resizing the dialog to show the report, these items should
+  // stay put
+  gAttachedBottom.erase(IDC_VIEWREPORTCHECK);
+  gAttachedBottom.erase(IDC_VIEWREPORTTEXT);
+
+  RECT r;
+  HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTTEXT);
+
+  GetWindowRect(hwnd, &r);
+  int diff = (r.bottom - r.top) + 10;
+  if (IsDlgButtonChecked(hwndDlg, IDC_VIEWREPORTCHECK)) {
+    SetDlgItemVisible(hwndDlg, IDC_VIEWREPORTTEXT, true);
+  } else {
+    SetDlgItemVisible(hwndDlg, IDC_VIEWREPORTTEXT, false);
+    diff = -diff;
+  }
+
+  StretchDialog(hwndDlg, diff);
+
+  // set these back to normal
+  gAttachedBottom.insert(IDC_VIEWREPORTCHECK);
+  gAttachedBottom.insert(IDC_VIEWREPORTTEXT);
+}
+
+static void UpdateEmail(HWND hwndDlg)
+{
+  if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) {
+    wchar_t email[MAX_EMAIL_LENGTH];
+    GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email));
+    gQueryParameters[L"Email"] = email;
+  } else {
+    gQueryParameters.erase(L"Email");
+  }
+}
+
+static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
+                                             WPARAM wParam, LPARAM lParam)
+{
+  static int sHeight = 0;
+
+  bool success;
+  bool enabled;
 
   switch (message) {
-  case WM_INITDIALOG:
-    {
-      //init strings
-      SetWindowText(hwndDlg, Str(ST_SENDTITLE).c_str());
-      SetDlgItemText(hwndDlg, IDCANCEL, Str(ST_CANCEL).c_str());
-      // init progressmeter
-      SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
-      SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETPOS, 0, 0);
-      // now create a thread to actually do the sending
-      SENDTHREADDATA* td = (SENDTHREADDATA*)lParam;
-      td->hDlg = hwndDlg;
-      CreateThread(NULL, 0, SendThreadProc, td, 0, NULL);
+  case WM_INITDIALOG: {
+    RECT r;
+    GetClientRect(hwndDlg, &r);
+    sHeight = r.bottom - r.top;
+
+    SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
+
+    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
+                       EM_SETEVENTMASK, (WPARAM)NULL,
+                       ENM_REQUESTRESIZE);
+    wstring description = Str(ST_CRASHREPORTERHEADER);
+    description += L"\n\n";
+    description += Str(ST_CRASHREPORTERDESCRIPTION);
+    SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str());
+
+    // Make the title bold.
+    CHARFORMAT fmt = { 0, };
+    fmt.cbSize = sizeof(fmt);
+    fmt.dwMask = CFM_BOLD;
+    fmt.dwEffects = CFE_BOLD;
+    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL,
+                       0, Str(ST_CRASHREPORTERHEADER).length());
+    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT,
+                       SCF_SELECTION, (LPARAM)&fmt);
+    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0);
+    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
+                       EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
+
+    SetDlgItemText(hwndDlg, IDC_VIEWREPORTCHECK, Str(ST_VIEWREPORT).c_str());
+    SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
+                       EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
+
+
+    SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
+                   Str(ST_CHECKSUBMIT).c_str());
+    if (CheckBoolKey(gCrashReporterKey.c_str(),
+                     SUBMIT_REPORT_VALUE, &enabled) &&
+        !enabled) {
+      CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, BST_UNCHECKED);
+      EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
+      EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), enabled);
+    } else {
+      CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, BST_CHECKED);
+    }
+
+    SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str());
+    if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) &&
+        enabled) {
+      CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
+    } else {
+      CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
+    }
+
+    wstring email;
+    if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) {
+      SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str());
     }
-    return TRUE;
+
+    SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_CLOSE).c_str());
+
+    if (gRestartArgs.size() > 0) {
+      SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str());
+    } else {
+      // No restart arguments, move the close button over to the side
+      // and hide the restart button
+      SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
+
+      RECT closeRect;
+      HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON);
+      GetRelativeRect(hwndClose, hwndDlg, &closeRect);
+
+      RECT restartRect;
+      HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON);
+      GetRelativeRect(hwndRestart, hwndDlg, &restartRect);
+
+      int size = closeRect.right - closeRect.left;
+      closeRect.right = restartRect.right;
+      closeRect.left = closeRect.right - size;
+
+      MoveWindow(hwndClose, closeRect.left, closeRect.top,
+                 closeRect.right - closeRect.left,
+                 closeRect.bottom - closeRect.top,
+                 TRUE);
+    }
+    UpdateEmail(hwndDlg);
+    ShowReportInfo(hwndDlg);
+
+    SetFocus(GetDlgItem(hwndDlg, IDC_EMAILTEXT));
+    return FALSE;
+  }
+  case WM_SIZE: {
+    ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
+    sHeight = HIWORD(lParam);
+    InvalidateRect(hwndDlg, NULL, TRUE);
+    return FALSE;
+  }
+  case WM_NOTIFY: {
+    NMHDR* notification = reinterpret_cast<NMHDR*>(lParam);
+    if (notification->code == EN_REQUESTRESIZE) {
+      // Resizing the rich edit control to fit the description text.
+      REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam);
+      RECT newSize = reqresize->rc;
+      RECT oldSize;
+      GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize);
+
+      // resize the text box as requested
+      MoveWindow(notification->hwndFrom, newSize.left, newSize.top,
+                 newSize.right - newSize.left, newSize.bottom - newSize.top,
+                 TRUE);
 
-  case WM_UPLOADCOMPLETE:
-    WaitForSingleObject(hThread, INFINITE);
-    finishedOk = (wParam == 1);
-    if (finishedOk) {
-      SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETPOS, 100, 0);
-      MessageBox(hwndDlg,
-                 Str(ST_SUBMITSUCCESS).c_str(),
-                 Str(ST_CRASHREPORTERTITLE).c_str(),
-                 MB_OK | MB_ICONINFORMATION);
+      // Resize the dialog to fit (the WM_SIZE handler will move the controls)
+      StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom);
     }
-    else {
+    return FALSE;
+  }
+  case WM_COMMAND: {
+    if (HIWORD(wParam) == BN_CLICKED) {
+      switch(LOWORD(wParam)) {
+      case IDC_VIEWREPORTCHECK:
+        ShowHideReport(hwndDlg);
+        break;
+      case IDC_SUBMITREPORTCHECK:
+        enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
+        EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), enabled);
+        break;
+      case IDC_EMAILMECHECK:
+        UpdateEmail(hwndDlg);
+        ShowReportInfo(hwndDlg);
+        break;
+      case IDC_CLOSEBUTTON:
+        // Hide the dialog after "closing", but leave it around to coordinate
+        // with the upload thread
+        ShowWindow(hwndDlg, SW_HIDE);
+        MaybeSendReport(hwndDlg);
+        break;
+      case IDC_RESTARTBUTTON:
+        // Hide the dialog after "closing", but leave it around to coordinate
+        // with the upload thread
+        ShowWindow(hwndDlg, SW_HIDE);
+        RestartApplication();
+        MaybeSendReport(hwndDlg);
+        break;
+      }
+    } else if (HIWORD(wParam) == EN_CHANGE) {
+      switch(LOWORD(wParam)) {
+      case IDC_EMAILTEXT:
+        wchar_t email[MAX_EMAIL_LENGTH];
+        if (GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email)) > 0)
+          CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
+        else
+          CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
+        UpdateEmail(hwndDlg);
+        ShowReportInfo(hwndDlg);
+      }
+    }
+
+    return FALSE;
+  }
+  case WM_UPLOADCOMPLETE: {
+    WaitForSingleObject(gThreadHandle, INFINITE);
+    success = (wParam == 1);
+    CrashReporterSendCompleted(success, WideToUTF8(gSendData.serverResponse));
+    if (!success) {
       MessageBox(hwndDlg,
                  Str(ST_SUBMITFAILED).c_str(),
                  Str(ST_CRASHREPORTERTITLE).c_str(),
                  MB_OK | MB_ICONERROR);
     }
-    EndDialog(hwndDlg, finishedOk ? 1 : 0);
-    return TRUE;
-
-  case WM_COMMAND:
-    if (LOWORD(wParam) == IDCANCEL && HIWORD(wParam) == BN_CLICKED) {
-      EndDialog(hwndDlg, finishedOk ? 1 : 0);
-    }
+    EndCrashReporterDialog(hwndDlg, success ? 1 : 0);
     return TRUE;
-
-  default:
-    return FALSE; 
-  } 
-}
-
-static bool SendCrashReport(wstring dumpFile,
-                            const StringTable* query_parameters,
-                            wstring send_url,
-                            wstring* server_response)
-{
-  SENDTHREADDATA td;
-  td.hDlg = NULL;
-  td.dumpFile = dumpFile;
-  td.query_parameters = query_parameters;
-  td.send_url = send_url;
-  td.server_response = server_response;
-
-  int res = (int)DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
-                                (DLGPROC)SendDialogProc, (LPARAM)&td);
-
-  return (res >= 0);
-}
-
-static DWORD WINAPI SendThreadProc(LPVOID param)
-{
-  bool finishedOk;
-  SENDTHREADDATA* td = (SENDTHREADDATA*)param;
-
-  if (td->send_url.empty()) {
-    finishedOk = false;
+  }
   }
-  else {
-    map<wstring,wstring>query_parameters;
-    StringTable::const_iterator end = td->query_parameters->end();
-    for (StringTable::const_iterator i = td->query_parameters->begin();
-         i != end;
-         i++) {
-      query_parameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
-    }
-
-    google_breakpad::CrashReportSender sender(L"");
-    finishedOk = (sender.SendCrashReport(td->send_url,
-                                         query_parameters,
-                                         td->dumpFile,
-                                         td->server_response)
-                  == google_breakpad::RESULT_SUCCEEDED);
-  }
-  PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
-
-  return 0;
+  return FALSE;
 }
 
 static wstring UTF8ToWide(const string& utf8, bool *success)
 {
   wchar_t* buffer = NULL;
   int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
                                         -1, NULL, 0);
   if(buffer_size == 0) {
@@ -324,54 +653,60 @@ static string WideToUTF8(const wstring& 
 
   return utf8;
 }
 
 /* === Crashreporter UI Functions === */
 
 bool UIInit()
 {
+  for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
+    gAttachedBottom.insert(kDefaultAttachedBottom[i]);
+  }
+
   DoInitCommonControls();
   return true;
 }
 
 void UIShutdown()
 {
 }
 
 void UIShowDefaultUI()
 {
-  bool enabled;
-  // no dump file specified, just ask about enabling
-  if (!CheckCrashReporterEnabled(&enabled))
-    enabled = true;
-
-  enabled = (1 == DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)enabled));
-  SetCrashReporterEnabled(enabled);
+  MessageBox(NULL, Str(ST_CRASHREPORTERDEFAULT).c_str(),
+             L"Crash Reporter",
+             MB_OK | MB_ICONSTOP);
 }
 
-void UIShowCrashUI(const string& dumpfile,
-                   const StringTable& query_parameters,
-                   const string& send_url)
+void UIShowCrashUI(const string& dumpFile,
+                   const StringTable& queryParameters,
+                   const string& sendURL,
+                   const vector<string>& restartArgs)
 {
-  bool enabled;
-  if (!CheckCrashReporterEnabled(&enabled)) {
-    //ask user if crash reporter should be enabled
-    enabled = (1 == DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)true));
-    SetCrashReporterEnabled(enabled);
+  gSendData.hDlg = NULL;
+  gSendData.dumpFile = UTF8ToWide(dumpFile);
+  gSendData.sendURL = UTF8ToWide(sendURL);
+
+  for (StringTable::const_iterator i = queryParameters.begin();
+       i != queryParameters.end();
+       i++) {
+    gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
   }
-  // if enabled, send crash report
-  if (enabled) {
-    wstring server_response;
-    bool success = SendCrashReport(UTF8ToWide(dumpfile),
-                                   &query_parameters,
-                                   UTF8ToWide(send_url),
-                                   &server_response);
-    CrashReporterSendCompleted(success, WideToUTF8(server_response));
+
+  if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
+    gCrashReporterKey = L"Software\\" +
+                        gQueryParameters[L"Vendor"] +
+                        L"\\Crash Reporter";
   }
+
+  gRestartArgs = restartArgs;
+
+  DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
+                 (DLGPROC)CrashReporterDialogProc, 0);
 }
 
 void UIError(const string& message)
 {
   wstring title = Str(ST_CRASHREPORTERTITLE);
   if (title.empty())
     title = L"Crash Reporter Error";
 
@@ -425,16 +760,19 @@ bool UIEnsurePathExists(const string& pa
       return false;
   }
 
   return true;
 }
 
 bool UIMoveFile(const string& oldfile, const string& newfile)
 {
+  if (oldfile == newfile)
+    return true;
+
   return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
     == TRUE;
 }
 
 bool UIDeleteFile(const string& oldfile)
 {
   return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE;
 }
--- a/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
+++ b/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
@@ -1,27 +1,36 @@
 {
     IBClasses = (
         {
-            ACTIONS = {closeClicked = id; sendClicked = id; }; 
+            ACTIONS = {
+                closeAndSendClicked = id; 
+                closeClicked = id; 
+                emailMeClicked = id; 
+                restartClicked = id; 
+                viewReportClicked = id; 
+            }; 
             CLASS = CrashReporterUI; 
             LANGUAGE = ObjC; 
             OUTLETS = {
                 closeButton = NSButton; 
                 descriptionLabel = NSTextField; 
-                disableReportingButton = NSButton; 
-                dontSendButton = NSButton; 
-                enableView = NSView; 
+                emailMeButton = NSButton; 
+                emailText = NSTextField; 
                 errorCloseButton = NSButton; 
+                errorHeaderLabel = NSTextField; 
                 errorLabel = NSTextField; 
                 errorView = NSView; 
-                progressBar = NSProgressIndicator; 
-                progressLabel = NSTextField; 
-                sendButton = NSButton; 
-                uploadingView = NSView; 
+                headerLabel = NSTextField; 
+                restartButton = NSButton; 
+                submitReportButton = NSButton; 
+                viewReportButton = NSButton; 
+                viewReportLabel = NSTextField; 
+                viewReportScrollView = NSScrollView; 
+                viewReportTextView = NSTextView; 
                 window = NSWindow; 
             }; 
             SUPERCLASS = NSObject; 
         }, 
         {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }
     ); 
     IBVersion = 1; 
 }
\ No newline at end of file
--- a/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib
+++ b/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib
@@ -1,37 +1,36 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 	<key>IBDocumentLocation</key>
-	<string>299 87 356 240 0 0 1440 878 </string>
+	<string>128 190 504 836 0 0 1920 1178 </string>
 	<key>IBEditorPositions</key>
 	<dict>
 		<key>282</key>
-		<string>24 681 550 163 0 0 1440 878 </string>
+		<string>685 720 550 163 0 0 1920 1178 </string>
 		<key>29</key>
 		<string>447 315 137 44 0 0 1440 878 </string>
 		<key>356</key>
-		<string>643 213 551 213 0 0 1440 878 </string>
+		<string>684 695 551 213 0 0 1920 1178 </string>
 		<key>386</key>
-		<string>23 537 456 145 0 0 1440 878 </string>
+		<string>492 520 456 164 0 0 1440 878 </string>
 	</dict>
 	<key>IBFramework Version</key>
 	<string>446.1</string>
 	<key>IBLockedObjects</key>
 	<array>
 		<integer>282</integer>
 		<integer>356</integer>
 	</array>
 	<key>IBOldestOS</key>
 	<integer>2</integer>
 	<key>IBOpenObjects</key>
 	<array>
+		<integer>29</integer>
 		<integer>386</integer>
-		<integer>282</integer>
-		<integer>29</integer>
 		<integer>21</integer>
 	</array>
 	<key>IBSystem Version</key>
 	<string>8P2137</string>
 </dict>
 </plist>
index 46138d68736b5534c8b428c76e9187a17ae1299c..e7b0de090f1f26d413667413ee7e0da645514f45
GIT binary patch
literal 14853
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
--- a/toolkit/crashreporter/client/resource.h
+++ b/toolkit/crashreporter/client/resource.h
@@ -1,21 +1,30 @@
 //{{NO_DEPENDENCIES}}
 // Microsoft Visual C++ generated include file.
-// Used by CrashReporter.rc
+// Used by crashreporter.rc
 //
-#define IDD_ENABLEDIALOG                101
 #define IDD_SENDDIALOG                  102
-#define IDC_RADIOENABLE                 1001
-#define IDC_RADIODISABLE                1002
 #define IDC_PROGRESS                    1003
 #define IDC_DESCRIPTIONTEXT             1004
+#define IDC_CLOSEBUTTON                 1005
+#define IDC_VIEWREPORTBUTTON            1006
+#define IDC_SUBMITCRASHCHECK            1007
+#define IDC_SUBMITREPORTCHECK           1007
+#define IDC_EMAILMECHECK                1008
+#define IDC_EMAILTEXT                   1009
+#define IDC_HEADERLABEL                 1010
+#define IDC_RESTARTBUTTON               1012
+#define IDC_DESCRIPTIONLABEL            1013
+#define IDC_VIEWREPORTTEXT              1015
+#define IDC_VIEWREPORTCHECK             1016
+#define IDC_PROGRESSLABEL               -1
 
 // Next default values for new objects
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        103
 #define _APS_NEXT_COMMAND_VALUE         40001
-#define _APS_NEXT_CONTROL_VALUE         1005
+#define _APS_NEXT_CONTROL_VALUE         1017
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -409,9 +409,31 @@ nsresult AnnotateCrashReport(const nsACS
   // now rebuild the file contents
   crashReporterAPIData->Truncate(0);
   crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
                                            crashReporterAPIData);
 
   return NS_OK;
 }
 
+nsresult
+SetRestartArgs(int argc, char **argv)
+{
+  int i;
+  nsCAutoString envVar;
+  for (i = 0; i < argc; i++) {
+    envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
+    envVar.AppendInt(i);
+    envVar += "=";
+    envVar += argv[i];
+    PR_SetEnv(envVar.get());
+  }
+
+  // make sure the arg list is terminated
+  envVar = "MOZ_CRASHREPORTER_RESTART_ARG_";
+  envVar.AppendInt(i);
+  envVar += "=";
+
+  PR_SetEnv(envVar.get());
+
+  return NS_OK;
+}
 } // namespace CrashReporter
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -43,11 +43,12 @@
 #include "nsStringGlue.h"
 
 namespace CrashReporter {
 nsresult SetExceptionHandler(nsILocalFile* aXREDirectory,
                              const char* aServerURL);
 nsresult SetMinidumpPath(const nsAString& aPath);
 nsresult UnsetExceptionHandler();
 nsresult AnnotateCrashReport(const nsACString &key, const nsACString &data);
+nsresult SetRestartArgs(int argc, char **argv);
 }
 
 #endif /* nsAirbagExceptionHandler_h__ */