Bug 798278 - Implement multiple plugin doorhanger UI (Port Bug 797677 and Bug 754472), r=Neil, a=Ratty
authorFrank Wein <mcsmurf@mcsmurf.de>
Sat, 03 Aug 2013 16:39:11 +0200
changeset 15901 9eedab7adf4a6873f6761b96d23fd548aa6bda3c
parent 15900 ac21aa1c88766a97a28e05eaa78dd6ea2f21529f
child 15902 8d7c23490db02d79e16428484e5f9e0fd7e3488c
push id942
push userbugzilla@standard8.plus.com
push dateMon, 05 Aug 2013 19:15:38 +0000
treeherdercomm-beta@0e1a1c4a9f0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil, Ratty
bugs798278, 797677, 754472
Bug 798278 - Implement multiple plugin doorhanger UI (Port Bug 797677 and Bug 754472), r=Neil, a=Ratty approval for CLOSED TREE by ewong
suite/browser/navigator.css
suite/browser/test/browser/browser_pluginnotification.js
suite/common/bindings/notification.xml
suite/themes/classic/jar.mn
suite/themes/classic/navigator/click-to-play-warning-stripes.png
suite/themes/classic/navigator/navigator.css
suite/themes/modern/jar.mn
suite/themes/modern/navigator/click-to-play-warning-stripes.png
suite/themes/modern/navigator/navigator.css
--- a/suite/browser/navigator.css
+++ b/suite/browser/navigator.css
@@ -90,16 +90,24 @@ searchbar {
 .notification-anchor-icon {
   -moz-user-focus: normal;
 }
 
 .notification-anchor-icon:not([showing]) {
   display: none;
 }
 
+#click-to-play-plugins-notification {
+  -moz-binding: url("chrome://communicator/content/bindings/notification.xml#click-to-play-plugins-notification");
+}
+
+popupnotification-centeritem {
+  -moz-binding: url("chrome://communicator/content/bindings/notification.xml#center-item");
+}
+
 #geolocation-notification {
   -moz-binding: url("chrome://communicator/content/bindings/notification.xml#geolocation-popup-notification");
 }
 
 #addon-install-started-notification {
   -moz-binding: url("chrome://communicator/content/bindings/notification.xml#addon-progress-popup-notification");
 }
 
