From d75dd0bc03248b76a48521973d94527ee44c4119 Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Wed, 7 Apr 2010 13:06:17 -0400 Subject: [PATCH] bug 546538 - crash reporter ui for maemo. r=mfinkle --HG-- rename : toolkit/crashreporter/client/crashreporter_linux.cpp => toolkit/crashreporter/client/crashreporter_gtk_common.cpp rename : toolkit/crashreporter/client/crashreporter_unix.cpp => toolkit/crashreporter/client/crashreporter_unix_common.cpp extra : rebase_source : 544ea2a4956ca55b094c0549b7474c135baa70c3 --- toolkit/crashreporter/client/Makefile.in | 10 +- .../client/crashreporter_gtk_common.cpp | 460 ++++++++++++++++++ .../client/crashreporter_gtk_common.h | 44 ++ .../client/crashreporter_linux.cpp | 432 +--------------- .../client/crashreporter_maemo_gtk.cpp | 267 ++++++++++ ...unix.cpp => crashreporter_unix_common.cpp} | 0 6 files changed, 803 insertions(+), 410 deletions(-) create mode 100644 toolkit/crashreporter/client/crashreporter_gtk_common.cpp create mode 100644 toolkit/crashreporter/client/crashreporter_gtk_common.h create mode 100644 toolkit/crashreporter/client/crashreporter_maemo_gtk.cpp rename toolkit/crashreporter/client/{crashreporter_unix.cpp => crashreporter_unix_common.cpp} (100%) diff --git a/toolkit/crashreporter/client/Makefile.in b/toolkit/crashreporter/client/Makefile.in index f62d3b32cbf..7f0d7a54b3b 100644 --- a/toolkit/crashreporter/client/Makefile.in +++ b/toolkit/crashreporter/client/Makefile.in @@ -69,7 +69,7 @@ MOZ_WINCONSOLE = 0 endif ifeq ($(OS_ARCH),Darwin) -CPPSRCS += crashreporter_unix.cpp +CPPSRCS += crashreporter_unix_common.cpp CMMSRCS += crashreporter_osx.mm OS_LIBS += -framework Cocoa LIBS += \ @@ -81,7 +81,13 @@ LOCAL_INCLUDES += -I$(srcdir) -I$(srcdir)/../google-breakpad/src/common/mac/ endif ifeq ($(OS_ARCH),Linux) -CPPSRCS += crashreporter_linux.cpp crashreporter_unix.cpp +CPPSRCS += crashreporter_gtk_common.cpp crashreporter_unix_common.cpp +ifdef MOZ_PLATFORM_MAEMO +CPPSRCS += crashreporter_maemo_gtk.cpp +else +CPPSRCS += crashreporter_linux.cpp +endif + LIBS += \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \ $(NULL) diff --git a/toolkit/crashreporter/client/crashreporter_gtk_common.cpp b/toolkit/crashreporter/client/crashreporter_gtk_common.cpp new file mode 100644 index 00000000000..b2cadd3562b --- /dev/null +++ b/toolkit/crashreporter/client/crashreporter_gtk_common.cpp @@ -0,0 +1,460 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Toolkit Crash Reporter. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ted Mielczarek + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "crashreporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common/linux/http_upload.h" +#include "crashreporter.h" +#include "crashreporter_gtk_common.h" + +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; +string gDumpFile; +StringTable gQueryParameters; +string gHttpProxy; +string gAuth; +string gSendURL; +string gURLParameter; +vector gRestartArgs; +GThread* gSendThreadID; + +// From crashreporter_linux.cpp or crashreporter_maemo_gtk.cpp +void SaveSettings(); +void SendReport(); +void TryInitGnome(); +void UpdateSubmit(); + +static bool RestartApplication() +{ + char** argv = reinterpret_cast( + 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) + return false; + else if (pid == 0) { + (void)execv(argv[0], argv); + _exit(1); + } + + free(argv); + + return true; +} + +// Quit the app, used as a timeout callback +static gboolean CloseApp(gpointer data) +{ + gtk_main_quit(); + g_thread_join(gSendThreadID); + return FALSE; +} + +static gboolean ReportCompleted(gpointer success) +{ + gtk_widget_hide_all(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; +} + +#ifdef MOZ_ENABLE_GCONF +#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", NULL)) { + gint port; + gchar *host = NULL, *httpproxy = NULL; + + host = gconf_client_get_string(conf, HTTP_PROXY_DIR "/host", NULL); + port = gconf_client_get_int(conf, HTTP_PROXY_DIR "/port", NULL); + + 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", NULL)) { + gchar *user, *password, *auth = NULL; + + user = gconf_client_get_string(conf, + HTTP_PROXY_DIR "/authentication_user", + NULL); + password = gconf_client_get_string(conf, + HTTP_PROXY_DIR + "/authentication_password", + NULL); + + 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(). +} +#endif + +gpointer SendThread(gpointer args) +{ + string response, error; + + bool success = google_breakpad::HTTPUpload::SendRequest + (gSendURL, + gQueryParameters, + gDumpFile, + "upload_file_minidump", + gHttpProxy, gAuth, + &response, + &error); + if (success) { + LogMessage("Crash report submitted successfully"); + } + else { + LogMessage("Crash report submission failed: " + error); + } + + SendCompleted(success, response); + // 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 NULL; +} + +gboolean WindowDeleted(GtkWidget* window, + GdkEvent* event, + gpointer userData) +{ + SaveSettings(); + gtk_main_quit(); + return TRUE; +} + +static void MaybeSubmitReport() +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) { + gDidTrySend = true; + SendReport(); + } 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.erase("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(NULL); + + 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); + +#ifndef MOZ_PLATFORM_MAEMO + TryInitGnome(); +#endif + return true; + } + + return false; +} + +void UIShowDefaultUI() +{ + GtkWidget* errorDialog = + gtk_message_dialog_new(NULL, 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(NULL, 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 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 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] = { + "mv", + strdup(file.c_str()), + strdup(newfile.c_str()), + 0 + }; + if (args[1] && args[2]) + execve("/bin/mv", args, 0); + if (args[1]) + free(args[1]); + if (args[2]) + free(args[2]); + exit(-1); + } + int status; + waitpid(pID, &status, 0); + return UIFileExists(newfile); +} + +bool UIDeleteFile(const string& file) +{ + return (unlink(file.c_str()) != -1); +} + +std::ifstream* UIOpenRead(const string& filename) +{ + return new std::ifstream(filename.c_str(), std::ios::in); +} + +std::ofstream* UIOpenWrite(const string& filename, bool append) // append=false +{ + return new std::ofstream(filename.c_str(), + append ? std::ios::out | std::ios::app + : std::ios::out); +} diff --git a/toolkit/crashreporter/client/crashreporter_gtk_common.h b/toolkit/crashreporter/client/crashreporter_gtk_common.h new file mode 100644 index 00000000000..3752e6e1688 --- /dev/null +++ b/toolkit/crashreporter/client/crashreporter_gtk_common.h @@ -0,0 +1,44 @@ +#ifndef CRASHREPORTER_GTK_COMMON_H__ +#define CRASHREPORTER_GTK_COMMON_H__ + +#include +#include + +#include +#include + +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 gRestartArgs; +extern GThread* gSendThreadID; + +extern bool gInitialized; +extern bool gDidTrySend; +extern std::string gDumpFile; +extern StringTable gQueryParameters; +extern std::string gHttpProxy; +extern std::string gAuth; +extern std::string gSendURL; +extern std::string gURLParameter; + +void LoadProxyinfo(); +gpointer SendThread(gpointer args); +gboolean WindowDeleted(GtkWidget* window, + GdkEvent* event, + gpointer userData); +void SubmitReportChecked(GtkButton* sender, gpointer userData); +void IncludeURLClicked(GtkButton* sender, gpointer userData); +void CloseClicked(GtkButton* button, + gpointer userData); +void RestartClicked(GtkButton* button, + gpointer userData); + +#endif // CRASHREPORTER_GTK_COMMON_H__ diff --git a/toolkit/crashreporter/client/crashreporter_linux.cpp b/toolkit/crashreporter/client/crashreporter_linux.cpp index 95062745747..e2e7afa0c08 100644 --- a/toolkit/crashreporter/client/crashreporter_linux.cpp +++ b/toolkit/crashreporter/client/crashreporter_linux.cpp @@ -35,67 +35,35 @@ * * ***** END LICENSE BLOCK ***** */ -#include "crashreporter.h" - -#include -#include -#include -#include -#include -#include -#include #include - -#include -#include - -#include - -#include +#include #include +#include #include -#include "common/linux/http_upload.h" +#include + +#include "crashreporter.h" +#include "crashreporter_gtk_common.h" using std::string; using std::vector; using namespace CrashReporter; -static GtkWidget* gWindow = 0; -static GtkWidget* gSubmitReportCheck = 0; static GtkWidget* gViewReportButton = 0; static GtkWidget* gCommentText = 0; -static GtkWidget* gIncludeURLCheck = 0; static GtkWidget* gEmailMeCheck = 0; static GtkWidget* gEmailEntry = 0; -static GtkWidget* gThrobber = 0; -static GtkWidget* gProgressLabel = 0; -static GtkWidget* gCloseButton = 0; -static GtkWidget* gRestartButton = 0; - -static bool gInitialized = false; -static bool gDidTrySend = false; -static string gDumpFile; -static StringTable gQueryParameters; -static string gSendURL; -static string gHttpProxy; -static string gAuth; -static vector gRestartArgs; -static string gURLParameter; static bool gEmailFieldHint = true; static bool gCommentFieldHint = true; -static GThread* gSendThreadID; - // handle from dlopen'ing libgnome static void* gnomeLib = NULL; // handle from dlopen'ing libgnomeui static void* gnomeuiLib = NULL; -static const char kIniFile[] = "crashreporter.ini"; - static void LoadSettings() { /* @@ -128,7 +96,7 @@ static void LoadSettings() } } -static void SaveSettings() +void SaveSettings() { /* * NOTE! This code needs to stay in sync with the preference setting @@ -157,159 +125,7 @@ static void SaveSettings() "Crash Reporter", settings, true); } -static bool RestartApplication() -{ - char** argv = reinterpret_cast( - 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) - return false; - else if (pid == 0) { - (void)execv(argv[0], argv); - _exit(1); - } - - free(argv); - - return true; -} - -// Quit the app, used as a timeout callback -static gboolean CloseApp(gpointer data) -{ - gtk_main_quit(); - g_thread_join(gSendThreadID); - return FALSE; -} - -static gboolean ReportCompleted(gpointer success) -{ - gtk_widget_hide_all(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; -} - -#ifdef MOZ_ENABLE_GCONF -#define HTTP_PROXY_DIR "/system/http_proxy" - -static 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", NULL)) { - gint port; - gchar *host = NULL, *httpproxy = NULL; - - host = gconf_client_get_string(conf, HTTP_PROXY_DIR "/host", NULL); - port = gconf_client_get_int(conf, HTTP_PROXY_DIR "/port", NULL); - - 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", NULL)) { - gchar *user, *password, *auth = NULL; - - user = gconf_client_get_string(conf, - HTTP_PROXY_DIR "/authentication_user", - NULL); - password = gconf_client_get_string(conf, - HTTP_PROXY_DIR - "/authentication_password", - NULL); - - 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(). -} -#endif - -static gpointer SendThread(gpointer args) -{ - string response, error; - - bool success = google_breakpad::HTTPUpload::SendRequest - (gSendURL, - gQueryParameters, - gDumpFile, - "upload_file_minidump", - gHttpProxy, gAuth, - &response, - &error); - if (success) { - LogMessage("Crash report submitted successfully"); - } - else { - LogMessage("Crash report submission failed: " + error); - } - - SendCompleted(success, response); - // 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 NULL; -} - -static void SendReport() +void SendReport() { // disable all our gui controls, show the throbber + change the progress text gtk_widget_set_sensitive(gSubmitReportCheck, FALSE); @@ -360,41 +176,7 @@ static void ShowReportInfo(GtkTextView* viewReportTextView) gStrings[ST_EXTRAREPORTINFO].c_str(), -1); } -static gboolean WindowDeleted(GtkWidget* window, - GdkEvent* event, - gpointer userData) -{ - SaveSettings(); - gtk_main_quit(); - return TRUE; -} - -static void MaybeSubmitReport() -{ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) { - gDidTrySend = true; - SendReport(); - } else { - gtk_main_quit(); - } -} - -static void CloseClicked(GtkButton* button, - gpointer userData) -{ - SaveSettings(); - MaybeSubmitReport(); -} - -static void RestartClicked(GtkButton* button, - gpointer userData) -{ - SaveSettings(); - RestartApplication(); - MaybeSubmitReport(); -} - -static void UpdateSubmit() +void UpdateSubmit() { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) { gtk_widget_set_sensitive(gViewReportButton, TRUE); @@ -417,11 +199,6 @@ static void UpdateSubmit() } } -static void SubmitReportChecked(GtkButton* sender, gpointer userData) -{ - UpdateSubmit(); -} - static void ViewReportClicked(GtkButton* button, gpointer userData) { @@ -542,15 +319,6 @@ static gboolean CommentFocusChange(GtkWidget* widget, GdkEventFocus* event, return FALSE; } -static void UpdateURL() -{ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))) { - gQueryParameters["URL"] = gURLParameter; - } else { - gQueryParameters.erase("URL"); - } -} - static void UpdateEmail() { const char* email = gtk_entry_get_text(GTK_ENTRY(gEmailEntry)); @@ -566,11 +334,6 @@ static void UpdateEmail() gQueryParameters["Email"] = email; } -static void IncludeURLClicked(GtkButton* sender, gpointer userData) -{ - UpdateURL(); -} - static void EmailMeClicked(GtkButton* sender, gpointer userData) { UpdateEmail(); @@ -597,7 +360,7 @@ typedef GnomeProgram * (*_gnome_program_init_fn)(const char *, const char *, char **, const char *, ...); typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)(); -static void TryInitGnome() +void TryInitGnome() { gnomeLib = dlopen("libgnome-2.so.0", RTLD_LAZY); if (!gnomeLib) @@ -621,29 +384,20 @@ static void TryInitGnome() /* === 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(NULL); - - 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); - - TryInitGnome(); - return true; - } - - return false; -} +/* + * Anything not listed here is in crashreporter_gtk_common.cpp: + * UIInit + * UIShowDefaultUI + * UIError_impl + * UIGetIniPath + * UIGetSettingsPath + * UIEnsurePathExists + * UIFileExists + * UIMoveFile + * UIDeleteFile + * UIOpenRead + * UIOpenWrite + */ void UIShutdown() { @@ -652,19 +406,6 @@ void UIShutdown() // Don't dlclose gnomeLib as libgnomevfs and libORBit-2 use atexit(). } -void UIShowDefaultUI() -{ - GtkWidget* errorDialog = - gtk_message_dialog_new(NULL, 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)); -} - bool UIShowCrashUI(const string& dumpfile, const StringTable& queryParameters, const string& sendURL, @@ -842,128 +583,3 @@ bool UIShowCrashUI(const string& dumpfile, return gDidTrySend; } - -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(NULL, 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 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 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] = { - "mv", - strdup(file.c_str()), - strdup(newfile.c_str()), - 0 - }; - if (args[1] && args[2]) - execve("/bin/mv", args, 0); - if (args[1]) - free(args[1]); - if (args[2]) - free(args[2]); - exit(-1); - } - int status; - waitpid(pID, &status, 0); - return UIFileExists(newfile); -} - -bool UIDeleteFile(const string& file) -{ - return (unlink(file.c_str()) != -1); -} - -std::ifstream* UIOpenRead(const string& filename) -{ - return new std::ifstream(filename.c_str(), std::ios::in); -} - -std::ofstream* UIOpenWrite(const string& filename, bool append) // append=false -{ - return new std::ofstream(filename.c_str(), - append ? std::ios::out | std::ios::app - : std::ios::out); -} diff --git a/toolkit/crashreporter/client/crashreporter_maemo_gtk.cpp b/toolkit/crashreporter/client/crashreporter_maemo_gtk.cpp new file mode 100644 index 00000000000..29170b6afe9 --- /dev/null +++ b/toolkit/crashreporter/client/crashreporter_maemo_gtk.cpp @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Toolkit Crash Reporter. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ted Mielczarek + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include +#include +#include + +#include + +#include "crashreporter.h" +#include "crashreporter_gtk_common.h" + +using std::string; +using std::vector; + +using namespace CrashReporter; + +void LoadSettings() +{ + /* + * NOTE! This code needs to stay in sync with the preference checking + * code in in nsExceptionHandler.cpp. + */ + + //XXX: share with desktop linux code? + StringTable settings; + if (ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true)) { + if (settings.find("IncludeURL") != settings.end() && + gIncludeURLCheck != 0) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck), + settings["IncludeURL"][0] != '0'); + } + bool enabled; + if (settings.find("SubmitReport") != settings.end()) + enabled = settings["SubmitReport"][0] != '0'; + else + enabled = ShouldEnableSending(); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck), + enabled); + } +} + +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() +{ + // disable all our gui controls, show the throbber + change the progress text + gtk_widget_set_sensitive(gSubmitReportCheck, 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()); + +#ifdef MOZ_ENABLE_GCONF + LoadProxyinfo(); +#endif + + // and spawn a thread to do the sending + GError* err; + gSendThreadID = g_thread_create(SendThread, NULL, TRUE, &err); +} + +void UpdateSubmit() +{ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) { + if (gIncludeURLCheck) + gtk_widget_set_sensitive(gIncludeURLCheck, TRUE); + gtk_label_set_text(GTK_LABEL(gProgressLabel), + gStrings[ST_REPORTPRESUBMIT].c_str()); + } else { + if (gIncludeURLCheck) + gtk_widget_set_sensitive(gIncludeURLCheck, FALSE); + gtk_label_set_text(GTK_LABEL(gProgressLabel), ""); + } +} + +/* === 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() +{ +} + +bool UIShowCrashUI(const string& dumpfile, + const StringTable& queryParameters, + const string& sendURL, + const vector& restartArgs) +{ + gDumpFile = dumpfile; + gQueryParameters = queryParameters; + gSendURL = sendURL; + gRestartArgs = restartArgs; + if (gQueryParameters.find("URL") != gQueryParameters.end()) + gURLParameter = gQueryParameters["URL"]; + + 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); + + 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("%s", + 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_widget_set_size_request(descriptionLabel, -1, -1); + 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); + + gSubmitReportCheck = + gtk_check_button_new_with_label(gStrings[ST_CHECKSUBMIT].c_str()); + + gtk_box_pack_start(GTK_BOX(vbox), gSubmitReportCheck, FALSE, FALSE, 0); + g_signal_connect(gSubmitReportCheck, "clicked", + G_CALLBACK(SubmitReportChecked), 0); + + if (gQueryParameters.find("URL") != gQueryParameters.end()) { + gIncludeURLCheck = + gtk_check_button_new_with_label(gStrings[ST_CHECKURL].c_str()); + gtk_box_pack_start(GTK_BOX(vbox), 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", NULL); + 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_widget_set_size_request(gProgressLabel, 500, -1); + 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_FLAGS(gCloseButton, GTK_CAN_DEFAULT); + g_signal_connect(gCloseButton, "clicked", G_CALLBACK(CloseClicked), 0); + + gRestartButton = 0; + if (restartArgs.size() > 0) { + 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_FLAGS(gRestartButton, GTK_CAN_DEFAULT); + g_signal_connect(gRestartButton, "clicked", G_CALLBACK(RestartClicked), 0); + } + + gtk_widget_grab_focus(gSubmitReportCheck); + + gtk_widget_grab_default(gRestartButton ? gRestartButton : gCloseButton); + + LoadSettings(); + + UpdateSubmit(); + + gtk_widget_show_all(gWindow); + // stick this here to avoid the show_all above... + gtk_widget_hide_all(gThrobber); + + gtk_main(); + + return gDidTrySend; +} diff --git a/toolkit/crashreporter/client/crashreporter_unix.cpp b/toolkit/crashreporter/client/crashreporter_unix_common.cpp similarity index 100% rename from toolkit/crashreporter/client/crashreporter_unix.cpp rename to toolkit/crashreporter/client/crashreporter_unix_common.cpp