Bug 538910 - Plugins: Need a "plugin crashed" UI. r=gavin, ui-r=faaborg, icon=shorlander
authorJustin Dolske <dolske@mozilla.com>
Tue, 09 Feb 2010 17:05:32 -0800
changeset 38033 b2e6a7183e282d1e1f9345586aba32c64ef40ce1
parent 38032 4df5c9eb3ea22c06f5ad1a025ddc13dcd0267386
child 38034 65f84c85378178f4306c3f83d77da2d293818ff7
push idunknown
push userunknown
push dateunknown
reviewersgavin, faaborg
bugs538910
milestone1.9.3a2pre
Bug 538910 - Plugins: Need a "plugin crashed" UI. r=gavin, ui-r=faaborg, icon=shorlander
browser/base/content/browser.js
browser/locales/en-US/chrome/browser/browser.properties
toolkit/crashreporter/content/oopcrashdialog.js
toolkit/crashreporter/content/oopcrashdialog.xul
toolkit/crashreporter/jar.mn
toolkit/crashreporter/nsExceptionHandler.cpp
toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd
toolkit/mozapps/plugins/content/pluginProblem.xml
toolkit/mozapps/plugins/content/pluginProblemBinding.css
toolkit/themes/pinstripe/mozapps/jar.mn
toolkit/themes/pinstripe/mozapps/plugins/pluginCrashed.png
toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css
toolkit/themes/winstripe/mozapps/jar.mn
toolkit/themes/winstripe/mozapps/plugins/pluginCrashed-aero.png
toolkit/themes/winstripe/mozapps/plugins/pluginCrashed.png
toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1096,24 +1096,28 @@ function HandleAppCommandEvent(evt) {
     BrowserHome();
     break;
   default:
     break;
   }
 }
 
 function prepareForStartup() {
+  var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
+
   gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
   // Note: we need to listen to untrusted events, because the pluginfinder XBL
   // binding can't fire trusted ones (runs with page privileges).
   gBrowser.addEventListener("PluginNotFound", gMissingPluginInstaller.newMissingPlugin, true, true);
+  gBrowser.addEventListener("PluginCrashed", gMissingPluginInstaller.pluginInstanceCrashed, true, true);
   gBrowser.addEventListener("PluginBlocklisted", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginOutdated", gMissingPluginInstaller.newMissingPlugin, true, true);
   gBrowser.addEventListener("PluginDisabled", gMissingPluginInstaller.newDisabledPlugin, true, true);
   gBrowser.addEventListener("NewPluginInstalled", gMissingPluginInstaller.refreshBrowser, false);
+  os.addObserver(gMissingPluginInstaller.pluginCrashed, "plugin-crashed", false);
   window.addEventListener("AppCommand", HandleAppCommandEvent, true);
 
   var webNavigation;
   try {
     webNavigation = getWebNavigation();
     if (!webNavigation)
       throw "no XBL binding for browser";
   } catch (e) {
@@ -1147,17 +1151,16 @@ function prepareForStartup() {
 
   // Manually hook up session and global history for the first browser
   // so that we don't have to load global history before bringing up a
   // window.
   // Wire up session and global history before any possible
   // progress notifications for back/forward button updating
   webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
                                            .createInstance(Components.interfaces.nsISHistory);
-  var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
   os.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
 
   // remove the disablehistory attribute so the browser cleans up, as
   // though it had done this work itself
   gBrowser.browsers[0].removeAttribute("disablehistory");
 
   // enable global history
   try {
@@ -5988,16 +5991,30 @@ function getPluginInfo(pluginElement)
     }
   }
 
   return {mimetype: tagMimetype, pluginsPage: pluginsPage};
 }
 
 var gMissingPluginInstaller = {
 
+  get CrashSubmit() {
+    delete this.CrashSubmit;
+    Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
+    return this.CrashSubmit;
+  },
+
+  get crashReportHelpURL() {
+    delete this.crashReportHelpURL;
+    let url = formatURL("app.support.baseURL", true);
+    url += "plugin-crashed";
+    this.crashReportHelpURL = url;
+    return this.crashReportHelpURL;
+  },
+
   installSinglePlugin: function (aEvent) {
     if (!aEvent.isTrusted)
         return;
     var missingPluginsArray = {};
 
     var pluginInfo = getPluginInfo(aEvent.target);
     missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
 
@@ -6163,16 +6180,139 @@ var gMissingPluginInstaller = {
                                    gMissingPluginInstaller.managePlugins,
                                    true);
     aEvent.target.addEventListener("keydown",
                                    function(evt) { if (evt.keyCode == evt.DOM_VK_RETURN)
                                                      gMissingPluginInstaller.managePlugins(evt) },
                                    true);
   },
 
+  // Crashed-plugin observer. Notified once per plugin crash, before events
+  // are dispatched to individual plugin instances.
+  pluginCrashed : function(subject, topic, data) {
+    let propertyBag = subject;
+    if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
+        !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
+     return;
+
+#ifdef MOZ_CRASHREPORTER
+    let minidumpID = subject.getPropertyAsAString("minidumpID");
+    let submitReports = gCrashReporter.submitReports;
+    // The crash reporter wants a DOM element it can append an IFRAME to,
+    // which it uses to submit a form. Let's just give it gBrowser.
+    if (submitReports)
+      gMissingPluginInstaller.CrashSubmit.submit(minidumpID, gBrowser, null, null);
+    propertyBag.setPropertyAsBool("submittedCrashReport", submitReports);
+#endif
+  },
+
+  pluginInstanceCrashed: function (aEvent) {
+    // Evil content could fire a fake event at us, ignore them.
+    if (!aEvent.isTrusted)
+      return;
+
+    if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent))
+      return;
+
+    let submittedReport = aEvent.getData("submittedCrashReport");
+    let pluginName      = aEvent.getData("pluginName");
+
+    // We're expecting this to be a plugin.
+    let plugin = aEvent.target;
+    if (!(plugin instanceof Ci.nsIObjectLoadingContent))
+      return;
+
+    // Force a style flush, so that we ensure our binding is attached.
+    plugin.clientTop;
+
+    let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
+
+    //
+    // Configure the crashed-plugin placeholder.
+    //
+    let doc = plugin.ownerDocument;
+    let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+
+    // The binding has role="link" here, since missing/disabled/blocked
+    // plugin UI has a onclick handler on the whole thing. This isn't needed
+    // for the plugin-crashed UI, because we use actual HTML links in the text.
+    overlay.removeAttribute("role");
+
+#ifdef MOZ_CRASHREPORTER
+    let helpClass = submittedReport ? "submitLink" : "notSubmitLink";
+    let helpLink = doc.getAnonymousElementByAttribute(plugin, "class", helpClass);
+    helpLink.href = gMissingPluginInstaller.crashReportHelpURL;
+    let showClass = submittedReport ? "msg msgSubmitted" : "msg msgNotSubmitted";
+    let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
+    textToShow.style.display = "block";
+#endif
+
+    let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
+    crashText.textContent = messageString;
+
+    let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
+    link.addEventListener("click", function(e) { if (e.isTrusted) browser.reload(); }, true);
+
+    let browser = gBrowser.getBrowserForDocument(plugin.ownerDocument
+                                                       .defaultView.top.document);
+    let notificationBox = gBrowser.getNotificationBox(browser);
+
+    // Is the <object>'s size too small to hold what we want to show?
+    let pluginRect = plugin.getBoundingClientRect();
+    // XXX bug 446693. The text-shadow on the submitted-report text at
+    //     the bottom causes scrollHeight to be larger than it should be.
+    let isObjectTooSmall = (overlay.scrollWidth > pluginRect.width) ||
+                           (overlay.scrollHeight - 5 > pluginRect.height);
+    if (isObjectTooSmall) {
+        // Hide the overlay's contents. Use visibility style, so that it
+        // doesn't collapse down to 0x0.
+        overlay.style.visibility = "hidden";
+        // If another plugin on the page was large enough to show our UI, we
+        // don't want to show a notification bar.
+        if (!doc.mozNoPluginCrashedNotification)
+          showNotificationBar();
+    } else {
+        // If a previous plugin on the page was too small and resulted in
+        // adding a notification bar, then remove it because this plugin
+        // instance it big enough to serve as in-content notification.
+        hideNotificationBar();
+        doc.mozNoPluginCrashedNotification = true;
+    }
+
+    function hideNotificationBar() {
+      let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+      if (notification)
+        notificationBox.removeNotification(notification, true);
+    }
+
+    function showNotificationBar() {
+      // If there's already an existing notification bar, don't do anything.
+      let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+      if (notification)
+        return;
+
+      // Configure the notification bar
+      let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+      let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
+      let label = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
+      let accessKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
+
+      let buttons = [{
+        label: label,
+        accessKey: accessKey,
+        popup: null,
+        callback: function() { browser.reload(); },
+      }];
+
+      let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
+                                                            iconURL, priority, buttons);
+    }
+
+  },
+
   refreshBrowser: function (aEvent) {
     // browser elements are anonymous so we can't just use target.
     var browser = aEvent.originalTarget;
     var notificationBox = gBrowser.getNotificationBox(browser);
     var notification = notificationBox.getNotificationWithValue("missing-plugins");
 
     // clear the plugin list, now that at least one plugin has been installed
     browser.missingPlugins = null;
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -69,16 +69,19 @@ missingpluginsMessage.button.accesskey=I
 outdatedpluginsMessage.title=Some plugins used by this page are out of date.
 outdatedpluginsMessage.updateButton.label=Update Plugins…
 outdatedpluginsMessage.updateButton.accesskey=U
 blockedpluginsMessage.title=Some plugins required by this page have been blocked for your protection.
 blockedpluginsMessage.infoButton.label=Details…
 blockedpluginsMessage.infoButton.accesskey=D
 blockedpluginsMessage.searchButton.label=Update Plugins…
 blockedpluginsMessage.searchButton.accesskey=U
+crashedpluginsMessage.title=The %S plugin has crashed.
+crashedpluginsMessage.reloadButton.label=Reload page
+crashedpluginsMessage.reloadButton.accesskey=R
 
 # Sanitize
 # LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to
 # clear" is set to "Everything", the Clear Recent History dialog's title is
 # changed to this.  See UI mockup and comment 11 at bug 480169 -->
 sanitizeDialog2.everything.title=Clear All History
 sanitizeButtonOK=Clear Now
 # LOCALIZATION NOTE (sanitizeEverythingWarning2): Warning that appears when
deleted file mode 100644
--- a/toolkit/crashreporter/content/oopcrashdialog.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// This code is TEMPORARY for submitting crashes via an ugly popup dialog:
-// bug 525849 tracks the real implementation.
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-Components.utils.import("resource://gre/modules/CrashSubmit.jsm");
-
-var id;
-
-function collectData() {
-  let directoryService = Cc["@mozilla.org/file/directory_service;1"].
-    getService(Ci.nsIProperties);
-
-  let dumpFile = window.arguments[0].QueryInterface(Ci.nsIFile);
-  id = dumpFile.leafName.replace(/.dmp$/, "");
-}
-
-function submitDone()
-{
-  // we don't currently distinguish between success or failure here
-  window.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';
-  CrashSubmit.submit(id, document.getElementById('iframe-holder'),
-                     submitDone, submitDone);
-  return false;
-}
deleted file mode 100644
--- a/toolkit/crashreporter/content/oopcrashdialog.xul
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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/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/jar.mn
+++ b/toolkit/crashreporter/jar.mn
@@ -1,6 +1,4 @@
 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
@@ -96,20 +96,16 @@
 #include "nsIXULAppInfo.h"
 
 #if defined(MOZ_IPC)
 using google_breakpad::CrashGenerationServer;
 using google_breakpad::ClientInfo;
 
 using mozilla::Mutex;
 using mozilla::MutexAutoLock;
-
-#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
@@ -1134,39 +1130,16 @@ nsresult GetSubmitReports(PRBool* aSubmi
 
 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) { }
-
-  NS_IMETHOD Run() {
-    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 PLDHashOperator EnumerateChildAnnotations(const nsACString& key,
                                                  nsCString entry,
                                                  void* userData)
 {
   // blacklist of entries from the parent process that we don't want to
   // submit with the child process
   static const char* kBlacklist[] = {
     "FramePoisonBase",
@@ -1278,21 +1251,16 @@ OnChildProcessDumpRequested(void* aConte
 
   if (doReport)
     MoveToPending(lf, extraFile);
 
   {
     MutexAutoLock lock(*dumpMapLock);
     pidToMinidump->Put(pid, lf);
   }
-
-  if (doReport) {
-    nsCOMPtr<nsIRunnable> r = new SubmitCrashReport(lf);
-    NS_DispatchToMainThread(r);
-  }
 }
 
 static bool
 OOPInitialized()
 {
   return crashServer != NULL;
 }
 
--- a/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd
@@ -18,8 +18,26 @@
 <!ENTITY pluginWizard.finalPage.description.label            "&brandShortName; finished installing the missing plugins:">
 
 <!ENTITY pluginWizard.finalPage.moreInfo.label               "Find out more about Plugins or manually find missing plugins.">
 <!ENTITY pluginWizard.finalPage.restart.label                "&brandShortName; needs to be restarted for the plugin(s) to work.">
   
 <!ENTITY missingPlugin.label                                 "Click here to download plugin.">
 <!ENTITY disabledPlugin.label                                "The plugin for this content has been disabled. Click here to manage your plugins.">
 <!ENTITY blockedPlugin.label                                 "This plugin has been blocked for your protection.">
+<!-- LOCALIZATION NOTE (reloadPlugin.pre): include a trailing space as needed -->
+<!-- LOCALIZATION NOTE (reloadPlugin.middle): avoid leading/trailing spaces, this text is a link -->
+<!-- LOCALIZATION NOTE (reloadPlugin.post): include a starting space as needed -->
+<!ENTITY reloadPlugin.pre                                    "">
+<!ENTITY reloadPlugin.middle                                 "Reload the page">
+<!ENTITY reloadPlugin.post                                   " to try again.">
+<!-- LOCALIZATION NOTE (submittedCrashReport.pre): include a trailing space as needed -->
+<!-- LOCALIZATION NOTE (submittedCrashReport.middle): avoid leading/trailing spaces, this text is a link -->
+<!-- LOCALIZATION NOTE (submittedCrashReport.post): include a starting space as needed -->
+<!ENTITY submittedCrashReport.pre                            "A ">
+<!ENTITY submittedCrashReport.middle                         "crash report">
+<!ENTITY submittedCrashReport.post                           " was submitted.">
+<!-- LOCALIZATION NOTE (notSubmittedCrashReport.pre): include a trailing space as needed -->
+<!-- LOCALIZATION NOTE (notSubmittedCrashReport.middle): avoid leading/trailing spaces, this text is a link -->
+<!-- LOCALIZATION NOTE (notSubmittedCrashReport.post): include a starting space as needed -->
+<!ENTITY notSubmittedCrashReport.pre                         "You have disabled ">
+<!ENTITY notSubmittedCrashReport.middle                      "crash report">
+<!ENTITY notSubmittedCrashReport.post                        " submission.">
--- a/toolkit/mozapps/plugins/content/pluginProblem.xml
+++ b/toolkit/mozapps/plugins/content/pluginProblem.xml
@@ -49,14 +49,19 @@
 
     <content>
         <xul:vbox class="mainBox" role="link" flex="1">
             <xul:spacer flex="1"/>
             <xul:box class="icon"/>
             <html:div class="msg msgUnsupported">&missingPlugin.label;</html:div>
             <html:div class="msg msgDisabled">&disabledPlugin.label;</html:div>
             <html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
+            <html:div class="msg msgCrashed"><!-- set at runtime --></html:div>
+            <html:div class="msg msgReload">&reloadPlugin.pre;<html:a class="reloadLink" href="">&reloadPlugin.middle;</html:a>&reloadPlugin.post;</html:div>
             <xul:spacer flex="1"/>
+            <!-- link hrefs set at runtime -->
+            <html:div class="msg msgSubmitted">&submittedCrashReport.pre;<html:a class="submitLink" href="">&submittedCrashReport.middle;</html:a>&submittedCrashReport.post;</html:div>
+            <html:div class="msg msgNotSubmitted">&notSubmittedCrashReport.pre;<html:a class="notSubmitLink" href="">&notSubmittedCrashReport.middle;</html:a>&notSubmittedCrashReport.post;</html:div>
         </xul:vbox>
         <html:div style="display:none;"><children/></html:div>
     </content>
 </binding>
 </bindings>
--- a/toolkit/mozapps/plugins/content/pluginProblemBinding.css
+++ b/toolkit/mozapps/plugins/content/pluginProblemBinding.css
@@ -36,16 +36,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 @namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */
 
 embed:-moz-type-unsupported,
 embed:-moz-handler-disabled,
 embed:-moz-handler-blocked,
+embed:-moz-handler-crashed,
 applet:-moz-type-unsupported,
 applet:-moz-handler-disabled,
 applet:-moz-handler-blocked,
+applet:-moz-handler-crashed,
 object:-moz-has-handlerref:-moz-type-unsupported,
 object:-moz-has-handlerref:-moz-handler-disabled,
-object:-moz-has-handlerref:-moz-handler-blocked {
+object:-moz-has-handlerref:-moz-handler-blocked,
+object:-moz-has-handlerref:-moz-handler-crashed {
     -moz-binding: url('chrome://mozapps/content/plugins/pluginProblem.xml#pluginProblem') !important;
 }
--- a/toolkit/themes/pinstripe/mozapps/jar.mn
+++ b/toolkit/themes/pinstripe/mozapps/jar.mn
@@ -19,16 +19,17 @@ toolkit.jar:
   skin/classic/mozapps/extensions/eula.css                        (extensions/eula.css)
   skin/classic/mozapps/extensions/blocklist.css                   (extensions/blocklist.css)
   skin/classic/mozapps/passwordmgr/key.png                        (passwordmgr/key.png)
   skin/classic/mozapps/plugins/pluginProblem.css                  (plugins/pluginProblem.css)
   skin/classic/mozapps/plugins/backgroundStripes.png              (plugins/backgroundStripes.png)
   skin/classic/mozapps/plugins/pluginGeneric.png                  (plugins/pluginGeneric.png)
   skin/classic/mozapps/plugins/pluginDisabled.png                 (plugins/pluginDisabled.png)
   skin/classic/mozapps/plugins/pluginBlocked.png                  (plugins/pluginBlocked.png)
+  skin/classic/mozapps/plugins/pluginCrashed.png                  (plugins/pluginCrashed.png)
   skin/classic/mozapps/plugins/pluginGeneric-16.png               (plugins/pluginGeneric-16.png)
   skin/classic/mozapps/plugins/pluginBlocked-16.png               (plugins/pluginBlocked-16.png)
   skin/classic/mozapps/plugins/pluginOutdated-16.png              (plugins/pluginOutdated-16.png)
   skin/classic/mozapps/profile/profileicon.png                    (profile/profileicon.png)
   skin/classic/mozapps/profile/profileicon-selected.png           (profile/profileicon-selected.png)
   skin/classic/mozapps/profile/profileSelection.css               (profile/profileSelection.css)
   skin/classic/mozapps/update/buttons.png                         (update/buttons.png)
   skin/classic/mozapps/update/updates.css                         (update/updates.css)
new file mode 100644
index 0000000000000000000000000000000000000000..89d3e91a995f0b60e0d4dd05a7bad57a77c8a319
GIT binary patch
literal 1470
zc$@*g1ws0WP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%U`a$lRCwC#T1`kKM-=X!=^4kLQCD|Y
zyck1>i6OXal&E-6Faa?!1VYThLQq#$Fc%L251z!6=uMEgsN|NAHLxJ=K@fw)A6%D^
zOxR>kYnYI@QD=3=$=LJLomcW|D9i5Y?wLVn$QFFqQ(f=XdsXkRsufjL6|%Y6ge-4a
zuYhRKFLE$yDhHlts=UtGyQCw$h`U2zXP#{uic_WvgBbHHq?wm$q=3Lo<^VYHuf$Ev
zHabBR5CMeougLIAm6w3ZnDPrUQ32p;G#Xt|Ei^Ko&>_7Gkq%x?Ers<=$c_YgcDwx{
z{CxzthRg`A0Ez)_G6wJmzyo*;<F~O-VR)v4S4?XMY&r_iEq5gu99&3mXG}3wUN$Y~
zmo%adoiGQGo8+Wq)nvxuT-cXPBQl&IzbNiTJSs^VQOAiB=HjHssEmWzu?R9f#&OR9
zK0pBbsRAM+L@K;a(3xW>d^{>i8c~N%mX8RcQW7^Q5%6DBb{}wvLdZxLMSgS#QsE`z
zIJ)}rs6<y@qAL%ce;=HuH50RLEEfAglB7?W9f`6Lhpc=7M6YKVT{?*3tGGKMqJ*Ro
zb(9P*e_Uu%r#{9iK+Q<i3t5-ksPnQuBuZSZ;y_OqwF;+9a04cS+4xLMI~610!B2dv
ztE=D5%*;G>xm;pzZ*O&DW8-IFpM|EDQdRwHYir+5Pf!0we)sqHS2s5|-!eh3Xw^^-
zOy#ct>VSCwAnQR<l)pjP&tYr_;61?mfDfSc!n^%Mv;+`I{%mV&>tW~#I!GURXFyMK
zLmkRWWOnFPgQ^}@R#v{=+}!*No)0i)?~&)l#YL~j<GG)RmSBB-{hQ_GWiLZda6tOV
zI|F*fOf3}S>hUw^?(Sa4EO`WYfk}rv2S!FleoRD5Fg!fG#nHc@^!P3jeauWXn$QOz
z$%QeIgfM&3Xq5v=MmF*vhr_|s)0QL4@`(X`>N_wyJNpiIGKwGhhK7dfjCNpjbhK7a
zy7BSxn^t$gvI4EGt#?{lTCOlP6G3iQSJ(C0+S=Pj3N$n{-0JM?yvEa$K8QrOttud5
z6e0yv5jkLhr$4~o`unIT&o?$UzM7brcwAIeWFH(HY_6%P>9N^trDnn?&>w7XZ{M4m
znj$tY3=9msTVG#)*93hKLhZ-s_(!L|H5B+Bk8qjTjzNJ>SOug6CM0BNCN_*hF$|4a
z(Ihik{%;ike+pv_@4QF_qRBAw`~AN|Wcp=cVd0-pC={KWoAWcdLWF{?2ojwg!sYd*
zrly+0!ooMxbvMdKp?g*s_<X*fA%U$yWx7v~dE8YXU+tcrJNo+iu8xh3HJ6l>+^~2e
zyK59ir_3@tJUo0-R#x^f91aJWb1E%GM2!Q9$liQ7ozDEdy}gfMH>xZsFu*FHmx)bb
za&qz!2v6^9!c|pO!q(Q-&E4JIj$kl&2Wp!y2;jMg^g;5vQBhGLprSI+%JcK{KUtYG
ztv*m(Tx_Fu4rTV+P-1k#3c2}lu+l<aUfvt9Tf)xH&WB4&OAp%H+sWmD3dNn0tl#PR
zz=VZsj-c1;{k62Tw8ZUpziwsXKp^n+=;$a=US3`e6>h!-6Z%ps6RXMW08E@ySy@?^
zO!NYUUjw8P`BdJ7*n9a*Zf7E##C1lpK!FH<H>xr#9|pVhK#BZTCQ1&nJ>hbVdhTiw
zow)!H8cuvK>e--9U@){@lXNPE&M+Q7kI{M0TrKj01dQte?i&0uZJvHBJ3$%aSM96`
zMHm8ENc<u0GMtwG=odF3tDjXb`aV<;^gp|*A}65LkI@&c%()NW;>&;bc&R{Y?LPqq
Y0E!xJo>h86SO5S307*qoM6N<$g3G$7cK`qY
--- a/toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css
+++ b/toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css
@@ -49,22 +49,31 @@ html|applet:not([height]), html|applet[h
     background-image: url(chrome://mozapps/skin/plugins/pluginGeneric.png);
 }
 :-moz-handler-disabled .icon {
     background-image: url(chrome://mozapps/skin/plugins/pluginDisabled.png);
 }
 :-moz-handler-blocked .icon {
     background-image: url(chrome://mozapps/skin/plugins/pluginBlocked.png);
 }
+:-moz-handler-crashed .icon {
+    background-image: url(chrome://mozapps/skin/plugins/pluginCrashed.png);
+}
 
 .msg {
     display: none;
     color: white;
     font: 12px sans-serif;
     font-weight: bold;
     cursor: default;
     text-shadow: rgba(0,0,0,0.8) 0 0 5px;
 }
 :-moz-type-unsupported .msgUnsupported,
 :-moz-handler-disabled .msgDisabled,
-:-moz-handler-blocked .msgBlocked {
+:-moz-handler-blocked .msgBlocked,
+:-moz-handler-crashed .msgCrashed,
+:-moz-handler-crashed .msgReload {
     display: block;
 }
+
+html|A {
+    color: white;
+}
--- a/toolkit/themes/winstripe/mozapps/jar.mn
+++ b/toolkit/themes/winstripe/mozapps/jar.mn
@@ -25,16 +25,17 @@ toolkit.jar:
         skin/classic/mozapps/places/defaultFavicon.png             (places/defaultFavicon.png)
         skin/classic/mozapps/places/tagContainerIcon.png           (places/tagContainerIcon.png)
 #endif
         skin/classic/mozapps/plugins/pluginProblem.css             (plugins/pluginProblem.css)
         skin/classic/mozapps/plugins/backgroundStripes.png         (plugins/backgroundStripes.png)
         skin/classic/mozapps/plugins/pluginGeneric.png             (plugins/pluginGeneric.png)
         skin/classic/mozapps/plugins/pluginDisabled.png            (plugins/pluginDisabled.png)
         skin/classic/mozapps/plugins/pluginBlocked.png             (plugins/pluginBlocked.png)
+        skin/classic/mozapps/plugins/pluginCrashed.png             (plugins/pluginCrashed.png)
         skin/classic/mozapps/plugins/pluginGeneric-16.png          (plugins/pluginGeneric-16.png)
         skin/classic/mozapps/plugins/pluginBlocked-16.png          (plugins/pluginBlocked-16.png)
         skin/classic/mozapps/plugins/pluginOutdated-16.png         (plugins/pluginOutdated-16.png)
         skin/classic/mozapps/plugins/pluginInstallerWizard.css     (plugins/pluginInstallerWizard.css)
         skin/classic/mozapps/profile/profileicon.png               (profile/profileicon.png)
         skin/classic/mozapps/profile/profileSelection.css          (profile/profileSelection.css)
         skin/classic/mozapps/update/downloadButtons.png            (update/downloadButtons.png)
         skin/classic/mozapps/update/update.png                     (update/update.png)
@@ -68,16 +69,17 @@ toolkit.jar:
         skin/classic/aero/mozapps/places/defaultFavicon.png                (places/defaultFavicon-aero.png)
         skin/classic/aero/mozapps/places/tagContainerIcon.png              (places/tagContainerIcon-aero.png)
 #endif
         skin/classic/aero/mozapps/plugins/pluginProblem.css                (plugins/pluginProblem.css)
         skin/classic/aero/mozapps/plugins/backgroundStripes.png            (plugins/backgroundStripes-aero.png)
         skin/classic/aero/mozapps/plugins/pluginGeneric.png                (plugins/pluginGeneric-aero.png)
         skin/classic/aero/mozapps/plugins/pluginDisabled.png               (plugins/pluginDisabled-aero.png)
         skin/classic/aero/mozapps/plugins/pluginBlocked.png                (plugins/pluginBlocked-aero.png)
+        skin/classic/aero/mozapps/plugins/pluginCrashed.png                (plugins/pluginCrashed-aero.png)
         skin/classic/aero/mozapps/plugins/pluginGeneric-16.png             (plugins/pluginGeneric-16-aero.png)
         skin/classic/aero/mozapps/plugins/pluginBlocked-16.png             (plugins/pluginBlocked-16-aero.png)
         skin/classic/aero/mozapps/plugins/pluginOutdated-16.png            (plugins/pluginOutdated-16-aero.png)
         skin/classic/aero/mozapps/plugins/pluginInstallerWizard.css        (plugins/pluginInstallerWizard.css)
         skin/classic/aero/mozapps/profile/profileicon.png                  (profile/profileicon-aero.png)
         skin/classic/aero/mozapps/profile/profileSelection.css             (profile/profileSelection.css)
         skin/classic/aero/mozapps/update/downloadButtons.png               (update/downloadButtons-aero.png)
         skin/classic/aero/mozapps/update/update.png                        (update/update-aero.png)
new file mode 100644
index 0000000000000000000000000000000000000000..89d3e91a995f0b60e0d4dd05a7bad57a77c8a319
GIT binary patch
literal 1470
zc$@*g1ws0WP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%U`a$lRCwC#T1`kKM-=X!=^4kLQCD|Y
zyck1>i6OXal&E-6Faa?!1VYThLQq#$Fc%L251z!6=uMEgsN|NAHLxJ=K@fw)A6%D^
zOxR>kYnYI@QD=3=$=LJLomcW|D9i5Y?wLVn$QFFqQ(f=XdsXkRsufjL6|%Y6ge-4a
zuYhRKFLE$yDhHlts=UtGyQCw$h`U2zXP#{uic_WvgBbHHq?wm$q=3Lo<^VYHuf$Ev
zHabBR5CMeougLIAm6w3ZnDPrUQ32p;G#Xt|Ei^Ko&>_7Gkq%x?Ers<=$c_YgcDwx{
z{CxzthRg`A0Ez)_G6wJmzyo*;<F~O-VR)v4S4?XMY&r_iEq5gu99&3mXG}3wUN$Y~
zmo%adoiGQGo8+Wq)nvxuT-cXPBQl&IzbNiTJSs^VQOAiB=HjHssEmWzu?R9f#&OR9
zK0pBbsRAM+L@K;a(3xW>d^{>i8c~N%mX8RcQW7^Q5%6DBb{}wvLdZxLMSgS#QsE`z
zIJ)}rs6<y@qAL%ce;=HuH50RLEEfAglB7?W9f`6Lhpc=7M6YKVT{?*3tGGKMqJ*Ro
zb(9P*e_Uu%r#{9iK+Q<i3t5-ksPnQuBuZSZ;y_OqwF;+9a04cS+4xLMI~610!B2dv
ztE=D5%*;G>xm;pzZ*O&DW8-IFpM|EDQdRwHYir+5Pf!0we)sqHS2s5|-!eh3Xw^^-
zOy#ct>VSCwAnQR<l)pjP&tYr_;61?mfDfSc!n^%Mv;+`I{%mV&>tW~#I!GURXFyMK
zLmkRWWOnFPgQ^}@R#v{=+}!*No)0i)?~&)l#YL~j<GG)RmSBB-{hQ_GWiLZda6tOV
zI|F*fOf3}S>hUw^?(Sa4EO`WYfk}rv2S!FleoRD5Fg!fG#nHc@^!P3jeauWXn$QOz
z$%QeIgfM&3Xq5v=MmF*vhr_|s)0QL4@`(X`>N_wyJNpiIGKwGhhK7dfjCNpjbhK7a
zy7BSxn^t$gvI4EGt#?{lTCOlP6G3iQSJ(C0+S=Pj3N$n{-0JM?yvEa$K8QrOttud5
z6e0yv5jkLhr$4~o`unIT&o?$UzM7brcwAIeWFH(HY_6%P>9N^trDnn?&>w7XZ{M4m
znj$tY3=9msTVG#)*93hKLhZ-s_(!L|H5B+Bk8qjTjzNJ>SOug6CM0BNCN_*hF$|4a
z(Ihik{%;ike+pv_@4QF_qRBAw`~AN|Wcp=cVd0-pC={KWoAWcdLWF{?2ojwg!sYd*
zrly+0!ooMxbvMdKp?g*s_<X*fA%U$yWx7v~dE8YXU+tcrJNo+iu8xh3HJ6l>+^~2e
zyK59ir_3@tJUo0-R#x^f91aJWb1E%GM2!Q9$liQ7ozDEdy}gfMH>xZsFu*FHmx)bb
za&qz!2v6^9!c|pO!q(Q-&E4JIj$kl&2Wp!y2;jMg^g;5vQBhGLprSI+%JcK{KUtYG
ztv*m(Tx_Fu4rTV+P-1k#3c2}lu+l<aUfvt9Tf)xH&WB4&OAp%H+sWmD3dNn0tl#PR
zz=VZsj-c1;{k62Tw8ZUpziwsXKp^n+=;$a=US3`e6>h!-6Z%ps6RXMW08E@ySy@?^
zO!NYUUjw8P`BdJ7*n9a*Zf7E##C1lpK!FH<H>xr#9|pVhK#BZTCQ1&nJ>hbVdhTiw
zow)!H8cuvK>e--9U@){@lXNPE&M+Q7kI{M0TrKj01dQte?i&0uZJvHBJ3$%aSM96`
zMHm8ENc<u0GMtwG=odF3tDjXb`aV<;^gp|*A}65LkI@&c%()NW;>&;bc&R{Y?LPqq
Y0E!xJo>h86SO5S307*qoM6N<$g3G$7cK`qY
new file mode 100644
index 0000000000000000000000000000000000000000..89d3e91a995f0b60e0d4dd05a7bad57a77c8a319
GIT binary patch
literal 1470
zc$@*g1ws0WP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%U`a$lRCwC#T1`kKM-=X!=^4kLQCD|Y
zyck1>i6OXal&E-6Faa?!1VYThLQq#$Fc%L251z!6=uMEgsN|NAHLxJ=K@fw)A6%D^
zOxR>kYnYI@QD=3=$=LJLomcW|D9i5Y?wLVn$QFFqQ(f=XdsXkRsufjL6|%Y6ge-4a
zuYhRKFLE$yDhHlts=UtGyQCw$h`U2zXP#{uic_WvgBbHHq?wm$q=3Lo<^VYHuf$Ev
zHabBR5CMeougLIAm6w3ZnDPrUQ32p;G#Xt|Ei^Ko&>_7Gkq%x?Ers<=$c_YgcDwx{
z{CxzthRg`A0Ez)_G6wJmzyo*;<F~O-VR)v4S4?XMY&r_iEq5gu99&3mXG}3wUN$Y~
zmo%adoiGQGo8+Wq)nvxuT-cXPBQl&IzbNiTJSs^VQOAiB=HjHssEmWzu?R9f#&OR9
zK0pBbsRAM+L@K;a(3xW>d^{>i8c~N%mX8RcQW7^Q5%6DBb{}wvLdZxLMSgS#QsE`z
zIJ)}rs6<y@qAL%ce;=HuH50RLEEfAglB7?W9f`6Lhpc=7M6YKVT{?*3tGGKMqJ*Ro
zb(9P*e_Uu%r#{9iK+Q<i3t5-ksPnQuBuZSZ;y_OqwF;+9a04cS+4xLMI~610!B2dv
ztE=D5%*;G>xm;pzZ*O&DW8-IFpM|EDQdRwHYir+5Pf!0we)sqHS2s5|-!eh3Xw^^-
zOy#ct>VSCwAnQR<l)pjP&tYr_;61?mfDfSc!n^%Mv;+`I{%mV&>tW~#I!GURXFyMK
zLmkRWWOnFPgQ^}@R#v{=+}!*No)0i)?~&)l#YL~j<GG)RmSBB-{hQ_GWiLZda6tOV
zI|F*fOf3}S>hUw^?(Sa4EO`WYfk}rv2S!FleoRD5Fg!fG#nHc@^!P3jeauWXn$QOz
z$%QeIgfM&3Xq5v=MmF*vhr_|s)0QL4@`(X`>N_wyJNpiIGKwGhhK7dfjCNpjbhK7a
zy7BSxn^t$gvI4EGt#?{lTCOlP6G3iQSJ(C0+S=Pj3N$n{-0JM?yvEa$K8QrOttud5
z6e0yv5jkLhr$4~o`unIT&o?$UzM7brcwAIeWFH(HY_6%P>9N^trDnn?&>w7XZ{M4m
znj$tY3=9msTVG#)*93hKLhZ-s_(!L|H5B+Bk8qjTjzNJ>SOug6CM0BNCN_*hF$|4a
z(Ihik{%;ike+pv_@4QF_qRBAw`~AN|Wcp=cVd0-pC={KWoAWcdLWF{?2ojwg!sYd*
zrly+0!ooMxbvMdKp?g*s_<X*fA%U$yWx7v~dE8YXU+tcrJNo+iu8xh3HJ6l>+^~2e
zyK59ir_3@tJUo0-R#x^f91aJWb1E%GM2!Q9$liQ7ozDEdy}gfMH>xZsFu*FHmx)bb
za&qz!2v6^9!c|pO!q(Q-&E4JIj$kl&2Wp!y2;jMg^g;5vQBhGLprSI+%JcK{KUtYG
ztv*m(Tx_Fu4rTV+P-1k#3c2}lu+l<aUfvt9Tf)xH&WB4&OAp%H+sWmD3dNn0tl#PR
zz=VZsj-c1;{k62Tw8ZUpziwsXKp^n+=;$a=US3`e6>h!-6Z%ps6RXMW08E@ySy@?^
zO!NYUUjw8P`BdJ7*n9a*Zf7E##C1lpK!FH<H>xr#9|pVhK#BZTCQ1&nJ>hbVdhTiw
zow)!H8cuvK>e--9U@){@lXNPE&M+Q7kI{M0TrKj01dQte?i&0uZJvHBJ3$%aSM96`
zMHm8ENc<u0GMtwG=odF3tDjXb`aV<;^gp|*A}65LkI@&c%()NW;>&;bc&R{Y?LPqq
Y0E!xJo>h86SO5S307*qoM6N<$g3G$7cK`qY
--- a/toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css
+++ b/toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css
@@ -49,22 +49,31 @@ html|applet:not([height]), html|applet[h
     background-image: url(chrome://mozapps/skin/plugins/pluginGeneric.png);
 }
 :-moz-handler-disabled .icon {
     background-image: url(chrome://mozapps/skin/plugins/pluginDisabled.png);
 }
 :-moz-handler-blocked .icon {
     background-image: url(chrome://mozapps/skin/plugins/pluginBlocked.png);
 }
+:-moz-handler-crashed .icon {
+    background-image: url(chrome://mozapps/skin/plugins/pluginCrashed.png);
+}
 
 .msg {
     display: none;
     color: white;
     font: 12px sans-serif;
     font-weight: bold;
     cursor: default;
     text-shadow: rgba(0,0,0,0.8) 0 0 5px;
 }
 :-moz-type-unsupported .msgUnsupported,
 :-moz-handler-disabled .msgDisabled,
-:-moz-handler-blocked .msgBlocked {
+:-moz-handler-blocked .msgBlocked,
+:-moz-handler-crashed .msgCrashed,
+:-moz-handler-crashed .msgReload {
     display: block;
 }
+
+html|A {
+    color: white;
+}