forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			530 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			530 lines
		
	
	
	
		
			20 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.pluginDumpID, msg.data.browserDumpID);
 | |
|         break;
 | |
|       case "PluginContent:SubmitReport":
 | |
|         this.submitReport(msg.data.pluginDumpID, msg.data.browserDumpID, msg.data.keyVals);
 | |
|         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");
 | |
|   },
 | |
| 
 | |
| #ifdef MOZ_CRASHREPORTER
 | |
|   submitReport: function submitReport(pluginDumpID, browserDumpID, keyVals) {
 | |
|     keyVals = keyVals || {};
 | |
|     this.CrashSubmit.submit(pluginDumpID, { recordSubmission: true,
 | |
|                                             extraExtraKeyVals: keyVals });
 | |
|     if (browserDumpID)
 | |
|       this.CrashSubmit.submit(browserDumpID);
 | |
|   },
 | |
| #endif
 | |
| 
 | |
|   // 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.
 | |
|   pluginCrashed : function(subject, topic, data) {
 | |
|     let propertyBag = subject;
 | |
|     if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
 | |
|         !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
 | |
|      return;
 | |
| 
 | |
| #ifdef 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);
 | |
|     }
 | |
| #endif
 | |
|   },
 | |
| 
 | |
|   showPluginCrashedNotification: function (browser, messageString, pluginDumpID, browserDumpID) {
 | |
|     // 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 submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
 | |
|     let submitKey   = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
 | |
| 
 | |
|     let buttons = [{
 | |
|       label: reloadLabel,
 | |
|       accessKey: reloadKey,
 | |
|       popup: null,
 | |
|       callback: function() { browser.reload(); },
 | |
|     }];
 | |
| 
 | |
| #ifdef MOZ_CRASHREPORTER
 | |
|     let submitButton = {
 | |
|       label: submitLabel,
 | |
|       accessKey: submitKey,
 | |
|       popup: null,
 | |
|         callback: function() { gPluginHandler.submitReport(pluginDumpID, browserDumpID); },
 | |
|     };
 | |
|     if (pluginDumpID)
 | |
|       buttons.push(submitButton);
 | |
| #endif
 | |
| 
 | |
|     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();
 | |
| 
 | 
