Bug 1759175 pt8 - Remove the old crash reporter code r=gsvelto

Differential Revision: https://phabricator.services.mozilla.com/D199638
This commit is contained in:
Alex Franchuk 2024-03-20 14:59:46 +00:00
Родитель 368cc2b5a4
Коммит 5bfaf67d7f
94 изменённых файлов: 22 добавлений и 5184 удалений

Просмотреть файл

@ -14,7 +14,7 @@ members = [
"security/manager/ssl/osclientcerts",
"testing/geckodriver",
"toolkit/components/uniffi-bindgen-gecko-js",
"toolkit/crashreporter/client-rust/app",
"toolkit/crashreporter/client/app",
"toolkit/crashreporter/mozwer-rust",
"toolkit/crashreporter/rust_minidump_writer_linux",
"toolkit/library/gtest/rust",

Просмотреть файл

@ -1,19 +0,0 @@
# vim:set ts=8 sw=8 sts=8 noet:
# 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/.
ifeq ($(OS_ARCH),WINNT)
MOZ_WINCONSOLE = 0
endif
include $(topsrcdir)/config/rules.mk
ifeq ($(OS_ARCH),Darwin)
libs::
$(NSINSTALL) -D $(DIST)/bin/crashreporter.app
rsync -a -C --exclude '*.in' $(srcdir)/macbuild/Contents $(DIST)/bin/crashreporter.app
$(call py_action,preprocessor crashreporter.app/Contents/Resources/English.lproj/InfoPlist.strings,-Fsubstitution --output-encoding utf-16 -DAPP_NAME='$(MOZ_APP_DISPLAYNAME)' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in -o $(DIST)/bin/crashreporter.app/Contents/Resources/English.lproj/InfoPlist.strings)
$(NSINSTALL) -D $(DIST)/bin/crashreporter.app/Contents/MacOS
$(NSINSTALL) $(DIST)/bin/crashreporter $(DIST)/bin/crashreporter.app/Contents/MacOS
endif

Двоичные данные
toolkit/crashreporter/client/Throbber-small.avi

Двоичный файл не отображается.

Двоичные данные
toolkit/crashreporter/client/Throbber-small.gif

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 825 B

Просмотреть файл

@ -1,36 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
/* 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/. */
//! The embedded Info.plist file.
const DATA: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDisplayName</key>
<string>crashreporter</string>
<string>Crash Reporter</string>
<key>CFBundleExecutable</key>
<string>crashreporter</string>
<key>CFBundleIconFile</key>
<string>crashreporter.icns</string>
<key>CFBundleIdentifier</key>
<string>org.mozilla.crashreporter</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>crashreporter</string>
<string>Crash Reporter</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSHasLocalizedDisplayName</key>
<true/>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSUIElement</key>
<true/>
</dict>
</plist>
</plist>"#;
const N: usize = DATA.len();
const PTR: *const [u8; N] = DATA.as_ptr() as *const [u8; N];
#[used]
#[link_section = "__TEXT,__info_plist"]
// # Safety
// The array pointer is created from `DATA` (a slice pointer) with `DATA.len()` as the length.
static PLIST: [u8; N] = unsafe { *PTR };

Просмотреть файл

@ -1,852 +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/. */
#include "crashreporter.h"
#ifdef _MSC_VER
// Disable exception handler warnings.
# pragma warning(disable : 4530)
#endif
#include <fstream>
#include <iomanip>
#include <sstream>
#include <memory>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <string>
#include <utility>
#ifdef XP_LINUX
# include <dlfcn.h>
#endif
#include "json/json.h"
#include "nss.h"
#include "sechash.h"
using std::ifstream;
using std::ios;
using std::istream;
using std::istringstream;
using std::ofstream;
using std::ostream;
using std::ostringstream;
using std::string;
using std::unique_ptr;
using std::vector;
namespace CrashReporter {
StringTable gStrings;
Json::Value gData;
string gSettingsPath;
string gEventsPath;
string gPingPath;
int gArgc;
char** gArgv;
bool gAutoSubmit;
enum SubmissionResult { Succeeded, Failed };
static unique_ptr<ofstream> gLogStream(nullptr);
static string gReporterDumpFile;
static string gExtraFile;
static string gMemoryFile;
static const char kExtraDataExtension[] = ".extra";
static const char kMemoryReportExtension[] = ".memory.json.gz";
void UIError(const string& message) {
if (gAutoSubmit) {
return;
}
string errorMessage;
if (!gStrings[ST_CRASHREPORTERERROR].empty()) {
char buf[2048];
snprintf(buf, 2048, gStrings[ST_CRASHREPORTERERROR].c_str(),
message.c_str());
errorMessage = buf;
} else {
errorMessage = message;
}
UIError_impl(errorMessage);
}
static string Unescape(const string& str) {
string ret;
for (string::const_iterator iter = str.begin(); iter != str.end(); iter++) {
if (*iter == '\\') {
iter++;
if (*iter == '\\') {
ret.push_back('\\');
} else if (*iter == 'n') {
ret.push_back('\n');
} else if (*iter == 't') {
ret.push_back('\t');
}
} else {
ret.push_back(*iter);
}
}
return ret;
}
bool ReadStrings(istream& in, StringTable& strings, bool unescape) {
while (!in.eof()) {
string line;
std::getline(in, line);
int sep = line.find('=');
if (sep >= 0) {
string key, value;
key = line.substr(0, sep);
value = line.substr(sep + 1);
if (unescape) value = Unescape(value);
strings[key] = value;
}
}
return true;
}
bool ReadStringsFromFile(const string& path, StringTable& strings,
bool unescape) {
ifstream* f = UIOpenRead(path, ios::in);
bool success = false;
if (f->is_open()) {
success = ReadStrings(*f, strings, unescape);
f->close();
}
delete f;
return success;
}
static bool ReadExtraFile(const string& aExtraDataPath, Json::Value& aExtra) {
bool success = false;
ifstream* f = UIOpenRead(aExtraDataPath, ios::in);
if (f->is_open()) {
Json::CharReaderBuilder builder;
success = parseFromStream(builder, *f, &aExtra, nullptr);
}
delete f;
return success;
}
static string Basename(const string& file) {
string::size_type slashIndex = file.rfind(UI_DIR_SEPARATOR);
if (slashIndex != string::npos) {
return file.substr(slashIndex + 1);
}
return file;
}
static bool ReadEventFile(const string& aPath, string& aEventVersion,
string& aTime, string& aUuid, Json::Value& aData) {
bool res = false;
ifstream* f = UIOpenRead(aPath, ios::binary);
if (f->is_open()) {
std::getline(*f, aEventVersion, '\n');
res = f->good();
std::getline(*f, aTime, '\n');
res &= f->good();
std::getline(*f, aUuid, '\n');
res &= f->good();
if (res) {
Json::CharReaderBuilder builder;
res = parseFromStream(builder, *f, &aData, nullptr);
}
}
delete f;
return res;
}
static void OverwriteEventFile(const string& aPath, const string& aEventVersion,
const string& aTime, const string& aUuid,
const Json::Value& aData) {
ofstream* f = UIOpenWrite(aPath, ios::binary | ios::trunc);
if (f->is_open()) {
f->write(aEventVersion.c_str(), aEventVersion.length()) << '\n';
f->write(aTime.c_str(), aTime.length()) << '\n';
f->write(aUuid.c_str(), aUuid.length()) << '\n';
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
writer->write(aData, f);
*f << "\n";
}
delete f;
}
static void UpdateEventFile(const Json::Value& aExtraData, const string& aHash,
const string& aPingUuid) {
if (gEventsPath.empty()) {
// If there is no path for finding the crash event, skip this step.
return;
}
string localId = CrashReporter::GetDumpLocalID();
string path = gEventsPath + UI_DIR_SEPARATOR + localId;
string eventVersion;
string crashTime;
string crashUuid;
Json::Value eventData;
if (!ReadEventFile(path, eventVersion, crashTime, crashUuid, eventData)) {
return;
}
if (!aHash.empty()) {
eventData["MinidumpSha256Hash"] = aHash;
}
if (!aPingUuid.empty()) {
eventData["CrashPingUUID"] = aPingUuid;
}
if (aExtraData.isMember("StackTraces")) {
eventData["StackTraces"] = aExtraData["StackTraces"];
}
OverwriteEventFile(path, eventVersion, crashTime, crashUuid, eventData);
}
static void WriteSubmissionEvent(SubmissionResult result,
const string& remoteId) {
if (gEventsPath.empty()) {
// If there is no path for writing the submission event, skip it.
return;
}
string localId = CrashReporter::GetDumpLocalID();
string fpath = gEventsPath + UI_DIR_SEPARATOR + localId + "-submission";
ofstream* f = UIOpenWrite(fpath, ios::binary);
time_t tm;
time(&tm);
if (f->is_open()) {
*f << "crash.submission.1\n";
*f << tm << "\n";
*f << localId << "\n";
*f << (result == Succeeded ? "true" : "false") << "\n";
*f << remoteId;
f->close();
}
delete f;
}
void LogMessage(const std::string& message) {
if (gLogStream.get()) {
char date[64];
time_t tm;
time(&tm);
if (strftime(date, sizeof(date) - 1, "%c", localtime(&tm)) == 0)
date[0] = '\0';
(*gLogStream) << "[" << date << "] " << message << '\n';
}
}
static void OpenLogFile() {
string logPath = gSettingsPath + UI_DIR_SEPARATOR + "submit.log";
gLogStream.reset(UIOpenWrite(logPath, ios::app));
}
static bool ReadConfig() {
string iniPath;
if (!UIGetIniPath(iniPath)) {
return false;
}
if (!ReadStringsFromFile(iniPath, gStrings, true)) return false;
// See if we have a string override file, if so process it
char* overrideEnv = getenv("MOZ_CRASHREPORTER_STRINGS_OVERRIDE");
if (overrideEnv && *overrideEnv && UIFileExists(overrideEnv))
ReadStringsFromFile(overrideEnv, gStrings, true);
return true;
}
static string GetAdditionalFilename(const string& dumpfile,
const char* extension) {
string filename(dumpfile);
int dot = filename.rfind('.');
if (dot < 0) return "";
filename.replace(dot, filename.length() - dot, extension);
return filename;
}
static bool MoveCrashData(const string& toDir, string& dumpfile,
string& extrafile, string& memoryfile) {
if (!UIEnsurePathExists(toDir)) {
UIError(gStrings[ST_ERROR_CREATEDUMPDIR]);
return false;
}
string newDump = toDir + UI_DIR_SEPARATOR + Basename(dumpfile);
string newExtra = toDir + UI_DIR_SEPARATOR + Basename(extrafile);
string newMemory = toDir + UI_DIR_SEPARATOR + Basename(memoryfile);
if (!UIMoveFile(dumpfile, newDump)) {
UIError(gStrings[ST_ERROR_DUMPFILEMOVE]);
return false;
}
if (!UIMoveFile(extrafile, newExtra)) {
UIError(gStrings[ST_ERROR_EXTRAFILEMOVE]);
return false;
}
if (!memoryfile.empty()) {
// Ignore errors from moving the memory file
if (!UIMoveFile(memoryfile, newMemory)) {
UIDeleteFile(memoryfile);
newMemory.erase();
}
memoryfile = newMemory;
}
dumpfile = newDump;
extrafile = newExtra;
return true;
}
static bool AddSubmittedReport(const string& serverResponse) {
StringTable responseItems;
istringstream in(serverResponse);
ReadStrings(in, responseItems, false);
if (responseItems.find("StopSendingReportsFor") != responseItems.end()) {
// server wants to tell us to stop sending reports for a certain version
string reportPath = gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" +
responseItems["StopSendingReportsFor"];
ofstream* reportFile = UIOpenWrite(reportPath, ios::trunc);
if (reportFile->is_open()) {
// don't really care about the contents
*reportFile << 1 << "\n";
reportFile->close();
}
delete reportFile;
}
if (responseItems.find("Discarded") != responseItems.end()) {
// server discarded this report... save it so the user can resubmit it
// manually
return false;
}
if (responseItems.find("CrashID") == responseItems.end()) return false;
string submittedDir = gSettingsPath + UI_DIR_SEPARATOR + "submitted";
if (!UIEnsurePathExists(submittedDir)) {
return false;
}
string path =
submittedDir + UI_DIR_SEPARATOR + responseItems["CrashID"] + ".txt";
ofstream* file = UIOpenWrite(path, ios::trunc);
if (!file->is_open()) {
delete file;
return false;
}
char buf[1024];
snprintf(buf, 1024, gStrings["CrashID"].c_str(),
responseItems["CrashID"].c_str());
*file << buf << "\n";
if (responseItems.find("ViewURL") != responseItems.end()) {
snprintf(buf, 1024, gStrings["CrashDetailsURL"].c_str(),
responseItems["ViewURL"].c_str());
*file << buf << "\n";
}
file->close();
delete file;
WriteSubmissionEvent(Succeeded, responseItems["CrashID"]);
return true;
}
void DeleteDump() {
const char* noDelete = getenv("MOZ_CRASHREPORTER_NO_DELETE_DUMP");
if (!noDelete || *noDelete == '\0') {
if (!gReporterDumpFile.empty()) UIDeleteFile(gReporterDumpFile);
if (!gExtraFile.empty()) UIDeleteFile(gExtraFile);
if (!gMemoryFile.empty()) UIDeleteFile(gMemoryFile);
}
}
void SendCompleted(bool success, const string& serverResponse) {
if (success) {
if (AddSubmittedReport(serverResponse)) {
DeleteDump();
} else {
string directory = gReporterDumpFile;
int slashpos = directory.find_last_of("/\\");
if (slashpos < 2) return;
directory.resize(slashpos);
UIPruneSavedDumps(directory);
WriteSubmissionEvent(Failed, "");
}
} else {
WriteSubmissionEvent(Failed, "");
}
}
static string ComputeDumpHash() {
#ifdef XP_LINUX
// On Linux we rely on the system-provided libcurl which uses nss so we have
// to also use the system-provided nss instead of the ones we have bundled.
const char* libnssNames[] = {
"libnss3.so",
# ifndef HAVE_64BIT_BUILD
// 32-bit versions on 64-bit hosts
"/usr/lib32/libnss3.so",
# endif
};
void* lib = nullptr;
for (const char* libname : libnssNames) {
lib = dlopen(libname, RTLD_NOW);
if (lib) {
break;
}
}
if (!lib) {
return "";
}
SECStatus (*NSS_Initialize)(const char*, const char*, const char*,
const char*, PRUint32);
HASHContext* (*HASH_Create)(HASH_HashType);
void (*HASH_Destroy)(HASHContext*);
void (*HASH_Begin)(HASHContext*);
void (*HASH_Update)(HASHContext*, const unsigned char*, unsigned int);
void (*HASH_End)(HASHContext*, unsigned char*, unsigned int*, unsigned int);
*(void**)(&NSS_Initialize) = dlsym(lib, "NSS_Initialize");
*(void**)(&HASH_Create) = dlsym(lib, "HASH_Create");
*(void**)(&HASH_Destroy) = dlsym(lib, "HASH_Destroy");
*(void**)(&HASH_Begin) = dlsym(lib, "HASH_Begin");
*(void**)(&HASH_Update) = dlsym(lib, "HASH_Update");
*(void**)(&HASH_End) = dlsym(lib, "HASH_End");
if (!HASH_Create || !HASH_Destroy || !HASH_Begin || !HASH_Update ||
!HASH_End) {
return "";
}
#endif
// Minimal NSS initialization so we can use the hash functions
const PRUint32 kNssFlags = NSS_INIT_READONLY | NSS_INIT_NOROOTINIT |
NSS_INIT_NOMODDB | NSS_INIT_NOCERTDB;
if (NSS_Initialize(nullptr, "", "", "", kNssFlags) != SECSuccess) {
return "";
}
HASHContext* hashContext = HASH_Create(HASH_AlgSHA256);
if (!hashContext) {
return "";
}
HASH_Begin(hashContext);
ifstream* f = UIOpenRead(gReporterDumpFile, ios::binary);
bool error = false;
// Read the minidump contents
if (f->is_open()) {
uint8_t buff[4096];
do {
f->read((char*)buff, sizeof(buff));
if (f->bad()) {
error = true;
break;
}
HASH_Update(hashContext, buff, f->gcount());
} while (!f->eof());
f->close();
} else {
error = true;
}
delete f;
// Finalize the hash computation
uint8_t result[SHA256_LENGTH];
uint32_t resultLen = 0;
HASH_End(hashContext, result, &resultLen, SHA256_LENGTH);
if (resultLen != SHA256_LENGTH) {
error = true;
}
HASH_Destroy(hashContext);
if (!error) {
ostringstream hash;
for (size_t i = 0; i < SHA256_LENGTH; i++) {
hash << std::setw(2) << std::setfill('0') << std::hex
<< static_cast<unsigned int>(result[i]);
}
return hash.str();
}
return ""; // If we encountered an error, return an empty hash
}
string GetDumpLocalID() {
string localId = Basename(gReporterDumpFile);
string::size_type dot = localId.rfind('.');
if (dot == string::npos) return "";
return localId.substr(0, dot);
}
string GetProgramPath(const string& exename) {
string path = gArgv[0];
size_t pos = path.rfind(UI_CRASH_REPORTER_FILENAME BIN_SUFFIX);
path.erase(pos);
#ifdef XP_MACOSX
// On macOS the crash reporter client is shipped as an application bundle
// contained within Firefox' main application bundle. So when it's invoked
// its current working directory looks like:
// Firefox.app/Contents/MacOS/crashreporter.app/Contents/MacOS/
// The other applications we ship with Firefox are stored in the main bundle
// (Firefox.app/Contents/MacOS/) so we we need to go back three directories
// to reach them.
path.erase(pos - 1);
for (size_t i = 0; i < 3; i++) {
pos = path.rfind(UI_DIR_SEPARATOR, pos - 1);
}
path.erase(pos + 1);
#endif // XP_MACOSX
path.append(exename + BIN_SUFFIX);
return path;
}
} // namespace CrashReporter
using namespace CrashReporter;
Json::Value kEmptyJsonString("");
void RewriteStrings(Json::Value& aExtraData) {
// rewrite some UI strings with the values from the query parameters
string product = aExtraData.get("ProductName", kEmptyJsonString).asString();
Json::Value mozilla("Mozilla");
string vendor = aExtraData.get("Vendor", mozilla).asString();
char buf[4096];
snprintf(buf, sizeof(buf), gStrings[ST_CRASHREPORTERVENDORTITLE].c_str(),
vendor.c_str());
gStrings[ST_CRASHREPORTERTITLE] = buf;
string str = gStrings[ST_CRASHREPORTERPRODUCTERROR];
// Only do the replacement here if the string has two
// format specifiers to start. Otherwise
// we assume it has the product name hardcoded.
string::size_type pos = str.find("%s");
if (pos != string::npos) pos = str.find("%s", pos + 2);
if (pos != string::npos) {
// Leave a format specifier for UIError to fill in
snprintf(buf, sizeof(buf), gStrings[ST_CRASHREPORTERPRODUCTERROR].c_str(),
product.c_str(), "%s");
gStrings[ST_CRASHREPORTERERROR] = buf;
} else {
// product name is hardcoded
gStrings[ST_CRASHREPORTERERROR] = str;
}
snprintf(buf, sizeof(buf), gStrings[ST_CRASHREPORTERDESCRIPTION].c_str(),
product.c_str());
gStrings[ST_CRASHREPORTERDESCRIPTION] = buf;
snprintf(buf, sizeof(buf), gStrings[ST_CHECKSUBMIT].c_str(), vendor.c_str());
gStrings[ST_CHECKSUBMIT] = buf;
snprintf(buf, sizeof(buf), gStrings[ST_RESTART].c_str(), product.c_str());
gStrings[ST_RESTART] = buf;
snprintf(buf, sizeof(buf), gStrings[ST_QUIT].c_str(), product.c_str());
gStrings[ST_QUIT] = buf;
snprintf(buf, sizeof(buf), gStrings[ST_ERROR_ENDOFLIFE].c_str(),
product.c_str());
gStrings[ST_ERROR_ENDOFLIFE] = buf;
}
bool CheckEndOfLifed(const Json::Value& aVersion) {
if (!aVersion.isString()) {
return false;
}
string reportPath =
gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" + aVersion.asString();
return UIFileExists(reportPath);
}
int main(int argc, char** argv) {
gArgc = argc;
gArgv = argv;
string autoSubmitEnv = UIGetEnv("MOZ_CRASHREPORTER_AUTO_SUBMIT");
gAutoSubmit = !autoSubmitEnv.empty();
if (!ReadConfig()) {
UIError("Couldn't read configuration.");
return 0;
}
if (!UIInit()) {
return 0;
}
if (argc > 1) {
gReporterDumpFile = argv[1];
}
if (gReporterDumpFile.empty()) {
// no dump file specified, run the default UI
if (!gAutoSubmit) {
UIShowDefaultUI();
}
} else {
// Start by running minidump analyzer to gather stack traces.
string reporterDumpFile = gReporterDumpFile;
vector<string> args = {reporterDumpFile};
string dumpAllThreadsEnv = UIGetEnv("MOZ_CRASHREPORTER_DUMP_ALL_THREADS");
if (!dumpAllThreadsEnv.empty()) {
args.insert(args.begin(), "--full");
}
UIRunProgram(CrashReporter::GetProgramPath(UI_MINIDUMP_ANALYZER_FILENAME),
args,
/* wait */ true);
// go ahead with the crash reporter
gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
if (gExtraFile.empty()) {
UIError(gStrings[ST_ERROR_BADARGUMENTS]);
return 0;
}
if (!UIFileExists(gExtraFile)) {
UIError(gStrings[ST_ERROR_EXTRAFILEEXISTS]);
return 0;
}
gMemoryFile =
GetAdditionalFilename(gReporterDumpFile, kMemoryReportExtension);
if (!UIFileExists(gMemoryFile)) {
gMemoryFile.erase();
}
Json::Value extraData;
if (!ReadExtraFile(gExtraFile, extraData)) {
UIError(gStrings[ST_ERROR_EXTRAFILEREAD]);
return 0;
}
if (!extraData.isMember("ProductName")) {
UIError(gStrings[ST_ERROR_NOPRODUCTNAME]);
return 0;
}
// There is enough information in the extra file to rewrite strings
// to be product specific
RewriteStrings(extraData);
if (!extraData.isMember("ServerURL")) {
UIError(gStrings[ST_ERROR_NOSERVERURL]);
return 0;
}
// Hopefully the settings path exists in the environment. Try that before
// asking the platform-specific code to guess.
gSettingsPath = UIGetEnv("MOZ_CRASHREPORTER_DATA_DIRECTORY");
if (gSettingsPath.empty()) {
string product =
extraData.get("ProductName", kEmptyJsonString).asString();
string vendor = extraData.get("Vendor", kEmptyJsonString).asString();
if (!UIGetSettingsPath(vendor, product, gSettingsPath)) {
gSettingsPath.clear();
}
}
if (gSettingsPath.empty() || !UIEnsurePathExists(gSettingsPath)) {
UIError(gStrings[ST_ERROR_NOSETTINGSPATH]);
return 0;
}
OpenLogFile();
gEventsPath = UIGetEnv("MOZ_CRASHREPORTER_EVENTS_DIRECTORY");
gPingPath = UIGetEnv("MOZ_CRASHREPORTER_PING_DIRECTORY");
// Assemble and send the crash ping
string hash = ComputeDumpHash();
string pingUuid;
SendCrashPing(extraData, hash, pingUuid, gPingPath);
UpdateEventFile(extraData, hash, pingUuid);
if (!UIFileExists(gReporterDumpFile)) {
UIError(gStrings[ST_ERROR_DUMPFILEEXISTS]);
return 0;
}
string pendingDir = gSettingsPath + UI_DIR_SEPARATOR + "pending";
if (!MoveCrashData(pendingDir, gReporterDumpFile, gExtraFile,
gMemoryFile)) {
return 0;
}
string sendURL = extraData.get("ServerURL", kEmptyJsonString).asString();
// we don't need to actually send these
extraData.removeMember("ServerURL");
extraData.removeMember("StackTraces");
extraData["SubmittedFrom"] = "Client";
extraData["Throttleable"] = "1";
// re-set XUL_APP_FILE for xulrunner wrapped apps
const char* appfile = getenv("MOZ_CRASHREPORTER_RESTART_XUL_APP_FILE");
if (appfile && *appfile) {
const char prefix[] = "XUL_APP_FILE=";
char* env = (char*)malloc(strlen(appfile) + strlen(prefix) + 1);
if (!env) {
UIError("Out of memory");
return 0;
}
strcpy(env, prefix);
strcat(env, appfile);
putenv(env);
free(env);
}
vector<string> restartArgs;
if (!extraData.isMember("WindowsErrorReporting")) {
// We relaunch the application associated with the client, but only when
// we encountered a crash caught by the exception handler. Crashes handled
// by WER are prevented from directly restarting the application.
string programPath = GetProgramPath(MOZ_APP_NAME);
#ifndef XP_WIN
const char* moz_app_launcher = getenv("MOZ_APP_LAUNCHER");
if (moz_app_launcher) {
programPath = moz_app_launcher;
}
#endif // XP_WIN
restartArgs.push_back(programPath);
ostringstream paramName;
int i = 1;
paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
const char* param = getenv(paramName.str().c_str());
while (param && *param) {
restartArgs.push_back(param);
paramName.str("");
paramName << "MOZ_CRASHREPORTER_RESTART_ARG_" << i++;
param = getenv(paramName.str().c_str());
}
}
// allow override of the server url via environment variable
// XXX: remove this in the far future when our robot
// masters force everyone to use XULRunner
char* urlEnv = getenv("MOZ_CRASHREPORTER_URL");
if (urlEnv && *urlEnv) {
sendURL = urlEnv;
}
// see if this version has been end-of-lifed
if (extraData.isMember("Version") &&
CheckEndOfLifed(extraData["Version"])) {
UIError(gStrings[ST_ERROR_ENDOFLIFE]);
DeleteDump();
return 0;
}
StringTable files;
files["upload_file_minidump"] = gReporterDumpFile;
if (!gMemoryFile.empty()) {
files["memory_report"] = gMemoryFile;
}
if (!UIShowCrashUI(files, extraData, sendURL, restartArgs)) {
DeleteDump();
}
}
UIShutdown();
return 0;
}
#if defined(XP_WIN) && !defined(__GNUC__)
# include <windows.h>
// We need WinMain in order to not be a console app. This function is unused
// if we are a console application.
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR args, int) {
// Remove everything except close window from the context menu
{
HKEY hkApp;
RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Classes\\Applications", 0,
nullptr, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, nullptr,
&hkApp, nullptr);
RegCloseKey(hkApp);
if (RegCreateKeyExW(HKEY_CURRENT_USER,
L"Software\\Classes\\Applications\\crashreporter.exe",
0, nullptr, REG_OPTION_VOLATILE, KEY_SET_VALUE, nullptr,
&hkApp, nullptr) == ERROR_SUCCESS) {
RegSetValueExW(hkApp, L"IsHostApp", 0, REG_NONE, 0, 0);
RegSetValueExW(hkApp, L"NoOpenWith", 0, REG_NONE, 0, 0);
RegSetValueExW(hkApp, L"NoStartPage", 0, REG_NONE, 0, 0);
RegCloseKey(hkApp);
}
}
char** argv = static_cast<char**>(malloc(__argc * sizeof(char*)));
for (int i = 0; i < __argc; i++) {
argv[i] = strdup(WideToUTF8(__wargv[i]).c_str());
}
// Do the real work.
return main(__argc, argv);
}
#endif