--- a/suite/browser/test/browser/browser_pluginnotification.js
+++ b/suite/browser/test/browser/browser_pluginnotification.js
@@ -236,21 +236,17 @@ function test8() {
 // Tests that activating one click-to-play plugin will activate only that plugin (part 1/3)
 function test9a() {
   var notificationBox = gBrowser.getNotificationBox(gTestBrowser);
   ok(!notificationBox.getNotificationWithValue("missing-plugins"), "Test 9a, Should not have displayed the missing plugin notification");
   ok(!notificationBox.getNotificationWithValue("blocked-plugins"), "Test 9a, Should not have displayed the blocked plugin notification");
   ok(!notificationBox._missingPlugins, "Test 9a, Should not be a missing plugin list");
   var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 9a, Should have a click-to-play notification");
-
-  // SeaMonkey does not support notification.options.centerActions yet, this is
-  // part of the multiple plugin doorhanger UI, Bug 798278 will port this code
-  // to SeaMonkey
-  // ok(notification.options.centerActions.length == 1, "Test 9a, Should have only one type of plugin in the notification");
+  ok(notification.options.centerActions.length == 1, "Test 9a, Should have only one type of plugin in the notification");
 
   var doc = gTestBrowser.contentDocument;
   var plugin1 = doc.getElementById("test1");
   var rect = doc.getAnonymousElementByAttribute(plugin1, "class", "mainBox").getBoundingClientRect();
   ok(rect.width == 200, "Test 9a, Plugin with id=" + plugin1.id + " overlay rect should have 200px width before being clicked");
   ok(rect.height == 200, "Test 9a, Plugin with id=" + plugin1.id + " overlay rect should have 200px height before being clicked");
   var objLoadingContent = plugin1.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(!objLoadingContent.activated, "Test 9a, Plugin with id=" + plugin1.id + " should not be activated");
@@ -271,20 +267,17 @@ function test9a() {
 // Tests that activating one click-to-play plugin will activate only that plugin (part 2/3)
 function test9b() {
   var notificationBox = gBrowser.getNotificationBox(gTestBrowser);
   ok(!notificationBox.getNotificationWithValue("missing-plugins"), "Test 9b, Should not have displayed the missing plugin notification");
   ok(!notificationBox.getNotificationWithValue("blocked-plugins"), "Test 9b, Should not have displayed the blocked plugin notification");
   ok(!notificationBox._missingPlugins, "Test 9b, Should not be a missing plugin list");
   var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 9b, Click to play notification should not be removed now");
-  // SeaMonkey does not support notification.options.centerActions yet, this is
-  // part of the multiple plugin doorhanger UI, Bug 798278 will port this code
-  // to SeaMonkey
-  // ok(notification.options.centerActions.length == 1, "Test 9b, Should have only one type of plugin in the notification");
+  ok(notification.options.centerActions.length == 1, "Test 9b, Should have only one type of plugin in the notification");
 
   var doc = gTestBrowser.contentDocument;
   var plugin1 = doc.getElementById("test1");
   var pluginRect1 = doc.getAnonymousElementByAttribute(plugin1, "class", "mainBox").getBoundingClientRect();
   ok(pluginRect1.width == 0, "Test 9b, Plugin with id=" + plugin1.id + " should have click-to-play overlay with zero width");
   ok(pluginRect1.height == 0, "Test 9b, Plugin with id=" + plugin1.id + " should have click-to-play overlay with zero height");
   var objLoadingContent = plugin1.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(objLoadingContent.activated, "Test 9b, Plugin with id=" + plugin1.id + " should be activated");
--- a/suite/common/bindings/notification.xml
+++ b/suite/common/bindings/notification.xml
@@ -87,17 +87,16 @@
               .usePrivateBrowsing
       </field>
 
       <method name="onDocumentChange">
         <body>
           <![CDATA[
             this.missingPlugins = null;
             this.crashNotified = false;
-            this.clickToPlayPromptShown = false;
             this.clickToPlayPluginsActivated = false;
             if (this.popupCount) {
               this.popupCount = 0;
               this.notifyPopupCountChanged();
             }
             this.removeTransientNotifications();
           ]]>
         </body>
@@ -508,26 +507,25 @@
       <property name="missingPlugins"
                 onget="return this._missingPlugins || (this._missingPlugins = new Map());"
                 onset="return this._missingPlugins = val;"/>
 
       <field name="CrashSubmit">null</field>
 
       <field name="crashNotified">false</field>
 
-      <field name="clickToPlayPromptShown">false</field>
-
       <field name="clickToPlayPluginsActivated">false</field>
 
       <method name="getPluginInfo">
         <parameter name="pluginElement"/>
         <body>
           <![CDATA[
             var mimetype;
             var pluginsPage;
+            var pluginName = this._stringBundle.GetStringFromName("pluginInfo.unknownPlugin");
             if (pluginElement instanceof HTMLAppletElement) {
               mimetype = "application/x-java-vm";
             } else {
               if (pluginElement instanceof HTMLObjectElement) {
                 pluginsPage = pluginElement.codebase;
               } else {
                 pluginsPage = pluginElement.getAttribute("pluginspage");
                 if (pluginsPage) {
@@ -541,17 +539,25 @@
               mimetype = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent)
                                       .actualType;
 
               if (!mimetype) {
                 mimetype = pluginElement.type;
               }
             }
 
-            return {mimetype: mimetype, pluginsPage: pluginsPage};
+            var navMimeType = navigator.mimeTypes.namedItem(mimetype);
+            if (navMimeType && navMimeType.enabledPlugin)
+              pluginName = this.makeNicePluginName(navMimeType.enabledPlugin.name);
+
+            return {
+              mimetype: mimetype,
+              pluginsPage: pluginsPage,
+              pluginName: pluginName
+            };
           ]]>
         </body>
       </method>
 
       <method name="pluginUnavailable">
         <parameter name="aPlugin"/>
         <parameter name="aNotification"/>
         <parameter name="aMessage"/>
@@ -559,18 +565,17 @@
         <parameter name="aPref"/>
         <body>
           <![CDATA[
             // Save information on the plugin to give to the plugin finder.
             var pluginInfo = this.getPluginInfo(aPlugin);
             this.missingPlugins.set(pluginInfo.mimetype, pluginInfo);
 
             // Hide the in-content UI if it's too big. The crashed plugin handler already does this.
-            var doc = aPlugin.ownerDocument;
-            var overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
+            var overlay = this.getPluginUI(aPlugin, "mainBox");
             if (this.isTooSmall(aPlugin, overlay))
               overlay.style.visibility = "hidden";
 
             if (this._prefs.getBoolPref(aPref || "plugins.hide_infobar_for_missing_plugin"))
               return;
 
             var notification;
             var notifications = [
@@ -598,20 +603,21 @@
             const priority = this.PRIORITY_WARNING_MEDIUM;
             this.appendNotification(aMessage, aNotification,
                                     null, priority, aButtons);
           ]]>
         </body>
       </method>
 
       <method name="canActivatePlugin">
-        <parameter name="objLoadingContent"/>
+        <parameter name="aPlugin"/>
         <body>
           <![CDATA[
             const nsIObjectLoadingContent = Components.interfaces.nsIObjectLoadingContent;
+            var objLoadingContent = aPlugin.QueryInterface(nsIObjectLoadingContent);
             var actualType = objLoadingContent.actualType;
             if (objLoadingContent.getContentTypeForMIMEType(actualType) != nsIObjectLoadingContent.TYPE_PLUGIN ||
                 objLoadingContent.activated ||
                 objLoadingContent.pluginFallbackType < nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY ||
                 objLoadingContent.pluginFallbackType > nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE)
               return false;
 
             var ph = Components.classes["@mozilla.org/plugin/host;1"]
@@ -632,57 +638,52 @@
           <![CDATA[
             var ph = Components.classes["@mozilla.org/plugin/host;1"]
                                .getService(Components.interfaces.nsIPluginHost);
             const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
             var pm = Components.classes["@mozilla.org/permissionmanager;1"]
                                .getService(nsIPermissionManager);
             var plugins = this.contentWindowUtils.plugins;
             for (var plugin of plugins) {
-              var objLoadingContent = plugin.QueryInterface(Components.interfaces.nsIObjectLoadingContent);
-              if (this.canActivatePlugin(objLoadingContent)) {
-                var permissionString = ph.getPermissionStringForType(objLoadingContent.actualType);
+              if (this.canActivatePlugin(plugin)) {
+                var permissionString = ph.getPermissionStringForType(plugin.actualType);
                 pm.add(this.activeBrowser.currentURI, permissionString, aPermission);
               }
             }
           ]]>
         </body>
       </method>
 
       <method name="activatePlugins">
         <body>
           <![CDATA[
             this.clickToPlayPluginsActivated = true;
             var plugins = this.contentWindowUtils.plugins;
             for (let plugin of plugins) {
-              let objLoadingContent = plugin.QueryInterface(Components.interfaces.nsIObjectLoadingContent);
-              if (this.canActivatePlugin(objLoadingContent))
-                objLoadingContent.playPlugin();
+              if (this.canActivatePlugin(plugin))
+                plugin.playPlugin();
             }
             this.removePluginClickToPlayPrompt();
           ]]>
         </body>
       </method>
 
       <method name="activateSinglePlugin">
         <parameter name="aPlugin"/>
         <body>
           <![CDATA[
-            var objLoadingContent = aPlugin.QueryInterface(Components.interfaces.nsIObjectLoadingContent);
-            if (this.canActivatePlugin(objLoadingContent))
-              objLoadingContent.playPlugin();
-
-            var haveUnplayedPlugins = this.contentWindowUtils.plugins.some(function(plugin) {
-              var hupObjLoadingContent = plugin.QueryInterface(Components.interfaces.nsIObjectLoadingContent);
-              return plugin != aPlugin && this.canActivatePlugin(hupObjLoadingContent);
-            }, this);
-            if (!haveUnplayedPlugins) {
+            if (this.canActivatePlugin(aPlugin))
+              aPlugin.playPlugin();
+
+            var pluginNeedsActivation = this.pluginNeedsActivationExceptThese([aPlugin]);
+
+            if (pluginNeedsActivation)
+              this.showPluginClickToPlayPrompt();
+            else
               this.removePluginClickToPlayPrompt();
-              this.clickToPlayPromptShown = false;
-            }
           ]]>
         </body>
       </method>
 
       <method name="stopPlayPreview">
         <parameter name="aPlugin"/>
         <parameter name="aPlayPlugin"/>
         <body>
@@ -733,27 +734,63 @@
               }
               browserWin.content.focus();
             }
             return true;
           ]]>
         </body>
       </method>
 
+      <method name="getBindingType">
+        <parameter name="aPlugin"/>
+        <body>
+          <![CDATA[
+            // Helper to get the binding handler type from a plugin object
+            if (!(aPlugin instanceof Components.interfaces.nsIObjectLoadingContent))
+              return null;
+
+            switch (aPlugin.pluginFallbackType) {
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
+                return "PluginNotFound";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_DISABLED:
+                return "PluginDisabled";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED:
+                return "PluginBlocklisted";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_OUTDATED:
+                return "PluginOutdated";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
+                return "PluginClickToPlay";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
+                return "PluginVulnerableUpdatable";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
+                return "PluginVulnerableNoUpdate";
+              case Components.interfaces.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW:
+                return "PluginPlayPreview";
+              default:
+                // Not all states map to a handler
+                return null;
+            }
+          ]]>
+        </body>
+      </method>
+
       <method name="makeNicePluginName">
         <parameter name="aName"/>
-        <parameter name="aFilename"/>
         <body>
           <![CDATA[
             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"
-            var newName = aName.replace(/\bplug-?in\b/i, "").replace(/[\s\d\.\-\_\(\)]+$/, "");
+            // Clean up the plugin name by stripping off any trailing version
+            // numbers or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
+            // Do this by first stripping the numbers, etc. off the end, and
+            // then removing "Plugin" (and then trimming to get rid of any
+            // whitespace). Otherwise, something like "Java(TM) Plug-in
+            // 1.7.0_07" gets mangled.
+            var newName = aName.replace(/[\s\d\.\-\_\(\)]+$/, "").replace(/\bplug-?in\b/i, "").trim();
             return newName;
           ]]>
         </body>
       </method>
 
       <method name="isTooSmall">
         <parameter name="aPlugin"/>
         <parameter name="aOverlay"/>
@@ -1269,19 +1306,28 @@
             var notification = this.getNotificationWithValue("click-to-play-plugins");
             if (notification)
               this.removeNotification(notification);
           ]]>
         </body>
       </method>
 
       <method name="showPluginClickToPlayPrompt">
+        <parameter name="aTriggeredByNewPlugin"/>
         <body>
           <![CDATA[
-            this.clickToPlayPromptShown = true;
+            // If there's already an existing notification bar, don't do anything.
+            // Also don't display a notification bar unless showPluginClickToPlayPrompt
+            // has been triggered by a new plugin object on the website; the other code
+            // path to this function here is by clicking on the click-to-play overlay on
+            // a plugin object. We don't want to re-show the notification bar in that
+            // case
+            var notification = this.getNotificationWithValue("click-to-play-plugins");
+            if (notification || !aTriggeredByNewPlugin)
+              return;
 
             var checkbox = document.createElement("checkbox");
             checkbox.className = "rememberChoice";
             checkbox.setAttribute("label", this._stringBundle.GetStringFromName("activatepluginsMessage.remember"));
 
             const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
             var messageString = this._stringBundle.GetStringFromName("activatepluginsMessage.title");
             var buttons = [{
@@ -1322,50 +1368,66 @@
           ]]>
         </body>
       </method>
 
       <method name="hideClickToPlayOverlay">
         <parameter name="pluginElement"/>
         <body>
           <![CDATA[
-            var doc = pluginElement.ownerDocument;
             var overlay = this.getPluginUI(pluginElement, "mainBox");
             if (overlay) // no overlay if plugin has been activated
               overlay.style.visibility = "hidden";
           ]]>
         </body>
       </method>
 
-      <method name="setupPluginClickToPlay">
-        <parameter name="pluginElement"/>
+      <method name="pluginNeedsActivationExceptThese">
+        <parameter name="aExceptThese"/>
         <body>
           <![CDATA[
-            var doc = pluginElement.ownerDocument;
+            var pluginNeedsActivation = this.contentWindowUtils
+                                            .plugins.some(function(aPlugin) {
+              return aExceptThese.indexOf(aPlugin) < 0 &&
+                     this.canActivatePlugin(aPlugin);
+            }, this);
+
+            return pluginNeedsActivation;
+          ]]>
+        </body>
+      </method>
+
+      <method name="setupPluginClickToPlay">
+        <parameter name="pluginElement"/>
+        <parameter name="aTriggeredByNewPlugin"/>
+        <body>
+          <![CDATA[
             var overlay = this.getPluginUI(pluginElement, "mainBox");
 
-            var objLoadingContent = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent);
-            if (!this.canActivatePlugin(objLoadingContent)) {
+            if (!this.canActivatePlugin(pluginElement)) {
               overlay.style.visibility = "hidden";
               return;
             }
 
             if (this.clickToPlayPluginsActivated) {
               pluginElement.playPlugin();
               return;
             }
 
-            if (!this.clickToPlayPromptShown)
-              this.showPluginClickToPlayPrompt();
+            this.showPluginClickToPlayPrompt(aTriggeredByNewPlugin);
 
             if (this.isTooSmall(pluginElement, overlay)) {
               overlay.style.visibility = "hidden";
               return;
             }
 
+            var pluginName = this.getPluginInfo(pluginElement).pluginName;
+            var messageString = this._stringBundle.formatStringFromName("PluginClickToActivate", [pluginName], 1);
+            var overlayText = this.getPluginUI(pluginElement, "msg msgClickToPlay");
+            overlayText.textContent = messageString;
             this.addLinkClickCallback(overlay, this.activateSinglePlugin, pluginElement);
 
             var closeIcon = this.getPluginUI(pluginElement, "closeIcon");
             this.addLinkClickCallback(closeIcon, this.hideClickToPlayOverlay, pluginElement);
           ]]>
         </body>
       </method>
 
@@ -2080,35 +2142,35 @@
               // For broken non-object plugin tags, register a click handler so
               // that the user can click the plugin replacement to get the new
               // plugin. Object tags can, and often do, deal with that
               // themselves, so don't stomp on the page developer's toes.
               if (!(plugin instanceof HTMLObjectElement)) {
                 // We don't yet check to see if there's actually an installer available.
                 var installStatus = this.getPluginUI(plugin, "installStatus");
                 installStatus.setAttribute("status", "ready");
-                var iconStatus = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
+                var iconStatus = this.getPluginUI(plugin, "icon");
                 iconStatus.setAttribute("status", "ready");
-                var installLink = doc.getAnonymousElementByAttribute(plugin, "class", "installPluginLink");
+                var installLink = this.getPluginUI(plugin, "installPluginLink");
                 this.addLinkClickCallback(installLink, installMissingPlugins);
               }
 
               notification = "missing-plugins";
               message = this._stringBundle.GetStringFromName("missingpluginsMessage.title");
               buttons = [{
                 label: this._stringBundle.GetStringFromName("missingpluginsMessage.button.label"),
                 accessKey: this._stringBundle.GetStringFromName("missingpluginsMessage.button.accesskey"),
                 popup: null,
                 callback: this.installMissingPlugins.bind(this)
               }];
               break;
 
             case nsIObjectLoadingContent.PLUGIN_DISABLED:
               if ("toEM" in window) {
-                var manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink");
+                var manageLink = this.getPluginUI(plugin, "managePluginsLink");
                 this.addLinkClickCallback(manageLink, window.toEM, "addons://list/plugin");
               }
               return;
 
             case nsIObjectLoadingContent.PLUGIN_BLOCKLISTED:
               notification = "blocked-plugins";
               message = this._stringBundle.GetStringFromName("blockedpluginsMessage.title");
               buttons = [{
@@ -2132,23 +2194,28 @@
                 accessKey: this._stringBundle.GetStringFromName("outdatedpluginsMessage.button.accesskey"),
                 popup: null,
                 callback: this.openURLPref.bind(this, "plugins.update.url")
               }];
               pref = "plugins.hide_infobar_for_outdated_plugin";
               break;
 
             case nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
-              var updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
+              var updateLink = this.getPluginUI(plugin, "checkForUpdatesLink");
               this.addLinkClickCallback(updateLink, this.openURLPref, "plugins.update.url");
               // fall through
             case nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
+              var eventType = this.getBindingType(plugin);
+              // When we got here, we deal with a vulnerable plugin object, so setup UI elements for this case
+              var vulnerabilityString = this._stringBundle.GetStringFromName(eventType);
+              var vulnerabilityText = doc.getAnonymousElementByAttribute(plugin, "anonid", "vulnerabilityStatus");
+              vulnerabilityText.textContent = vulnerabilityString;
               // fall through
             case nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
-              this.setupPluginClickToPlay(plugin);
+              this.setupPluginClickToPlay(plugin, true);
               return;
 
             case nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW:
               this.handlePlayPreviewEvent(plugin);
               return;
 
             default:
               return;
@@ -2165,27 +2232,26 @@
           var doPrompt        = true; // XXX followup for .getData("doPrompt");
           var submitReports   = true; // XXX followup for .getData("submitReports");
           var pluginName      = event.getData("pluginName");
           var pluginFilename  = event.getData("pluginFilename");
           var pluginDumpID    = event.getData("pluginDumpID");
           var browserDumpID   = event.getData("browserDumpID");
 
           // Remap the plugin name to a more user-presentable form.
-          pluginName = this.makeNicePluginName(pluginName, pluginFilename);
+          pluginName = this.makeNicePluginName(pluginName);
 
           // Force a style flush, so that we ensure our binding is attached.
           plugin.clientTop;
 
           // Configure the crashed-plugin placeholder.
-          var doc = plugin.ownerDocument;
-          var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+          var overlay = this.getPluginUI(plugin, "mainBox");
 
           var status;
-          var statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus");
+          var statusDiv = this.getPluginUI(plugin, "submitStatus");
 
           if (this.CrashSubmit) {
             // Determine which message to show regarding crash reports.
             if (submittedReport) { // submitReports && !doPrompt, handled in observer
               status = "submitted";
             }
             else if (!submitReports && !doPrompt) {
               status = "noSubmit";
@@ -2241,24 +2307,24 @@
           // 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);
 
-          var helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon");
+          var helpIcon = this.getPluginUI(plugin, "helpIcon");
           this.addLinkClickCallback(helpIcon, this.openHelpPage);
 
           var messageString = this._stringBundle.formatStringFromName("crashedpluginsMessage.title", [pluginName], 1);
           var crashText = this.getPluginUI(plugin, "msgCrashedText");
           crashText.textContent = messageString;
 
-          var link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
+          var link = this.getPluginUI(plugin, "reloadLink");
           this.addLinkClickCallback(link, this.reloadPage);
 
           var isShowing = true;
 
           // Is the <object>'s size too small to hold what we want to show?
           if (this.isTooSmall(plugin, overlay)) {
             // First try hiding the comment box and related report submission UI.
             statusDiv.removeAttribute("status");
@@ -2367,25 +2433,21 @@
         ]]>
       </handler>
 
       <handler event="pageshow" phase="capturing">
         <![CDATA[
           // The PluginClickToPlay events are not fired when navigating using the
           // BF cache. |event.persisted| is true when the page is loaded from the
           // BF cache, so this code reshows the notification if necessary.
-          if (!event.persisted || !this._prefs.getBoolPref("plugins.click_to_play"))
+          if (!event.persisted)
             return;
 
-          var pluginNeedsActivation = this.contentWindowUtils.plugins.some(function(aPlugin) {
-            var objLoadingContent = aPlugin.QueryInterface(Components.interfaces.nsIObjectLoadingContent);
-            return this.canActivatePlugin(objLoadingContent);
-          }, this);
-          if (pluginNeedsActivation)
-            this.showPluginClickToPlayPrompt();
+          if (pluginNeedsActivationExceptThese([]))
+            this.showPluginClickToPlayPrompt(true);
         ]]>
       </handler>
     </handlers>
   </binding>
 
   <binding id="popup-notification"
            extends="chrome://communicator/content/bindings/notification.xml#browser-notificationbox">
     <implementation>
@@ -2483,38 +2545,113 @@
             PopupNotifications.show(this.activeBrowser,
                                     "lwtheme-install-notification", message,
                                     "addons-notification-icon", action, null,
                                     options);
           ]]>
         </body>
       </method>
 
+      <method name="makeCenterActions">
+        <body>
+          <![CDATA[
+            let pluginsDictionary = new Map();
+            for (let plugin of this.contentWindowUtils.plugins) {
+              if (this.canActivatePlugin(plugin)) {
+                let pluginName = this.getPluginInfo(plugin).pluginName;
+                if (!pluginsDictionary.has(pluginName)) {
+                  pluginsDictionary.set(pluginName, []);
+                }
+                pluginsDictionary.get(pluginName).push(plugin);
+              }
+            }
+
+            let centerActions = [];
+            for (let [pluginName, namedPluginArray] of pluginsDictionary) {
+              let plugin = namedPluginArray[0];
+              let warn = false;
+              let warningText = "";
+              let updateLink = "";
+
+              if (plugin.pluginFallbackType) {
+                if (plugin.pluginFallbackType ==
+                      Components.interfaces.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE) {
+                  updateLink = Services.urlFormatter.formatURLPref("plugins.update.url");
+                  warn = true;
+                  warningText = this._stringBundle.GetStringFromName("vulnerableUpdatablePluginWarning");
+                }
+                else if (plugin.pluginFallbackType ==
+                           Components.interfaces.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
+                  warn = true;
+                  warningText = this._stringBundle.GetStringFromName("vulnerableNoUpdatePluginWarning");
+                }
+              }
+
+              let action = {
+                message: pluginName,
+                warn: warn,
+                warningText: warningText,
+                updateLink: updateLink,
+                label: this._stringBundle.GetStringFromName("activateSinglePlugin"),
+                callback: (function(aName) {
+                  /* Play all plugins currently in the page of the given name
+                     and, if needed, remove the notification */
+                  for (let plugin of this.contentWindowUtils.plugins) {
+                    if (this.canActivatePlugin(plugin) &&
+                        this.getPluginInfo(plugin).pluginName == aName)
+                      plugin.playPlugin();
+                  }
+
+                  let notification = PopupNotifications.getNotification("click-to-play-plugins", this.activeBrowser)
+;
+                  if (notification &&
+                      !this.pluginNeedsActivationExceptThese([]))
+                    notification.remove();
+                }).bind(this, pluginName)
+              };
+              centerActions.push(action);
+            }
+
+            return centerActions;
+          ]]>
+        </body>
+      </method>
+
+
       <method name="removePluginClickToPlayPrompt">
         <body>
           <![CDATA[
             var notification = PopupNotifications.getNotification("click-to-play-plugins",
                                                                   this.activeBrowser);
             if (notification)
               notification.remove();
           ]]>
         </body>
       </method>
 
       <method name="showPluginClickToPlayPrompt">
         <body>
           <![CDATA[
-            this.clickToPlayPromptShown = true;
             const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
-            var messageString = this._stringBundle.GetStringFromName("activatepluginsMessage.title");
             var mainAction = {
               label: this._stringBundle.GetStringFromName("activatepluginsMessage.activate.label"),
               accessKey: this._stringBundle.GetStringFromName("activatepluginsMessage.activate.accesskey"),
               callback: this.activatePlugins.bind(this)
             };
+            var centerActions = this.makeCenterActions();
+            var haveVulnerablePlugin = this.contentWindowUtils.plugins.some(function(plugin) {
+              return (this.canActivatePlugin(plugin) &&
+                      (plugin.pluginFallbackType ==
+                       Components.interfaces.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE ||
+                       plugin.pluginFallbackType ==
+                       Components.interfaces.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE));
+            }, this);
+            var messageString = this._stringBundle.GetStringFromName(haveVulnerablePlugin ?
+                                                                     "vulnerablePluginsMessage" :
+                                                                     "activatepluginsMessage.title");
             var secondaryActions = [{
               label: this._stringBundle.GetStringFromName("activatepluginsMessage.always.label"),
               accessKey: this._stringBundle.GetStringFromName("activatepluginsMessage.always.accesskey"),
               callback: (function () {
                 this.setPermissionForPlugins(nsIPermissionManager.ALLOW_ACTION);
                 this.activatePlugins();
               }).bind(this)
             }, {
@@ -2525,17 +2662,18 @@
                 var notification = PopupNotifications.getNotification("click-to-play-plugins",
                                                                       this.activeBrowser);
                 if (notification)
                   notification.remove();
                 this.removeClickToPlayOverlays();
               }).bind(this)
             }];
             var options = {
-              dismissed: true
+              dismissed: true,
+              centerActions: centerActions
             };
             PopupNotifications.show(this.activeBrowser, "click-to-play-plugins",
                                     messageString, "plugins-notification-icon",
                                     mainAction, secondaryActions, options);
            ]]>
         </body>
       </method>
 
@@ -3303,9 +3441,126 @@
         <body>
           <![CDATA[
             this.updateProgress();
           ]]>
         </body>
       </method>
     </implementation>
   </binding>
+
+  <binding id="center-item">
+    <content>
+      <xul:vbox flex="1" class="center-item-box"
+                xbl:inherits="warn,showseparator,padbottom">
+        <xul:hbox align="center">
+          <xul:image class="center-item-icon"
+                     xbl:inherits="src=itemicon"/>
+          <xul:description class="center-item-label"
+                           xbl:inherits="xbl:text=itemtext"/>
+          <xul:spacer flex="1"/>
+          <xul:button class="popup-notification-menubutton center-item-button"
+                      oncommand="document.getBindingParent(this).runCallback();"
+                      xbl:inherits="label=buttonlabel"/>
+        </xul:hbox>
+        <xul:hbox align="center" class="center-item-warning">
+          <xul:image class="center-item-warning-icon"/>
+          <xul:label class="center-item-warning-description" xbl:inherits="xbl:text=warningText"/>
+          <xul:label xbl:inherits="href=updateLink" value="&checkForUpdates;" class="text-link"/>
+        </xul:hbox>
+      </xul:vbox>
+    </content>
+    <resources>
+      <stylesheet src="chrome://global/skin/notification.css"/>
+    </resources>
+    <implementation>
+      <field name="action"></field>
+      <method name="runCallback">
+        <body><![CDATA[
+          let action = this.action;
+          action.callback();
+          let cas = action.popupnotification.notification.options.centerActions;
+          cas.splice(cas.indexOf(action), 1);
+          PopupNotifications._dismiss();
+        ]]></body>
+      </method>
+    </implementation>
+  </binding>
+
+  <binding id="click-to-play-plugins-notification" extends="chrome://global/content/bindings/notification.xml#popup-notification">
+    <content class="click-to-play-plugins-notification-content">
+      <xul:hbox flex="1">
+        <xul:vbox class="click-to-play-plugins-notification-icon-box">
+          <xul:image class="popup-notification-icon"
+                     xbl:inherits="popupid,src=icon"/>
+        </xul:vbox>
+        <xul:spacer class="click-to-play-plugins-notification-separator"/>
+        <xul:vbox flex="1" class="popup-notification-main-box"
+                  xbl:inherits="popupid">
+          <xul:box class="click-to-play-plugins-notification-description-box" flex="1">
+            <xul:description xbl:inherits="xbl:text=label"/>
+          </xul:box>
+          <xul:spacer class="click-to-play-plugins-notification-separator"/>
+          <xul:vbox class="click-to-play-plugins-notification-center-box">
+            <children includes="popupnotification-centeritem"/>
+          </xul:vbox>
+          <xul:spacer class="click-to-play-plugins-notification-separator"/>
+          <xul:hbox class="click-to-play-plugins-notification-button-container"
+                    pack="end" align="center">
+            <xul:button anonid="button"
+                        class="popup-notification-menubutton"
+                        type="menu-button"
+                        xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
+              <xul:menupopup anonid="menupopup"
+                             xbl:inherits="oncommand=menucommand">
+                <children/>
+                <xul:menuitem class="menuitem-iconic popup-notification-closeitem"
+                              label="&closeNotificationItem.label;"
+                              xbl:inherits="oncommand=closeitemcommand"/>
+              </xul:menupopup>
+            </xul:button>
+          </xul:hbox>
+        </xul:vbox>
+      </xul:hbox>
+    </content>
+    <resources>
+      <stylesheet src="chrome://global/skin/notification.css"/>
+    </resources>
+    <implementation>
+      <field name="button" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "button");
+      </field>
+      <field name="menupopup" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "menupopup");
+      </field>
+      <constructor><![CDATA[
+        const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+        let popupnotification = this;
+        let item = null;
+        let prev = null;
+        this.notification.options.centerActions.forEach(function(action) {
+          action.popupnotification = popupnotification;
+          item = document.createElementNS(XUL_NS, "popupnotification-centeritem");
+          item.action = action;
+          item.setAttribute("itemtext", action.message);
+          item.setAttribute("buttonlabel", action.label);
+          item.setAttribute("warn", action.warn);
+          item.setAttribute("warningText", action.warningText);
+          item.setAttribute("updateLink", action.updateLink);
+          if (prev &&
+              (prev.getAttribute("warn") == "true" ||
+               item.getAttribute("warn") == "true")) {
+            item.setAttribute("showseparator", true);
+            if (prev.getAttribute("warn") != "true") {
+              prev.setAttribute("padbottom", true);
+            }
+          }
+          popupnotification.appendChild(item);
+          prev = item;
+        });
+        if (item) {
+          item.setAttribute("padbottom", "true");
+        }
+      ]]></constructor>
+    </implementation>
+  </binding>
+
 </bindings>
