mirror of
https://github.com/ManchildProductions/binoc-central-mirror.git
synced 2026-06-15 21:09:05 +00:00
688 lines
26 KiB
JavaScript
688 lines
26 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
/* A note to the curious: a large portion of this code was copied over from
|
|
* mozilla/browser/base/content/browser.js
|
|
*/
|
|
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
|
|
|
if (AppConstants.MOZ_CRASHREPORTER) {
|
|
XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
|
|
"@mozilla.org/xre/app-info;1",
|
|
"nsICrashReporter");
|
|
}
|
|
|
|
function getPluginInfo(pluginElement)
|
|
{
|
|
var tagMimetype;
|
|
var pluginsPage;
|
|
if (pluginElement instanceof HTMLAppletElement) {
|
|
tagMimetype = "application/x-java-vm";
|
|
} else {
|
|
if (pluginElement instanceof HTMLObjectElement) {
|
|
pluginsPage = pluginElement.getAttribute("codebase");
|
|
} else {
|
|
pluginsPage = pluginElement.getAttribute("pluginspage");
|
|
}
|
|
|
|
// only attempt if a pluginsPage is defined.
|
|
if (pluginsPage) {
|
|
var doc = pluginElement.ownerDocument;
|
|
var docShell = findChildShell(doc, gBrowser.docShell, null);
|
|
try {
|
|
pluginsPage = makeURI(pluginsPage, doc.characterSet, docShell.currentURI).spec;
|
|
} catch (ex) {
|
|
pluginsPage = "";
|
|
}
|
|
}
|
|
|
|
tagMimetype = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent)
|
|
.actualType;
|
|
|
|
if (tagMimetype == "") {
|
|
tagMimetype = pluginElement.type;
|
|
}
|
|
}
|
|
|
|
return {mimetype: tagMimetype, pluginsPage: pluginsPage};
|
|
}
|
|
|
|
/**
|
|
* Format a URL
|
|
* eg:
|
|
* echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
|
|
* > https://addons.mozilla.org/en-US/firefox/3.0a1/
|
|
*
|
|
* Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
|
|
*/
|
|
function formatURL(aFormat, aIsPref) {
|
|
var formatter = Services.urlFormatter;
|
|
return aIsPref ? formatter.formatURLPref(aFormat) : formatter.formatURL(aFormat);
|
|
}
|
|
|
|
var gPluginHandler = {
|
|
addEventListeners: function ph_addEventListeners(browser) {
|
|
// Note that the XBL binding is untrusted
|
|
browser.addEventListener("PluginBindingAttached", gPluginHandler, true, true);
|
|
browser.addEventListener("PluginCrashed", gPluginHandler, true);
|
|
browser.addEventListener("PluginOutdated", gPluginHandler, true);
|
|
browser.addEventListener("NewPluginInstalled", gPluginHandler, true);
|
|
},
|
|
|
|
removeEventListeners: function ph_removeEventListeners(browser) {
|
|
browser.removeEventListener("PluginBindingAttached", gPluginHandler);
|
|
browser.removeEventListener("PluginCrashed", gPluginHandler);
|
|
browser.removeEventListener("PluginOutdated", gPluginHandler);
|
|
browser.removeEventListener("NewPluginInstalled", gPluginHandler);
|
|
},
|
|
|
|
getPluginUI: function (plugin, anonId) {
|
|
return plugin.ownerDocument.getAnonymousElementByAttribute(plugin, "anonid", anonId);
|
|
},
|
|
|
|
get CrashSubmit() {
|
|
delete this.CrashSubmit;
|
|
Components.utils.import("resource://gre/modules/CrashSubmit.jsm", this);
|
|
return this.CrashSubmit;
|
|
},
|
|
|
|
// Map the plugin's name to a filtered version more suitable for user UI.
|
|
makeNicePluginName : function ph_makeNicePluginName(aName, aFilename) {
|
|
if (aName == "Shockwave Flash")
|
|
return "Adobe Flash";
|
|
|
|
// Clean up the plugin name by stripping off any trailing version numbers
|
|
// or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
|
|
return aName.replace(/\bplug-?in\b/i, "").replace(/[\s\d\.\-\_\(\)]+$/, "");
|
|
},
|
|
|
|
/**
|
|
* Update the visibility of the plugin overlay.
|
|
*/
|
|
setVisibility : function (plugin, overlay, shouldShow) {
|
|
overlay.classList.toggle("visible", shouldShow);
|
|
},
|
|
|
|
/**
|
|
* Check whether the plugin should be visible on the page. A plugin should
|
|
* not be visible if the overlay is too big, or if any other page content
|
|
* overlays it.
|
|
*
|
|
* This function will handle showing or hiding the overlay.
|
|
* @returns true if the plugin is invisible.
|
|
*/
|
|
shouldShowOverlay : function (plugin, overlay) {
|
|
// If the overlay size is 0, we haven't done layout yet. Presume that
|
|
// plugins are visible until we know otherwise.
|
|
if (overlay.scrollWidth == 0) {
|
|
return true;
|
|
}
|
|
|
|
// 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 overflows = (overlay.scrollWidth > pluginRect.width) ||
|
|
(overlay.scrollHeight - 5 > pluginRect.height);
|
|
if (overflows) {
|
|
return false;
|
|
}
|
|
|
|
// Is the plugin covered up by other content so that it is not clickable?
|
|
// Floating point can confuse .elementFromPoint, so inset just a bit
|
|
let left = pluginRect.left + 2;
|
|
let right = pluginRect.right - 2;
|
|
let top = pluginRect.top + 2;
|
|
let bottom = pluginRect.bottom - 2;
|
|
let centerX = left + (right - left) / 2;
|
|
let centerY = top + (bottom - top) / 2;
|
|
let points = [[left, top],
|
|
[left, bottom],
|
|
[right, top],
|
|
[right, bottom],
|
|
[centerX, centerY]];
|
|
|
|
if (right <= 0 || top <= 0) {
|
|
return false;
|
|
}
|
|
|
|
for (let [x, y] of points) {
|
|
let el = plugin.ownerDocument.elementFromPoint(x, y);
|
|
if (el !== plugin) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
addLinkClickCallback: function ph_addLinkClickCallback(linkNode, callbackName /*callbackArgs...*/) {
|
|
// XXX just doing (callback)(arg) was giving a same-origin error. bug?
|
|
let self = this;
|
|
let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
|
|
linkNode.addEventListener("click",
|
|
function(evt) {
|
|
if (!evt.isTrusted)
|
|
return;
|
|
evt.preventDefault();
|
|
if (callbackArgs.length == 0)
|
|
callbackArgs = [ evt ];
|
|
(self[callbackName]).apply(self, callbackArgs);
|
|
},
|
|
true);
|
|
|
|
linkNode.addEventListener("keydown",
|
|
function(evt) {
|
|
if (!evt.isTrusted)
|
|
return;
|
|
if (evt.keyCode == evt.DOM_VK_RETURN) {
|
|
evt.preventDefault();
|
|
if (callbackArgs.length == 0)
|
|
callbackArgs = [ evt ];
|
|
evt.preventDefault();
|
|
(self[callbackName]).apply(self, callbackArgs);
|
|
}
|
|
},
|
|
true);
|
|
},
|
|
|
|
// Helper to get the binding handler type from a plugin object
|
|
_getBindingType : function(plugin) {
|
|
let Ci = Components.interfaces;
|
|
|
|
if (!(plugin instanceof Ci.nsIObjectLoadingContent))
|
|
return null;
|
|
|
|
switch (plugin.pluginFallbackType) {
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
|
|
return "PluginNotFound";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_DISABLED:
|
|
return "PluginDisabled";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED:
|
|
return "PluginBlocklisted";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_OUTDATED:
|
|
return "PluginOutdated";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
|
|
return "PluginClickToPlay";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
|
|
return "PluginVulnerableUpdatable";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
|
|
return "PluginVulnerableNoUpdate";
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW:
|
|
return "PluginPlayPreview";
|
|
default:
|
|
// Not all states map to a handler
|
|
return null;
|
|
}
|
|
},
|
|
|
|
handleEvent : function ph_handleEvent(event) {
|
|
let self = gPluginHandler;
|
|
let plugin = event.target;
|
|
let doc = plugin.ownerDocument;
|
|
|
|
// We're expecting the target to be a plugin.
|
|
if (!(plugin instanceof Components.interfaces.nsIObjectLoadingContent))
|
|
return;
|
|
|
|
let eventType = event.type;
|
|
if (eventType == "PluginBindingAttached") {
|
|
// The plugin binding fires this event when it is created.
|
|
// As an untrusted event, ensure that this object actually has a binding
|
|
// and make sure we don't handle it twice
|
|
let overlay = this.getPluginUI(plugin, "main");
|
|
if (!overlay || overlay._bindingHandled) {
|
|
return;
|
|
}
|
|
overlay._bindingHandled = true;
|
|
|
|
// Lookup the handler for this binding
|
|
eventType = self._getBindingType(plugin);
|
|
if (!eventType) {
|
|
// Not all bindings have handlers
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (eventType) {
|
|
case "PluginCrashed":
|
|
self.pluginInstanceCrashed(plugin, event);
|
|
break;
|
|
|
|
case "PluginNotFound":
|
|
// NOP. Plugin finder service (PFS) is dead.
|
|
break;
|
|
|
|
case "PluginBlocklisted":
|
|
case "PluginOutdated":
|
|
case "npapi-carbon-event-model-failure":
|
|
self.pluginUnavailable(plugin, eventType);
|
|
break;
|
|
|
|
case "PluginDisabled":
|
|
let manageLink = this.getPluginUI(plugin, "managePluginsLink");
|
|
self.addLinkClickCallback(manageLink, "managePlugins");
|
|
break;
|
|
}
|
|
|
|
// Hide the in-content UI if it's too big. The crashed plugin handler already did this.
|
|
if (eventType != "PluginCrashed") {
|
|
let overlay = this.getPluginUI(plugin, "main");
|
|
if (overlay != null) {
|
|
this.setVisibility(plugin, overlay,
|
|
this.shouldShowOverlay(plugin, overlay));
|
|
let resizeListener = (event) => {
|
|
this.setVisibility(plugin, overlay,
|
|
this.shouldShowOverlay(plugin, overlay));
|
|
this._setPluginNotificationIcon(browser);
|
|
};
|
|
plugin.addEventListener("overflow", resizeListener);
|
|
plugin.addEventListener("underflow", resizeListener);
|
|
}
|
|
}
|
|
},
|
|
|
|
newPluginInstalled : function(event) {
|
|
// browser elements are anonymous so we can't just use target.
|
|
var browser = event.originalTarget;
|
|
|
|
// reload the browser to make the new plugin show.
|
|
browser.reload();
|
|
},
|
|
|
|
// Callback for user clicking on a disabled plugin
|
|
managePlugins: function ph_managePlugins(aEvent) {
|
|
openAddonsMgr("addons://list/plugin");
|
|
},
|
|
|
|
// Callback for user clicking "submit a report" link
|
|
submitReport : function ph_submitReport(pluginDumpID, browserDumpID, plugin) {
|
|
let keyVals = {};
|
|
if (plugin) {
|
|
let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
|
|
if (userComment)
|
|
keyVals.PluginUserComment = userComment;
|
|
if (this.getPluginUI(plugin, "submitURLOptIn").checked)
|
|
keyVals.PluginContentURL = plugin.ownerDocument.URL;
|
|
}
|
|
var curBrowser = document.getElementById('tabmail').getBrowserForSelectedTab();
|
|
this.CrashSubmit.submit(pluginDumpID, { extraExtraKeyVals: keyVals });
|
|
if (browserDumpID)
|
|
this.CrashSubmit.submit(browserDumpID);
|
|
},
|
|
|
|
// Callback for user clicking a "reload page" link
|
|
reloadPage: function ph_reloadPage(browser) {
|
|
browser.reload();
|
|
},
|
|
|
|
// Callback for user clicking the help icon
|
|
openPluginCrashHelpPage: function ph_openHelpPage() {
|
|
// Grab the plugin crash support URL
|
|
let url = Services.urlFormatter.formatURLPref("plugins.crash.supportUrl");
|
|
// Now open up a content tab to display it in
|
|
let tabmail = document.getElementById('tabmail');
|
|
tabmail.openTab("contentTab", {contentPage: url,
|
|
background: false});
|
|
},
|
|
|
|
// Event listener for blocklisted/outdated/carbonFailure plugins.
|
|
pluginUnavailable: function(plugin, eventType) {
|
|
let Cc = Components.classes;
|
|
let Ci = Components.interfaces;
|
|
var tabmail = document.getElementById('tabmail');
|
|
let browser = tabmail.getBrowserForDocument(plugin.ownerDocument
|
|
.defaultView).browser;
|
|
|
|
var notificationBox = getNotificationBox(browser.contentWindow);
|
|
|
|
// Should only display one of these warnings per page.
|
|
// In order of priority, they are: outdated > blocklisted
|
|
let outdatedNotification = notificationBox.getNotificationWithValue("outdated-plugins");
|
|
let blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins");
|
|
|
|
function showBlocklistInfo() {
|
|
var url = formatURL("extensions.blocklist.detailsURL", true);
|
|
tabmail.openTab("contentTab", {contentPage: url,
|
|
background: false});
|
|
return true;
|
|
}
|
|
|
|
function showOutdatedPluginsInfo() {
|
|
Services.prefs.setBoolPref("plugins.update.notifyUser", false);
|
|
var url = formatURL("plugins.update.url", true);
|
|
tabmail.openTab("contentTab", {contentPage: url,
|
|
background: false});
|
|
return true;
|
|
}
|
|
|
|
function carbonFailurePluginsRestartBrowser()
|
|
{
|
|
// Notify all windows that an application quit has been requested.
|
|
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
|
|
createInstance(Ci.nsISupportsPRBool);
|
|
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
|
|
|
|
// Something aborted the quit process.
|
|
if (cancelQuit.data)
|
|
return;
|
|
|
|
Services.startup.quit(Ci.nsIAppStartup.eRestarti386 |
|
|
Ci.nsIAppStartup.eRestart |
|
|
Ci.nsIAppStartup.eAttemptQuit);
|
|
}
|
|
|
|
let messengerBundle = document.getElementById("bundle_messenger");
|
|
|
|
let notifications = {
|
|
PluginBlocklisted : {
|
|
barID: "blocked-plugins",
|
|
iconURL: "chrome://mozapps/skin/plugins/pluginGeneric-16.png",
|
|
message: messengerBundle.getString("blockedpluginsMessage.title"),
|
|
buttons: [{
|
|
label: messengerBundle.getString("blockedpluginsMessage.infoButton.label"),
|
|
accessKey: messengerBundle.getString("blockedpluginsMessage.infoButton.accesskey"),
|
|
popup: null,
|
|
callback: showBlocklistInfo
|
|
},
|
|
{
|
|
label: messengerBundle.getString("blockedpluginsMessage.searchButton.label"),
|
|
accessKey: messengerBundle.getString("blockedpluginsMessage.searchButton.accesskey"),
|
|
popup: null,
|
|
callback: showOutdatedPluginsInfo
|
|
}],
|
|
},
|
|
PluginOutdated: {
|
|
barID: "outdated-plugins",
|
|
iconURL: "chrome://mozapps/skin/plugins/pluginGeneric-16.png",
|
|
message: messengerBundle.getString("outdatedpluginsMessage.title"),
|
|
buttons: [{
|
|
label: messengerBundle.getString("outdatedpluginsMessage.updateButton.label"),
|
|
accessKey: messengerBundle.getString("outdatedpluginsMessage.updateButton.accesskey"),
|
|
popup: null,
|
|
callback: showOutdatedPluginsInfo
|
|
}],
|
|
}
|
|
};
|
|
if (AppConstants.platform == "macosx") {
|
|
notifications["npapi-carbon-event-model-failure"] = {
|
|
barID: "carbon-failure-plugins",
|
|
iconURL: "chrome://mozapps/skin/plugins/pluginGeneric-16.png",
|
|
message: messengerBundle.getString("carbonFailurePluginsMessage.message"),
|
|
buttons: [{
|
|
label: messengerBundle.getString("carbonFailurePluginsMessage.restartButton.label"),
|
|
accessKey: messengerBundle.getString("carbonFailurePluginsMessage.restartButton.accesskey"),
|
|
popup: null,
|
|
callback: carbonFailurePluginsRestartBrowser
|
|
}],
|
|
}
|
|
}
|
|
|
|
// If there is already an outdated plugin notification then do nothing
|
|
if (outdatedNotification)
|
|
return;
|
|
|
|
if ((AppConstants.platform == "macosx") &&
|
|
(eventType == "npapi-carbon-event-model-failure")) {
|
|
if (Services.prefs.getBoolPref("plugins.hide_infobar_for_carbon_failure_plugin"))
|
|
return;
|
|
|
|
let carbonFailureNotification =
|
|
notificationBox.getNotificationWithValue("carbon-failure-plugins");
|
|
|
|
if (carbonFailureNotification)
|
|
carbonFailureNotification.close();
|
|
|
|
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].getService(Ci.nsIMacUtils);
|
|
// if this is not a Universal build, just follow PluginNotFound path
|
|
if (!macutils.isUniversalBinary)
|
|
eventType = "PluginNotFound";
|
|
}
|
|
|
|
if (eventType == "PluginBlocklisted") {
|
|
if (blockedNotification)
|
|
return;
|
|
}
|
|
else if (eventType == "PluginOutdated") {
|
|
if (Services.prefs.getBoolPref("plugins.hide_infobar_for_outdated_plugin"))
|
|
return;
|
|
|
|
// Cancel any notification about blocklisted plugins.
|
|
if (blockedNotification)
|
|
blockedNotification.close();
|
|
}
|
|
else if (eventType == "PluginNotFound") {
|
|
return;
|
|
}
|
|
|
|
let notify = notifications[eventType];
|
|
notificationBox.appendNotification(notify.message, notify.barID, notify.iconURL,
|
|
notificationBox.PRIORITY_WARNING_MEDIUM,
|
|
notify.buttons);
|
|
},
|
|
|
|
// 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 Components.interfaces.nsIPropertyBag2) ||
|
|
!(propertyBag instanceof Components.interfaces.nsIWritablePropertyBag2))
|
|
return;
|
|
|
|
if (AppConstants.MOZ_CRASHREPORTER) {
|
|
let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
|
|
let browserDumpID = propertyBag.getPropertyAsAString("browserDumpID");
|
|
let shouldSubmit = gCrashReporter.submitReports;
|
|
let doPrompt = true; // XXX followup to get via gCrashReporter
|
|
|
|
// Submit automatically when appropriate.
|
|
if (pluginDumpID && shouldSubmit && !doPrompt) {
|
|
this.submitReport(pluginDumpID, browserDumpID);
|
|
// Submission is async, so we can't easily show failure UI.
|
|
propertyBag.setPropertyAsBool("submittedCrashReport", true);
|
|
}
|
|
}
|
|
},
|
|
|
|
// Crashed-plugin event listener. Called for every instance of a
|
|
// plugin in content.
|
|
pluginInstanceCrashed: function (plugin, aEvent) {
|
|
if (!(aEvent instanceof PluginCrashedEvent))
|
|
return;
|
|
|
|
let submittedReport = aEvent.submittedCrashReport;
|
|
let doPrompt = true; // XXX followup for aEvent.doPrompt;
|
|
let submitReports = true; // XXX followup for aEvent.submitReports;
|
|
let pluginName = aEvent.pluginName;
|
|
let pluginFilename = aEvent.pluginFilename;
|
|
let pluginDumpID = aEvent.pluginDumpID;
|
|
let browserDumpID = aEvent.browserDumpID;
|
|
|
|
let messengerBundle = document.getElementById("bundle_messenger");
|
|
let tabmail = document.getElementById('tabmail');
|
|
|
|
// Remap the plugin name to a more user-presentable form.
|
|
pluginName = this.makeNicePluginName(pluginName, pluginFilename);
|
|
|
|
let messageString = messengerBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
|
|
|
|
//
|
|
// Configure the crashed-plugin placeholder.
|
|
//
|
|
|
|
|
|
// Force a layout flush so the binding is attached.
|
|
plugin.clientTop;
|
|
let doc = plugin.ownerDocument;
|
|
let overlay = this.getPluginUI(plugin, "main");
|
|
let statusDiv = this.getPluginUI(plugin, "submitStatus");
|
|
|
|
if (AppConstants.MOZ_CRASHREPORTER) {
|
|
let status;
|
|
|
|
// Determine which message to show regarding crash reports.
|
|
if (submittedReport) { // submitReports && !doPrompt, handled in observer
|
|
status = "submitted";
|
|
}
|
|
else if (!submitReports && !doPrompt) {
|
|
status = "noSubmit";
|
|
}
|
|
else { // doPrompt
|
|
status = "please";
|
|
// XXX can we make the link target actually be blank?
|
|
this.getPluginUI(plugin, "submitButton").addEventListener("click",
|
|
function (event) {
|
|
if (event.button != 0 || !event.isTrusted)
|
|
return;
|
|
this.submitReport(pluginDumpID, browserDumpID, plugin);
|
|
pref.setBoolPref("", optInCB.checked);
|
|
}.bind(this));
|
|
let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
|
|
let pref = Services.prefs.getBranch("dom.ipc.plugins.reportCrashURL");
|
|
optInCB.checked = pref.getBoolPref("");
|
|
}
|
|
|
|
// If we don't have a minidumpID, we can't (or didn't) submit anything.
|
|
// This can happen if the plugin is killed from the task manager.
|
|
if (!pluginDumpID) {
|
|
status = "noReport";
|
|
}
|
|
|
|
statusDiv.setAttribute("status", status);
|
|
|
|
let helpIcon = this.getPluginUI(plugin, "helpIcon");
|
|
this.addLinkClickCallback(helpIcon, "openPluginCrashHelpPage");
|
|
|
|
// 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([Components.interfaces.nsIObserver,
|
|
Components.interfaces.nsISupportsWeakReference]),
|
|
observe : function(subject, topic, data) {
|
|
let propertyBag = subject;
|
|
if (!(propertyBag instanceof Components.interfaces.nsIPropertyBag2))
|
|
return;
|
|
// Ignore notifications for other crashes.
|
|
if (propertyBag.get("minidumpID") != pluginDumpID)
|
|
return;
|
|
statusDiv.setAttribute("status", 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);
|
|
}
|
|
}
|
|
|
|
let crashText = this.getPluginUI(plugin, "crashedText");
|
|
crashText.textContent = messageString;
|
|
let browser = tabmail.getBrowserForSelectedTab();
|
|
|
|
let link = this.getPluginUI(plugin, "reloadLink");
|
|
this.addLinkClickCallback(link, "reloadPage", browser);
|
|
|
|
let notificationBox = getNotificationBox(browser.contentWindow);
|
|
|
|
let isShowing = this.shouldShowOverlay(plugin, overlay);
|
|
|
|
// Is the <object>'s size too small to hold what we want to show?
|
|
if (!isShowing) {
|
|
// First try hiding the crash report submission UI.
|
|
statusDiv.removeAttribute("status");
|
|
|
|
isShowing = this.shouldShowOverlay(plugin, overlay);
|
|
}
|
|
this.setVisibility(plugin, overlay, isShowing);
|
|
|
|
if (isShowing) {
|
|
// If a previous plugin on the page was too small and resulted in adding a
|
|
// notification bar, then remove it because this plugin instance is big
|
|
// enough to serve as in-content notification.
|
|
hideNotificationBar();
|
|
doc.mozNoPluginCrashedNotification = true;
|
|
} else {
|
|
// 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(pluginDumpID, browserDumpID);
|
|
}
|
|
|
|
function hideNotificationBar() {
|
|
let notification = notificationBox.getNotificationWithValue("plugin-crashed");
|
|
if (notification)
|
|
notificationBox.removeNotification(notification, true);
|
|
}
|
|
|
|
function showNotificationBar(pluginDumpID, browserDumpID) {
|
|
// If there's already an existing notification bar, don't do anything.
|
|
let messengerBundle = document.getElementById("bundle_messenger");
|
|
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 reloadLabel = messengerBundle.getString("crashedpluginsMessage.reloadButton.label");
|
|
let reloadKey = messengerBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
|
|
let submitLabel = messengerBundle.getString("crashedpluginsMessage.submitButton.label");
|
|
let submitKey = messengerBundle.getString("crashedpluginsMessage.submitButton.accesskey");
|
|
|
|
let buttons = [{
|
|
label: reloadLabel,
|
|
accessKey: reloadKey,
|
|
popup: null,
|
|
callback: function() { browser.reload(); },
|
|
}];
|
|
if (AppConstants.MOZ_CRASHREPORTER) {
|
|
let submitButton = {
|
|
label: submitLabel,
|
|
accessKey: submitKey,
|
|
popup: null,
|
|
callback: function() { gPluginHandler.submitReport(pluginDumpID, browserDumpID); },
|
|
};
|
|
if (pluginDumpID)
|
|
buttons.push(submitButton);
|
|
}
|
|
|
|
notification = notificationBox.appendNotification(messageString, "plugin-crashed",
|
|
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");
|
|
let crashHelpUrl = Services.urlFormatter
|
|
.formatURLPref("plugins.crash.supportUrl");
|
|
link.className = "text-link";
|
|
link.setAttribute("value", messengerBundle.getString("crashedpluginsMessage.learnMore"));
|
|
link.href = crashHelpUrl;
|
|
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
|
|
description.appendChild(link);
|
|
|
|
// Remove the notfication when the page is reloaded.
|
|
doc.defaultView.top.addEventListener("unload", function() {
|
|
notificationBox.removeNotification(notification);
|
|
}, false);
|
|
}
|
|
|
|
}
|
|
};
|
|
|