toolkit/crashreporter/client/crashreporter_win.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #ifdef WIN32_LEAN_AND_MEAN
     7 #undef WIN32_LEAN_AND_MEAN
     8 #endif
    10 #define NOMINMAX
    12 #include "crashreporter.h"
    14 #include <windows.h>
    15 #include <commctrl.h>
    16 #include <richedit.h>
    17 #include <shellapi.h>
    18 #include <shlobj.h>
    19 #include <shlwapi.h>
    20 #include <math.h>
    21 #include <set>
    22 #include <algorithm>
    23 #include "resource.h"
    24 #include "client/windows/sender/crash_report_sender.h"
    25 #include "common/windows/string_utils-inl.h"
    26 #include "mozilla/NullPtr.h"
    28 #define CRASH_REPORTER_VALUE L"Enabled"
    29 #define SUBMIT_REPORT_VALUE  L"SubmitCrashReport"
    30 #define SUBMIT_REPORT_OLD    L"SubmitReport"
    31 #define INCLUDE_URL_VALUE    L"IncludeURL"
    32 #define EMAIL_ME_VALUE       L"EmailMe"
    33 #define EMAIL_VALUE          L"Email"
    34 #define MAX_EMAIL_LENGTH     1024
    36 #define WM_UPLOADCOMPLETE WM_APP
    38 // Thanks, Windows.h :(
    39 #undef min
    40 #undef max
    42 using std::string;
    43 using std::wstring;
    44 using std::map;
    45 using std::vector;
    46 using std::set;
    47 using std::ios;
    48 using std::ifstream;
    49 using std::ofstream;
    51 using namespace CrashReporter;
    53 typedef struct {
    54   HWND hDlg;
    55   wstring dumpFile;
    56   map<wstring,wstring> queryParameters;
    57   wstring sendURL;
    59   wstring serverResponse;
    60 } SendThreadData;
    62 /*
    63  * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx
    64  * "The DLGTEMPLATEEX structure is not defined in any standard header file.
    65  * The structure definition is provided here to explain the format of an
    66  * extended template for a dialog box.
    67 */
    68 typedef struct {
    69     WORD dlgVer;
    70     WORD signature;
    71     DWORD helpID;
    72     DWORD exStyle;
    73   // There's more to this struct, but it has weird variable-length
    74   // members, and I only actually need to touch exStyle on an existing
    75   // instance, so I've omitted the rest.
    76 } DLGTEMPLATEEX;
    78 static HANDLE               gThreadHandle;
    79 static SendThreadData       gSendData = { 0, };
    80 static vector<string>       gRestartArgs;
    81 static map<wstring,wstring> gQueryParameters;
    82 static wstring              gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
    83 static wstring              gURLParameter;
    84 static int                  gCheckboxPadding = 6;
    85 static bool                 gRTLlayout = false;
    87 // When vertically resizing the dialog, these items should move down
    88 static set<UINT> gAttachedBottom;
    90 // Default set of items for gAttachedBottom
    91 static const UINT kDefaultAttachedBottom[] = {
    92   IDC_SUBMITREPORTCHECK,
    93   IDC_VIEWREPORTBUTTON,
    94   IDC_COMMENTTEXT,
    95   IDC_INCLUDEURLCHECK,
    96   IDC_EMAILMECHECK,
    97   IDC_EMAILTEXT,
    98   IDC_PROGRESSTEXT,
    99   IDC_THROBBER,
   100   IDC_CLOSEBUTTON,
   101   IDC_RESTARTBUTTON,
   102 };
   104 static wstring UTF8ToWide(const string& utf8, bool *success = 0);
   105 static DWORD WINAPI SendThreadProc(LPVOID param);
   107 static wstring Str(const char* key)
   108 {
   109   return UTF8ToWide(gStrings[key]);
   110 }
   112 /* === win32 helper functions === */
   114 static void DoInitCommonControls()
   115 {
   116   INITCOMMONCONTROLSEX ic;
   117   ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
   118   ic.dwICC = ICC_PROGRESS_CLASS;
   119   InitCommonControlsEx(&ic);
   120   // also get the rich edit control
   121   LoadLibrary(L"Msftedit.dll");
   122 }
   124 static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
   125 {
   126   DWORD type, dataSize;
   127   dataSize = sizeof(DWORD);
   128   if (RegQueryValueEx(hRegKey, valueName, nullptr,
   129                       &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS &&
   130       type == REG_DWORD)
   131     return true;
   133   return false;
   134 }
   136 // Removes a value from HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER, if it exists.
   137 static void RemoveUnusedValues(const wchar_t* key, LPCTSTR valueName)
   138 {
   139   HKEY hRegKey;
   141   if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_SET_VALUE, &hRegKey) 
   142       == ERROR_SUCCESS) {
   143     RegDeleteValue(hRegKey, valueName);
   144     RegCloseKey(hRegKey);
   145   }
   147   if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_SET_VALUE, &hRegKey) 
   148       == ERROR_SUCCESS) {
   149     RegDeleteValue(hRegKey, valueName);
   150     RegCloseKey(hRegKey);
   151   }
   152 }
   154 static bool CheckBoolKey(const wchar_t* key,
   155                          const wchar_t* valueName,
   156                          bool* enabled)
   157 {
   158   /*
   159    * NOTE! This code needs to stay in sync with the preference checking
   160    *       code in in nsExceptionHandler.cpp.
   161    */
   162   *enabled = false;
   163   bool found = false;
   164   HKEY hRegKey;
   165   DWORD val;
   166   // see if our reg key is set globally
   167   if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
   168     if (GetBoolValue(hRegKey, valueName, &val)) {
   169       *enabled = (val == 1);
   170       found = true;
   171     }
   172     RegCloseKey(hRegKey);
   173   } else {
   174     // look for it in user settings
   175     if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   176       if (GetBoolValue(hRegKey, valueName, &val)) {
   177         *enabled = (val == 1);
   178         found = true;
   179       }
   180       RegCloseKey(hRegKey);
   181     }
   182   }
   184   return found;
   185 }
   187 static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
   188 {
   189   /*
   190    * NOTE! This code needs to stay in sync with the preference setting
   191    *       code in in nsExceptionHandler.cpp.
   192    */
   193   HKEY hRegKey;
   195   // remove the old value from the registry if it exists
   196   RemoveUnusedValues(key, SUBMIT_REPORT_OLD);
   198   if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   199     DWORD data = (enabled ? 1 : 0);
   200     RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
   201     RegCloseKey(hRegKey);
   202   }
   203 }
   205 static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value)
   206 {
   207   DWORD type, dataSize;
   208   wchar_t buf[2048];
   209   dataSize = sizeof(buf);
   210   if (RegQueryValueEx(hRegKey, valueName, nullptr,
   211                      &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS &&
   212       type == REG_SZ) {
   213     value = buf;
   214     return true;
   215   }
   217   return false;
   218 }
   220 static bool GetStringKey(const wchar_t* key,
   221                          const wchar_t* valueName,
   222                          wstring& value)
   223 {
   224   value = L"";
   225   bool found = false;
   226   HKEY hRegKey;
   227   // see if our reg key is set globally
   228   if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
   229     if (GetStringValue(hRegKey, valueName, value)) {
   230       found = true;
   231     }
   232     RegCloseKey(hRegKey);
   233   } else {
   234     // look for it in user settings
   235     if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   236       if (GetStringValue(hRegKey, valueName, value)) {
   237         found = true;
   238       }
   239       RegCloseKey(hRegKey);
   240     }
   241   }
   243   return found;
   244 }
   246 static void SetStringKey(const wchar_t* key,
   247                          const wchar_t* valueName,
   248                          const wstring& value)
   249 {
   250   HKEY hRegKey;
   251   if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   252     RegSetValueEx(hRegKey, valueName, 0, REG_SZ,
   253                   (LPBYTE)value.c_str(),
   254                   (value.length() + 1) * sizeof(wchar_t));
   255     RegCloseKey(hRegKey);
   256   }
   257 }
   259 static string FormatLastError()
   260 {
   261   DWORD err = GetLastError();
   262   LPWSTR s;
   263   string message = "Crash report submission failed: ";
   264   // odds are it's a WinInet error
   265   HANDLE hInetModule = GetModuleHandle(L"WinInet.dll");
   266   if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
   267                    FORMAT_MESSAGE_FROM_SYSTEM |
   268                    FORMAT_MESSAGE_FROM_HMODULE,
   269                    hInetModule,
   270                    err,
   271                    0,
   272                    (LPWSTR)&s,
   273                    0,
   274                    nullptr) != 0) {
   275     message += WideToUTF8(s, nullptr);
   276     LocalFree(s);
   277     // strip off any trailing newlines
   278     string::size_type n = message.find_last_not_of("\r\n");
   279     if (n < message.size() - 1) {
   280       message.erase(n+1);
   281     }
   282   }
   283   else {
   284     char buf[64];
   285     sprintf(buf, "Unknown error, error code: 0x%08x", err);
   286     message += buf;
   287   }
   288   return message;
   289 }
   291 #define TS_DRAW 2
   292 #define BP_CHECKBOX  3
   294 typedef  HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
   295 typedef  HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
   296 typedef  HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
   297                                               int iStateId, RECT* prc, int ts,
   298                                               SIZE* psz);
   299 typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
   300                                                 int iStateId, const RECT* pRect,
   301                                                 RECT* pContentRect);
   304 static void GetThemeSizes(HWND hwnd)
   305 {
   306   HMODULE themeDLL = LoadLibrary(L"uxtheme.dll");
   308   if (!themeDLL)
   309     return;
   311   OpenThemeDataPtr openTheme = 
   312     (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData");
   313   CloseThemeDataPtr closeTheme =
   314     (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData");
   315   GetThemePartSizePtr getThemePartSize = 
   316     (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize");
   318   if (!openTheme || !closeTheme || !getThemePartSize) {
   319     FreeLibrary(themeDLL);
   320     return;
   321   }
   323   HANDLE buttonTheme = openTheme(hwnd, L"Button");
   324   if (!buttonTheme) {
   325     FreeLibrary(themeDLL);
   326     return;
   327   }
   328   HDC hdc = GetDC(hwnd);
   329   SIZE s;
   330   getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, nullptr, TS_DRAW, &s);
   331   gCheckboxPadding = s.cx;
   332   closeTheme(buttonTheme);
   333   FreeLibrary(themeDLL);
   334 }
   336 // Gets the position of a window relative to another window's client area
   337 static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r)
   338 {
   339   GetWindowRect(hwnd, r);
   340   MapWindowPoints(nullptr, hwndParent, (POINT*)r, 2);
   341 }
   343 static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
   344 {
   345   HWND hwnd = GetDlgItem(hwndDlg, item);
   347   ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE);
   348 }
   350 static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled)
   351 {
   352   HWND hwnd = GetDlgItem(hwndDlg, item);
   353   LONG style = GetWindowLong(hwnd, GWL_STYLE);
   354   if (!disabled)
   355     style |= WS_DISABLED;
   356   else
   357     style &= ~WS_DISABLED;
   359   SetWindowLong(hwnd, GWL_STYLE, style);
   360 }
   362 /* === Crash Reporting Dialog === */
   364 static void StretchDialog(HWND hwndDlg, int ydiff)
   365 {
   366   RECT r;
   367   GetWindowRect(hwndDlg, &r);
   368   r.bottom += ydiff;
   369   MoveWindow(hwndDlg, r.left, r.top,
   370              r.right - r.left, r.bottom - r.top, TRUE);
   371 }
   373 static void ReflowDialog(HWND hwndDlg, int ydiff)
   374 {
   375   // Move items attached to the bottom down/up by as much as
   376   // the window resize
   377   for (set<UINT>::const_iterator item = gAttachedBottom.begin();
   378        item != gAttachedBottom.end();
   379        item++) {
   380     RECT r;
   381     HWND hwnd = GetDlgItem(hwndDlg, *item);
   382     GetRelativeRect(hwnd, hwndDlg, &r);
   383     r.top += ydiff;
   384     r.bottom += ydiff;
   385     MoveWindow(hwnd, r.left, r.top,
   386                r.right - r.left, r.bottom - r.top, TRUE);
   387   }
   388 }
   390 static DWORD WINAPI SendThreadProc(LPVOID param)
   391 {
   392   bool finishedOk;
   393   SendThreadData* td = (SendThreadData*)param;
   395   if (td->sendURL.empty()) {
   396     finishedOk = false;
   397     LogMessage("No server URL, not sending report");
   398   } else {
   399     google_breakpad::CrashReportSender sender(L"");
   400     finishedOk = (sender.SendCrashReport(td->sendURL,
   401                                          td->queryParameters,
   402                                          td->dumpFile,
   403                                          &td->serverResponse)
   404                   == google_breakpad::RESULT_SUCCEEDED);
   405     if (finishedOk) {
   406       LogMessage("Crash report submitted successfully");
   407     }
   408     else {
   409       // get an error string and print it to the log
   410       //XXX: would be nice to get the HTTP status code here, filed:
   411       // http://code.google.com/p/google-breakpad/issues/detail?id=220
   412       LogMessage(FormatLastError());
   413     }
   414   }
   416   PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
   418   return 0;
   419 }
   421 static void EndCrashReporterDialog(HWND hwndDlg, int code)
   422 {
   423   // Save the current values to the registry
   424   wchar_t email[MAX_EMAIL_LENGTH];
   425   GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
   426                   sizeof(email) / sizeof(email[0]));
   427   SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email);
   429   SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE,
   430              IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0);
   431   SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE,
   432              IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0);
   433   SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE,
   434              IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
   436   EndDialog(hwndDlg, code);
   437 }
   439 static void MaybeResizeProgressText(HWND hwndDlg)
   440 {
   441   HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT);
   442   HDC hdc = GetDC(hwndProgress);
   443   HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0);
   444   if (hfont)
   445     SelectObject(hdc, hfont);
   446   SIZE size;
   447   RECT rect;
   448   GetRelativeRect(hwndProgress, hwndDlg, &rect);
   450   wchar_t text[1024];
   451   GetWindowText(hwndProgress, text, 1024);
   453   if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size))
   454     return;
   456   if (size.cx < (rect.right - rect.left))
   457     return;
   459   // Figure out how much we need to resize things vertically
   460   // This is sort of a fudge, but it should be good enough.
   461   int wantedHeight = size.cy *
   462     (int)ceil((float)size.cx / (float)(rect.right - rect.left));
   463   int diff = wantedHeight - (rect.bottom - rect.top);
   464   if (diff <= 0)
   465     return;
   467   MoveWindow(hwndProgress, rect.left, rect.top,
   468              rect.right - rect.left,
   469              wantedHeight,
   470              TRUE);
   472   gAttachedBottom.clear();
   473   gAttachedBottom.insert(IDC_CLOSEBUTTON);
   474   gAttachedBottom.insert(IDC_RESTARTBUTTON);
   476   StretchDialog(hwndDlg, diff);
   478   for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
   479     gAttachedBottom.insert(kDefaultAttachedBottom[i]);
   480   }
   481 }
   483 static void MaybeSendReport(HWND hwndDlg)
   484 {
   485   if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
   486     EndCrashReporterDialog(hwndDlg, 0);
   487     return;
   488   }
   490   // disable all the form controls
   491   EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false);
   492   EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false);
   493   EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false);
   494   EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false);
   495   EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false);
   496   EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
   497   EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false);
   498   EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false);
   500   SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str());
   501   MaybeResizeProgressText(hwndDlg);
   502   // start throbber
   503   // play entire AVI, and loop
   504   Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1);
   505   SetDlgItemVisible(hwndDlg, IDC_THROBBER, true);
   506   gThreadHandle = nullptr;
   507   gSendData.hDlg = hwndDlg;
   508   gSendData.queryParameters = gQueryParameters;
   510   gThreadHandle = CreateThread(nullptr, 0, SendThreadProc, &gSendData, 0,
   511                                nullptr);
   512 }
   514 static void RestartApplication()
   515 {
   516   wstring cmdLine;
   518   for (unsigned int i = 0; i < gRestartArgs.size(); i++) {
   519     cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" ";
   520   }
   522   STARTUPINFO si;
   523   PROCESS_INFORMATION pi;
   525   ZeroMemory(&si, sizeof(si));
   526   si.cb = sizeof(si);
   527   si.dwFlags = STARTF_USESHOWWINDOW;
   528   si.wShowWindow = SW_SHOWNORMAL;
   529   ZeroMemory(&pi, sizeof(pi));
   531   if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
   532                     0, nullptr, nullptr, &si, &pi)) {
   533     CloseHandle(pi.hProcess);
   534     CloseHandle(pi.hThread);
   535   }
   536 }
   538 static void ShowReportInfo(HWND hwndDlg)
   539 {
   540   wstring description;
   542   for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin();
   543        i != gQueryParameters.end();
   544        i++) {
   545     description += i->first;
   546     description += L": ";
   547     description += i->second;
   548     description += L"\n";
   549   }
   551   description += L"\n";
   552   description += Str(ST_EXTRAREPORTINFO);
   554   SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str());
   555 }
   557 static void UpdateURL(HWND hwndDlg)
   558 {
   559   if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) {
   560     gQueryParameters[L"URL"] = gURLParameter;
   561   } else {
   562     gQueryParameters.erase(L"URL");
   563   }
   564 }
   566 static void UpdateEmail(HWND hwndDlg)
   567 {
   568   if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) {
   569     wchar_t email[MAX_EMAIL_LENGTH];
   570     GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
   571                     sizeof(email) / sizeof(email[0]));
   572     gQueryParameters[L"Email"] = email;
   573     if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK))
   574       EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true);
   575   } else {
   576     gQueryParameters.erase(L"Email");
   577     EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
   578   }
   579 }
   581 static void UpdateComment(HWND hwndDlg)
   582 {
   583   wchar_t comment[MAX_COMMENT_LENGTH + 1];
   584   GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment,
   585                   sizeof(comment) / sizeof(comment[0]));
   586   if (wcslen(comment) > 0)
   587     gQueryParameters[L"Comments"] = comment;
   588   else
   589     gQueryParameters.erase(L"Comments");
   590 }
   592 /*
   593  * Dialog procedure for the "view report" dialog.
   594  */
   595 static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message,
   596                                           WPARAM wParam, LPARAM lParam)
   597 {
   598   switch (message) {
   599   case WM_INITDIALOG: {
   600     SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str());    
   601     SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
   602     SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
   603                        EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
   604     ShowReportInfo(hwndDlg);
   605     SetFocus(GetDlgItem(hwndDlg, IDOK));
   606     return FALSE;
   607   }
   609   case WM_COMMAND: {
   610     if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
   611       EndDialog(hwndDlg, 0);
   612     return FALSE;
   613   }
   614   }
   615   return FALSE;
   616 }
   618 // Return the number of bytes this string will take encoded
   619 // in UTF-8
   620 static inline int BytesInUTF8(wchar_t* str)
   621 {
   622   // Just count size of buffer for UTF-8, minus one
   623   // (we don't need to count the null terminator)
   624   return WideCharToMultiByte(CP_UTF8, 0, str, -1,
   625                              nullptr, 0, nullptr, nullptr) - 1;
   626 }
   628 // Calculate the length of the text in this edit control (in bytes,
   629 // in the UTF-8 encoding) after replacing the current selection
   630 // with |insert|.
   631 static int NewTextLength(HWND hwndEdit, wchar_t* insert)
   632 {
   633   wchar_t current[MAX_COMMENT_LENGTH + 1];
   635   GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1);
   636   DWORD selStart, selEnd;
   637   SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
   639   int selectionLength = 0;
   640   if (selEnd - selStart > 0) {
   641     wchar_t selection[MAX_COMMENT_LENGTH + 1];
   642     google_breakpad::WindowsStringUtils::safe_wcsncpy(selection,
   643                                                       MAX_COMMENT_LENGTH + 1,
   644                                                       current + selStart,
   645                                                       selEnd - selStart);
   646     selection[selEnd - selStart] = '\0';
   647     selectionLength = BytesInUTF8(selection);
   648   }
   650   // current string length + replacement text length
   651   // - replaced selection length
   652   return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength;
   653 }
   655 // Window procedure for subclassing edit controls
   656 static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
   657                                          LPARAM lParam)
   658 {
   659   static WNDPROC super = nullptr;
   661   if (super == nullptr)
   662     super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
   664   switch (uMsg) {
   665   case WM_PAINT: {
   666     HDC hdc;
   667     PAINTSTRUCT ps;
   668     RECT r;
   669     wchar_t windowText[1024];
   671     GetWindowText(hwnd, windowText, 1024);
   672     // if the control contains text or is focused, draw it normally
   673     if (GetFocus() == hwnd || windowText[0] != '\0')
   674       return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
   676     GetClientRect(hwnd, &r);
   677     hdc = BeginPaint(hwnd, &ps);
   678     FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd)
   679                                        ? COLOR_WINDOW : COLOR_BTNFACE));
   680     SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
   681     SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT));
   682     SetBkMode(hdc, TRANSPARENT);
   683     wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT");
   684     // Get the actual edit control rect
   685     CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r);
   686     UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL;
   687     if (gRTLlayout)
   688       format |= DT_RIGHT;
   689     if (txt)
   690       DrawText(hdc, txt, wcslen(txt), &r, format);
   691     EndPaint(hwnd, &ps);
   692     return 0;
   693   }
   695     // We handle WM_CHAR and WM_PASTE to limit the comment box to 500
   696     // bytes in UTF-8.
   697   case WM_CHAR: {
   698     // Leave accelerator keys and non-printing chars (except LF) alone
   699     if (wParam & (1<<24) || wParam & (1<<29) ||
   700         (wParam < ' ' && wParam != '\n'))
   701       break;
   703     wchar_t ch[2] = { (wchar_t)wParam, 0 };
   704     if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH)
   705       return 0;
   707     break;
   708   }
   710   case WM_PASTE: {
   711     if (IsClipboardFormatAvailable(CF_UNICODETEXT) &&
   712         OpenClipboard(hwnd)) {
   713       HGLOBAL hg = GetClipboardData(CF_UNICODETEXT); 
   714       wchar_t* pastedText = (wchar_t*)GlobalLock(hg);
   715       int newSize = 0;
   717       if (pastedText)
   718         newSize = NewTextLength(hwnd, pastedText);
   720       GlobalUnlock(hg);
   721       CloseClipboard();
   723       if (newSize > MAX_COMMENT_LENGTH)
   724         return 0;
   725     }
   726     break;
   727   }
   729   case WM_SETFOCUS:
   730   case WM_KILLFOCUS: {
   731     RECT r;
   732     GetClientRect(hwnd, &r);
   733     InvalidateRect(hwnd, &r, TRUE);
   734     break;
   735   }
   737   case WM_DESTROY: {
   738     // cleanup our property
   739     HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT");
   740     if (hData)
   741       GlobalFree(hData);
   742   }
   743   }
   745   return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
   746 }
   748 // Resize a control to fit this text
   749 static int ResizeControl(HWND hwndButton, RECT& rect, wstring text,
   750                          bool shiftLeft, int userDefinedPadding)
   751 {
   752   HDC hdc = GetDC(hwndButton);
   753   HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0);
   754   if (hfont)
   755     SelectObject(hdc, hfont);
   756   SIZE size, oldSize;
   757   int sizeDiff = 0;
   759   wchar_t oldText[1024];
   760   GetWindowText(hwndButton, oldText, 1024);
   762   if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size)
   763       // default text on the button
   764       && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) {
   765     /*
   766      Expand control widths to accomidate wider text strings. For most
   767      controls (including buttons) the text padding is defined by the
   768      dialog's rc file. Some controls (such as checkboxes) have padding
   769      that extends to the end of the dialog, in which case we ignore the
   770      rc padding and rely on a user defined value passed in through
   771      userDefinedPadding.
   772     */
   773     int textIncrease = size.cx - oldSize.cx;
   774     if (textIncrease < 0)
   775       return 0;
   776     int existingTextPadding;
   777     if (userDefinedPadding == 0) 
   778       existingTextPadding = (rect.right - rect.left) - oldSize.cx;
   779     else 
   780       existingTextPadding = userDefinedPadding;
   781     sizeDiff = textIncrease + existingTextPadding;
   783     if (shiftLeft) {
   784       // shift left by the amount the button should grow
   785       rect.left -= sizeDiff;
   786     }
   787     else {
   788       // grow right instead
   789       rect.right += sizeDiff;
   790     }
   791     MoveWindow(hwndButton, rect.left, rect.top,
   792                rect.right - rect.left,
   793                rect.bottom - rect.top,
   794                TRUE);
   795   }
   796   return sizeDiff;
   797 }
   799 // The window was resized horizontally, so widen some of our
   800 // controls to make use of the space
   801 static void StretchControlsToFit(HWND hwndDlg)
   802 {
   803   int controls[] = {
   804     IDC_DESCRIPTIONTEXT,
   805     IDC_SUBMITREPORTCHECK,
   806     IDC_COMMENTTEXT,
   807     IDC_INCLUDEURLCHECK,
   808     IDC_EMAILMECHECK,
   809     IDC_EMAILTEXT,
   810     IDC_PROGRESSTEXT
   811   };
   813   RECT dlgRect;
   814   GetClientRect(hwndDlg, &dlgRect);
   816   for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) {
   817     RECT r;
   818     HWND hwndControl = GetDlgItem(hwndDlg, controls[i]);
   819     GetRelativeRect(hwndControl, hwndDlg, &r);
   820     // 6 pixel spacing on the right
   821     if (r.right + 6 != dlgRect.right) {
   822       r.right = dlgRect.right - 6;
   823       MoveWindow(hwndControl, r.left, r.top,
   824                  r.right - r.left,
   825                  r.bottom - r.top,
   826                  TRUE);
   827     }
   828   }
   829 }
   831 static void SubmitReportChecked(HWND hwndDlg)
   832 {
   833   bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
   834   EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled);
   835   EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled);
   836   EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
   837   EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
   838   EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT),
   839                enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)
   840                            != 0));
   841   SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled);
   842 }
   844 static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent,
   845                                       DLGPROC dlgProc, LPARAM param)
   846 {
   847   INT_PTR rv = 0;
   848   if (gRTLlayout) {
   849     // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog
   850     // template.
   851     HRSRC hDialogRC = FindResource(nullptr, MAKEINTRESOURCE(idd),
   852                                    RT_DIALOG);
   853     HGLOBAL  hDlgTemplate = LoadResource(nullptr, hDialogRC);
   854     DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate);
   855     unsigned long sizeDlg = SizeofResource(nullptr, hDialogRC);
   856     HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg);
   857      DLGTEMPLATEEX* pMyDlgTemplate =
   858       (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate);
   859     memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg);
   861     pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL;
   863     rv = DialogBoxIndirectParam(nullptr, (LPCDLGTEMPLATE)pMyDlgTemplate,
   864                                 hwndParent, dlgProc, param);
   865     GlobalUnlock(hMyDlgTemplate);
   866     GlobalFree(hMyDlgTemplate);
   867   }
   868   else {
   869     rv = DialogBoxParam(nullptr, MAKEINTRESOURCE(idd), hwndParent,
   870                         dlgProc, param);
   871   }
   873   return rv;
   874 }
   877 static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
   878                                              WPARAM wParam, LPARAM lParam)
   879 {
   880   static int sHeight = 0;
   882   bool success;
   883   bool enabled;
   885   switch (message) {
   886   case WM_INITDIALOG: {
   887     GetThemeSizes(hwndDlg);
   888     RECT r;
   889     GetClientRect(hwndDlg, &r);
   890     sHeight = r.bottom - r.top;
   892     SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
   893     HICON hIcon = LoadIcon(GetModuleHandle(nullptr),
   894                            MAKEINTRESOURCE(IDI_MAINICON));
   895     SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
   896     SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
   898     // resize the "View Report" button based on the string length
   899     RECT rect;
   900     HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON);
   901     GetRelativeRect(hwnd, hwndDlg, &rect);
   902     ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0);
   903     SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str());
   905     hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK);
   906     GetRelativeRect(hwnd, hwndDlg, &rect);
   907     long maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false,
   908                                 gCheckboxPadding);
   909     SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
   910                    Str(ST_CHECKSUBMIT).c_str());
   912     if (!CheckBoolKey(gCrashReporterKey.c_str(),
   913                       SUBMIT_REPORT_VALUE, &enabled))
   914       enabled = ShouldEnableSending();
   916     CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED
   917                                                            : BST_UNCHECKED);
   918     SubmitReportChecked(hwndDlg);
   920     HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT);
   921     WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment,
   922                                                    GWLP_WNDPROC,
   923                                                    (LONG_PTR)EditSubclassProc);
   925     // Subclass comment edit control to get placeholder text
   926     SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc);
   927     wstring commentGrayText = Str(ST_COMMENTGRAYTEXT);
   928     wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t));
   929     wcscpy(hMem, commentGrayText.c_str());
   930     SetProp(hwndComment, L"PROP_GRAYTEXT", hMem);
   932     hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK);
   933     GetRelativeRect(hwnd, hwndDlg, &rect);
   934     long diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false,
   935                              gCheckboxPadding);
   936     maxdiff = std::max(diff, maxdiff);
   937     SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str());
   939     // want this on by default
   940     if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) &&
   941         !enabled) {
   942       CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED);
   943     } else {
   944       CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED);
   945     }
   947     hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK);
   948     GetRelativeRect(hwnd, hwndDlg, &rect);
   949     diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false,
   950                          gCheckboxPadding);
   951     maxdiff = std::max(diff, maxdiff);
   952     SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str());
   954     if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) &&
   955         enabled) {
   956       CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
   957     } else {
   958       CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
   959     }
   961     wstring email;
   962     if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) {
   963       SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str());
   964     }
   966     // Subclass email edit control to get placeholder text
   967     HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
   968     OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail,
   969                                            GWLP_WNDPROC,
   970                                            (LONG_PTR)EditSubclassProc);
   971     SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc);
   972     wstring emailGrayText = Str(ST_EMAILGRAYTEXT);
   973     hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t));
   974     wcscpy(hMem, emailGrayText.c_str());
   975     SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem);
   977     SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str());
   979     RECT closeRect;
   980     HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON);
   981     GetRelativeRect(hwndClose, hwndDlg, &closeRect);
   983     RECT restartRect;
   984     HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON);
   985     GetRelativeRect(hwndRestart, hwndDlg, &restartRect);
   987     // set the close button text and shift the buttons around
   988     // since the size may need to change
   989     int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT),
   990                                  true, 0);
   991     restartRect.left -= sizeDiff;
   992     restartRect.right -= sizeDiff;
   993     SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str());
   995     if (gRestartArgs.size() > 0) {
   996       // Resize restart button to fit text
   997       ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0);
   998       SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str());
   999     } else {
  1000       // No restart arguments, so just hide the restart button
  1001       SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
  1003     // See if we need to widen the window
  1004     // Leave 6 pixels on either side + 6 pixels between the buttons
  1005     int neededSize = closeRect.right - closeRect.left +
  1006       restartRect.right - restartRect.left + 6 * 3;
  1007     GetClientRect(hwndDlg, &r);
  1008     // We may already have resized one of the checkboxes above
  1009     maxdiff = std::max(maxdiff, neededSize - (r.right - r.left));
  1011     if (maxdiff > 0) {
  1012       // widen window
  1013       GetWindowRect(hwndDlg, &r);
  1014       r.right += maxdiff;
  1015       MoveWindow(hwndDlg, r.left, r.top,
  1016                  r.right - r.left, r.bottom - r.top, TRUE);
  1017       // shift both buttons right
  1018       if (restartRect.left + maxdiff < 6)
  1019         maxdiff += 6;
  1020       closeRect.left += maxdiff;
  1021       closeRect.right += maxdiff;
  1022       restartRect.left += maxdiff;
  1023       restartRect.right += maxdiff;
  1024       MoveWindow(hwndClose, closeRect.left, closeRect.top,
  1025                  closeRect.right - closeRect.left,
  1026                  closeRect.bottom - closeRect.top,
  1027                  TRUE);
  1028       StretchControlsToFit(hwndDlg);
  1030     // need to move the restart button regardless
  1031     MoveWindow(hwndRestart, restartRect.left, restartRect.top,
  1032                restartRect.right - restartRect.left,
  1033                restartRect.bottom - restartRect.top,
  1034                TRUE);
  1036     // Resize the description text last, in case the window was resized
  1037     // before this.
  1038     SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
  1039                        EM_SETEVENTMASK, (WPARAM)nullptr,
  1040                        ENM_REQUESTRESIZE);
  1042     wstring description = Str(ST_CRASHREPORTERHEADER);
  1043     description += L"\n\n";
  1044     description += Str(ST_CRASHREPORTERDESCRIPTION);
  1045     SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str());
  1048     // Make the title bold.
  1049     CHARFORMAT fmt = { 0, };
  1050     fmt.cbSize = sizeof(fmt);
  1051     fmt.dwMask = CFM_BOLD;
  1052     fmt.dwEffects = CFE_BOLD;
  1053     SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL,
  1054                        0, Str(ST_CRASHREPORTERHEADER).length());
  1055     SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT,
  1056                        SCF_SELECTION, (LPARAM)&fmt);
  1057     SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0);
  1058     // Force redraw.
  1059     SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
  1060                        EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
  1061     // Force resize.
  1062     SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
  1063                        EM_REQUESTRESIZE, 0, 0);
  1065     // if no URL was given, hide the URL checkbox
  1066     if (gQueryParameters.find(L"URL") == gQueryParameters.end()) {
  1067       RECT urlCheckRect, emailCheckRect;
  1068       GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect);
  1069       GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect);
  1071       SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false);
  1073       gAttachedBottom.erase(IDC_VIEWREPORTBUTTON);
  1074       gAttachedBottom.erase(IDC_SUBMITREPORTCHECK);
  1075       gAttachedBottom.erase(IDC_COMMENTTEXT);
  1077       StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top);
  1079       gAttachedBottom.insert(IDC_VIEWREPORTBUTTON);
  1080       gAttachedBottom.insert(IDC_SUBMITREPORTCHECK);
  1081       gAttachedBottom.insert(IDC_COMMENTTEXT);
  1084     MaybeResizeProgressText(hwndDlg);
  1086     // Open the AVI resource for the throbber
  1087     Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER),
  1088                  MAKEINTRESOURCE(IDR_THROBBER));
  1090     UpdateURL(hwndDlg);
  1091     UpdateEmail(hwndDlg);
  1093     SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK));
  1094     return FALSE;
  1096   case WM_SIZE: {
  1097     ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
  1098     sHeight = HIWORD(lParam);
  1099     InvalidateRect(hwndDlg, nullptr, TRUE);
  1100     return FALSE;
  1102   case WM_NOTIFY: {
  1103     NMHDR* notification = reinterpret_cast<NMHDR*>(lParam);
  1104     if (notification->code == EN_REQUESTRESIZE) {
  1105       // Resizing the rich edit control to fit the description text.
  1106       REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam);
  1107       RECT newSize = reqresize->rc;
  1108       RECT oldSize;
  1109       GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize);
  1111       // resize the text box as requested
  1112       MoveWindow(notification->hwndFrom, newSize.left, newSize.top,
  1113                  newSize.right - newSize.left, newSize.bottom - newSize.top,
  1114                  TRUE);
  1116       // Resize the dialog to fit (the WM_SIZE handler will move the controls)
  1117       StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom);
  1119     return FALSE;
  1121   case WM_COMMAND: {
  1122     if (HIWORD(wParam) == BN_CLICKED) {
  1123       switch(LOWORD(wParam)) {
  1124       case IDC_VIEWREPORTBUTTON:
  1125         DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg,
  1126                        (DLGPROC)ViewReportDialogProc, 0);
  1127         break;
  1128       case IDC_SUBMITREPORTCHECK:
  1129         SubmitReportChecked(hwndDlg);
  1130         break;
  1131       case IDC_INCLUDEURLCHECK:
  1132         UpdateURL(hwndDlg);
  1133         break;
  1134       case IDC_EMAILMECHECK:
  1135         UpdateEmail(hwndDlg);
  1136         break;
  1137       case IDC_CLOSEBUTTON:
  1138         MaybeSendReport(hwndDlg);
  1139         break;
  1140       case IDC_RESTARTBUTTON:
  1141         RestartApplication();
  1142         MaybeSendReport(hwndDlg);
  1143         break;
  1145     } else if (HIWORD(wParam) == EN_CHANGE) {
  1146       switch(LOWORD(wParam)) {
  1147       case IDC_EMAILTEXT:
  1148         UpdateEmail(hwndDlg);
  1149         break;
  1150       case IDC_COMMENTTEXT:
  1151         UpdateComment(hwndDlg);
  1155     return FALSE;
  1157   case WM_UPLOADCOMPLETE: {
  1158     WaitForSingleObject(gThreadHandle, INFINITE);
  1159     success = (wParam == 1);
  1160     SendCompleted(success, WideToUTF8(gSendData.serverResponse));
  1161     // hide throbber
  1162     Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER));
  1163     SetDlgItemVisible(hwndDlg, IDC_THROBBER, false);
  1165     SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
  1166                    success ?
  1167                    Str(ST_REPORTSUBMITSUCCESS).c_str() :
  1168                    Str(ST_SUBMITFAILED).c_str());
  1169     MaybeResizeProgressText(hwndDlg);
  1170     // close dialog after 5 seconds
  1171     SetTimer(hwndDlg, 0, 5000, nullptr);
  1172     //
  1173     return TRUE;
  1176   case WM_LBUTTONDOWN: {
  1177     HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
  1178     POINT p = { LOWORD(lParam), HIWORD(lParam) };
  1179     // if the email edit control is clicked, enable it,
  1180     // check the email checkbox, and focus the email edit control
  1181     if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail &&
  1182         IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) &&
  1183         !IsWindowEnabled(hwndEmail) &&
  1184         IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) {
  1185       CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
  1186       UpdateEmail(hwndDlg);
  1187       SetFocus(hwndEmail);
  1189     break;
  1192   case WM_TIMER: {
  1193     // The "1" gets used down in UIShowCrashUI to indicate that we at least
  1194     // tried to send the report.
  1195     EndCrashReporterDialog(hwndDlg, 1);
  1196     return FALSE;
  1199   case WM_CLOSE: {
  1200     EndCrashReporterDialog(hwndDlg, 0);
  1201     return FALSE;
  1204   return FALSE;
  1207 static wstring UTF8ToWide(const string& utf8, bool *success)
  1209   wchar_t* buffer = nullptr;
  1210   int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
  1211                                         -1, nullptr, 0);
  1212   if(buffer_size == 0) {
  1213     if (success)
  1214       *success = false;
  1215     return L"";
  1218   buffer = new wchar_t[buffer_size];
  1219   if(buffer == nullptr) {
  1220     if (success)
  1221       *success = false;
  1222     return L"";
  1225   MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
  1226                       -1, buffer, buffer_size);
  1227   wstring str = buffer;
  1228   delete [] buffer;
  1230   if (success)
  1231     *success = true;
  1233   return str;
  1236 static string WideToMBCP(const wstring& wide,
  1237                          unsigned int cp,
  1238                          bool* success = nullptr)
  1240   char* buffer = nullptr;
  1241   int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(),
  1242                                         -1, nullptr, 0, nullptr, nullptr);
  1243   if(buffer_size == 0) {
  1244     if (success)
  1245       *success = false;
  1246     return "";
  1249   buffer = new char[buffer_size];
  1250   if(buffer == nullptr) {
  1251     if (success)
  1252       *success = false;
  1253     return "";
  1256   WideCharToMultiByte(cp, 0, wide.c_str(),
  1257                       -1, buffer, buffer_size, nullptr, nullptr);
  1258   string mb = buffer;
  1259   delete [] buffer;
  1261   if (success)
  1262     *success = true;
  1264   return mb;
  1267 string WideToUTF8(const wstring& wide, bool* success)
  1269   return WideToMBCP(wide, CP_UTF8, success);
  1272 /* === Crashreporter UI Functions === */
  1274 bool UIInit()
  1276   for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
  1277     gAttachedBottom.insert(kDefaultAttachedBottom[i]);
  1280   DoInitCommonControls();
  1282   return true;
  1285 void UIShutdown()
  1289 void UIShowDefaultUI()
  1291   MessageBox(nullptr, Str(ST_CRASHREPORTERDEFAULT).c_str(),
  1292              L"Crash Reporter",
  1293              MB_OK | MB_ICONSTOP);
  1296 bool UIShowCrashUI(const string& dumpFile,
  1297                    const StringTable& queryParameters,
  1298                    const string& sendURL,
  1299                    const vector<string>& restartArgs)
  1301   gSendData.hDlg = nullptr;
  1302   gSendData.dumpFile = UTF8ToWide(dumpFile);
  1303   gSendData.sendURL = UTF8ToWide(sendURL);
  1305   for (StringTable::const_iterator i = queryParameters.begin();
  1306        i != queryParameters.end();
  1307        i++) {
  1308     gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
  1311   if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
  1312     gCrashReporterKey = L"Software\\";
  1313     if (!gQueryParameters[L"Vendor"].empty()) {
  1314       gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
  1316     gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter";
  1319   if (gQueryParameters.find(L"URL") != gQueryParameters.end())
  1320     gURLParameter = gQueryParameters[L"URL"];
  1322   gRestartArgs = restartArgs;
  1324   if (gStrings.find("isRTL") != gStrings.end() &&
  1325       gStrings["isRTL"] == "yes")
  1326     gRTLlayout = true;
  1328   return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, nullptr,
  1329                                      (DLGPROC)CrashReporterDialogProc, 0);
  1332 void UIError_impl(const string& message)
  1334   wstring title = Str(ST_CRASHREPORTERTITLE);
  1335   if (title.empty())
  1336     title = L"Crash Reporter Error";
  1338   MessageBox(nullptr, UTF8ToWide(message).c_str(), title.c_str(),
  1339              MB_OK | MB_ICONSTOP);
  1342 bool UIGetIniPath(string& path)
  1344   wchar_t fileName[MAX_PATH];
  1345   if (GetModuleFileName(nullptr, fileName, MAX_PATH)) {
  1346     // get crashreporter ini
  1347     wchar_t* s = wcsrchr(fileName, '.');
  1348     if (s) {
  1349       wcscpy(s, L".ini");
  1350       path = WideToUTF8(fileName);
  1351       return true;
  1355   return false;
  1358 bool UIGetSettingsPath(const string& vendor,
  1359                        const string& product,
  1360                        string& settings_path)
  1362   wchar_t path[MAX_PATH];
  1363   HRESULT hRes = SHGetFolderPath(nullptr,
  1364                                  CSIDL_APPDATA,
  1365                                  nullptr,
  1366                                  0,
  1367                                  path);
  1368   if (FAILED(hRes)) {
  1369     // This provides a fallback for getting the path to APPDATA by querying the
  1370     // registry when the call to SHGetFolderPath is unable to provide this path
  1371     // (Bug 513958).
  1372     HKEY key;
  1373     DWORD type, size, dwRes;
  1374     dwRes = ::RegOpenKeyExW(HKEY_CURRENT_USER,
  1375                             L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
  1376                             0,
  1377                             KEY_READ,
  1378                             &key);
  1379     if (dwRes != ERROR_SUCCESS)
  1380       return false;
  1382     dwRes = RegQueryValueExW(key,
  1383                              L"AppData",
  1384                              nullptr,
  1385                              &type,
  1386                              (LPBYTE)&path,
  1387                              &size);
  1388     ::RegCloseKey(key);
  1389     // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
  1390     // buffer size must not equal 0, and the buffer size be a multiple of 2.
  1391     if (dwRes != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0)
  1392         return false;
  1395   if (!vendor.empty()) {
  1396     PathAppend(path, UTF8ToWide(vendor).c_str());
  1398   PathAppend(path, UTF8ToWide(product).c_str());
  1399   PathAppend(path, L"Crash Reports");
  1400   settings_path = WideToUTF8(path);
  1401   return true;
  1404 bool UIEnsurePathExists(const string& path)
  1406   if (CreateDirectory(UTF8ToWide(path).c_str(), nullptr) == 0) {
  1407     if (GetLastError() != ERROR_ALREADY_EXISTS)
  1408       return false;
  1411   return true;
  1414 bool UIFileExists(const string& path)
  1416   DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str());
  1417   return (attrs != INVALID_FILE_ATTRIBUTES);
  1420 bool UIMoveFile(const string& oldfile, const string& newfile)
  1422   if (oldfile == newfile)
  1423     return true;
  1425   return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
  1426     == TRUE;
  1429 bool UIDeleteFile(const string& oldfile)
  1431   return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE;
  1434 ifstream* UIOpenRead(const string& filename)
  1436   // adapted from breakpad's src/common/windows/http_upload.cc
  1438   // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
  1439   // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
  1440   // later, _wfopen has been deprecated in favor of _wfopen_s, which does
  1441   // not exist in earlier versions, so let the ifstream open the file itself.
  1442 #if _MSC_VER >= 1400  // MSVC 2005/8
  1443   ifstream* file = new ifstream();
  1444   file->open(UTF8ToWide(filename).c_str(), ios::in);
  1445 #elif defined(_MSC_VER)
  1446   ifstream* file = new ifstream(_wfopen(UTF8ToWide(filename).c_str(), L"r"));
  1447 #else   // GCC
  1448   ifstream* file = new ifstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
  1449                                 ios::in);
  1450 #endif  // _MSC_VER >= 1400
  1452   return file;
  1455 ofstream* UIOpenWrite(const string& filename, bool append) // append=false
  1457   // adapted from breakpad's src/common/windows/http_upload.cc
  1459   // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
  1460   // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
  1461   // later, _wfopen has been deprecated in favor of _wfopen_s, which does
  1462   // not exist in earlier versions, so let the ifstream open the file itself.
  1463 #if _MSC_VER >= 1400  // MSVC 2005/8
  1464   ofstream* file = new ofstream();
  1465   file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app
  1466                                                   : ios::out);
  1467 #elif defined(_MSC_VER)
  1468   ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(),
  1469                                         append ? L"a" : L"w"));
  1470 #else   // GCC
  1471   ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
  1472                                 append ? ios::out | ios::app : ios::out);
  1473 #endif  // _MSC_VER >= 1400
  1475   return file;
  1478 struct FileData
  1480   FILETIME timestamp;
  1481   wstring path;
  1482 };
  1484 static bool CompareFDTime(const FileData& fd1, const FileData& fd2)
  1486   return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0;
  1489 void UIPruneSavedDumps(const std::string& directory)
  1491   wstring wdirectory = UTF8ToWide(directory);
  1493   WIN32_FIND_DATA fdata;
  1494   wstring findpath = wdirectory + L"\\*.dmp";
  1495   HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata);
  1496   if (dirlist == INVALID_HANDLE_VALUE)
  1497     return;
  1499   vector<FileData> dumpfiles;
  1501   for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) {
  1502     FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName};
  1503     dumpfiles.push_back(fd);
  1506   sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime);
  1508   while (dumpfiles.size() > kSaveCount) {
  1509     // get the path of the oldest file
  1510     wstring path = (--dumpfiles.end())->path;
  1511     DeleteFile(path.c_str());
  1513     // s/.dmp/.extra/
  1514     path.replace(path.size() - 4, 4, L".extra");
  1515     DeleteFile(path.c_str());
  1517     dumpfiles.pop_back();

mercurial