--- a/suite/themes/classic/jar.mn
+++ b/suite/themes/classic/jar.mn
@@ -417,16 +417,17 @@ classic.jar:
   skin/classic/navigator/icons/identity.png                             (mac/navigator/icons/identity.png)
   skin/classic/navigator/icons/navigatoricons.png                       (mac/navigator/icons/navigatoricons.png)
   skin/classic/navigator/icons/navigatoricons-small.png                 (mac/navigator/icons/navigatoricons-small.png)
   skin/classic/navigator/icons/panel-expander-closed.png                (mac/navigator/icons/panel-expander-closed.png)
   skin/classic/navigator/icons/panel-expander-open.png                  (mac/navigator/icons/panel-expander-open.png)
   skin/classic/navigator/icons/panel-plus-sign.png                      (mac/navigator/icons/panel-plus-sign.png)
   skin/classic/navigator/icons/restore.png                              (mac/navigator/icons/restore.png)
 #else
+  skin/classic/navigator/click-to-play-warning-stripes.png              (navigator/click-to-play-warning-stripes.png)
   skin/classic/navigator/navigator.css                                  (navigator/navigator.css)
   skin/classic/navigator/linkToolbar.css                                (navigator/linkToolbar.css)
   skin/classic/navigator/pageInfo.css                                   (navigator/pageInfo.css)
   skin/classic/navigator/tabbrowser.css                                 (navigator/tabbrowser.css)
   skin/classic/navigator/btn1/first-disabled.gif                        (navigator/btn1/first-disabled.gif)
   skin/classic/navigator/btn1/first-hover.gif                           (navigator/btn1/first-hover.gif)
   skin/classic/navigator/btn1/last-disabled.gif                         (navigator/btn1/last-disabled.gif)
   skin/classic/navigator/btn1/last-hover.gif                            (navigator/btn1/last-hover.gif)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..29f15f7b8c20a39a79454fc8b81852daa36548a2
