bug 366970, crash reporter needs to send Product, Build ID, Platform. r=mento
authorted.mielczarek@gmail.com
Tue, 24 Jul 2007 18:05:55 -0700
changeset 3866 35c291264a4fb5a576b730919398fe5142f3abb8
parent 3865 e06864445ca158a46155be17e24a72824252bb39
child 3867 540649a56cac3d8a67767cc0f312c8356d0f1ab5
push idunknown
push userunknown
push dateunknown
reviewersmento
bugs366970
milestone1.9a7pre
bug 366970, crash reporter needs to send Product, Build ID, Platform. r=mento
toolkit/crashreporter/Makefile.in
toolkit/crashreporter/client/Makefile.in
toolkit/crashreporter/client/crashreporter_win.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
--- a/toolkit/crashreporter/Makefile.in
+++ b/toolkit/crashreporter/Makefile.in
@@ -43,16 +43,17 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = airbag
 LIBXUL_LIBRARY = 1
 LIBRARY_NAME = airbagexception_s
 
 REQUIRES = \
 	xpcom \
 	string \
+	xulapp \
 	$(NULL)
 
 DIRS = \
 	$(NULL)
 
 ifeq  ($(OS_ARCH),WINNT)
 	DIRS += airbag/src/common/windows \
 		airbag/src/client/windows \
--- a/toolkit/crashreporter/client/Makefile.in
+++ b/toolkit/crashreporter/client/Makefile.in
@@ -56,11 +56,12 @@ LOCAL_INCLUDES = -I$(srcdir)/../airbag/s
 ifeq ($(OS_ARCH),WINNT)
 CPPSRCS = crashreporter_win.cpp
 LIBS += $(DEPTH)/toolkit/airbag/airbag/src/client/windows/sender/$(LIB_PREFIX)crash_report_sender.$(LIB_SUFFIX)
 LIBS += $(DEPTH)/toolkit/airbag/airbag/src/common/windows/$(LIB_PREFIX)airbag_common_s.$(LIB_SUFFIX)
 LOCAL_INCLUDES += -I$(srcdir)
 RCINCLUDE = crashreporter.rc
 DEFINES += -DUNICODE -D_UNICODE
 OS_LIBS += $(call EXPAND_LIBNAME,comctl32 shell32 wininet)
+MOZ_WINCONSOLE = 0
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ b/toolkit/crashreporter/client/crashreporter_win.cpp
@@ -40,37 +40,44 @@
 #endif
 
 #include <windows.h>
 #include <commctrl.h>
 #include <richedit.h>
 #include <shellapi.h>
 #include "resource.h"
 #include "client/windows/sender/crash_report_sender.h"
+#include <fstream>
 
 #define CRASH_REPORTER_KEY L"Software\\Mozilla\\Crash Reporter"
 #define CRASH_REPORTER_VALUE L"Enabled"
 
 #define WM_UPLOADCOMPLETE WM_APP
 
+using std::string;
 using std::wstring;
 using std::map;
+using std::ifstream;
 
 bool ReadConfig();
-BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
-BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam,
+                               LPARAM lParam);
+BOOL CALLBACK SendDialogProc(HWND hwndDlg, UINT message, WPARAM wParam,
+                             LPARAM lParam);
 HANDLE CreateSendThread(HWND hDlg, LPCTSTR dumpFile);
 bool CheckCrashReporterEnabled(bool* enabled);
 void SetCrashReporterEnabled(bool enabled);