Просмотреть файл

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="CrashReporter"
type="win32"
/>
<description>Crash Reporter</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
</ms_asmv3:windowsSettings>
</ms_asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

Просмотреть файл

@ -1,158 +0,0 @@
/* 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/. */
#ifndef CRASHREPORTER_H__
#define CRASHREPORTER_H__
#ifdef _MSC_VER
# pragma warning(push)
// Disable exception handler warnings.
# pragma warning(disable : 4530)
#endif
#include <string>
#include <map>
#include <vector>
#include <stdlib.h>
#include <stdio.h>
#include <fstream>
#define MAX_COMMENT_LENGTH 10000
#if defined(XP_WIN)
# include <windows.h>
# define UI_DIR_SEPARATOR "\\"
std::string WideToUTF8(const std::wstring& wide, bool* success = 0);
#else
# define UI_DIR_SEPARATOR "/"
#endif
#include "json/json.h"
#define UI_CRASH_REPORTER_FILENAME "crashreporter"
#define UI_MINIDUMP_ANALYZER_FILENAME "minidump-analyzer"
#define UI_PING_SENDER_FILENAME "pingsender"
typedef std::map<std::string, std::string> StringTable;
#define ST_CRASHREPORTERTITLE "CrashReporterTitle"
#define ST_CRASHREPORTERVENDORTITLE "CrashReporterVendorTitle"
#define ST_CRASHREPORTERERROR "CrashReporterErrorText"
#define ST_CRASHREPORTERPRODUCTERROR "CrashReporterProductErrorText2"
#define ST_CRASHREPORTERHEADER "CrashReporterSorry"
#define ST_CRASHREPORTERDESCRIPTION "CrashReporterDescriptionText2"
#define ST_CRASHREPORTERDEFAULT "CrashReporterDefault"
#define ST_VIEWREPORT "Details"
#define ST_VIEWREPORTTITLE "ViewReportTitle"
#define ST_COMMENTGRAYTEXT "CommentGrayText"
#define ST_EXTRAREPORTINFO "ExtraReportInfo"
#define ST_CHECKSUBMIT "CheckSendReport"
#define ST_CHECKURL "CheckIncludeURL"
#define ST_REPORTPRESUBMIT "ReportPreSubmit2"
#define ST_REPORTDURINGSUBMIT "ReportDuringSubmit2"
#define ST_REPORTSUBMITSUCCESS "ReportSubmitSuccess"
#define ST_SUBMITFAILED "ReportSubmitFailed"
#define ST_QUIT "Quit2"
#define ST_RESTART "Restart"
#define ST_OK "Ok"
#define ST_CLOSE "Close"
#define ST_ERROR_BADARGUMENTS "ErrorBadArguments"
#define ST_ERROR_EXTRAFILEEXISTS "ErrorExtraFileExists"
#define ST_ERROR_EXTRAFILEREAD "ErrorExtraFileRead"
#define ST_ERROR_EXTRAFILEMOVE "ErrorExtraFileMove"
#define ST_ERROR_DUMPFILEEXISTS "ErrorDumpFileExists"
#define ST_ERROR_DUMPFILEMOVE "ErrorDumpFileMove"
#define ST_ERROR_NOPRODUCTNAME "ErrorNoProductName"
#define ST_ERROR_NOSERVERURL "ErrorNoServerURL"
#define ST_ERROR_NOSETTINGSPATH "ErrorNoSettingsPath"
#define ST_ERROR_CREATEDUMPDIR "ErrorCreateDumpDir"
#define ST_ERROR_ENDOFLIFE "ErrorEndOfLife"
//=============================================================================
// implemented in crashreporter.cpp and ping.cpp
//=============================================================================
namespace CrashReporter {
extern StringTable gStrings;
extern std::string gSettingsPath;
extern std::string gEventsPath;
extern int gArgc;
extern char** gArgv;
extern bool gAutoSubmit;
void UIError(const std::string& message);
// The UI finished sending the report
void SendCompleted(bool success, const std::string& serverResponse);
bool ReadStrings(std::istream& in, StringTable& strings, bool unescape);
bool ReadStringsFromFile(const std::string& path, StringTable& strings,
bool unescape);
void LogMessage(const std::string& message);
void DeleteDump();
std::string GetDumpLocalID();
std::string GetProgramPath(const std::string& exename);
// Telemetry ping
bool SendCrashPing(Json::Value& extra, const std::string& hash,
std::string& pingUuid, const std::string& pingDir);
static const unsigned int kSaveCount = 10;
} // namespace CrashReporter
//=============================================================================
// implemented in the platform-specific files
//=============================================================================
bool UIInit();
void UIShutdown();
// Run the UI for when the app was launched without a dump file
void UIShowDefaultUI();
// Run the UI for when the app was launched with a dump file
// Return true if the user sent (or tried to send) the crash report,
// false if they chose not to, and it should be deleted.
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
const std::string& sendURL,
const std::vector<std::string>& restartArgs);
void UIError_impl(const std::string& message);
bool UIGetIniPath(std::string& path);
bool UIGetSettingsPath(const std::string& vendor, const std::string& product,
std::string& settingsPath);
bool UIEnsurePathExists(const std::string& path);
bool UIFileExists(const std::string& path);
bool UIMoveFile(const std::string& oldfile, const std::string& newfile);
bool UIDeleteFile(const std::string& oldfile);
std::ifstream* UIOpenRead(const std::string& filename,
std::ios_base::openmode mode);
std::ofstream* UIOpenWrite(const std::string& filename,
std::ios_base::openmode mode);
void UIPruneSavedDumps(const std::string& directory);
// Run the program specified by exename, passing it the parameters in arg.
// If wait is true, wait for the program to terminate execution before
// returning. Returns true if the program was launched correctly, false
// otherwise.
bool UIRunProgram(const std::string& exename,
const std::vector<std::string>& args, bool wait = false);
// Read the environment variable specified by name
std::string UIGetEnv(const std::string& name);
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif

Двоичные данные
toolkit/crashreporter/client/crashreporter.ico

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 25 KiB

Просмотреть файл

@ -1,143 +0,0 @@
/* 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/. */
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winresrc.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winresrc.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_MAINICON ICON "crashreporter.ico"
/////////////////////////////////////////////////////////////////////////////
//
// AVI
//
IDR_THROBBER AVI "Throbber-small.avi"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_SENDDIALOG DIALOGEX 0, 0, 241, 187
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "Sending Crash Report..."
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_DESCRIPTIONTEXT,"RICHEDIT50W",ES_MULTILINE | ES_READONLY,8,7,226,12,WS_EX_TRANSPARENT
CONTROL "tell mozilla about this crash so they can fix it",IDC_SUBMITREPORTCHECK,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,25,222,10
CHECKBOX "details...",IDC_VIEWREPORTBUTTON,24,40,54,14,BS_PUSHLIKE
EDITTEXT IDC_COMMENTTEXT,24,59,210,43,ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL
CONTROL "include the address of the page i was on",IDC_INCLUDEURLCHECK,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,24,107,210,10
CONTROL "",IDC_THROBBER,"SysAnimate32",ACS_TRANSPARENT | NOT WS_VISIBLE | WS_TABSTOP,4,152,16,16
LTEXT "your crash report will be submitted when you restart",IDC_PROGRESSTEXT,24,152,210,10,SS_NOPREFIX
DEFPUSHBUTTON "restart firefox",IDC_RESTARTBUTTON,84,166,68,14
PUSHBUTTON "quit without sending",IDC_CLOSEBUTTON,157,166,77,14
END
IDD_VIEWREPORTDIALOG DIALOGEX 0, 0, 208, 126
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION
CAPTION "view report"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_VIEWREPORTTEXT,"RICHEDIT50W",ES_MULTILINE | ES_READONLY | WS_BORDER | WS_VSCROLL | WS_TABSTOP,7,7,194,92
DEFPUSHBUTTON "OK",IDOK,151,105,50,14
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_SENDDIALOG, DIALOG
BEGIN
LEFTMARGIN, 8
RIGHTMARGIN, 234
TOPMARGIN, 7
BOTTOMMARGIN, 180
END
IDD_VIEWREPORTDIALOG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 201
TOPMARGIN, 7
BOTTOMMARGIN, 119
END
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

Просмотреть файл

@ -1,361 +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/. */
#include <unistd.h>
#include <dlfcn.h>
#include <errno.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <gdk/gdkkeysyms.h>
#include <algorithm>
#include <string>
#include <vector>
#include "common/linux/http_upload.h"
#include "crashreporter.h"
#include "crashreporter_gtk_common.h"
#ifndef GDK_KEY_Escape
# define GDK_KEY_Escape GDK_Escape
#endif
using std::string;
using std::vector;
using namespace CrashReporter;
GtkWidget* gWindow = 0;
GtkWidget* gSubmitReportCheck = 0;
GtkWidget* gIncludeURLCheck = 0;
GtkWidget* gThrobber = 0;
GtkWidget* gProgressLabel = 0;
GtkWidget* gCloseButton = 0;
GtkWidget* gRestartButton = 0;
bool gInitialized = false;
bool gDidTrySend = false;
StringTable gFiles;
Json::Value gQueryParameters;
string gHttpProxy;
string gAuth;
string gCACertificateFile;
string gSendURL;
string gURLParameter;
vector<string> gRestartArgs;
GThread* gSendThreadID;
// From crashreporter_linux.cpp
void SendReport();
void DisableGUIAndSendReport();
void TryInitGnome();
void UpdateSubmit();
static bool RestartApplication() {
char** argv = reinterpret_cast<char**>(
malloc(sizeof(char*) * (gRestartArgs.size() + 1)));
if (!argv) return false;
unsigned int i;
for (i = 0; i < gRestartArgs.size(); i++) {
argv[i] = (char*)gRestartArgs[i].c_str();
}
argv[i] = 0;
pid_t pid = fork();
if (pid == -1) {
free(argv);
return false;
}
if (pid == 0) {
(void)execv(argv[0], argv);
_exit(1);
}
free(argv);
return true;
}
// Quit the app, used as a timeout callback
gboolean CloseApp(gpointer data) {
if (!gAutoSubmit) {
gtk_main_quit();
}
g_thread_join(gSendThreadID);
return FALSE;
}
static gboolean ReportCompleted(gpointer success) {
gtk_widget_hide(gThrobber);
string str =
success ? gStrings[ST_REPORTSUBMITSUCCESS] : gStrings[ST_SUBMITFAILED];
gtk_label_set_text(GTK_LABEL(gProgressLabel), str.c_str());
g_timeout_add(5000, CloseApp, 0);
return FALSE;
}
#define HTTP_PROXY_DIR "/system/http_proxy"
void LoadProxyinfo() {
class GConfClient;
typedef GConfClient* (*_gconf_default_fn)();
typedef gboolean (*_gconf_bool_fn)(GConfClient*, const gchar*, GError**);
typedef gint (*_gconf_int_fn)(GConfClient*, const gchar*, GError**);
typedef gchar* (*_gconf_string_fn)(GConfClient*, const gchar*, GError**);
if (getenv("http_proxy"))
return; // libcurl can use the value from the environment
static void* gconfLib = dlopen("libgconf-2.so.4", RTLD_LAZY);
if (!gconfLib) return;
_gconf_default_fn gconf_client_get_default =
(_gconf_default_fn)dlsym(gconfLib, "gconf_client_get_default");
_gconf_bool_fn gconf_client_get_bool =
(_gconf_bool_fn)dlsym(gconfLib, "gconf_client_get_bool");
_gconf_int_fn gconf_client_get_int =
(_gconf_int_fn)dlsym(gconfLib, "gconf_client_get_int");
_gconf_string_fn gconf_client_get_string =
(_gconf_string_fn)dlsym(gconfLib, "gconf_client_get_string");
if (!(gconf_client_get_default && gconf_client_get_bool &&
gconf_client_get_int && gconf_client_get_string)) {
dlclose(gconfLib);
return;
}
GConfClient* conf = gconf_client_get_default();
if (gconf_client_get_bool(conf, HTTP_PROXY_DIR "/use_http_proxy", nullptr)) {
gint port;
gchar *host = nullptr, *httpproxy = nullptr;
host = gconf_client_get_string(conf, HTTP_PROXY_DIR "/host", nullptr);
port = gconf_client_get_int(conf, HTTP_PROXY_DIR "/port", nullptr);
if (port && host && *host != '\0') {
httpproxy = g_strdup_printf("http://%s:%d/", host, port);
gHttpProxy = httpproxy;
}
g_free(host);
g_free(httpproxy);
if (gconf_client_get_bool(conf, HTTP_PROXY_DIR "/use_authentication",
nullptr)) {
gchar *user, *password, *auth = nullptr;
user = gconf_client_get_string(
conf, HTTP_PROXY_DIR "/authentication_user", nullptr);
password = gconf_client_get_string(
conf, HTTP_PROXY_DIR "/authentication_password", nullptr);
if (user && password) {
auth = g_strdup_printf("%s:%s", user, password);
gAuth = auth;
}
g_free(user);
g_free(password);
g_free(auth);
}
}
g_object_unref(conf);
// Don't dlclose gconfLib as libORBit-2 uses atexit().
}
gpointer SendThread(gpointer args) {
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
string parameters(writeString(builder, gQueryParameters));
string response, error;
long response_code;
bool success = google_breakpad::HTTPUpload::SendRequest(
gSendURL, parameters, gFiles, gHttpProxy, gAuth, gCACertificateFile,
&response, &response_code, &error);
if (success) {
LogMessage("Crash report submitted successfully");
} else {
LogMessage("Crash report submission failed: " + error);
}
SendCompleted(success, response);
if (!gAutoSubmit) {
// Apparently glib is threadsafe, and will schedule this
// on the main thread, see:
// http://library.gnome.org/devel/gtk-faq/stable/x499.html
g_idle_add(ReportCompleted, (gpointer)success);
}
return nullptr;
}
gboolean WindowDeleted(GtkWidget* window, GdkEvent* event, gpointer userData) {
SaveSettings();
gtk_main_quit();
return TRUE;
}
gboolean check_escape(GtkWidget* window, GdkEventKey* event,
gpointer userData) {
if (event->keyval == GDK_KEY_Escape) {
gtk_main_quit();
return TRUE;
}
return FALSE;
}
static void MaybeSubmitReport() {
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) {
gDidTrySend = true;
DisableGUIAndSendReport();
} else {
gtk_main_quit();
}
}
void CloseClicked(GtkButton* button, gpointer userData) {
SaveSettings();
MaybeSubmitReport();
}
void RestartClicked(GtkButton* button, gpointer userData) {
SaveSettings();
RestartApplication();
MaybeSubmitReport();
}
static void UpdateURL() {
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))) {
gQueryParameters["URL"] = gURLParameter;
} else {
gQueryParameters.removeMember("URL");
}
}
void SubmitReportChecked(GtkButton* sender, gpointer userData) {
UpdateSubmit();
}
void IncludeURLClicked(GtkButton* sender, gpointer userData) { UpdateURL(); }
/* === Crashreporter UI Functions === */
bool UIInit() {
// breakpad probably left us with blocked signals, unblock them here
sigset_t signals, old;
sigfillset(&signals);
sigprocmask(SIG_UNBLOCK, &signals, &old);
// tell glib we're going to use threads
g_thread_init(nullptr);
if (gtk_init_check(&gArgc, &gArgv)) {
gInitialized = true;
if (gStrings.find("isRTL") != gStrings.end() && gStrings["isRTL"] == "yes")
gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL);
return true;
}
return false;
}
void UIShowDefaultUI() {
GtkWidget* errorDialog = gtk_message_dialog_new(
nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s",
gStrings[ST_CRASHREPORTERDEFAULT].c_str());
gtk_window_set_title(GTK_WINDOW(errorDialog),
gStrings[ST_CRASHREPORTERTITLE].c_str());
gtk_dialog_run(GTK_DIALOG(errorDialog));
}
void UIError_impl(const string& message) {
if (!gInitialized) {
// Didn't initialize, this is the best we can do
printf("Error: %s\n", message.c_str());
return;
}
GtkWidget* errorDialog =
gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE, "%s", message.c_str());
gtk_window_set_title(GTK_WINDOW(errorDialog),
gStrings[ST_CRASHREPORTERTITLE].c_str());
gtk_dialog_run(GTK_DIALOG(errorDialog));
}
bool UIGetIniPath(string& path) {
path = gArgv[0];
path.append(".ini");
return true;
}
/*
* Settings are stored in ~/.vendor/product, or
* ~/.product if vendor is empty.
*/
bool UIGetSettingsPath(const string& vendor, const string& product,
string& settingsPath) {
char* home = getenv("HOME");
if (!home) return false;
settingsPath = home;
settingsPath += "/.";
if (!vendor.empty()) {
string lc_vendor;
std::transform(vendor.begin(), vendor.end(), back_inserter(lc_vendor),
(int (*)(int))std::tolower);
settingsPath += lc_vendor + "/";
}
string lc_product;
std::transform(product.begin(), product.end(), back_inserter(lc_product),
(int (*)(int))std::tolower);
settingsPath += lc_product + "/Crash Reports";
return true;
}
bool UIMoveFile(const string& file, const string& newfile) {
if (!rename(file.c_str(), newfile.c_str())) return true;
if (errno != EXDEV) return false;
// use system /bin/mv instead, time to fork
pid_t pID = vfork();
if (pID < 0) {
// Failed to fork
return false;
}
if (pID == 0) {
char* const args[4] = {const_cast<char*>("mv"), strdup(file.c_str()),
strdup(newfile.c_str()), 0};
if (args[1] && args[2]) execve("/bin/mv", args, 0);
free(args[1]);
free(args[2]);
exit(-1);
}
int status;
waitpid(pID, &status, 0);
return UIFileExists(newfile);
}

