Improve crash reporter errors. b=385359, r=luser
authordcamp@mozilla.com
Tue, 24 Jul 2007 18:06:09 -0700
changeset 3906 f6f7878656d576a6534a1abfa0bf7eed8427d4a9
parent 3905 023456fb738894d07fb766a1ea47ae6334ec3566
child 3907 a7db7bd3dca51c1750702fe35d2e2c574914aa9f
push idunknown
push userunknown
push dateunknown
reviewersluser
bugs385359
milestone1.9a7pre
Improve crash reporter errors. b=385359, r=luser
toolkit/crashreporter/client/crashreporter.cpp
toolkit/crashreporter/client/crashreporter.h
toolkit/crashreporter/client/crashreporter.ini
toolkit/crashreporter/client/crashreporter_linux.cpp
toolkit/crashreporter/client/crashreporter_osx.mm
toolkit/crashreporter/client/crashreporter_win.cpp
--- a/toolkit/crashreporter/client/crashreporter.cpp
+++ b/toolkit/crashreporter/client/crashreporter.cpp
@@ -63,16 +63,32 @@ string       gSettingsPath;
 int          gArgc;
 char**       gArgv;
 
 static string       gDumpFile;
 static string       gExtraFile;
 
 static string kExtraDataExtension = ".extra";
 
+void UIError(const string& message)
+{
+  string errorMessage;
+  if (!gStrings[ST_CRASHREPORTERERROR].empty()) {
+    char buf[2048];
+    UI_SNPRINTF(buf, 2048,
+                gStrings[ST_CRASHREPORTERERROR].c_str(),
+                message.c_str());
+    errorMessage = buf;
+  } else {
+    errorMessage = message;
+  }
+
+  UIError_impl(errorMessage);
+}
+
 static string Unescape(const string& str)
 {
   string ret;
   for (string::const_iterator iter = str.begin();
        iter != str.end();
        iter++) {
     if (*iter == '\\') {
       iter++;
@@ -205,24 +221,30 @@ static string Basename(const string& fil
     return file;
 }
 
 static bool MoveCrashData(const string& toDir,
                           string& dumpfile,
                           string& extrafile)
 {
   if (!UIEnsurePathExists(toDir)) {
+    UIError(gStrings[ST_ERROR_CREATEDUMPDIR]);
     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)) {
+  if (!UIMoveFile(dumpfile, newDump)) {
+    UIError(gStrings[ST_ERROR_DUMPFILEMOVE]);
+    return false;
+  }
+
+  if (!UIMoveFile(extrafile, newExtra)) {
+    UIError(gStrings[ST_ERROR_EXTRAFILEMOVE]);
     return false;
   }
 
   dumpfile = newDump;
   extrafile = newExtra;
 
   return true;
 }
@@ -283,69 +305,121 @@ bool SendCompleted(bool success, const s
   }
   return true;
 }
 
 } // namespace CrashReporter
 
 using namespace CrashReporter;
 
+void RewriteStrings(StringTable& queryParameters)
+{
+  // rewrite some UI strings with the values from the query parameters
+  string product = queryParameters["ProductName"];
+  string vendor = queryParameters["Vendor"];
+  if (vendor.empty()) {
+    // Assume Mozilla if no vendor is specified
+    vendor = "Mozilla";
+  }
+
+  char buf[4096];
+  UI_SNPRINTF(buf, sizeof(buf),
+              gStrings[ST_CRASHREPORTERVENDORTITLE].c_str(),
+              vendor.c_str());
+  gStrings[ST_CRASHREPORTERTITLE] = buf;
+
+  // Leave a format specifier for UIError to fill in
+  UI_SNPRINTF(buf, sizeof(buf),
+              gStrings[ST_CRASHREPORTERPRODUCTERROR].c_str(),
+              product.c_str(),
+              "%s");
+  gStrings[ST_CRASHREPORTERERROR] = 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.c_str());
+  gStrings[ST_CHECKSUBMIT] = buf;
+
+  UI_SNPRINTF(buf, sizeof(buf),
+              gStrings[ST_RESTART].c_str(),
+              product.c_str());
+  gStrings[ST_RESTART] = buf;
+}
+
 int main(int argc, char** argv)
 {
   gArgc = argc;
   gArgv = argv;
 
   if (!ReadConfig()) {
-    UIError("Couldn't read configuration");
+    UIError("Couldn't read configuration.");
     return 0;
   }
 
   if (!UIInit())
     return 0;
 
   if (argc > 1) {
     gDumpFile = argv[1];
   }
 
   if (gDumpFile.empty()) {
     // no dump file specified, run the default UI
     UIShowDefaultUI();
   } else {
     gExtraFile = GetExtraDataFilename(gDumpFile);
     if (gExtraFile.empty()) {
-      UIError("Couldn't get extra data filename");
+      UIError(gStrings[ST_ERROR_BADARGUMENTS]);
+      return 0;
+    }
+
+    if (!UIFileExists(gExtraFile)) {
+      UIError(gStrings[ST_ERROR_EXTRAFILEEXISTS]);
       return 0;
     }
 
     StringTable queryParameters;
     if (!ReadStringsFromFile(gExtraFile, queryParameters, true)) {
-      UIError("Couldn't read extra data");
+      UIError(gStrings[ST_ERROR_EXTRAFILEREAD]);
       return 0;
     }
 
     if (queryParameters.find("ProductName") == queryParameters.end()) {
-      UIError("No product name specified");
+      UIError(gStrings[ST_ERROR_NOPRODUCTNAME]);
       return 0;
     }
 
+    // There is enough information in the extra file to rewrite strings
+    // to be product specific
+    RewriteStrings(queryParameters);
+
     if (queryParameters.find("ServerURL") == queryParameters.end()) {
-      UIError("No server URL specified");
+      UIError(gStrings[ST_ERROR_NOSERVERURL]);
       return 0;
     }
 
     string product = queryParameters["ProductName"];
     string vendor = queryParameters["Vendor"];
     if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
-      UIError("Couldn't get settings path");
+      UIError(gStrings[ST_ERROR_NOSETTINGSPATH]);
+      return 0;
+    }
+
+    if (!UIFileExists(gDumpFile)) {
+      UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
       return 0;
     }
 
     string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending";
     if (!MoveCrashData(pendingDir, gDumpFile, gExtraFile)) {
-      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;
@@ -365,38 +439,16 @@ int main(int argc, char** argv)
     // 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;
     }
 
-    // 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;
 }
 
