Bug 550293 - plugin-crashed UI needs more user opt-in. r=vlad, ui-r=faaborg

This commit is contained in:
Justin Dolske 2010-03-16 22:10:08 -07:00
parent 8bcfd80422
commit 1ab06617a3
16 changed files with 341 additions and 94 deletions

View File

@ -5944,30 +5944,72 @@ var gMissingPluginInstaller = {
return this.crashReportHelpURL; return this.crashReportHelpURL;
}, },
addLinkClickCallback: function (linkNode, callbackName, callbackArg) {
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
let self = this;
linkNode.addEventListener("click",
function(evt) {
if (!evt.isTrusted)
return;
evt.preventDefault();
if (callbackArg == undefined)
callbackArg = evt;
(self[callbackName])(callbackArg);
},
true);
linkNode.addEventListener("keydown",
function(evt) {
if (!evt.isTrusted)
return;
if (evt.keyCode == evt.DOM_VK_RETURN) {
evt.preventDefault();
if (callbackArg == undefined)
callbackArg = evt;
evt.preventDefault();
(self[callbackName])(callbackArg);
}
},
true);
},
// Callback for user clicking on a missing (unsupported) plugin.
installSinglePlugin: function (aEvent) { installSinglePlugin: function (aEvent) {
if (!aEvent.isTrusted)
return;
var missingPluginsArray = {}; var missingPluginsArray = {};
var pluginInfo = getPluginInfo(aEvent.target); var pluginInfo = getPluginInfo(aEvent.target);
missingPluginsArray[pluginInfo.mimetype] = pluginInfo; missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
if (missingPluginsArray) { openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", "PFSWindow", "chrome,centerscreen,resizable=yes",
"PFSWindow", "chrome,centerscreen,resizable=yes", {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
{plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
}
aEvent.stopPropagation();
}, },
// Callback for user clicking on a disabled plugin
managePlugins: function (aEvent) { managePlugins: function (aEvent) {
if (!aEvent.isTrusted)
return;
BrowserOpenAddonsMgr("plugins"); BrowserOpenAddonsMgr("plugins");
aEvent.stopPropagation();
}, },
// Callback for user clicking "submit a report" link
submitReport : function(minidumpID) {
// 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.
this.CrashSubmit.submit(minidumpID, gBrowser, null, null);
},
// Callback for user clicking a "reload page" link
reloadPage: function (browser) {
browser.reload();
},
// Callback for user clicking the help icon
openHelpPage: function () {
openHelpLink("plugin-crashed", false);
},
// event listener for missing/blocklisted/outdated plugins.
newMissingPlugin: function (aEvent) { newMissingPlugin: function (aEvent) {
// Since we are expecting also untrusted events, make sure // Since we are expecting also untrusted events, make sure
// that the target is a plugin // that the target is a plugin
@ -5982,14 +6024,7 @@ var gMissingPluginInstaller = {
if (aEvent.type != "PluginBlocklisted" && if (aEvent.type != "PluginBlocklisted" &&
aEvent.type != "PluginOutdated" && aEvent.type != "PluginOutdated" &&
!(aEvent.target instanceof HTMLObjectElement)) { !(aEvent.target instanceof HTMLObjectElement)) {
aEvent.target.addEventListener("click", gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "installSinglePlugin");
gMissingPluginInstaller.installSinglePlugin,
true);
aEvent.target.addEventListener("keydown",
function(evt) { if (evt.keyCode == evt.DOM_VK_RETURN)
gMissingPluginInstaller.installSinglePlugin(evt) },
true);
} }
let hideBarPrefName = aEvent.type == "PluginOutdated" ? let hideBarPrefName = aEvent.type == "PluginOutdated" ?
@ -6110,13 +6145,7 @@ var gMissingPluginInstaller = {
if (!(aEvent.target instanceof Ci.nsIObjectLoadingContent)) if (!(aEvent.target instanceof Ci.nsIObjectLoadingContent))
return; return;
aEvent.target.addEventListener("click", gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "managePlugins");
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 // Crashed-plugin observer. Notified once per plugin crash, before events
@ -6128,17 +6157,24 @@ var gMissingPluginInstaller = {
return; return;
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
let minidumpID = subject.getPropertyAsAString("minidumpID"); let minidumpID = propertyBag.getPropertyAsAString("minidumpID");
let submitted = gCrashReporter.submitReports && minidumpID.length; let shouldSubmit = gCrashReporter.submitReports;
// The crash reporter wants a DOM element it can append an IFRAME to, let doPrompt = true; // XXX followup to get via gCrashReporter
// which it uses to submit a form. Let's just give it gBrowser.
if (submitted) // Submit automatically when appropriate.
submitted = gMissingPluginInstaller.CrashSubmit.submit(minidumpID, gBrowser, null, null); if (minidumpID && shouldSubmit && !doPrompt) {
propertyBag.setPropertyAsBool("submittedCrashReport", submitted); this.submitReport(minidumpID);
// Submission is async, so we can't easily show failure UI.
propertyBag.setPropertyAsBool("submittedCrashReport", true);
}
#endif #endif
}, },
// Crashed-plugin event listener. Called for every instance of a
// plugin in content.
pluginInstanceCrashed: function (aEvent) { pluginInstanceCrashed: function (aEvent) {
let self = gMissingPluginInstaller;
// Evil content could fire a fake event at us, ignore them. // Evil content could fire a fake event at us, ignore them.
if (!aEvent.isTrusted) if (!aEvent.isTrusted)
return; return;
@ -6147,7 +6183,10 @@ var gMissingPluginInstaller = {
return; return;
let submittedReport = aEvent.getData("submittedCrashReport"); let submittedReport = aEvent.getData("submittedCrashReport");
let doPrompt = true; // XXX followup for .getData("doPrompt");
let submitReports = true; // XXX followup for .getData("submitReports");
let pluginName = aEvent.getData("pluginName"); let pluginName = aEvent.getData("pluginName");
let minidumpID = aEvent.getData("minidumpID");
// We're expecting this to be a plugin. // We're expecting this to be a plugin.
let plugin = aEvent.target; let plugin = aEvent.target;
@ -6171,36 +6210,77 @@ var gMissingPluginInstaller = {
overlay.removeAttribute("role"); overlay.removeAttribute("role");
#ifdef MOZ_CRASHREPORTER #ifdef MOZ_CRASHREPORTER
// Determine which message to show regarding crash reports.
let helpClass, showClass; let helpClass, showClass;
if (submittedReport) { // submitReports && !doPrompt, handled in observer
// If we didn't submit a report but don't have submission disabled,
// we probably just didn't collect a crash report; don't put up any
// special crashing text.
if (submittedReport) {
helpClass = "submitLink";
showClass = "msg msgSubmitted"; showClass = "msg msgSubmitted";
} }
else if (!gCrashReporter.submitReports) { else if (!submitReports && !doPrompt) {
helpClass = "notSubmitLink";
showClass = "msg msgNotSubmitted"; showClass = "msg msgNotSubmitted";
} }
else { // doPrompt
showClass = "msg msgPleaseSubmit";
// XXX can we make the link target actually be blank?
let pleaseLink = doc.getAnonymousElementByAttribute(
plugin, "class", "pleaseSubmitLink");
self.addLinkClickCallback(pleaseLink, "submitReport", minidumpID);
}
if (helpClass) { // If we don't have a minidumpID, we can't (or didn't) submit anything.
let helpLink = doc.getAnonymousElementByAttribute(plugin, "class", helpClass); // This can happen if the plugin is killed from the task manager.
helpLink.href = gMissingPluginInstaller.crashReportHelpURL; if (!minidumpID) {
let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass); showClass = "msg msgNoCrashReport";
textToShow.style.display = "block"; }
let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", showClass);
textToShow.style.display = "block";
let bottomLinks = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgBottomLinks");
bottomLinks.style.display = "block";
let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
self.addLinkClickCallback(helpIcon, "openHelpPage");
// If we're showing the link to manually trigger report submission, we'll
// want to be able to update all the instances of the UI for this crash to
// show an updated message when a report is submitted.
if (doPrompt) {
let observer = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
observe : function(subject, topic, data) {
let propertyBag = subject;
if (!(propertyBag instanceof Ci.nsIPropertyBag2))
return;
// Ignore notifications for other crashes.
if (propertyBag.get("minidumpID") != minidumpID)
return;
self.updateSubmissionStatus(plugin, propertyBag, data);
},
handleEvent : function(event) {
// Not expected to be called, just here for the closure.
}
}
// Use a weak reference, so we don't have to remove it...
Services.obs.addObserver(observer, "crash-report-status", true);
// ...alas, now we need something to hold a strong reference to prevent
// it from being GC. But I don't want to manually manage the reference's
// lifetime (which should be no greater than the page).
// Clever solution? Use a closue with an event listener on the document.
// When the doc goes away, so do the listener references and the closure.
doc.addEventListener("mozCleverClosureHack", observer, false);
} }
#endif #endif
let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed"); let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
crashText.textContent = messageString; crashText.textContent = messageString;
let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink"); let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
link.addEventListener("click", function(e) { if (e.isTrusted) browser.reload(); }, true);
let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
self.addLinkClickCallback(link, "reloadPage", browser);
let browser = gBrowser.getBrowserForDocument(plugin.ownerDocument
.defaultView.top.document);
let notificationBox = gBrowser.getNotificationBox(browser); let notificationBox = gBrowser.getNotificationBox(browser);
// Is the <object>'s size too small to hold what we want to show? // Is the <object>'s size too small to hold what we want to show?
@ -6216,7 +6296,7 @@ var gMissingPluginInstaller = {
// If another plugin on the page was large enough to show our UI, we // If another plugin on the page was large enough to show our UI, we
// don't want to show a notification bar. // don't want to show a notification bar.
if (!doc.mozNoPluginCrashedNotification) if (!doc.mozNoPluginCrashedNotification)
showNotificationBar(); showNotificationBar(minidumpID);
} else { } else {
// If a previous plugin on the page was too small and resulted in // If a previous plugin on the page was too small and resulted in
// adding a notification bar, then remove it because this plugin // adding a notification bar, then remove it because this plugin
@ -6231,7 +6311,7 @@ var gMissingPluginInstaller = {
notificationBox.removeNotification(notification, true); notificationBox.removeNotification(notification, true);
} }
function showNotificationBar() { function showNotificationBar(minidumpID) {
// If there's already an existing notification bar, don't do anything. // If there's already an existing notification bar, don't do anything.
let notification = notificationBox.getNotificationWithValue("plugin-crashed"); let notification = notificationBox.getNotificationWithValue("plugin-crashed");
if (notification) if (notification)
@ -6240,22 +6320,68 @@ var gMissingPluginInstaller = {
// Configure the notification bar // Configure the notification bar
let priority = notificationBox.PRIORITY_WARNING_MEDIUM; let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png"; let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
let label = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label"); let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
let accessKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey"); let reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
let submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
let buttons = [{ let buttons = [
label: label, #ifdef MOZ_CRASHREPORTER
accessKey: accessKey, {
popup: null, label: submitLabel,
callback: function() { browser.reload(); }, accessKey: submitKey,
}]; popup: null,
callback: function() { gMissingPluginInstaller.submitReport(minidumpID); },
},
#endif
{
label: reloadLabel,
accessKey: reloadKey,
popup: null,
callback: function() { browser.reload(); },
}];
let notification = notificationBox.appendNotification(messageString, "plugin-crashed", let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
iconURL, priority, buttons); iconURL, priority, buttons);
// Add the "learn more" link.
let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let link = notification.ownerDocument.createElementNS(XULNS, "label");
link.className = "text-link";
link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
link.href = gMissingPluginInstaller.crashReportHelpURL;
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
description.appendChild(link);
} }
}, },
updateSubmissionStatus : function (plugin, propBag, status) {
let doc = plugin.ownerDocument;
// One of these two may already be visible, reset them to be hidden.
let pleaseText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgPleaseSubmit");
let submittingText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgSubmitting");
pleaseText.style.display = "";
submittingText.style.display = "";
let msgClass;
switch (status) {
case "submitting":
msgClass = "msg msgSubmitting";
break;
case "success":
msgClass = "msg msgSubmitted";
break;
case "failed":
msgClass = "msg msgSubmitFailed";
break;
}
let textToShow = doc.getAnonymousElementByAttribute(plugin, "class", msgClass);
textToShow.style.display = "block";
},
refreshBrowser: function (aEvent) { refreshBrowser: function (aEvent) {
// browser elements are anonymous so we can't just use target. // browser elements are anonymous so we can't just use target.
var browser = aEvent.originalTarget; var browser = aEvent.originalTarget;

View File

@ -77,6 +77,9 @@ blockedpluginsMessage.searchButton.accesskey=U
crashedpluginsMessage.title=The %S plugin has crashed. crashedpluginsMessage.title=The %S plugin has crashed.
crashedpluginsMessage.reloadButton.label=Reload page crashedpluginsMessage.reloadButton.label=Reload page
crashedpluginsMessage.reloadButton.accesskey=R crashedpluginsMessage.reloadButton.accesskey=R
crashedpluginsMessage.submitButton.label=Submit a crash report
crashedpluginsMessage.submitButton.accesskey=S
crashedpluginsMessage.learnMore=Learn More…
# Sanitize # Sanitize
# LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to # LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to

View File

@ -40,6 +40,7 @@
interface nsIFrame; interface nsIFrame;
interface nsIObjectFrame; interface nsIObjectFrame;
interface nsIPluginInstance; interface nsIPluginInstance;
interface nsIPluginTag;
interface nsIDOMElement; interface nsIDOMElement;
interface nsIDOMClientRect; interface nsIDOMClientRect;
@ -124,6 +125,7 @@ interface nsIObjectLoadingContent : nsISupports
*/ */
[noscript] nsIFrame getPrintFrame(); [noscript] nsIFrame getPrintFrame();
[noscript] void pluginCrashed(in AString pluginName, [noscript] void pluginCrashed(in nsIPluginTag pluginTag,
in AString minidumpID,
in boolean submittedCrashReport); in boolean submittedCrashReport);
}; };

View File

@ -221,13 +221,16 @@ nsPluginErrorEvent::Run()
class nsPluginCrashedEvent : public nsRunnable { class nsPluginCrashedEvent : public nsRunnable {
public: public:
nsCOMPtr<nsIContent> mContent; nsCOMPtr<nsIContent> mContent;
nsString mMinidumpID;
nsString mPluginName; nsString mPluginName;
PRBool mSubmittedCrashReport; PRBool mSubmittedCrashReport;
nsPluginCrashedEvent(nsIContent* aContent, nsPluginCrashedEvent(nsIContent* aContent,
const nsAString& aMinidumpID,
const nsAString& aPluginName, const nsAString& aPluginName,
PRBool submittedCrashReport) PRBool submittedCrashReport)
: mContent(aContent), : mContent(aContent),
mMinidumpID(aMinidumpID),
mPluginName(aPluginName), mPluginName(aPluginName),
mSubmittedCrashReport(submittedCrashReport) mSubmittedCrashReport(submittedCrashReport)
{} {}
@ -266,6 +269,15 @@ nsPluginCrashedEvent::Run()
nsCOMPtr<nsIWritableVariant> variant; nsCOMPtr<nsIWritableVariant> variant;
// add a "minidumpID" property to this event
variant = do_CreateInstance("@mozilla.org/variant;1");
if (!variant) {
NS_WARNING("Couldn't create minidumpID variant for PluginCrashed event!");
return NS_OK;
}
variant->SetAsAString(mMinidumpID);
containerEvent->SetData(NS_LITERAL_STRING("minidumpID"), variant);
// add a "pluginName" property to this event // add a "pluginName" property to this event
variant = do_CreateInstance("@mozilla.org/variant;1"); variant = do_CreateInstance("@mozilla.org/variant;1");
if (!variant) { if (!variant) {
@ -2008,15 +2020,23 @@ nsObjectLoadingContent::SetAbsoluteScreenPosition(nsIDOMElement* element,
} }
NS_IMETHODIMP NS_IMETHODIMP
nsObjectLoadingContent::PluginCrashed(const nsAString& pluginName, nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
const nsAString& minidumpID,
PRBool submittedCrashReport) PRBool submittedCrashReport)
{ {
AutoNotifier notifier(this, PR_TRUE); AutoNotifier notifier(this, PR_TRUE);
UnloadContent(); UnloadContent();
mFallbackReason = ePluginCrashed; mFallbackReason = ePluginCrashed;
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this)); nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
// Note that aPluginTag in invalidated after we're called, so copy
// out any data we need now.
nsCAutoString pluginName;
aPluginTag->GetName(pluginName);
nsCOMPtr<nsIRunnable> ev = new nsPluginCrashedEvent(thisContent, nsCOMPtr<nsIRunnable> ev = new nsPluginCrashedEvent(thisContent,
pluginName, minidumpID,
NS_ConvertUTF8toUTF16(pluginName),
submittedCrashReport); submittedCrashReport);
nsresult rv = NS_DispatchToCurrentThread(ev); nsresult rv = NS_DispatchToCurrentThread(ev);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {

View File

@ -5035,6 +5035,7 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin, const nsAString& dumpID)
nsCOMPtr<nsIWritablePropertyBag2> propbag = do_CreateInstance("@mozilla.org/hash-property-bag;1"); nsCOMPtr<nsIWritablePropertyBag2> propbag = do_CreateInstance("@mozilla.org/hash-property-bag;1");
if (obsService && propbag) { if (obsService && propbag) {
propbag->SetPropertyAsAString(NS_LITERAL_STRING("minidumpID"), dumpID); propbag->SetPropertyAsAString(NS_LITERAL_STRING("minidumpID"), dumpID);
propbag->SetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"), submittedCrashReport);
obsService->NotifyObservers(propbag, "plugin-crashed", nsnull); obsService->NotifyObservers(propbag, "plugin-crashed", nsnull);
// see if an observer submitted a crash report. // see if an observer submitted a crash report.
propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"), &submittedCrashReport); propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"), &submittedCrashReport);
@ -5050,8 +5051,7 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin, const nsAString& dumpID)
instanceTag->mInstance->GetDOMElement(getter_AddRefs(domElement)); instanceTag->mInstance->GetDOMElement(getter_AddRefs(domElement));
nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement)); nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
if (objectContent) { if (objectContent) {
objectContent->PluginCrashed(NS_ConvertUTF8toUTF16(pluginTag->mName), objectContent->PluginCrashed(pluginTag, dumpID, submittedCrashReport);
submittedCrashReport);
} }
instanceTag->mInstance->Stop(); instanceTag->mInstance->Stop();

View File

@ -35,6 +35,8 @@
* *
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
Components.utils.import("resource://gre/modules/Services.jsm");
let EXPORTED_SYMBOLS = [ let EXPORTED_SYMBOLS = [
"CrashSubmit" "CrashSubmit"
]; ];
@ -44,6 +46,10 @@ const Ci = Components.interfaces;
const STATE_START = Ci.nsIWebProgressListener.STATE_START; const STATE_START = Ci.nsIWebProgressListener.STATE_START;
const STATE_STOP = Ci.nsIWebProgressListener.STATE_STOP; const STATE_STOP = Ci.nsIWebProgressListener.STATE_STOP;
const SUCCESS = "success";
const FAILED = "failed";
const SUBMITTING = "submitting";
let reportURL = null; let reportURL = null;
let strings = null; let strings = null;
let myListener = null; let myListener = null;
@ -191,6 +197,7 @@ Submitter.prototype = {
submitSuccess: function Submitter_submitSuccess(ret) submitSuccess: function Submitter_submitSuccess(ret)
{ {
if (!ret.CrashID) { if (!ret.CrashID) {
this.notifyStatus(FAILED);
this.cleanup(); this.cleanup();
return; return;
} }
@ -207,8 +214,7 @@ Submitter.prototype = {
// report an error? not much the user can do here. // report an error? not much the user can do here.
} }
if (this.successCallback) this.notifyStatus(SUCCESS, ret);
this.successCallback(this.id, ret);
this.cleanup(); this.cleanup();
}, },
@ -272,9 +278,7 @@ Submitter.prototype = {
// check general request status first // check general request status first
if (!Components.isSuccessCode(aStatus)) { if (!Components.isSuccessCode(aStatus)) {
this.element.removeChild(this.iframe); this.element.removeChild(this.iframe);
if (this.errorCallback) { this.notifyStatus(FAILED);
this.errorCallback(this.id);
}
this.cleanup(); this.cleanup();
return 0; return 0;
} }
@ -282,9 +286,7 @@ Submitter.prototype = {
if (aRequest instanceof Ci.nsIHttpChannel && if (aRequest instanceof Ci.nsIHttpChannel &&
aRequest.responseStatus != 200) { aRequest.responseStatus != 200) {
this.element.removeChild(this.iframe); this.element.removeChild(this.iframe);
if (this.errorCallback) { this.notifyStatus(FAILED);
this.errorCallback(this.id);
}
this.cleanup(); this.cleanup();
return 0; return 0;
} }
@ -301,13 +303,39 @@ Submitter.prototype = {
onStatusChange: function() {return 0;}, onStatusChange: function() {return 0;},
onSecurityChange: function() {return 0;}, onSecurityChange: function() {return 0;},
notifyStatus: function Submitter_notify(status, ret)
{
let propBag = Cc["@mozilla.org/hash-property-bag;1"].
createInstance(Ci.nsIWritablePropertyBag2);
propBag.setPropertyAsAString("minidumpID", this.id);
Services.obs.notifyObservers(propBag, "crash-report-status", status);
switch (status) {
case SUCCESS:
if (this.successCallback)
this.successCallback(this.id, ret);
break;
case FAILED:
if (this.errorCallback)
this.errorCallback(this.id);
break;
default:
// no callbacks invoked.
}
},
submit: function Submitter_submit() submit: function Submitter_submit()
{ {
let [dump, extra] = getPendingMinidump(this.id); let [dump, extra] = getPendingMinidump(this.id);
if (!dump.exists() || !extra.exists()) { if (!dump.exists() || !extra.exists()) {
this.notifyStatus(FAILED);
this.cleanup(); this.cleanup();
return false; return false;
} }
this.notifyStatus(SUBMITTING);
this.dump = dump; this.dump = dump;
this.extra = extra; this.extra = extra;
let iframe = this.document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "iframe"); let iframe = this.document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "iframe");
@ -320,8 +348,10 @@ Submitter.prototype = {
if (iframe.contentWindow.location == "about:blank") if (iframe.contentWindow.location == "about:blank")
return; return;
iframe.removeEventListener("load", loadHandler, true); iframe.removeEventListener("load", loadHandler, true);
if (!self.submitForm()) if (!self.submitForm()) {
this.notifyStatus(FAILED);
self.cleanup(); self.cleanup();
}
} }
iframe.addEventListener("load", loadHandler, true); iframe.addEventListener("load", loadHandler, true);

View File

@ -29,15 +29,10 @@
<!ENTITY reloadPlugin.pre ""> <!ENTITY reloadPlugin.pre "">
<!ENTITY reloadPlugin.middle "Reload the page"> <!ENTITY reloadPlugin.middle "Reload the page">
<!ENTITY reloadPlugin.post " to try again."> <!ENTITY reloadPlugin.post " to try again.">
<!-- LOCALIZATION NOTE (submittedCrashReport.pre): include a trailing space as needed --> <!-- LOCALIZATION NOTE (report.please): This and the other report.* strings should be as short as possible, ideally 2-3 words. -->
<!-- LOCALIZATION NOTE (submittedCrashReport.middle): avoid leading/trailing spaces, this text is a link --> <!ENTITY report.please "Send crash report">
<!-- LOCALIZATION NOTE (submittedCrashReport.post): include a starting space as needed --> <!ENTITY report.submitting "Sending report…">
<!ENTITY submittedCrashReport.pre "A "> <!ENTITY report.submitted "Crash report sent.">
<!ENTITY submittedCrashReport.middle "crash report"> <!ENTITY report.disabled "Crash reporting disabled.">
<!ENTITY submittedCrashReport.post " was submitted."> <!ENTITY report.failed "Submission failed.">
<!-- LOCALIZATION NOTE (notSubmittedCrashReport.pre): include a trailing space as needed --> <!ENTITY report.unavailable "No report available.">
<!-- 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.">

View File

@ -58,9 +58,17 @@
<html:div class="msg msgCrashed"><!-- set at runtime --></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> <html:div class="msg msgReload">&reloadPlugin.pre;<html:a class="reloadLink" href="">&reloadPlugin.middle;</html:a>&reloadPlugin.post;</html:div>
<xul:spacer flex="1"/> <xul:spacer flex="1"/>
<!-- link hrefs set at runtime --> <html:div class="msg msgBottomLinks">
<html:div class="msg msgSubmitted">&submittedCrashReport.pre;<html:a class="submitLink" href="">&submittedCrashReport.middle;</html:a>&submittedCrashReport.post;</html:div> <!-- link href set at runtime -->
<html:div class="msg msgNotSubmitted">&notSubmittedCrashReport.pre;<html:a class="notSubmitLink" href="">&notSubmittedCrashReport.middle;</html:a>&notSubmittedCrashReport.post;</html:div> <html:div class="msg msgPleaseSubmit"><html:a class="pleaseSubmitLink" href="">&report.please;</html:a></html:div>
<html:div class="msg msgSubmitting">&report.submitting;<html:span class="throbber"> </html:span></html:div>
<html:div class="msg msgSubmitted">&report.submitted;</html:div>
<html:div class="msg msgNotSubmitted">&report.disabled;</html:div>
<html:div class="msg msgSubmitFailed">&report.failed;</html:div>
<html:div class="msg msgNoCrashReport">&report.unavailable;</html:div>
<!-- link href set at runtime -->
<html:span class="helpIcon" role="link"/>
</html:div>
</xul:vbox> </xul:vbox>
<html:div style="display:none;"><children/></html:div> <html:div style="display:none;"><children/></html:div>
</content> </content>

View File

@ -12,6 +12,10 @@ html|applet:not([height]), html|applet[height=""] {
height: 200px; height: 200px;
} }
:-moz-type-unsupported .mainBox {
cursor: pointer;
}
:-moz-type-unsupported .mainBox, :-moz-type-unsupported .mainBox,
:-moz-handler-disabled .mainBox, :-moz-handler-disabled .mainBox,
:-moz-handler-blocked .mainBox { :-moz-handler-blocked .mainBox {
@ -40,3 +44,7 @@ html|applet:not([height]), html|applet[height=""] {
:-moz-handler-crashed .msgReload { :-moz-handler-crashed .msgReload {
display: block; display: block;
} }
.helpIcon {
cursor: pointer;
}

View File

@ -27,6 +27,7 @@ toolkit.jar:
skin/classic/mozapps/plugins/pluginGeneric-16.png (plugins/pluginGeneric-16.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/pluginBlocked-16.png (plugins/pluginBlocked-16.png)
skin/classic/mozapps/plugins/pluginOutdated-16.png (plugins/pluginOutdated-16.png) skin/classic/mozapps/plugins/pluginOutdated-16.png (plugins/pluginOutdated-16.png)
skin/classic/mozapps/plugins/pluginHelp-16.png (plugins/pluginHelp-16.png)
skin/classic/mozapps/profile/profileicon.png (profile/profileicon.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/profileicon-selected.png (profile/profileicon-selected.png)
skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css) skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css)

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

View File

@ -41,9 +41,35 @@ html|a {
background-image: url(chrome://mozapps/skin/plugins/pluginCrashed.png); background-image: url(chrome://mozapps/skin/plugins/pluginCrashed.png);
} }
.throbber {
padding-left: 16px; /* width of the background image */
background: url(chrome://global/skin/icons/loading_16.png) no-repeat;
margin-left: 5px;
}
.msg { .msg {
font: 12px sans-serif; font: message-box;
font-weight: bold; font-size: 12px;
cursor: default; cursor: default;
text-shadow: rgba(0,0,0,0.8) 0 0 5px; text-shadow: rgba(0,0,0,0.8) 0 0 5px;
} }
.msgBottomLinks {
padding-left: 2px;
padding-right: 2px;
}
.msgBottomLinks div {
text-align: right;
margin-right: 4px;
margin-bottom: -19px;
min-height: 19px; /* height of biggest line (with throbber) */
}
.helpIcon {
float: left;
display: inline-block;
min-width: 16px;
min-height: 16px;
background: url(chrome://mozapps/skin/plugins/pluginHelp-16.png) no-repeat;
}

View File

@ -33,6 +33,7 @@ toolkit.jar:
skin/classic/mozapps/plugins/pluginGeneric-16.png (plugins/pluginGeneric-16.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/pluginBlocked-16.png (plugins/pluginBlocked-16.png)
skin/classic/mozapps/plugins/pluginOutdated-16.png (plugins/pluginOutdated-16.png) skin/classic/mozapps/plugins/pluginOutdated-16.png (plugins/pluginOutdated-16.png)
skin/classic/mozapps/plugins/pluginHelp-16.png (plugins/pluginHelp-16.png)
skin/classic/mozapps/plugins/pluginInstallerWizard.css (plugins/pluginInstallerWizard.css) skin/classic/mozapps/plugins/pluginInstallerWizard.css (plugins/pluginInstallerWizard.css)
skin/classic/mozapps/profile/profileicon.png (profile/profileicon.png) skin/classic/mozapps/profile/profileicon.png (profile/profileicon.png)
skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css) skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css)
@ -75,6 +76,7 @@ toolkit.jar:
skin/classic/aero/mozapps/plugins/pluginGeneric-16.png (plugins/pluginGeneric-16-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/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/pluginOutdated-16.png (plugins/pluginOutdated-16-aero.png)
skin/classic/aero/mozapps/plugins/pluginHelp-16.png (plugins/pluginHelp-16-aero.png)
skin/classic/aero/mozapps/plugins/pluginInstallerWizard.css (plugins/pluginInstallerWizard.css) 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/profileicon.png (profile/profileicon-aero.png)
skin/classic/aero/mozapps/profile/profileSelection.css (profile/profileSelection.css) skin/classic/aero/mozapps/profile/profileSelection.css (profile/profileSelection.css)

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

View File

@ -41,9 +41,35 @@ html|a {
background-image: url(chrome://mozapps/skin/plugins/pluginCrashed.png); background-image: url(chrome://mozapps/skin/plugins/pluginCrashed.png);
} }
.throbber {
padding-left: 16px; /* width of the background image */
background: url(chrome://global/skin/icons/loading_16.png) no-repeat;
margin-left: 5px;
}
.msg { .msg {
font: 12px sans-serif; font: message-box;
font-weight: bold; font-size: 12px;
cursor: default; cursor: default;
text-shadow: rgba(0,0,0,0.8) 0 0 5px; text-shadow: rgba(0,0,0,0.8) 0 0 5px;
} }
.msgBottomLinks {
padding-left: 2px;
padding-right: 2px;
}
.msgBottomLinks div {
text-align: right;
margin-right: 4px;
margin-bottom: -19px;
min-height: 19px; /* height of biggest line (with throbber) */
}
.helpIcon {
float: left;
display: inline-block;
min-width: 16px;
min-height: 16px;
background: url(chrome://mozapps/skin/plugins/pluginHelp-16.png) no-repeat;
}