Просмотреть файл

@ -1,50 +0,0 @@
/* 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/. */
#ifndef CRASHREPORTER_GTK_COMMON_H__
#define CRASHREPORTER_GTK_COMMON_H__
#include <glib.h>
#include <gtk/gtk.h>
#include <string>
#include <vector>
#include "json/json.h"
const char kIniFile[] = "crashreporter.ini";
extern GtkWidget* gWindow;
extern GtkWidget* gSubmitReportCheck;
extern GtkWidget* gIncludeURLCheck;
extern GtkWidget* gThrobber;
extern GtkWidget* gProgressLabel;
extern GtkWidget* gCloseButton;
extern GtkWidget* gRestartButton;
extern std::vector<std::string> gRestartArgs;
extern GThread* gSendThreadID;
extern bool gInitialized;
extern bool gDidTrySend;
extern StringTable gFiles;
extern Json::Value gQueryParameters;
extern std::string gHttpProxy;
extern std::string gAuth;
extern std::string gCACertificateFile;
extern std::string gSendURL;
extern std::string gURLParameter;
void LoadProxyinfo();
gboolean CloseApp(gpointer data);
gpointer SendThread(gpointer args);
gboolean WindowDeleted(GtkWidget* window, GdkEvent* event, gpointer userData);
gboolean check_escape(GtkWidget* window, GdkEventKey* event, gpointer data);
void SubmitReportChecked(GtkButton* sender, gpointer userData);
void IncludeURLClicked(GtkButton* sender, gpointer userData);
void CloseClicked(GtkButton* button, gpointer userData);
void RestartClicked(GtkButton* button, gpointer userData);
void SaveSettings(void);
#endif // CRASHREPORTER_GTK_COMMON_H__

Просмотреть файл

@ -1,525 +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/. */
#include <dlfcn.h>
#include <fcntl.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <string.h>
#include <cctype>
#include "crashreporter.h"
#include "crashreporter_gtk_common.h"
#define LABEL_MAX_CHAR_WIDTH 48
using std::ios;
using std::string;
using std::vector;
using namespace CrashReporter;
static GtkWidget* gViewReportButton = 0;
static GtkWidget* gCommentTextLabel = 0;
static GtkWidget* gCommentText = 0;
static bool gCommentFieldHint = true;
// handle from dlopen'ing libgnome
static void* gnomeLib = nullptr;
// handle from dlopen'ing libgnomeui
static void* gnomeuiLib = nullptr;
static void LoadSettings() {
/*
* NOTE! This code needs to stay in sync with the preference checking
* code in in nsExceptionHandler.cpp.
*/
bool includeURL = true;
bool submitReport = true;
StringTable settings;
if (ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true)) {
if (settings.find("IncludeURL") != settings.end()) {
includeURL = settings["IncludeURL"][0] != '0';
}
if (settings.find("SubmitReport") != settings.end()) {
submitReport = settings["SubmitReport"][0] != '0';
}
}
if (gIncludeURLCheck) {
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck),
includeURL);
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck),
submitReport);
}
static string Escape(const string& str) {
string ret;
for (auto c : str) {
if (c == '\\') {
ret += "\\\\";
} else if (c == '\n') {
ret += "\\n";
} else if (c == '\t') {
ret += "\\t";
} else {
ret.push_back(c);
}
}
return ret;
}
static bool WriteStrings(std::ostream& out, const string& header,
StringTable& strings, bool escape) {
out << "[" << header << "]\n";
for (const auto& iter : strings) {
out << iter.first << "=";
if (escape) {
out << Escape(iter.second);
} else {
out << iter.second;
}
out << '\n';
}
return true;
}
static bool WriteStringsToFile(const string& path, const string& header,
StringTable& strings, bool escape) {
std::ofstream* f = UIOpenWrite(path, ios::trunc);
bool success = false;
if (f->is_open()) {
success = WriteStrings(*f, header, strings, escape);
f->close();
}
delete f;
return success;
}
void SaveSettings() {
/*
* NOTE! This code needs to stay in sync with the preference setting
* code in in nsExceptionHandler.cpp.
*/
StringTable settings;
ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true);
if (gIncludeURLCheck != 0)
settings["IncludeURL"] =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck)) ? "1"
: "0";
settings["SubmitReport"] =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck)) ? "1"
: "0";
WriteStringsToFile(gSettingsPath + "/" + kIniFile, "Crash Reporter", settings,
true);
}
void SendReport() {
LoadProxyinfo();
// spawn a thread to do the sending
gSendThreadID = g_thread_create(SendThread, nullptr, TRUE, nullptr);
}
void DisableGUIAndSendReport() {
// disable all our gui controls, show the throbber + change the progress text
gtk_widget_set_sensitive(gSubmitReportCheck, FALSE);
gtk_widget_set_sensitive(gViewReportButton, FALSE);
gtk_widget_set_sensitive(gCommentText, FALSE);
if (gIncludeURLCheck) gtk_widget_set_sensitive(gIncludeURLCheck, FALSE);
gtk_widget_set_sensitive(gCloseButton, FALSE);
if (gRestartButton) gtk_widget_set_sensitive(gRestartButton, FALSE);
gtk_widget_show_all(gThrobber);
gtk_label_set_text(GTK_LABEL(gProgressLabel),
gStrings[ST_REPORTDURINGSUBMIT].c_str());
SendReport();
}
static void ShowReportInfo(GtkTextView* viewReportTextView) {
GtkTextBuffer* buffer = gtk_text_view_get_buffer(viewReportTextView);
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(buffer, &start);
gtk_text_buffer_get_end_iter(buffer, &end);
gtk_text_buffer_delete(buffer, &start, &end);
for (Json::ValueConstIterator iter = gQueryParameters.begin();
iter != gQueryParameters.end(); ++iter) {
gtk_text_buffer_insert(buffer, &end, iter.name().c_str(),
iter.name().length());
gtk_text_buffer_insert(buffer, &end, ": ", -1);
string value;
if (iter->isString()) {
value = iter->asString();
} else {
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
value = writeString(builder, *iter);
}
gtk_text_buffer_insert(buffer, &end, value.c_str(), value.length());
gtk_text_buffer_insert(buffer, &end, "\n", -1);
}
gtk_text_buffer_insert(buffer, &end, "\n", -1);
gtk_text_buffer_insert(buffer, &end, gStrings[ST_EXTRAREPORTINFO].c_str(),
-1);
}
void UpdateSubmit() {
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) {
gtk_widget_set_sensitive(gViewReportButton, TRUE);
gtk_widget_set_sensitive(gCommentText, TRUE);
if (gIncludeURLCheck) gtk_widget_set_sensitive(gIncludeURLCheck, TRUE);
gtk_label_set_text(GTK_LABEL(gProgressLabel),
gStrings[ST_REPORTPRESUBMIT].c_str());
} else {
gtk_widget_set_sensitive(gViewReportButton, FALSE);
gtk_widget_set_sensitive(gCommentText, FALSE);
if (gIncludeURLCheck) gtk_widget_set_sensitive(gIncludeURLCheck, FALSE);
gtk_label_set_text(GTK_LABEL(gProgressLabel), "");
}
}
static void ViewReportClicked(GtkButton* button, gpointer userData) {
GtkDialog* dialog = GTK_DIALOG(gtk_dialog_new_with_buttons(
gStrings[ST_VIEWREPORTTITLE].c_str(), GTK_WINDOW(gWindow),
GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_OK, nullptr));
GtkWidget* scrolled = gtk_scrolled_window_new(0, 0);
gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(dialog)),
scrolled);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
GTK_SHADOW_IN);
gtk_widget_set_vexpand(scrolled, TRUE);
GtkWidget* viewReportTextView = gtk_text_view_new();
gtk_container_add(GTK_CONTAINER(scrolled), viewReportTextView);
gtk_text_view_set_editable(GTK_TEXT_VIEW(viewReportTextView), FALSE);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(viewReportTextView), GTK_WRAP_WORD);
gtk_widget_set_size_request(GTK_WIDGET(viewReportTextView), -1, 100);
ShowReportInfo(GTK_TEXT_VIEW(viewReportTextView));
gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK);
gtk_widget_set_size_request(GTK_WIDGET(dialog), 400, 200);
gtk_widget_show_all(GTK_WIDGET(dialog));
gtk_dialog_run(dialog);
gtk_widget_destroy(GTK_WIDGET(dialog));
}
static void CommentChanged(GtkTextBuffer* buffer, gpointer userData) {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(buffer, &start);
gtk_text_buffer_get_end_iter(buffer, &end);
const char* comment = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
if (comment[0] == '\0' || gCommentFieldHint) {
gQueryParameters.removeMember("Comments");
} else {
gQueryParameters["Comments"] = comment;
}
}
static void CommentInsert(GtkTextBuffer* buffer, GtkTextIter* location,
gchar* text, gint len, gpointer userData) {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(buffer, &start);
gtk_text_buffer_get_end_iter(buffer, &end);
const char* comment = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
// limit to 500 bytes in utf-8
if (strlen(comment) + len > MAX_COMMENT_LENGTH) {
g_signal_stop_emission_by_name(buffer, "insert-text");
}
}
static void UpdateHintText(GtkWidget* widget, gboolean gainedFocus,
bool* hintShowing, const char* hintText) {
GtkTextBuffer* buffer = nullptr;
if (GTK_IS_TEXT_VIEW(widget))
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
if (gainedFocus) {
if (*hintShowing) {
if (buffer == nullptr) { // sort of cheating
gtk_entry_set_text(GTK_ENTRY(widget), "");
} else { // GtkTextView
gtk_text_buffer_set_text(buffer, "", 0);
}
gtk_widget_modify_text(widget, GTK_STATE_NORMAL, nullptr);
*hintShowing = false;
}
} else {
// lost focus
const char* text = nullptr;
if (buffer == nullptr) {
text = gtk_entry_get_text(GTK_ENTRY(widget));
} else {
GtkTextIter start, end;
gtk_text_buffer_get_start_iter(buffer, &start);
gtk_text_buffer_get_end_iter(buffer, &end);
text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
}
if (text == nullptr || text[0] == '\0') {
*hintShowing = true;
if (buffer == nullptr) {
gtk_entry_set_text(GTK_ENTRY(widget), hintText);
} else {
gtk_text_buffer_set_text(buffer, hintText, -1);
}
gtk_widget_modify_text(
widget, GTK_STATE_NORMAL,
&gtk_widget_get_style(widget)->text[GTK_STATE_INSENSITIVE]);
}
}
}
static gboolean CommentFocusChange(GtkWidget* widget, GdkEventFocus* event,
gpointer userData) {
UpdateHintText(widget, event->in, &gCommentFieldHint,
gStrings[ST_COMMENTGRAYTEXT].c_str());
return FALSE;
}
typedef struct _GnomeProgram GnomeProgram;
typedef struct _GnomeModuleInfo GnomeModuleInfo;
typedef GnomeProgram* (*_gnome_program_init_fn)(const char*, const char*,
const GnomeModuleInfo*, int,
char**, const char*, ...);
typedef const GnomeModuleInfo* (*_libgnomeui_module_info_get_fn)();
void TryInitGnome() {
gnomeLib = dlopen("libgnome-2.so.0", RTLD_LAZY);
if (!gnomeLib) return;
gnomeuiLib = dlopen("libgnomeui-2.so.0", RTLD_LAZY);
if (!gnomeuiLib) return;
_gnome_program_init_fn gnome_program_init =
(_gnome_program_init_fn)(dlsym(gnomeLib, "gnome_program_init"));
_libgnomeui_module_info_get_fn libgnomeui_module_info_get =
(_libgnomeui_module_info_get_fn)(dlsym(gnomeuiLib,
"libgnomeui_module_info_get"));
if (gnome_program_init && libgnomeui_module_info_get) {
gnome_program_init("crashreporter", "1.0", libgnomeui_module_info_get(),
gArgc, gArgv, nullptr);
}
}
/* === Crashreporter UI Functions === */
/*
* Anything not listed here is in crashreporter_gtk_common.cpp:
* UIInit
* UIShowDefaultUI
* UIError_impl
* UIGetIniPath
* UIGetSettingsPath
* UIEnsurePathExists
* UIFileExists
* UIMoveFile
* UIDeleteFile
* UIOpenRead
* UIOpenWrite
*/
void UIShutdown() {
if (gnomeuiLib) dlclose(gnomeuiLib);
// Don't dlclose gnomeLib as libgnomevfs and libORBit-2 use atexit().
}
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
const string& sendURL, const vector<string>& restartArgs) {
gFiles = files;
gQueryParameters = queryParameters;
gSendURL = sendURL;
gRestartArgs = restartArgs;
if (gQueryParameters.isMember("URL")) {
gURLParameter = gQueryParameters["URL"].asString();
}
if (gAutoSubmit) {
SendReport();
CloseApp(nullptr);
return true;
}
gWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(gWindow),
gStrings[ST_CRASHREPORTERTITLE].c_str());
gtk_window_set_resizable(GTK_WINDOW(gWindow), FALSE);
gtk_window_set_position(GTK_WINDOW(gWindow), GTK_WIN_POS_CENTER);
gtk_container_set_border_width(GTK_CONTAINER(gWindow), 12);
g_signal_connect(gWindow, "delete-event", G_CALLBACK(WindowDeleted), 0);
g_signal_connect(gWindow, "key_press_event", G_CALLBACK(check_escape),
nullptr);
GtkWidget* vbox = gtk_vbox_new(FALSE, 6);
gtk_container_add(GTK_CONTAINER(gWindow), vbox);
GtkWidget* titleLabel = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(vbox), titleLabel, FALSE, FALSE, 0);
gtk_misc_set_alignment(GTK_MISC(titleLabel), 0, 0.5);
char* markup =
g_strdup_printf("<b>%s</b>", gStrings[ST_CRASHREPORTERHEADER].c_str());
gtk_label_set_markup(GTK_LABEL(titleLabel), markup);
g_free(markup);
GtkWidget* descriptionLabel =
gtk_label_new(gStrings[ST_CRASHREPORTERDESCRIPTION].c_str());
gtk_box_pack_start(GTK_BOX(vbox), descriptionLabel, TRUE, TRUE, 0);
// force the label to line wrap
gtk_label_set_max_width_chars(GTK_LABEL(descriptionLabel),
LABEL_MAX_CHAR_WIDTH);
gtk_label_set_line_wrap(GTK_LABEL(descriptionLabel), TRUE);
gtk_label_set_selectable(GTK_LABEL(descriptionLabel), TRUE);
gtk_misc_set_alignment(GTK_MISC(descriptionLabel), 0, 0.5);
// this is honestly how they suggest you indent a section
GtkWidget* indentBox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), indentBox, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(indentBox), gtk_label_new(""), FALSE, FALSE, 6);
GtkWidget* innerVBox1 = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(indentBox), innerVBox1, TRUE, TRUE, 0);
gSubmitReportCheck =
gtk_check_button_new_with_label(gStrings[ST_CHECKSUBMIT].c_str());
gtk_box_pack_start(GTK_BOX(innerVBox1), gSubmitReportCheck, FALSE, FALSE, 0);
g_signal_connect(gSubmitReportCheck, "clicked",
G_CALLBACK(SubmitReportChecked), 0);
// indent again, below the "submit report" checkbox
GtkWidget* indentBox2 = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(innerVBox1), indentBox2, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(indentBox2), gtk_label_new(""), FALSE, FALSE, 6);
GtkWidget* innerVBox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(indentBox2), innerVBox, TRUE, TRUE, 0);
gtk_box_set_spacing(GTK_BOX(innerVBox), 6);
GtkWidget* viewReportButtonBox = gtk_hbutton_box_new();
gtk_box_pack_start(GTK_BOX(innerVBox), viewReportButtonBox, FALSE, FALSE, 0);
gtk_box_set_spacing(GTK_BOX(viewReportButtonBox), 6);
gtk_button_box_set_layout(GTK_BUTTON_BOX(viewReportButtonBox),
GTK_BUTTONBOX_START);
gViewReportButton =
gtk_button_new_with_label(gStrings[ST_VIEWREPORT].c_str());
gtk_box_pack_start(GTK_BOX(viewReportButtonBox), gViewReportButton, FALSE,
FALSE, 0);
g_signal_connect(gViewReportButton, "clicked", G_CALLBACK(ViewReportClicked),
0);
GtkWidget* scrolled = gtk_scrolled_window_new(0, 0);
gtk_container_add(GTK_CONTAINER(innerVBox), scrolled);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
GTK_SHADOW_IN);
gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(scrolled),
100);
gCommentTextLabel = gtk_label_new(gStrings[ST_COMMENTGRAYTEXT].c_str());
gCommentText = gtk_text_view_new();
gtk_label_set_mnemonic_widget(GTK_LABEL(gCommentTextLabel), gCommentText);
gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(gCommentText), FALSE);
g_signal_connect(gCommentText, "focus-in-event",
G_CALLBACK(CommentFocusChange), 0);
g_signal_connect(gCommentText, "focus-out-event",
G_CALLBACK(CommentFocusChange), 0);
GtkTextBuffer* commentBuffer =
gtk_text_view_get_buffer(GTK_TEXT_VIEW(gCommentText));
g_signal_connect(commentBuffer, "changed", G_CALLBACK(CommentChanged), 0);
g_signal_connect(commentBuffer, "insert-text", G_CALLBACK(CommentInsert), 0);
gtk_container_add(GTK_CONTAINER(scrolled), gCommentText);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gCommentText), GTK_WRAP_WORD_CHAR);
gtk_widget_set_size_request(GTK_WIDGET(gCommentText), -1, 100);
if (gQueryParameters.isMember("URL")) {
gIncludeURLCheck =
gtk_check_button_new_with_label(gStrings[ST_CHECKURL].c_str());
gtk_box_pack_start(GTK_BOX(innerVBox), gIncludeURLCheck, FALSE, FALSE, 0);
g_signal_connect(gIncludeURLCheck, "clicked", G_CALLBACK(IncludeURLClicked),
0);
// on by default
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck), TRUE);
}
GtkWidget* progressBox = gtk_hbox_new(FALSE, 6);
gtk_box_pack_start(GTK_BOX(vbox), progressBox, TRUE, TRUE, 0);
// Get the throbber image from alongside the executable
char* dir = g_path_get_dirname(gArgv[0]);
char* path = g_build_filename(dir, "Throbber-small.gif", nullptr);
g_free(dir);
gThrobber = gtk_image_new_from_file(path);
gtk_box_pack_start(GTK_BOX(progressBox), gThrobber, FALSE, FALSE, 0);
gProgressLabel = gtk_label_new(gStrings[ST_REPORTPRESUBMIT].c_str());
gtk_box_pack_start(GTK_BOX(progressBox), gProgressLabel, TRUE, TRUE, 0);
// force the label to line wrap
gtk_label_set_max_width_chars(GTK_LABEL(gProgressLabel),
LABEL_MAX_CHAR_WIDTH);
gtk_label_set_line_wrap(GTK_LABEL(gProgressLabel), TRUE);
GtkWidget* buttonBox = gtk_hbutton_box_new();
gtk_box_pack_end(GTK_BOX(vbox), buttonBox, FALSE, FALSE, 0);
gtk_box_set_spacing(GTK_BOX(buttonBox), 6);
gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonBox), GTK_BUTTONBOX_END);
gCloseButton = gtk_button_new_with_label(gStrings[ST_QUIT].c_str());
gtk_box_pack_start(GTK_BOX(buttonBox), gCloseButton, FALSE, FALSE, 0);
gtk_widget_set_can_default(gCloseButton, TRUE);
g_signal_connect(gCloseButton, "clicked", G_CALLBACK(CloseClicked), 0);
gRestartButton = 0;
if (!restartArgs.empty()) {
gRestartButton = gtk_button_new_with_label(gStrings[ST_RESTART].c_str());
gtk_box_pack_start(GTK_BOX(buttonBox), gRestartButton, FALSE, FALSE, 0);
gtk_widget_set_can_default(gRestartButton, TRUE);
g_signal_connect(gRestartButton, "clicked", G_CALLBACK(RestartClicked), 0);
}
gtk_widget_grab_focus(gSubmitReportCheck);
gtk_widget_grab_default(gRestartButton ? gRestartButton : gCloseButton);
LoadSettings();
UpdateSubmit();
UpdateHintText(gCommentText, FALSE, &gCommentFieldHint,
gStrings[ST_COMMENTGRAYTEXT].c_str());
gtk_widget_show_all(gWindow);
// stick this here to avoid the show_all above...
gtk_widget_hide(gThrobber);
gtk_main();
return gDidTrySend;
}

Просмотреть файл

@ -1,108 +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/. */
#ifndef CRASHREPORTER_OSX_H__
#define CRASHREPORTER_OSX_H__
#include <Cocoa/Cocoa.h>
#include "HTTPMultipartUpload.h"
#include "crashreporter.h"
#include "json/json.h"
// Defined below
@class TextViewWithPlaceHolder;
@interface CrashReporterUI : NSObject {
IBOutlet NSWindow* mWindow;
/* Crash reporter view */
IBOutlet NSTextField* mHeaderLabel;
IBOutlet NSTextField* mDescriptionLabel;
IBOutlet NSButton* mViewReportButton;
IBOutlet NSScrollView* mCommentScrollView;
IBOutlet TextViewWithPlaceHolder* mCommentText;
IBOutlet NSButton* mSubmitReportButton;
IBOutlet NSButton* mIncludeURLButton;
IBOutlet NSButton* mEmailMeButton;
IBOutlet NSTextField* mEmailText;
IBOutlet NSButton* mCloseButton;
IBOutlet NSButton* mRestartButton;
IBOutlet NSProgressIndicator* mProgressIndicator;
IBOutlet NSTextField* mProgressText;
/* Error view */
IBOutlet NSView* mErrorView;
IBOutlet NSTextField* mErrorHeaderLabel;
IBOutlet NSTextField* mErrorLabel;
IBOutlet NSButton* mErrorCloseButton;
/* For "show info" alert */
IBOutlet NSWindow* mViewReportWindow;
IBOutlet NSTextView* mViewReportTextView;
IBOutlet NSButton* mViewReportOkButton;
HTTPMultipartUpload* mPost;
}
- (void)showCrashUI:(const StringTable&)files
queryParameters:(const Json::Value&)queryParameters
sendURL:(const std::string&)sendURL;
- (void)showErrorUI:(const std::string&)message;
- (void)showReportInfo;
- (void)maybeSubmitReport;
- (void)closeMeDown:(id)unused;
- (IBAction)submitReportClicked:(id)sender;
- (IBAction)viewReportClicked:(id)sender;
- (IBAction)viewReportOkClicked:(id)sender;
- (IBAction)closeClicked:(id)sender;
- (IBAction)restartClicked:(id)sender;
- (IBAction)includeURLClicked:(id)sender;
- (void)textDidChange:(NSNotification*)aNotification;
- (BOOL)textView:(NSTextView*)aTextView
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString*)replacementString;
- (void)doInitialResizing;
- (float)setStringFitVertically:(NSControl*)control
string:(NSString*)str
resizeWindow:(BOOL)resizeWindow;
- (void)setView:(NSView*)v animate:(BOOL)animate;
- (void)enableControls:(BOOL)enabled;
- (void)updateSubmit;
- (void)updateURL;
- (void)updateEmail;
- (void)sendReport;
- (bool)setupPost;
- (void)uploadThread:(HTTPMultipartUpload*)post;
- (void)uploadComplete:(NSData*)data;
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:
(NSApplication*)theApplication;
- (void)applicationWillTerminate:(NSNotification*)aNotification;
@end
/*
* Subclass NSTextView to provide a text view with placeholder text.
* Also provide a setEnabled implementation.
*/
@interface TextViewWithPlaceHolder : NSTextView {
NSMutableAttributedString* mPlaceHolderString;
}
- (BOOL)becomeFirstResponder;
- (void)drawRect:(NSRect)rect;
- (BOOL)resignFirstResponder;
- (void)setPlaceholder:(NSString*)placeholder;
- (void)insertTab:(id)sender;
- (void)insertBacktab:(id)sender;
- (void)setEnabled:(BOOL)enabled;
- (void)dealloc;
@end
#endif

Просмотреть файл

@ -1,805 +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/. */
#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#include "crashreporter.h"
#include "crashreporter_osx.h"
#include <crt_externs.h>
#include <spawn.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sstream>
using std::ostringstream;
using std::string;
using std::vector;
using namespace CrashReporter;
static NSAutoreleasePool* gMainPool;
static CrashReporterUI* gUI = 0;
static StringTable gFiles;
static Json::Value gQueryParameters;
static string gURLParameter;
static string gSendURL;
static vector<string> gRestartArgs;
static bool gDidTrySend = false;
static bool gRTLlayout = false;
static cpu_type_t pref_cpu_types[2] = {
#if defined(__i386__)
CPU_TYPE_X86,
#elif defined(__x86_64__)
CPU_TYPE_X86_64,
#elif defined(__ppc__)
CPU_TYPE_POWERPC,
#elif defined(__aarch64__)
CPU_TYPE_ARM64,
#endif
CPU_TYPE_ANY};
#define NSSTR(s) [NSString stringWithUTF8String:(s).c_str()]
static NSString* Str(const char* aName) {
string str = gStrings[aName];
if (str.empty()) str = "?";
return NSSTR(str);
}
static bool RestartApplication() {
vector<char*> argv(gRestartArgs.size() + 1);
posix_spawnattr_t spawnattr;
if (posix_spawnattr_init(&spawnattr) != 0) {
return false;
}
// Set spawn attributes.
size_t attr_count = sizeof(pref_cpu_types) / sizeof(pref_cpu_types[0]);
size_t attr_ocount = 0;
if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, pref_cpu_types,
&attr_ocount) != 0 ||
attr_ocount != attr_count) {
posix_spawnattr_destroy(&spawnattr);
return false;
}
unsigned int i;
for (i = 0; i < gRestartArgs.size(); i++) {
argv[i] = (char*)gRestartArgs[i].c_str();
}
argv[i] = 0;
char** env = NULL;
char*** nsEnv = _NSGetEnviron();
if (nsEnv) env = *nsEnv;
int result = posix_spawnp(NULL, argv[0], NULL, &spawnattr, &argv[0], env);
posix_spawnattr_destroy(&spawnattr);
return result == 0;
}
@implementation CrashReporterUI
- (void)awakeFromNib {
gUI = self;
[mWindow center];
[mWindow setTitle:[[NSBundle mainBundle]
objectForInfoDictionaryKey:@"CFBundleName"]];
[NSApp activateIgnoringOtherApps:YES];
}
- (void)showCrashUI:(const StringTable&)files
queryParameters:(const Json::Value&)queryParameters
sendURL:(const string&)sendURL {
gFiles = files;
gQueryParameters = queryParameters;
gSendURL = sendURL;
if (gAutoSubmit) {
gDidTrySend = true;
[self sendReport];
return;
}
[mWindow setTitle:Str(ST_CRASHREPORTERTITLE)];
[mHeaderLabel setStringValue:Str(ST_CRASHREPORTERHEADER)];
NSRect viewReportFrame = [mViewReportButton frame];
[mViewReportButton setTitle:Str(ST_VIEWREPORT)];
[mViewReportButton sizeToFit];
if (gRTLlayout) {
// sizeToFit will keep the left side fixed, so realign
float oldWidth = viewReportFrame.size.width;
viewReportFrame = [mViewReportButton frame];
viewReportFrame.origin.x += oldWidth - viewReportFrame.size.width;
[mViewReportButton setFrame:viewReportFrame];
}
[mSubmitReportButton setTitle:Str(ST_CHECKSUBMIT)];
[mIncludeURLButton setTitle:Str(ST_CHECKURL)];
[mViewReportOkButton setTitle:Str(ST_OK)];
[mCommentText setPlaceholder:Str(ST_COMMENTGRAYTEXT)];
if (gRTLlayout) [mCommentText toggleBaseWritingDirection:self];
if (gQueryParameters.isMember("URL")) {
// save the URL value in case the checkbox gets unchecked
gURLParameter = gQueryParameters["URL"].asString();
} else {
// no URL specified, hide checkbox
[mIncludeURLButton removeFromSuperview];
// shrink window to fit
NSRect frame = [mWindow frame];
NSRect includeURLFrame = [mIncludeURLButton frame];
NSRect emailFrame = [mEmailMeButton frame];
int buttonMask = [mViewReportButton autoresizingMask];
int checkMask = [mSubmitReportButton autoresizingMask];
int commentScrollMask = [mCommentScrollView autoresizingMask];
[mViewReportButton setAutoresizingMask:NSViewMinYMargin];
[mSubmitReportButton setAutoresizingMask:NSViewMinYMargin];
[mCommentScrollView setAutoresizingMask:NSViewMinYMargin];
// remove all the space in between
frame.size.height -= includeURLFrame.origin.y - emailFrame.origin.y;
[mWindow setFrame:frame display:true animate:NO];
[mViewReportButton setAutoresizingMask:buttonMask];
[mSubmitReportButton setAutoresizingMask:checkMask];
[mCommentScrollView setAutoresizingMask:commentScrollMask];
}
// resize some buttons horizontally and possibly some controls vertically
[self doInitialResizing];
// load default state of submit checkbox
// we don't just do this via IB because we want the default to be
// off a certain percentage of the time
BOOL submitChecked = YES;
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
if (nil != [userDefaults objectForKey:@"submitReport"]) {
submitChecked = [userDefaults boolForKey:@"submitReport"];
} else {
[userDefaults setBool:submitChecked forKey:@"submitReport"];
}
[mSubmitReportButton setState:(submitChecked ? NSOnState : NSOffState)];
// load default state of include URL checkbox
BOOL includeChecked = YES;
if (nil != [userDefaults objectForKey:@"IncludeURL"]) {
includeChecked = [userDefaults boolForKey:@"IncludeURL"];
} else {
[userDefaults setBool:includeChecked forKey:@"IncludeURL"];
}
[mIncludeURLButton setState:(includeChecked ? NSOnState : NSOffState)];
[self updateSubmit];
[self updateURL];
[self updateEmail];
[mWindow makeKeyAndOrderFront:nil];
}
- (void)showErrorUI:(const string&)message {
[self setView:mErrorView animate:NO];
[mErrorHeaderLabel setStringValue:Str(ST_CRASHREPORTERHEADER)];
[self setStringFitVertically:mErrorLabel
string:NSSTR(message)
resizeWindow:YES];
[mErrorCloseButton setTitle:Str(ST_OK)];
[mErrorCloseButton setKeyEquivalent:@"\r"];
[mWindow makeFirstResponder:mErrorCloseButton];
[mWindow makeKeyAndOrderFront:nil];
}
- (void)showReportInfo {
NSDictionary* boldAttr = @{
NSFontAttributeName :
[NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]],
NSForegroundColorAttributeName : NSColor.textColor,
};
NSDictionary* normalAttr = @{
NSFontAttributeName :
[NSFont systemFontOfSize:[NSFont smallSystemFontSize]],
NSForegroundColorAttributeName : NSColor.textColor,
};
[mViewReportTextView setString:@""];
for (Json::ValueConstIterator iter = gQueryParameters.begin();
iter != gQueryParameters.end(); ++iter) {
NSAttributedString* key =
[[NSAttributedString alloc] initWithString:NSSTR(iter.name() + ": ")
attributes:boldAttr];
string str;
if (iter->isString()) {
str = iter->asString();
} else {
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
str = writeString(builder, *iter);
}
NSAttributedString* value =
[[NSAttributedString alloc] initWithString:NSSTR(str + "\n")
attributes:normalAttr];
[[mViewReportTextView textStorage] appendAttributedString:key];
[[mViewReportTextView textStorage] appendAttributedString:value];
[key release];
[value release];
}
NSAttributedString* extra = [[NSAttributedString alloc]
initWithString:NSSTR("\n" + gStrings[ST_EXTRAREPORTINFO])
attributes:normalAttr];
[[mViewReportTextView textStorage] appendAttributedString:extra];
[extra release];
}
- (void)maybeSubmitReport {
if ([mSubmitReportButton state] == NSOnState) {
[self setStringFitVertically:mProgressText
string:Str(ST_REPORTDURINGSUBMIT)
resizeWindow:YES];
// disable all the controls
[self enableControls:NO];
[mSubmitReportButton setEnabled:NO];
[mRestartButton setEnabled:NO];
[mCloseButton setEnabled:NO];
[mProgressIndicator startAnimation:self];
gDidTrySend = true;
[self sendReport];
} else {
[NSApp terminate:self];
}
}
- (void)closeMeDown:(id)unused {
[NSApp terminate:self];
}
- (IBAction)submitReportClicked:(id)sender {
[self updateSubmit];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:([mSubmitReportButton state] == NSOnState)
forKey:@"submitReport"];
[userDefaults synchronize];
}
- (IBAction)viewReportClicked:(id)sender {
[self showReportInfo];
[NSApp beginSheet:mViewReportWindow
modalForWindow:mWindow
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
}
- (IBAction)viewReportOkClicked:(id)sender {
[mViewReportWindow orderOut:nil];
[NSApp endSheet:mViewReportWindow];
}
- (IBAction)closeClicked:(id)sender {
[self maybeSubmitReport];
}
- (IBAction)restartClicked:(id)sender {
RestartApplication();
[self maybeSubmitReport];
}
- (IBAction)includeURLClicked:(id)sender {
[self updateURL];
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setBool:([mIncludeURLButton state] == NSOnState)
forKey:@"IncludeURL"];
[userDefaults synchronize];
}
- (void)textDidChange:(NSNotification*)aNotification {
// update comment parameter
if ([[[mCommentText textStorage] mutableString] length] > 0)
gQueryParameters["Comments"] =
[[[mCommentText textStorage] mutableString] UTF8String];
else
gQueryParameters.removeMember("Comments");
}
// Limit the comment field to 500 bytes in UTF-8
- (BOOL)textView:(NSTextView*)aTextView
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString*)replacementString {
// current string length + replacement text length - replaced range length
if (([[aTextView string] lengthOfBytesUsingEncoding:NSUTF8StringEncoding] +
[replacementString lengthOfBytesUsingEncoding:NSUTF8StringEncoding] -
[[[aTextView string] substringWithRange:affectedCharRange]
lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) >
MAX_COMMENT_LENGTH) {
return NO;
}
return YES;
}
- (void)doInitialResizing {
NSRect windowFrame = [mWindow frame];
NSRect restartFrame = [mRestartButton frame];
NSRect closeFrame = [mCloseButton frame];
// resize close button to fit text
float oldCloseWidth = closeFrame.size.width;
[mCloseButton setTitle:Str(ST_QUIT)];
[mCloseButton sizeToFit];
closeFrame = [mCloseButton frame];
// move close button left if it grew
if (!gRTLlayout) {
closeFrame.origin.x -= closeFrame.size.width - oldCloseWidth;
}
if (gRestartArgs.size() == 0) {
[mRestartButton removeFromSuperview];
if (!gRTLlayout) {
closeFrame.origin.x = restartFrame.origin.x +
(restartFrame.size.width - closeFrame.size.width);
} else {
closeFrame.origin.x = restartFrame.origin.x;
}
[mCloseButton setFrame:closeFrame];
[mCloseButton setKeyEquivalent:@"\r"];
} else {
[mRestartButton setTitle:Str(ST_RESTART)];
// resize "restart" button
float oldRestartWidth = restartFrame.size.width;
[mRestartButton sizeToFit];
restartFrame = [mRestartButton frame];
if (!gRTLlayout) {
// move left by the amount that the button grew
restartFrame.origin.x -= restartFrame.size.width - oldRestartWidth;
closeFrame.origin.x -= restartFrame.size.width - oldRestartWidth;
} else {
// shift the close button right in RTL
closeFrame.origin.x += restartFrame.size.width - oldRestartWidth;
}
[mRestartButton setFrame:restartFrame];
[mCloseButton setFrame:closeFrame];
// possibly resize window if both buttons no longer fit
// leave 20 px from either side of the window, and 12 px
// between the buttons
float neededWidth =
closeFrame.size.width + restartFrame.size.width + 2 * 20 + 12;
if (neededWidth > windowFrame.size.width) {
windowFrame.size.width = neededWidth;
[mWindow setFrame:windowFrame display:true animate:NO];
}
[mRestartButton setKeyEquivalent:@"\r"];
}
NSButton* checkboxes[] = {mSubmitReportButton, mIncludeURLButton};
for (auto checkbox : checkboxes) {
NSRect frame = [checkbox frame];
[checkbox sizeToFit];
if (gRTLlayout) {
// sizeToFit will keep the left side fixed, so realign
float oldWidth = frame.size.width;
frame = [checkbox frame];
frame.origin.x += oldWidth - frame.size.width;
[checkbox setFrame:frame];
}
// keep existing spacing on left side, + 20 px spare on right
float neededWidth =
frame.origin.x + checkbox.intrinsicContentSize.width + 20;
if (neededWidth > windowFrame.size.width) {
windowFrame.size.width = neededWidth;
[mWindow setFrame:windowFrame display:true animate:NO];
}
}
// do this down here because we may have made the window wider
// up above
[self setStringFitVertically:mDescriptionLabel
string:Str(ST_CRASHREPORTERDESCRIPTION)
resizeWindow:YES];
// now pin all the controls (except quit/submit) in place,
// if we lengthen the window after this, it's just to lengthen
// the progress text, so nothing above that text should move.
NSView* views[] = {mSubmitReportButton, mViewReportButton,
mCommentScrollView, mIncludeURLButton,
mProgressIndicator, mProgressText};
for (auto view : views) {
[view setAutoresizingMask:NSViewMinYMargin];
}
}
- (float)setStringFitVertically:(NSControl*)control
string:(NSString*)str
resizeWindow:(BOOL)resizeWindow {
// hack to make the text field grow vertically
NSRect frame = [control frame];
float oldHeight = frame.size.height;
frame.size.height = 10000;
NSSize oldCellSize = [[control cell] cellSizeForBounds:frame];
[control setStringValue:str];
NSSize newCellSize = [[control cell] cellSizeForBounds:frame];
float delta = newCellSize.height - oldCellSize.height;
frame.origin.y -= delta;
frame.size.height = oldHeight + delta;
[control setFrame:frame];
if (resizeWindow) {
NSRect frame = [mWindow frame];
frame.origin.y -= delta;
frame.size.height += delta;
[mWindow setFrame:frame display:true animate:NO];
}
return delta;
}
- (void)setView:(NSView*)v animate:(BOOL)animate {
NSRect frame = [mWindow frame];
NSRect oldViewFrame = [[mWindow contentView] frame];
NSRect newViewFrame = [v frame];
frame.origin.y += oldViewFrame.size.height - newViewFrame.size.height;
frame.size.height -= oldViewFrame.size.height - newViewFrame.size.height;
frame.origin.x += oldViewFrame.size.width - newViewFrame.size.width;
frame.size.width -= oldViewFrame.size.width - newViewFrame.size.width;
[mWindow setContentView:v];
[mWindow setFrame:frame display:true animate:animate];
}
- (void)enableControls:(BOOL)enabled {
[mViewReportButton setEnabled:enabled];
[mIncludeURLButton setEnabled:enabled];
[mCommentText setEnabled:enabled];
[mCommentScrollView setHasVerticalScroller:enabled];
}
- (void)updateSubmit {
if ([mSubmitReportButton state] == NSOnState) {
[self setStringFitVertically:mProgressText
string:Str(ST_REPORTPRESUBMIT)
resizeWindow:YES];
[mProgressText setHidden:NO];
// enable all the controls
[self enableControls:YES];
} else {
// not submitting, disable all the controls under
// the submit checkbox, and hide the status text
[mProgressText setHidden:YES];
[self enableControls:NO];
}
}
- (void)updateURL {
if ([mIncludeURLButton state] == NSOnState && !gURLParameter.empty()) {
gQueryParameters["URL"] = gURLParameter;
} else {
gQueryParameters.removeMember("URL");
}
}
- (void)updateEmail {
// In order to remove the email fields, we have to edit the .nib files which
// we can't do with current xcode so we make them hidden; updating the
// crashreporter interface for mac is covered in bug #1696164
[mEmailMeButton setHidden:YES];
[mEmailText setHidden:YES];
}
- (void)sendReport {
if (![self setupPost]) {
LogMessage("Crash report submission failed: could not set up POST data");
if (gAutoSubmit) {
[NSApp terminate:self];
}
[self setStringFitVertically:mProgressText
string:Str(ST_SUBMITFAILED)
resizeWindow:YES];
// quit after 5 seconds
[self performSelector:@selector(closeMeDown:)
withObject:nil
afterDelay:5.0];
}
[NSThread detachNewThreadSelector:@selector(uploadThread:)
toTarget:self
withObject:mPost];
}
- (bool)setupPost {
NSURL* url = [NSURL
URLWithString:[NSSTR(gSendURL) stringByAddingPercentEscapesUsingEncoding:
NSUTF8StringEncoding]];
if (!url) return false;
mPost = [[HTTPMultipartUpload alloc] initWithURL:url];
if (!mPost) return false;
for (StringTable::const_iterator i = gFiles.begin(); i != gFiles.end(); i++) {
[mPost addFileAtPath:NSSTR(i->second) name:NSSTR(i->first)];
}
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
string output = writeString(builder, gQueryParameters).append("\r\n");
NSMutableString* parameters =
[[NSMutableString alloc] initWithUTF8String:output.c_str()];
[mPost setParameters:parameters];
[parameters release];
return true;
}
- (void)uploadComplete:(NSData*)data {
NSHTTPURLResponse* response = [mPost response];
[mPost release];
bool success;
string reply;
if (!data || !response || [response statusCode] != 200) {
success = false;
reply = "";
// if data is nil, we probably logged an error in uploadThread
if (data != nil && response != nil) {
ostringstream message;
message << "Crash report submission failed: server returned status "
<< [response statusCode];
LogMessage(message.str());
}
} else {
success = true;
LogMessage("Crash report submitted successfully");
NSString* encodingName = [response textEncodingName];
NSStringEncoding encoding;
if (encodingName) {
encoding = CFStringConvertEncodingToNSStringEncoding(
CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName));
} else {
encoding = NSISOLatin1StringEncoding;
}
NSString* r = [[NSString alloc] initWithData:data encoding:encoding];
reply = [r UTF8String];
[r release];
}
SendCompleted(success, reply);
if (gAutoSubmit) {
[NSApp terminate:self];
}
[mProgressIndicator stopAnimation:self];
if (success) {
[self setStringFitVertically:mProgressText
string:Str(ST_REPORTSUBMITSUCCESS)
resizeWindow:YES];
} else {
[self setStringFitVertically:mProgressText
string:Str(ST_SUBMITFAILED)
resizeWindow:YES];
}
// quit after 5 seconds
[self performSelector:@selector(closeMeDown:) withObject:nil afterDelay:5.0];
}
- (void)uploadThread:(HTTPMultipartUpload*)post {
NSAutoreleasePool* autoreleasepool = [[NSAutoreleasePool alloc] init];
NSError* error = nil;
NSData* data = [post send:&error];
if (error) {
data = nil;
NSString* errorDesc = [error localizedDescription];
string message = [errorDesc UTF8String];
LogMessage("Crash report submission failed: " + message);
}
[self performSelectorOnMainThread:@selector(uploadComplete:)
withObject:data
waitUntilDone:YES];
[autoreleasepool release];
}
// to get auto-quit when we close the window
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:
(NSApplication*)theApplication {
return YES;
}
- (void)applicationWillTerminate:(NSNotification*)aNotification {
// since we use [NSApp terminate:] we never return to main,
// so do our cleanup here
if (!gDidTrySend) DeleteDump();
}
@end
@implementation TextViewWithPlaceHolder
- (BOOL)becomeFirstResponder {
[self setNeedsDisplay:YES];
return [super becomeFirstResponder];
}
- (void)drawRect:(NSRect)rect {
[super drawRect:rect];
if (mPlaceHolderString && [[self string] isEqualToString:@""] &&
self != [[self window] firstResponder])
[mPlaceHolderString drawInRect:[self frame]];
}
- (BOOL)resignFirstResponder {
[self setNeedsDisplay:YES];
return [super resignFirstResponder];
}
- (void)setPlaceholder:(NSString*)placeholder {
NSColor* txtColor = [NSColor disabledControlTextColor];
NSDictionary* txtDict = [NSDictionary
dictionaryWithObjectsAndKeys:txtColor, NSForegroundColorAttributeName,
nil];
mPlaceHolderString =
[[NSMutableAttributedString alloc] initWithString:placeholder
attributes:txtDict];
if (gRTLlayout)
[mPlaceHolderString setAlignment:NSTextAlignmentRight
range:NSMakeRange(0, [placeholder length])];
}
- (void)insertTab:(id)sender {
// don't actually want to insert tabs, just tab to next control
[[self window] selectNextKeyView:sender];
}
- (void)insertBacktab:(id)sender {
[[self window] selectPreviousKeyView:sender];
}
- (void)setEnabled:(BOOL)enabled {
[self setSelectable:enabled];
[self setEditable:enabled];
if (![[self string] isEqualToString:@""]) {
NSAttributedString* colorString;
NSColor* txtColor;
if (enabled)
txtColor = [NSColor textColor];
else
txtColor = [NSColor disabledControlTextColor];
NSDictionary* txtDict = [NSDictionary
dictionaryWithObjectsAndKeys:txtColor, NSForegroundColorAttributeName,
nil];
colorString = [[NSAttributedString alloc] initWithString:[self string]
attributes:txtDict];
[[self textStorage] setAttributedString:colorString];
[self setInsertionPointColor:txtColor];
[colorString release];
}
}
- (void)dealloc {
[mPlaceHolderString release];
[super dealloc];
}
@end
/* === Crashreporter UI Functions === */
bool UIInit() {
gMainPool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
if (gStrings.find("isRTL") != gStrings.end() && gStrings["isRTL"] == "yes")
gRTLlayout = true;
if (gAutoSubmit) {
gUI = [[CrashReporterUI alloc] init];
} else {
[[NSBundle mainBundle]
loadNibNamed:(gRTLlayout ? @"MainMenuRTL" : @"MainMenu")
owner:NSApp
topLevelObjects:nil];
}
return true;
}
void UIShutdown() { [gMainPool release]; }
void UIShowDefaultUI() {
[gUI showErrorUI:gStrings[ST_CRASHREPORTERDEFAULT]];
[NSApp run];
}
bool UIShowCrashUI(const StringTable& files, const Json::Value& queryParameters,
const string& sendURL, const vector<string>& restartArgs) {
gRestartArgs = restartArgs;
[gUI showCrashUI:files queryParameters:queryParameters sendURL:sendURL];
[NSApp run];
return gDidTrySend;
}
void UIError_impl(const string& message) {
if (!gUI) {
// UI failed to initialize, printing is the best we can do
printf("Error: %s\n", message.c_str());
return;
}
[gUI showErrorUI:message];
[NSApp run];
}
bool UIGetIniPath(string& path) {
NSString* tmpPath = [NSString stringWithUTF8String:gArgv[0]];
NSString* iniName = [tmpPath lastPathComponent];
iniName = [iniName stringByAppendingPathExtension:@"ini"];
tmpPath = [tmpPath stringByDeletingLastPathComponent];
tmpPath = [tmpPath stringByDeletingLastPathComponent];
tmpPath = [tmpPath stringByAppendingPathComponent:@"Resources"];
tmpPath = [tmpPath stringByAppendingPathComponent:iniName];
path = [tmpPath UTF8String];
return true;
}
bool UIGetSettingsPath(const string& vendor, const string& product,
string& settingsPath) {
NSArray* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString* destPath = [paths firstObject];
// Note that MacOS ignores the vendor when creating the profile hierarchy -
// all application preferences directories live alongside one another in
// ~/Library/Application Support/
destPath = [destPath stringByAppendingPathComponent:NSSTR(product)];
// Thunderbird stores its profile in ~/Library/Thunderbird,
// but we're going to put stuff in ~/Library/Application Support/Thunderbird
// anyway, so we have to ensure that path exists.
string tempPath = [destPath UTF8String];
if (!UIEnsurePathExists(tempPath)) return false;
destPath = [destPath stringByAppendingPathComponent:@"Crash Reports"];
settingsPath = [destPath UTF8String];
return true;
}
bool UIMoveFile(const string& file, const string& newfile) {
if (!rename(file.c_str(), newfile.c_str())) return true;
if (errno != EXDEV) return false;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSString* source =
[fileManager stringWithFileSystemRepresentation:file.c_str()
length:file.length()];
NSString* dest =
[fileManager stringWithFileSystemRepresentation:newfile.c_str()
length:newfile.length()];
if (!source || !dest) return false;
[fileManager moveItemAtPath:source toPath:dest error:NULL];
return UIFileExists(newfile);
}

