Bug 922349 - Removing iframe with plugin content breaks doorhanger. r=gavin
authorGeorg Fritzsche <georg.fritzsche@googlemail.com>
Wed, 16 Oct 2013 00:42:48 +0200
changeset 166101 e337dc39645fab0b0868f971db77fa244e3e9acc
parent 166100 fe28224ef7193714374c6f7c7c514500b6a66691
child 166102 4c0ebfb35fc31fe85ebbe7224241521242cfb75a
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs922349
milestone27.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 922349 - Removing iframe with plugin content breaks doorhanger. r=gavin
browser/base/content/browser-plugins.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_CTP_iframe.js
browser/base/content/test/general/plugin_iframe.html
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -703,16 +703,25 @@ var gPluginHandler = {
       }
       else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
         url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
       }
       pluginInfo.detailsLink = url;
 
       centerActions.push(pluginInfo);
     }
+
+    if (centerActions.length == 0) {
+      // TODO: this is a temporary band-aid to avoid broken doorhangers
+      // until bug 926605 is landed.
+      notification.options.centerActions = [];
+      setTimeout(() => PopupNotifications.remove(notification), 0);
+      return;
+    }
+
     centerActions.sort(function(a, b) {
       return a.pluginName.localeCompare(b.pluginName);
     });
 
     notification.options.centerActions = centerActions;
 
     // Histograms always start at 0, even though our data starts at 1
     let histogramCount = centerActions.length - 1;
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -54,16 +54,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_iframe.html
   plugin_small.html
   plugin_test.html
   plugin_test2.html
   plugin_test3.html
   plugin_two_types.html
   plugin_unknown.html
   print_postdata.sjs
   redirect_bug623155.sjs
@@ -74,16 +75,17 @@ support-files =
   test_bug839103.html
   test_wyciwyg_copying.html
   title_test.svg
   video.ogg
   zoom_test.html
 
 [browser_CTP_data_urls.js]
 [browser_CTP_drag_drop.js]
+[browser_CTP_iframe.js]
 [browser_CTP_nonplugins.js]
 [browser_CTP_resize.js]
 [browser_URLBarSetURI.js]
 [browser_aboutHealthReport.js]
 [browser_aboutHome.js]
 [browser_aboutSyncProgress.js]
 [browser_addKeywordSearch.js]
 [browser_addon_bar_aomlistener.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_CTP_iframe.js
@@ -0,0 +1,135 @@
+var rootDir = getRootDirectory(gTestPath);
+const gTestRoot = rootDir;
+const gHttpTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+var gTestBrowser = null;
+var gNextTest = null;
+var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
+var gPageLoads = 0;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// This listens for the next opened tab and checks it is of the right url.
+// opencallback is called when the new tab is fully loaded
+// closecallback is called when the tab is closed
+function TabOpenListener(url, opencallback, closecallback) {
+  this.url = url;
+  this.opencallback = opencallback;
+  this.closecallback = closecallback;
+
+  gBrowser.tabContainer.addEventListener("TabOpen", this, false);
+}
+
+TabOpenListener.prototype = {
+  url: null,
+  opencallback: null,
+  closecallback: null,
+  tab: null,
+  browser: null,
+
+  handleEvent: function(event) {
+    if (event.type == "TabOpen") {
+      gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
+      this.tab = event.originalTarget;
+      this.browser = this.tab.linkedBrowser;
+      gBrowser.addEventListener("pageshow", this, false);
+    } else if (event.type == "pageshow") {
+      if (event.target.location.href != this.url)
+        return;
+      gBrowser.removeEventListener("pageshow", this, false);
+      this.tab.addEventListener("TabClose", this, false);
+      var url = this.browser.contentDocument.location.href;
+      is(url, this.url, "Should have opened the correct tab");
+      this.opencallback(this.tab, this.browser.contentWindow);
+    } else if (event.type == "TabClose") {
+      if (event.originalTarget != this.tab)
+        return;
+      this.tab.removeEventListener("TabClose", this, false);
+      this.opencallback = null;
+      this.tab = null;
+      this.browser = null;
+      // Let the window close complete
+      executeSoon(this.closecallback);
+      this.closecallback = null;
+    }
+  }
+};
+
+function test() {
+  waitForExplicitFinish();
+  registerCleanupFunction(function() {
+    clearAllPluginPermissions();
+    Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
+  });
+  Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
+
+  var newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  gTestBrowser = gBrowser.selectedBrowser;
+  gTestBrowser.addEventListener("load", pageLoad, true);
+
+  Services.prefs.setBoolPref("plugins.click_to_play", true);
+  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+
+  prepareTest(runAfterPluginBindingAttached(test1), gHttpTestRoot + "plugin_iframe.html");
+}
+
+function finishTest() {
+  clearAllPluginPermissions();
+  gTestBrowser.removeEventListener("load", pageLoad, true);
+  gBrowser.removeCurrentTab();
+  window.focus();
+  finish();
+}
+
+function pageLoad() {
+  // Wait for the iframe to be loaded as well.
+  if (gPageLoads++ < 1)
+    return;
+
+  // The plugin events are async dispatched and can come after the load event
+  // This just allows the events to fire before we then go on to test the states.
+  executeSoon(gNextTest);
+}
+
+function prepareTest(nextTest, url) {
+  gNextTest = nextTest;
+  gTestBrowser.contentWindow.location = url;
+}
+
+// Due to layout being async, "PluginBindAttached" may trigger later.
+// This wraps a function to force a layout flush, thus triggering it,
+// and schedules the function execution so they're definitely executed
+// afterwards.
+function runAfterPluginBindingAttached(func) {
+  return function() {
+    let doc = gTestBrowser.contentDocument.getElementById('frame').contentDocument;
+    let elems = doc.getElementsByTagName('embed');
+    if (elems.length < 1) {
+      elems = doc.getElementsByTagName('object');
+    }
+    elems[0].clientTop;
+    executeSoon(func);
+  };
+}
+
+// Test that we don't show a doorhanger after removing the last plugin
+// when the plugin was in an iframe.
+
+function test1() {
+  let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(popupNotification, "Test 1, Should have a click-to-play notification");
+
+  let frame = gTestBrowser.contentDocument.getElementById("frame");
+  frame.parentElement.removeChild(frame);
+
+  let condition = () => {
+    let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+    if (notification) {
+      notification.reshow();
+    }
+    return !notification;
+  }
+
+  waitForCondition(condition, finishTest, "Test1, Waited too long for notification too be removed");
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/plugin_iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+</head>
+<body>
+<iframe src="plugin_test.html" id="frame" width="300" height="300">
+  This should load plugin_test.html
+</iframe>
+</body>
+</html>