mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
959 lines
28 KiB
C++
959 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla Toolkit Crash Reporter
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Ted Mielczarek <ted.mielczarek@gmail.com>
|
|
* Portions created by the Initial Developer are Copyright (C) 2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Ted Mielczarek <ted.mielczarek@gmail.com>
|
|
* Dave Camp <dcamp@mozilla.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifdef WIN32_LEAN_AND_MEAN
|
|
#undef WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#include "crashreporter.h"
|
|
|
|
#include <windows.h>
|
|
#include <commctrl.h>
|
|
#include <richedit.h>
|
|
#include <shellapi.h>
|
|
#include <shlobj.h>
|
|
#include <shlwapi.h>
|
|
#include <set>
|
|
#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"SubmitReport"
|
|
#define INCLUDE_URL_VALUE L"IncludeURL"
|
|
#define EMAIL_ME_VALUE L"EmailMe"
|
|
#define EMAIL_VALUE L"Email"
|
|
#define MAX_EMAIL_LENGTH 1024
|
|
|
|
#define WM_UPLOADCOMPLETE WM_APP
|
|
|
|
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;
|
|
wstring dumpFile;
|
|
map<wstring,wstring> queryParameters;
|
|
wstring sendURL;
|
|
|
|
wstring serverResponse;
|
|
} SendThreadData;
|
|
|
|
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;
|
|
|
|
// 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_VIEWREPORTCHECK,
|
|
IDC_VIEWREPORTTEXT,
|
|
IDC_SUBMITCRASHCHECK,
|
|
IDC_INCLUDEURLCHECK,
|
|
IDC_EMAILMECHECK,
|
|
IDC_EMAILTEXT,
|
|
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"riched20.dll");
|
|
}
|
|
|
|
static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
|
|
{
|
|
DWORD type, dataSize;
|
|
dataSize = sizeof(DWORD);
|
|
if (RegQueryValueEx(hRegKey, valueName, NULL, &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS
|
|
&& type == REG_DWORD)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool CheckBoolKey(const wchar_t* key,
|
|
const wchar_t* valueName,
|
|
bool* enabled)
|
|
{
|
|
*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)
|
|
{
|
|
HKEY hRegKey;
|
|
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, NULL, &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,
|
|
NULL) != 0) {
|
|
message += WideToUTF8(s, NULL);
|
|
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;
|
|
}
|
|
|
|
// 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);
|
|
ScreenToClient(hwndParent, (POINT*)&(r->left));
|
|
ScreenToClient(hwndParent, (POINT*)&(r->right));
|
|
}
|
|
|
|
static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
|
|
{
|
|
HWND hwnd = GetDlgItem(hwndDlg, item);
|
|
LONG style = GetWindowLong(hwnd, GWL_STYLE);
|
|
if (visible)
|
|
style |= WS_VISIBLE;
|
|
else
|
|
style &= ~WS_VISIBLE;
|
|
|
|
SetWindowLong(hwnd, GWL_STYLE, style);
|
|
}
|
|
|
|
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->dumpFile,
|
|
&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];
|
|
GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email));
|
|
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 MaybeSendReport(HWND hwndDlg)
|
|
{
|
|
if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
|
|
EndCrashReporterDialog(hwndDlg, 0);
|
|
return;
|
|
}
|
|
|
|
gThreadHandle = NULL;
|
|
gSendData.hDlg = hwndDlg;
|
|
gSendData.queryParameters = gQueryParameters;
|
|
|
|
gThreadHandle = CreateThread(NULL, 0, SendThreadProc, &gSendData, 0, NULL);
|
|
}
|
|
|
|
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(NULL, (LPWSTR)cmdLine.c_str(), NULL, NULL, FALSE, 0,
|
|
NULL, NULL, &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 ShowHideReport(HWND hwndDlg)
|
|
{
|
|
// When resizing the dialog to show the report, these items should
|
|
// stay put
|
|
gAttachedBottom.erase(IDC_VIEWREPORTCHECK);
|
|
gAttachedBottom.erase(IDC_VIEWREPORTTEXT);
|
|
|
|
RECT r;
|
|
HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTTEXT);
|
|
|
|
GetWindowRect(hwnd, &r);
|
|
int diff = (r.bottom - r.top) + 10;
|
|
if (IsDlgButtonChecked(hwndDlg, IDC_VIEWREPORTCHECK)) {
|
|
SetDlgItemVisible(hwndDlg, IDC_VIEWREPORTTEXT, true);
|
|
} else {
|
|
SetDlgItemVisible(hwndDlg, IDC_VIEWREPORTTEXT, false);
|
|
diff = -diff;
|
|
}
|
|
|
|
StretchDialog(hwndDlg, diff);
|
|
|
|
// set these back to normal
|
|
gAttachedBottom.insert(IDC_VIEWREPORTCHECK);
|
|
gAttachedBottom.insert(IDC_VIEWREPORTTEXT);
|
|
}
|
|
|
|
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];
|
|
GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email));
|
|
gQueryParameters[L"Email"] = email;
|
|
} else {
|
|
gQueryParameters.erase(L"Email");
|
|
}
|
|
}
|
|
|
|
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: {
|
|
RECT r;
|
|
GetClientRect(hwndDlg, &r);
|
|
sHeight = r.bottom - r.top;
|
|
|
|
SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
|
|
|
|
SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
|
|
EM_SETEVENTMASK, (WPARAM)NULL,
|
|
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);
|
|
SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
|
|
EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
|
|
|
|
// resize the "View Report" button based on the string length
|
|
RECT viewRect;
|
|
HWND hwndView = GetDlgItem(hwndDlg, IDC_VIEWREPORTCHECK);
|
|
GetRelativeRect(hwndView, hwndDlg, &viewRect);
|
|
HDC hdc = GetDC(hwndView);
|
|
const wstring viewButtonText = Str(ST_VIEWREPORT);
|
|
SIZE size;
|
|
if (GetTextExtentPoint32(hdc, viewButtonText.c_str(),
|
|
viewButtonText.length(), &size)) {
|
|
// shift right by the amount the button should grow
|
|
int sizeDiff = size.cx - (viewRect.right - viewRect.left);
|
|
viewRect.right += sizeDiff;
|
|
MoveWindow(hwndView, viewRect.left, viewRect.top,
|
|
viewRect.right - viewRect.left,
|
|
viewRect.bottom - viewRect.top,
|
|
TRUE);
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_VIEWREPORTCHECK, viewButtonText.c_str());
|
|
SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
|
|
EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
|
|
|
|
|
|
SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
|
|
Str(ST_CHECKSUBMIT).c_str());
|
|
if (CheckBoolKey(gCrashReporterKey.c_str(),
|
|
SUBMIT_REPORT_VALUE, &enabled) &&
|
|
!enabled) {
|
|
CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, BST_UNCHECKED);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), enabled);
|
|
} else {
|
|
CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, BST_CHECKED);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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());
|
|
}
|
|
|
|
SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_CLOSE).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);
|
|
|
|
if (gRestartArgs.size() > 0) {
|
|
// set the restart button text and shift the buttons around
|
|
// since the size may need to change
|
|
hdc = GetDC(hwndRestart);
|
|
const wstring restartButtonText = Str(ST_RESTART);
|
|
if (GetTextExtentPoint32(hdc, restartButtonText.c_str(),
|
|
restartButtonText.length(), &size)) {
|
|
// shift left by the amount the button should grow
|
|
int sizeDiff = size.cx - (restartRect.right - restartRect.left);
|
|
restartRect.left -= sizeDiff;
|
|
closeRect.left -= sizeDiff;
|
|
closeRect.right -= sizeDiff;
|
|
MoveWindow(hwndRestart, restartRect.left, restartRect.top,
|
|
restartRect.right - restartRect.left,
|
|
restartRect.bottom - restartRect.top,
|
|
TRUE);
|
|
}
|
|
SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, restartButtonText.c_str());
|
|
} else {
|
|
// No restart arguments, move the close button over to the side
|
|
// and hide the restart button
|
|
SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
|
|
|
|
int size = closeRect.right - closeRect.left;
|
|
closeRect.right = restartRect.right;
|
|
closeRect.left = closeRect.right - size;
|
|
}
|
|
// need to move the close button regardless
|
|
MoveWindow(hwndClose, closeRect.left, closeRect.top,
|
|
closeRect.right - closeRect.left,
|
|
closeRect.bottom - closeRect.top,
|
|
TRUE);
|
|
|
|
// 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_VIEWREPORTCHECK);
|
|
gAttachedBottom.erase(IDC_VIEWREPORTTEXT);
|
|
gAttachedBottom.erase(IDC_SUBMITREPORTCHECK);
|
|
|
|
StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top);
|
|
|
|
gAttachedBottom.insert(IDC_VIEWREPORTCHECK);
|
|
gAttachedBottom.insert(IDC_VIEWREPORTTEXT);
|
|
gAttachedBottom.insert(IDC_SUBMITREPORTCHECK);
|
|
}
|
|
|
|
UpdateURL(hwndDlg);
|
|
UpdateEmail(hwndDlg);
|
|
ShowReportInfo(hwndDlg);
|
|
|
|
SetFocus(GetDlgItem(hwndDlg, IDC_EMAILTEXT));
|
|
return FALSE;
|
|
}
|
|
case WM_SIZE: {
|
|
ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
|
|
sHeight = HIWORD(lParam);
|
|
InvalidateRect(hwndDlg, NULL, 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_VIEWREPORTCHECK:
|
|
ShowHideReport(hwndDlg);
|
|
break;
|
|
case IDC_SUBMITREPORTCHECK:
|
|
enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
|
|
EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), enabled);
|
|
break;
|
|
case IDC_INCLUDEURLCHECK:
|
|
UpdateURL(hwndDlg);
|
|
ShowReportInfo(hwndDlg);
|
|
break;
|
|
case IDC_EMAILMECHECK:
|
|
UpdateEmail(hwndDlg);
|
|
ShowReportInfo(hwndDlg);
|
|
break;
|
|
case IDC_CLOSEBUTTON:
|
|
// Hide the dialog after "closing", but leave it around to coordinate
|
|
// with the upload thread
|
|
ShowWindow(hwndDlg, SW_HIDE);
|
|
MaybeSendReport(hwndDlg);
|
|
break;
|
|
case IDC_RESTARTBUTTON:
|
|
// Hide the dialog after "closing", but leave it around to coordinate
|
|
// with the upload thread
|
|
ShowWindow(hwndDlg, SW_HIDE);
|
|
RestartApplication();
|
|
MaybeSendReport(hwndDlg);
|
|
break;
|
|
}
|
|
} else if (HIWORD(wParam) == EN_CHANGE) {
|
|
switch(LOWORD(wParam)) {
|
|
case IDC_EMAILTEXT:
|
|
wchar_t email[MAX_EMAIL_LENGTH];
|
|
if (GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email)) > 0)
|
|
CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
|
|
else
|
|
CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
|
|
UpdateEmail(hwndDlg);
|
|
ShowReportInfo(hwndDlg);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
case WM_UPLOADCOMPLETE: {
|
|
WaitForSingleObject(gThreadHandle, INFINITE);
|
|
success = (wParam == 1);
|
|
SendCompleted(success, WideToUTF8(gSendData.serverResponse));
|
|
if (!success) {
|
|
MessageBox(hwndDlg,
|
|
Str(ST_SUBMITFAILED).c_str(),
|
|
Str(ST_CRASHREPORTERTITLE).c_str(),
|
|
MB_OK | MB_ICONERROR);
|
|
}
|
|
EndCrashReporterDialog(hwndDlg, success ? 1 : 0);
|
|
return TRUE;
|
|
}
|
|
case WM_CLOSE: {
|
|
EndCrashReporterDialog(hwndDlg, 0);
|
|
return FALSE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static wstring UTF8ToWide(const string& utf8, bool *success)
|
|
{
|
|
wchar_t* buffer = NULL;
|
|
int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
|
|
-1, NULL, 0);
|
|
if(buffer_size == 0) {
|
|
if (success)
|
|
*success = false;
|
|
return L"";
|
|
}
|
|
|
|
buffer = new wchar_t[buffer_size];
|
|
if(buffer == NULL) {
|
|
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;
|
|
}
|
|
|
|
string WideToUTF8(const wstring& wide, bool* success)
|
|
{
|
|
char* buffer = NULL;
|
|
int buffer_size = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(),
|
|
-1, NULL, 0, NULL, NULL);
|
|
if(buffer_size == 0) {
|
|
if (success)
|
|
*success = false;
|
|
return "";
|
|
}
|
|
|
|
buffer = new char[buffer_size];
|
|
if(buffer == NULL) {
|
|
if (success)
|
|
*success = false;
|
|
return "";
|
|
}
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, wide.c_str(),
|
|
-1, buffer, buffer_size, NULL, NULL);
|
|
string utf8 = buffer;
|
|
delete [] buffer;
|
|
|
|
if (success)
|
|
*success = true;
|
|
|
|
return utf8;
|
|
}
|
|
|
|
/* === 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(NULL, Str(ST_CRASHREPORTERDEFAULT).c_str(),
|
|
L"Crash Reporter",
|
|
MB_OK | MB_ICONSTOP);
|
|
}
|
|
|
|
void UIShowCrashUI(const string& dumpFile,
|
|
const StringTable& queryParameters,
|
|
const string& sendURL,
|
|
const vector<string>& restartArgs)
|
|
{
|
|
gSendData.hDlg = NULL;
|
|
gSendData.dumpFile = UTF8ToWide(dumpFile);
|
|
gSendData.sendURL = UTF8ToWide(sendURL);
|
|
|
|
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;
|
|
|
|
DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_SENDDIALOG), NULL,
|
|
(DLGPROC)CrashReporterDialogProc, 0);
|
|
}
|
|
|
|
void UIError_impl(const string& message)
|
|
{
|
|
wstring title = Str(ST_CRASHREPORTERTITLE);
|
|
if (title.empty())
|
|
title = L"Crash Reporter Error";
|
|
|
|
MessageBox(NULL, UTF8ToWide(message).c_str(), title.c_str(),
|
|
MB_OK | MB_ICONSTOP);
|
|
}
|
|
|
|
bool UIGetIniPath(string& path)
|
|
{
|
|
wchar_t fileName[MAX_PATH];
|
|
if (GetModuleFileName(NULL, 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];
|
|
if(SUCCEEDED(SHGetFolderPath(NULL,
|
|
CSIDL_APPDATA,
|
|
NULL,
|
|
0,
|
|
path))) {
|
|
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;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool UIEnsurePathExists(const string& path)
|
|
{
|
|
if (CreateDirectory(UTF8ToWide(path).c_str(), NULL) == 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
|
|
|
|
// The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
|
|
// wchar_t* filename, so use _wfopen directly in that case. For VC8 and
|
|
// later, _wfopen has been deprecated in favor of _wfopen_s, which does
|
|
// not exist in earlier versions, so let the ifstream open the file itself.
|
|
#if _MSC_VER >= 1400 // MSVC 2005/8
|
|
ifstream* file = new ifstream();
|
|
file->open(UTF8ToWide(filename).c_str(), ios::in);
|
|
#else // _MSC_VER >= 1400
|
|
ifstream* file = new ifstream(_wfopen(UTF8ToWide(filename).c_str(), L"r"));
|
|
#endif // _MSC_VER >= 1400
|
|
|
|
return file;
|
|
}
|
|
|
|
ofstream* UIOpenWrite(const string& filename, bool append) // append=false
|
|
{
|
|
// adapted from breakpad's src/common/windows/http_upload.cc
|
|
|
|
// The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
|
|
// wchar_t* filename, so use _wfopen directly in that case. For VC8 and
|
|
// later, _wfopen has been deprecated in favor of _wfopen_s, which does
|
|
// not exist in earlier versions, so let the ifstream open the file itself.
|
|
#if _MSC_VER >= 1400 // MSVC 2005/8
|
|
ofstream* file = new ofstream();
|
|
file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app
|
|
: ios::out);
|
|
#else // _MSC_VER >= 1400
|
|
ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(),
|
|
append ? L"a" : L"w"));
|
|
#endif // _MSC_VER >= 1400
|
|
|
|
return file;
|
|
}
|