Просмотреть файл

@ -1,139 +0,0 @@
/* 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/. */
#include "crashreporter.h"
#include <algorithm>
#include <sys/wait.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
using namespace CrashReporter;
using std::ios_base;
using std::sort;
using std::string;
using std::vector;
struct FileData {
time_t timestamp;
string path;
};
static bool CompareFDTime(const FileData& fd1, const FileData& fd2) {
return fd1.timestamp > fd2.timestamp;
}
void UIPruneSavedDumps(const string& directory) {
DIR* dirfd = opendir(directory.c_str());
if (!dirfd) return;
vector<FileData> dumpfiles;
while (dirent* dir = readdir(dirfd)) {
FileData fd;
fd.path = directory + '/' + dir->d_name;
if (fd.path.size() < 5) continue;
if (fd.path.compare(fd.path.size() - 4, 4, ".dmp") != 0) continue;
struct stat st;
if (stat(fd.path.c_str(), &st)) {
closedir(dirfd);
return;
}
fd.timestamp = st.st_mtime;
dumpfiles.push_back(fd);
}
closedir(dirfd);
sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime);
while (dumpfiles.size() > kSaveCount) {
// get the path of the oldest file
string path = dumpfiles[dumpfiles.size() - 1].path;
UIDeleteFile(path);
// s/.dmp/.extra/
path.replace(path.size() - 4, 4, ".extra");
UIDeleteFile(path);
dumpfiles.pop_back();
}
}
bool UIRunProgram(const string& exename, const vector<string>& args,
bool wait) {
pid_t pid = fork();
if (pid == -1) {
return false;
} else if (pid == 0) {
// Child
size_t argvLen = args.size() + 2;
vector<char*> argv(argvLen);
argv[0] = const_cast<char*>(exename.c_str());
for (size_t i = 0; i < args.size(); i++) {
argv[i + 1] = const_cast<char*>(args[i].c_str());
}
argv[argvLen - 1] = nullptr;
// Run the program
int rv = execv(exename.c_str(), argv.data());
if (rv == -1) {
exit(EXIT_FAILURE);
}
} else {
// Parent
if (wait) {
waitpid(pid, nullptr, 0);
}
}
return true;
}
bool UIEnsurePathExists(const string& path) {
int ret = mkdir(path.c_str(), S_IRWXU);
int e = errno;
if (ret == -1 && e != EEXIST) return false;
return true;
}
bool UIFileExists(const string& path) {
struct stat sb;
int ret = stat(path.c_str(), &sb);
if (ret == -1 || !(sb.st_mode & S_IFREG)) return false;
return true;
}
bool UIDeleteFile(const string& file) { return (unlink(file.c_str()) != -1); }
std::ifstream* UIOpenRead(const string& filename, ios_base::openmode mode) {
return new std::ifstream(filename.c_str(), mode);
}
std::ofstream* UIOpenWrite(const string& filename, ios_base::openmode mode) {
return new std::ofstream(filename.c_str(), mode);
}
string UIGetEnv(const string& name) {
const char* var = getenv(name.c_str());
if (var && *var) {
return var;
}
return "";
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,2 +0,0 @@
APPL????

Просмотреть файл

@ -1,8 +0,0 @@
/* 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/. */
/* Localized versions of Info.plist keys */
CFBundleName = "Crash Reporter";
CFBundleDisplayName = "@APP_NAME@ Crash Reporter";

Просмотреть файл

@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>closeClicked</key>
<string>id</string>
<key>includeURLClicked</key>
<string>id</string>
<key>restartClicked</key>
<string>id</string>
<key>submitReportClicked</key>
<string>id</string>
<key>viewReportClicked</key>
<string>id</string>
<key>viewReportOkClicked</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>CrashReporterUI</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>mCloseButton</key>
<string>NSButton</string>
<key>mCommentScrollView</key>
<string>NSScrollView</string>
<key>mCommentText</key>
<string>TextViewWithPlaceHolder</string>
<key>mDescriptionLabel</key>
<string>NSTextField</string>
<key>mEmailMeButton</key>
<string>NSButton</string>
<key>mEmailText</key>
<string>NSTextField</string>
<key>mErrorCloseButton</key>
<string>NSButton</string>
<key>mErrorHeaderLabel</key>
<string>NSTextField</string>
<key>mErrorLabel</key>
<string>NSTextField</string>
<key>mErrorView</key>
<string>NSView</string>
<key>mHeaderLabel</key>
<string>NSTextField</string>
<key>mIncludeURLButton</key>
<string>NSButton</string>
<key>mProgressIndicator</key>
<string>NSProgressIndicator</string>
<key>mProgressText</key>
<string>NSTextField</string>
<key>mRestartButton</key>
<string>NSButton</string>
<key>mSubmitReportButton</key>
<string>NSButton</string>
<key>mViewReportButton</key>
<string>NSButton</string>
<key>mViewReportOkButton</key>
<string>NSButton</string>
<key>mViewReportTextView</key>
<string>NSTextView</string>
<key>mViewReportWindow</key>
<string>NSWindow</string>
<key>mWindow</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
<dict>
<key>ACTIONS</key>
<dict>
<key>insertTab</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>TextViewWithPlaceHolder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSTextView</string>
</dict>
<dict>
<key>CLASS</key>
<string>FirstResponder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

Просмотреть файл

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>629</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9C7010</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

Двоичный файл не отображается.

Просмотреть файл

@ -1,100 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>closeClicked</key>
<string>id</string>
<key>includeURLClicked</key>
<string>id</string>
<key>restartClicked</key>
<string>id</string>
<key>submitReportClicked</key>
<string>id</string>
<key>viewReportClicked</key>
<string>id</string>
<key>viewReportOkClicked</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>CrashReporterUI</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>mCloseButton</key>
<string>NSButton</string>
<key>mCommentScrollView</key>
<string>NSScrollView</string>
<key>mCommentText</key>
<string>TextViewWithPlaceHolder</string>
<key>mDescriptionLabel</key>
<string>NSTextField</string>
<key>mEmailMeButton</key>
<string>NSButton</string>
<key>mEmailText</key>
<string>NSTextField</string>
<key>mErrorCloseButton</key>
<string>NSButton</string>
<key>mErrorHeaderLabel</key>
<string>NSTextField</string>
<key>mErrorLabel</key>
<string>NSTextField</string>
<key>mErrorView</key>
<string>NSView</string>
<key>mHeaderLabel</key>
<string>NSTextField</string>
<key>mIncludeURLButton</key>
<string>NSButton</string>
<key>mProgressIndicator</key>
<string>NSProgressIndicator</string>
<key>mProgressText</key>
<string>NSTextField</string>
<key>mRestartButton</key>
<string>NSButton</string>
<key>mSubmitReportButton</key>
<string>NSButton</string>
<key>mViewReportButton</key>
<string>NSButton</string>
<key>mViewReportOkButton</key>
<string>NSButton</string>
<key>mViewReportTextView</key>
<string>NSTextView</string>
<key>mViewReportWindow</key>
<string>NSWindow</string>
<key>mWindow</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
<dict>
<key>ACTIONS</key>
<dict>
<key>insertTab</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>TextViewWithPlaceHolder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSTextView</string>
</dict>
<dict>
<key>CLASS</key>
<string>FirstResponder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

Просмотреть файл

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>629</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9D34</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -1,97 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
if CONFIG["OS_TARGET"] != "Android":
Program("crashreporter")
UNIFIED_SOURCES += [
"../CrashAnnotations.cpp",
"crashreporter.cpp",
"ping.cpp",
]
LOCAL_INCLUDES += [
"/toolkit/components/jsoncpp/include",
]
USE_LIBS += [
"jsoncpp",
]
if CONFIG["OS_ARCH"] == "WINNT":
UNIFIED_SOURCES += [
"crashreporter_win.cpp",
]
include("/toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild")
SOURCES += objs_sender
SOURCES += [
"../google-breakpad/src/common/windows/http_upload.cc",
]
DEFINES["UNICODE"] = True
DEFINES["_UNICODE"] = True
USE_LIBS += [
"nss",
]
OS_LIBS += [
"advapi32",
"comctl32",
"gdi32",
"ole32",
"shell32",
"wininet",
"shlwapi",
"user32",
]
elif CONFIG["OS_ARCH"] == "Darwin":
UNIFIED_SOURCES += [
"../google-breakpad/src/common/mac/HTTPMultipartUpload.m",
"crashreporter_osx.mm",
"crashreporter_unix_common.cpp",
]
LOCAL_INCLUDES += [
"../google-breakpad/src/common/mac",
]
OS_LIBS += ["-framework Cocoa"]
USE_LIBS += [
"nss",
]
LDFLAGS += ["-Wl,-rpath,@executable_path/../../../"]
elif CONFIG["OS_ARCH"] == "SunOS":
SOURCES += [
"crashreporter_linux.cpp",
"crashreporter_unix.cpp",
]
USE_LIBS += [
"breakpad_solaris_common_s",
]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
UNIFIED_SOURCES += [
"../google-breakpad/src/common/linux/http_upload.cc",
"crashreporter_gtk_common.cpp",
"crashreporter_linux.cpp",
"crashreporter_unix_common.cpp",
]
OS_LIBS += CONFIG["MOZ_GTK3_LIBS"]
OS_LIBS += CONFIG["MOZ_GTHREAD_LIBS"]
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
CXXFLAGS += CONFIG["MOZ_GTHREAD_CFLAGS"]
if CONFIG["OS_ARCH"] == "Linux" or CONFIG["OS_ARCH"] == "SunOS":
FINAL_TARGET_FILES += [
"Throbber-small.gif",
]
DEFINES["MOZ_APP_NAME"] = '"%s"' % CONFIG["MOZ_APP_NAME"]
DEFINES["BIN_SUFFIX"] = '"%s"' % CONFIG["BIN_SUFFIX"]
RCINCLUDE = "crashreporter.rc"
# Don't use the STL wrappers in the crashreporter clients; they don't
# link with -lmozalloc, and it really doesn't matter here anyway.
DisableStlWrapping()
include("/toolkit/crashreporter/crashreporter.mozbuild")

Просмотреть файл

@ -1,324 +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/. */
#include "crashreporter.h"
#include <cstring>
#include <ctime>
#include <string>
#if defined(XP_LINUX)
# include <fcntl.h>
# include <unistd.h>
# include <sys/stat.h>
#elif defined(XP_MACOSX)
# include <CoreFoundation/CoreFoundation.h>
#elif defined(XP_WIN)
# include <objbase.h>
#endif
#include "json/json.h"
#include "CrashAnnotations.h"
using std::string;
namespace CrashReporter {
struct UUID {
uint32_t m0;
uint16_t m1;
uint16_t m2;
uint8_t m3[8];
};
// Generates an UUID; the code here is mostly copied from nsUUIDGenerator.cpp
static string GenerateUUID() {
UUID id = {};
#if defined(XP_WIN) // Windows
HRESULT hr = CoCreateGuid((GUID*)&id);
if (FAILED(hr)) {
return "";
}
#elif defined(XP_MACOSX) // MacOS X
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
if (!uuid) {
return "";
}
CFUUIDBytes bytes = CFUUIDGetUUIDBytes(uuid);
memcpy(&id, &bytes, sizeof(UUID));
CFRelease(uuid);
#elif defined(HAVE_ARC4RANDOM_BUF) // Android, BSD, ...
arc4random_buf(&id, sizeof(UUID));
#else // Linux
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
return "";
}
if (read(fd, &id, sizeof(UUID)) != sizeof(UUID)) {
close(fd);
return "";
}
close(fd);
#endif
/* Put in the version */
id.m2 &= 0x0fff;
id.m2 |= 0x4000;
/* Put in the variant */
id.m3[0] &= 0x3f;
id.m3[0] |= 0x80;
const char* kUUIDFormatString =
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
const size_t kUUIDFormatStringLength = 36;
char str[kUUIDFormatStringLength + 1] = {'\0'};
int num = snprintf(str, kUUIDFormatStringLength + 1, kUUIDFormatString, id.m0,
id.m1, id.m2, id.m3[0], id.m3[1], id.m3[2], id.m3[3],
id.m3[4], id.m3[5], id.m3[6], id.m3[7]);
if (num != kUUIDFormatStringLength) {
return "";
}
return str;
}
const char kISO8601Date[] = "%F";
const char kISO8601DateHours[] = "%FT%H:00:00.000Z";
// Return the current date as a string in the specified format, the following
// constants are provided:
// - kISO8601Date, the ISO 8601 date format, YYYY-MM-DD
// - kISO8601DateHours, the ISO 8601 full date format, YYYY-MM-DDTHH:00:00.000Z
static string CurrentDate(string format) {
time_t now;
time(&now);
char buf[64]; // This should be plenty
strftime(buf, sizeof buf, format.c_str(), gmtime(&now));
return buf;
}
const char kTelemetryClientId[] = "TelemetryClientId";
const char kTelemetryUrl[] = "TelemetryServerURL";
const char kTelemetrySessionId[] = "TelemetrySessionId";
const int kTelemetryVersion = 4;
// Create the payload.metadata node of the crash ping using fields extracted
// from the .extra file
static Json::Value CreateMetadataNode(const Json::Value& aExtra) {
Json::Value node;
for (Json::ValueConstIterator iter = aExtra.begin(); iter != aExtra.end();
++iter) {
Annotation annotation;
if (AnnotationFromString(annotation, iter.memberName())) {
if (IsAnnotationAllowedForPing(annotation)) {
node[iter.memberName()] = *iter;
}
}
}
return node;
}
// Create the payload node of the crash ping
static Json::Value CreatePayloadNode(const Json::Value& aExtra,
const string& aHash,
const string& aSessionId) {
Json::Value payload;
payload["sessionId"] = aSessionId;
payload["version"] = 1;
payload["crashDate"] = CurrentDate(kISO8601Date);
payload["crashTime"] = CurrentDate(kISO8601DateHours);
payload["hasCrashEnvironment"] = true;
payload["crashId"] = CrashReporter::GetDumpLocalID();
payload["minidumpSha256Hash"] = aHash;
payload["processType"] = "main"; // This is always a main crash
if (aExtra.isMember("StackTraces")) {
payload["stackTraces"] = aExtra["StackTraces"];
}
// Assemble the payload metadata
payload["metadata"] = CreateMetadataNode(aExtra);
return payload;
}
// Create the application node of the crash ping
static Json::Value CreateApplicationNode(
const string& aVendor, const string& aName, const string& aVersion,
const string& aDisplayVersion, const string& aPlatformVersion,
const string& aChannel, const string& aBuildId, const string& aArchitecture,
const string& aXpcomAbi) {
Json::Value application;
application["vendor"] = aVendor;
application["name"] = aName;
application["buildId"] = aBuildId;
application["displayVersion"] = aDisplayVersion;
application["platformVersion"] = aPlatformVersion;
application["version"] = aVersion;
application["channel"] = aChannel;
if (!aArchitecture.empty()) {
application["architecture"] = aArchitecture;
}
if (!aXpcomAbi.empty()) {
application["xpcomAbi"] = aXpcomAbi;
}
return application;
}
// Create the root node of the crash ping
static Json::Value CreateRootNode(
const Json::Value& aExtra, const string& aUuid, const string& aHash,
const string& aClientId, const string& aSessionId, const string& aName,
const string& aVersion, const string& aChannel, const string& aBuildId) {
Json::Value root;
root["type"] = "crash"; // This is a crash ping
root["id"] = aUuid;
root["version"] = kTelemetryVersion;
root["creationDate"] = CurrentDate(kISO8601DateHours);
root["clientId"] = aClientId;
// Parse the telemetry environment
Json::Value environment;
Json::Reader reader;
string architecture;
string xpcomAbi;
string displayVersion;
string platformVersion;
if (reader.parse(aExtra["TelemetryEnvironment"].asString(), environment,
/* collectComments */ false)) {
if (environment.isMember("build") && environment["build"].isObject()) {
Json::Value build = environment["build"];
if (build.isMember("architecture") && build["architecture"].isString()) {
architecture = build["architecture"].asString();
}
if (build.isMember("xpcomAbi") && build["xpcomAbi"].isString()) {
xpcomAbi = build["xpcomAbi"].asString();
}
if (build.isMember("displayVersion") &&
build["displayVersion"].isString()) {
displayVersion = build["displayVersion"].asString();
}
if (build.isMember("platformVersion") &&
build["platformVersion"].isString()) {
platformVersion = build["platformVersion"].asString();
}
}
root["environment"] = environment;
}
root["payload"] = CreatePayloadNode(aExtra, aHash, aSessionId);
root["application"] = CreateApplicationNode(
aExtra["Vendor"].asString(), aName, aVersion, displayVersion,
platformVersion, aChannel, aBuildId, architecture, xpcomAbi);
return root;
}
// Generates the URL used to submit the crash ping, see TelemetrySend.sys.mjs
string GenerateSubmissionUrl(const string& aUrl, const string& aId,
const string& aName, const string& aVersion,
const string& aChannel, const string& aBuildId) {
return aUrl + "/submit/telemetry/" + aId + "/crash/" + aName + "/" +
aVersion + "/" + aChannel + "/" + aBuildId +
"?v=" + std::to_string(kTelemetryVersion);
}
// Write out the ping into the specified file.
//
// Returns true if the ping was written out successfully, false otherwise.
static bool WritePing(const string& aPath, const string& aPing) {
std::ofstream* f = UIOpenWrite(aPath, std::ios::trunc);
bool success = false;
if (f->is_open()) {
*f << aPing;
f->close();
success = f->good();
}
delete f;
return success;
}
// Assembles the crash ping using the JSON data extracted from the .extra file
// and sends it using the crash sender. All the telemetry specific data but the
// environment will be stripped from the annotations so that it won't be sent
// together with the crash report.
//
// Note that the crash ping sender is invoked in a fire-and-forget way so this
// won't block waiting for the ping to be delivered.
//
// Returns true if the ping was assembled and handed over to the pingsender
// correctly, also populates the aPingUuid parameter with the ping UUID. Returns
// false otherwise and leaves the aPingUuid parameter unmodified.
bool SendCrashPing(Json::Value& aExtra, const string& aHash, string& aPingUuid,
const string& pingDir) {
// Remove the telemetry-related data from the crash annotations
Json::Value value;
aExtra.removeMember(kTelemetryClientId, &value);
string clientId = value.asString();
aExtra.removeMember(kTelemetryUrl, &value);
string serverUrl = value.asString();
aExtra.removeMember(kTelemetrySessionId, &value);
string sessionId = value.asString();
if (clientId.empty() || serverUrl.empty() || sessionId.empty()) {
return false;
}
string buildId = aExtra["BuildID"].asString();
string channel = aExtra["ReleaseChannel"].asString();
string name = aExtra["ProductName"].asString();
string version = aExtra["Version"].asString();
string uuid = GenerateUUID();
string url =
GenerateSubmissionUrl(serverUrl, uuid, name, version, channel, buildId);
if (serverUrl.empty() || uuid.empty()) {
return false;
}
Json::Value root = CreateRootNode(aExtra, uuid, aHash, clientId, sessionId,
name, version, channel, buildId);
// Write out the result to the pending pings directory
Json::StreamWriterBuilder builder;
builder["indentation"] = "";
string ping = Json::writeString(builder, root);
string pingPath = pingDir + UI_DIR_SEPARATOR + uuid + ".json";
if (!WritePing(pingPath, ping)) {
return false;
}
// Hand over the ping to the sender
std::vector<string> args = {url, pingPath};
if (UIRunProgram(CrashReporter::GetProgramPath(UI_PING_SENDER_FILENAME),
args)) {
aPingUuid = uuid;
return true;
} else {
return false;
}
}
} // namespace CrashReporter

