Bug 539048 - Hacky temporary UI for sending plugin-process crash reports, r=ted
authorBenjamin Smedberg <benjamin@smedbergs.us>
Mon, 11 Jan 2010 15:13:12 -0500
changeset 37207 f98a151769d7a1eb47caa0e6cedc4216a0020713
parent 37206 e828ac5006650b19def0b3b35cca72b80ef46548
child 37208 6e638c0eedf76209f32b813b7832fb15a8217bec
push idunknown
push userunknown
push dateunknown
reviewersted
bugs539048
milestone1.9.3a1pre
Bug 539048 - Hacky temporary UI for sending plugin-process crash reports, r=ted
toolkit/crashreporter/content/crashes.js
toolkit/crashreporter/content/oopcrashdialog.js
toolkit/crashreporter/content/oopcrashdialog.xul
toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
toolkit/crashreporter/jar.mn
toolkit/crashreporter/nsExceptionHandler.cpp
--- a/toolkit/crashreporter/content/crashes.js
+++ b/toolkit/crashreporter/content/crashes.js
@@ -139,27 +139,32 @@ function submitSuccess(ret, link, dump, 
   try {
     dump.remove(false);
     extra.remove(false);
   }
   catch (ex) {
     // report an error? not much the user can do here.
   }
 
-  // reset the link to point at our new crash report. this way, if the
-  // user clicks "Back", the link will be correct.
-  let CrashID = ret.CrashID;
-  link.firstChild.textContent = CrashID;
-  link.setAttribute("id", CrashID);
-  link.removeEventListener("click", submitPendingReport, true);
+  if (link) {
+    // reset the link to point at our new crash report. this way, if the
+    // user clicks "Back", the link will be correct.
+    let CrashID = ret.CrashID;
+    link.firstChild.textContent = CrashID;
+    link.setAttribute("id", CrashID);
+    link.removeEventListener("click", submitPendingReport, true);
 
-  if (reportURL) {
-    link.setAttribute("href", reportURL + CrashID);
-    // redirect the user to their brand new crash report
-    window.location.href = reportURL + CrashID;
+    if (reportURL) {
+      link.setAttribute("href", reportURL + CrashID);
+      // redirect the user to their brand new crash report
+      window.location.href = reportURL + CrashID;
+    }
+  }
+  else {
+    window.close();
   }
 }
 
 function submitForm(iframe, dump, extra, link)
 {
   let reportData = parseKeyValuePairsFromFile(extra);
   let form = iframe.contentDocument.forms[0];
   if ('ServerURL' in reportData) {
@@ -189,17 +194,18 @@ function submitForm(iframe, dump, extra,
         return this;
       throw Components.results.NS_NOINTERFACE;
     },
 
     onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) {
       if(aFlag & STATE_STOP) {
         iframe.docShell.removeProgressListener(myListener);
         myListener = null;
-        link.className = "";
+	if (link)
+          link.className = "";
 
         //XXX: give some indication of failure?
         // check general request status first
         if (!Components.isSuccessCode(aStatus)) {
           document.body.removeChild(iframe);
           return 0;
         }
         // check HTTP status
@@ -228,22 +234,25 @@ function submitForm(iframe, dump, extra,
 }
 
 function createAndSubmitForm(id, link) {
   let [dump, extra] = getPendingMinidump(id);
   if (!dump.exists() || !extra.exists())
     return false;
   let iframe = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "iframe");
   iframe.setAttribute("type", "content");
-  iframe.onload = function() {
+
+  function loadHandler() {
     if (iframe.contentWindow.location == "about:blank")
       return;
-    iframe.onload = null;
+    iframe.removeEventListener("load", loadHandler, true);
     submitForm(iframe, dump, extra, link);
-  };
+  }      
+
+  iframe.addEventListener("load", loadHandler, true);
   document.body.appendChild(iframe);
   iframe.webNavigation.loadURI("chrome://global/content/crash-submit-form.xhtml", 0, null, null, null);
   return true;
 }
 
 function submitPendingReport(event) {
   var link = event.target;
   var id = link.firstChild.textContent;
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/content/oopcrashdialog.js
@@ -0,0 +1,73 @@
+// This code is TEMPORARY for submitting crashes via an ugly popup dialog:
+// bug 525849 tracks the real implementation.
+
+var id;
+
+function getExtraData() {
+  let appData = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+  appData.QueryInterface(Ci.nsICrashReporter);
+  appData.QueryInterface(Ci.nsIXULAppInfo);
+
+  let r = "";
+  r += "ServerURL=" + appData.serverURL.spec + "\n";
+  r += "BuildID=" + appData.appBuildID + "\n";
+  r += "ProductName=" + appData.name + "\n";
+  r += "Vendor=" + appData.vendor + "\n";
+  r += "Version=" + appData.version + "\n";
+  r += "CrashTime=" + ((new Date()).getTime() / 1000).toFixed() + "\n";
+  r += "ProcessType=plugin\n";
+  return r;
+}
+
+function collectData() {
+  // HACK: crashes.js uses document.body, so we just alias it
+  document.body = document.getElementById('iframe-holder');
+
+  getL10nStrings();
+
+  let directoryService = Cc["@mozilla.org/file/directory_service;1"].
+    getService(Ci.nsIProperties);
+  pendingDir = directoryService.get("UAppData", Ci.nsIFile);
+  pendingDir.append("Crash Reports");
+  pendingDir.append("pending");
+  if (!pendingDir.exists())
+    pendingDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
+
+  reportsDir = directoryService.get("UAppData", Ci.nsIFile);
+  reportsDir.append("Crash Reports");
+  reportsDir.append("submitted");
+  if (!reportsDir.exists())
+      reportsDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
+
+  let dumpFile = window.arguments[0].QueryInterface(Ci.nsIFile);
+  dumpFile.moveTo(pendingDir, "");
+  let leafName = dumpFile.leafName;
+
+  id = /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.dmp/(leafName)[1];
+
+  dumpFile = pendingDir.clone();
+  dumpFile.append(leafName);
+
+  let extraFile = pendingDir.clone();
+  extraFile.append(id + ".extra");
+
+  let fstream = Cc["@mozilla.org/network/file-output-stream;1"].
+    createInstance(Ci.nsIFileOutputStream);
+  fstream.init(extraFile, -1, -1, 0);
+
+  var os = Cc["@mozilla.org/intl/converter-output-stream;1"].
+    createInstance(Ci.nsIConverterOutputStream);
+  os.init(fstream, "UTF-8", 0, 0x0000);
+  os.writeString(getExtraData());
+  os.close();
+  fstream.close();
+}
+
+function onSubmit()
+{
+  document.documentElement.getButton('accept').disabled = true;
+  document.documentElement.getButton('accept').label = 'Sending';
+  document.getElementById('throbber').src = 'chrome://global/skin/icons/loading_16.png';
+  createAndSubmitForm(id, null);
+  return false;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/content/oopcrashdialog.xul
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        id="oopCrashDialog"
+        buttons="accept,cancel"
+        buttonlabelaccept="Send"
+        ondialogaccept="return onSubmit()"
+        onload="collectData()">
+
+  <style xmlns="http://www.w3.org/1999/xhtml" type="text/css">
+    #iframe-holder {
+      visibility: hidden;
+      height: 1px;
+      overflow: hidden;
+    }
+  </style>
+
+  <script type="application/javascript;version=1.8" src="chrome://global/content/crashes.js"/>
+  <script type="application/javascript;version=1.8" src="chrome://global/content/oopcrashdialog.js"/>
+
+  <dialogheader title="A Plugin Crashed" />
+
+  <hbox align="center">
+    <description>A plugin crashed while Firefox was running. Please choose to send a crash report
+    to Mozilla. Reloading should cause your plugin to restart.</description>
+    <image width="16" height="16" id="throbber" />
+  </hbox>
+
+  <hbox id="iframe-holder" height="1"/>
+
+</dialog>
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
@@ -200,18 +200,19 @@ void ExceptionHandler::Initialize(const 
 
     // The first time an ExceptionHandler that installs a handler is
     // created, set up the handler stack.
     if (!handler_stack_) {
       handler_stack_ = new vector<ExceptionHandler*>();
     }
     handler_stack_->push_back(this);
 
-    if (handler_types & HANDLER_EXCEPTION)
+    if (handler_types & HANDLER_EXCEPTION) {
       previous_filter_ = SetUnhandledExceptionFilter(HandleException);
+    }
 
 #if _MSC_VER >= 1400  // MSVC 2005/8
     if (handler_types & HANDLER_INVALID_PARAMETER)
       previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);
 #endif  // _MSC_VER >= 1400
 
     if (handler_types & HANDLER_PURECALL)
       previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);
--- a/toolkit/crashreporter/jar.mn
+++ b/toolkit/crashreporter/jar.mn
@@ -1,4 +1,6 @@
 toolkit.jar:
   content/global/crashes.xhtml            (content/crashes.xhtml)
   content/global/crashes.js               (content/crashes.js)
   content/global/crash-submit-form.xhtml  (content/crash-submit-form.xhtml)
+  content/global/oopcrashdialog.xul       (content/oopcrashdialog.xul)
+  content/global/oopcrashdialog.js        (content/oopcrashdialog.js)
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -82,16 +82,20 @@
 #include "nsDebug.h"
 #include "nsCRT.h"
 #include "nsILocalFile.h"
 #include "nsDataHashtable.h"
 
 #if defined(MOZ_IPC)
 using google_breakpad::CrashGenerationServer;
 using google_breakpad::ClientInfo;
+
+#include "nsThreadUtils.h"
+#include "nsIWindowWatcher.h"
+#include "nsIDOMWindow.h"
 #endif
 
 namespace CrashReporter {
 
 #ifdef XP_WIN32
 typedef wchar_t XP_CHAR;
 #define CONVERT_UTF16_TO_XP_CHAR(x) x
 #define CONVERT_XP_CHAR_TO_UTF16(x) x
@@ -936,27 +940,61 @@ nsresult AppendObjCExceptionInfoToAppNot
   AppendAppNotesToCrashReport(excString);
   return NS_OK;
 }
 #endif
 
 #if defined(MOZ_IPC)
 //-----------------------------------------------------------------------------
 // Out-of-process crash reporting API wrappers
+class SubmitCrashReport : public nsRunnable
+{
+public:
+  SubmitCrashReport(nsIFile* dumpFile) : mDumpFile(dumpFile) { }
+
+  NS_IMETHOD Run() {
+    char* e = getenv("MOZ_CRASHREPORTER_NO_REPORT");
+    if (e && *e)
+      return NS_OK;
+
+    nsCOMPtr<nsIWindowWatcher> windowWatcher =
+      do_GetService(NS_WINDOWWATCHER_CONTRACTID);
+    nsCOMPtr<nsIDOMWindow> newWindow;
+    windowWatcher->OpenWindow(nsnull,
+                              "chrome://global/content/oopcrashdialog.xul",
+                              "_blank",
+                              "centerscreen,chrome,titlebar",
+                              mDumpFile, getter_AddRefs(newWindow));
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIFile> mDumpFile;
+};
+
 static void
 OnChildProcessDumpRequested(void* aContext,
                             const ClientInfo* aClientInfo,
 #if defined(XP_WIN)
                             const std::wstring*
 #else
                             const std::string*
 #endif
                               aFilePath)
 {
-  printf("CHILD DUMP REQUEST\n");
+  nsCOMPtr<nsILocalFile> lf;
+#ifdef XP_WIN
+  NS_NewLocalFile(nsDependentString(aFilePath->c_str()), PR_FALSE,
+                  getter_AddRefs(lf));
+#else
+  NS_NewNativeLocalFile(nsDependentCString(aFilePath->c_str()), PR_FALSE,
+                        getter_AddRefs(lf));
+#endif
+  nsCOMPtr<nsIRunnable> r = new SubmitCrashReport(lf);
+  NS_DispatchToMainThread(r);
 }
 
 static bool
 OOPInitialized()
 {
   return crashServer != NULL;
 }