Bug 1497940 - Part V, Convert pluginProblem to UA Widget r=smaug
☠☠ backed out by 373839b9f787 ☠ ☠
authorTimothy Guan-tin Chien <timdream@gmail.com>
Thu, 22 Nov 2018 02:13:09 +0000
changeset 447643 493083d55865df594eef9f26c5ba7a98873cacb1
parent 447642 b503b1a1552c5c92622d9a7da04a65221ae483a8
child 447644 9201152ee77ca64d5cf737fb29e578e348916705
push id73505
push usertchien@mozilla.com
push dateThu, 22 Nov 2018 02:14:38 +0000
treeherderautoland@493083d55865 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1497940
milestone65.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 1497940 - Part V, Convert pluginProblem to UA Widget r=smaug This patch creates a pluginProblem UA Widget and constructs it (instead of the XBL pluginProblem binding) when UA Widget is enabled. Tests in browser/base/content/test/plugins/ are duplicated so that we could test both versions. Depends on D11702 Differential Revision: https://phabricator.services.mozilla.com/D11703
browser/actors/ContextMenuChild.jsm
browser/actors/PluginChild.jsm
browser/base/content/test/plugins/browser.ini
browser/base/content/test/plugins/browser_CTP_crashreporting.js
browser/base/content/test/plugins/browser_CTP_hide_overlay.js
browser/base/content/test/plugins/browser_CTP_iframe.js
browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
browser/base/content/test/plugins/browser_CTP_overlay_styles.js
browser/base/content/test/plugins/browser_CTP_resize.js
browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js
browser/base/content/test/plugins/browser_CTP_zoom.js
browser/base/content/test/plugins/browser_blocking.js
browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
browser/base/content/test/plugins/browser_pluginnotification.js
browser/base/content/test/plugins/browser_private_clicktoplay.js
browser/base/content/test/plugins/xbl/.eslintrc.js
browser/base/content/test/plugins/xbl/blockNoPlugins.xml
browser/base/content/test/plugins/xbl/blockPluginHard.xml
browser/base/content/test/plugins/xbl/blockPluginInfoURL.xml
browser/base/content/test/plugins/xbl/blockPluginVulnerableNoUpdate.xml
browser/base/content/test/plugins/xbl/blockPluginVulnerableUpdatable.xml
browser/base/content/test/plugins/xbl/blocklist_proxy.js
browser/base/content/test/plugins/xbl/browser.ini
browser/base/content/test/plugins/xbl/browser_CTP_context_menu.js
browser/base/content/test/plugins/xbl/browser_CTP_crashreporting.js
browser/base/content/test/plugins/xbl/browser_CTP_drag_drop.js
browser/base/content/test/plugins/xbl/browser_CTP_favorfallback.js
browser/base/content/test/plugins/xbl/browser_CTP_hide_overlay.js
browser/base/content/test/plugins/xbl/browser_CTP_iframe.js
browser/base/content/test/plugins/xbl/browser_CTP_nonplugins.js
browser/base/content/test/plugins/xbl/browser_CTP_outsideScrollArea.js
browser/base/content/test/plugins/xbl/browser_CTP_overlay_styles.js
browser/base/content/test/plugins/xbl/browser_CTP_resize.js
browser/base/content/test/plugins/xbl/browser_CTP_shouldShowOverlay.js
browser/base/content/test/plugins/xbl/browser_CTP_zoom.js
browser/base/content/test/plugins/xbl/browser_blocking.js
browser/base/content/test/plugins/xbl/browser_blocklist_content.js
browser/base/content/test/plugins/xbl/browser_bug743421.js
browser/base/content/test/plugins/xbl/browser_bug744745.js
browser/base/content/test/plugins/xbl/browser_bug787619.js
browser/base/content/test/plugins/xbl/browser_bug797677.js
browser/base/content/test/plugins/xbl/browser_bug812562.js
browser/base/content/test/plugins/xbl/browser_bug818118.js
browser/base/content/test/plugins/xbl/browser_bug820497.js
browser/base/content/test/plugins/xbl/browser_clearplugindata.html
browser/base/content/test/plugins/xbl/browser_clearplugindata.js
browser/base/content/test/plugins/xbl/browser_clearplugindata_noage.html
browser/base/content/test/plugins/xbl/browser_enable_DRM_prompt.js
browser/base/content/test/plugins/xbl/browser_globalplugin_crashinfobar.js
browser/base/content/test/plugins/xbl/browser_iterate_hidden_plugins.js
browser/base/content/test/plugins/xbl/browser_pluginCrashCommentAndURL.js
browser/base/content/test/plugins/xbl/browser_pluginCrashReportNonDeterminism.js
browser/base/content/test/plugins/xbl/browser_plugin_reloading.js
browser/base/content/test/plugins/xbl/browser_pluginnotification.js
browser/base/content/test/plugins/xbl/browser_private_browsing_eme_persistent_state.js
browser/base/content/test/plugins/xbl/browser_private_clicktoplay.js
browser/base/content/test/plugins/xbl/browser_subframe_access_hidden_plugins.js
browser/base/content/test/plugins/xbl/empty_file.html
browser/base/content/test/plugins/xbl/plugin_add_dynamically.html
browser/base/content/test/plugins/xbl/plugin_alternate_content.html
browser/base/content/test/plugins/xbl/plugin_big.html
browser/base/content/test/plugins/xbl/plugin_both.html
browser/base/content/test/plugins/xbl/plugin_both2.html
browser/base/content/test/plugins/xbl/plugin_bug744745.html
browser/base/content/test/plugins/xbl/plugin_bug749455.html
browser/base/content/test/plugins/xbl/plugin_bug787619.html
browser/base/content/test/plugins/xbl/plugin_bug797677.html
browser/base/content/test/plugins/xbl/plugin_bug820497.html
browser/base/content/test/plugins/xbl/plugin_clickToPlayAllow.html
browser/base/content/test/plugins/xbl/plugin_clickToPlayDeny.html
browser/base/content/test/plugins/xbl/plugin_crashCommentAndURL.html
browser/base/content/test/plugins/xbl/plugin_favorfallback.html
browser/base/content/test/plugins/xbl/plugin_hidden_to_visible.html
browser/base/content/test/plugins/xbl/plugin_iframe.html
browser/base/content/test/plugins/xbl/plugin_outsideScrollArea.html
browser/base/content/test/plugins/xbl/plugin_overlay_styles.html
browser/base/content/test/plugins/xbl/plugin_shouldShowOverlay.html
browser/base/content/test/plugins/xbl/plugin_simple_blank.swf
browser/base/content/test/plugins/xbl/plugin_small.html
browser/base/content/test/plugins/xbl/plugin_small_2.html
browser/base/content/test/plugins/xbl/plugin_syncRemoved.html
browser/base/content/test/plugins/xbl/plugin_test.html
browser/base/content/test/plugins/xbl/plugin_test2.html
browser/base/content/test/plugins/xbl/plugin_test3.html
browser/base/content/test/plugins/xbl/plugin_two_types.html
browser/base/content/test/plugins/xbl/plugin_unknown.html
browser/base/content/test/plugins/xbl/plugin_zoom.html
browser/base/moz.build
dom/base/nsObjectLoadingContent.cpp
dom/plugins/test/mochitest/test_crash_submit.xul
dom/plugins/test/mochitest/test_hang_submit.xul
toolkit/actors/UAWidgetsChild.jsm
toolkit/content/jar.mn
toolkit/content/widgets/pluginProblem.js
toolkit/pluginproblem/content/pluginProblemBinding.css
toolkit/pluginproblem/content/pluginProblemContent.css
toolkit/themes/shared/plugins/pluginProblem.css
--- a/browser/actors/ContextMenuChild.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -690,23 +690,25 @@ class ContextMenuChild extends ActorChil
 
     context.timeStamp = aEvent.timeStamp;
     context.screenX = aEvent.screenX;
     context.screenY = aEvent.screenY;
     context.mozInputSource = aEvent.mozInputSource;
 
     let node = aEvent.composedTarget;
 
-    // Set the node to containing <video>/<audio> if the node
-    // is in the videocontrols UA Widget.
+    // Set the node to containing <video>/<audio>/<embed>/<object> if the node
+    // is in the videocontrols/pluginProblem UA Widget.
     if (this.content.ShadowRoot) {
       let n = node;
       while (n) {
         if (n instanceof this.content.ShadowRoot) {
-          if (n.host instanceof this.content.HTMLMediaElement) {
+          if (n.host instanceof this.content.HTMLMediaElement ||
+              n.host instanceof this.content.HTMLEmbedElement ||
+              n.host instanceof this.content.HTMLObjectElement) {
             node = n.host;
             break;
           }
           break;
         }
         n = n.parentNode;
       }
     }
--- a/browser/actors/PluginChild.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -116,18 +116,22 @@ class PluginChild extends ActorChild {
       return;
     }
 
     this.clearPluginCaches();
     this.haveShownNotification = false;
   }
 
   getPluginUI(plugin, anonid) {
+    if (plugin.openOrClosedShadowRoot &&
+        plugin.openOrClosedShadowRoot.isUAWidget()) {
+      return plugin.openOrClosedShadowRoot.getElementById(anonid);
+    }
     return plugin.ownerDocument.
-           getAnonymousElementByAttribute(plugin, "anonid", anonid);
+      getAnonymousElementByAttribute(plugin, "anonid", anonid);
   }
 
   _getPluginInfo(pluginElement) {
     let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
     pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);
 
     let tagMimetype;
     let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin");
@@ -627,17 +631,18 @@ class PluginChild extends ActorChild {
   }
 
   onOverlayClick(event) {
     let document = event.target.ownerDocument;
     let plugin = document.getBindingParent(event.target);
     let overlay = this.getPluginUI(plugin, "main");
     // Have to check that the target is not the link to update the plugin
     if (!(ChromeUtils.getClassName(event.originalTarget) === "HTMLAnchorElement") &&
-        (event.originalTarget.getAttribute("anonid") != "closeIcon") &&
+        event.originalTarget.getAttribute("anonid") != "closeIcon" &&
+        event.originalTarget.id != "closeIcon" &&
         !overlay.hasAttribute("dismissed") &&
         event.button == 0 &&
         event.isTrusted) {
       this._showClickToPlayNotification(plugin, true);
     event.stopPropagation();
     event.preventDefault();
     }
   }
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/browser.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+prefs = dom.ua_widget.enabled=true
 support-files =
   blocklist_proxy.js
   blockNoPlugins.xml
   blockPluginHard.xml
   blockPluginInfoURL.xml
   blockPluginVulnerableNoUpdate.xml
   blockPluginVulnerableUpdatable.xml
   browser_clearplugindata.html
--- a/browser/base/content/test/plugins/browser_CTP_crashreporting.js
+++ b/browser/base/content/test/plugins/browser_CTP_crashreporting.js
@@ -91,20 +91,18 @@ add_task(async function() {
         return plugin.activated;
       }, "Waited too long for plugin to activate.");
 
       try {
         Cu.waiveXrays(plugin).crash();
       } catch (e) {
       }
 
-      let doc = plugin.ownerDocument;
-
-      let getUI = (anonid) => {
-        return doc.getAnonymousElementByAttribute(plugin, "anonid", anonid);
+      let getUI = (id) => {
+        return plugin.openOrClosedShadowRoot.getElementById(id);
       };
 
       // Now wait until the plugin crash report UI shows itself, which is
       // asynchronous.
       let statusDiv;
 
       await ContentTaskUtils.waitForCondition(() => {
         statusDiv = getUI("submitStatus");
--- a/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
+++ b/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
@@ -28,18 +28,18 @@ add_task(async function() {
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gBrowser.selectedBrowser);
 
   // Tests that the overlay can be hidden for plugins using the close icon.
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
+    let closeIcon = plugin.openOrClosedShadowRoot.getElementById("closeIcon");
     let bounds = closeIcon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.windowUtils;
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
 
     Assert.ok(!overlay.classList.contains("visible"), "overlay should be hidden.");
@@ -54,18 +54,18 @@ add_task(async function() {
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gBrowser.selectedBrowser);
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
-    let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
+    let closeIcon = plugin.openOrClosedShadowRoot.getElementById("closeIcon");
     let closeIconBounds = closeIcon.getBoundingClientRect();
     let overlayBounds = overlay.getBoundingClientRect();
     let overlayLeft = (overlayBounds.left + overlayBounds.right) / 2;
     let overlayTop = (overlayBounds.left + overlayBounds.right) / 2 ;
     let closeIconLeft = (closeIconBounds.left + closeIconBounds.right) / 2;
     let closeIconTop = (closeIconBounds.top + closeIconBounds.bottom) / 2;
     let utils = content.windowUtils;
     // Simulate clicking on the close icon.
--- a/browser/base/content/test/plugins/browser_CTP_iframe.js
+++ b/browser/base/content/test/plugins/browser_CTP_iframe.js
@@ -24,21 +24,21 @@ add_task(async function() {
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_iframe.html");
 
   // Tests that the overlays are visible and actionable if the plugin is in an iframe.
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     let frame = content.document.getElementById("frame");
     let doc = frame.contentDocument;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(plugin && overlay.classList.contains("visible"),
       "Test 1, Plugin overlay should exist, not be hidden");
 
-    let closeIcon = doc.getAnonymousElementByAttribute(plugin, "anonid", "closeIcon");
+    let closeIcon = plugin.openOrClosedShadowRoot.getElementById("closeIcon");
     let bounds = closeIcon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = doc.defaultView.windowUtils;
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
     Assert.ok(!overlay.classList.contains("visible"),
       "Test 1, Plugin overlay should exist, be hidden");
--- a/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
+++ b/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
@@ -50,18 +50,17 @@ add_task(async function() {
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gTestBrowser);
 
   await promisePopupNotification("click-to-play-plugins");
 
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     let plugin = content.document.getElementById("test");
-    let doc = content.document;
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.classList.contains("visible") &&
               overlay.getAttribute("sizing") != "blank",
               "Test 2, overlay should be visible.");
   });
 });
 
 add_task(async function() {
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
@@ -80,18 +79,17 @@ add_task(async function() {
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gTestBrowser);
 
   await promisePopupNotification("click-to-play-plugins");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let plugin = content.document.getElementById("test");
-    let doc = content.document;
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.classList.contains("visible") &&
               overlay.getAttribute("sizing") != "blank",
               "Test 3, overlay should be visible.");
   });
 });
 
 add_task(async function() {
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_outsideScrollArea.html");
@@ -109,14 +107,13 @@ add_task(async function() {
   });
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gTestBrowser);
 
   await promisePopupNotification("click-to-play-plugins");
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let plugin = content.document.getElementById("test");
-    let doc = content.document;
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(!overlay || overlay.getAttribute("sizing") == "blank",
       "Test 4, overlay should be blank.");
   });
 });
--- a/browser/base/content/test/plugins/browser_CTP_overlay_styles.js
+++ b/browser/base/content/test/plugins/browser_CTP_overlay_styles.js
@@ -70,17 +70,17 @@ add_task(async function() {
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gTestBrowser);
 
   await ContentTask.spawn(gTestBrowser, gTestcases, async function(testcases) {
     let doc = content.document;
 
     for (let testcaseId of Object.keys(testcases)) {
       let plugin = doc.querySelector(`#${testcaseId} > object`);
-      let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+      let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
       Assert.ok(overlay, `overlay exists in ${testcaseId}`);
 
       let expectations = testcases[testcaseId];
 
       Assert.ok(overlay.classList.contains("visible"),
                 `The expected visibility is correct in ${testcaseId}`);
 
       Assert.ok(overlay.getAttribute("sizing") == expectations.sizing,
--- a/browser/base/content/test/plugins/browser_CTP_resize.js
+++ b/browser/base/content/test/plugins/browser_CTP_resize.js
@@ -40,17 +40,17 @@ add_task(async function() {
 // once they are resized to a size that can hold the overlay
 add_task(async function() {
   let popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(popupNotification, "Test 2, Should have a click-to-play notification");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(!overlay || overlay.getAttribute("sizing") == "blank",
       "Test 2, overlay should be blank.");
   });
 });
 
 add_task(async function() {
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     let plugin = content.document.getElementById("test");
@@ -58,17 +58,17 @@ add_task(async function() {
   });
 
   // Work around for delayed PluginBindingAttached
   await promiseUpdatePluginBindings(gTestBrowser);
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(!overlay || overlay.getAttribute("sizing") == "blank",
       "Test 3, overlay should be blank.");
   });
 });
 
 
 add_task(async function() {
   await ContentTask.spawn(gTestBrowser, {}, async function() {
@@ -78,17 +78,17 @@ add_task(async function() {
 
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     content.document.getElementById("test").clientTop;
   });
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.getAttribute("sizing") != "blank",
       "Test 4, overlay should be visible.");
   });
 });
 
 add_task(async function() {
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     let plugin = content.document.getElementById("test");
@@ -98,17 +98,17 @@ add_task(async function() {
 
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     content.document.getElementById("test").clientTop;
   });
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(!overlay || overlay.getAttribute("sizing") == "blank",
       "Test 5, overlay should be blank.");
   });
 });
 
 add_task(async function() {
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     let plugin = content.document.getElementById("test");
@@ -118,13 +118,13 @@ add_task(async function() {
 
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     content.document.getElementById("test").clientTop;
   });
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.getAttribute("sizing") != "blank",
       "Test 6, overlay should be visible.");
   });
 });
--- a/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js
+++ b/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js
@@ -46,17 +46,17 @@ add_task(async function() {
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let testcases = doc.querySelectorAll(".testcase");
 
     for (let testcase of testcases) {
       let plugin = testcase.querySelector("object");
       Assert.ok(plugin, `plugin exists in ${testcase.id}`);
 
-      let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+      let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
       Assert.ok(overlay, `overlay exists in ${testcase.id}`);
 
       let expectedVisibility = (testcase.getAttribute("shouldshow") == "true");
       Assert.ok((overlay.getAttribute("sizing") != "blank") == expectedVisibility,
                 `The expected visibility is correct in ${testcase.id}`);
     }
   });
 });
--- a/browser/base/content/test/plugins/browser_CTP_zoom.js
+++ b/browser/base/content/test/plugins/browser_CTP_zoom.js
@@ -47,16 +47,16 @@ add_task(async function() {
     FullZoom.enlarge();
 
     // Reload the page
     await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_zoom.html");
     await promiseUpdatePluginBindings(gTestBrowser);
     await ContentTask.spawn(gTestBrowser, { count }, async function(args) {
       let doc = content.document;
       let plugin = doc.getElementById("test");
-      let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+      let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
       Assert.ok(overlay && overlay.classList.contains("visible"),
         "Overlay should be visible for zoom change count " + args.count);
     });
   }
 });
 
 
--- a/browser/base/content/test/plugins/browser_blocking.js
+++ b/browser/base/content/test/plugins/browser_blocking.js
@@ -50,31 +50,31 @@ add_task(async function() {
   let pluginInfo = await promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
      "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
   ok(!pluginInfo.activated, "Test 18a, Plugin should not be activated");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.classList.contains("visible"),
       "Test 18a, Plugin overlay should exist, not be hidden");
 
-    let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
+    let updateLink = plugin.openOrClosedShadowRoot.getElementById("checkForUpdatesLink");
     Assert.ok(updateLink.style.visibility != "hidden",
       "Test 18a, Plugin should have an update link");
   });
 
   let promise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen", true);
 
   await ContentTask.spawn(gTestBrowser, {}, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
+    let updateLink = plugin.openOrClosedShadowRoot.getElementById("checkForUpdatesLink");
     let bounds = updateLink.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.windowUtils;
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
   });
   await promise;
