Bug 853973 - If a plugin is overlayed by other content on the page, then treat it as a hidden plugin: hide the overlay and present the infobar for click-to-play or crashed plugins, r=jaws
authorBenjamin Smedberg <benjamin@smedbergs.us>
Tue, 17 Dec 2013 10:53:32 -0500
changeset 177832 97a3f38d295979cb6667c5c181c65565164e4b14
parent 177831 4f541dfa1a41bb920b67633e296a8d3bedb85141
child 177833 0be09b04530270a93d1e9228ddb07ec39276b4e1
push id462
push userraliiev@mozilla.com
push dateTue, 22 Apr 2014 00:22:30 +0000
treeherdermozilla-release@ac5db8c74ac0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs853973
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 853973 - If a plugin is overlayed by other content on the page, then treat it as a hidden plugin: hide the overlay and present the infobar for click-to-play or crashed plugins, r=jaws
browser/base/content/browser-plugins.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_CTP_resize.js
browser/base/content/test/general/browser_pluginnotification.js
browser/base/content/test/general/plugin_overlayed.html
mobile/android/chrome/content/PluginHelper.js
toolkit/mozapps/plugins/content/pluginProblemContent.css
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -79,24 +79,70 @@ var gPluginHandler = {
     // removing "Plugin" (and then trimming to get rid of any whitespace).
     // (Otherwise, something like "Java(TM) Plug-in 1.7.0_07" gets mangled)
     let newName = aName.replace(/\(.*?\)/g, "").
                         replace(/[\s\d\.\-\_\(\)]+$/, "").
                         replace(/\bplug-?in\b/i, "").trim();
     return newName;
   },
 
