summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/client/crashreporter_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/client/crashreporter_win.cpp')
-rw-r--r--toolkit/crashreporter/client/crashreporter_win.cpp1568
1 files changed, 0 insertions, 1568 deletions
diff --git a/toolkit/crashreporter/client/crashreporter_win.cpp b/toolkit/crashreporter/client/crashreporter_win.cpp
deleted file mode 100644
index 57ca495ba..000000000
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ /dev/null
@@ -1,1568 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifdef WIN32_LEAN_AND_MEAN
-#undef WIN32_LEAN_AND_MEAN
-#endif
-
-#include "crashreporter.h"
-
-#include <windows.h>
-#include <versionhelpers.h>
-#include <commctrl.h>
-#include <richedit.h>
-#include <shellapi.h>
-#include <shlobj.h>
-#include <shlwapi.h>
-#include <math.h>
-#include <set>
-#include <algorithm>
-#include "resource.h"
-#include "client/windows/sender/crash_report_sender.h"
-#include "common/windows/string_utils-inl.h"
-
-#define CRASH_REPORTER_VALUE L"Enabled"
-#define SUBMIT_REPORT_VALUE L"SubmitCrashReport"
-#define SUBMIT_REPORT_OLD L"SubmitReport"
-#define INCLUDE_URL_VALUE L"IncludeURL"
-#define EMAIL_ME_VALUE L"EmailMe"
-#define EMAIL_VALUE L"Email"
-#define MAX_EMAIL_LENGTH 1024
-
-#define SENDURL_ORIGINAL L"https://crash-reports.mozilla.com/submit"
-#define SENDURL_XPSP2 L"https://crash-reports-xpsp2.mozilla.com/submit"
-
-#define WM_UPLOADCOMPLETE WM_APP
-
-// Thanks, Windows.h :(
-#undef min
-#undef max
-
-using std::string;
-using std::wstring;
-using std::map;
-using std::vector;
-using std::set;
-using std::ios;
-using std::ifstream;
-using std::ofstream;
-
-using namespace CrashReporter;
-
-typedef struct {
- HWND hDlg;
- map<wstring,wstring> queryParameters;
- map<wstring,wstring> files;
- wstring sendURL;
-
- wstring serverResponse;
-} SendThreadData;
-
-/*
- * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx
- * "The DLGTEMPLATEEX structure is not defined in any standard header file.
- * The structure definition is provided here to explain the format of an
- * extended template for a dialog box.
-*/
-typedef struct {
- WORD dlgVer;
- WORD signature;
- DWORD helpID;
- DWORD exStyle;
- // There's more to this struct, but it has weird variable-length
- // members, and I only actually need to touch exStyle on an existing
- // instance, so I've omitted the rest.
-} DLGTEMPLATEEX;
-
-static HANDLE gThreadHandle;
-static SendThreadData gSendData = { 0, };
-static vector<string> gRestartArgs;
-static map<wstring,wstring> gQueryParameters;
-static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
-static wstring gURLParameter;
-static int gCheckboxPadding = 6;
-static bool gRTLlayout = false;
-
-// When vertically resizing the dialog, these items should move down
-static set<UINT> gAttachedBottom;
-
-// Default set of items for gAttachedBottom
-static const UINT kDefaultAttachedBottom[] = {
- IDC_SUBMITREPORTCHECK,
- IDC_VIEWREPORTBUTTON,
- IDC_COMMENTTEXT,
- IDC_INCLUDEURLCHECK,
- IDC_EMAILMECHECK,
- IDC_EMAILTEXT,
- IDC_PROGRESSTEXT,
- IDC_THROBBER,
- IDC_CLOSEBUTTON,
- IDC_RESTARTBUTTON,
-};
-
-static wstring UTF8ToWide(const string& utf8, bool *success = 0);
-static DWORD WINAPI SendThreadProc(LPVOID param);
-
-static wstring Str(const char* key)
-{
- return UTF8ToWide(gStrings[key]);
-}
-
-/* === win32 helper functions === */
-
-static void DoInitCommonControls()
-{
- INITCOMMONCONTROLSEX ic;
- ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
- ic.dwICC = ICC_PROGRESS_CLASS;
- InitCommonControlsEx(&ic);
- // also get the rich edit control
- LoadLibrary(L"Msftedit.dll");
-}
-
-static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
-{
- DWORD type, dataSize;
- dataSize = sizeof(DWORD);
- if (RegQueryValueEx(hRegKey, valueName, nullptr,
- &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS &&
- type == REG_DWORD)
- return true;
-
- return false;
-}
-
-// Removes a value from HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER, if it exists.
-static void RemoveUnusedValues(const wchar_t* key, LPCTSTR valueName)
-{
- HKEY hRegKey;
-
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_SET_VALUE, &hRegKey)
- == ERROR_SUCCESS) {
- RegDeleteValue(hRegKey, valueName);
- RegCloseKey(hRegKey);
- }
-
- if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_SET_VALUE, &hRegKey)
- == ERROR_SUCCESS) {
- RegDeleteValue(hRegKey, valueName);
- RegCloseKey(hRegKey);
- }
-}
-
-static bool CheckBoolKey(const wchar_t* key,
- const wchar_t* valueName,
- bool* enabled)
-{
- /*
- * NOTE! This code needs to stay in sync with the preference checking
- * code in in nsExceptionHandler.cpp.
- */
- *enabled = false;
- bool found = false;
- HKEY hRegKey;
- DWORD val;
- // see if our reg key is set globally
- if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
- if (GetBoolValue(hRegKey, valueName, &val)) {
- *enabled = (val == 1);
- found = true;
- }
- RegCloseKey(hRegKey);
- } else {
- // look for it in user settings
- if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
- if (GetBoolValue(hRegKey, valueName, &val)) {
- *enabled = (val == 1);
- found = true;
- }
- RegCloseKey(hRegKey);
- }
- }
-
- return found;
-}
-
-static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
-{
- /*
- * NOTE! This code needs to stay in sync with the preference setting
- * code in in nsExceptionHandler.cpp.
- */
- HKEY hRegKey;
-
- // remove the old value from the registry if it exists
- RemoveUnusedValues(key, SUBMIT_REPORT_OLD);
-
- if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
- DWORD data = (enabled ? 1 : 0);
- RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
- RegCloseKey(hRegKey);
- }
-}
-
-static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value)
-{
- DWORD type, dataSize;
- wchar_t buf[2048];
- dataSize = sizeof(buf);
- if (RegQueryValueEx(hRegKey, valueName, nullptr,
- &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS &&
- type == REG_SZ) {
- value = buf;
- return true;
- }
-
- return false;
-}
-
-static bool GetStringKey(const wchar_t* key,
- const wchar_t* valueName,
- wstring& value)
-{
- value = L"";
- bool found = false;
- HKEY hRegKey;
- // see if our reg key is set globally
- if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
- if (GetStringValue(hRegKey, valueName, value)) {
- found = true;
- }
- RegCloseKey(hRegKey);
- } else {
- // look for it in user settings
- if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
- if (GetStringValue(hRegKey, valueName, value)) {
- found = true;
- }
- RegCloseKey(hRegKey);
- }
- }
-
- return found;
-}
-
-static void SetStringKey(const wchar_t* key,
- const wchar_t* valueName,
- const wstring& value)
-{
- HKEY hRegKey;
- if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
- RegSetValueEx(hRegKey, valueName, 0, REG_SZ,
- (LPBYTE)value.c_str(),
- (value.length() + 1) * sizeof(wchar_t));
- RegCloseKey(hRegKey);
- }
-}
-
-static string FormatLastError()
-{
- DWORD err = GetLastError();
- LPWSTR s;
- string message = "Crash report submission failed: ";
- // odds are it's a WinInet error
- HANDLE hInetModule = GetModuleHandle(L"WinInet.dll");
- if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_FROM_HMODULE,
- hInetModule,
- err,
- 0,
- (LPWSTR)&s,
- 0,
- nullptr) != 0) {
- message += WideToUTF8(s, nullptr);
- LocalFree(s);
- // strip off any trailing newlines
- string::size_type n = message.find_last_not_of("\r\n");
- if (n < message.size() - 1) {
- message.erase(n+1);
- }
- }
- else {
- char buf[64];
- sprintf(buf, "Unknown error, error code: 0x%08x", err);
- message += buf;
- }
- return message;
-}
-
-#define TS_DRAW 2
-#define BP_CHECKBOX 3
-
-typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
-typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
-typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
- int iStateId, RECT* prc, int ts,
- SIZE* psz);
-typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
- int iStateId, const RECT* pRect,
- RECT* pContentRect);
-
-
-static void GetThemeSizes(HWND hwnd)
-{
- HMODULE themeDLL = LoadLibrary(L"uxtheme.dll");
-
- if (!themeDLL)
- return;
-
- OpenThemeDataPtr openTheme =
- (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData");
- CloseThemeDataPtr closeTheme =
- (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData");
- GetThemePartSizePtr getThemePartSize =
- (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize");
-
- if (!openTheme || !closeTheme || !getThemePartSize) {
- FreeLibrary(themeDLL);
- return;
- }
-
- HANDLE buttonTheme = openTheme(hwnd, L"Button");
- if (!buttonTheme) {
- FreeLibrary(themeDLL);
- return;
- }
- HDC hdc = GetDC(hwnd);
- SIZE s;
- getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, nullptr, TS_DRAW, &s);
- gCheckboxPadding = s.cx;
- closeTheme(buttonTheme);
- FreeLibrary(themeDLL);
-}
-
-// Gets the position of a window relative to another window's client area
-static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r)
-{
- GetWindowRect(hwnd, r);
- MapWindowPoints(nullptr, hwndParent, (POINT*)r, 2);
-}
-
-static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
-{
- HWND hwnd = GetDlgItem(hwndDlg, item);
-
- ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE);
-}
-
-static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled)
-{
- HWND hwnd = GetDlgItem(hwndDlg, item);
- LONG style = GetWindowLong(hwnd, GWL_STYLE);
- if (!disabled)
- style |= WS_DISABLED;
- else
- style &= ~WS_DISABLED;
-
- SetWindowLong(hwnd, GWL_STYLE, style);
-}
-
-/* === Crash Reporting Dialog === */
-
-static void StretchDialog(HWND hwndDlg, int ydiff)
-{
- RECT r;
- GetWindowRect(hwndDlg, &r);
- r.bottom += ydiff;
- MoveWindow(hwndDlg, r.left, r.top,
- r.right - r.left, r.bottom - r.top, TRUE);
-}
-
-static void ReflowDialog(HWND hwndDlg, int ydiff)
-{
- // Move items attached to the bottom down/up by as much as
- // the window resize
- for (set<UINT>::const_iterator item = gAttachedBottom.begin();
- item != gAttachedBottom.end();
- item++) {
- RECT r;
- HWND hwnd = GetDlgItem(hwndDlg, *item);
- GetRelativeRect(hwnd, hwndDlg, &r);
- r.top += ydiff;
- r.bottom += ydiff;
- MoveWindow(hwnd, r.left, r.top,
- r.right - r.left, r.bottom - r.top, TRUE);
- }
-}
-
-static DWORD WINAPI SendThreadProc(LPVOID param)
-{
- bool finishedOk;
- SendThreadData* td = (SendThreadData*)param;
-
- if (td->sendURL.empty()) {
- finishedOk = false;
- LogMessage("No server URL, not sending report");
- } else {
- google_breakpad::CrashReportSender sender(L"");
- finishedOk = (sender.SendCrashReport(td->sendURL,
- td->queryParameters,
- td->files,
- &td->serverResponse)
- == google_breakpad::RESULT_SUCCEEDED);
- if (finishedOk) {
- LogMessage("Crash report submitted successfully");
- }
- else {
- // get an error string and print it to the log
- //XXX: would be nice to get the HTTP status code here, filed:
- // http://code.google.com/p/google-breakpad/issues/detail?id=220
- LogMessage(FormatLastError());
- }
- }
-
- PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
-
- return 0;
-}
-
-static void EndCrashReporterDialog(HWND hwndDlg, int code)
-{
- // Save the current values to the registry
- wchar_t email[MAX_EMAIL_LENGTH];
- GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
- sizeof(email) / sizeof(email[0]));
- SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email);
-
- SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE,
- IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0);
- SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE,
- IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0);
- SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE,
- IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
-
- EndDialog(hwndDlg, code);
-}
-
-static void MaybeResizeProgressText(HWND hwndDlg)
-{
- HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT);
- HDC hdc = GetDC(hwndProgress);
- HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0);
- if (hfont)
- SelectObject(hdc, hfont);
- SIZE size;
- RECT rect;
- GetRelativeRect(hwndProgress, hwndDlg, &rect);
-
- wchar_t text[1024];
- GetWindowText(hwndProgress, text, 1024);
-
- if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size))
- return;
-
- if (size.cx < (rect.right - rect.left))
- return;
-
- // Figure out how much we need to resize things vertically
- // This is sort of a fudge, but it should be good enough.
- int wantedHeight = size.cy *
- (int)ceil((float)size.cx / (float)(rect.right - rect.left));
- int diff = wantedHeight - (rect.bottom - rect.top);
- if (diff <= 0)
- return;
-
- MoveWindow(hwndProgress, rect.left, rect.top,
- rect.right - rect.left,
- wantedHeight,
- TRUE);
-
- gAttachedBottom.clear();
- gAttachedBottom.insert(IDC_CLOSEBUTTON);
- gAttachedBottom.insert(IDC_RESTARTBUTTON);
-
- StretchDialog(hwndDlg, diff);
-
- for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
- gAttachedBottom.insert(kDefaultAttachedBottom[i]);
- }
-}
-
-static void MaybeSendReport(HWND hwndDlg)
-{
- if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
- EndCrashReporterDialog(hwndDlg, 0);
- return;
- }
-
- // disable all the form controls
- EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false);
- EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false);
-
- SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str());
- MaybeResizeProgressText(hwndDlg);
- // start throbber
- // play entire AVI, and loop
- Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1);
- SetDlgItemVisible(hwndDlg, IDC_THROBBER, true);
- gThreadHandle = nullptr;
- gSendData.hDlg = hwndDlg;
- gSendData.queryParameters = gQueryParameters;
-
- gThreadHandle = CreateThread(nullptr, 0, SendThreadProc, &gSendData, 0,
- nullptr);
-}
-
-static void RestartApplication()
-{
- wstring cmdLine;
-
- for (unsigned int i = 0; i < gRestartArgs.size(); i++) {
- cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" ";
- }
-
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
-
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_SHOWNORMAL;
- ZeroMemory(&pi, sizeof(pi));
-
- if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
- 0, nullptr, nullptr, &si, &pi)) {
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- }
-}
-
-static void ShowReportInfo(HWND hwndDlg)
-{
- wstring description;
-
- for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin();
- i != gQueryParameters.end();
- i++) {
- description += i->first;
- description += L": ";
- description += i->second;
- description += L"\n";
- }
-
- description += L"\n";
- description += Str(ST_EXTRAREPORTINFO);
-
- SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str());
-}
-
-static void UpdateURL(HWND hwndDlg)
-{
- if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) {
- gQueryParameters[L"URL"] = gURLParameter;
- } else {
- gQueryParameters.erase(L"URL");
- }
-}
-
-static void UpdateEmail(HWND hwndDlg)
-{
- if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) {
- wchar_t email[MAX_EMAIL_LENGTH];
- GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
- sizeof(email) / sizeof(email[0]));
- gQueryParameters[L"Email"] = email;
- if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK))
- EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true);
- } else {
- gQueryParameters.erase(L"Email");
- EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
- }
-}
-
-static void UpdateComment(HWND hwndDlg)
-{
- wchar_t comment[MAX_COMMENT_LENGTH + 1];
- GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment,
- sizeof(comment) / sizeof(comment[0]));
- if (wcslen(comment) > 0)
- gQueryParameters[L"Comments"] = comment;
- else
- gQueryParameters.erase(L"Comments");
-}
-
-/*
- * Dialog procedure for the "view report" dialog.
- */
-static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message,
- WPARAM wParam, LPARAM lParam)
-{
- switch (message) {
- case WM_INITDIALOG: {
- SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str());
- SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
- SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
- EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
- ShowReportInfo(hwndDlg);
- SetFocus(GetDlgItem(hwndDlg, IDOK));
- return FALSE;
- }
-
- case WM_COMMAND: {
- if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
- EndDialog(hwndDlg, 0);
- return FALSE;
- }
- }
- return FALSE;
-}
-
-// Return the number of bytes this string will take encoded
-// in UTF-8
-static inline int BytesInUTF8(wchar_t* str)
-{
- // Just count size of buffer for UTF-8, minus one
- // (we don't need to count the null terminator)
- return WideCharToMultiByte(CP_UTF8, 0, str, -1,
- nullptr, 0, nullptr, nullptr) - 1;
-}
-
-// Calculate the length of the text in this edit control (in bytes,
-// in the UTF-8 encoding) after replacing the current selection
-// with |insert|.
-static int NewTextLength(HWND hwndEdit, wchar_t* insert)
-{
- wchar_t current[MAX_COMMENT_LENGTH + 1];
-
- GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1);
- DWORD selStart, selEnd;
- SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
-
- int selectionLength = 0;
- if (selEnd - selStart > 0) {
- wchar_t selection[MAX_COMMENT_LENGTH + 1];
- google_breakpad::WindowsStringUtils::safe_wcsncpy(selection,
- MAX_COMMENT_LENGTH + 1,
- current + selStart,
- selEnd - selStart);
- selection[selEnd - selStart] = '\0';
- selectionLength = BytesInUTF8(selection);
- }
-
- // current string length + replacement text length
- // - replaced selection length
- return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength;
-}
-
-// Window procedure for subclassing edit controls
-static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
- LPARAM lParam)
-{
- static WNDPROC super = nullptr;
-
- if (super == nullptr)
- super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
-
- switch (uMsg) {
- case WM_PAINT: {
- HDC hdc;
- PAINTSTRUCT ps;
- RECT r;
- wchar_t windowText[1024];
-
- GetWindowText(hwnd, windowText, 1024);
- // if the control contains text or is focused, draw it normally
- if (GetFocus() == hwnd || windowText[0] != '\0')
- return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
-
- GetClientRect(hwnd, &r);
- hdc = BeginPaint(hwnd, &ps);
- FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd)
- ? COLOR_WINDOW : COLOR_BTNFACE));
- SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
- SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT));
- SetBkMode(hdc, TRANSPARENT);
- wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT");
- // Get the actual edit control rect
- CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r);
- UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL;
- if (gRTLlayout)
- format |= DT_RIGHT;
- if (txt)
- DrawText(hdc, txt, wcslen(txt), &r, format);
- EndPaint(hwnd, &ps);
- return 0;
- }
-
- // We handle WM_CHAR and WM_PASTE to limit the comment box to 500
- // bytes in UTF-8.
- case WM_CHAR: {
- // Leave accelerator keys and non-printing chars (except LF) alone
- if (wParam & (1<<24) || wParam & (1<<29) ||
- (wParam < ' ' && wParam != '\n'))
- break;
-
- wchar_t ch[2] = { (wchar_t)wParam, 0 };
- if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH)
- return 0;
-
- break;
- }
-
- case WM_PASTE: {
- if (IsClipboardFormatAvailable(CF_UNICODETEXT) &&
- OpenClipboard(hwnd)) {
- HGLOBAL hg = GetClipboardData(CF_UNICODETEXT);
- wchar_t* pastedText = (wchar_t*)GlobalLock(hg);
- int newSize = 0;
-
- if (pastedText)
- newSize = NewTextLength(hwnd, pastedText);
-
- GlobalUnlock(hg);
- CloseClipboard();
-
- if (newSize > MAX_COMMENT_LENGTH)
- return 0;
- }
- break;
- }
-
- case WM_SETFOCUS:
- case WM_KILLFOCUS: {
- RECT r;
- GetClientRect(hwnd, &r);
- InvalidateRect(hwnd, &r, TRUE);
- break;
- }
-
- case WM_DESTROY: {
- // cleanup our property
- HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT");
- if (hData)
- GlobalFree(hData);
- }
- }
-
- return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
-}
-
-// Resize a control to fit this text
-static int ResizeControl(HWND hwndButton, RECT& rect, wstring text,
- bool shiftLeft, int userDefinedPadding)
-{
- HDC hdc = GetDC(hwndButton);
- HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0);
- if (hfont)
- SelectObject(hdc, hfont);
- SIZE size, oldSize;
- int sizeDiff = 0;
-
- wchar_t oldText[1024];
- GetWindowText(hwndButton, oldText, 1024);
-
- if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size)
- // default text on the button
- && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) {
- /*
- Expand control widths to accomidate wider text strings. For most
- controls (including buttons) the text padding is defined by the
- dialog's rc file. Some controls (such as checkboxes) have padding
- that extends to the end of the dialog, in which case we ignore the
- rc padding and rely on a user defined value passed in through
- userDefinedPadding.
- */
- int textIncrease = size.cx - oldSize.cx;
- if (textIncrease < 0)
- return 0;
- int existingTextPadding;
- if (userDefinedPadding == 0)
- existingTextPadding = (rect.right - rect.left) - oldSize.cx;
- else
- existingTextPadding = userDefinedPadding;
- sizeDiff = textIncrease + existingTextPadding;
-
- if (shiftLeft) {
- // shift left by the amount the button should grow
- rect.left -= sizeDiff;
- }
- else {
- // grow right instead
- rect.right += sizeDiff;
- }
- MoveWindow(hwndButton, rect.left, rect.top,
- rect.right - rect.left,
- rect.bottom - rect.top,
- TRUE);
- }
- return sizeDiff;
-}
-
-// The window was resized horizontally, so widen some of our
-// controls to make use of the space
-static void StretchControlsToFit(HWND hwndDlg)
-{
- int controls[] = {
- IDC_DESCRIPTIONTEXT,
- IDC_SUBMITREPORTCHECK,
- IDC_COMMENTTEXT,
- IDC_INCLUDEURLCHECK,
- IDC_EMAILMECHECK,
- IDC_EMAILTEXT,
- IDC_PROGRESSTEXT
- };
-
- RECT dlgRect;
- GetClientRect(hwndDlg, &dlgRect);
-
- for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) {
- RECT r;
- HWND hwndControl = GetDlgItem(hwndDlg, controls[i]);
- GetRelativeRect(hwndControl, hwndDlg, &r);
- // 6 pixel spacing on the right
- if (r.right + 6 != dlgRect.right) {
- r.right = dlgRect.right - 6;
- MoveWindow(hwndControl, r.left, r.top,
- r.right - r.left,
- r.bottom - r.top,
- TRUE);
- }
- }
-}
-
-static void SubmitReportChecked(HWND hwndDlg)
-{
- bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
- EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
- EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT),
- enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)
- != 0));
- SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled);
-}
-
-static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent,
- DLGPROC dlgProc, LPARAM param)
-{
- INT_PTR rv = 0;
- if (gRTLlayout) {
- // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog
- // template.
- HRSRC hDialogRC = FindResource(nullptr, MAKEINTRESOURCE(idd),
- RT_DIALOG);
- HGLOBAL hDlgTemplate = LoadResource(nullptr, hDialogRC);
- DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate);
- unsigned long sizeDlg = SizeofResource(nullptr, hDialogRC);
- HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg);
- DLGTEMPLATEEX* pMyDlgTemplate =
- (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate);
- memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg);
-
- pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL;
-
- rv = DialogBoxIndirectParam(nullptr, (LPCDLGTEMPLATE)pMyDlgTemplate,
- hwndParent, dlgProc, param);
- GlobalUnlock(hMyDlgTemplate);
- GlobalFree(hMyDlgTemplate);
- }
- else {
- rv = DialogBoxParam(nullptr, MAKEINTRESOURCE(idd), hwndParent,
- dlgProc, param);
- }
-
- return rv;
-}
-
-
-static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
- WPARAM wParam, LPARAM lParam)
-{
- static int sHeight = 0;
-
- bool success;
- bool enabled;
-
- switch (message) {
- case WM_INITDIALOG: {
- GetThemeSizes(hwndDlg);
- RECT r;
- GetClientRect(hwndDlg, &r);
- sHeight = r.bottom - r.top;
-
- SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
- HICON hIcon = LoadIcon(GetModuleHandle(nullptr),
- MAKEINTRESOURCE(IDI_MAINICON));
- SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
- SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
-
- // resize the "View Report" button based on the string length
- RECT rect;
- HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON);
- GetRelativeRect(hwnd, hwndDlg, &rect);
- ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0);
- SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str());
-
- hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK);
- GetRelativeRect(hwnd, hwndDlg, &rect);
- long maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false,
- gCheckboxPadding);
- SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
- Str(ST_CHECKSUBMIT).c_str());
-
- if (!CheckBoolKey(gCrashReporterKey.c_str(),
- SUBMIT_REPORT_VALUE, &enabled))
- enabled = ShouldEnableSending();
-
- CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED
- : BST_UNCHECKED);
- SubmitReportChecked(hwndDlg);
-
- HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT);
- WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment,
- GWLP_WNDPROC,
- (LONG_PTR)EditSubclassProc);
-
- // Subclass comment edit control to get placeholder text
- SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc);
- wstring commentGrayText = Str(ST_COMMENTGRAYTEXT);
- wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t));
- wcscpy(hMem, commentGrayText.c_str());
- SetProp(hwndComment, L"PROP_GRAYTEXT", hMem);
-
- hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK);
- GetRelativeRect(hwnd, hwndDlg, &rect);
- long diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false,
- gCheckboxPadding);
- maxdiff = std::max(diff, maxdiff);
- SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str());
-
- // want this on by default
- if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) &&
- !enabled) {
- CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED);
- } else {
- CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED);
- }
-
- hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK);
- GetRelativeRect(hwnd, hwndDlg, &rect);
- diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false,
- gCheckboxPadding);
- maxdiff = std::max(diff, maxdiff);
- SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str());
-
- if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) &&
- enabled) {
- CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
- } else {
- CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
- }
-
- wstring email;
- if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) {
- SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str());
- }
-
- // Subclass email edit control to get placeholder text
- HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
- OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail,
- GWLP_WNDPROC,
- (LONG_PTR)EditSubclassProc);
- SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc);
- wstring emailGrayText = Str(ST_EMAILGRAYTEXT);
- hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t));
- wcscpy(hMem, emailGrayText.c_str());
- SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem);
-
- SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str());
-
- RECT closeRect;
- HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON);
- GetRelativeRect(hwndClose, hwndDlg, &closeRect);
-
- RECT restartRect;
- HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON);
- GetRelativeRect(hwndRestart, hwndDlg, &restartRect);
-
- // set the close button text and shift the buttons around
- // since the size may need to change
- int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT),
- true, 0);
- restartRect.left -= sizeDiff;
- restartRect.right -= sizeDiff;
- SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str());
-
- if (gRestartArgs.size() > 0) {
- // Resize restart button to fit text
- ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0);
- SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str());
- } else {
- // No restart arguments, so just hide the restart button
- SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
- }
- // See if we need to widen the window
- // Leave 6 pixels on either side + 6 pixels between the buttons
- int neededSize = closeRect.right - closeRect.left +
- restartRect.right - restartRect.left + 6 * 3;
- GetClientRect(hwndDlg, &r);
- // We may already have resized one of the checkboxes above
- maxdiff = std::max(maxdiff, neededSize - (r.right - r.left));
-
- if (maxdiff > 0) {
- // widen window
- GetWindowRect(hwndDlg, &r);
- r.right += maxdiff;
- MoveWindow(hwndDlg, r.left, r.top,
- r.right - r.left, r.bottom - r.top, TRUE);
- // shift both buttons right
- if (restartRect.left + maxdiff < 6)
- maxdiff += 6;
- closeRect.left += maxdiff;
- closeRect.right += maxdiff;
- restartRect.left += maxdiff;
- restartRect.right += maxdiff;
- MoveWindow(hwndClose, closeRect.left, closeRect.top,
- closeRect.right - closeRect.left,
- closeRect.bottom - closeRect.top,
- TRUE);
- StretchControlsToFit(hwndDlg);
- }
- // need to move the restart button regardless
- MoveWindow(hwndRestart, restartRect.left, restartRect.top,
- restartRect.right - restartRect.left,
- restartRect.bottom - restartRect.top,
- TRUE);
-
- // Resize the description text last, in case the window was resized
- // before this.
- SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
- EM_SETEVENTMASK, (WPARAM)nullptr,
- ENM_REQUESTRESIZE);
-
- wstring description = Str(ST_CRASHREPORTERHEADER);
- description += L"\n\n";
- description += Str(ST_CRASHREPORTERDESCRIPTION);
- SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str());
-
-
- // Make the title bold.
- CHARFORMAT fmt = { 0, };
- fmt.cbSize = sizeof(fmt);
- fmt.dwMask = CFM_BOLD;
- fmt.dwEffects = CFE_BOLD;
- SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL,
- 0, Str(ST_CRASHREPORTERHEADER).length());
- SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT,
- SCF_SELECTION, (LPARAM)&fmt);
- SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0);
- // Force redraw.
- SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
- EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
- // Force resize.
- SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
- EM_REQUESTRESIZE, 0, 0);
-
- // if no URL was given, hide the URL checkbox
- if (gQueryParameters.find(L"URL") == gQueryParameters.end()) {
- RECT urlCheckRect, emailCheckRect;
- GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect);
- GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect);
-
- SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false);
-
- gAttachedBottom.erase(IDC_VIEWREPORTBUTTON);
- gAttachedBottom.erase(IDC_SUBMITREPORTCHECK);
- gAttachedBottom.erase(IDC_COMMENTTEXT);
-
- StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top);
-
- gAttachedBottom.insert(IDC_VIEWREPORTBUTTON);
- gAttachedBottom.insert(IDC_SUBMITREPORTCHECK);
- gAttachedBottom.insert(IDC_COMMENTTEXT);
- }
-
- MaybeResizeProgressText(hwndDlg);
-
- // Open the AVI resource for the throbber
- Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER),
- MAKEINTRESOURCE(IDR_THROBBER));
-
- UpdateURL(hwndDlg);
- UpdateEmail(hwndDlg);
-
- SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK));
- return FALSE;
- }
- case WM_SIZE: {
- ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
- sHeight = HIWORD(lParam);
- InvalidateRect(hwndDlg, nullptr, TRUE);
- return FALSE;
- }
- case WM_NOTIFY: {
- NMHDR* notification = reinterpret_cast<NMHDR*>(lParam);
- if (notification->code == EN_REQUESTRESIZE) {
- // Resizing the rich edit control to fit the description text.
- REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam);
- RECT newSize = reqresize->rc;
- RECT oldSize;
- GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize);
-
- // resize the text box as requested
- MoveWindow(notification->hwndFrom, newSize.left, newSize.top,
- newSize.right - newSize.left, newSize.bottom - newSize.top,
- TRUE);
-
- // Resize the dialog to fit (the WM_SIZE handler will move the controls)
- StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom);
- }
- return FALSE;
- }
- case WM_COMMAND: {
- if (HIWORD(wParam) == BN_CLICKED) {
- switch(LOWORD(wParam)) {
- case IDC_VIEWREPORTBUTTON:
- DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg,
- (DLGPROC)ViewReportDialogProc, 0);
- break;
- case IDC_SUBMITREPORTCHECK:
- SubmitReportChecked(hwndDlg);
- break;
- case IDC_INCLUDEURLCHECK:
- UpdateURL(hwndDlg);
- break;
- case IDC_EMAILMECHECK:
- UpdateEmail(hwndDlg);
- break;
- case IDC_CLOSEBUTTON:
- MaybeSendReport(hwndDlg);
- break;
- case IDC_RESTARTBUTTON:
- RestartApplication();
- MaybeSendReport(hwndDlg);
- break;
- }
- } else if (HIWORD(wParam) == EN_CHANGE) {
- switch(LOWORD(wParam)) {
- case IDC_EMAILTEXT:
- UpdateEmail(hwndDlg);
- break;
- case IDC_COMMENTTEXT:
- UpdateComment(hwndDlg);
- }
- }
-
- return FALSE;
- }
- case WM_UPLOADCOMPLETE: {
- WaitForSingleObject(gThreadHandle, INFINITE);
- success = (wParam == 1);
- SendCompleted(success, WideToUTF8(gSendData.serverResponse));
- // hide throbber
- Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER));
- SetDlgItemVisible(hwndDlg, IDC_THROBBER, false);
-
- SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
- success ?
- Str(ST_REPORTSUBMITSUCCESS).c_str() :
- Str(ST_SUBMITFAILED).c_str());
- MaybeResizeProgressText(hwndDlg);
- // close dialog after 5 seconds
- SetTimer(hwndDlg, 0, 5000, nullptr);
- //
- return TRUE;
- }
-
- case WM_LBUTTONDOWN: {
- HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
- POINT p = { LOWORD(lParam), HIWORD(lParam) };
- // if the email edit control is clicked, enable it,
- // check the email checkbox, and focus the email edit control
- if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail &&
- IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) &&
- !IsWindowEnabled(hwndEmail) &&
- IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) {
- CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
- UpdateEmail(hwndDlg);
- SetFocus(hwndEmail);
- }
- break;
- }
-
- case WM_TIMER: {
- // The "1" gets used down in UIShowCrashUI to indicate that we at least
- // tried to send the report.
- EndCrashReporterDialog(hwndDlg, 1);
- return FALSE;
- }
-
- case WM_CLOSE: {
- EndCrashReporterDialog(hwndDlg, 0);
- return FALSE;
- }
- }
- return FALSE;
-}
-
-static wstring UTF8ToWide(const string& utf8, bool *success)
-{
- wchar_t* buffer = nullptr;
- int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
- -1, nullptr, 0);
- if(buffer_size == 0) {
- if (success)
- *success = false;
- return L"";
- }
-
- buffer = new wchar_t[buffer_size];
- if(buffer == nullptr) {
- if (success)
- *success = false;
- return L"";
- }
-
- MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
- -1, buffer, buffer_size);
- wstring str = buffer;
- delete [] buffer;
-
- if (success)
- *success = true;
-
- return str;
-}
-
-static string WideToMBCP(const wstring& wide,
- unsigned int cp,
- bool* success = nullptr)
-{
- char* buffer = nullptr;
- int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(),
- -1, nullptr, 0, nullptr, nullptr);
- if(buffer_size == 0) {
- if (success)
- *success = false;
- return "";
- }
-
- buffer = new char[buffer_size];
- if(buffer == nullptr) {
- if (success)
- *success = false;
- return "";
- }
-
- WideCharToMultiByte(cp, 0, wide.c_str(),
- -1, buffer, buffer_size, nullptr, nullptr);
- string mb = buffer;
- delete [] buffer;
-
- if (success)
- *success = true;
-
- return mb;
-}
-
-string WideToUTF8(const wstring& wide, bool* success)
-{
- return WideToMBCP(wide, CP_UTF8, success);
-}
-
-/* === Crashreporter UI Functions === */
-
-bool UIInit()
-{
- for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
- gAttachedBottom.insert(kDefaultAttachedBottom[i]);
- }
-
- DoInitCommonControls();
-
- return true;
-}
-
-void UIShutdown()
-{
-}
-
-void UIShowDefaultUI()
-{
- MessageBox(nullptr, Str(ST_CRASHREPORTERDEFAULT).c_str(),
- L"Crash Reporter",
- MB_OK | MB_ICONSTOP);
-}
-
-static bool CanUseMainCrashReportServer()
-{
- // Any NT from 6.0 and above is fine.
- if (IsWindowsVersionOrGreater(6, 0, 0)) {
- return true;
- }
-
- // On NT 5 servers, we need Server 2003 SP2.
- if (IsWindowsServer()) {
- return IsWindowsVersionOrGreater(5, 2, 2);
- }
-
- // Otherwise we have an NT 5 client.
- // We need exactly XP SP3 (version 5.1 SP3 but not version 5.2).
- return (IsWindowsVersionOrGreater(5, 1, 3) &&
- !IsWindowsVersionOrGreater(5, 2, 0));
-}
-
-bool UIShowCrashUI(const StringTable& files,
- const StringTable& queryParameters,
- const string& sendURL,
- const vector<string>& restartArgs)
-{
- gSendData.hDlg = nullptr;
- gSendData.sendURL = UTF8ToWide(sendURL);
-
- // Older Windows don't support the crash report server's crypto.
- // This is a hack to use an alternate server.
- if (!CanUseMainCrashReportServer() &&
- gSendData.sendURL.find(SENDURL_ORIGINAL) == 0) {
- gSendData.sendURL.replace(0, ARRAYSIZE(SENDURL_ORIGINAL) - 1,
- SENDURL_XPSP2);
- }
-
- for (StringTable::const_iterator i = files.begin();
- i != files.end();
- i++) {
- gSendData.files[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
- }
-
- for (StringTable::const_iterator i = queryParameters.begin();
- i != queryParameters.end();
- i++) {
- gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
- }
-
- if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
- gCrashReporterKey = L"Software\\";
- if (!gQueryParameters[L"Vendor"].empty()) {
- gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
- }
- gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter";
- }
-
- if (gQueryParameters.find(L"URL") != gQueryParameters.end())
- gURLParameter = gQueryParameters[L"URL"];
-
- gRestartArgs = restartArgs;
-
- if (gStrings.find("isRTL") != gStrings.end() &&
- gStrings["isRTL"] == "yes")
- gRTLlayout = true;
-
- return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, nullptr,
- (DLGPROC)CrashReporterDialogProc, 0);
-}
-
-void UIError_impl(const string& message)
-{
- wstring title = Str(ST_CRASHREPORTERTITLE);
- if (title.empty())
- title = L"Crash Reporter Error";
-
- MessageBox(nullptr, UTF8ToWide(message).c_str(), title.c_str(),
- MB_OK | MB_ICONSTOP);
-}
-
-bool UIGetIniPath(string& path)
-{
- wchar_t fileName[MAX_PATH];
- if (GetModuleFileName(nullptr, fileName, MAX_PATH)) {
- // get crashreporter ini
- wchar_t* s = wcsrchr(fileName, '.');
- if (s) {
- wcscpy(s, L".ini");
- path = WideToUTF8(fileName);
- return true;
- }
- }
-
- return false;
-}
-
-bool UIGetSettingsPath(const string& vendor,
- const string& product,
- string& settings_path)
-{
- wchar_t path[MAX_PATH];
- HRESULT hRes = SHGetFolderPath(nullptr,
- CSIDL_APPDATA,
- nullptr,
- 0,
- path);
- if (FAILED(hRes)) {
- // This provides a fallback for getting the path to APPDATA by querying the
- // registry when the call to SHGetFolderPath is unable to provide this path
- // (Bug 513958).
- HKEY key;
- DWORD type, size, dwRes;
- dwRes = ::RegOpenKeyExW(HKEY_CURRENT_USER,
- L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
- 0,
- KEY_READ,
- &key);
- if (dwRes != ERROR_SUCCESS)
- return false;
-
- dwRes = RegQueryValueExW(key,
- L"AppData",
- nullptr,
- &type,
- (LPBYTE)&path,
- &size);
- ::RegCloseKey(key);
- // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
- // buffer size must not equal 0, and the buffer size be a multiple of 2.
- if (dwRes != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0)
- return false;
- }
-
- if (!vendor.empty()) {
- PathAppend(path, UTF8ToWide(vendor).c_str());
- }
- PathAppend(path, UTF8ToWide(product).c_str());
- PathAppend(path, L"Crash Reports");
- settings_path = WideToUTF8(path);
- return true;
-}
-
-bool UIEnsurePathExists(const string& path)
-{
- if (CreateDirectory(UTF8ToWide(path).c_str(), nullptr) == 0) {
- if (GetLastError() != ERROR_ALREADY_EXISTS)
- return false;
- }
-
- return true;
-}
-
-bool UIFileExists(const string& path)
-{
- DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str());
- return (attrs != INVALID_FILE_ATTRIBUTES);
-}
-
-bool UIMoveFile(const string& oldfile, const string& newfile)
-{
- if (oldfile == newfile)
- return true;
-
- return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
- == TRUE;
-}
-
-bool UIDeleteFile(const string& oldfile)
-{
- return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE;
-}
-
-ifstream* UIOpenRead(const string& filename)
-{
- // adapted from breakpad's src/common/windows/http_upload.cc
-
-#if defined(_MSC_VER)
- ifstream* file = new ifstream();
- file->open(UTF8ToWide(filename).c_str(), ios::in);
-#else // GCC
- ifstream* file = new ifstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
- ios::in);
-#endif // _MSC_VER
-
- return file;
-}
-
-ofstream* UIOpenWrite(const string& filename,
- bool append, // append=false
- bool binary) // binary=false
-{
- // adapted from breakpad's src/common/windows/http_upload.cc
- std::ios_base::openmode mode = ios::out;
- if (append) {
- mode = mode | ios::app;
- }
- if (binary) {
- mode = mode | ios::binary;
- }
-
-#if defined(_MSC_VER)
- ofstream* file = new ofstream();
- file->open(UTF8ToWide(filename).c_str(), mode);
-#else // GCC
- ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
- mode);
-#endif // _MSC_VER
-
- return file;
-}
-
-struct FileData
-{
- FILETIME timestamp;
- wstring path;
-};
-
-static bool CompareFDTime(const FileData& fd1, const FileData& fd2)
-{
- return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0;
-}
-
-void UIPruneSavedDumps(const std::string& directory)
-{
- wstring wdirectory = UTF8ToWide(directory);
-
- WIN32_FIND_DATA fdata;
- wstring findpath = wdirectory + L"\\*.dmp";
- HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata);
- if (dirlist == INVALID_HANDLE_VALUE)
- return;
-
- vector<FileData> dumpfiles;
-
- for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) {
- FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName};
- dumpfiles.push_back(fd);
- }
-
- sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime);
-
- while (dumpfiles.size() > kSaveCount) {
- // get the path of the oldest file
- wstring path = (--dumpfiles.end())->path;
- DeleteFile(path.c_str());
-
- // s/.dmp/.extra/
- path.replace(path.size() - 4, 4, L".extra");
- DeleteFile(path.c_str());
-
- dumpfiles.pop_back();
- }
-}
-
-void UIRunMinidumpAnalyzer(const string& exename, const string& filename)
-{
- wstring cmdLine;
-
- cmdLine += L"\"" + UTF8ToWide(exename) + L"\" ";
- cmdLine += L"\"" + UTF8ToWide(filename) + L"\" ";
-
- STARTUPINFO si = {};
- PROCESS_INFORMATION pi = {};
-
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_SHOWNORMAL;
-
- if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
- 0, nullptr, nullptr, &si, &pi)) {
- WaitForSingleObject(pi.hProcess, INFINITE);
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- }
-}