Bug 742753 - Click-to-play: activate only the clicked plugin. r=jaws
authorDavid Keeler <dkeeler@mozilla.com>
Tue, 15 May 2012 11:10:43 -0700
changeset 95591 e428a3d95bcdafae76193a5eaa3033bcf4907ab9
parent 95590 3b6bafa382f6f47a0e885846aabd8e57ca858f07
child 95592 95def0230d0cdff26a9c4eb8d9be20127269e3b6
push idunknown
push userunknown
push dateunknown
reviewersjaws
bugs742753
milestone15.0a1
Bug 742753 - Click-to-play: activate only the clicked plugin. r=jaws
browser/base/content/browser.js
browser/base/content/test/browser_bug743421.js
browser/base/content/test/browser_pluginnotification.js
browser/base/content/test/plugin_test2.html
toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd
toolkit/mozapps/plugins/content/pluginProblem.xml
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7242,16 +7242,35 @@ var gPluginHandler = {
       if (!objLoadingContent.activated)
         objLoadingContent.playPlugin();
     }
     let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
     if (notification)
       notification.remove();
   },
 
+  activateSinglePlugin: function PH_activateSinglePlugin(aContentWindow, aPlugin) {
+    let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    if (!objLoadingContent.activated)
+      objLoadingContent.playPlugin();
+
+    let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    let haveUnplayedPlugins = cwu.plugins.some(function(plugin) {
+      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+      return (plugin != aPlugin && !objLoadingContent.activated);
+    });
+    let browser = gBrowser.getBrowserForDocument(aContentWindow.document);
+    let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
+    if (notification && !haveUnplayedPlugins) {
+      browser._clickToPlayDoorhangerShown = false;
+      notification.remove();
+    }
+  },
+
   newPluginInstalled : function(event) {
     // browser elements are anonymous so we can't just use target.
     var browser = event.originalTarget;
     // clear the plugin list, now that at least one plugin has been installed
     browser.missingPlugins = null;
 
     var notificationBox = gBrowser.getNotificationBox(browser);
     var notification = notificationBox.getNotificationWithValue("missing-plugins");
@@ -7316,17 +7335,17 @@ var gPluginHandler = {
       return;
     }
 
     let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
     // The overlay is null if the XBL binding is not attached (element is display:none).
     if (overlay) {
       overlay.addEventListener("click", function(aEvent) {
         if (aEvent.button == 0 && aEvent.isTrusted)
-          gPluginHandler.activatePlugins(aEvent.target.ownerDocument.defaultView.top);
+          gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin);
       }, true);
     }
 
     if (!browser._clickToPlayDoorhangerShown)
       gPluginHandler._showClickToPlayNotification(browser);
   },
 
   reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() {
--- a/browser/base/content/test/browser_bug743421.js
+++ b/browser/base/content/test/browser_bug743421.js
@@ -47,17 +47,17 @@ function test1a() {
 
 function test1b() {
   var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "Test 1b, Should have a click-to-play notification");
   var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(!objLoadingContent.activated, "Test 1b, Plugin should not be activated");
 
-  EventUtils.synthesizeMouse(plugin, 100, 100, { });
+  popupNotification.mainAction.callback();
   setTimeout(test1c, 500);
 }
 
 function test1c() {
   var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(!popupNotification, "Test 1c, Should not have a click-to-play notification");
   var plugin = gTestBrowser.contentWindow.addPlugin();
 
--- a/browser/base/content/test/browser_pluginnotification.js
+++ b/browser/base/content/test/browser_pluginnotification.js
@@ -1,15 +1,15 @@
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir;
 
 var gTestBrowser = null;
 var gNextTest = null;
 var gClickToPlayPluginActualEvents = 0;
-var gClickToPlayPluginExpectedEvents = 6;
+var gClickToPlayPluginExpectedEvents = 5;
 
 function get_test_plugin() {
   var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
   var tags = ph.getPluginTags();
 
   // Find the test plugin
   for (var i = 0; i < tags.length; i++) {
     if (tags[i].name == "Test Plug-in")
@@ -208,60 +208,92 @@ function test8() {
   ok(!notificationBox.getNotificationWithValue("missing-plugins"), "Test 8, Should not have displayed the missing plugin notification");
   ok(!notificationBox.getNotificationWithValue("blocked-plugins"), "Test 8, Should not have displayed the blocked plugin notification");
   ok(!gTestBrowser.missingPlugins, "Test 8, Should not be a missing plugin list");
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 8, Should have a click-to-play notification");
 
   prepareTest(test9a, gTestRoot + "plugin_test2.html");
 }
 
-// Tests that activating one click-to-play plugin will activate the other plugins (part 1/1)
+// 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(!gTestBrowser.missingPlugins, "Test 9a, Should not be a missing plugin list");
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 9a, Should have a click-to-play notification");
-  var plugin1 = gTestBrowser.contentDocument.getElementById("test");
+
   var doc = gTestBrowser.contentDocument;
-  var plugins = [];
-  plugins.push(doc.getElementById("test"));
-  plugins.push(doc.getElementById("test1"));
-  plugins.push(doc.getElementById("test2"));
-  plugins.forEach(function(plugin) {
-    var rect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
-    ok(rect.width == 200, "Test 9a, Plugin with id=" + plugin.id + " overlay rect should have 200px width before being clicked");
-    ok(rect.height == 200, "Test 9a, Plugin with id=" + plugin.id + " overlay rect should have 200px height before being clicked");
-    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    ok(!objLoadingContent.activated, "Test 9a, Plugin with id=" + plugin.id + " should not be activated");
-  });
+  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");
+
+  var plugin2 = doc.getElementById("test2");
+  var rect = doc.getAnonymousElementByAttribute(plugin2, "class", "mainBox").getBoundingClientRect();
+  ok(rect.width == 200, "Test 9a, Plugin with id=" + plugin2.id + " overlay rect should have 200px width before being clicked");
+  ok(rect.height == 200, "Test 9a, Plugin with id=" + plugin2.id + " overlay rect should have 200px height before being clicked");
+  var objLoadingContent = plugin2.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(!objLoadingContent.activated, "Test 9a, Plugin with id=" + plugin2.id + " should not be activated");
 
   EventUtils.synthesizeMouse(plugin1, 100, 100, { });
   setTimeout(test9b, 1000);
 }
 
-// Tests that activating one click-to-play plugin will activate the other plugins (part 2/2)
+// 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(!gTestBrowser.missingPlugins, "Test 9b, Should not be a missing plugin list");
-  ok(!PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 9b, Click to play notification should be removed now");
+  ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 9b, Click to play notification should not be removed now");
+
   var doc = gTestBrowser.contentDocument;
-  var plugins = [];
-  plugins.push(doc.getElementById("test"));
-  plugins.push(doc.getElementById("test1"));
-  plugins.push(doc.getElementById("test2"));
-  plugins.forEach(function(plugin) {
-    var pluginRect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
-    ok(pluginRect.width == 0, "Test 9b, Plugin with id=" + plugin.id + " should have click-to-play overlay with zero width");
-    ok(pluginRect.height == 0, "Test 9b, Plugin with id=" + plugin.id + " should have click-to-play overlay with zero height");
-    var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    ok(objLoadingContent.activated, "Test 9b, Plugin with id=" + plugin.id + " should be activated");
-  });
+  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");
+
+  var plugin2 = doc.getElementById("test2");
+  var pluginRect2 = doc.getAnonymousElementByAttribute(plugin2, "class", "mainBox").getBoundingClientRect();
+  ok(pluginRect2.width != 0, "Test 9b, Plugin with id=" + plugin2.id + " should not have click-to-play overlay with zero width");
+  ok(pluginRect2.height != 0, "Test 9b, Plugin with id=" + plugin2.id + " should not have click-to-play overlay with zero height");
+  var objLoadingContent = plugin2.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(!objLoadingContent.activated, "Test 9b, Plugin with id=" + plugin2.id + " should not be activated");
+
+  EventUtils.synthesizeMouse(plugin2, 100, 100, { });
+  setTimeout(test9c, 1000);
+}
+//
+// Tests that activating one click-to-play plugin will activate only that plugin (part 3/3)
+function test9c() {
+  var notificationBox = gBrowser.getNotificationBox(gTestBrowser);
+  ok(!notificationBox.getNotificationWithValue("missing-plugins"), "Test 9c, Should not have displayed the missing plugin notification");
+  ok(!notificationBox.getNotificationWithValue("blocked-plugins"), "Test 9c, Should not have displayed the blocked plugin notification");
+  ok(!gTestBrowser.missingPlugins, "Test 9c, Should not be a missing plugin list");
+  ok(!PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser), "Test 9c, Click to play notification should be removed now");
+
+  var doc = gTestBrowser.contentDocument;
+  var plugin1 = doc.getElementById("test1");
+  var pluginRect1 = doc.getAnonymousElementByAttribute(plugin1, "class", "mainBox").getBoundingClientRect();
+  ok(pluginRect1.width == 0, "Test 9c, Plugin with id=" + plugin1.id + " should have click-to-play overlay with zero width");
+  ok(pluginRect1.height == 0, "Test 9c, Plugin with id=" + plugin1.id + " should have click-to-play overlay with zero height");
+  var objLoadingContent = plugin1.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(objLoadingContent.activated, "Test 9c, Plugin with id=" + plugin1.id + " should be activated");
+
+  var plugin2 = doc.getElementById("test1");
+  var pluginRect2 = doc.getAnonymousElementByAttribute(plugin2, "class", "mainBox").getBoundingClientRect();
+  ok(pluginRect2.width == 0, "Test 9c, Plugin with id=" + plugin2.id + " should have click-to-play overlay with zero width");
+  ok(pluginRect2.height == 0, "Test 9c, Plugin with id=" + plugin2.id + " should have click-to-play overlay with zero height");
+  var objLoadingContent = plugin2.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(objLoadingContent.activated, "Test 9c, Plugin with id=" + plugin2.id + " should be activated");
 
   prepareTest(test10a, gTestRoot + "plugin_test3.html");
 }
 
 // Tests that activating a hidden click-to-play plugin through the notification works (part 1/2)
 function test10a() {
   var notificationBox = gBrowser.getNotificationBox(gTestBrowser);
   ok(!notificationBox.getNotificationWithValue("missing-plugins"), "Test 10a, Should not have displayed the missing plugin notification");
@@ -318,17 +350,16 @@ function test11d() {
     ok(popupNotification, "Test 11d, Should have a click-to-play notification");
     is(gClickToPlayPluginActualEvents, gClickToPlayPluginExpectedEvents,
        "There should be a PluginClickToPlay event for each plugin that was " +
        "blocked due to the plugins.click_to_play pref");
 
     prepareTest(test12a, gTestRoot + "plugin_clickToPlayAllow.html");
   }, 1000);
 
-  
 }
 
 // Tests that the "Allow Always" permission works for click-to-play plugins (part 1/3)
 function test12a() {
   var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "Test 12a, Should have a click-to-play notification");
   var plugin = gTestBrowser.contentDocument.getElementById("test");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
@@ -402,17 +433,17 @@ function test13c() {
 
   Services.perms.removeAll();
   Services.prefs.setBoolPref("plugins.click_to_play", false);
   prepareTest(test14, gTestRoot + "plugin_test2.html");
 }
 
 // Tests that the plugin's "activated" property is true for working plugins with click-to-play disabled.
 function test14() {
-  var plugin = gTestBrowser.contentDocument.getElementById("test");
+  var plugin = gTestBrowser.contentDocument.getElementById("test1");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(objLoadingContent.activated, "Test 14, Plugin should be activated");
 
   var plugin = get_test_plugin();
   plugin.disabled = false;
   plugin.blocklisted = false;
   Services.perms.removeAll();
   Services.prefs.setBoolPref("plugins.click_to_play", true);
@@ -422,10 +453,54 @@ function test14() {
 // Tests that the overlay is shown instead of alternate content when
 // plugins are click to play
 function test15() {
   var plugin = gTestBrowser.contentDocument.getElementById("test");
   var doc = gTestBrowser.contentDocument;
   var mainBox = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
   ok(mainBox, "Test 15, Plugin with id=" + plugin.id + " overlay should exist");
 
+  prepareTest(test16a, gTestRoot + "plugin_bug743421.html");
+}
+
+// Tests that a plugin dynamically added to a page after one plugin is clicked
+// to play (which removes the notification) gets its own notification (1/4)
+function test16a() {
+  var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(!popupNotification, "Test 16a, Should not have a click-to-play notification");
+  var plugin = gTestBrowser.contentWindow.addPlugin();
+
+  setTimeout(test16b, 100);
+}
+
+// 2/4
+function test16b() {
+  var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(popupNotification, "Test 16b, Should have a click-to-play notification");
+  var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
+  var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(!objLoadingContent.activated, "Test 16b, Plugin should not be activated");
+  EventUtils.synthesizeMouse(plugin, 100, 100, { });
+  setTimeout(test16c, 100);
+}
+
+// 3/4
+function test16c() {
+  var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(!popupNotification, "Test 16c, Should not have a click-to-play notification");
+  var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[0];
+  var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(objLoadingContent.activated, "Test 16c, Plugin should be activated");
+  var plugin = gTestBrowser.contentWindow.addPlugin();
+
+  setTimeout(test16d, 100);
+}
+
+// 4/4
+function test16d() {
+  var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(popupNotification, "Test 16d, Should have a click-to-play notification");
+  var plugin = gTestBrowser.contentDocument.getElementsByTagName("embed")[1];
+  var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(!objLoadingContent.activated, "Test 16d, Plugin should not be activated");
+
   finishTest();
 }
--- a/browser/base/content/test/plugin_test2.html
+++ b/browser/base/content/test/plugin_test2.html
@@ -1,11 +1,10 @@
 <!DOCTYPE html>
 <html>
-<head>
-<meta charset="utf-8">
+<head>
+<meta charset="utf-8">
 </head>
 <body>
-<embed id="test" style="width: 200px; height: 200px" type="application/x-test">
 <embed id="test1" style="width: 200px; height: 200px" type="application/x-test">
 <embed id="test2" style="width: 200px; height: 200px" type="application/x-test">
 </body>
 </html>
--- a/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd
@@ -18,17 +18,17 @@
 <!ENTITY pluginWizard.finalPage.description.label            "&brandShortName; finished installing the missing plugins:">
 
 <!ENTITY pluginWizard.finalPage.moreInfo.label               "Find out more about Plugins or manually find missing plugins.">
 <!ENTITY pluginWizard.finalPage.restart.label                "&brandShortName; needs to be restarted for the plugin(s) to work.">
 
 <!ENTITY missingPlugin                                       "A plugin is needed to display this content.">
 <!-- LOCALIZATION NOTE (tapToPlayPlugin): Mobile (used for touch interfaces) only has one type of plugin possible. -->
 <!ENTITY tapToPlayPlugin                                     "Tap here to activate plugin.">
-<!ENTITY clickToPlayPlugins                                  "Click here to activate plugins.">
+<!ENTITY clickToPlayPlugin                                   "Click here to activate plugin.">
 <!ENTITY disabledPlugin                                      "This plugin is disabled.">
 <!ENTITY blockedPlugin.label                                 "This plugin has been blocked for your protection.">
 
 <!ENTITY installPlugin                                       "Install plugin…">
 <!ENTITY managePlugins                                       "Manage plugins…">
 
 <!-- LOCALIZATION NOTE (reloadPlugin.pre): include a trailing space as needed -->
 <!-- LOCALIZATION NOTE (reloadPlugin.middle): avoid leading/trailing spaces, this text is a link -->
--- a/toolkit/mozapps/plugins/content/pluginProblem.xml
+++ b/toolkit/mozapps/plugins/content/pluginProblem.xml
@@ -54,17 +54,17 @@
     </resources>
 
     <content>
         <xul:vbox class="mainBox" flex="1" chromedir="&locale.dir;">
             <xul:spacer flex="1"/>
             <xul:box class="icon"/>
             <html:div class="msg msgUnsupported">&missingPlugin;</html:div>
             <html:div class="msg msgTapToPlay">&tapToPlayPlugin;</html:div>
-            <html:div class="msg msgClickToPlay">&clickToPlayPlugins;</html:div>
+            <html:div class="msg msgClickToPlay">&clickToPlayPlugin;</html:div>
             <html:div class="msg msgDisabled">&disabledPlugin;</html:div>
             <html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
             <html:div class="msg msgCrashed"><!-- set at runtime --></html:div>
 
             <html:div class="installStatus">
                 <html:div class="msg msgInstallPlugin"><html:a class="installPluginLink" href="">&installPlugin;</html:a></html:div>
             </html:div>
             <html:div class="msg msgManagePlugins"><html:a class="managePluginsLink" href="">&managePlugins;</html:a></html:div>