Bug 540532 - allow setting report submission preference via XPCOM. r=gavin, r=ted
authorJustin Dolske <dolske@mozilla.com>
Tue, 09 Feb 2010 17:05:31 -0800
changeset 38030 b8586e71aa93c335defe7f639aade874c95ed2ca
parent 38029 a435d40af2da12a7a5e031542f41defcc1dcd476
child 38031 70257fee51dfd7dfd6022f27ac3a4bf458a6ead5
push idunknown
push userunknown
push dateunknown
reviewersgavin, ted
bugs540532
milestone1.9.3a2pre
Bug 540532 - allow setting report submission preference via XPCOM. r=gavin, r=ted
browser/components/preferences/advanced.js
browser/components/preferences/advanced.xul
browser/locales/en-US/chrome/browser/preferences/advanced.dtd
toolkit/crashreporter/client/crashreporter_linux.cpp
toolkit/crashreporter/client/crashreporter_win.cpp
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/crashreporter/nsExceptionHandler.h
toolkit/xre/nsAppRunner.cpp
xpcom/system/nsICrashReporter.idl
--- a/browser/components/preferences/advanced.js
+++ b/browser/components/preferences/advanced.js
@@ -61,16 +61,19 @@ var gAdvancedPane = {
     }
 
 #ifdef MOZ_UPDATER
     this.updateAppUpdateItems();
     this.updateAutoItems();
     this.updateModeItems();
 #endif
     this.updateOfflineApps();
+#ifdef MOZ_CRASHREPORTER
+    this.initSubmitCrashes();
+#endif
   },
 
   /**
    * Stores the identity of the current tab in preferences so that the selected
    * tab can be persisted between openings of the preferences window.
    */
   tabSelectionChanged: function ()
   {
@@ -134,16 +137,45 @@ var gAdvancedPane = {
    * unchanged and represents a value not strictly allowed in UI.
    */
   writeCheckSpelling: function ()
   {
     var checkbox = document.getElementById("checkSpelling");
     return checkbox.checked ? (this._storedSpellCheck == 2 ? 2 : 1) : 0;
   },
 
+  /**
+   *
+   */
+  initSubmitCrashes: function ()
+  {
+    var checkbox = document.getElementById("submitCrashesBox");
+    try {
+      var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
+               getService(Components.interfaces.nsICrashReporter);
+      checkbox.checked = cr.submitReports;
+    } catch (e) {
+      checkbox.style.display = "none";
+    }
+  },
+
+  /**
+   *
+   */
+  updateSubmitCrashes: function ()
+  {
+    var checkbox = document.getElementById("submitCrashesBox");
+    try {
+      var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
+               getService(Components.interfaces.nsICrashReporter);
+      cr.submitReports = checkbox.checked;
+    } catch (e) { }
+  },
+
+
   // NETWORK TAB
 
   /*
    * Preferences:
    *
    * browser.cache.disk.capacity
    * - the size of the browser cache in KB
    */
--- a/browser/components/preferences/advanced.xul
+++ b/browser/components/preferences/advanced.xul
@@ -179,28 +179,33 @@
                       accesskey="&checkSpelling.accesskey;"
                       onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
                       onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
                       preference="layout.spellcheckDefault"/>
           </groupbox>
 
 #ifdef HAVE_SHELL_SERVICE
           <!-- System Defaults -->
-          <groupbox id="systemDefaultsGroup" orient="horizontal">
+          <groupbox id="systemDefaultsGroup" orient="vertical">
             <caption label="&systemDefaults.label;"/>
 
             <hbox id="checkDefaultBox" align="center" flex="1">      
               <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
                         label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
                         flex="1"/>
               <button id="checkDefaultButton"
                       label="&checkNow.label;" accesskey="&checkNow.accesskey;"
                       oncommand="gAdvancedPane.checkNow()"
                       preference="pref.general.disable_button.default_browser"/>
             </hbox>
+#ifdef MOZ_CRASHREPORTER
+            <checkbox id="submitCrashesBox" flex="1"
+                      oncommand="gAdvancedPane.updateSubmitCrashes();"
+                      label="&submitCrashes.label;" accesskey="&submitCrashes.accesskey;"/>
+#endif
           </groupbox>
 #endif
         </tabpanel>
 
         <!-- Network -->
         <tabpanel id="networkPanel" orient="vertical">
 
            <!-- Connection -->
--- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd
@@ -20,16 +20,18 @@
 <!ENTITY checkSpelling.label             "Check my spelling as I type">
 <!ENTITY checkSpelling.accesskey         "t">
 
 <!ENTITY systemDefaults.label            "System Defaults">
 <!ENTITY alwaysCheckDefault.label        "Always check to see if &brandShortName; is the default browser on startup"><!--XXX-->
 <!ENTITY alwaysCheckDefault.accesskey    "w">
 <!ENTITY checkNow.label                  "Check Now">
 <!ENTITY checkNow.accesskey              "N">
+<!ENTITY submitCrashes.label             "Submit crash reports">
+<!ENTITY submitCrashes.accesskey         "S">
 
 <!ENTITY networkTab.label                "Network">
 
 <!ENTITY connection.label                "Connection">
 
 <!ENTITY connectionDesc.label            "Configure how &brandShortName; connects to the Internet">
 <!ENTITY connectionSettings.label        "Settingsā€¦">
 <!ENTITY connectionSettings.accesskey    "e">
--- a/toolkit/crashreporter/client/crashreporter_linux.cpp
+++ b/toolkit/crashreporter/client/crashreporter_linux.cpp
@@ -93,16 +93,21 @@ static GThread* gSendThreadID;
 static void* gnomeLib = NULL;
 // handle from dlopen'ing libgnomeui
 static void* gnomeuiLib = NULL;
 
 static const char kIniFile[] = "crashreporter.ini";
 
 static void LoadSettings()
 {
+  /*
+   * NOTE! This code needs to stay in sync with the preference checking
+   *       code in in nsExceptionHandler.cpp.
+   */
+
   StringTable settings;
   if (ReadStringsFromFile(gSettingsPath + "/" + kIniFile, settings, true)) {
     if (settings.find("Email") != settings.end()) {
       gtk_entry_set_text(GTK_ENTRY(gEmailEntry), settings["Email"].c_str());
       gEmailFieldHint = false;
     }
     if (settings.find("EmailMe") != settings.end()) {
       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gEmailMeCheck),
@@ -120,16 +125,21 @@ static void LoadSettings()
       enabled = ShouldEnableSending();
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck),
                                  enabled);
   }
 }
 
 static 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 (!gEmailFieldHint)
     settings["Email"] = gtk_entry_get_text(GTK_ENTRY(gEmailEntry));
   else
     settings.erase("Email");
 