-bool SendCrashReport(HINSTANCE hInstance, LPCTSTR dumpFile);
+bool SendCrashReport(wstring dumpFile,
+                     const map<wstring,wstring> *query_parameters);
 DWORD WINAPI SendThreadProc(LPVOID param);
 
 typedef struct {
   HWND hDlg;
-  LPCTSTR dumpFile;
+  wstring dumpFile;
+  const map<wstring,wstring> *query_parameters;
 } SENDTHREADDATA;
 
 TCHAR sendURL[2048] = L"\0";
 bool  deleteDump = true;
 
 // Sort of a hack to get l10n
 enum {
   ST_OK,
@@ -94,16 +101,18 @@ LPCTSTR stringNames[] = {
   L"RadioDisable",
   L"SendTitle",
   L"SubmitSuccess",
   L"SubmitFailed"
 };
 
 LPTSTR strings[NUM_STRINGS];
 
+const wchar_t* kExtraDataExtension = L".extra";
+
 void DoInitCommonControls()
 {
 	INITCOMMONCONTROLSEX ic;
 	ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
 	ic.dwICC = ICC_PROGRESS_CLASS;
 	InitCommonControlsEx(&ic);
   // also get the rich edit control
   LoadLibrary(L"riched20.dll");
@@ -138,62 +147,16 @@ bool ReadConfig()
       deleteDump = _wtoi(tmp) > 0;
 
       return LoadStrings(fileName);
     }
   }
   return false;
 }
 
-int WINAPI WinMain(HINSTANCE hInstance,
-    HINSTANCE hPrevInstance,
-    LPSTR lpCmdLine,
-    int nCmdShow)
-
-{
-	bool enabled;
-  LPTSTR* argv = NULL;
-  int argc = 0;
-
-	DoInitCommonControls();
-  if (!ReadConfig()) {
-    MessageBox(NULL, L"Missing crashreporter.ini file", L"Crash Reporter Error", MB_OK | MB_ICONSTOP);
-    return 0;
-  }
-
-  argv = CommandLineToArgvW(GetCommandLine(), &argc);
-
-  if (argc == 1) {
-    // nothing specified, just ask about enabling
-    if (!CheckCrashReporterEnabled(&enabled))
-      enabled = true;
-
-    enabled = (1 == DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)enabled));
-    SetCrashReporterEnabled(enabled);
-  }
-  else {
-    if (!CheckCrashReporterEnabled(&enabled)) {
-      //ask user if crash reporter should be enabled
-      enabled = (1 == DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ENABLEDIALOG), NULL, (DLGPROC)EnableDialogProc, (LPARAM)true));
-      SetCrashReporterEnabled(enabled);
-    }
-    // if enabled, send crash report
-    if (enabled) {
-      if (SendCrashReport(hInstance, argv[1]) && deleteDump)
-        DeleteFile(argv[1]);
-      //TODO: show details?
-    }
-  }
-
-  if (argv)
-    LocalFree(argv);
-	
-	return 0;
-}
-
 BOOL CALLBACK EnableDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) 
 { 
   switch (message) {
   case WM_INITDIALOG:
     SetWindowText(hwndDlg, strings[ST_CRASHREPORTERTITLE]);
     SetDlgItemText(hwndDlg, IDOK, strings[ST_OK]);
     SetDlgItemText(hwndDlg, IDC_RADIOENABLE, strings[ST_RADIOENABLE]);
     SetDlgItemText(hwndDlg, IDC_RADIODISABLE, strings[ST_RADIODISABLE]);
@@ -281,18 +244,19 @@ BOOL CALLBACK SendDialogProc(HWND hwndDl
     {
       //init strings
       SetWindowText(hwndDlg, strings[ST_SENDTITLE]);
       SetDlgItemText(hwndDlg, IDCANCEL, strings[ST_CANCEL]);
       // 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
-      LPCTSTR dumpFile = (LPCTSTR)lParam;
-      hThread = CreateSendThread(hwndDlg, dumpFile);
+      SENDTHREADDATA* td = (SENDTHREADDATA*)lParam;
+      td->hDlg = hwndDlg;
+      CreateThread(NULL, 0, SendThreadProc, td, 0, NULL);
     }
     return TRUE;
 
   case WM_UPLOADCOMPLETE:
     WaitForSingleObject(hThread, INFINITE);
     finishedOk = (wParam == 1);
     if (finishedOk) {
       SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETPOS, 100, 0);
@@ -310,42 +274,160 @@ BOOL CALLBACK SendDialogProc(HWND hwndDl
     }
     return TRUE;
 
   default:
     return FALSE; 
   } 
 }
 
-bool SendCrashReport(HINSTANCE hInstance, LPCTSTR dumpFile)
+bool SendCrashReport(wstring dumpFile,
+                     const map<wstring,wstring> *query_parameters)
 {
-  int res = (int)DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL, (DLGPROC)SendDialogProc, (LPARAM)dumpFile);
+  SENDTHREADDATA td;
+  td.hDlg = NULL;
+  td.dumpFile = dumpFile;
+  td.query_parameters = query_parameters;
+
+  int res = (int)DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
+                                (DLGPROC)SendDialogProc, (LPARAM)&td);
+
   return (res >= 0);
 }
 
 DWORD WINAPI SendThreadProc(LPVOID param)
 {
   bool finishedOk;
   SENDTHREADDATA* td = (SENDTHREADDATA*)param;
-  //XXX: send some extra params?
-  map<wstring, wstring> params;
+
   wstring url(sendURL);
   if (url.empty()) {
     finishedOk = false;
   }
   else {
     finishedOk = google_airbag::CrashReportSender
       ::SendCrashReport(url,
-                        params,
-                        wstring(td->dumpFile));
+                        *(td->query_parameters),
+                        td->dumpFile);
   }
   PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
   delete td;
   return 0;
 }
 