@@ -89,17 +89,17 @@ add_task(async function() {
   let pluginInfo = await promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE,
      "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE");
   ok(!pluginInfo.activated, "Test 18b, Plugin should not be activated");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.classList.contains("visible"),
       "Test 18b, Plugin overlay should exist, not be hidden");
   });
 });
 
 // Tests a vulnerable plugin with no update
 add_task(async function() {
   updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
@@ -117,21 +117,21 @@ add_task(async function() {
   let pluginInfo = await promiseForPluginInfo("test");
   is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE,
      "Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE");
   ok(!pluginInfo.activated, "Test 18c, Plugin should not be activated");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(overlay && overlay.classList.contains("visible"),
       "Test 18c, Plugin overlay should exist, not be hidden");
 
-    let updateLink = doc.getAnonymousElementByAttribute(plugin, "anonid", "checkForUpdatesLink");
+    let updateLink = plugin.openOrClosedShadowRoot.getElementById("checkForUpdatesLink");
     Assert.ok(updateLink && updateLink.style.display != "block",
       "Test 18c, Plugin should not have an update link");
   });
 
   // check that click "Allow" works with blocked plugins
   await promiseForNotificationShown(notification);
 
   PopupNotifications.panel.firstElementChild.button.click();
--- a/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
+++ b/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
@@ -58,20 +58,20 @@ add_task(async function() {
   await pluginCrashed;
 
   let crashReportStatus = TestUtils.topicObserved("crash-report-status", onSubmitStatus);
 
   // Test that the crash submission UI is actually visible and submit the crash report.
   await ContentTask.spawn(gTestBrowser, config, async function(aConfig) {
     let doc = content.document;
     let plugin = doc.getElementById("plugin");
-    let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
-    let submitButton = doc.getAnonymousElementByAttribute(plugin, "anonid", "submitButton");
+    let pleaseSubmit = plugin.openOrClosedShadowRoot.getElementById("pleaseSubmit");
+    let submitButton = plugin.openOrClosedShadowRoot.getElementById("submitButton");
     // Test that we don't send the URL when urlOptIn is false.
-    doc.getAnonymousElementByAttribute(plugin, "anonid", "submitURLOptIn").checked = aConfig.urlOptIn;
+    plugin.openOrClosedShadowRoot.getElementById("submitURLOptIn").checked = aConfig.urlOptIn;
     submitButton.click();
     Assert.equal(content.getComputedStyle(pleaseSubmit).display == "block",
       aConfig.shouldSubmissionUIBeVisible, "The crash UI should be visible");
   });
 
   await crashReportStatus;
 });
 
@@ -95,21 +95,21 @@ add_task(async function() {
   await pluginCrashed;
 
   let crashReportStatus = TestUtils.topicObserved("crash-report-status", onSubmitStatus);
 
   // Test that the crash submission UI is actually visible and submit the crash report.
   await ContentTask.spawn(gTestBrowser, config, async function(aConfig) {
     let doc = content.document;
     let plugin = doc.getElementById("plugin");
-    let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
-    let submitButton = doc.getAnonymousElementByAttribute(plugin, "anonid", "submitButton");
+    let pleaseSubmit = plugin.openOrClosedShadowRoot.getElementById("pleaseSubmit");
+    let submitButton = plugin.openOrClosedShadowRoot.getElementById("submitButton");
     // Test that we send the URL when urlOptIn is true.
-    doc.getAnonymousElementByAttribute(plugin, "anonid", "submitURLOptIn").checked = aConfig.urlOptIn;
-    doc.getAnonymousElementByAttribute(plugin, "anonid", "submitComment").value = aConfig.comment;
+    plugin.openOrClosedShadowRoot.getElementById("submitURLOptIn").checked = aConfig.urlOptIn;
+    plugin.openOrClosedShadowRoot.getElementById("submitComment").value = aConfig.comment;
     submitButton.click();
     Assert.equal(content.getComputedStyle(pleaseSubmit).display == "block",
       aConfig.shouldSubmissionUIBeVisible, "The crash UI should be visible");
   });
 
   await crashReportStatus;
 });
 
@@ -166,17 +166,17 @@ add_task(async function() {
 
   // Wait for the plugin to crash
   await pluginCrashed;
 
   // Test that the crash submission UI is not visible and do not submit a crash report.
   await ContentTask.spawn(gTestBrowser, config, async function(aConfig) {
     let doc = content.document;
     let plugin = doc.getElementById("plugin");
-    let pleaseSubmit = doc.getAnonymousElementByAttribute(plugin, "anonid", "pleaseSubmit");
+    let pleaseSubmit = plugin.openOrClosedShadowRoot.getElementById("pleaseSubmit");
     Assert.equal(!!pleaseSubmit && content.getComputedStyle(pleaseSubmit).display == "block",
       aConfig.shouldSubmissionUIBeVisible, "Plugin crash UI should not be visible");
   });
 
   await crashDeferred.promise;
   Services.obs.removeObserver(crashObserver, "plugin-crashed");
 });
 
--- a/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
+++ b/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
@@ -54,19 +54,18 @@ function preparePlugin(browser, pluginFa
   return ContentTask.spawn(browser, pluginFallbackState, async function(contentPluginFallbackState) {
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     // CRASH_URL will load a plugin that crashes immediately. We
     // wait until the plugin has finished being put into the crash
     // state.
     let statusDiv;
     await ContentTaskUtils.waitForCondition(() => {
-      statusDiv = plugin.ownerDocument
-                        .getAnonymousElementByAttribute(plugin, "anonid",
-                                                        "submitStatus");
+      statusDiv = plugin.openOrClosedShadowRoot.getElementById("submitStatus");
+
       return statusDiv && statusDiv.getAttribute("status") == "please";
     }, "Timed out waiting for plugin to be in crash report state");
 
     // "Rewind", by wiping out the status attribute...
     statusDiv.removeAttribute("status");
     // Somehow, I'm able to get away with overriding the getter for
     // this XPCOM object. Probably because I've got chrome privledges.
     Object.defineProperty(plugin, "pluginFallbackType", {
@@ -153,19 +152,17 @@ add_task(async function testChromeHearsP
                       { pluginName: "", runID, state: "please" });
 
   await ContentTask.spawn(browser, null, async function() {
     // At this point, the content process should have heard the
     // plugin crash message from the parent, and we are OK to emit
     // the PluginCrashed event.
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    let statusDiv = plugin.ownerDocument
-                          .getAnonymousElementByAttribute(plugin, "anonid",
-                                                          "submitStatus");
+    let statusDiv = plugin.openOrClosedShadowRoot.getElementById("submitStatus");
 
     if (statusDiv.getAttribute("status") == "please") {
       Assert.ok(false, "Did not expect plugin to be in crash report mode yet.");
       return;
     }
 
     // Now we need the plugin to seem crashed to PluginContent.jsm, without
     // actually crashing the plugin again. We hack around this by overriding
@@ -215,19 +212,17 @@ add_task(async function testContentHears
                                   Ci.nsIObjectLoadingContent.PLUGIN_CRASHED);
 
   await ContentTask.spawn(browser, null, async function() {
     // At this point, the content process has not yet heard from the
     // parent about the crash report. Let's ensure that by making sure
     // we're not showing the plugin crash report UI.
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    let statusDiv = plugin.ownerDocument
-                          .getAnonymousElementByAttribute(plugin, "anonid",
-                                                          "submitStatus");
+    let statusDiv = plugin.openOrClosedShadowRoot.getElementById("submitStatus");
 
     if (statusDiv.getAttribute("status") == "please") {
       Assert.ok(false, "Did not expect plugin to be in crash report mode yet.");
     }
 
     let event = new content.PluginCrashedEvent("PluginCrashed", {
       pluginName: "",
       pluginDumpID: "",
@@ -250,19 +245,17 @@ add_task(async function testContentHears
                       { pluginName: "", runID, state: "please"});
 
   await ContentTask.spawn(browser, null, async function() {
     // At this point, the content process will have heard the message
     // from the parent and reacted to it. We should be showing the plugin
     // crash report UI now.
     let plugin = content.document.getElementById("plugin");
     plugin.QueryInterface(Ci.nsIObjectLoadingContent);
-    let statusDiv = plugin.ownerDocument
-                          .getAnonymousElementByAttribute(plugin, "anonid",
-                                                          "submitStatus");
+    let statusDiv = plugin.openOrClosedShadowRoot.getElementById("submitStatus");
 
     Assert.equal(statusDiv.getAttribute("status"), "please",
       "Should have been showing crash report UI");
   });
 
   await BrowserTestUtils.closeWindow(win);
   await crashDeferred.promise;
 });
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/browser_pluginnotification.js
@@ -85,17 +85,17 @@ add_task(async function() {
   gTestBrowser.reload();
   await browserLoaded;
   notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
 
   // Simulate clicking the overlay
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let bounds = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
+    let bounds = plugin.openOrClosedShadowRoot.getElementById("main").getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.windowUtils;
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
   });
 
   ok(!notification.dismissed, "A plugin notification should be shown.");
@@ -190,17 +190,17 @@ add_task(async function() {
 add_task(async function() {
   updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_alternate_content.html");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let mainBox = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(!!mainBox, "Test 15, Plugin overlay should exist");
   });
 });
 
 // Tests that mContentType is used for click-to-play plugins, and not the
 // inspected type.
 add_task(async function() {
   await promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_bug749455.html");
@@ -225,17 +225,17 @@ add_task(async function() {
   ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed,
      "Test 19a, Doorhanger should start out dismissed");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let icon = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
+    let icon = plugin.openOrClosedShadowRoot.getElementById("icon");
     let bounds = icon.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.windowUtils;
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
   });
 
@@ -254,17 +254,17 @@ add_task(async function() {
   ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated");
 
   ok(PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser).dismissed,
      "Test 19c, Doorhanger should start out dismissed");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let text = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgClickToPlay");
+    let text = plugin.openOrClosedShadowRoot.getElementById("clickToPlay");
     let bounds = text.getBoundingClientRect();
     let left = (bounds.left + bounds.right) / 2;
     let top = (bounds.top + bounds.bottom) / 2;
     let utils = content.windowUtils;
     utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
     utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
   });
 
@@ -314,24 +314,24 @@ add_task(async function() {
   await promiseUpdatePluginBindings(gTestBrowser);
 
   let notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(notification, "Test 20a, Should have a click-to-play notification");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let overlay = plugin.openOrClosedShadowRoot.getElementById("main");
     Assert.ok(!!overlay, "Test 20a, Plugin overlay should exist");
   });
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let mainBox = plugin.openOrClosedShadowRoot.getElementById("main");
     let overlayRect = mainBox.getBoundingClientRect();
     Assert.ok(overlayRect.width == 0 && overlayRect.height == 0,
       "Test 20a, plugin should have an overlay with 0px width and height");
   });
 
   let pluginInfo = await promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 20b, plugin should not be activated");
 
@@ -346,17 +346,17 @@ add_task(async function() {
     let doc = content.document;
     let div = doc.getElementById("container");
     div.style.display = "block";
   });
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let mainBox = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
+    let mainBox = plugin.openOrClosedShadowRoot.getElementById("main");
     let overlayRect = mainBox.getBoundingClientRect();
     Assert.ok(overlayRect.width == 200 && overlayRect.height == 200,
       "Test 20c, plugin should have overlay dims of 200px");
   });
 
   pluginInfo = await promiseForPluginInfo("test");
   ok(!pluginInfo.activated, "Test 20b, plugin should not be activated");
 
@@ -378,19 +378,18 @@ add_task(async function() {
   PopupNotifications.panel.firstElementChild.button.click();
 
   pluginInfo = await promiseForPluginInfo("test");
   ok(pluginInfo.activated, "Test 20c, plugin should be activated");
 
   await ContentTask.spawn(gTestBrowser, null, async function() {
     let doc = content.document;
     let plugin = doc.getElementById("test");
-    let overlayRect = doc.getAnonymousElementByAttribute(plugin, "anonid", "main").getBoundingClientRect();
-    Assert.ok(overlayRect.width == 0 && overlayRect.height == 0,
-      "Test 20c, plugin should have overlay dims of 0px");
+    Assert.ok(!plugin.openOrClosedShadowRoot,
+      "Test 20c, CTP UA Widget Shadow Root is removed");
   });
 
   clearAllPluginPermissions();
 });
 
 // Tests that a click-to-play plugin resets its activated state when changing types
 add_task(async function() {
   clearAllPluginPermissions();
--- a/browser/base/content/test/plugins/browser_private_clicktoplay.js
+++ b/browser/base/content/test/plugins/browser_private_clicktoplay.js
@@ -195,23 +195,22 @@ add_task(async function test3d() {
   let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
   ok(popupNotification, "Test 3d, Should have a click-to-play notification");
 
   // Check the list item status
   let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
                                                    "Shown");
   popupNotification.reshow();
   await promiseShown;
-  let doc = gPrivateWindow.document;
   for (let item of gPrivateWindow.PopupNotifications.panel.firstElementChild.children) {
-    let allowalways = doc.getAnonymousElementByAttribute(item, "anonid", "allowalways");
+    let allowalways = item.openOrClosedShadowRoot.getElementById("allowalways");
     ok(allowalways, "Test 3d, should have list item for allow always");
-    let allownow = doc.getAnonymousElementByAttribute(item, "anonid", "allownow");
+    let allownow = item.openOrClosedShadowRoot.getElementById("allownow");
     ok(allownow, "Test 3d, should have list item for allow now");
-    let block = doc.getAnonymousElementByAttribute(item, "anonid", "block");
+    let block = item.openOrClosedShadowRoot.getElementById("block");
     ok(block, "Test 3d, should have list item for block");
 
     if (item.action.pluginName === "Test") {
       is(item.value, "allowalways", "Test 3d, Plugin should bet set to 'allow always'");
       ok(!allowalways.hidden, "Test 3d, Plugin set to 'always allow' should have a visible 'always allow' action.");
       ok(allownow.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'allow now' action.");
       ok(block.hidden, "Test 3d, Plugin set to 'always allow' should have an invisible 'block' action.");
     } else if (item.action.pluginName === "Second Test") {
copy from browser/base/content/test/plugins/.eslintrc.js
copy to browser/base/content/test/plugins/xbl/.eslintrc.js
copy from browser/base/content/test/plugins/blockNoPlugins.xml
copy to browser/base/content/test/plugins/xbl/blockNoPlugins.xml
copy from browser/base/content/test/plugins/blockPluginHard.xml
copy to browser/base/content/test/plugins/xbl/blockPluginHard.xml
copy from browser/base/content/test/plugins/blockPluginInfoURL.xml
copy to browser/base/content/test/plugins/xbl/blockPluginInfoURL.xml
copy from browser/base/content/test/plugins/blockPluginVulnerableNoUpdate.xml
copy to browser/base/content/test/plugins/xbl/blockPluginVulnerableNoUpdate.xml
copy from browser/base/content/test/plugins/blockPluginVulnerableUpdatable.xml
copy to browser/base/content/test/plugins/xbl/blockPluginVulnerableUpdatable.xml
copy from browser/base/content/test/plugins/blocklist_proxy.js
copy to browser/base/content/test/plugins/xbl/blocklist_proxy.js
copy from browser/base/content/test/plugins/browser.ini
copy to browser/base/content/test/plugins/xbl/browser.ini
--- a/browser/base/content/test/plugins/browser.ini
+++ b/browser/base/content/test/plugins/xbl/browser.ini
@@ -1,20 +1,21 @@
 [DEFAULT]
+prefs = dom.ua_widget.enabled=false
 support-files =
   blocklist_proxy.js
   blockNoPlugins.xml
   blockPluginHard.xml
   blockPluginInfoURL.xml
   blockPluginVulnerableNoUpdate.xml
   blockPluginVulnerableUpdatable.xml
   browser_clearplugindata.html
   browser_clearplugindata_noage.html
   empty_file.html
-  head.js
+  ../head.js
   plugin_add_dynamically.html
   plugin_alternate_content.html
   plugin_big.html
   plugin_both.html
   plugin_both2.html
   plugin_bug744745.html
   plugin_bug749455.html
   plugin_bug787619.html
copy from browser/base/content/test/plugins/browser_CTP_context_menu.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_context_menu.js
--- a/browser/base/content/test/plugins/browser_CTP_context_menu.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_context_menu.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
copy from browser/base/content/test/plugins/browser_CTP_crashreporting.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_crashreporting.js
--- a/browser/base/content/test/plugins/browser_CTP_crashreporting.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_crashreporting.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
 const PLUGIN_PAGE = gTestRoot + "plugin_big.html";
 const PLUGIN_SMALL_PAGE = gTestRoot + "plugin_small.html";
 
 /**
  * Takes an nsIPropertyBag and converts it into a JavaScript Object. It
copy from browser/base/content/test/plugins/browser_CTP_drag_drop.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_drag_drop.js
--- a/browser/base/content/test/plugins/browser_CTP_drag_drop.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_drag_drop.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gNewWindow = null;
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
copy from browser/base/content/test/plugins/browser_CTP_favorfallback.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_favorfallback.js
--- a/browser/base/content/test/plugins/browser_CTP_favorfallback.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_favorfallback.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Shockwave Flash");
copy from browser/base/content/test/plugins/browser_CTP_hide_overlay.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_hide_overlay.js
--- a/browser/base/content/test/plugins/browser_CTP_hide_overlay.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_hide_overlay.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
copy from browser/base/content/test/plugins/browser_CTP_iframe.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_iframe.js
--- a/browser/base/content/test/plugins/browser_CTP_iframe.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_iframe.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
copy from browser/base/content/test/plugins/browser_CTP_nonplugins.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_nonplugins.js
--- a/browser/base/content/test/plugins/browser_CTP_nonplugins.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_nonplugins.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
copy from browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_outsideScrollArea.js
--- a/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_outsideScrollArea.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
copy from browser/base/content/test/plugins/browser_CTP_overlay_styles.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_overlay_styles.js
--- a/browser/base/content/test/plugins/browser_CTP_overlay_styles.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_overlay_styles.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* This test ensures that the click-to-play "Activate Plugin" overlay
  * is shown in the right style (which is dependent on its size).
copy from browser/base/content/test/plugins/browser_CTP_resize.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_resize.js
--- a/browser/base/content/test/plugins/browser_CTP_resize.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_resize.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
copy from browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_shouldShowOverlay.js
--- a/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_shouldShowOverlay.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* This test ensures that the click-to-play "Activate Plugin" overlay
  * is shown when expected.
copy from browser/base/content/test/plugins/browser_CTP_zoom.js
copy to browser/base/content/test/plugins/xbl/browser_CTP_zoom.js
--- a/browser/base/content/test/plugins/browser_CTP_zoom.js
+++ b/browser/base/content/test/plugins/xbl/browser_CTP_zoom.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 "use strict";
 
 var rootDir = getRootDirectory(gTestPath);
 const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 
 var gTestBrowser = null;
 
 add_task(async function() {
copy from browser/base/content/test/plugins/browser_blocking.js
copy to browser/base/content/test/plugins/xbl/browser_blocking.js
--- a/browser/base/content/test/plugins/browser_blocking.js
+++ b/browser/base/content/test/plugins/xbl/browser_blocking.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
 function updateAllTestPlugins(aState) {
   setTestPluginEnabledState(aState, "Test Plug-in");
   setTestPluginEnabledState(aState, "Second Test Plug-in");
 }
copy from browser/base/content/test/plugins/browser_blocklist_content.js
copy to browser/base/content/test/plugins/xbl/browser_blocklist_content.js
--- a/browser/base/content/test/plugins/browser_blocklist_content.js
+++ b/browser/base/content/test/plugins/xbl/browser_blocklist_content.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestBrowser = null;
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gChromeRoot = getRootDirectory(gTestPath);
 
 add_task(async function() {
   registerCleanupFunction(async function() {
     clearAllPluginPermissions();
     Services.prefs.clearUserPref("extensions.blocklist.suppressUI");
copy from browser/base/content/test/plugins/browser_bug743421.js
copy to browser/base/content/test/plugins/xbl/browser_bug743421.js
--- a/browser/base/content/test/plugins/browser_bug743421.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug743421.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 
 add_task(async function() {
   registerCleanupFunction(async function() {
     clearAllPluginPermissions();
     Services.prefs.clearUserPref("plugins.click_to_play");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
copy from browser/base/content/test/plugins/browser_bug744745.js
copy to browser/base/content/test/plugins/xbl/browser_bug744745.js
--- a/browser/base/content/test/plugins/browser_bug744745.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug744745.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var gNumPluginBindingsAttached = 0;
 
 function pluginBindingAttached() {
   gNumPluginBindingsAttached++;
   if (gNumPluginBindingsAttached != 1) {
     ok(false, "if we've gotten here, something is quite wrong");
copy from browser/base/content/test/plugins/browser_bug787619.js
copy to browser/base/content/test/plugins/xbl/browser_bug787619.js
--- a/browser/base/content/test/plugins/browser_bug787619.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug787619.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var gWrapperClickCount = 0;
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     Services.prefs.clearUserPref("plugins.click_to_play");
copy from browser/base/content/test/plugins/browser_bug797677.js
copy to browser/base/content/test/plugins/xbl/browser_bug797677.js
--- a/browser/base/content/test/plugins/browser_bug797677.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug797677.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var gConsoleErrors = 0;
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
copy from browser/base/content/test/plugins/browser_bug812562.js
copy to browser/base/content/test/plugins/xbl/browser_bug812562.js
--- a/browser/base/content/test/plugins/browser_bug812562.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug812562.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 
 add_task(async function() {
   registerCleanupFunction(async function() {
     clearAllPluginPermissions();
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
copy from browser/base/content/test/plugins/browser_bug818118.js
copy to browser/base/content/test/plugins/xbl/browser_bug818118.js
--- a/browser/base/content/test/plugins/browser_bug818118.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug818118.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     Services.prefs.clearUserPref("plugins.click_to_play");
     setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
copy from browser/base/content/test/plugins/browser_bug820497.js
copy to browser/base/content/test/plugins/xbl/browser_bug820497.js
--- a/browser/base/content/test/plugins/browser_bug820497.js
+++ b/browser/base/content/test/plugins/xbl/browser_bug820497.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var gNumPluginBindingsAttached = 0;
 
 add_task(async function() {
   registerCleanupFunction(function() {
     clearAllPluginPermissions();
     Services.prefs.clearUserPref("plugins.click_to_play");
copy from browser/base/content/test/plugins/browser_clearplugindata.html
copy to browser/base/content/test/plugins/xbl/browser_clearplugindata.html
copy from browser/base/content/test/plugins/browser_clearplugindata.js
copy to browser/base/content/test/plugins/xbl/browser_clearplugindata.js
--- a/browser/base/content/test/plugins/browser_clearplugindata.js
+++ b/browser/base/content/test/plugins/xbl/browser_clearplugindata.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 var gTestBrowser = null;
 
 // Test clearing plugin data using Sanitizer.jsm.
 const testURL1 = gTestRoot + "browser_clearplugindata.html";
 const testURL2 = gTestRoot + "browser_clearplugindata_noage.html";
 
copy from browser/base/content/test/plugins/browser_clearplugindata_noage.html
copy to browser/base/content/test/plugins/xbl/browser_clearplugindata_noage.html
copy from browser/base/content/test/plugins/browser_enable_DRM_prompt.js
copy to browser/base/content/test/plugins/xbl/browser_enable_DRM_prompt.js
copy from browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
copy to browser/base/content/test/plugins/xbl/browser_globalplugin_crashinfobar.js
--- a/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
+++ b/browser/base/content/test/plugins/xbl/browser_globalplugin_crashinfobar.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 /**
  * Test that the notification bar for crashed GMPs works.
  */
 add_task(async function() {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: "about:blank",
   }, async function(browser) {
copy from browser/base/content/test/plugins/browser_iterate_hidden_plugins.js
copy to browser/base/content/test/plugins/xbl/browser_iterate_hidden_plugins.js
--- a/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js
+++ b/browser/base/content/test/plugins/xbl/browser_iterate_hidden_plugins.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 "use strict";
 
 const TEST_PLUGIN_NAME = "Test Plug-in";
 const HIDDEN_CTP_PLUGIN_PREF = "plugins.navigator.hidden_ctp_plugin";
 
 /**
  * If a plugin is click-to-play and named in HIDDEN_CTP_PLUGIN_PREF,
  * then the plugin should be hidden in the navigator.plugins list by default
copy from browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
copy to browser/base/content/test/plugins/xbl/browser_pluginCrashCommentAndURL.js
--- a/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js
+++ b/browser/base/content/test/plugins/xbl/browser_pluginCrashCommentAndURL.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 /* global gBrowser */
 ChromeUtils.import("resource://gre/modules/CrashSubmit.jsm", this);
 
 const SERVER_URL = "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs";
 
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gTestBrowser = null;
 var config = {};
copy from browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
copy to browser/base/content/test/plugins/xbl/browser_pluginCrashReportNonDeterminism.js
--- a/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js
+++ b/browser/base/content/test/plugins/xbl/browser_pluginCrashReportNonDeterminism.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 
 /**
  * With e10s, plugins must run in their own process. This means we have
  * three processes at a minimum when we're running a plugin:
copy from browser/base/content/test/plugins/browser_plugin_reloading.js
copy to browser/base/content/test/plugins/xbl/browser_plugin_reloading.js
--- a/browser/base/content/test/plugins/browser_plugin_reloading.js
+++ b/browser/base/content/test/plugins/xbl/browser_plugin_reloading.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 var gTestBrowser = null;
 
 function updateAllTestPlugins(aState) {
   setTestPluginEnabledState(aState, "Test Plug-in");
   setTestPluginEnabledState(aState, "Second Test Plug-in");
 }
copy from browser/base/content/test/plugins/browser_pluginnotification.js
copy to browser/base/content/test/plugins/xbl/browser_pluginnotification.js
--- a/browser/base/content/test/plugins/browser_pluginnotification.js
+++ b/browser/base/content/test/plugins/xbl/browser_pluginnotification.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
 var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 var gTestBrowser = null;
 
 function updateAllTestPlugins(aState) {
   setTestPluginEnabledState(aState, "Test Plug-in");
   setTestPluginEnabledState(aState, "Second Test Plug-in");
 }
copy from browser/base/content/test/plugins/browser_private_browsing_eme_persistent_state.js
copy to browser/base/content/test/plugins/xbl/browser_private_browsing_eme_persistent_state.js
copy from browser/base/content/test/plugins/browser_private_clicktoplay.js
copy to browser/base/content/test/plugins/xbl/browser_private_clicktoplay.js
--- a/browser/base/content/test/plugins/browser_private_clicktoplay.js
+++ b/browser/base/content/test/plugins/xbl/browser_private_clicktoplay.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 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 = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
copy from browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js
copy to browser/base/content/test/plugins/xbl/browser_subframe_access_hidden_plugins.js
--- a/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js
+++ b/browser/base/content/test/plugins/xbl/browser_subframe_access_hidden_plugins.js
@@ -1,8 +1,10 @@
+/* import-globals-from ../head.js */
+
 "use strict";
 
 const TEST_PLUGIN_NAME = "Test Plug-in";
 const HIDDEN_CTP_PLUGIN_PREF = "plugins.navigator.hidden_ctp_plugin";
 const DOMAIN_1 = "http://example.com";
 const DOMAIN_2 = "http://mochi.test:8888";
 
 /**
copy from browser/base/content/test/plugins/empty_file.html
copy to browser/base/content/test/plugins/xbl/empty_file.html
copy from browser/base/content/test/plugins/plugin_add_dynamically.html
copy to browser/base/content/test/plugins/xbl/plugin_add_dynamically.html
copy from browser/base/content/test/plugins/plugin_alternate_content.html
copy to browser/base/content/test/plugins/xbl/plugin_alternate_content.html
copy from browser/base/content/test/plugins/plugin_big.html
copy to browser/base/content/test/plugins/xbl/plugin_big.html
copy from browser/base/content/test/plugins/plugin_both.html
copy to browser/base/content/test/plugins/xbl/plugin_both.html
copy from browser/base/content/test/plugins/plugin_both2.html
copy to browser/base/content/test/plugins/xbl/plugin_both2.html
copy from browser/base/content/test/plugins/plugin_bug744745.html
copy to browser/base/content/test/plugins/xbl/plugin_bug744745.html
copy from browser/base/content/test/plugins/plugin_bug749455.html
copy to browser/base/content/test/plugins/xbl/plugin_bug749455.html
copy from browser/base/content/test/plugins/plugin_bug787619.html
copy to browser/base/content/test/plugins/xbl/plugin_bug787619.html
copy from browser/base/content/test/plugins/plugin_bug797677.html
copy to browser/base/content/test/plugins/xbl/plugin_bug797677.html
copy from browser/base/content/test/plugins/plugin_bug820497.html
copy to browser/base/content/test/plugins/xbl/plugin_bug820497.html
copy from browser/base/content/test/plugins/plugin_clickToPlayAllow.html
copy to browser/base/content/test/plugins/xbl/plugin_clickToPlayAllow.html
copy from browser/base/content/test/plugins/plugin_clickToPlayDeny.html
copy to browser/base/content/test/plugins/xbl/plugin_clickToPlayDeny.html
copy from browser/base/content/test/plugins/plugin_crashCommentAndURL.html
copy to browser/base/content/test/plugins/xbl/plugin_crashCommentAndURL.html
copy from browser/base/content/test/plugins/plugin_favorfallback.html
copy to browser/base/content/test/plugins/xbl/plugin_favorfallback.html
copy from browser/base/content/test/plugins/plugin_hidden_to_visible.html
copy to browser/base/content/test/plugins/xbl/plugin_hidden_to_visible.html
copy from browser/base/content/test/plugins/plugin_iframe.html
copy to browser/base/content/test/plugins/xbl/plugin_iframe.html
copy from browser/base/content/test/plugins/plugin_outsideScrollArea.html
copy to browser/base/content/test/plugins/xbl/plugin_outsideScrollArea.html
copy from browser/base/content/test/plugins/plugin_overlay_styles.html
copy to browser/base/content/test/plugins/xbl/plugin_overlay_styles.html
copy from browser/base/content/test/plugins/plugin_shouldShowOverlay.html
copy to browser/base/content/test/plugins/xbl/plugin_shouldShowOverlay.html
copy from browser/base/content/test/plugins/plugin_simple_blank.swf
copy to browser/base/content/test/plugins/xbl/plugin_simple_blank.swf
copy from browser/base/content/test/plugins/plugin_small.html
copy to browser/base/content/test/plugins/xbl/plugin_small.html
copy from browser/base/content/test/plugins/plugin_small_2.html
copy to browser/base/content/test/plugins/xbl/plugin_small_2.html
copy from browser/base/content/test/plugins/plugin_syncRemoved.html
copy to browser/base/content/test/plugins/xbl/plugin_syncRemoved.html
copy from browser/base/content/test/plugins/plugin_test.html
copy to browser/base/content/test/plugins/xbl/plugin_test.html
copy from browser/base/content/test/plugins/plugin_test2.html
copy to browser/base/content/test/plugins/xbl/plugin_test2.html
copy from browser/base/content/test/plugins/plugin_test3.html
copy to browser/base/content/test/plugins/xbl/plugin_test3.html
copy from browser/base/content/test/plugins/plugin_two_types.html
copy to browser/base/content/test/plugins/xbl/plugin_two_types.html
copy from browser/base/content/test/plugins/plugin_unknown.html
copy to browser/base/content/test/plugins/xbl/plugin_unknown.html
copy from browser/base/content/test/plugins/plugin_zoom.html
copy to browser/base/content/test/plugins/xbl/plugin_zoom.html
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -32,16 +32,17 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/historySwipeAnimation/browser.ini',
     'content/test/metaTags/browser.ini',
     'content/test/pageinfo/browser.ini',
     'content/test/performance/browser.ini',
     'content/test/performance/hidpi/browser.ini',
     'content/test/performance/lowdpi/browser.ini',
     'content/test/permissions/browser.ini',
     'content/test/plugins/browser.ini',
+    'content/test/plugins/xbl/browser.ini',
     'content/test/popupNotifications/browser.ini',
     'content/test/popups/browser.ini',
     'content/test/referrer/browser.ini',
     'content/test/sanitize/browser.ini',
     'content/test/sidebar/browser.ini',
     'content/test/siteIdentity/browser.ini',
     'content/test/static/browser.ini',
     'content/test/statuspanel/browser.ini',
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -600,16 +600,17 @@ nsObjectLoadingContent::BindToTree(nsIDo
                                    nsIContent* aParent,
                                    nsIContent* aBindingParent)
 {
   nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent);
 
   if (aDocument) {
     aDocument->AddPlugin(this);
   }
+
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
 
@@ -629,16 +630,31 @@ nsObjectLoadingContent::UnbindFromTree(b
     QueueCheckPluginStopEvent();
   } else if (mType != eType_Image) {
     // nsImageLoadingContent handles the image case.
     // Reset state and clear pending events
     /// XXX(johns): The implementation for GenericFrame notes that ideally we
     ///             would keep the docshell around, but trash the frameloader
     UnloadObject();
   }
+
+  // Unattach plugin problem UIWidget if any.
+  if (thisElement->IsInComposedDoc() && thisElement->GetShadowRoot()) {
+    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
+      "nsObjectLoadingContent::UnbindFromTree::UAWidgetUnbindFromTree",
+      [thisElement]() {
+        nsContentUtils::DispatchChromeEvent(
+          thisElement->OwnerDoc(), thisElement,
+          NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
+          CanBubble::eYes, Cancelable::eNo);
+        thisElement->UnattachShadow();
+      })
+    );
+  }
+
   if (mType == eType_Plugin) {
     nsIDocument* doc = thisElement->GetComposedDoc();
     if (doc && doc->IsActive()) {
       nsCOMPtr<nsIRunnable> ev = new nsSimplePluginEvent(doc,
                                                          NS_LITERAL_STRING("PluginRemoved"));
       NS_DispatchToCurrentThread(ev);
     }
   }
@@ -2627,64 +2643,97 @@ nsObjectLoadingContent::NotifyStateChang
                                            EventStates aOldState,
                                            bool aSync,
                                            bool aNotify)
 {
   LOG(("OBJLC [%p]: Notifying about state change: (%u, %" PRIx64 ") -> (%u, %" PRIx64 ")"
        " (sync %i, notify %i)", this, aOldType, aOldState.GetInternalValue(),
        mType, ObjectState().GetInternalValue(), aSync, aNotify));
 
-  nsCOMPtr<nsIContent> thisContent =
+  nsCOMPtr<dom::Element> thisEl =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  NS_ASSERTION(thisContent, "must be a content");
-
-  NS_ASSERTION(thisContent->IsElement(), "Not an element?");
+  MOZ_ASSERT(thisEl, "must be an element");
 
   // XXX(johns): A good bit of the code below replicates UpdateState(true)
 
   // Unfortunately, we do some state changes without notifying
   // (e.g. in Fallback when canceling image requests), so we have to
   // manually notify object state changes.
-  thisContent->AsElement()->UpdateState(false);
+  thisEl->UpdateState(false);
 
   if (!aNotify) {
     // We're done here
     return;
   }
 
-  nsIDocument* doc = thisContent->GetComposedDoc();
+  nsIDocument* doc = thisEl->GetComposedDoc();
   if (!doc) {
     return; // Nothing to do
   }
 
   EventStates newState = ObjectState();
 
   if (newState == aOldState && mType == aOldType) {
     return; // Also done.
   }
 
   if (newState != aOldState) {
-    NS_ASSERTION(thisContent->IsInComposedDoc(), "Something is confused");
+    MOZ_ASSERT(thisEl->IsInComposedDoc(), "Something is confused");
     // This will trigger frame construction
     EventStates changedBits = aOldState ^ newState;
     {
       nsAutoScriptBlocker scriptBlocker;
-      doc->ContentStateChanged(thisContent, changedBits);
+      doc->ContentStateChanged(thisEl, changedBits);
+    }
+
+    // Create/destroy plugin problem UAWidget if needed.
+    if (nsContentUtils::IsUAWidgetEnabled()) {
+      const EventStates pluginProblemState =
+        NS_EVENT_STATE_HANDLER_BLOCKED |
+        NS_EVENT_STATE_HANDLER_CRASHED |
+        NS_EVENT_STATE_TYPE_CLICK_TO_PLAY |
+        NS_EVENT_STATE_VULNERABLE_UPDATABLE |
+        NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
+
+      bool hadProblemState = !(aOldState & pluginProblemState).IsEmpty();
+      bool hasProblemState = !(newState & pluginProblemState).IsEmpty();
+
+      if (hadProblemState && !hasProblemState) {
+        nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
+          "nsObjectLoadingContent::UnbindFromTree::UAWidgetUnbindFromTree",
+          [thisEl]() {
+            nsContentUtils::DispatchChromeEvent(
+              thisEl->OwnerDoc(), thisEl,
+              NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
+              CanBubble::eYes, Cancelable::eNo);
+            thisEl->UnattachShadow();
+          })
+        );
+      } else if (!hadProblemState && hasProblemState) {
+        nsGenericHTMLElement::FromNode(thisEl)->AttachAndSetUAShadowRoot();
+
+        AsyncEventDispatcher* dispatcher =
+          new AsyncEventDispatcher(thisEl,
+                                   NS_LITERAL_STRING("UAWidgetBindToTree"),
+                                   CanBubble::eYes,
+                                   ChromeOnlyDispatch::eYes);
+        dispatcher->RunDOMEventWhenSafe();
+      }
     }
   } else if (aOldType != mType) {
     // If our state changed, then we already recreated frames
     // Otherwise, need to do that here
     nsCOMPtr<nsIPresShell> shell = doc->GetShell();
     if (shell) {
-      shell->PostRecreateFramesFor(thisContent->AsElement());
+      shell->PostRecreateFramesFor(thisEl);
     }
   }
 
   if (aSync) {
-    NS_ASSERTION(InActiveDocument(thisContent), "Something is confused");
+    MOZ_ASSERT(InActiveDocument(thisEl), "Something is confused");
     // Make sure that frames are actually constructed immediately.
     doc->FlushPendingNotifications(FlushType::Frames);
   }
 }
 
 nsObjectLoadingContent::ObjectType
 nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType,
                                          bool aNoFakePlugin)
--- a/dom/plugins/test/mochitest/test_crash_submit.xul
+++ b/dom/plugins/test/mochitest/test_crash_submit.xul
@@ -112,17 +112,18 @@ var testObserver = {
 
 
 function onPluginCrashed(aEvent) {
   ok(true, "Plugin crashed notification received");
   is(aEvent.type, "PluginCrashed", "event is correct type");
 
   let submitButton = document.getAnonymousElementByAttribute(aEvent.target,
                                                              "class",
-                                                             "submitButton");
+                                                             "submitButton") ||
+                     aEvent.target.openOrClosedShadowRoot.getElementById("submitButton");
   // try to submit this report
   sendMouseEvent({type:'click'}, submitButton, window);
 }
 
 function runTests() {
   // the test harness will have set MOZ_CRASHREPORTER_NO_REPORT,
   // ensure that we can change the setting and have our minidumps
   // wind up in Crash Reports/pending
--- a/dom/plugins/test/mochitest/test_hang_submit.xul
+++ b/dom/plugins/test/mochitest/test_hang_submit.xul
@@ -116,17 +116,18 @@ var testObserver = {
 };
 
 function onPluginCrashed(aEvent) {
   ok(true, "Plugin crashed notification received");
   is(aEvent.type, "PluginCrashed", "event is correct type");
 
   let submitButton = document.getAnonymousElementByAttribute(aEvent.target,
                                                              "class",
-                                                             "submitButton");
+                                                             "submitButton") ||
+                     aEvent.target.openOrClosedShadowRoot.getElementById("submitButton");
   // try to submit this report
   sendMouseEvent({type:'click'}, submitButton, window);
 }
 
 async function runTests() {
   // Default plugin hang timeout is too high for mochitests
   Services.prefs.setIntPref("dom.ipc.plugins.timeoutSecs", 1);
 
--- a/toolkit/actors/UAWidgetsChild.jsm
+++ b/toolkit/actors/UAWidgetsChild.jsm
@@ -51,20 +51,20 @@ class UAWidgetsChild extends ActorChild 
       case "audio":
         uri = "chrome://global/content/elements/videocontrols.js";
         widgetName = "VideoControlsPageWidget";
         break;
       case "input":
         uri = "chrome://global/content/elements/datetimebox.js";
         widgetName = "DateTimeBoxWidget";
         break;
-      case "applet":
       case "embed":
       case "object":
-        // TODO (pluginProblems)
+        uri = "chrome://global/content/elements/pluginProblem.js";
+        widgetName = "PluginProblemWidget";
         break;
       case "marquee":
         uri = "chrome://global/content/elements/marquee.js";
         widgetName = "MarqueeWidget";
         break;
     }
 
     if (!uri || !widgetName) {
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -92,16 +92,17 @@ toolkit.jar:
    content/global/bindings/tree.xml            (widgets/tree.xml)
    content/global/bindings/videocontrols.xml   (widgets/videocontrols.xml)
 *  content/global/bindings/wizard.xml          (widgets/wizard.xml)
    content/global/elements/datetimebox.js      (widgets/datetimebox.js)
    content/global/elements/findbar.js          (widgets/findbar.js)
    content/global/elements/editor.js          (widgets/editor.js)
    content/global/elements/general.js          (widgets/general.js)
    content/global/elements/notificationbox.js  (widgets/notificationbox.js)
+   content/global/elements/pluginProblem.js    (widgets/pluginProblem.js)
    content/global/elements/radio.js            (widgets/radio.js)
    content/global/elements/marquee.css         (widgets/marquee.css)
    content/global/elements/marquee.js          (widgets/marquee.js)
    content/global/elements/stringbundle.js     (widgets/stringbundle.js)
    content/global/elements/tabbox.js           (widgets/tabbox.js)
    content/global/elements/textbox.js          (widgets/textbox.js)
    content/global/elements/videocontrols.js    (widgets/videocontrols.js)
    content/global/elements/tree.js             (widgets/tree.js)
copy from toolkit/pluginproblem/content/pluginProblem.xml
copy to toolkit/content/widgets/pluginProblem.js
--- a/toolkit/pluginproblem/content/pluginProblem.xml
+++ b/toolkit/content/widgets/pluginProblem.js
@@ -1,78 +1,79 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<!DOCTYPE bindings [
-  <!ENTITY % pluginproblemDTD SYSTEM "chrome://pluginproblem/locale/pluginproblem.dtd">
-  <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
-  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
-  %pluginproblemDTD;
-  %globalDTD;
-  %brandDTD;
-]>
+/* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// This is a UA Widget. It runs in per-origin UA widget scope,
+// to be loaded by UAWidgetsChild.jsm.
+
+this.PluginProblemWidget = class {
+  constructor(shadowRoot) {
+    this.shadowRoot = shadowRoot;
+    this.element = shadowRoot.host;
+    this.window = this.element.ownerGlobal;
 
-<bindings id="pluginBindings"
-              xmlns="http://www.mozilla.org/xbl"
-              xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-              xmlns:html="http://www.w3.org/1999/xhtml">
-<binding id="pluginProblem" inheritstyle="false" chromeOnlyContent="true" bindToUntrustedContent="true">
-    <resources>
-        <stylesheet src="chrome://pluginproblem/content/pluginProblemContent.css"/>
-        <stylesheet src="chrome://global/skin/plugins/pluginProblem.css"/>
-    </resources>
-
-    <content>
-        <html:div class="mainBox" anonid="main" chromedir="&locale.dir;">
-            <html:div class="hoverBox">
-                <html:label>
-                    <html:button class="icon" anonid="icon"/>
-                    <html:div class="msg msgVulnerabilityStatus" anonid="vulnerabilityStatus"><!-- set at runtime --></html:div>
-                    <html:div class="msg msgTapToPlay">&tapToPlayPlugin;</html:div>
-                    <html:div class="msg msgClickToPlay" anonid="clickToPlay">&clickToActivatePlugin;</html:div>
-                </html:label>
+    const parser = new this.window.DOMParser();
+    let parserDoc = parser.parseFromString(`
+      <!DOCTYPE bindings [
+        <!ENTITY % pluginproblemDTD SYSTEM "chrome://pluginproblem/locale/pluginproblem.dtd">
+        <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+        <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+        %pluginproblemDTD;
+        %globalDTD;
+        %brandDTD;
+      ]>
+      <div xmlns="http://www.w3.org/1999/xhtml" class="mainBox" id="main" chromedir="&locale.dir;">
+        <link rel="stylesheet" type="text/css" href="chrome://pluginproblem/content/pluginProblemContent.css" />
+        <link rel="stylesheet" type="text/css" href="chrome://global/skin/plugins/pluginProblem.css" />
+        <div class="hoverBox">
+          <label>
+            <button class="icon" id="icon"/>
+            <div class="msg msgVulnerabilityStatus" id="vulnerabilityStatus"><!-- set at runtime --></div>
+            <div class="msg msgTapToPlay">&tapToPlayPlugin;</div>
+            <div class="msg msgClickToPlay" id="clickToPlay">&clickToActivatePlugin;</div>
+          </label>
 
-                <html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
-                <html:div class="msg msgCrashed">
-                    <html:div class="msgCrashedText" anonid="crashedText"><!-- set at runtime --></html:div>
-                    <!-- link href set at runtime -->
-                    <html:div class="msgReload">&reloadPlugin.pre;<html:a class="reloadLink" anonid="reloadLink" href="">&reloadPlugin.middle;</html:a>&reloadPlugin.post;</html:div>
-                </html:div>
+          <div class="msg msgBlocked">&blockedPlugin.label;</div>
+          <div class="msg msgCrashed">
+            <div class="msgCrashedText" id="crashedText"><!-- set at runtime --></div>
+            <!-- link href set at runtime -->
+            <div class="msgReload">&reloadPlugin.pre;<a class="reloadLink" id="reloadLink" href="">&reloadPlugin.middle;</a>&reloadPlugin.post;</div>
+          </div>
 
-                <html:div class="msg msgManagePlugins"><html:a class="action-link" anonid="managePluginsLink" href="">&managePlugins;</html:a></html:div>
-                <html:div class="submitStatus" anonid="submitStatus">
-                    <html:div class="msg msgPleaseSubmit" anonid="pleaseSubmit">
-                        <html:textarea class="submitComment"
-                                       anonid="submitComment"
-                                       placeholder="&report.comment;"/>
-                        <html:div class="submitURLOptInBox">
-                            <html:label><html:input class="submitURLOptIn" anonid="submitURLOptIn" type="checkbox"/> &report.pageURL;</html:label>
-                        </html:div>
-                        <html:div class="submitButtonBox">
-                            <html:span class="helpIcon" anonid="helpIcon" role="link"/>
-                            <html:input class="submitButton" type="button"
-                                        anonid="submitButton"
-                                        value="&report.please;"/>
-                        </html:div>
-                    </html:div>
-                    <html:div class="msg msgSubmitting">&report.submitting;<html:span class="throbber"> </html:span></html:div>
-                    <html:div class="msg msgSubmitted">&report.submitted;</html:div>
-                    <html:div class="msg msgNotSubmitted">&report.disabled;</html:div>
-                    <html:div class="msg msgSubmitFailed">&report.failed;</html:div>
-                    <html:div class="msg msgNoCrashReport">&report.unavailable;</html:div>
-                </html:div>
-                <html:div class="msg msgCheckForUpdates"><html:a class="action-link" anonid="checkForUpdatesLink" href="">&checkForUpdates;</html:a></html:div>
-            </html:div>
-            <html:button class="closeIcon" anonid="closeIcon" title="&hidePluginBtn.label;"/>
-        </html:div>
-        <html:div style="display:none;"><children/></html:div>
-    </content>
-    <implementation>
-      <constructor>
-        // Notify browser-plugins.js that we were attached, on a delay because
-        // this binding doesn't complete layout until the constructor
-        // completes.
-        this.dispatchEvent(new CustomEvent("PluginBindingAttached"));
-      </constructor>
-    </implementation>
-</binding>
-</bindings>
+          <div class="msg msgManagePlugins"><a class="action-link" id="managePluginsLink" href="">&managePlugins;</a></div>
+          <div class="submitStatus" id="submitStatus">
+            <div class="msg msgPleaseSubmit" id="pleaseSubmit">
+              <textarea class="submitComment"
+                        id="submitComment"
+                        placeholder="&report.comment;"/>
+              <div class="submitURLOptInBox">
+                <label><input class="submitURLOptIn" id="submitURLOptIn" type="checkbox"/> &report.pageURL;</label>
+              </div>
+              <div class="submitButtonBox">
+                <span class="helpIcon" id="helpIcon" role="link"/>
+                <input class="submitButton" type="button"
+                       id="submitButton"
+                       value="&report.please;"/>
+              </div>
+            </div>
+            <div class="msg msgSubmitting">&report.submitting;<span class="throbber"> </span></div>
+            <div class="msg msgSubmitted">&report.submitted;</div>
+            <div class="msg msgNotSubmitted">&report.disabled;</div>
+            <div class="msg msgSubmitFailed">&report.failed;</div>
+            <div class="msg msgNoCrashReport">&report.unavailable;</div>
+          </div>
+          <div class="msg msgCheckForUpdates"><a class="action-link" id="checkForUpdatesLink" href="">&checkForUpdates;</a></div>
+      </div>
+      <button class="closeIcon" id="closeIcon" title="&hidePluginBtn.label;"/>
+    </div>
+    `, "application/xml");
+    this.shadowRoot.importNodeAndAppendChildAt(this.shadowRoot,
+                                               parserDoc.documentElement, true);
+
+    // Notify browser-plugins.js that we were attached, on a delay because
+    // this binding doesn't complete layout until the constructor
+    // completes.
+    this.element.dispatchEvent(new this.window.CustomEvent("PluginBindingAttached"));
+  }
+};
--- a/toolkit/pluginproblem/content/pluginProblemBinding.css
+++ b/toolkit/pluginproblem/content/pluginProblemBinding.css
@@ -4,23 +4,34 @@
 
 @namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */
 
 embed:-moz-handler-blocked,
 embed:-moz-handler-crashed,
 embed:-moz-handler-clicktoplay,
 embed:-moz-handler-vulnerable-updatable,
 embed:-moz-handler-vulnerable-no-update,
-applet:-moz-handler-blocked,
-applet:-moz-handler-crashed,
-applet:-moz-handler-clicktoplay,
-applet:-moz-handler-vulnerable-updatable,
-applet:-moz-handler-vulnerable-no-update,
 object:-moz-handler-blocked,
 object:-moz-handler-crashed,
 object:-moz-handler-clicktoplay,
 object:-moz-handler-vulnerable-updatable,
 object:-moz-handler-vulnerable-no-update {
     display: inline-block;
     overflow: hidden;
     opacity: 1 !important;
-    -moz-binding: url('chrome://pluginproblem/content/pluginProblem.xml#pluginProblem') !important;
 }
+
+@supports not -moz-bool-pref("dom.ua_widget.enabled") {
+
+    embed:-moz-handler-blocked,
+    embed:-moz-handler-crashed,
+    embed:-moz-handler-clicktoplay,
+    embed:-moz-handler-vulnerable-updatable,
+    embed:-moz-handler-vulnerable-no-update,
+    object:-moz-handler-blocked,
+    object:-moz-handler-crashed,
+    object:-moz-handler-clicktoplay,
+    object:-moz-handler-vulnerable-updatable,
+    object:-moz-handler-vulnerable-no-update {
+        -moz-binding: url('chrome://pluginproblem/content/pluginProblem.xml#pluginProblem') !important;
+    }
+
+}
--- a/toolkit/pluginproblem/content/pluginProblemContent.css
+++ b/toolkit/pluginproblem/content/pluginProblemContent.css
@@ -3,38 +3,48 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @namespace html url(http://www.w3.org/1999/xhtml);
 
 /* Do not change this without also changing the appropriate line in
  * browser-plugins.js (near where this file is mentioned). */
 html|object:not([width]), html|object[width=""],
 html|embed:not([width]), html|embed[width=""],
-html|applet:not([width]), html|applet[width=""] {
+html|applet:not([width]), html|applet[width=""],
+:host(:not([width])), :host([width=""]) {
   width: 240px;
 }
 
 /* Do not change this without also changing the appropriate line in
  * browser-plugins.js (near where this file is mentioned). */
 html|object:not([height]), html|object[height=""],
 html|embed:not([height]), html|embed[height=""],
-html|applet:not([height]), html|applet[height=""] {
+html|applet:not([height]), html|applet[height=""],
+:host(:not([width])), :host([width=""]) {
   height: 200px;
 }
 
 :-moz-handler-clicktoplay .mainBox,
 :-moz-handler-vulnerable-updatable .mainBox,
 :-moz-handler-vulnerable-no-update .mainBox,
-:-moz-handler-blocked .mainBox {
+:-moz-handler-blocked .mainBox,
+:host(:-moz-handler-clicktoplay) .mainBox,
+:host(:-moz-handler-vulnerable-updatable) .mainBox,
+:host(:-moz-handler-vulnerable-no-update) .mainBox,
+:host(:-moz-handler-blocked) .mainBox {
   -moz-user-focus: normal;
 }
 :-moz-handler-clicktoplay .mainBox:focus,
 :-moz-handler-vulnerable-updatable .mainBox:focus,
 :-moz-handler-vulnerable-no-update .mainBox:focus,
-:-moz-handler-blocked .mainBox:focus {
+:-moz-handler-blocked .mainBox:focus,
+:host(:-moz-handler-clicktoplay) .mainBox:focus,
+:host(:-moz-handler-vulnerable-updatable) .mainBox:focus,
+:host(:-moz-handler-vulnerable-no-update) .mainBox:focus,
+:host(:-moz-handler-blocked) .mainBox:focus {
   outline: 1px dotted;
 }
 
 .mainBox {
   width: inherit;
   height: inherit;
   overflow: hidden;
   direction: ltr;
@@ -67,17 +77,20 @@ html|applet:not([height]), html|applet[h
 }
 
 .mainBox[chromedir="rtl"] {
   direction: rtl;
 }
 
 :-moz-handler-clicktoplay .hoverBox,
 :-moz-handler-vulnerable-updatable .hoverBox,
-:-moz-handler-vulnerable-no-update .hoverBox {
+:-moz-handler-vulnerable-no-update .hoverBox,
+:host(:-moz-handler-clicktoplay) .hoverBox,
+:host(:-moz-handler-vulnerable-updatable) .hoverBox,
+:host(:-moz-handler-vulnerable-no-update) .hoverBox {
   cursor: pointer;
 }
 
 .hoverBox > label {
   cursor: inherit;
 }
 .icon {
   cursor: inherit;
@@ -89,17 +102,25 @@ html|applet:not([height]), html|applet[h
 
 :-moz-handler-clicktoplay .msgClickToPlay,
 :-moz-handler-vulnerable-updatable .msgVulnerabilityStatus,
 :-moz-handler-vulnerable-updatable .msgCheckForUpdates,
 :-moz-handler-vulnerable-updatable .msgClickToPlay,
 :-moz-handler-vulnerable-no-update .msgVulnerabilityStatus,
 :-moz-handler-vulnerable-no-update .msgClickToPlay,
 :-moz-handler-blocked .msgBlocked,
-:-moz-handler-crashed .msgCrashed {
+:-moz-handler-crashed .msgCrashed,
+:host(:-moz-handler-clicktoplay) .msgClickToPlay,
+:host(:-moz-handler-vulnerable-updatable) .msgVulnerabilityStatus,
+:host(:-moz-handler-vulnerable-updatable) .msgCheckForUpdates,
+:host(:-moz-handler-vulnerable-updatable) .msgClickToPlay,
+:host(:-moz-handler-vulnerable-no-update) .msgVulnerabilityStatus,
+:host(:-moz-handler-vulnerable-no-update) .msgClickToPlay,
+:host(:-moz-handler-blocked) .msgBlocked,
+:host(:-moz-handler-crashed) .msgCrashed {
   display: block;
 }
 
 .submitStatus[status] {
   display: -moz-box;
   -moz-box-align: center;
   -moz-box-pack: center;
   height: 160px;
--- a/toolkit/themes/shared/plugins/pluginProblem.css
+++ b/toolkit/themes/shared/plugins/pluginProblem.css
@@ -27,29 +27,37 @@
   display: table-cell;
   box-sizing: border-box;
   padding: 5px;
   vertical-align: middle;
   width: 100%;
   height: 100%;
 }
 :-moz-handler-vulnerable-updatable .hoverBox:active,
-:-moz-handler-vulnerable-no-update .hoverBox:active {
+:-moz-handler-vulnerable-no-update .hoverBox:active,
+:host(:-moz-handler-vulnerable-updatable) .hoverBox:active,
+:host(:-moz-handler-vulnerable-no-update) .hoverBox:active {
   background-color: rgb(65, 65, 65);
 }
 
 :-moz-handler-vulnerable-updatable .hoverBox:active .msgClickToPlay,
-:-moz-handler-vulnerable-no-update .hoverBox:active .msgClickToPlay {
+:-moz-handler-vulnerable-no-update .hoverBox:active .msgClickToPlay,
+:host(:-moz-handler-vulnerable-updatable) .hoverBox:active .msgClickToPlay,
+:host(:-moz-handler-vulnerable-no-update) .hoverBox:active .msgClickToPlay {
   color: red;
 }
 
 :-moz-handler-vulnerable-updatable .hoverBox,
 :-moz-handler-vulnerable-no-update .hoverBox,
 :-moz-handler-blocked .hoverBox,
-:-moz-handler-crashed .hoverBox {
+:-moz-handler-crashed .hoverBox,
+:host(:-moz-handler-vulnerable-updatable) .hoverBox,
+:host(:-moz-handler-vulnerable-no-update) .hoverBox,
+:host(:-moz-handler-blocked) .hoverBox,
+:host(:-moz-handler-crashed) .hoverBox {
   background-image: url(chrome://global/skin/plugins/contentPluginStripe.png);
 }
 
 html|a {
   color: white;
 }
 
 .icon {
@@ -61,29 +69,34 @@ html|a {
   border: none;
   background-color: transparent;
   -moz-user-focus: ignore;
   margin-bottom: 6px;
   -moz-context-properties: fill;
 }
 
 :-moz-handler-vulnerable-updatable .icon,
-:-moz-handler-vulnerable-no-update .icon {
+:-moz-handler-vulnerable-no-update .icon,
+:host(:-moz-handler-vulnerable-updatable) .icon,
+:host(:-moz-handler-vulnerable-no-update) .icon {
   background-image: url(chrome://global/skin/plugins/contentPluginBlocked.png);
   -moz-user-focus: normal;
 }
-:-moz-handler-blocked .icon {
+:-moz-handler-blocked .icon,
+:host(:-moz-handler-blocked) .icon {
   background-image: url(chrome://global/skin/plugins/contentPluginBlocked.png);
 }
-:-moz-handler-clicktoplay .icon {
+:-moz-handler-clicktoplay .icon,
+:host(:-moz-handler-clicktoplay) .icon {
   background-image: url(chrome://global/skin/plugins/plugin.svg);
   fill: var(--grey-10);
   -moz-user-focus: normal;
 }
-:-moz-handler-crashed .icon {
+:-moz-handler-crashed .icon,
+:host(:-moz-handler-crashed) .icon {
   background-image: url(chrome://global/skin/plugins/contentPluginCrashed.png);
 }
 
 .throbber {
   padding-left: 16px; /* width of the background image */
   background: url(chrome://global/skin/icons/loading.png) no-repeat;
   margin-left: 5px;
 }
@@ -91,17 +104,18 @@ html|a {
 @media (min-resolution: 1.1dppx) {
   .throbber {
     background-image: url(chrome://global/skin/icons/loading@2x.png);
     background-size: 16px;
   }
 }
 
 /* on desktop, don't ever show the tap-to-play UI: that is for mobile only */
-:-moz-handler-clicktoplay .msgTapToPlay {
+:-moz-handler-clicktoplay .msgTapToPlay,
+:host(:-moz-handler-clicktoplay) .msgTapToPlay {
   display: none;
 }
 
 .submitStatus div {
   min-height: 19px; /* height of biggest line (with throbber) */
 }
 
 .submitComment {
@@ -185,63 +199,75 @@ html|a {
   padding: 2px 8px;
   margin-top: 7px;
   text-decoration: none;
 }
 .action-link:active {
   background-color: rgb(20, 20, 20);
 }
 
-:-moz-handler-vulnerable-updatable .action-link {
+:-moz-handler-vulnerable-updatable .action-link,
+:host(:-moz-handler-vulnerable-updatable) .action-link {
   background-color: #a81b0c;
 }
-:-moz-handler-vulnerable-updatable .action-link:active {
+:-moz-handler-vulnerable-updatable .action-link:active,
+:host(:-moz-handler-vulnerable-updatable) .action-link:active {
   background-color: #801409;
 }
 
 /* New Photon styling
  *
  * At the moment, these rules are written to only override
  * :-moz-handler-clicktoplay styling. Once we finish the
  * redesign for all cases, we can change the CSS directly
  * above instead of writing these super-specific rules.
  */
-:-moz-handler-clicktoplay .mainBox {
+:-moz-handler-clicktoplay .mainBox,
+:host(:-moz-handler-clicktoplay) .mainBox {
   background-color: var(--grey-70);
 }
 
-:-moz-handler-clicktoplay .msgClickToPlay {
+:-moz-handler-clicktoplay .msgClickToPlay,
+:host(:-moz-handler-clicktoplay) .msgClickToPlay {
   font-size: 13px;
 }
 
-:-moz-handler-clicktoplay .mainBox[sizing=blank] .hoverBox {
+:-moz-handler-clicktoplay .mainBox[sizing=blank] .hoverBox,
+:host(:-moz-handler-clicktoplay) .mainBox[sizing=blank] .hoverBox {
   visibility: hidden;
 }
 
-:-moz-handler-clicktoplay .mainBox[sizing=tiny] .icon {
+:-moz-handler-clicktoplay .mainBox[sizing=tiny] .icon,
+:host(:-moz-handler-clicktoplay) .mainBox[sizing=tiny] .icon {
   width: 16px;
   height: 16px;
 }
 
-:-moz-handler-clicktoplay .mainBox[sizing=reduced] .icon {
+:-moz-handler-clicktoplay .mainBox[sizing=reduced] .icon,
+:host(:-moz-handler-clicktoplay) .mainBox[sizing=reduced] .icon {
   width: 32px;
   height: 32px;
 }
 
 :-moz-handler-clicktoplay .mainBox[sizing=blank] .closeIcon,
 :-moz-handler-clicktoplay .mainBox[sizing=tiny] .closeIcon,
-:-moz-handler-clicktoplay .mainBox[sizing=reduced] .closeIcon {
+:-moz-handler-clicktoplay .mainBox[sizing=reduced] .closeIcon,
+:host(:-moz-handler-clicktoplay) .mainBox[sizing=blank] .closeIcon,
+:host(:-moz-handler-clicktoplay) .mainBox[sizing=tiny] .closeIcon,
+:host(:-moz-handler-clicktoplay) .mainBox[sizing=reduced] .closeIcon {
   display: none;
 }
 
-:-moz-handler-clicktoplay .mainBox[notext] .msgClickToPlay {
+:-moz-handler-clicktoplay .mainBox[notext] .msgClickToPlay,
+:host(:-moz-handler-clicktoplay) .mainBox[notext] .msgClickToPlay {
   display: none;
 }
 
-:-moz-handler-clicktoplay .mainBox[notext] .icon {
+:-moz-handler-clicktoplay .mainBox[notext] .icon,
+:host(:-moz-handler-clicktoplay) .mainBox[notext] .icon {
   /* Override a `margin-bottom: 6px` now that .msgClickToPlay
    * below the icon is no longer shown, in order to achieve
    * a perfect vertical centering of the icon.
    */
   margin-bottom: 0;
 }
 
 :-moz-handler-clicktoplay .hoverBox:active {