--- a/toolkit/crashreporter/client/crashreporter_win.cpp
+++ b/toolkit/crashreporter/client/crashreporter_win.cpp
@@ -176,16 +176,20 @@ static void RemoveUnusedValues(const wch
     RegCloseKey(hRegKey);
   }
 }
 
 static bool CheckBoolKey(const wchar_t* key,
                          const wchar_t* valueName,
                          bool* enabled)
 {
+  /*
+   * NOTE! This code needs to stay in sync with the preference checking
+   *       code in in nsExceptionHandler.cpp.
+   */
   *enabled = false;
   bool found = false;
   HKEY hRegKey;
   DWORD val;
   // see if our reg key is set globally
   if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
     if (GetBoolValue(hRegKey, valueName, &val)) {
       *enabled = (val == 1);
@@ -203,16 +207,20 @@ static bool CheckBoolKey(const wchar_t* 
     }
   }
 
   return found;
 }
 
 static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
 {
+  /*
+   * NOTE! This code needs to stay in sync with the preference setting
+   *       code in in nsExceptionHandler.cpp.
+   */
   HKEY hRegKey;
 
   // remove the old value from the registry if it exists
   RemoveUnusedValues(key, SUBMIT_REPORT_OLD);
 
   if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
     DWORD data = (enabled ? 1 : 0);
     RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is
  * Ted Mielczarek <ted.mielczarek@gmail.com>
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Josh Aas <josh@mozilla.com>
+ *  Justin Dolske <dolske@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -38,31 +39,35 @@
 
 #include "nsExceptionHandler.h"
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
 #endif
 
+#include "nsIWindowsRegKey.h"
 #if defined(MOZ_IPC)
 #  include "client/windows/crash_generation/crash_generation_server.h"
 #endif
 #include "client/windows/handler/exception_handler.h"
 #include <DbgHelp.h>
 #include <string.h>
 #elif defined(XP_MACOSX)
 #include "client/mac/handler/exception_handler.h"
 #include <string>
 #include <Carbon/Carbon.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <unistd.h>
 #include "mac_utils.h"
 #elif defined(XP_LINUX)
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIINIParser.h"
 #if defined(MOZ_IPC)
 #  include "client/linux/crash_generation/client_info.h"
 #  include "client/linux/crash_generation/crash_generation_server.h"
 #endif
 #include "client/linux/handler/exception_handler.h"
 #include <fcntl.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -83,16 +88,17 @@
 #include "mozilla/Mutex.h"
 #include "nsDebug.h"
 #include "nsCRT.h"
 #include "nsILocalFile.h"
 #include "nsIFileStreams.h"
 #include "nsDataHashtable.h"
 #include "nsInterfaceHashtable.h"
 #include "prprf.h"
+#include "nsIXULAppInfo.h"
 
 #if defined(MOZ_IPC)
 using google_breakpad::CrashGenerationServer;
 using google_breakpad::ClientInfo;
 
 using mozilla::Mutex;
 using mozilla::MutexAutoLock;
 
@@ -961,16 +967,182 @@ nsresult AppendObjCExceptionInfoToAppNot
 {
   nsCAutoString excString;
   GetObjCExceptionInfo(inException, excString);
   AppendAppNotesToCrashReport(excString);
   return NS_OK;
 }
 #endif
 
+/*
+ * Combined code to get/set the crash reporter submission pref on
+ * different platforms.
+ */
+static nsresult PrefSubmitReports(PRBool* aSubmitReports, bool writePref)
+{
+  nsresult rv;
+#if defined(XP_WIN32)
+  /*
+   * NOTE! This needs to stay in sync with the preference checking code
+   *       in toolkit/crashreporter/client/crashreporter_win.cpp
+   */
+  nsCOMPtr<nsIXULAppInfo> appinfo =
+    do_GetService("@mozilla.org/xre/app-info;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString appVendor, appName;
+  rv = appinfo->GetVendor(appVendor);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = appinfo->GetName(appName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIWindowsRegKey> regKey
+    (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString regPath;
+
+  regPath.AppendLiteral("Software\\");
+  if(!appVendor.IsEmpty()) {
+    regPath.Append(appVendor);
+    regPath.AppendLiteral("\\");
+  }
+  regPath.Append(appName);
+  regPath.AppendLiteral("\\Crash Reporter");
+
+  // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER
+  // and we're done.
+  if (writePref) {
+    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                      NS_ConvertUTF8toUTF16(regPath),
+                      nsIWindowsRegKey::ACCESS_SET_VALUE);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 value = *aSubmitReports ? 1 : 0;
+    rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value);
+    regKey->Close();
+    return rv;
+  }
+
+  // We're reading the pref value, so we need to first look under
+  // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to
+  // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults
+  // to "true".
+  PRUint32 value;
+  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
+                    NS_ConvertUTF8toUTF16(regPath),
+                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_SUCCEEDED(rv)) {
+    rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
+    regKey->Close();
+    if (NS_SUCCEEDED(rv)) {
+      *aSubmitReports = !!value;
+      return NS_OK;
+    }
+  }
+
+  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                    NS_ConvertUTF8toUTF16(regPath),
+                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_FAILED(rv)) {
+    *aSubmitReports = PR_TRUE;
+    return NS_OK;
+  }
+  
+  rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value);
+  // default to true on failure
+  if (NS_FAILED(rv)) {
+    value = 1;
+    rv = NS_OK;
+  }
+  regKey->Close();
+
+  *aSubmitReports = !!value;
+  return NS_OK;
+#elif defined(XP_UNIX)
+  /*
+   * NOTE! This needs to stay in sync with the preference checking code
+   *       in toolkit/crashreporter/client/crashreporter_linux.cpp
+   */
+  nsCOMPtr<nsIFile> reporterINI;
+  rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI));
+  NS_ENSURE_SUCCESS(rv, rv);
+  reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports"));
+  reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini"));
+
+  PRBool exists;
+  rv = reporterINI->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!exists) {
+    if (!writePref) {
+        // If reading the pref, default to true if .ini doesn't exist.
+        *aSubmitReports = PR_TRUE;
+        return NS_OK;
+    }
+    // Create the file so the INI processor can write to it.
+    rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIINIParserFactory> iniFactory =
+    do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(reporterINI);
+  NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIINIParser> iniParser;
+  rv = iniFactory->CreateINIParser(localFile,
+                                   getter_AddRefs(iniParser));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If we're writing the pref, just set and we're done.
+  if (writePref) {
+    nsCOMPtr<nsIINIParserWriter> iniWriter = do_QueryInterface(iniParser);
+    NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE);
+
+    rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"),
+                              NS_LITERAL_CSTRING("SubmitReport"),
+                              *aSubmitReports ?  NS_LITERAL_CSTRING("1") :
+                                                 NS_LITERAL_CSTRING("0"));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = iniWriter->WriteFile(NULL);
+    return rv;
+  }
+  
+  nsCAutoString submitReportValue;
+  rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"),
+                            NS_LITERAL_CSTRING("SubmitReport"),
+                            submitReportValue);
+
+  // Default to "true" if the pref can't be found.
+  if (NS_FAILED(rv))
+    *aSubmitReports = PR_TRUE;
+  else if (submitReportValue.EqualsASCII("0"))
+    *aSubmitReports = PR_FALSE;
+  else
+    *aSubmitReports = PR_TRUE;
+
+  return NS_OK;
+#else
+  // TODO: Implement for OSX (bug 542379)
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult GetSubmitReports(PRBool* aSubmitReports)
+{
+    return PrefSubmitReports(aSubmitReports, false);
+}
+
+nsresult SetSubmitReports(PRBool aSubmitReports)
+{
+    return PrefSubmitReports(&aSubmitReports, true);
+}
+
+
 #if defined(MOZ_IPC)
 //-----------------------------------------------------------------------------
 // Out-of-process crash reporting API wrappers
 class SubmitCrashReport : public nsRunnable
 {
 public:
   SubmitCrashReport(nsIFile* dumpFile) : mDumpFile(dumpFile) { }
 
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -65,16 +65,18 @@ nsresult SetRestartArgs(int argc, char**
 nsresult SetupExtraData(nsILocalFile* aAppDataDirectory,
                         const nsACString& aBuildID);
 #ifdef XP_WIN32
   nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo);
 #endif
 #ifdef XP_MACOSX
   nsresult AppendObjCExceptionInfoToAppNotes(void *inException);
 #endif
+nsresult GetSubmitReports(PRBool* aSubmitReport);
+nsresult SetSubmitReports(PRBool aSubmitReport);
 
 #ifdef MOZ_IPC
 // Out-of-process crash reporter API.
 
 // Return true iff a dump was found for |childPid|, and return the
 // path in |dump|.
 bool GetMinidumpForChild(PRUint32 childPid, nsIFile** dump NS_OUTPARAM);
 
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -970,16 +970,29 @@ NS_IMETHODIMP
 nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException)
 {
 #ifdef XP_MACOSX
   return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException);
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
+
+NS_IMETHODIMP
+nsXULAppInfo::GetSubmitReports(PRBool* aEnabled)
+{
+  return CrashReporter::GetSubmitReports(aEnabled);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SetSubmitReports(PRBool aEnabled)
+{
+  return CrashReporter::SetSubmitReports(aEnabled);
+}
+
 #endif
 
 static const nsXULAppInfo kAppInfo;
 static NS_METHOD AppInfoConstructor(nsISupports* aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   NS_ENSURE_NO_AGGREGATION(aOuter);
 
--- a/xpcom/system/nsICrashReporter.idl
+++ b/xpcom/system/nsICrashReporter.idl
@@ -42,17 +42,17 @@ interface nsIURL;
 
 /**
  * Provides access to crash reporting functionality.
  *
  * @status UNSTABLE - This interface is not frozen and will probably change in
  *                    future releases.
  */
 
-[scriptable, uuid(44650737-59f7-4c9b-adbe-2b6d4dfee86a)]
+[scriptable, uuid(56761088-57ad-4f5c-bd61-f678c2807fe0)]
 interface nsICrashReporter : nsISupports
 {
   /**
    * Enable or disable the crashreporter at runtime.
    */
   attribute boolean enabled;
 
   /**
@@ -112,9 +112,14 @@ interface nsICrashReporter : nsISupports
   [noscript] void writeMinidumpForException(in voidPtr aExceptionInfo);
   
   /**
    * Append note containing an Obj-C exception's info.
    *
    * @param aException  NSException object to append note for
    */
   [noscript] void appendObjCExceptionInfoToAppNotes(in voidPtr aException);
+
+  /**
+   * User preference for submitting crash reports.
+   */
+  attribute boolean submitReports;
 };