-HANDLE CreateSendThread(HWND hDlg, LPCTSTR dumpFile)
+//XXX: change this to use Breakpad's string conversion functions
+// when we update to latest SVN
+bool ConvertString(const char* utf8_string, wstring& ucs2_string)
+{
+  wchar_t *buffer = NULL;
+  int buffer_size = MultiByteToWideChar(CP_ACP, 0, utf8_string,
+                                        -1, NULL, 0);
+  if(buffer_size == 0)
+    return false;
+
+  buffer = new wchar_t[buffer_size];
+  if(buffer == NULL)
+    return false;
+  
+  MultiByteToWideChar(CP_ACP, 0, utf8_string,
+                      -1, buffer, buffer_size);
+  ucs2_string = buffer;
+  delete [] buffer;
+  return true;
+}
+
+void ReadExtraData(const wstring& filename,
+                   map<wstring, wstring>& query_parameters)
+{
+#if _MSC_VER >= 1400  // MSVC 2005/8
+  ifstream file;
+  file.open(filename.c_str(), std::ios::in);
+#else  // _MSC_VER >= 1400
+  ifstream file(_wfopen(filename.c_str(), L"rb"));
+#endif  // _MSC_VER >= 1400
+  if (file.is_open()) {
+    do {
+      string in_line;
+      std::getline(file, in_line);
+      if (!in_line.empty()) {
+        int pos = in_line.find('=');
+        if (pos >= 0) {
+          wstring key, value;
+          ConvertString(in_line.substr(0, pos).c_str(), key);
+          ConvertString(in_line.substr(pos + 1,
+                                       in_line.length() - pos - 1).c_str(),
+                        value);
+          query_parameters[key] = value;
+        }
+      }
+    } while(!file.eof());
+    file.close();
+  }
+}
+
+wstring GetExtraDataFilename(const wstring& dumpfile)
 {
-  SENDTHREADDATA* td = new SENDTHREADDATA;
-  td->hDlg = hDlg;
-  td->dumpFile = dumpFile;
-  return CreateThread(NULL, 0, SendThreadProc, td, 0, NULL);
+  wstring filename(dumpfile);
+  int dot = filename.rfind('.');
+  if (dot < 0)
+    return L"";
+
+  filename.replace(dot, filename.length() - dot, kExtraDataExtension);
+  return filename;
 }
