mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-08 20:28:42 +02:00
Backed out changeset 84fe04b2e7d1 (bug 1172080) Backed out changeset 0ff004760a1f (bug 1172080) Backed out changeset af147585ad55 (bug 1165263) Backed out changeset c3af8ebb6db0 (bug 1165263) Backed out changeset cd3f33a888fe (bug 1165263) Backed out changeset e5db39044a1e (bug 1165263) Backed out changeset c01c9ed77061 (bug 1165263) Backed out changeset fb723aaa4267 (bug 1165263) Backed out changeset f754e52e74dc (bug 1165263) Backed out changeset c6bda3a0afd6 (bug 817007) Backed out changeset bfa100253349 (bug 817007) Backed out changeset b787b3f9aadc (bug 1173523) Backed out changeset 4a0676b73f77 (bug 1173523) Backed out changeset 82034a4560c5 (bug 1173523) Backed out changeset 4bdb91114c7a (bug 1173523) Backed out changeset 72406261eccc (bug 1173523) Backed out changeset 541b6faf7196 (bug 1173523) Backed out changeset 1caac4569616 (bug 1173523) Backed out changeset 0d4f9f9e1b4e (bug 1173523) Backed out changeset 2d5661eb966c (bug 1173523) Backed out changeset 89833c0bb0cd (bug 1173523) Backed out changeset ea64d70eacfe (bug 1173523) Backed out changeset a8e4f1c0c445 (bug 1173523) Backed out changeset cf498d466b85 (bug 1173523)
560 lines
21 KiB
JavaScript
560 lines
21 KiB
JavaScript
# -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
|
# 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/.
|
|
|
|
var gPluginHandler = {
|
|
PREF_SESSION_PERSIST_MINUTES: "plugin.sessionPermissionNow.intervalInMinutes",
|
|
PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
|
|
MESSAGES: [
|
|
"PluginContent:ShowClickToPlayNotification",
|
|
"PluginContent:RemoveNotification",
|
|
"PluginContent:UpdateHiddenPluginUI",
|
|
"PluginContent:HideNotificationBar",
|
|
"PluginContent:ShowInstallNotification",
|
|
"PluginContent:InstallSinglePlugin",
|
|
"PluginContent:ShowPluginCrashedNotification",
|
|
"PluginContent:SubmitReport",
|
|
"PluginContent:LinkClickCallback",
|
|
],
|
|
|
|
init: function () {
|
|
const mm = window.messageManager;
|
|
for (let msg of this.MESSAGES) {
|
|
mm.addMessageListener(msg, this);
|
|
}
|
|
window.addEventListener("unload", this);
|
|
},
|
|
|
|
uninit: function () {
|
|
const mm = window.messageManager;
|
|
for (let msg of this.MESSAGES) {
|
|
mm.removeMessageListener(msg, this);
|
|
}
|
|
window.removeEventListener("unload", this);
|
|
},
|
|
|
|
handleEvent: function (event) {
|
|
if (event.type == "unload") {
|
|
this.uninit();
|
|
}
|
|
},
|
|
|
|
receiveMessage: function (msg) {
|
|
switch (msg.name) {
|
|
case "PluginContent:ShowClickToPlayNotification":
|
|
this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
|
|
msg.principal, msg.data.host, msg.data.location);
|
|
break;
|
|
case "PluginContent:RemoveNotification":
|
|
this.removeNotification(msg.target, msg.data.name);
|
|
break;
|
|
case "PluginContent:UpdateHiddenPluginUI":
|
|
this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
|
|
msg.principal, msg.data.host, msg.data.location);
|
|
break;
|
|
case "PluginContent:HideNotificationBar":
|
|
this.hideNotificationBar(msg.target, msg.data.name);
|
|
break;
|
|
case "PluginContent:ShowInstallNotification":
|
|
return this.showInstallNotification(msg.target, msg.data.pluginInfo);
|
|
case "PluginContent:InstallSinglePlugin":
|
|
this.installSinglePlugin(msg.data.pluginInfo);
|
|
break;
|
|
case "PluginContent:ShowPluginCrashedNotification":
|
|
this.showPluginCrashedNotification(msg.target, msg.data.messageString,
|
|
msg.data.pluginID);
|
|
break;
|
|
case "PluginContent:SubmitReport":
|
|
if (AppConstants.MOZ_CRASHREPORTER) {
|
|
this.submitReport(msg.data.runID, msg.data.keyVals, msg.data.submitURLOptIn);
|
|
}
|
|
break;
|
|
case "PluginContent:LinkClickCallback":
|
|
switch (msg.data.name) {
|
|
case "managePlugins":
|
|
case "openHelpPage":
|
|
case "openPluginUpdatePage":
|
|
this[msg.data.name].apply(this);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
Cu.reportError("gPluginHandler did not expect to handle message " + msg.name);
|
|
break;
|
|
}
|
|
},
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
get CrashSubmit() {
|
|
delete this.CrashSubmit;
|
|
Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
|
|
return this.CrashSubmit;
|
|
},
|
|
#endif
|
|
|
|
// Callback for user clicking on a disabled plugin
|
|
managePlugins: function () {
|
|
BrowserOpenAddonsMgr("addons://list/plugin");
|
|
},
|
|
|
|
// Callback for user clicking on the link in a click-to-play plugin
|
|
// (where the plugin has an update)
|
|
openPluginUpdatePage: function () {
|
|
openUILinkIn(Services.urlFormatter.formatURLPref("plugins.update.url"), "tab");
|
|
},
|
|
|
|
submitReport: function submitReport(runID, keyVals, submitURLOptIn) {
|
|
if (!AppConstants.MOZ_CRASHREPORTER) {
|
|
return;
|
|
}
|
|
Services.prefs.setBoolPref("dom.ipc.plugins.reportCrashURL", submitURLOptIn);
|
|
PluginCrashReporter.submitCrashReport(runID, keyVals);
|
|
},
|
|
|
|
// 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);
|
|
},
|
|
|
|
_clickToPlayNotificationEventCallback: function PH_ctpEventCallback(event) {
|
|
if (event == "showing") {
|
|
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_SHOWN")
|
|
.add(!this.options.primaryPlugin);
|
|
// Histograms always start at 0, even though our data starts at 1
|
|
let histogramCount = this.options.pluginData.size - 1;
|
|
if (histogramCount > 4) {
|
|
histogramCount = 4;
|
|
}
|
|
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_PLUGIN_COUNT")
|
|
.add(histogramCount);
|
|
}
|
|
else if (event == "dismissed") {
|
|
// Once the popup is dismissed, clicking the icon should show the full
|
|
// list again
|
|
this.options.primaryPlugin = null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called from the plugin doorhanger to set the new permissions for a plugin
|
|
* and activate plugins if necessary.
|
|
* aNewState should be either "allownow" "allowalways" or "block"
|
|
*/
|
|
_updatePluginPermission: function (aNotification, aPluginInfo, aNewState) {
|
|
let permission;
|
|
let expireType;
|
|
let expireTime;
|
|
let histogram =
|
|
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_USER_ACTION");
|
|
|
|
// Update the permission manager.
|
|
// Also update the current state of pluginInfo.fallbackType so that
|
|
// subsequent opening of the notification shows the current state.
|
|
switch (aNewState) {
|
|
case "allownow":
|
|
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
|
|
expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
|
|
expireTime = Date.now() + Services.prefs.getIntPref(this.PREF_SESSION_PERSIST_MINUTES) * 60 * 1000;
|
|
histogram.add(0);
|
|
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
|
|
break;
|
|
|
|
case "allowalways":
|
|
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
|
|
expireType = Ci.nsIPermissionManager.EXPIRE_TIME;
|
|
expireTime = Date.now() +
|
|
Services.prefs.getIntPref(this.PREF_PERSISTENT_DAYS) * 24 * 60 * 60 * 1000;
|
|
histogram.add(1);
|
|
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
|
|
break;
|
|
|
|
case "block":
|
|
permission = Ci.nsIPermissionManager.PROMPT_ACTION;
|
|
expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
|
|
expireTime = 0;
|
|
histogram.add(2);
|
|
switch (aPluginInfo.blocklistState) {
|
|
case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
|
|
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE;
|
|
break;
|
|
case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
|
|
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE;
|
|
break;
|
|
default:
|
|
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY;
|
|
}
|
|
break;
|
|
|
|
// In case a plugin has already been allowed in another tab, the "continue allowing" button
|
|
// shouldn't change any permissions but should run the plugin-enablement code below.
|
|
case "continue":
|
|
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
|
|
break;
|
|
default:
|
|
Cu.reportError(Error("Unexpected plugin state: " + aNewState));
|
|
return;
|
|
}
|
|
|
|
let browser = aNotification.browser;
|
|
let contentWindow = browser.contentWindow;
|
|
if (aNewState != "continue") {
|
|
let principal = aNotification.options.principal;
|
|
Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
|
|
permission, expireType, expireTime);
|
|
aPluginInfo.pluginPermissionType = expireType;
|
|
}
|
|
|
|
browser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
|
|
pluginInfo: aPluginInfo,
|
|
newState: aNewState,
|
|
});
|
|
},
|
|
|
|
showClickToPlayNotification: function (browser, plugins, showNow, principal,
|
|
host, location) {
|
|
// It is possible that we've received a message from the frame script to show
|
|
// a click to play notification for a principal that no longer matches the one
|
|
// that the browser's content now has assigned (ie, the browser has browsed away
|
|
// after the message was sent, but before the message was received). In that case,
|
|
// we should just ignore the message.
|
|
if (!principal.equals(browser.contentPrincipal)) {
|
|
return;
|
|
}
|
|
|
|
// Data URIs, when linked to from some page, inherit the principal of that
|
|
// page. That means that we also need to compare the actual locations to
|
|
// ensure we aren't getting a message from a Data URI that we're no longer
|
|
// looking at.
|
|
let receivedURI = BrowserUtils.makeURI(location);
|
|
if (!browser.documentURI.equalsExceptRef(receivedURI)) {
|
|
return;
|
|
}
|
|
|
|
let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
|
|
|
|
// If this is a new notification, create a pluginData map, otherwise append
|
|
let pluginData;
|
|
if (notification) {
|
|
pluginData = notification.options.pluginData;
|
|
} else {
|
|
pluginData = new Map();
|
|
}
|
|
|
|
for (var pluginInfo of plugins) {
|
|
if (pluginData.has(pluginInfo.permissionString)) {
|
|
continue;
|
|
}
|
|
|
|
// If a block contains an infoURL, we should always prefer that to the default
|
|
// URL that we construct in-product, even for other blocklist types.
|
|
let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
|
|
|
|
if (pluginInfo.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) {
|
|
if (!url) {
|
|
url = Services.urlFormatter.formatURLPref("plugins.update.url");
|
|
}
|
|
}
|
|
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
|
|
if (!url) {
|
|
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
|
|
}
|
|
}
|
|
else {
|
|
url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
|
|
}
|
|
pluginInfo.detailsLink = url;
|
|
|
|
pluginData.set(pluginInfo.permissionString, pluginInfo);
|
|
}
|
|
|
|
let primaryPluginPermission = null;
|
|
if (showNow) {
|
|
primaryPluginPermission = plugins[0].permissionString;
|
|
}
|
|
|
|
if (notification) {
|
|
// Don't modify the notification UI while it's on the screen, that would be
|
|
// jumpy and might allow clickjacking.
|
|
if (showNow) {
|
|
notification.options.primaryPlugin = primaryPluginPermission;
|
|
notification.reshow();
|
|
browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
|
|
}
|
|
return;
|
|
}
|
|
|
|
let options = {
|
|
dismissed: !showNow,
|
|
eventCallback: this._clickToPlayNotificationEventCallback,
|
|
primaryPlugin: primaryPluginPermission,
|
|
pluginData: pluginData,
|
|
principal: principal,
|
|
host: host,
|
|
};
|
|
PopupNotifications.show(browser, "click-to-play-plugins",
|
|
"", "plugins-notification-icon",
|
|
null, null, options);
|
|
browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
|
|
},
|
|
|
|
removeNotification: function (browser, name) {
|
|
let notification = PopupNotifications.getNotification(name, browser);
|
|
if (notification)
|
|
PopupNotifications.remove(notification);
|
|
},
|
|
|
|
hideNotificationBar: function (browser, name) {
|
|
let notificationBox = gBrowser.getNotificationBox(browser);
|
|
let notification = notificationBox.getNotificationWithValue(name);
|
|
if (notification)
|
|
notificationBox.removeNotification(notification, true);
|
|
},
|
|
|
|
updateHiddenPluginUI: function (browser, haveInsecure, actions, principal,
|
|
host, location) {
|
|
// It is possible that we've received a message from the frame script to show
|
|
// the hidden plugin notification for a principal that no longer matches the one
|
|
// that the browser's content now has assigned (ie, the browser has browsed away
|
|
// after the message was sent, but before the message was received). In that case,
|
|
// we should just ignore the message.
|
|
if (!principal.equals(browser.contentPrincipal)) {
|
|
return;
|
|
}
|
|
|
|
// Data URIs, when linked to from some page, inherit the principal of that
|
|
// page. That means that we also need to compare the actual locations to
|
|
// ensure we aren't getting a message from a Data URI that we're no longer
|
|
// looking at.
|
|
let receivedURI = BrowserUtils.makeURI(location);
|
|
if (!browser.documentURI.equalsExceptRef(receivedURI)) {
|
|
return;
|
|
}
|
|
|
|
// Set up the icon
|
|
document.getElementById("plugins-notification-icon").classList.
|
|
toggle("plugin-blocked", haveInsecure);
|
|
|
|
// Now configure the notification bar
|
|
let notificationBox = gBrowser.getNotificationBox(browser);
|
|
|
|
function hideNotification() {
|
|
let n = notificationBox.getNotificationWithValue("plugin-hidden");
|
|
if (n) {
|
|
notificationBox.removeNotification(n, true);
|
|
}
|
|
}
|
|
|
|
// There are three different cases when showing an infobar:
|
|
// 1. A single type of plugin is hidden on the page. Show the UI for that
|
|
// plugin.
|
|
// 2a. Multiple types of plugins are hidden on the page. Show the multi-UI
|
|
// with the vulnerable styling.
|
|
// 2b. Multiple types of plugins are hidden on the page, but none are
|
|
// vulnerable. Show the nonvulnerable multi-UI.
|
|
function showNotification() {
|
|
let n = notificationBox.getNotificationWithValue("plugin-hidden");
|
|
if (n) {
|
|
// If something is already shown, just keep it
|
|
return;
|
|
}
|
|
|
|
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_SHOWN").
|
|
add(true);
|
|
|
|
let message;
|
|
// Icons set directly cannot be manipulated using moz-image-region, so
|
|
// we use CSS classes instead.
|
|
let brand = document.getElementById("bundle_brand").getString("brandShortName");
|
|
|
|
if (actions.length == 1) {
|
|
let pluginInfo = actions[0];
|
|
let pluginName = pluginInfo.pluginName;
|
|
|
|
switch (pluginInfo.fallbackType) {
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
|
|
message = gNavigatorBundle.getFormattedString(
|
|
"pluginActivateNew.message",
|
|
[pluginName, host]);
|
|
break;
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
|
|
message = gNavigatorBundle.getFormattedString(
|
|
"pluginActivateOutdated.message",
|
|
[pluginName, host, brand]);
|
|
break;
|
|
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
|
|
message = gNavigatorBundle.getFormattedString(
|
|
"pluginActivateVulnerable.message",
|
|
[pluginName, host, brand]);
|
|
}
|
|
} else {
|
|
// Multi-plugin
|
|
message = gNavigatorBundle.getFormattedString(
|
|
"pluginActivateMultiple.message", [host]);
|
|
}
|
|
|
|
let buttons = [
|
|
{
|
|
label: gNavigatorBundle.getString("pluginContinueBlocking.label"),
|
|
accessKey: gNavigatorBundle.getString("pluginContinueBlocking.accesskey"),
|
|
callback: function() {
|
|
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_BLOCK").
|
|
add(true);
|
|
|
|
Services.perms.addFromPrincipal(principal,
|
|
"plugin-hidden-notification",
|
|
Services.perms.DENY_ACTION);
|
|
}
|
|
},
|
|
{
|
|
label: gNavigatorBundle.getString("pluginActivateTrigger.label"),
|
|
accessKey: gNavigatorBundle.getString("pluginActivateTrigger.accesskey"),
|
|
callback: function() {
|
|
Services.telemetry.getHistogramById("PLUGINS_INFOBAR_ALLOW").
|
|
add(true);
|
|
|
|
let curNotification =
|
|
PopupNotifications.getNotification("click-to-play-plugins",
|
|
browser);
|
|
if (curNotification) {
|
|
curNotification.reshow();
|
|
}
|
|
}
|
|
}
|
|
];
|
|
n = notificationBox.
|
|
appendNotification(message, "plugin-hidden", null,
|
|
notificationBox.PRIORITY_INFO_HIGH, buttons);
|
|
if (haveInsecure) {
|
|
n.classList.add('pluginVulnerable');
|
|
}
|
|
}
|
|
|
|
if (actions.length == 0) {
|
|
hideNotification();
|
|
} else {
|
|
let notificationPermission = Services.perms.testPermissionFromPrincipal(
|
|
principal, "plugin-hidden-notification");
|
|
if (notificationPermission == Ci.nsIPermissionManager.DENY_ACTION) {
|
|
hideNotification();
|
|
} else {
|
|
showNotification();
|
|
}
|
|
}
|
|
},
|
|
|
|
contextMenuCommand: function (browser, plugin, command) {
|
|
browser.messageManager.sendAsyncMessage("BrowserPlugins:ContextMenuCommand",
|
|
{ command: command }, { plugin: plugin });
|
|
},
|
|
|
|
// Crashed-plugin observer. Notified once per plugin crash, before events
|
|
// are dispatched to individual plugin instances.
|
|
NPAPIPluginCrashed : function(subject, topic, data) {
|
|
let propertyBag = subject;
|
|
if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
|
|
!(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
|
|
!propertyBag.hasKey("runID") ||
|
|
!propertyBag.hasKey("pluginName")) {
|
|
Cu.reportError("A NPAPI plugin crashed, but the properties of this plugin " +
|
|
"cannot be read.");
|
|
return;
|
|
}
|
|
|
|
let runID = propertyBag.getPropertyAsUint32("runID");
|
|
let uglyPluginName = propertyBag.getPropertyAsAString("pluginName");
|
|
let pluginName = BrowserUtils.makeNicePluginName(uglyPluginName);
|
|
let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
|
|
|
|
// 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.
|
|
let state;
|
|
if (!AppConstants.MOZ_CRASHREPORTER || !gCrashReporter.enabled) {
|
|
// This state tells the user that crash reporting is disabled, so we
|
|
// cannot send a report.
|
|
state = "noSubmit";
|
|
} else if (!pluginDumpID) {
|
|
// This state tells the user that there is no crash report available.
|
|
state = "noReport";
|
|
} else {
|
|
// This state asks the user to submit a crash report.
|
|
state = "please";
|
|
}
|
|
|
|
let mm = window.getGroupMessageManager("browsers");
|
|
mm.broadcastAsyncMessage("BrowserPlugins:NPAPIPluginProcessCrashed",
|
|
{ pluginName, runID, state });
|
|
},
|
|
|
|
/**
|
|
* Shows a plugin-crashed notification bar for a browser that has had an
|
|
* invisiable NPAPI plugin crash, or a GMP plugin crash.
|
|
*
|
|
* @param browser
|
|
* The browser to show the notification for.
|
|
* @param messageString
|
|
* The string to put in the notification bar
|
|
* @param pluginID
|
|
* The unique-per-process identifier for the NPAPI plugin or GMP.
|
|
* For a GMP, this is the pluginID. For NPAPI plugins (where "pluginID"
|
|
* means something different), this is the runID.
|
|
*/
|
|
showPluginCrashedNotification: function (browser, messageString, pluginID) {
|
|
// If there's already an existing notification bar, don't do anything.
|
|
let notificationBox = gBrowser.getNotificationBox(browser);
|
|
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/notifyPluginCrashed.png";
|
|
let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
|
|
let reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
|
|
|
|
let buttons = [{
|
|
label: reloadLabel,
|
|
accessKey: reloadKey,
|
|
popup: null,
|
|
callback: function() { browser.reload(); },
|
|
}];
|
|
|
|
if (AppConstants.MOZ_CRASHREPORTER &&
|
|
PluginCrashReporter.hasCrashReport(pluginID)) {
|
|
let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
|
|
let submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
|
|
let submitButton = {
|
|
label: submitLabel,
|
|
accessKey: submitKey,
|
|
popup: null,
|
|
callback: () => {
|
|
PluginCrashReporter.submitCrashReport(pluginID);
|
|
},
|
|
};
|
|
|
|
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");
|
|
link.className = "text-link";
|
|
link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
|
|
let crashurl = formatURL("app.support.baseURL", true);
|
|
crashurl += "plugin-crashed-notificationbar";
|
|
link.href = crashurl;
|
|
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
|
|
description.appendChild(link);
|
|
},
|
|
};
|
|
|
|
gPluginHandler.init();
|