toolkit/crashreporter/client/crashreporter_win.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/crashreporter/client/crashreporter_win.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1519 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifdef WIN32_LEAN_AND_MEAN
    1.10 +#undef WIN32_LEAN_AND_MEAN
    1.11 +#endif
    1.12 +
    1.13 +#define NOMINMAX
    1.14 +
    1.15 +#include "crashreporter.h"
    1.16 +
    1.17 +#include <windows.h>
    1.18 +#include <commctrl.h>
    1.19 +#include <richedit.h>
    1.20 +#include <shellapi.h>
    1.21 +#include <shlobj.h>
    1.22 +#include <shlwapi.h>
    1.23 +#include <math.h>
    1.24 +#include <set>
    1.25 +#include <algorithm>
    1.26 +#include "resource.h"
    1.27 +#include "client/windows/sender/crash_report_sender.h"
    1.28 +#include "common/windows/string_utils-inl.h"
    1.29 +#include "mozilla/NullPtr.h"
    1.30 +
    1.31 +#define CRASH_REPORTER_VALUE L"Enabled"
    1.32 +#define SUBMIT_REPORT_VALUE  L"SubmitCrashReport"
    1.33 +#define SUBMIT_REPORT_OLD    L"SubmitReport"
    1.34 +#define INCLUDE_URL_VALUE    L"IncludeURL"
    1.35 +#define EMAIL_ME_VALUE       L"EmailMe"
    1.36 +#define EMAIL_VALUE          L"Email"
    1.37 +#define MAX_EMAIL_LENGTH     1024
    1.38 +
    1.39 +#define WM_UPLOADCOMPLETE WM_APP
    1.40 +
    1.41 +// Thanks, Windows.h :(
    1.42 +#undef min
    1.43 +#undef max
    1.44 +
    1.45 +using std::string;
    1.46 +using std::wstring;
    1.47 +using std::map;
    1.48 +using std::vector;
    1.49 +using std::set;
    1.50 +using std::ios;
    1.51 +using std::ifstream;
    1.52 +using std::ofstream;
    1.53 +
    1.54 +using namespace CrashReporter;
    1.55 +
    1.56 +typedef struct {
    1.57 +  HWND hDlg;
    1.58 +  wstring dumpFile;
    1.59 +  map<wstring,wstring> queryParameters;
    1.60 +  wstring sendURL;
    1.61 +
    1.62 +  wstring serverResponse;
    1.63 +} SendThreadData;
    1.64 +
    1.65 +/*
    1.66 + * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx
    1.67 + * "The DLGTEMPLATEEX structure is not defined in any standard header file.
    1.68 + * The structure definition is provided here to explain the format of an
    1.69 + * extended template for a dialog box.
    1.70 +*/
    1.71 +typedef struct {
    1.72 +    WORD dlgVer;
    1.73 +    WORD signature;
    1.74 +    DWORD helpID;
    1.75 +    DWORD exStyle;
    1.76 +  // There's more to this struct, but it has weird variable-length
    1.77 +  // members, and I only actually need to touch exStyle on an existing
    1.78 +  // instance, so I've omitted the rest.
    1.79 +} DLGTEMPLATEEX;
    1.80 +
    1.81 +static HANDLE               gThreadHandle;
    1.82 +static SendThreadData       gSendData = { 0, };
    1.83 +static vector<string>       gRestartArgs;
    1.84 +static map<wstring,wstring> gQueryParameters;
    1.85 +static wstring              gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
    1.86 +static wstring              gURLParameter;
    1.87 +static int                  gCheckboxPadding = 6;
    1.88 +static bool                 gRTLlayout = false;
    1.89 +
    1.90 +// When vertically resizing the dialog, these items should move down
    1.91 +static set<UINT> gAttachedBottom;
    1.92 +
    1.93 +// Default set of items for gAttachedBottom
    1.94 +static const UINT kDefaultAttachedBottom[] = {
    1.95 +  IDC_SUBMITREPORTCHECK,
    1.96 +  IDC_VIEWREPORTBUTTON,
    1.97 +  IDC_COMMENTTEXT,
    1.98 +  IDC_INCLUDEURLCHECK,
    1.99 +  IDC_EMAILMECHECK,
   1.100 +  IDC_EMAILTEXT,
   1.101 +  IDC_PROGRESSTEXT,
   1.102 +  IDC_THROBBER,
   1.103 +  IDC_CLOSEBUTTON,
   1.104 +  IDC_RESTARTBUTTON,
   1.105 +};
   1.106 +
   1.107 +static wstring UTF8ToWide(const string& utf8, bool *success = 0);
   1.108 +static DWORD WINAPI SendThreadProc(LPVOID param);
   1.109 +
   1.110 +static wstring Str(const char* key)
   1.111 +{
   1.112 +  return UTF8ToWide(gStrings[key]);
   1.113 +}
   1.114 +
   1.115 +/* === win32 helper functions === */
   1.116 +
   1.117 +static void DoInitCommonControls()
   1.118 +{
   1.119 +  INITCOMMONCONTROLSEX ic;
   1.120 +  ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
   1.121 +  ic.dwICC = ICC_PROGRESS_CLASS;
   1.122 +  InitCommonControlsEx(&ic);
   1.123 +  // also get the rich edit control
   1.124 +  LoadLibrary(L"Msftedit.dll");
   1.125 +}
   1.126 +
   1.127 +static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
   1.128 +{
   1.129 +  DWORD type, dataSize;
   1.130 +  dataSize = sizeof(DWORD);
   1.131 +  if (RegQueryValueEx(hRegKey, valueName, nullptr,
   1.132 +                      &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS &&
   1.133 +      type == REG_DWORD)
   1.134 +    return true;
   1.135 +
   1.136 +  return false;
   1.137 +}
   1.138 +
   1.139 +// Removes a value from HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER, if it exists.
   1.140 +static void RemoveUnusedValues(const wchar_t* key, LPCTSTR valueName)
   1.141 +{
   1.142 +  HKEY hRegKey;
   1.143 +
   1.144 +  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_SET_VALUE, &hRegKey) 
   1.145 +      == ERROR_SUCCESS) {
   1.146 +    RegDeleteValue(hRegKey, valueName);
   1.147 +    RegCloseKey(hRegKey);
   1.148 +  }
   1.149 +
   1.150 +  if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_SET_VALUE, &hRegKey) 
   1.151 +      == ERROR_SUCCESS) {
   1.152 +    RegDeleteValue(hRegKey, valueName);
   1.153 +    RegCloseKey(hRegKey);
   1.154 +  }
   1.155 +}
   1.156 +
   1.157 +static bool CheckBoolKey(const wchar_t* key,
   1.158 +                         const wchar_t* valueName,
   1.159 +                         bool* enabled)
   1.160 +{
   1.161 +  /*
   1.162 +   * NOTE! This code needs to stay in sync with the preference checking
   1.163 +   *       code in in nsExceptionHandler.cpp.
   1.164 +   */
   1.165 +  *enabled = false;
   1.166 +  bool found = false;
   1.167 +  HKEY hRegKey;
   1.168 +  DWORD val;
   1.169 +  // see if our reg key is set globally
   1.170 +  if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
   1.171 +    if (GetBoolValue(hRegKey, valueName, &val)) {
   1.172 +      *enabled = (val == 1);
   1.173 +      found = true;
   1.174 +    }
   1.175 +    RegCloseKey(hRegKey);
   1.176 +  } else {
   1.177 +    // look for it in user settings
   1.178 +    if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   1.179 +      if (GetBoolValue(hRegKey, valueName, &val)) {
   1.180 +        *enabled = (val == 1);
   1.181 +        found = true;
   1.182 +      }
   1.183 +      RegCloseKey(hRegKey);
   1.184 +    }
   1.185 +  }
   1.186 +
   1.187 +  return found;
   1.188 +}
   1.189 +
   1.190 +static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
   1.191 +{
   1.192 +  /*
   1.193 +   * NOTE! This code needs to stay in sync with the preference setting
   1.194 +   *       code in in nsExceptionHandler.cpp.
   1.195 +   */
   1.196 +  HKEY hRegKey;
   1.197 +
   1.198 +  // remove the old value from the registry if it exists
   1.199 +  RemoveUnusedValues(key, SUBMIT_REPORT_OLD);
   1.200 +
   1.201 +  if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   1.202 +    DWORD data = (enabled ? 1 : 0);
   1.203 +    RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
   1.204 +    RegCloseKey(hRegKey);
   1.205 +  }
   1.206 +}
   1.207 +
   1.208 +static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value)
   1.209 +{
   1.210 +  DWORD type, dataSize;
   1.211 +  wchar_t buf[2048];
   1.212 +  dataSize = sizeof(buf);
   1.213 +  if (RegQueryValueEx(hRegKey, valueName, nullptr,
   1.214 +                     &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS &&
   1.215 +      type == REG_SZ) {
   1.216 +    value = buf;
   1.217 +    return true;
   1.218 +  }
   1.219 +
   1.220 +  return false;
   1.221 +}
   1.222 +
   1.223 +static bool GetStringKey(const wchar_t* key,
   1.224 +                         const wchar_t* valueName,
   1.225 +                         wstring& value)
   1.226 +{
   1.227 +  value = L"";
   1.228 +  bool found = false;
   1.229 +  HKEY hRegKey;
   1.230 +  // see if our reg key is set globally
   1.231 +  if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
   1.232 +    if (GetStringValue(hRegKey, valueName, value)) {
   1.233 +      found = true;
   1.234 +    }
   1.235 +    RegCloseKey(hRegKey);
   1.236 +  } else {
   1.237 +    // look for it in user settings
   1.238 +    if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   1.239 +      if (GetStringValue(hRegKey, valueName, value)) {
   1.240 +        found = true;
   1.241 +      }
   1.242 +      RegCloseKey(hRegKey);
   1.243 +    }
   1.244 +  }
   1.245 +
   1.246 +  return found;
   1.247 +}
   1.248 +
   1.249 +static void SetStringKey(const wchar_t* key,
   1.250 +                         const wchar_t* valueName,
   1.251 +                         const wstring& value)
   1.252 +{
   1.253 +  HKEY hRegKey;
   1.254 +  if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
   1.255 +    RegSetValueEx(hRegKey, valueName, 0, REG_SZ,
   1.256 +                  (LPBYTE)value.c_str(),
   1.257 +                  (value.length() + 1) * sizeof(wchar_t));
   1.258 +    RegCloseKey(hRegKey);
   1.259 +  }
   1.260 +}
   1.261 +
   1.262 +static string FormatLastError()
   1.263 +{
   1.264 +  DWORD err = GetLastError();
   1.265 +  LPWSTR s;
   1.266 +  string message = "Crash report submission failed: ";
   1.267 +  // odds are it's a WinInet error
   1.268 +  HANDLE hInetModule = GetModuleHandle(L"WinInet.dll");
   1.269 +  if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
   1.270 +                   FORMAT_MESSAGE_FROM_SYSTEM |
   1.271 +                   FORMAT_MESSAGE_FROM_HMODULE,
   1.272 +                   hInetModule,
   1.273 +                   err,
   1.274 +                   0,
   1.275 +                   (LPWSTR)&s,
   1.276 +                   0,
   1.277 +                   nullptr) != 0) {
   1.278 +    message += WideToUTF8(s, nullptr);
   1.279 +    LocalFree(s);
   1.280 +    // strip off any trailing newlines
   1.281 +    string::size_type n = message.find_last_not_of("\r\n");
   1.282 +    if (n < message.size() - 1) {
   1.283 +      message.erase(n+1);
   1.284 +    }
   1.285 +  }
   1.286 +  else {
   1.287 +    char buf[64];
   1.288 +    sprintf(buf, "Unknown error, error code: 0x%08x", err);
   1.289 +    message += buf;
   1.290 +  }
   1.291 +  return message;
   1.292 +}
   1.293 +
   1.294 +#define TS_DRAW 2
   1.295 +#define BP_CHECKBOX  3
   1.296 +
   1.297 +typedef  HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
   1.298 +typedef  HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
   1.299 +typedef  HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
   1.300 +                                              int iStateId, RECT* prc, int ts,
   1.301 +                                              SIZE* psz);
   1.302 +typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
   1.303 +                                                int iStateId, const RECT* pRect,
   1.304 +                                                RECT* pContentRect);
   1.305 +
   1.306 +
   1.307 +static void GetThemeSizes(HWND hwnd)
   1.308 +{
   1.309 +  HMODULE themeDLL = LoadLibrary(L"uxtheme.dll");
   1.310 +
   1.311 +  if (!themeDLL)
   1.312 +    return;
   1.313 +
   1.314 +  OpenThemeDataPtr openTheme = 
   1.315 +    (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData");
   1.316 +  CloseThemeDataPtr closeTheme =
   1.317 +    (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData");
   1.318 +  GetThemePartSizePtr getThemePartSize = 
   1.319 +    (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize");
   1.320 +
   1.321 +  if (!openTheme || !closeTheme || !getThemePartSize) {
   1.322 +    FreeLibrary(themeDLL);
   1.323 +    return;
   1.324 +  }
   1.325 +
   1.326 +  HANDLE buttonTheme = openTheme(hwnd, L"Button");
   1.327 +  if (!buttonTheme) {
   1.328 +    FreeLibrary(themeDLL);
   1.329 +    return;
   1.330 +  }
   1.331 +  HDC hdc = GetDC(hwnd);
   1.332 +  SIZE s;
   1.333 +  getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, nullptr, TS_DRAW, &s);
   1.334 +  gCheckboxPadding = s.cx;
   1.335 +  closeTheme(buttonTheme);
   1.336 +  FreeLibrary(themeDLL);
   1.337 +}
   1.338 +
   1.339 +// Gets the position of a window relative to another window's client area
   1.340 +static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r)
   1.341 +{
   1.342 +  GetWindowRect(hwnd, r);
   1.343 +  MapWindowPoints(nullptr, hwndParent, (POINT*)r, 2);
   1.344 +}
   1.345 +
   1.346 +static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
   1.347 +{
   1.348 +  HWND hwnd = GetDlgItem(hwndDlg, item);
   1.349 +
   1.350 +  ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE);
   1.351 +}
   1.352 +
   1.353 +static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled)
   1.354 +{
   1.355 +  HWND hwnd = GetDlgItem(hwndDlg, item);
   1.356 +  LONG style = GetWindowLong(hwnd, GWL_STYLE);
   1.357 +  if (!disabled)
   1.358 +    style |= WS_DISABLED;
   1.359 +  else
   1.360 +    style &= ~WS_DISABLED;
   1.361 +
   1.362 +  SetWindowLong(hwnd, GWL_STYLE, style);
   1.363 +}
   1.364 +
   1.365 +/* === Crash Reporting Dialog === */
   1.366 +
   1.367 +static void StretchDialog(HWND hwndDlg, int ydiff)
   1.368 +{
   1.369 +  RECT r;
   1.370 +  GetWindowRect(hwndDlg, &r);
   1.371 +  r.bottom += ydiff;
   1.372 +  MoveWindow(hwndDlg, r.left, r.top,
   1.373 +             r.right - r.left, r.bottom - r.top, TRUE);
   1.374 +}
   1.375 +
   1.376 +static void ReflowDialog(HWND hwndDlg, int ydiff)
   1.377 +{
   1.378 +  // Move items attached to the bottom down/up by as much as
   1.379 +  // the window resize
   1.380 +  for (set<UINT>::const_iterator item = gAttachedBottom.begin();
   1.381 +       item != gAttachedBottom.end();
   1.382 +       item++) {
   1.383 +    RECT r;
   1.384 +    HWND hwnd = GetDlgItem(hwndDlg, *item);
   1.385 +    GetRelativeRect(hwnd, hwndDlg, &r);
   1.386 +    r.top += ydiff;
   1.387 +    r.bottom += ydiff;
   1.388 +    MoveWindow(hwnd, r.left, r.top,
   1.389 +               r.right - r.left, r.bottom - r.top, TRUE);
   1.390 +  }
   1.391 +}
   1.392 +
   1.393 +static DWORD WINAPI SendThreadProc(LPVOID param)
   1.394 +{
   1.395 +  bool finishedOk;
   1.396 +  SendThreadData* td = (SendThreadData*)param;
   1.397 +
   1.398 +  if (td->sendURL.empty()) {
   1.399 +    finishedOk = false;
   1.400 +    LogMessage("No server URL, not sending report");
   1.401 +  } else {
   1.402 +    google_breakpad::CrashReportSender sender(L"");
   1.403 +    finishedOk = (sender.SendCrashReport(td->sendURL,
   1.404 +                                         td->queryParameters,
   1.405 +                                         td->dumpFile,
   1.406 +                                         &td->serverResponse)
   1.407 +                  == google_breakpad::RESULT_SUCCEEDED);
   1.408 +    if (finishedOk) {
   1.409 +      LogMessage("Crash report submitted successfully");
   1.410 +    }
   1.411 +    else {
   1.412 +      // get an error string and print it to the log
   1.413 +      //XXX: would be nice to get the HTTP status code here, filed:
   1.414 +      // http://code.google.com/p/google-breakpad/issues/detail?id=220
   1.415 +      LogMessage(FormatLastError());
   1.416 +    }
   1.417 +  }
   1.418 +
   1.419 +  PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
   1.420 +
   1.421 +  return 0;
   1.422 +}
   1.423 +
   1.424 +static void EndCrashReporterDialog(HWND hwndDlg, int code)
   1.425 +{
   1.426 +  // Save the current values to the registry
   1.427 +  wchar_t email[MAX_EMAIL_LENGTH];
   1.428 +  GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
   1.429 +                  sizeof(email) / sizeof(email[0]));
   1.430 +  SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email);
   1.431 +
   1.432 +  SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE,
   1.433 +             IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0);
   1.434 +  SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE,
   1.435 +             IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0);
   1.436 +  SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE,
   1.437 +             IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
   1.438 +
   1.439 +  EndDialog(hwndDlg, code);
   1.440 +}
   1.441 +
   1.442 +static void MaybeResizeProgressText(HWND hwndDlg)
   1.443 +{
   1.444 +  HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT);
   1.445 +  HDC hdc = GetDC(hwndProgress);
   1.446 +  HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0);
   1.447 +  if (hfont)
   1.448 +    SelectObject(hdc, hfont);
   1.449 +  SIZE size;
   1.450 +  RECT rect;
   1.451 +  GetRelativeRect(hwndProgress, hwndDlg, &rect);
   1.452 +
   1.453 +  wchar_t text[1024];
   1.454 +  GetWindowText(hwndProgress, text, 1024);
   1.455 +
   1.456 +  if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size))
   1.457 +    return;
   1.458 +
   1.459 +  if (size.cx < (rect.right - rect.left))
   1.460 +    return;
   1.461 +
   1.462 +  // Figure out how much we need to resize things vertically
   1.463 +  // This is sort of a fudge, but it should be good enough.
   1.464 +  int wantedHeight = size.cy *
   1.465 +    (int)ceil((float)size.cx / (float)(rect.right - rect.left));
   1.466 +  int diff = wantedHeight - (rect.bottom - rect.top);
   1.467 +  if (diff <= 0)
   1.468 +    return;
   1.469 +
   1.470 +  MoveWindow(hwndProgress, rect.left, rect.top,
   1.471 +             rect.right - rect.left,
   1.472 +             wantedHeight,
   1.473 +             TRUE);
   1.474 +
   1.475 +  gAttachedBottom.clear();
   1.476 +  gAttachedBottom.insert(IDC_CLOSEBUTTON);
   1.477 +  gAttachedBottom.insert(IDC_RESTARTBUTTON);
   1.478 +
   1.479 +  StretchDialog(hwndDlg, diff);
   1.480 +
   1.481 +  for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
   1.482 +    gAttachedBottom.insert(kDefaultAttachedBottom[i]);
   1.483 +  }
   1.484 +}
   1.485 +
   1.486 +static void MaybeSendReport(HWND hwndDlg)
   1.487 +{
   1.488 +  if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
   1.489 +    EndCrashReporterDialog(hwndDlg, 0);
   1.490 +    return;
   1.491 +  }
   1.492 +
   1.493 +  // disable all the form controls
   1.494 +  EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false);
   1.495 +  EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false);
   1.496 +  EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false);
   1.497 +  EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false);
   1.498 +  EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false);
   1.499 +  EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
   1.500 +  EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false);
   1.501 +  EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false);
   1.502 +
   1.503 +  SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str());
   1.504 +  MaybeResizeProgressText(hwndDlg);
   1.505 +  // start throbber
   1.506 +  // play entire AVI, and loop
   1.507 +  Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1);
   1.508 +  SetDlgItemVisible(hwndDlg, IDC_THROBBER, true);
   1.509 +  gThreadHandle = nullptr;
   1.510 +  gSendData.hDlg = hwndDlg;
   1.511 +  gSendData.queryParameters = gQueryParameters;
   1.512 +
   1.513 +  gThreadHandle = CreateThread(nullptr, 0, SendThreadProc, &gSendData, 0,
   1.514 +                               nullptr);
   1.515 +}
   1.516 +
   1.517 +static void RestartApplication()
   1.518 +{
   1.519 +  wstring cmdLine;
   1.520 +
   1.521 +  for (unsigned int i = 0; i < gRestartArgs.size(); i++) {
   1.522 +    cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" ";
   1.523 +  }
   1.524 +
   1.525 +  STARTUPINFO si;
   1.526 +  PROCESS_INFORMATION pi;
   1.527 +
   1.528 +  ZeroMemory(&si, sizeof(si));
   1.529 +  si.cb = sizeof(si);
   1.530 +  si.dwFlags = STARTF_USESHOWWINDOW;
   1.531 +  si.wShowWindow = SW_SHOWNORMAL;
   1.532 +  ZeroMemory(&pi, sizeof(pi));
   1.533 +
   1.534 +  if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
   1.535 +                    0, nullptr, nullptr, &si, &pi)) {
   1.536 +    CloseHandle(pi.hProcess);
   1.537 +    CloseHandle(pi.hThread);
   1.538 +  }
   1.539 +}
   1.540 +
   1.541 +static void ShowReportInfo(HWND hwndDlg)
   1.542 +{
   1.543 +  wstring description;
   1.544 +
   1.545 +  for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin();
   1.546 +       i != gQueryParameters.end();
   1.547 +       i++) {
   1.548 +    description += i->first;
   1.549 +    description += L": ";
   1.550 +    description += i->second;
   1.551 +    description += L"\n";
   1.552 +  }
   1.553 +
   1.554 +  description += L"\n";
   1.555 +  description += Str(ST_EXTRAREPORTINFO);
   1.556 +
   1.557 +  SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str());
   1.558 +}
   1.559 +
   1.560 +static void UpdateURL(HWND hwndDlg)
   1.561 +{
   1.562 +  if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) {
   1.563 +    gQueryParameters[L"URL"] = gURLParameter;
   1.564 +  } else {
   1.565 +    gQueryParameters.erase(L"URL");
   1.566 +  }
   1.567 +}
   1.568 +
   1.569 +static void UpdateEmail(HWND hwndDlg)
   1.570 +{
   1.571 +  if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) {
   1.572 +    wchar_t email[MAX_EMAIL_LENGTH];
   1.573 +    GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
   1.574 +                    sizeof(email) / sizeof(email[0]));
   1.575 +    gQueryParameters[L"Email"] = email;
   1.576 +    if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK))
   1.577 +      EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true);
   1.578 +  } else {
   1.579 +    gQueryParameters.erase(L"Email");
   1.580 +    EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
   1.581 +  }
   1.582 +}
   1.583 +
   1.584 +static void UpdateComment(HWND hwndDlg)
   1.585 +{
   1.586 +  wchar_t comment[MAX_COMMENT_LENGTH + 1];
   1.587 +  GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment,
   1.588 +                  sizeof(comment) / sizeof(comment[0]));
   1.589 +  if (wcslen(comment) > 0)
   1.590 +    gQueryParameters[L"Comments"] = comment;
   1.591 +  else
   1.592 +    gQueryParameters.erase(L"Comments");
   1.593 +}
   1.594 +
   1.595 +/*
   1.596 + * Dialog procedure for the "view report" dialog.
   1.597 + */
   1.598 +static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message,
   1.599 +                                          WPARAM wParam, LPARAM lParam)
   1.600 +{
   1.601 +  switch (message) {
   1.602 +  case WM_INITDIALOG: {
   1.603 +    SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str());    
   1.604 +    SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
   1.605 +    SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
   1.606 +                       EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
   1.607 +    ShowReportInfo(hwndDlg);
   1.608 +    SetFocus(GetDlgItem(hwndDlg, IDOK));
   1.609 +    return FALSE;
   1.610 +  }
   1.611 +
   1.612 +  case WM_COMMAND: {
   1.613 +    if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
   1.614 +      EndDialog(hwndDlg, 0);
   1.615 +    return FALSE;
   1.616 +  }
   1.617 +  }
   1.618 +  return FALSE;
   1.619 +}
   1.620 +
   1.621 +// Return the number of bytes this string will take encoded
   1.622 +// in UTF-8
   1.623 +static inline int BytesInUTF8(wchar_t* str)
   1.624 +{
   1.625 +  // Just count size of buffer for UTF-8, minus one
   1.626 +  // (we don't need to count the null terminator)
   1.627 +  return WideCharToMultiByte(CP_UTF8, 0, str, -1,
   1.628 +                             nullptr, 0, nullptr, nullptr) - 1;
   1.629 +}
   1.630 +
   1.631 +// Calculate the length of the text in this edit control (in bytes,
   1.632 +// in the UTF-8 encoding) after replacing the current selection
   1.633 +// with |insert|.
   1.634 +static int NewTextLength(HWND hwndEdit, wchar_t* insert)
   1.635 +{
   1.636 +  wchar_t current[MAX_COMMENT_LENGTH + 1];
   1.637 +
   1.638 +  GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1);
   1.639 +  DWORD selStart, selEnd;
   1.640 +  SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
   1.641 +
   1.642 +  int selectionLength = 0;
   1.643 +  if (selEnd - selStart > 0) {
   1.644 +    wchar_t selection[MAX_COMMENT_LENGTH + 1];
   1.645 +    google_breakpad::WindowsStringUtils::safe_wcsncpy(selection,
   1.646 +                                                      MAX_COMMENT_LENGTH + 1,
   1.647 +                                                      current + selStart,
   1.648 +                                                      selEnd - selStart);
   1.649 +    selection[selEnd - selStart] = '\0';
   1.650 +    selectionLength = BytesInUTF8(selection);
   1.651 +  }
   1.652 +
   1.653 +  // current string length + replacement text length
   1.654 +  // - replaced selection length
   1.655 +  return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength;
   1.656 +}
   1.657 +
   1.658 +// Window procedure for subclassing edit controls
   1.659 +static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
   1.660 +                                         LPARAM lParam)
   1.661 +{
   1.662 +  static WNDPROC super = nullptr;
   1.663 +
   1.664 +  if (super == nullptr)
   1.665 +    super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
   1.666 +
   1.667 +  switch (uMsg) {
   1.668 +  case WM_PAINT: {
   1.669 +    HDC hdc;
   1.670 +    PAINTSTRUCT ps;
   1.671 +    RECT r;
   1.672 +    wchar_t windowText[1024];
   1.673 +
   1.674 +    GetWindowText(hwnd, windowText, 1024);
   1.675 +    // if the control contains text or is focused, draw it normally
   1.676 +    if (GetFocus() == hwnd || windowText[0] != '\0')
   1.677 +      return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
   1.678 +    
   1.679 +    GetClientRect(hwnd, &r);
   1.680 +    hdc = BeginPaint(hwnd, &ps);
   1.681 +    FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd)
   1.682 +                                       ? COLOR_WINDOW : COLOR_BTNFACE));
   1.683 +    SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
   1.684 +    SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT));
   1.685 +    SetBkMode(hdc, TRANSPARENT);
   1.686 +    wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT");
   1.687 +    // Get the actual edit control rect
   1.688 +    CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r);
   1.689 +    UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL;
   1.690 +    if (gRTLlayout)
   1.691 +      format |= DT_RIGHT;
   1.692 +    if (txt)
   1.693 +      DrawText(hdc, txt, wcslen(txt), &r, format);
   1.694 +    EndPaint(hwnd, &ps);
   1.695 +    return 0;
   1.696 +  }
   1.697 +
   1.698 +    // We handle WM_CHAR and WM_PASTE to limit the comment box to 500
   1.699 +    // bytes in UTF-8.
   1.700 +  case WM_CHAR: {
   1.701 +    // Leave accelerator keys and non-printing chars (except LF) alone
   1.702 +    if (wParam & (1<<24) || wParam & (1<<29) ||
   1.703 +        (wParam < ' ' && wParam != '\n'))
   1.704 +      break;
   1.705 +  
   1.706 +    wchar_t ch[2] = { (wchar_t)wParam, 0 };
   1.707 +    if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH)
   1.708 +      return 0;
   1.709 +
   1.710 +    break;
   1.711 +  }
   1.712 +
   1.713 +  case WM_PASTE: {
   1.714 +    if (IsClipboardFormatAvailable(CF_UNICODETEXT) &&
   1.715 +        OpenClipboard(hwnd)) {
   1.716 +      HGLOBAL hg = GetClipboardData(CF_UNICODETEXT); 
   1.717 +      wchar_t* pastedText = (wchar_t*)GlobalLock(hg);
   1.718 +      int newSize = 0;
   1.719 +
   1.720 +      if (pastedText)
   1.721 +        newSize = NewTextLength(hwnd, pastedText);
   1.722 +
   1.723 +      GlobalUnlock(hg);
   1.724 +      CloseClipboard();
   1.725 +
   1.726 +      if (newSize > MAX_COMMENT_LENGTH)
   1.727 +        return 0;
   1.728 +    }
   1.729 +    break;
   1.730 +  }
   1.731 +
   1.732 +  case WM_SETFOCUS:
   1.733 +  case WM_KILLFOCUS: {
   1.734 +    RECT r;
   1.735 +    GetClientRect(hwnd, &r);
   1.736 +    InvalidateRect(hwnd, &r, TRUE);
   1.737 +    break;
   1.738 +  }
   1.739 +
   1.740 +  case WM_DESTROY: {
   1.741 +    // cleanup our property
   1.742 +    HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT");
   1.743 +    if (hData)
   1.744 +      GlobalFree(hData);
   1.745 +  }
   1.746 +  }
   1.747 +
   1.748 +  return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
   1.749 +}
   1.750 +
   1.751 +// Resize a control to fit this text
   1.752 +static int ResizeControl(HWND hwndButton, RECT& rect, wstring text,
   1.753 +                         bool shiftLeft, int userDefinedPadding)
   1.754 +{
   1.755 +  HDC hdc = GetDC(hwndButton);
   1.756 +  HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0);
   1.757 +  if (hfont)
   1.758 +    SelectObject(hdc, hfont);
   1.759 +  SIZE size, oldSize;
   1.760 +  int sizeDiff = 0;
   1.761 +
   1.762 +  wchar_t oldText[1024];
   1.763 +  GetWindowText(hwndButton, oldText, 1024);
   1.764 +
   1.765 +  if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size)
   1.766 +      // default text on the button
   1.767 +      && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) {
   1.768 +    /*
   1.769 +     Expand control widths to accomidate wider text strings. For most
   1.770 +     controls (including buttons) the text padding is defined by the
   1.771 +     dialog's rc file. Some controls (such as checkboxes) have padding
   1.772 +     that extends to the end of the dialog, in which case we ignore the
   1.773 +     rc padding and rely on a user defined value passed in through
   1.774 +     userDefinedPadding.
   1.775 +    */
   1.776 +    int textIncrease = size.cx - oldSize.cx;
   1.777 +    if (textIncrease < 0)
   1.778 +      return 0;
   1.779 +    int existingTextPadding;
   1.780 +    if (userDefinedPadding == 0) 
   1.781 +      existingTextPadding = (rect.right - rect.left) - oldSize.cx;
   1.782 +    else 
   1.783 +      existingTextPadding = userDefinedPadding;
   1.784 +    sizeDiff = textIncrease + existingTextPadding;
   1.785 +
   1.786 +    if (shiftLeft) {
   1.787 +      // shift left by the amount the button should grow
   1.788 +      rect.left -= sizeDiff;
   1.789 +    }
   1.790 +    else {
   1.791 +      // grow right instead
   1.792 +      rect.right += sizeDiff;
   1.793 +    }
   1.794 +    MoveWindow(hwndButton, rect.left, rect.top,
   1.795 +               rect.right - rect.left,
   1.796 +               rect.bottom - rect.top,
   1.797 +               TRUE);
   1.798 +  }
   1.799 +  return sizeDiff;
   1.800 +}
   1.801 +
   1.802 +// The window was resized horizontally, so widen some of our
   1.803 +// controls to make use of the space
   1.804 +static void StretchControlsToFit(HWND hwndDlg)
   1.805 +{
   1.806 +  int controls[] = {
   1.807 +    IDC_DESCRIPTIONTEXT,
   1.808 +    IDC_SUBMITREPORTCHECK,
   1.809 +    IDC_COMMENTTEXT,
   1.810 +    IDC_INCLUDEURLCHECK,
   1.811 +    IDC_EMAILMECHECK,
   1.812 +    IDC_EMAILTEXT,
   1.813 +    IDC_PROGRESSTEXT
   1.814 +  };
   1.815 +
   1.816 +  RECT dlgRect;
   1.817 +  GetClientRect(hwndDlg, &dlgRect);
   1.818 +
   1.819 +  for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) {
   1.820 +    RECT r;
   1.821 +    HWND hwndControl = GetDlgItem(hwndDlg, controls[i]);
   1.822 +    GetRelativeRect(hwndControl, hwndDlg, &r);
   1.823 +    // 6 pixel spacing on the right
   1.824 +    if (r.right + 6 != dlgRect.right) {
   1.825 +      r.right = dlgRect.right - 6;
   1.826 +      MoveWindow(hwndControl, r.left, r.top,
   1.827 +                 r.right - r.left,
   1.828 +                 r.bottom - r.top,
   1.829 +                 TRUE);
   1.830 +    }
   1.831 +  }
   1.832 +}
   1.833 +
   1.834 +static void SubmitReportChecked(HWND hwndDlg)
   1.835 +{
   1.836 +  bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
   1.837 +  EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled);
   1.838 +  EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled);
   1.839 +  EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
   1.840 +  EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
   1.841 +  EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT),
   1.842 +               enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)
   1.843 +                           != 0));
   1.844 +  SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled);
   1.845 +}
   1.846 +
   1.847 +static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent,
   1.848 +                                      DLGPROC dlgProc, LPARAM param)
   1.849 +{
   1.850 +  INT_PTR rv = 0;
   1.851 +  if (gRTLlayout) {
   1.852 +    // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog
   1.853 +    // template.
   1.854 +    HRSRC hDialogRC = FindResource(nullptr, MAKEINTRESOURCE(idd),
   1.855 +                                   RT_DIALOG);
   1.856 +    HGLOBAL  hDlgTemplate = LoadResource(nullptr, hDialogRC);
   1.857 +    DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate);
   1.858 +    unsigned long sizeDlg = SizeofResource(nullptr, hDialogRC);
   1.859 +    HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg);
   1.860 +     DLGTEMPLATEEX* pMyDlgTemplate =
   1.861 +      (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate);
   1.862 +    memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg);
   1.863 +
   1.864 +    pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL;
   1.865 +
   1.866 +    rv = DialogBoxIndirectParam(nullptr, (LPCDLGTEMPLATE)pMyDlgTemplate,
   1.867 +                                hwndParent, dlgProc, param);
   1.868 +    GlobalUnlock(hMyDlgTemplate);
   1.869 +    GlobalFree(hMyDlgTemplate);
   1.870 +  }
   1.871 +  else {
   1.872 +    rv = DialogBoxParam(nullptr, MAKEINTRESOURCE(idd), hwndParent,
   1.873 +                        dlgProc, param);
   1.874 +  }
   1.875 +
   1.876 +  return rv;
   1.877 +}
   1.878 +
   1.879 +
   1.880 +static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
   1.881 +                                             WPARAM wParam, LPARAM lParam)
   1.882 +{
   1.883 +  static int sHeight = 0;
   1.884 +
   1.885 +  bool success;
   1.886 +  bool enabled;
   1.887 +
   1.888 +  switch (message) {
   1.889 +  case WM_INITDIALOG: {
   1.890 +    GetThemeSizes(hwndDlg);
   1.891 +    RECT r;
   1.892 +    GetClientRect(hwndDlg, &r);
   1.893 +    sHeight = r.bottom - r.top;
   1.894 +
   1.895 +    SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
   1.896 +    HICON hIcon = LoadIcon(GetModuleHandle(nullptr),
   1.897 +                           MAKEINTRESOURCE(IDI_MAINICON));
   1.898 +    SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
   1.899 +    SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
   1.900 +
   1.901 +    // resize the "View Report" button based on the string length
   1.902 +    RECT rect;
   1.903 +    HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON);
   1.904 +    GetRelativeRect(hwnd, hwndDlg, &rect);
   1.905 +    ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0);
   1.906 +    SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str());
   1.907 +
   1.908 +    hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK);
   1.909 +    GetRelativeRect(hwnd, hwndDlg, &rect);
   1.910 +    long maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false,
   1.911 +                                gCheckboxPadding);
   1.912 +    SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
   1.913 +                   Str(ST_CHECKSUBMIT).c_str());
   1.914 +
   1.915 +    if (!CheckBoolKey(gCrashReporterKey.c_str(),
   1.916 +                      SUBMIT_REPORT_VALUE, &enabled))
   1.917 +      enabled = ShouldEnableSending();
   1.918 +
   1.919 +    CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED
   1.920 +                                                           : BST_UNCHECKED);
   1.921 +    SubmitReportChecked(hwndDlg);
   1.922 +
   1.923 +    HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT);
   1.924 +    WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment,
   1.925 +                                                   GWLP_WNDPROC,
   1.926 +                                                   (LONG_PTR)EditSubclassProc);
   1.927 +
   1.928 +    // Subclass comment edit control to get placeholder text
   1.929 +    SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc);
   1.930 +    wstring commentGrayText = Str(ST_COMMENTGRAYTEXT);
   1.931 +    wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t));
   1.932 +    wcscpy(hMem, commentGrayText.c_str());
   1.933 +    SetProp(hwndComment, L"PROP_GRAYTEXT", hMem);
   1.934 +
   1.935 +    hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK);
   1.936 +    GetRelativeRect(hwnd, hwndDlg, &rect);
   1.937 +    long diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false,
   1.938 +                             gCheckboxPadding);
   1.939 +    maxdiff = std::max(diff, maxdiff);
   1.940 +    SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str());
   1.941 +
   1.942 +    // want this on by default
   1.943 +    if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) &&
   1.944 +        !enabled) {
   1.945 +      CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED);
   1.946 +    } else {
   1.947 +      CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED);
   1.948 +    }
   1.949 +
   1.950 +    hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK);
   1.951 +    GetRelativeRect(hwnd, hwndDlg, &rect);
   1.952 +    diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false,
   1.953 +                         gCheckboxPadding);
   1.954 +    maxdiff = std::max(diff, maxdiff);
   1.955 +    SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str());
   1.956 +
   1.957 +    if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) &&
   1.958 +        enabled) {
   1.959 +      CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
   1.960 +    } else {
   1.961 +      CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
   1.962 +    }
   1.963 +
   1.964 +    wstring email;
   1.965 +    if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) {
   1.966 +      SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str());
   1.967 +    }
   1.968 +
   1.969 +    // Subclass email edit control to get placeholder text
   1.970 +    HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
   1.971 +    OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail,
   1.972 +                                           GWLP_WNDPROC,
   1.973 +                                           (LONG_PTR)EditSubclassProc);
   1.974 +    SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc);
   1.975 +    wstring emailGrayText = Str(ST_EMAILGRAYTEXT);
   1.976 +    hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t));
   1.977 +    wcscpy(hMem, emailGrayText.c_str());
   1.978 +    SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem);
   1.979 +
   1.980 +    SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str());
   1.981 +
   1.982 +    RECT closeRect;
   1.983 +    HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON);
   1.984 +    GetRelativeRect(hwndClose, hwndDlg, &closeRect);
   1.985 +
   1.986 +    RECT restartRect;
   1.987 +    HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON);
   1.988 +    GetRelativeRect(hwndRestart, hwndDlg, &restartRect);
   1.989 +
   1.990 +    // set the close button text and shift the buttons around
   1.991 +    // since the size may need to change
   1.992 +    int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT),
   1.993 +                                 true, 0);
   1.994 +    restartRect.left -= sizeDiff;
   1.995 +    restartRect.right -= sizeDiff;
   1.996 +    SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str());
   1.997 +
   1.998 +    if (gRestartArgs.size() > 0) {
   1.999 +      // Resize restart button to fit text
  1.1000 +      ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0);
  1.1001 +      SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str());
  1.1002 +    } else {
  1.1003 +      // No restart arguments, so just hide the restart button
  1.1004 +      SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
  1.1005 +    }
  1.1006 +    // See if we need to widen the window
  1.1007 +    // Leave 6 pixels on either side + 6 pixels between the buttons
  1.1008 +    int neededSize = closeRect.right - closeRect.left +
  1.1009 +      restartRect.right - restartRect.left + 6 * 3;
  1.1010 +    GetClientRect(hwndDlg, &r);
  1.1011 +    // We may already have resized one of the checkboxes above
  1.1012 +    maxdiff = std::max(maxdiff, neededSize - (r.right - r.left));
  1.1013 +
  1.1014 +    if (maxdiff > 0) {
  1.1015 +      // widen window
  1.1016 +      GetWindowRect(hwndDlg, &r);
  1.1017 +      r.right += maxdiff;
  1.1018 +      MoveWindow(hwndDlg, r.left, r.top,
  1.1019 +                 r.right - r.left, r.bottom - r.top, TRUE);
  1.1020 +      // shift both buttons right
  1.1021 +      if (restartRect.left + maxdiff < 6)
  1.1022 +        maxdiff += 6;
  1.1023 +      closeRect.left += maxdiff;
  1.1024 +      closeRect.right += maxdiff;
  1.1025 +      restartRect.left += maxdiff;
  1.1026 +      restartRect.right += maxdiff;
  1.1027 +      MoveWindow(hwndClose, closeRect.left, closeRect.top,
  1.1028 +                 closeRect.right - closeRect.left,
  1.1029 +                 closeRect.bottom - closeRect.top,
  1.1030 +                 TRUE);
  1.1031 +      StretchControlsToFit(hwndDlg);
  1.1032 +    }
  1.1033 +    // need to move the restart button regardless
  1.1034 +    MoveWindow(hwndRestart, restartRect.left, restartRect.top,
  1.1035 +               restartRect.right - restartRect.left,
  1.1036 +               restartRect.bottom - restartRect.top,
  1.1037 +               TRUE);
  1.1038 +
  1.1039 +    // Resize the description text last, in case the window was resized
  1.1040 +    // before this.
  1.1041 +    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
  1.1042 +                       EM_SETEVENTMASK, (WPARAM)nullptr,
  1.1043 +                       ENM_REQUESTRESIZE);
  1.1044 +    
  1.1045 +    wstring description = Str(ST_CRASHREPORTERHEADER);
  1.1046 +    description += L"\n\n";
  1.1047 +    description += Str(ST_CRASHREPORTERDESCRIPTION);
  1.1048 +    SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str());
  1.1049 +
  1.1050 +
  1.1051 +    // Make the title bold.
  1.1052 +    CHARFORMAT fmt = { 0, };
  1.1053 +    fmt.cbSize = sizeof(fmt);
  1.1054 +    fmt.dwMask = CFM_BOLD;
  1.1055 +    fmt.dwEffects = CFE_BOLD;
  1.1056 +    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL,
  1.1057 +                       0, Str(ST_CRASHREPORTERHEADER).length());
  1.1058 +    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT,
  1.1059 +                       SCF_SELECTION, (LPARAM)&fmt);
  1.1060 +    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0);
  1.1061 +    // Force redraw.
  1.1062 +    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
  1.1063 +                       EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
  1.1064 +    // Force resize.
  1.1065 +    SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
  1.1066 +                       EM_REQUESTRESIZE, 0, 0);
  1.1067 +
  1.1068 +    // if no URL was given, hide the URL checkbox
  1.1069 +    if (gQueryParameters.find(L"URL") == gQueryParameters.end()) {
  1.1070 +      RECT urlCheckRect, emailCheckRect;
  1.1071 +      GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect);
  1.1072 +      GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect);
  1.1073 +
  1.1074 +      SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false);
  1.1075 +
  1.1076 +      gAttachedBottom.erase(IDC_VIEWREPORTBUTTON);
  1.1077 +      gAttachedBottom.erase(IDC_SUBMITREPORTCHECK);
  1.1078 +      gAttachedBottom.erase(IDC_COMMENTTEXT);
  1.1079 +
  1.1080 +      StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top);
  1.1081 +
  1.1082 +      gAttachedBottom.insert(IDC_VIEWREPORTBUTTON);
  1.1083 +      gAttachedBottom.insert(IDC_SUBMITREPORTCHECK);
  1.1084 +      gAttachedBottom.insert(IDC_COMMENTTEXT);
  1.1085 +    }
  1.1086 +
  1.1087 +    MaybeResizeProgressText(hwndDlg);
  1.1088 +
  1.1089 +    // Open the AVI resource for the throbber
  1.1090 +    Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER),
  1.1091 +                 MAKEINTRESOURCE(IDR_THROBBER));
  1.1092 +
  1.1093 +    UpdateURL(hwndDlg);
  1.1094 +    UpdateEmail(hwndDlg);
  1.1095 +
  1.1096 +    SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK));
  1.1097 +    return FALSE;
  1.1098 +  }
  1.1099 +  case WM_SIZE: {
  1.1100 +    ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
  1.1101 +    sHeight = HIWORD(lParam);
  1.1102 +    InvalidateRect(hwndDlg, nullptr, TRUE);
  1.1103 +    return FALSE;
  1.1104 +  }
  1.1105 +  case WM_NOTIFY: {
  1.1106 +    NMHDR* notification = reinterpret_cast<NMHDR*>(lParam);
  1.1107 +    if (notification->code == EN_REQUESTRESIZE) {
  1.1108 +      // Resizing the rich edit control to fit the description text.
  1.1109 +      REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam);
  1.1110 +      RECT newSize = reqresize->rc;
  1.1111 +      RECT oldSize;
  1.1112 +      GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize);
  1.1113 +
  1.1114 +      // resize the text box as requested
  1.1115 +      MoveWindow(notification->hwndFrom, newSize.left, newSize.top,
  1.1116 +                 newSize.right - newSize.left, newSize.bottom - newSize.top,
  1.1117 +                 TRUE);
  1.1118 +
  1.1119 +      // Resize the dialog to fit (the WM_SIZE handler will move the controls)
  1.1120 +      StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom);
  1.1121 +    }
  1.1122 +    return FALSE;
  1.1123 +  }
  1.1124 +  case WM_COMMAND: {
  1.1125 +    if (HIWORD(wParam) == BN_CLICKED) {
  1.1126 +      switch(LOWORD(wParam)) {
  1.1127 +      case IDC_VIEWREPORTBUTTON:
  1.1128 +        DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg,
  1.1129 +                       (DLGPROC)ViewReportDialogProc, 0);
  1.1130 +        break;
  1.1131 +      case IDC_SUBMITREPORTCHECK:
  1.1132 +        SubmitReportChecked(hwndDlg);
  1.1133 +        break;
  1.1134 +      case IDC_INCLUDEURLCHECK:
  1.1135 +        UpdateURL(hwndDlg);
  1.1136 +        break;
  1.1137 +      case IDC_EMAILMECHECK:
  1.1138 +        UpdateEmail(hwndDlg);
  1.1139 +        break;
  1.1140 +      case IDC_CLOSEBUTTON:
  1.1141 +        MaybeSendReport(hwndDlg);
  1.1142 +        break;
  1.1143 +      case IDC_RESTARTBUTTON:
  1.1144 +        RestartApplication();
  1.1145 +        MaybeSendReport(hwndDlg);
  1.1146 +        break;
  1.1147 +      }
  1.1148 +    } else if (HIWORD(wParam) == EN_CHANGE) {
  1.1149 +      switch(LOWORD(wParam)) {
  1.1150 +      case IDC_EMAILTEXT:
  1.1151 +        UpdateEmail(hwndDlg);
  1.1152 +        break;
  1.1153 +      case IDC_COMMENTTEXT:
  1.1154 +        UpdateComment(hwndDlg);
  1.1155 +      }
  1.1156 +    }
  1.1157 +
  1.1158 +    return FALSE;
  1.1159 +  }
  1.1160 +  case WM_UPLOADCOMPLETE: {
  1.1161 +    WaitForSingleObject(gThreadHandle, INFINITE);
  1.1162 +    success = (wParam == 1);
  1.1163 +    SendCompleted(success, WideToUTF8(gSendData.serverResponse));
  1.1164 +    // hide throbber
  1.1165 +    Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER));
  1.1166 +    SetDlgItemVisible(hwndDlg, IDC_THROBBER, false);
  1.1167 +
  1.1168 +    SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
  1.1169 +                   success ?
  1.1170 +                   Str(ST_REPORTSUBMITSUCCESS).c_str() :
  1.1171 +                   Str(ST_SUBMITFAILED).c_str());
  1.1172 +    MaybeResizeProgressText(hwndDlg);
  1.1173 +    // close dialog after 5 seconds
  1.1174 +    SetTimer(hwndDlg, 0, 5000, nullptr);
  1.1175 +    //
  1.1176 +    return TRUE;
  1.1177 +  }
  1.1178 +
  1.1179 +  case WM_LBUTTONDOWN: {
  1.1180 +    HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
  1.1181 +    POINT p = { LOWORD(lParam), HIWORD(lParam) };
  1.1182 +    // if the email edit control is clicked, enable it,
  1.1183 +    // check the email checkbox, and focus the email edit control
  1.1184 +    if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail &&
  1.1185 +        IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) &&
  1.1186 +        !IsWindowEnabled(hwndEmail) &&
  1.1187 +        IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) {
  1.1188 +      CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
  1.1189 +      UpdateEmail(hwndDlg);
  1.1190 +      SetFocus(hwndEmail);
  1.1191 +    }
  1.1192 +    break;
  1.1193 +  }
  1.1194 +
  1.1195 +  case WM_TIMER: {
  1.1196 +    // The "1" gets used down in UIShowCrashUI to indicate that we at least
  1.1197 +    // tried to send the report.
  1.1198 +    EndCrashReporterDialog(hwndDlg, 1);
  1.1199 +    return FALSE;
  1.1200 +  }
  1.1201 +
  1.1202 +  case WM_CLOSE: {
  1.1203 +    EndCrashReporterDialog(hwndDlg, 0);
  1.1204 +    return FALSE;
  1.1205 +  }
  1.1206 +  }
  1.1207 +  return FALSE;
  1.1208 +}
  1.1209 +
  1.1210 +static wstring UTF8ToWide(const string& utf8, bool *success)
  1.1211 +{
  1.1212 +  wchar_t* buffer = nullptr;
  1.1213 +  int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
  1.1214 +                                        -1, nullptr, 0);
  1.1215 +  if(buffer_size == 0) {
  1.1216 +    if (success)
  1.1217 +      *success = false;
  1.1218 +    return L"";
  1.1219 +  }
  1.1220 +
  1.1221 +  buffer = new wchar_t[buffer_size];
  1.1222 +  if(buffer == nullptr) {
  1.1223 +    if (success)
  1.1224 +      *success = false;
  1.1225 +    return L"";
  1.1226 +  }
  1.1227 +
  1.1228 +  MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
  1.1229 +                      -1, buffer, buffer_size);
  1.1230 +  wstring str = buffer;
  1.1231 +  delete [] buffer;
  1.1232 +
  1.1233 +  if (success)
  1.1234 +    *success = true;
  1.1235 +
  1.1236 +  return str;
  1.1237 +}
  1.1238 +
  1.1239 +static string WideToMBCP(const wstring& wide,
  1.1240 +                         unsigned int cp,
  1.1241 +                         bool* success = nullptr)
  1.1242 +{
  1.1243 +  char* buffer = nullptr;
  1.1244 +  int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(),
  1.1245 +                                        -1, nullptr, 0, nullptr, nullptr);
  1.1246 +  if(buffer_size == 0) {
  1.1247 +    if (success)
  1.1248 +      *success = false;
  1.1249 +    return "";
  1.1250 +  }
  1.1251 +
  1.1252 +  buffer = new char[buffer_size];
  1.1253 +  if(buffer == nullptr) {
  1.1254 +    if (success)
  1.1255 +      *success = false;
  1.1256 +    return "";
  1.1257 +  }
  1.1258 +
  1.1259 +  WideCharToMultiByte(cp, 0, wide.c_str(),
  1.1260 +                      -1, buffer, buffer_size, nullptr, nullptr);
  1.1261 +  string mb = buffer;
  1.1262 +  delete [] buffer;
  1.1263 +
  1.1264 +  if (success)
  1.1265 +    *success = true;
  1.1266 +
  1.1267 +  return mb;
  1.1268 +}
  1.1269 +
  1.1270 +string WideToUTF8(const wstring& wide, bool* success)
  1.1271 +{
  1.1272 +  return WideToMBCP(wide, CP_UTF8, success);
  1.1273 +}
  1.1274 +
  1.1275 +/* === Crashreporter UI Functions === */
  1.1276 +
  1.1277 +bool UIInit()
  1.1278 +{
  1.1279 +  for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
  1.1280 +    gAttachedBottom.insert(kDefaultAttachedBottom[i]);
  1.1281 +  }
  1.1282 +
  1.1283 +  DoInitCommonControls();
  1.1284 +
  1.1285 +  return true;
  1.1286 +}
  1.1287 +
  1.1288 +void UIShutdown()
  1.1289 +{
  1.1290 +}
  1.1291 +
  1.1292 +void UIShowDefaultUI()
  1.1293 +{
  1.1294 +  MessageBox(nullptr, Str(ST_CRASHREPORTERDEFAULT).c_str(),
  1.1295 +             L"Crash Reporter",
  1.1296 +             MB_OK | MB_ICONSTOP);
  1.1297 +}
  1.1298 +
  1.1299 +bool UIShowCrashUI(const string& dumpFile,
  1.1300 +                   const StringTable& queryParameters,
  1.1301 +                   const string& sendURL,
  1.1302 +                   const vector<string>& restartArgs)
  1.1303 +{
  1.1304 +  gSendData.hDlg = nullptr;
  1.1305 +  gSendData.dumpFile = UTF8ToWide(dumpFile);
  1.1306 +  gSendData.sendURL = UTF8ToWide(sendURL);
  1.1307 +
  1.1308 +  for (StringTable::const_iterator i = queryParameters.begin();
  1.1309 +       i != queryParameters.end();
  1.1310 +       i++) {
  1.1311 +    gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
  1.1312 +  }
  1.1313 +
  1.1314 +  if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
  1.1315 +    gCrashReporterKey = L"Software\\";
  1.1316 +    if (!gQueryParameters[L"Vendor"].empty()) {
  1.1317 +      gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
  1.1318 +    }
  1.1319 +    gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter";
  1.1320 +  }
  1.1321 +
  1.1322 +  if (gQueryParameters.find(L"URL") != gQueryParameters.end())
  1.1323 +    gURLParameter = gQueryParameters[L"URL"];
  1.1324 +
  1.1325 +  gRestartArgs = restartArgs;
  1.1326 +
  1.1327 +  if (gStrings.find("isRTL") != gStrings.end() &&
  1.1328 +      gStrings["isRTL"] == "yes")
  1.1329 +    gRTLlayout = true;
  1.1330 +
  1.1331 +  return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, nullptr,
  1.1332 +                                     (DLGPROC)CrashReporterDialogProc, 0);
  1.1333 +}
  1.1334 +
  1.1335 +void UIError_impl(const string& message)
  1.1336 +{
  1.1337 +  wstring title = Str(ST_CRASHREPORTERTITLE);
  1.1338 +  if (title.empty())
  1.1339 +    title = L"Crash Reporter Error";
  1.1340 +
  1.1341 +  MessageBox(nullptr, UTF8ToWide(message).c_str(), title.c_str(),
  1.1342 +             MB_OK | MB_ICONSTOP);
  1.1343 +}
  1.1344 +
  1.1345 +bool UIGetIniPath(string& path)
  1.1346 +{
  1.1347 +  wchar_t fileName[MAX_PATH];
  1.1348 +  if (GetModuleFileName(nullptr, fileName, MAX_PATH)) {
  1.1349 +    // get crashreporter ini
  1.1350 +    wchar_t* s = wcsrchr(fileName, '.');
  1.1351 +    if (s) {
  1.1352 +      wcscpy(s, L".ini");
  1.1353 +      path = WideToUTF8(fileName);
  1.1354 +      return true;
  1.1355 +    }
  1.1356 +  }
  1.1357 +
  1.1358 +  return false;
  1.1359 +}
  1.1360 +
  1.1361 +bool UIGetSettingsPath(const string& vendor,
  1.1362 +                       const string& product,
  1.1363 +                       string& settings_path)
  1.1364 +{
  1.1365 +  wchar_t path[MAX_PATH];
  1.1366 +  HRESULT hRes = SHGetFolderPath(nullptr,
  1.1367 +                                 CSIDL_APPDATA,
  1.1368 +                                 nullptr,
  1.1369 +                                 0,
  1.1370 +                                 path);
  1.1371 +  if (FAILED(hRes)) {
  1.1372 +    // This provides a fallback for getting the path to APPDATA by querying the
  1.1373 +    // registry when the call to SHGetFolderPath is unable to provide this path
  1.1374 +    // (Bug 513958).
  1.1375 +    HKEY key;
  1.1376 +    DWORD type, size, dwRes;
  1.1377 +    dwRes = ::RegOpenKeyExW(HKEY_CURRENT_USER,
  1.1378 +                            L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
  1.1379 +                            0,
  1.1380 +                            KEY_READ,
  1.1381 +                            &key);
  1.1382 +    if (dwRes != ERROR_SUCCESS)
  1.1383 +      return false;
  1.1384 +
  1.1385 +    dwRes = RegQueryValueExW(key,
  1.1386 +                             L"AppData",
  1.1387 +                             nullptr,
  1.1388 +                             &type,
  1.1389 +                             (LPBYTE)&path,
  1.1390 +                             &size);
  1.1391 +    ::RegCloseKey(key);
  1.1392 +    // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
  1.1393 +    // buffer size must not equal 0, and the buffer size be a multiple of 2.
  1.1394 +    if (dwRes != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0)
  1.1395 +        return false;
  1.1396 +  }
  1.1397 +
  1.1398 +  if (!vendor.empty()) {
  1.1399 +    PathAppend(path, UTF8ToWide(vendor).c_str());
  1.1400 +  }
  1.1401 +  PathAppend(path, UTF8ToWide(product).c_str());
  1.1402 +  PathAppend(path, L"Crash Reports");
  1.1403 +  settings_path = WideToUTF8(path);
  1.1404 +  return true;
  1.1405 +}
  1.1406 +
  1.1407 +bool UIEnsurePathExists(const string& path)
  1.1408 +{
  1.1409 +  if (CreateDirectory(UTF8ToWide(path).c_str(), nullptr) == 0) {
  1.1410 +    if (GetLastError() != ERROR_ALREADY_EXISTS)
  1.1411 +      return false;
  1.1412 +  }
  1.1413 +
  1.1414 +  return true;
  1.1415 +}
  1.1416 +
  1.1417 +bool UIFileExists(const string& path)
  1.1418 +{
  1.1419 +  DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str());
  1.1420 +  return (attrs != INVALID_FILE_ATTRIBUTES);
  1.1421 +}
  1.1422 +
  1.1423 +bool UIMoveFile(const string& oldfile, const string& newfile)
  1.1424 +{
  1.1425 +  if (oldfile == newfile)
  1.1426 +    return true;
  1.1427 +
  1.1428 +  return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
  1.1429 +    == TRUE;
  1.1430 +}
  1.1431 +
  1.1432 +bool UIDeleteFile(const string& oldfile)
  1.1433 +{
  1.1434 +  return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE;
  1.1435 +}
  1.1436 +
  1.1437 +ifstream* UIOpenRead(const string& filename)
  1.1438 +{
  1.1439 +  // adapted from breakpad's src/common/windows/http_upload.cc
  1.1440 +
  1.1441 +  // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
  1.1442 +  // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
  1.1443 +  // later, _wfopen has been deprecated in favor of _wfopen_s, which does
  1.1444 +  // not exist in earlier versions, so let the ifstream open the file itself.
  1.1445 +#if _MSC_VER >= 1400  // MSVC 2005/8
  1.1446 +  ifstream* file = new ifstream();
  1.1447 +  file->open(UTF8ToWide(filename).c_str(), ios::in);
  1.1448 +#elif defined(_MSC_VER)
  1.1449 +  ifstream* file = new ifstream(_wfopen(UTF8ToWide(filename).c_str(), L"r"));
  1.1450 +#else   // GCC
  1.1451 +  ifstream* file = new ifstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
  1.1452 +                                ios::in);
  1.1453 +#endif  // _MSC_VER >= 1400
  1.1454 +
  1.1455 +  return file;
  1.1456 +}
  1.1457 +
  1.1458 +ofstream* UIOpenWrite(const string& filename, bool append) // append=false
  1.1459 +{
  1.1460 +  // adapted from breakpad's src/common/windows/http_upload.cc
  1.1461 +
  1.1462 +  // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
  1.1463 +  // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
  1.1464 +  // later, _wfopen has been deprecated in favor of _wfopen_s, which does
  1.1465 +  // not exist in earlier versions, so let the ifstream open the file itself.
  1.1466 +#if _MSC_VER >= 1400  // MSVC 2005/8
  1.1467 +  ofstream* file = new ofstream();
  1.1468 +  file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app
  1.1469 +                                                  : ios::out);
  1.1470 +#elif defined(_MSC_VER)
  1.1471 +  ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(),
  1.1472 +                                        append ? L"a" : L"w"));
  1.1473 +#else   // GCC
  1.1474 +  ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
  1.1475 +                                append ? ios::out | ios::app : ios::out);
  1.1476 +#endif  // _MSC_VER >= 1400
  1.1477 +
  1.1478 +  return file;
  1.1479 +}
  1.1480 +
  1.1481 +struct FileData
  1.1482 +{
  1.1483 +  FILETIME timestamp;
  1.1484 +  wstring path;
  1.1485 +};
  1.1486 +
  1.1487 +static bool CompareFDTime(const FileData& fd1, const FileData& fd2)
  1.1488 +{
  1.1489 +  return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0;
  1.1490 +}
  1.1491 +
  1.1492 +void UIPruneSavedDumps(const std::string& directory)
  1.1493 +{
  1.1494 +  wstring wdirectory = UTF8ToWide(directory);
  1.1495 +
  1.1496 +  WIN32_FIND_DATA fdata;
  1.1497 +  wstring findpath = wdirectory + L"\\*.dmp";
  1.1498 +  HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata);
  1.1499 +  if (dirlist == INVALID_HANDLE_VALUE)
  1.1500 +    return;
  1.1501 +
  1.1502 +  vector<FileData> dumpfiles;
  1.1503 +
  1.1504 +  for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) {
  1.1505 +    FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName};
  1.1506 +    dumpfiles.push_back(fd);
  1.1507 +  }
  1.1508 +
  1.1509 +  sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime);
  1.1510 +
  1.1511 +  while (dumpfiles.size() > kSaveCount) {
  1.1512 +    // get the path of the oldest file
  1.1513 +    wstring path = (--dumpfiles.end())->path;
  1.1514 +    DeleteFile(path.c_str());
  1.1515 +
  1.1516 +    // s/.dmp/.extra/
  1.1517 +    path.replace(path.size() - 4, 4, L".extra");
  1.1518 +    DeleteFile(path.c_str());
  1.1519 +
  1.1520 +    dumpfiles.pop_back();
  1.1521 +  }
  1.1522 +}

mercurial