GIT binary patch
literal 1563
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
N002ovPDHLkV1i?V(r^F(
--- a/suite/themes/classic/navigator/navigator.css
+++ b/suite/themes/classic/navigator/navigator.css
@@ -303,16 +303,17 @@ toolbar[mode="text"] > #window-controls 
 
 .popup-notification-icon[popupid="password-change"],
 .popup-notification-icon[popupid="password-save"] {
   list-style-image: url("chrome://mozapps/skin/passwordmgr/key-64.png");
 }
 
 .popup-notification-icon[popupid="click-to-play-plugins"] {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+  margin: 0px;
   width: 32px;
   height: 32px;
 }
 
 .addon-progress-description {
   width: 350px;
   max-width: 350px;
 }
@@ -363,16 +364,119 @@ toolbar[mode="text"] > #window-controls 
 }
 
 #plugins-notification-icon {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
   width: 16px;
   height: 16px;
 }
 
+.click-to-play-plugins-notification-content {
+  margin: -10px;
+  border-radius: 4px;
+}
+
+.click-to-play-plugins-notification-icon-box {
+  background-color: rgba(255, 255, 255, .4);
+  -moz-border-end: 1px solid rgba(255, 255, 255, .2);
+  padding: 24px;
+}
+
+.click-to-play-plugins-notification-icon-box:-moz-locale-dir(ltr) {
+  border-bottom-left-radius: 4px;
+  border-top-left-radius: 4px;
+}
+
+.click-to-play-plugins-notification-icon-box:-moz-locale-dir(rtl) {
+  border-bottom-right-radius: 4px;
+  border-top-right-radius: 4px;
+}
+
+.click-to-play-plugins-notification-separator {
+  -moz-border-start: 1px solid rgba(3, 14, 27, .1);
+  border-top: 1px solid rgba(3, 14, 27, .1);
+}
+
+.click-to-play-plugins-notification-description-box {
+  border-bottom: 1px solid rgba(255, 255, 255, .2);
+  -moz-border-start: 1px solid rgba(255, 255, 255, .2);
+  padding-top: 12px;
+  -moz-padding-end: 11px;
+   padding-bottom: 9px;
+  -moz-padding-start: 10px;
+}
+
+.click-to-play-plugins-notification-center-box {
+  border-top: 1px solid rgba(255, 255, 255, .2);
+  border-bottom: 1px solid rgba(255, 255, 255, .2);
+  -moz-border-start: 1px solid rgba(255, 255, 255, .2);
+  background-color: rgba(3, 14, 27, .05);
+}
+
+.click-to-play-plugins-notification-button-container {
+  border-top: 1px solid rgba(255, 255, 255, .2);
+  -moz-border-start: 1px solid rgba(255, 255, 255, .2);
+  margin: 0px;
+  padding: 16px;
+}
+
+.center-item-box {
+  padding: 12px 16px 0px 16px;
+}
+
+.center-item-box[padbottom="true"] {
+  padding-bottom: 12px;
+}
+
+.center-item-icon {
+  background-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+  background-repeat: no-repeat;
+  height: 16px;
+  width: 16px;
+  margin-bottom: 4px;
+}
+
+.center-item-box[warn="true"] {
+  background-image: url("chrome://navigator/skin/click-to-play-warning-stripes.png");
+  background-repeat: repeat-x;
+  padding: 8px 16px 6px 16px;
+}
+
+.center-item-box[padbottom="true"][warn="true"] {
+  padding-bottom: 4px;
+}
+
+.center-item-box[showseparator="true"] {
+  border-top: 1px solid rgba(3, 14, 27, .1);
+}
+
+.center-item-box[warn="false"] > .center-item-warning {
+  display: none;
+}
+
+.center-item-warning > .text-link[href=""] {
+  display: none;
+}
+
+.center-item-warning-icon {
+  background-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+  background-repeat: no-repeat;
+  width: 16px;
+  height: 15px;
+  margin-bottom: 4px;
+}
+
+.center-item-warning-description {
+  color: #828282;
+}
+
+.center-item-button {
+  min-width: 0px;
+}
+
 /* ::::: page proxy icon ::::: */
 
 #page-proxy-deck,
 #page-proxy-favicon,
 #page-proxy-button {
   width: 16px;
   height: 16px;
 }