-  isTooSmall : function (plugin, overlay) {
+  /**
+   * Update the visibility of the plugin overlay.
+   */
+  setVisibility : function (plugin, overlay, shouldShow) {
+    overlay.classList.toggle("visible", shouldShow);
+  },
+
+  /**
+   * Check whether the plugin should be visible on the page. A plugin should
+   * not be visible if the overlay is too big, or if any other page content
+   * overlays it.
+   *
+   * This function will handle showing or hiding the overlay.
+   * @returns true if the plugin is invisible.
+   */
+  shouldShowOverlay : function (plugin, overlay) {
+    // If the overlay size is 0, we haven't done layout yet. Presume that
+    // plugins are visible until we know otherwise.
+    if (overlay.scrollWidth == 0) {
+      return true;
+    }
+
     // Is the <object>'s size too small to hold what we want to show?
     let pluginRect = plugin.getBoundingClientRect();
     // XXX bug 446693. The text-shadow on the submitted-report text at
     //     the bottom causes scrollHeight to be larger than it should be.
     let overflows = (overlay.scrollWidth > pluginRect.width) ||
                     (overlay.scrollHeight - 5 > pluginRect.height);
-    return overflows;
+    if (overflows) {
+      return false;
+    }
+
+    // Is the plugin covered up by other content so that it is not clickable?
+    // Floating point can confuse .elementFromPoint, so inset just a bit
+    let left = pluginRect.left + 2;
+    let right = pluginRect.right - 2;
+    let top = pluginRect.top + 2;
+    let bottom = pluginRect.bottom - 2;
+    let centerX = left + (right - left) / 2;
+    let centerY = top + (bottom - top) / 2;
+    let points = [[left, top],
+                   [left, bottom],
+                   [right, top],
+                   [right, bottom],
+                   [centerX, centerY]];
+
+    for (let [x, y] of points) {
+      let el = plugin.ownerDocument.elementFromPoint(x, y);
+      if (el !== plugin) {
+        return false;
+      }
+    }
+
+    return true;
   },
 
   addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
     // XXX just doing (callback)(arg) was giving a same-origin error. bug?
     let self = this;
     let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
     linkNode.addEventListener("click",
                               function(evt) {
@@ -314,36 +360,28 @@ var gPluginHandler = {
         shouldShowNotification = true;
         break;
     }
 
     // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
     if (eventType != "PluginCrashed") {
       let overlay = this.getPluginUI(plugin, "main");
       if (overlay != null) {
-        if (!this.isTooSmall(plugin, overlay))
-          overlay.style.visibility = "visible";
-
-        plugin.addEventListener("overflow", function(event) {
-          overlay.style.visibility = "hidden";
-          gPluginHandler._setPluginNotificationIcon(browser);
-        });
-        plugin.addEventListener("underflow", function(event) {
-          // this is triggered if only one dimension underflows,
-          // the other dimension might still overflow
-          if (!gPluginHandler.isTooSmall(plugin, overlay)) {
-            overlay.style.visibility = "visible";
-          }
-          gPluginHandler._setPluginNotificationIcon(browser);
-        });
+        this.setVisibility(plugin, overlay,
+                           this.shouldShowOverlay(plugin, overlay));
+        let resizeListener = (event) => {
+          this.setVisibility(plugin, overlay,
+            this.shouldShowOverlay(plugin, overlay));
+          this._setPluginNotificationIcon(browser);
+        };
+        plugin.addEventListener("overflow", resizeListener);
+        plugin.addEventListener("underflow", resizeListener);
       }
     }
 
-    // Only show the notification after we've done the isTooSmall check, so
-    // that the notification can decide whether to show the "alert" icon
     if (shouldShowNotification) {
       this._showClickToPlayNotification(browser, plugin, false);
     }
   },
 
   isKnownPlugin: function PH_isKnownPlugin(objLoadingContent) {
     return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
             Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
@@ -373,18 +411,19 @@ var gPluginHandler = {
 
     return !objLoadingContent.activated &&
            pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
            isFallbackTypeValid;
   },
 
   hideClickToPlayOverlay: function(aPlugin) {
     let overlay = this.getPluginUI(aPlugin, "main");
-    if (overlay)
-      overlay.style.visibility = "hidden";
+    if (overlay) {
+      overlay.classList.remove("visible");
+    }
   },
 
   stopPlayPreview: function PH_stopPlayPreview(aPlugin, aPlayPlugin) {
     let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
     if (objLoadingContent.activated)
       return;
 
     if (aPlayPlugin)
@@ -525,18 +564,19 @@ var gPluginHandler = {
       return;
     let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
     let principal = doc.defaultView.top.document.nodePrincipal;
     let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
 
     let overlay = this.getPluginUI(aPlugin, "main");
 
     if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION) {
-      if (overlay)
-        overlay.style.visibility = "hidden";
+      if (overlay) {
+        overlay.classList.remove("visible");
+      }
       return;
     }
 
     if (overlay) {
       overlay.addEventListener("click", gPluginHandler._overlayClickListener, true);
       let closeIcon = this.getPluginUI(aPlugin, "closeIcon");
       closeIcon.addEventListener("click", function(aEvent) {
         if (aEvent.button == 0 && aEvent.isTrusted)
@@ -924,17 +964,19 @@ var gPluginHandler = {
           fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE &&
           fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
         continue;
       }
       let overlay = this.getPluginUI(plugin, "main");
       if (!overlay) {
         continue;
       }
-      if (!this.isTooSmall(plugin, overlay)) {
+      let shouldShow = this.shouldShowOverlay(plugin, overlay);
+      this.setVisibility(plugin, overlay, shouldShow);
+      if (shouldShow) {
         actions.delete(info.permissionString);
         if (actions.size == 0) {
           break;
         }
       }
     }
 
     // Set up the icon
@@ -1181,32 +1223,28 @@ var gPluginHandler = {
 
     let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
 
     let link = this.getPluginUI(plugin, "reloadLink");
     this.addLinkClickCallback(link, "reloadPage", browser);
 
     let notificationBox = gBrowser.getNotificationBox(browser);
 
-    let isShowing = true;
+    let isShowing = this.shouldShowOverlay(plugin, overlay);
 
     // Is the <object>'s size too small to hold what we want to show?
-    if (this.isTooSmall(plugin, overlay)) {
+    if (!isShowing) {
       // First try hiding the crash report submission UI.
       statusDiv.removeAttribute("status");
 
-      if (this.isTooSmall(plugin, overlay)) {
-        // Hide the overlay's contents. Use visibility style, so that it doesn't
-        // collapse down to 0x0.
-        isShowing = false;
-      }
+      isShowing = this.shouldShowOverlay(plugin, overlay);
     }
+    this.setVisibility(plugin, overlay, isShowing);
 
     if (isShowing) {
-      overlay.style.visibility = "visible";
       // If a previous plugin on the page was too small and resulted in adding a
       // notification bar, then remove it because this plugin instance it big
       // enough to serve as in-content notification.
       hideNotificationBar();
       doc.mozNoPluginCrashedNotification = true;
     } else {
       // If another plugin on the page was large enough to show our UI, we don't
       // want to show a notification bar.
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -67,16 +67,17 @@ support-files =
   plugin_bug752516.html
   plugin_bug787619.html
   plugin_bug797677.html
   plugin_bug820497.html
   plugin_clickToPlayAllow.html
   plugin_clickToPlayDeny.html
   plugin_data_url.html
   plugin_hidden_to_visible.html
+  plugin_overlayed.html
   plugin_small.html
   plugin_syncRemoved.html
   plugin_test.html
   plugin_test2.html
   plugin_test3.html
   plugin_two_types.html
   plugin_unknown.html
   pluginCrashCommentAndURL.html
--- a/browser/base/content/test/general/browser_CTP_resize.js
+++ b/browser/base/content/test/general/browser_CTP_resize.js
@@ -66,51 +66,51 @@ function runAfterPluginBindingAttached(f
 // once they are resized to a size that can hold the overlay
 
 function test1() {
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "Test 1, Should have a click-to-play notification");
 
   let plugin = gTestBrowser.contentDocument.getElementById("test");
   let doc = gTestBrowser.contentDocument;
-  let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
   ok(overlay, "Test 1, Should have an overlay.");
-  is(window.getComputedStyle(overlay).visibility, 'hidden', "Test 1, Overlay should be hidden");
+  ok(!overlay.classList.contains("visible"), "Test 1, Overlay should be hidden");
 
   plugin.style.width = '300px';
   executeSoon(test2);
 }
 
 function test2() {
   let plugin = gTestBrowser.contentDocument.getElementById("test");
   let doc = gTestBrowser.contentDocument;
-  let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
   ok(overlay, "Test 2, Should have an overlay.");
-  is(window.getComputedStyle(overlay).visibility, 'hidden', "Test 2, Overlay should be hidden");
+  ok(!overlay.classList.contains("visible"), "Test 2, Overlay should be hidden");
 
   plugin.style.height = '300px';
-  let condition = () => window.getComputedStyle(overlay).visibility == 'visible';
+  let condition = () => overlay.classList.contains("visible");
   waitForCondition(condition, test3, "Test 2, Waited too long for overlay to become visible");
 }
 
 function test3() {
   let plugin = gTestBrowser.contentDocument.getElementById("test");
   let doc = gTestBrowser.contentDocument;
-  let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
   ok(overlay, "Test 3, Should have an overlay.");
-  is(window.getComputedStyle(overlay).visibility, 'visible', "Test 3, Overlay should be visible");
+  ok(overlay.classList.contains("visible"), "Test 3, Overlay should be visible");
 
   plugin.style.width = '10px';
   plugin.style.height = '10px';
-  let condition = () => window.getComputedStyle(overlay).visibility == 'hidden';
+  let condition = () => !overlay.classList.contains("visible");
   waitForCondition(condition, test4, "Test 3, Waited too long for overlay to become hidden");
 }
 
 function test4() {
   let plugin = gTestBrowser.contentDocument.getElementById("test");
   let doc = gTestBrowser.contentDocument;
-  let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
   ok(overlay, "Test 4, Should have an overlay.");
-  is(window.getComputedStyle(overlay).visibility, 'hidden', "Test 4, Overlay should be hidden");
+  ok(!overlay.classList.contains("visible"), "Test 4, Overlay should be hidden");
 
   clearAllPluginPermissions();
   finishTest();
 }
--- a/browser/base/content/test/general/browser_pluginnotification.js
+++ b/browser/base/content/test/general/browser_pluginnotification.js
@@ -319,17 +319,17 @@ function test14() {
   prepareTest(runAfterPluginBindingAttached(test15), gTestRoot + "plugin_alternate_content.html");
 }
 
 // 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");
+  var mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
   ok(mainBox, "Test 15, Plugin with id=" + plugin.id + " overlay should exist");
 
   prepareTest(runAfterPluginBindingAttached(test17), gTestRoot + "plugin_bug749455.html");
 }
 
 // Test 16 removed
 
 // Tests that mContentType is used for click-to-play plugins, and not the
@@ -351,18 +351,18 @@ function test18a() {
   var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(clickToPlayNotification, "Test 18a, Should have a click-to-play notification");
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("test");
   ok(plugin, "Test 18a, Found plugin in page");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
   ok(!objLoadingContent.activated, "Test 18a, Plugin should not be activated");
-  var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-  ok(overlay.style.visibility != "hidden", "Test 18a, Plugin overlay should exist, not be hidden");
+  var overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+  ok(overlay.classList.contains("visible"), "Test 18a, Plugin overlay should exist, not be hidden");
   var updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
   ok(updateLink.style.visibility != "hidden", "Test 18a, Plugin should have an update link");
 
   var tabOpenListener = new TabOpenListener(Services.urlFormatter.formatURLPref("plugins.update.url"), false, false);
   tabOpenListener.handleEvent = function(event) {
     if (event.type == "TabOpen") {
       gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
       this.tab = event.originalTarget;
@@ -375,18 +375,18 @@ function test18a() {
 }
 
 function test18b() {
   // clicking the update link should not activate the plugin
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("test");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(!objLoadingContent.activated, "Test 18b, Plugin should not be activated");
-  var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-  ok(overlay.style.visibility != "hidden", "Test 18b, Plugin overlay should exist, not be hidden");
+  var overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+  ok(overlay.classList.contains("visible"), "Test 18b, Plugin overlay should exist, not be hidden");
 
   setAndUpdateBlocklist(gHttpTestRoot + "blockPluginVulnerableNoUpdate.xml",
   function() {
     prepareTest(runAfterPluginBindingAttached(test18c), gHttpTestRoot + "plugin_test.html");
   });
 }
 
 // Tests a vulnerable plugin with no update
@@ -394,18 +394,18 @@ function test18c() {
   var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(clickToPlayNotification, "Test 18c, Should have a click-to-play notification");
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("test");
   ok(plugin, "Test 18c, Found plugin in page");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE, "Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE");
   ok(!objLoadingContent.activated, "Test 18c, Plugin should not be activated");
-  var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-  ok(overlay.style.visibility != "hidden", "Test 18c, Plugin overlay should exist, not be hidden");
+  var overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+  ok(overlay.classList.contains("visible"), "Test 18c, Plugin overlay should exist, not be hidden");
   var updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
   ok(updateLink.style.display != "block", "Test 18c, Plugin should not have an update link");
 
   // check that click "Always allow" works with blocklisted plugins
   clickToPlayNotification.reshow();
   PopupNotifications.panel.firstChild._primaryButton.click();
 
   var condition = function() objLoadingContent.activated;
@@ -523,17 +523,17 @@ function test19f() {
 
 // Tests that a plugin in a div that goes from style="display: none" to
 // "display: block" can be clicked to activate.
 function test20a() {
   var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(!clickToPlayNotification, "Test 20a, Should not have a click-to-play notification");
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("plugin");
-  var mainBox = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  var mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
   ok(mainBox, "Test 20a, plugin overlay should not be null");
   var pluginRect = mainBox.getBoundingClientRect();
   ok(pluginRect.width == 0, "Test 20a, plugin should have an overlay with 0px width");
   ok(pluginRect.height == 0, "Test 20a, plugin should have an overlay with 0px height");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(!objLoadingContent.activated, "Test 20a, plugin should not be activated");
   var div = doc.getElementById("container");
   ok(div.style.display == "none", "Test 20a, container div should be display: none");
@@ -546,17 +546,17 @@ function test20a() {
   waitForCondition(condition, test20b, "Test 20a, Waited too long for plugin to become visible");
 }
 
 function test20b() {
   var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(clickToPlayNotification, "Test 20b, Should now have a click-to-play notification");
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("plugin");
-  var pluginRect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
+  var pluginRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
   ok(pluginRect.width == 200, "Test 20b, plugin should have an overlay with 200px width");
   ok(pluginRect.height == 200, "Test 20b, plugin should have an overlay with 200px height");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(!objLoadingContent.activated, "Test 20b, plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed, "Test 20b, Doorhanger should start out dismissed");
 
   EventUtils.synthesizeMouseAtCenter(plugin, {}, gTestBrowser.contentWindow);
@@ -570,17 +570,17 @@ function test20c() {
   var plugin = doc.getElementById("plugin");
   let condition = function() plugin.activated;
   waitForCondition(condition, test20d, "Test 20c", "Waiting for plugin to activate");
 }
 
 function test20d() {
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("plugin");
-  var pluginRect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
+  var pluginRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
   ok(pluginRect.width == 0, "Test 20d, plugin should have click-to-play overlay with zero width");
   ok(pluginRect.height == 0, "Test 20d, plugin should have click-to-play overlay with zero height");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(objLoadingContent.activated, "Test 20d, plugin should be activated");
 
   clearAllPluginPermissions();
 
   prepareTest(runAfterPluginBindingAttached(test21a), gTestRoot + "plugin_two_types.html");
@@ -590,17 +590,17 @@ function test20d() {
 function test21a() {
   var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21a, Should have a click-to-play notification");
 
   var doc = gTestBrowser.contentDocument;
   var ids = ["test", "secondtestA", "secondtestB"];
   for (var id of ids) {
     var plugin = doc.getElementById(id);
-    var rect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
+    var rect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
     ok(rect.width == 200, "Test 21a, Plugin with id=" + plugin.id + " overlay rect should have 200px width before being clicked");
     ok(rect.height == 200, "Test 21a, Plugin with id=" + plugin.id + " overlay rect should have 200px height before being clicked");
     var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     ok(!objLoadingContent.activated, "Test 21a, Plugin with id=" + plugin.id + " should not be activated");
   }
 
   // we have to actually show the panel to get the bindings to instantiate
   notification.reshow();
@@ -640,26 +640,26 @@ function test21c() {
   var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 21c, Should have a click-to-play notification");
 
   notification.reshow();
   ok(notification.options.centerActions.size == 2, "Test 21c, Should have one type of plugin in the notification");
 
   var doc = gTestBrowser.contentDocument;
   var plugin = doc.getElementById("test");
-  var rect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
+  var rect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
   ok(rect.width == 0, "Test 21c, Plugin with id=" + plugin.id + " overlay rect should have 0px width after being clicked");
   ok(rect.height == 0, "Test 21c, Plugin with id=" + plugin.id + " overlay rect should have 0px height after being clicked");
   var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(objLoadingContent.activated, "Test 21c, Plugin with id=" + plugin.id + " should be activated");
 
   var ids = ["secondtestA", "secondtestB"];
   for (var id of ids) {
     var plugin = doc.getElementById(id);
-    var rect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
+    var rect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
     ok(rect.width == 200, "Test 21c, Plugin with id=" + plugin.id + " overlay rect should have 200px width before being clicked");
     ok(rect.height == 200, "Test 21c, Plugin with id=" + plugin.id + " overlay rect should have 200px height before being clicked");
     var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     ok(!objLoadingContent.activated, "Test 21c, Plugin with id=" + plugin.id + " should not be activated");
   }
 
   var centerAction = null;
   for (var action of notification.options.centerActions.values()) {
@@ -694,17 +694,17 @@ function test21c() {
   waitForCondition(condition, test21e, "Test 21d, Waited too long for plugin to activate");
 }
 
 function test21e() {
   var doc = gTestBrowser.contentDocument;
   var ids = ["test", "secondtestA", "secondtestB"];
   for (var id of ids) {
     var plugin = doc.getElementById(id);
-    var rect = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox").getBoundingClientRect();
+    var rect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
     ok(rect.width == 0, "Test 21e, Plugin with id=" + plugin.id + " overlay rect should have 0px width after being clicked");
     ok(rect.height == 0, "Test 21e, Plugin with id=" + plugin.id + " overlay rect should have 0px height after being clicked");
     var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     ok(objLoadingContent.activated, "Test 21e, Plugin with id=" + plugin.id + " should be activated");
   }
 
   getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
   getTestPlugin("Second Test Plug-in").enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
@@ -865,11 +865,38 @@ function test26() {
 
 function test27() {
   let notification = PopupNotifications.getNotification("click-to-play-plugins");
   ok(notification, "Test 27: There should be a plugin notification");
 
   let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
 
   waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
-    finishTest,
+    () => {
+      getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
+      prepareTest(test28, gTestRoot + "plugin_overlayed.html");
+    },
     "Test 27, expected to not have a plugin notification bar");
 }
+
+function test28() {
+  let notification = PopupNotifications.getNotification("click-to-play-plugins");
+  ok(notification, "Test 28: There should be a plugin notification");
+
+  let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
+  waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
+    test28b,
+    "Test 28, expected the plugin infobar to be triggered when plugin was overlayed");
+}
+
+function test28b()
+{
+  let doc = gTestBrowser.contentDocument;
+  let plugin = doc.getElementById("test");
+  ok(plugin, "Test 28b, Found plugin in page");
+  plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+  is(plugin.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 28b, plugin fallback type should be PLUGIN_CLICK_TO_PLAY");
+  ok(!plugin.activated, "Test 28b, Plugin should not be activated");
+  let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+  ok(!overlay.classList.contains("visible"), "Test 28b, Plugin overlay should be hidden");
+
+  finishTest();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/plugin_overlayed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<head>
+  <meta charset="utf-8">
+  <style type="text/css">
+    .absthing {
+      width: 400px;
+      height: 400px;
+      position: absolute;
+      left: 20px;
+      top: 20px;
+    }
+    #d1 {
+      z-index: 1;
+    }
+    #d2 {
+      z-index: 2;
+      background-color: rgba(0,0,255,0.5);
+      border: 1px solid red;
+    }
+  </style>
+<body>
+  <div class="absthing" id="d1">
+    <embed id="test" type="application/x-test">
+  </div>
+  <div class="absthing" id="d2">
+    <p>This is overlaying
+  </div>
--- a/mobile/android/chrome/content/PluginHelper.js
+++ b/mobile/android/chrome/content/PluginHelper.js
@@ -149,17 +149,17 @@ var PluginHelper = {
     }
 
     return tagMimetype;
   },
 
   handlePluginBindingAttached: function (aTab, aEvent) {
     let plugin = aEvent.target;
     let doc = plugin.ownerDocument;
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
     if (!overlay || overlay._bindingHandled) {
       return;
     }
     overlay._bindingHandled = true;
 
     let eventType = PluginHelper._getBindingType(plugin);
     if (!eventType) {
       // Not all bindings have handlers
@@ -180,17 +180,17 @@ var PluginHelper = {
         // If the plugin is hidden, or if the overlay is too small, show a 
         // doorhanger notification
         if (PluginHelper.isTooSmall(plugin, overlay)) {
           PluginHelper.delayAndShowDoorHanger(aTab);
         } else {
           // There's a large enough visible overlay that we don't need to show
           // the doorhanger.
           aTab.shouldShowPluginDoorhanger = false;
-          overlay.style.visibility = "visible";
+          overlay.classList.add("visible");
         }
 
         // Add click to play listener to the overlay
         overlay.addEventListener("click", function(e) {
           if (!e.isTrusted)
             return;
           e.preventDefault();
           let win = e.target.ownerDocument.defaultView.top;
@@ -198,24 +198,24 @@ var PluginHelper = {
           tab.clickToPlayPluginsActivated = true;
           PluginHelper.playAllPlugins(win);
 
           NativeWindow.doorhanger.hide("ask-to-play-plugins", tab.id);
         }, true);
 
         // Add handlers for over- and underflow in case the plugin gets resized
         plugin.addEventListener("overflow", function(event) {
-          overlay.style.visibility = "hidden";
+          overlay.classList.remove("visible");
           PluginHelper.delayAndShowDoorHanger(aTab);
         });
         plugin.addEventListener("underflow", function(event) {
           // This is also triggered if only one dimension underflows,
           // the other dimension might still overflow
           if (!PluginHelper.isTooSmall(plugin, overlay)) {
-            overlay.style.visibility = "visible";
+            overlay.classList.add("visible");
           }
         });
 
         break;
       }
 
       case "PluginPlayPreview": {
         let previewContent = doc.getAnonymousElementByAttribute(plugin, "class", "previewPluginContent");
@@ -267,17 +267,17 @@ var PluginHelper = {
 
       case "PluginNotFound": {
         // On devices where we don't support Flash, there will be a
         // "Learn More..." link in the missing plugin error message.
         let learnMoreLink = doc.getAnonymousElementByAttribute(plugin, "class", "unsupportedLearnMoreLink");
         let learnMoreUrl = Services.urlFormatter.formatURLPref("app.support.baseURL");
         learnMoreUrl += "why-cant-firefox-mobile-play-flash-on-my-device";
         learnMoreLink.href = learnMoreUrl;
-        overlay.style.visibility = "visible";
+        overlay.classList.add("visible");
         break;
       }
     }
   },
 
   // Helper to get the binding handler type from a plugin object
   _getBindingType: function(plugin) {
     if (!(plugin instanceof Ci.nsIObjectLoadingContent))
--- a/toolkit/mozapps/plugins/content/pluginProblemContent.css
+++ b/toolkit/mozapps/plugins/content/pluginProblemContent.css
@@ -37,34 +37,43 @@ html|applet:not([height]), html|applet[h
 :-moz-handler-disabled .mainBox:focus,
 :-moz-handler-blocked .mainBox:focus {
   outline: 1px dotted;
 }
 
 .mainBox {
   width: inherit;
   height: inherit;
-  /* Initialize the overlay with visibility:hidden to prevent flickering if
-   * the plugin is too small to show the overlay */
-  visibility: hidden;
   overflow: hidden;
   direction: ltr;
   unicode-bidi: embed;
   /* used to block inherited properties */
   text-transform: none;
   text-indent: 0;
   cursor: initial;
   white-space: initial;
   word-spacing: initial;
   letter-spacing: initial;
   line-height: initial;
   z-index: 2147483647;
   position: relative;
 }
 
+/* Initialize the overlay with visibility:hidden to prevent flickering if
+* the plugin is too small to show the overlay */
+.mainBox > .hoverBox,
+.mainBox > .closeIcon {
+  visibility: hidden;
+}
+
+.visible > .hoverBox,
+.visible > .closeIcon {
+  visibility: visible;
+}
+
 .mainBox[chromedir="rtl"] {
   direction: rtl;
 }
 
 :-moz-handler-playpreview .mainBox {
   display: none;
 }