Просмотреть файл

@ -1,35 +0,0 @@
/* 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/. */
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by crashreporter.rc
//
#define IDD_SENDDIALOG 102
#define IDR_THROBBER 103
#define IDD_VIEWREPORTDIALOG 104
#define IDI_MAINICON 105
#define IDC_PROGRESS 1003
#define IDC_DESCRIPTIONTEXT 1004
#define IDC_CLOSEBUTTON 1005
#define IDC_VIEWREPORTBUTTON 1006
#define IDC_SUBMITREPORTCHECK 1007
#define IDC_INCLUDEURLCHECK 1010
#define IDC_COMMENTTEXT 1011
#define IDC_RESTARTBUTTON 1012
#define IDC_DESCRIPTIONLABEL 1013
#define IDC_PROGRESSTEXT 1014
#define IDC_THROBBER 1015
#define IDC_VIEWREPORTTEXT 1016
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
# ifndef APSTUDIO_READONLY_SYMBOLS
# define _APS_NEXT_RESOURCE_VALUE 106
# define _APS_NEXT_COMMAND_VALUE 40001
# define _APS_NEXT_CONTROL_VALUE 1017
# define _APS_NEXT_SYMED_VALUE 101
# endif
#endif

Просмотреть файл

@ -68,7 +68,7 @@ if CONFIG["MOZ_CRASHREPORTER"]:
if CONFIG["OS_TARGET"] != "Android":
DIRS += [
"client-rust/app",
"client/app",
"minidump-analyzer",
]