--- a/suite/themes/modern/jar.mn
+++ b/suite/themes/modern/jar.mn
@@ -616,16 +616,17 @@ modern.jar:
   skin/modern/mozapps/plugins/pluginHelp-16.png                    (mozapps/plugins/pluginHelp-16.png)
   skin/modern/mozapps/plugins/pluginInstallerWizard.css            (mozapps/plugins/pluginInstallerWizard.css)
   skin/modern/mozapps/plugins/pluginProblem.css                    (mozapps/plugins/pluginProblem.css)
   skin/modern/mozapps/update/update.png                            (mozapps/update/update.png)
   skin/modern/mozapps/update/updates.css                           (mozapps/update/updates.css)
   skin/modern/mozapps/viewsource/viewsource.css                    (mozapps/viewsource/viewsource.css)
   skin/modern/mozapps/xpinstall/xpinstallConfirm.css               (mozapps/xpinstall/xpinstallConfirm.css)
   skin/modern/mozapps/xpinstall/xpinstallItemGeneric.png           (mozapps/extensions/extensionGeneric.png)
+  skin/modern/navigator/click-to-play-warning-stripes.png          (navigator/click-to-play-warning-stripes.png)
   skin/modern/navigator/linkToolbar.css                            (navigator/linkToolbar.css)
   skin/modern/navigator/navigator.css                              (navigator/navigator.css)
   skin/modern/navigator/pageInfo.css                               (navigator/pageInfo.css)
   skin/modern/navigator/tabbrowser.css                             (navigator/tabbrowser.css)
   skin/modern/navigator/btn1/feeds.png                             (navigator/btn1/feeds.png)
   skin/modern/navigator/btn1/first-dis.gif                         (navigator/btn1/first-dis.gif)
   skin/modern/navigator/btn1/first-hov.gif                         (navigator/btn1/first-hov.gif)
   skin/modern/navigator/btn1/first.gif                             (navigator/btn1/first.gif)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..29f15f7b8c20a39a79454fc8b81852daa36548a2