+
+int main(int argc, char **argv)
+{
+  map<wstring,wstring> query_parameters;
+
+	DoInitCommonControls();
+  if (!ReadConfig()) {
+    MessageBox(NULL, L"Missing crashreporter.ini file", L"Crash Reporter Error", MB_OK | MB_ICONSTOP);
+    return 0;
+  }
+
+  wstring dumpfile;
+  bool enabled = false;
+
+  if (argc > 1) {
+    if (!ConvertString(argv[1], dumpfile))
+      return 0;
+  }
+
+  if (dumpfile.empty()) {
+    // 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);
+  }
+  else {
+    wstring extrafile = GetExtraDataFilename(dumpfile);
+    if (!extrafile.empty())
+      ReadExtraData(extrafile, query_parameters);
+
+    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);
+    }
+    // if enabled, send crash report
+    if (enabled) {
+      if (SendCrashReport(dumpfile, &query_parameters) && deleteDump) {
+        DeleteFile(dumpfile.c_str());
+        if (!extrafile.empty())
+          DeleteFile(extrafile.c_str());
+      }
+      //TODO: show details?
+    }
+  }
+}
+
+#if defined(XP_WIN) && !defined(DEBUG) && !defined(__GNUC__)
+// We need WinMain in order to not be a console app.  This function is unused
+// if we are a console application.
+int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR args, int )
+{
+  // Do the real work.
+  return main(__argc, __argv);
+}
+#endif
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -39,120 +39,327 @@
 
 #ifdef XP_WIN32
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
 #endif
 
 #include "client/windows/handler/exception_handler.h"
 #include <string.h>
+#else
+#error "Not yet implemented for this platform"
+#endif // XP_WIN32
+
+#ifndef HAVE_CPP_2BYTE_WCHAR_T
+#error "This code expects a 2 byte wchar_t.  You should --disable-airbag."
 #endif
 
 #include <stdlib.h>
 #include <prenv.h>
 #include "nsDebug.h"
+#include "nsCRT.h"
+#include "nsILocalFile.h"
 
