From 1f895f93b6d788f922fd39bf00b51e47f14a0287 Mon Sep 17 00:00:00 2001 From: Justin Dolske Date: Tue, 9 Feb 2010 17:05:32 -0800 Subject: [PATCH] Bug 538910 - Plugins: Need a "plugin crashed" UI. r=gavin, ui-r=faaborg, icon=shorlander --- browser/base/content/browser.js | 142 +++++++++++++++++- .../en-US/chrome/browser/browser.properties | 3 + .../crashreporter/content/oopcrashdialog.js | 33 ---- .../crashreporter/content/oopcrashdialog.xul | 32 ---- toolkit/crashreporter/jar.mn | 2 - toolkit/crashreporter/nsExceptionHandler.cpp | 32 ---- .../en-US/chrome/mozapps/plugins/plugins.dtd | 18 +++ .../mozapps/plugins/content/pluginProblem.xml | 5 + .../plugins/content/pluginProblemBinding.css | 5 +- toolkit/themes/pinstripe/mozapps/jar.mn | 1 + .../mozapps/plugins/pluginCrashed.png | Bin 0 -> 1470 bytes .../mozapps/plugins/pluginProblem.css | 11 +- toolkit/themes/winstripe/mozapps/jar.mn | 2 + .../mozapps/plugins/pluginCrashed-aero.png | Bin 0 -> 1470 bytes .../mozapps/plugins/pluginCrashed.png | Bin 0 -> 1470 bytes .../mozapps/plugins/pluginProblem.css | 11 +- 16 files changed, 194 insertions(+), 103 deletions(-) delete mode 100644 toolkit/crashreporter/content/oopcrashdialog.js delete mode 100644 toolkit/crashreporter/content/oopcrashdialog.xul create mode 100644 toolkit/themes/pinstripe/mozapps/plugins/pluginCrashed.png create mode 100644 toolkit/themes/winstripe/mozapps/plugins/pluginCrashed-aero.png create mode 100644 toolkit/themes/winstripe/mozapps/plugins/pluginCrashed.png diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 2f42e7ca17e..37f5de130f3 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1101,14 +1101,18 @@ function HandleAppCommandEvent(evt) { } 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; @@ -1152,7 +1156,6 @@ function prepareForStartup() { // 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 @@ -5993,6 +5996,20 @@ function getPluginInfo(pluginElement) 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; @@ -6168,6 +6185,129 @@ var gMissingPluginInstaller = { 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 '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; diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 0550d4fcf5d..19974e9a102 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -74,6 +74,9 @@ 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 diff --git a/toolkit/crashreporter/content/oopcrashdialog.js b/toolkit/crashreporter/content/oopcrashdialog.js deleted file mode 100644 index b0be226734c..00000000000 --- 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; -} diff --git a/toolkit/crashreporter/content/oopcrashdialog.xul b/toolkit/crashreporter/content/oopcrashdialog.xul deleted file mode 100644 index 6bb8f1bd9da..00000000000 --- a/toolkit/crashreporter/content/oopcrashdialog.xul +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - -