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