-static google_airbag::ExceptionHandler* gAirbagExceptionHandler = nsnull;
+namespace CrashReporter {
+
+using std::wstring;
+
+static const PRUnichar dumpFileExtension[] = {'.', 'd', 'm', 'p',
+                                              '\"', '\0'}; // .dmp"
+static const PRUnichar extraFileExtension[] = {'.', 'e', 'x', 't',
+                                              'r', 'a', '\0'}; // .extra
+
+// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+static const PRInt32 kGUIDLength = 36;
+// length of a GUID + .dmp" (yes, trailing double quote)
+static const PRInt32 kMinidumpFilenameLength =
+  kGUIDLength + sizeof(dumpFileExtension) / sizeof(dumpFileExtension[0]);
 
 #ifdef XP_WIN32
-static TCHAR crashReporterExe[MAX_PATH] = L"\0";
-static TCHAR minidumpPath[MAX_PATH] = L"\0";
+NS_NAMED_LITERAL_STRING(crashReporterFilename, "crashreporter.exe");
+#else
+NS_NAMED_LITERAL_STRING(crashReporterFilename, "crashreporter");
 #endif
 
-void AirbagMinidumpCallback(const std::wstring &minidump_id,
-                            void *context, bool succeeded)
+static google_airbag::ExceptionHandler* gExceptionHandler = nsnull;
+
+// for ease of replacing the dump path when someone
+// calls SetMinidumpPath
+static nsString crashReporterCmdLine_withoutDumpPath;
+// this is set up so we don't have to do heap allocation in the handler
+static PRUnichar* crashReporterCmdLine = nsnull;
+// points at the end of the previous string
+// so we can append the minidump filename
+static PRUnichar* crashReporterCmdLineEnd = nsnull;
+// space to hold a filename for the API data
+static PRUnichar* crashReporterAPIDataFilename = nsnull;
+static PRUnichar* crashReporterAPIDataFilenameEnd = nsnull;
+
+// this holds additional data sent via the API
+static nsCString crashReporterAPIData;
+
+static void MinidumpCallback(const wstring &minidump_id,
+                             void *context, bool succeeded)
 {
+  // append minidump filename to command line
+  memcpy(crashReporterCmdLineEnd, minidump_id.c_str(),
+         kGUIDLength * sizeof(PRUnichar));
+  // this will copy the null terminator as well
+  memcpy(crashReporterCmdLineEnd + kGUIDLength,
+         dumpFileExtension, sizeof(dumpFileExtension));
+
+  // append minidump filename to API data filename
+  memcpy(crashReporterAPIDataFilenameEnd, minidump_id.c_str(),
+         kGUIDLength * sizeof(PRUnichar));
+  // this will copy the null terminator as well
+  memcpy(crashReporterAPIDataFilenameEnd + kGUIDLength,
+         extraFileExtension, sizeof(extraFileExtension));
+
 #ifdef XP_WIN32
+  if (!crashReporterAPIData.IsEmpty()) {
+    // write out API data
+    HANDLE hFile = CreateFile(crashReporterAPIDataFilename, GENERIC_WRITE, 0,
+                              NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+                              NULL);
+    if(hFile != INVALID_HANDLE_VALUE) {
+      DWORD nBytes;
+      WriteFile(hFile, crashReporterAPIData.get(),
+                crashReporterAPIData.Length(), &nBytes, NULL);
+      CloseHandle(hFile);
+    }
+  }
+
   STARTUPINFO si;
   PROCESS_INFORMATION pi;
-  TCHAR cmdLine[2*MAX_PATH];
 
   ZeroMemory(&si, sizeof(si));
   si.cb = sizeof(si);
   si.dwFlags = STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_SHOWNORMAL;
   ZeroMemory(&pi, sizeof(pi));
 
-  wcscat(minidumpPath, minidump_id.c_str());
-  wcscat(minidumpPath, L".dmp");
-  wsprintf(cmdLine, L"\"%s\" \"%s\"", crashReporterExe, minidumpPath);
-  
-  if (CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
+  if (CreateProcess(NULL, (LPWSTR)crashReporterCmdLine, NULL, NULL, FALSE, 0,
+                    NULL, NULL, &si, &pi)) {
     CloseHandle( pi.hProcess );
     CloseHandle( pi.hThread );
   }
   // we're not really in a position to do anything if the CreateProcess fails
   TerminateProcess(GetCurrentProcess(), 1);
 #endif
 }
 
-nsresult SetAirbagExceptionHandler()
+static nsresult BuildCommandLine(const nsAString &tempPath)
+{
+  nsString crashReporterCmdLine_temp =
+    crashReporterCmdLine_withoutDumpPath + NS_LITERAL_STRING(" \"") + tempPath;
+  PRInt32 cmdLineLength = crashReporterCmdLine_temp.Length();
+
+  // allocate extra space for minidump file name
+  crashReporterCmdLine_temp.SetLength(cmdLineLength + kMinidumpFilenameLength
+                                      + 1);
+  crashReporterCmdLine = ToNewUnicode(crashReporterCmdLine_temp);
+  crashReporterCmdLineEnd = crashReporterCmdLine + cmdLineLength;
+
+  // build API data filename
+  if(crashReporterAPIDataFilename != nsnull) {
+    NS_Free(crashReporterAPIDataFilename);
+    crashReporterAPIDataFilename = nsnull;
+  }
+
+  nsString apiDataFilename_temp(tempPath);
+  PRInt32 filenameLength = apiDataFilename_temp.Length();
+  apiDataFilename_temp.SetLength(filenameLength + kMinidumpFilenameLength + 1);
+  crashReporterAPIDataFilename = ToNewUnicode(apiDataFilename_temp);
+  crashReporterAPIDataFilenameEnd =
+    crashReporterAPIDataFilename + filenameLength;
+
+  return NS_OK;
+}
+
+static nsresult GetExecutablePath(nsString& exePath)
 {
-  if (gAirbagExceptionHandler)
+#ifdef XP_WIN32
+  // sort of arbitrary, but MAX_PATH is kinda small
+  exePath.SetLength(4096);
+  if (!GetModuleFileName(NULL, (LPWSTR)exePath.BeginWriting(), 4096))
+    return NS_ERROR_FAILURE;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+
+#ifdef XP_WIN32
+  NS_NAMED_LITERAL_STRING(pathSep, "\\");
+#else
+  NS_NAMED_LITERAL_STRING(pathSep, "/");
+#endif
+
+  PRInt32 lastSlash = exePath.RFind(pathSep);
+  if (lastSlash < 0)
+    return NS_ERROR_FAILURE;
+
+  exePath.Truncate(lastSlash + 1);
+
+  return NS_OK;
+}
+
+nsresult SetExceptionHandler(nsILocalFile* aXREDirectory)
+{
+  nsresult rv;
+
+  if (gExceptionHandler)
     return NS_ERROR_ALREADY_INITIALIZED;
 
   // check environment var to see if we're enabled.
   // we're off by default until we sort out the
   // rest of the infrastructure,
   // so it must exist and be set to a non-zero value.
   const char *airbagEnv = PR_GetEnv("MOZ_AIRBAG");
   if (airbagEnv == NULL || atoi(airbagEnv) == 0)
-    return NS_OK;
+    return NS_ERROR_NOT_AVAILABLE;
+
+  // locate crashreporter executable
+  nsString exePath;
 
+  if (aXREDirectory) {
+    aXREDirectory->GetPath(exePath);
+  }
+  else {
+    rv = GetExecutablePath(exePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // note that we enclose the exe filename in double quotes
+  crashReporterCmdLine_withoutDumpPath = NS_LITERAL_STRING("\"") +
+    exePath + crashReporterFilename + NS_LITERAL_STRING("\"");
+
+  // get temp path to use for minidump path
+  nsString tempPath;
 #ifdef XP_WIN32
-  //TODO: check registry to see if crash reporting is enabled
-  if (!GetTempPath(MAX_PATH, minidumpPath))
+  // first figure out buffer size
+  int pathLen = GetTempPath(0, NULL);
+  if (pathLen == 0)
     return NS_ERROR_FAILURE;
 
-  std::wstring tempStr(minidumpPath);
-  gAirbagExceptionHandler = new google_airbag::ExceptionHandler(tempStr,
-                                                 AirbagMinidumpCallback,
-                                                 nsnull,
-                                                 true);
-  if (GetModuleFileName(NULL, crashReporterExe, MAX_PATH)) {
-    // get crashreporter exe
-    LPTSTR s = wcsrchr(crashReporterExe, '\\');
-    if (s) {
-      s++;
-      wcscpy(s, L"crashreporter.exe");
-    }
-  }
-#else
-  return NS_ERROR_NOT_IMPLEMENTED;
+  tempPath.SetLength(pathLen);
+  GetTempPath(pathLen, (LPWSTR)tempPath.BeginWriting());
 #endif
 
-  if (!gAirbagExceptionHandler)
+  rv = BuildCommandLine(tempPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // finally, set the exception handler
+  gExceptionHandler = new google_airbag::ExceptionHandler(
+                                            PromiseFlatString(tempPath).get(),
+                                            MinidumpCallback,
+                                            nsnull,
+                                            true);
+
+  if (!gExceptionHandler)
     return NS_ERROR_OUT_OF_MEMORY;
 
   return NS_OK;
 }
 
-nsresult SetAirbagMinidumpPath(const nsAString* aPath)
+nsresult SetMinidumpPath(const nsAString& aPath)
 {
-  NS_ENSURE_ARG_POINTER(aPath);
-
-  if (!gAirbagExceptionHandler)
+  if (!gExceptionHandler)
     return NS_ERROR_NOT_INITIALIZED;
 
-  std::wstring path;
+  if(crashReporterCmdLine != nsnull) {
+    NS_Free(crashReporterCmdLine);
+    crashReporterCmdLine = nsnull;
+  }
+
 #ifdef XP_WIN32
-  wcsncpy(minidumpPath, PromiseFlatString(*aPath).get(), MAX_PATH);
-  path = std::wstring(minidumpPath);
-  int l = wcslen(minidumpPath);
-  if (minidumpPath[l-1] != '\\' && l < MAX_PATH - 1) {
-    minidumpPath[l] = '\\';
-    minidumpPath[l+1] = '\0';
+  NS_NAMED_LITERAL_STRING(pathSep, "\\");
+#else
+  NS_NAMED_LITERAL_STRING(pathSep, "/");
+#endif
+
+  nsresult rv;
+
+  if(!StringEndsWith(aPath, pathSep)) {
+    rv = BuildCommandLine(aPath + pathSep);
   }
-#endif
-  gAirbagExceptionHandler->set_dump_path(path);
+  else {
+    rv = BuildCommandLine(aPath);
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  gExceptionHandler->set_dump_path(PromiseFlatString(aPath).get());
   return NS_OK;
 }
 
-nsresult UnsetAirbagExceptionHandler()
+nsresult UnsetExceptionHandler()
 {
-  if (!gAirbagExceptionHandler)
+  if (!gExceptionHandler)
     return NS_ERROR_NOT_INITIALIZED;
 
-  delete gAirbagExceptionHandler;
-  gAirbagExceptionHandler = nsnull;
+  delete gExceptionHandler;
+  gExceptionHandler = nsnull;
+
+  if(crashReporterCmdLine != nsnull) {
+    NS_Free(crashReporterCmdLine);
+    crashReporterCmdLine = nsnull;
+  }
+
+  if(crashReporterAPIDataFilename != nsnull) {
+    NS_Free(crashReporterAPIDataFilename);
+    crashReporterAPIDataFilename = nsnull;
+  }
+
+  crashReporterCmdLineEnd = nsnull;
 
   return NS_OK;
 }
+
+static void ReplaceChar(nsCString& str, const nsACString& character,
+                        const nsACString& replacement)
+{
+  nsCString::const_iterator start, end;
+  
+  str.BeginReading(start);
+  str.EndReading(end);
+
+  while (FindInReadable(character, start, end)) {
+    PRInt32 pos = end.size_backward();
+    str.Replace(pos - 1, 1, replacement);
+
+    str.BeginReading(start);
+    start.advance(pos + replacement.Length() - 1);
+    str.EndReading(end);
+  }
+}
+
+static PRBool DoFindInReadable(const nsACString& str, const nsACString& value)
+{
+  nsACString::const_iterator start, end;
+  str.BeginReading(start);
+  str.EndReading(end);
+
+  return FindInReadable(value, start, end);
+}
+
+nsresult AnnotateCrashReport(const nsACString &key, const nsACString &data)
+{
+  if (!gExceptionHandler)
+    return NS_ERROR_NOT_INITIALIZED;
+
+  if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
+      DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
+    return NS_ERROR_INVALID_ARG;
+
+  nsCString escapedData(data);
+  
+  // escape backslashes
+  ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
+              NS_LITERAL_CSTRING("\\\\"));
+  // escape newlines
+  ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
+              NS_LITERAL_CSTRING("\\n"));
+
+  crashReporterAPIData.Append(key + NS_LITERAL_CSTRING("=") + escapedData +
+                              NS_LITERAL_CSTRING("\n"));
+  return NS_OK;
+}
+
+} // namespace CrashReporter
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -37,13 +37,16 @@
 
 #ifndef nsAirbagExceptionHandler_h__
 #define nsAirbagExceptionHandler_h__
 
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsStringGlue.h"
 
-nsresult SetAirbagExceptionHandler();
-nsresult SetAirbagMinidumpPath(const nsAString* aPath);
-nsresult UnsetAirbagExceptionHandler();
+namespace CrashReporter {
+nsresult SetExceptionHandler(nsILocalFile* aXREDirectory);
+nsresult SetMinidumpPath(const nsAString& aPath);
+nsresult UnsetExceptionHandler();
+nsresult AnnotateCrashReport(const nsACString &key, const nsACString &data);
+}
 
 #endif /* nsAirbagExceptionHandler_h__ */