--- a/toolkit/crashreporter/client/crashreporter.h
+++ b/toolkit/crashreporter/client/crashreporter.h
@@ -24,38 +24,54 @@
 
 #define UI_SNPRINTF snprintf
 #define UI_DIR_SEPARATOR "/"
 
 #endif
 
 typedef std::map<std::string, std::string> StringTable;
 
-#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_RESTART                  "Restart"
-#define ST_SUBMITFAILED             "SubmitFailed"
+#define ST_CRASHREPORTERTITLE        "CrashReporterTitle"
+#define ST_CRASHREPORTERVENDORTITLE  "CrashReporterVendorTitle"
+#define ST_CRASHREPORTERERROR        "CrashReporterError"
+#define ST_CRASHREPORTERPRODUCTERROR "CrashReporterProductError"
+#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_RESTART                   "Restart"
+#define ST_SUBMITFAILED              "SubmitFailed"
+
+#define ST_ERROR_BADARGUMENTS        "ErrorBadArguments"
+#define ST_ERROR_EXTRAFILEEXISTS     "ErrorExtraFileExists"
+#define ST_ERROR_EXTRAFILEREAD       "ErrorExtraFileRead"
+#define ST_ERROR_EXTRAFILEMOVE       "ErrorExtraFileMove"
+#define ST_ERROR_DUMPFILEEXISTS      "ErrorDumpFileExists"
+#define ST_ERROR_DUMPFILEMOVE        "ErrorDumpFileMove"
+#define ST_ERROR_NOPRODUCTNAME       "ErrorNoProductName"
+#define ST_ERROR_NOSERVERURL         "ErrorNoServerURL"
+#define ST_ERROR_NOSETTINGSPATH      "ErrorNoSettingsPath"
+#define ST_ERROR_CREATEDUMPDIR       "ErrorCreateDumpDir"
 
 //=============================================================================
 // implemented in crashreporter.cpp
 //=============================================================================
 
 namespace CrashReporter {
   extern StringTable  gStrings;
   extern std::string  gSettingsPath;
   extern int          gArgc;
   extern char**       gArgv;
 
+  void UIError(const std::string& message);
+
   // The UI finished sending the report
   bool SendCompleted(bool success, const std::string& serverResponse);
 
   bool ReadStrings(std::istream& in,
                    StringTable& strings,
                    bool unescape);
   bool ReadStringsFromFile(const std::string& path,
                            StringTable& strings,
@@ -82,23 +98,24 @@ void UIShutdown();
 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::vector<std::string>& restartArgs);
 
-void UIError(const std::string& message);
+void UIError_impl(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 UIFileExists(const std::string& path);
 bool UIMoveFile(const std::string& oldfile, const std::string& newfile);
 bool UIDeleteFile(const std::string& oldfile);
 
 #ifdef _MSC_VER
 # pragma warning( pop )
 #endif
 
 #endif
--- a/toolkit/crashreporter/client/crashreporter.ini
+++ b/toolkit/crashreporter/client/crashreporter.ini
@@ -1,15 +1,30 @@
 ; This file is in the UTF-8 encoding
 [Strings]
-CrashReporterTitle=%s Crash Reporter
+CrashReporterTitle=Crash Reporter
+CrashReporterVendorTitle=%s Crash Reporter
+CrashReporterError=We're sorry, but the application hit an unexpected problem and crashed.\n\nUnfortunately the crash reporter is unable to submit a report for this crash.\n\nDetails: %s
+CrashReporterProductError=We're sorry, but %s hit an unexpected problem and crashed.  We'll try to restore your tabs and windows when it restarts.\n\nUnfortunately the crash reporter is unable to submit a crash report.\n\nDetails: %s
 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
 Restart=Restart %s
 SubmitFailed=Failed to submit crash report
 CrashID=Crash ID: %s
 CrashDetailsURL=You can view details of this crash at %s
+
+ErrorBadArguments=The application passed an invalid argument.
+ErrorExtraFileExists=The application didn't leave an application data file.
+ErrorExtraFileRead=Couldn't read the application data file.
+ErrorExtraFileMove=Couldn't move application data file.
+ErrorDumpFileExists=The application did not leave a crash dump file.
+ErrorDumpFileMove=Couldn't move crash dump.
+ErrorNoProductName=The application did not identify itself.
+ErrorNoServerURL=The application did not specify a crash reporting server.
+ErrorNoSettingsPath=Couldn't find the crash reporter's settings.
+ErrorCreateDumpDir=Couldn't create pending dump directory.
+
--- a/toolkit/crashreporter/client/crashreporter_linux.cpp
+++ b/toolkit/crashreporter/client/crashreporter_linux.cpp
@@ -407,17 +407,17 @@ void UIShowCrashUI(const string& dumpfil
   LoadSettings();
   ShowReportInfo();
 
   gtk_widget_show_all(gWindow);
 
   gtk_main();
 }
 
-void UIError(const string& message)
+void UIError_impl(const string& message)
 {
   if (!gInitialized) {
     // Didn't initialize, this is the best we can do
     printf("Error: %s\n", message.c_str());
     return;
   }
 
   GtkWidget* errorDialog =
@@ -472,16 +472,26 @@ bool UIEnsurePathExists(const string& pa
   int ret = mkdir(path.c_str(), S_IRWXU);
   int e = errno;
   if (ret == -1 && e != EEXIST)
     return false;
 
   return true;
 }
 
+bool UIFileExists(const string& path)
+{
+  struct stat sb;
+  int ret = stat(path.c_str(), &sb);
+  if (ret == -1 || !(sb.st_mode & S_IFREG))
+    return false;
+
+  return true;
+}
+
 bool UIMoveFile(const string& file, const string& newfile)
 {
   return (rename(file.c_str(), newfile.c_str()) != -1);
 }
 
 bool UIDeleteFile(const string& file)
 {
   return (unlink(file.c_str()) != -1);
--- a/toolkit/crashreporter/client/crashreporter_osx.mm
+++ b/toolkit/crashreporter/client/crashreporter_osx.mm
@@ -423,33 +423,34 @@ bool UIInit()
 
 void UIShutdown()
 {
   [gMainPool release];
 }
 
 void UIShowDefaultUI()
 {
-  UIError(gStrings[ST_CRASHREPORTERDEFAULT]);
+  [gUI showErrorUI: gStrings[ST_CRASHREPORTERDEFAULT]];
+  [NSApp run];
 }
 
 void UIShowCrashUI(const string& dumpfile,
                    const StringTable& queryParameters,
                    const string& sendURL,
                    const vector<string>& restartArgs)
 {
   gRestartArgs = restartArgs;
 
   [gUI showCrashUI: dumpfile
        queryParameters: queryParameters
        sendURL: sendURL];
   [NSApp run];
 }
 
-void UIError(const string& message)
+void UIError_impl(const string& message)
 {
   if (!gUI) {
     // UI failed to initialize, printing is the best we can do
     printf("Error: %s\n", message.c_str());
     return;
   }
 
   [gUI showErrorUI: message];
@@ -497,16 +498,26 @@ bool UIEnsurePathExists(const string& pa
   int ret = mkdir(path.c_str(), S_IRWXU);
   int e = errno;
   if (ret == -1 && e != EEXIST)
     return false;
 
   return true;
 }
 
+bool UIFileExists(const string& path)
+{
+  struct stat sb;
+  int ret = stat(path.c_str(), &sb);
+  if (ret == -1 || !(sb.st_mode & S_IFREG))
+    return false;
+
+  return true;
+}
+
 bool UIMoveFile(const string& file, const string& newfile)
 {
   return (rename(file.c_str(), newfile.c_str()) != -1);
 }
 
 bool UIDeleteFile(const string& file)
 {
   return (unlink(file.c_str()) != -1);
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ b/toolkit/crashreporter/client/crashreporter_win.cpp
@@ -701,17 +701,17 @@ void UIShowCrashUI(const string& dumpFil
   }
 
   gRestartArgs = restartArgs;
 
   DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
                  (DLGPROC)CrashReporterDialogProc, 0);
 }
 
-void UIError(const string& message)
+void UIError_impl(const string& message)
 {
   wstring title = Str(ST_CRASHREPORTERTITLE);
   if (title.empty())
     title = L"Crash Reporter Error";
 
   MessageBox(NULL, UTF8ToWide(message).c_str(), title.c_str(),
              MB_OK | MB_ICONSTOP);
 }
@@ -760,16 +760,22 @@ bool UIEnsurePathExists(const string& pa
   if (CreateDirectory(UTF8ToWide(path).c_str(), NULL) == 0) {
     if (GetLastError() != ERROR_ALREADY_EXISTS)
       return false;
   }
 
   return true;
 }
 
+bool UIFileExists(const string& path)
+{
+  DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str());
+  return (attrs != INVALID_FILE_ATTRIBUTES);
+}
+
 bool UIMoveFile(const string& oldfile, const string& newfile)
 {
   if (oldfile == newfile)
     return true;
 
   return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
     == TRUE;
 }