GIT binary patch
literal 1563
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
N002ovPDHLkV1i?V(r^F(
--- a/suite/themes/modern/navigator/navigator.css
+++ b/suite/themes/modern/navigator/navigator.css
@@ -511,16 +511,17 @@ toolbar[mode="icons"] #search-button > .
 
 .popup-notification-icon[popupid="password-change"],
 .popup-notification-icon[popupid="password-save"] {
   list-style-image: url("chrome://mozapps/skin/passwordmgr/key-64.png");
 }
 
 .popup-notification-icon[popupid="click-to-play-plugins"] {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+  margin: 0px;
   width: 32px;
   height: 32px;
 }
 
 .addon-progress-description {
   width: 350px;
   max-width: 350px;
 }
@@ -571,16 +572,96 @@ toolbar[mode="icons"] #search-button > .
 }
 
 #plugins-notification-icon {
   list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
   width: 16px;
   height: 16px;
 }
 
+.click-to-play-plugins-notification-icon-box {
+  background-color: #DDE3EB;
+  -moz-border-end: 1px solid #E6ECF5;
+  padding: 24px;
+}
+
+.click-to-play-plugins-notification-separator {
+  -moz-border-start: 1px solid #B3BCC6;
+  border-top: 1px solid #B3BCC6;
+}
+
+.click-to-play-plugins-notification-description-box {
+  padding-top: 12px;
+  -moz-padding-end: 11px;
+   padding-bottom: 9px;
+  -moz-padding-start: 10px;
+}
+
+.click-to-play-plugins-notification-center-box {
+  background-color: #BDC7D6;
+}
+
+.click-to-play-plugins-notification-button-container {
+  padding: 12px;
+}
+
+.center-item-box {
+  padding: 8px 16px 0px 16px;
+}
+
+.center-item-box[padbottom="true"] {
+  padding-bottom: 8px;
+}
+
+.center-item-icon {
+  background-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+  background-repeat: no-repeat;
+  height: 16px;
+  width: 16px;
+  margin-bottom: 4px;
+}
+
+.center-item-box[warn="true"] {
+  background-image: url("chrome://navigator/skin/click-to-play-warning-stripes.png");
+  background-repeat: repeat-x;
+  padding: 8px 16px 6px 16px;
+}
+
+.center-item-box[padbottom="true"][warn="true"] {
+  padding-bottom: 4px;
+}
+
+.center-item-box[showseparator="true"] {
+  border-top: 1px solid #B1BBC5;
+}
+
+.center-item-box[warn="false"] > .center-item-warning {
+  display: none;
+}
+
+.center-item-warning > .text-link[href=""] {
+  display: none;
+}
+
+.center-item-warning-icon {
+  background-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+  background-repeat: no-repeat;
+  width: 16px;
+  height: 15px;
+  margin-bottom: 4px;
+}
+
+.center-item-warning-description {
+  color: #828282;
+}
+
+.center-item-button {
+  min-width: 0px;
+}
+
 /* ::::: page proxy icon ::::: */
 
 #page-proxy-deck,
 #page-proxy-button,
 #page-proxy-favicon {
   width: 16px;
   height: 16px;
 }