Backed out 2 changesets (bug 1503019) for failing crashtests at dom/base/crashtests/1505811.html on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Thu, 15 Nov 2018 01:52:30 +0200
changeset 505664 0210ae2d20cc9637f496df75e0515843e3587700
parent 505663 bec62e8ca661fffe9942cb40d232c632106d2e6c
child 505665 328bd11034330164e63ccc409788346ca3ff6ff9
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1503019, 1505811
milestone65.0a1
backs out06b12fd41ff14bbcba2d2bf268ad5da44496a02a
7b845eac9dd726c7213024ccb94b009da5674592
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
Backed out 2 changesets (bug 1503019) for failing crashtests at dom/base/crashtests/1505811.html on a CLOSED TREE Backed out changeset 06b12fd41ff1 (bug 1503019) Backed out changeset 7b845eac9dd7 (bug 1503019)
accessible/tests/mochitest/elm/test_shadowroot.html
accessible/tests/mochitest/hittest/test_shadowroot.html
accessible/tests/mochitest/relations/test_shadowdom.html
accessible/tests/mochitest/treeupdate/test_bug1276857.html
browser/base/content/test/general/browser_contentAltClick.js
browser/base/content/test/general/browser_contextmenu.js
browser/components/extensions/test/browser/browser_ext_menus_replace_menu.js
browser/components/extensions/test/browser/browser_ext_menus_targetElement_shadow.js
devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
devtools/client/inspector/markup/test/browser_markup_shadowdom.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_clickreveal.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_clickreveal_scroll.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_copy_paths.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_delete.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_dynamic.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_hover.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_maxchildren.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_mutations_shadow.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_navigation.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_nested_pick_inspect.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_noslot.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_open_debugger.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_shadowroot_mode.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_show_nodes_button.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_slotted_keyboard_focus.js
devtools/client/inspector/markup/test/browser_markup_shadowdom_slotupdate.js
devtools/client/inspector/markup/test/head.js
devtools/client/inspector/rules/test/browser_rules_keyframes-rule-shadowdom.js
devtools/client/inspector/rules/test/browser_rules_shadowdom_slot_rules.js
devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js
devtools/client/locales/en-US/inspector.properties
devtools/client/shared/test/shared-head.js
devtools/client/webconsole/test/mochitest/browser_jsterm_syntax_highlight_output.js
devtools/server/tests/mochitest/test_inspector-anonymous.html
dom/base/ChildIterator.cpp
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
dom/base/DocumentOrShadowRoot.cpp
dom/base/Element.cpp
dom/base/crashtests/1324463.html
dom/base/crashtests/1422931.html
dom/base/crashtests/crashtests.list
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsFocusManager.cpp
dom/base/nsIDocument.h
dom/base/nsNodeUtils.cpp
dom/base/nsTextNode.cpp
dom/base/nsTextNode.h
dom/base/test/test_bug1025933.html
dom/base/test/test_bug1037687.html
dom/base/test/test_bug1100912.html
dom/base/test/test_bug1421568.html
dom/base/test/test_bug1453693.html
dom/base/test/test_bug1472427.html
dom/base/test/test_find.html
dom/base/test/test_range_bounds.html
dom/bindings/Codegen.py
dom/events/test/test_bug1079236.html
dom/events/test/test_bug1145910.html
dom/events/test/test_bug1150308.html
dom/events/test/test_bug1264380.html
dom/events/test/test_bug1429572.html
dom/events/test/test_bug1446834.html
dom/events/test/test_bug1484371.html
dom/events/test/test_slotted_mouse_event.html
dom/events/test/test_slotted_text_click.html
dom/html/HTMLSlotElement.cpp
dom/html/test/test_bug1472426.html
dom/html/test/test_fullscreen-api.html
dom/tests/mochitest/pointerlock/test_pointerlock-api.html
dom/tests/mochitest/webcomponents/chrome.ini
dom/tests/mochitest/webcomponents/chrome_disabled.ini
dom/tests/mochitest/webcomponents/head.js
dom/tests/mochitest/webcomponents/test_bug1269155.html
dom/tests/mochitest/webcomponents/test_bug1276240.html
dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
dom/tests/mochitest/webcomponents/test_detached_style.html
dom/tests/mochitest/webcomponents/test_document_adoptnode.html
dom/tests/mochitest/webcomponents/test_document_importnode.html
dom/tests/mochitest/webcomponents/test_event_retarget.html
dom/tests/mochitest/webcomponents/test_event_stopping.html
dom/tests/mochitest/webcomponents/test_shadowroot.html
dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
dom/tests/mochitest/webcomponents/test_shadowroot_style.html
dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
dom/tests/mochitest/webcomponents/test_style_fallback_content.html
dom/tests/moz.build
dom/webidl/CustomElementRegistry.webidl
dom/webidl/Element.webidl
dom/webidl/HTMLSlotElement.webidl
dom/webidl/ShadowRoot.webidl
dom/webidl/Text.webidl
dom/webidl/Window.webidl
dom/xml/nsXMLPrettyPrinter.cpp
dom/xml/resources/XMLPrettyPrint.xml
dom/xml/resources/jar.mn
layout/base/crashtests/crashtests.list
layout/base/tests/chrome/test_printpreview.xul
layout/generic/crashtests/crashtests.list
layout/reftests/bugs/reftest.list
layout/reftests/mathml/reftest.list
layout/reftests/svg/reftest.list
layout/reftests/webcomponents/reftest.list
layout/style/crashtests/crashtests.list
layout/svg/crashtests/crashtests.list
modules/libpref/init/all.js
parser/html/nsHtml5TreeOperation.cpp
servo/components/style/gecko/non_ts_pseudo_class_list.rs
servo/components/style/gecko/selector_parser.rs
testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
testing/profiles/unittest/user.js
testing/web-platform/meta/content-security-policy/securitypolicyviolation/targeting.html.ini
testing/web-platform/meta/css/css-backgrounds/background-size-027.html.ini
testing/web-platform/meta/css/css-scoping/__dir__.ini
testing/web-platform/meta/css/cssom-view/elementsFromPoint-shadowroot.html.ini
testing/web-platform/meta/css/cssom-view/scrollIntoView-shadow.html.ini
testing/web-platform/meta/css/cssom/getComputedStyle-display-none-001.html.ini
testing/web-platform/meta/css/selectors/__dir__.ini
testing/web-platform/meta/custom-elements/CustomElementRegistry.html.ini
testing/web-platform/meta/custom-elements/adopted-callback.html.ini
testing/web-platform/meta/custom-elements/connected-callbacks.html.ini
testing/web-platform/meta/custom-elements/custom-element-registry/upgrade.html.ini
testing/web-platform/meta/custom-elements/disconnected-callbacks.html.ini
testing/web-platform/meta/custom-elements/reactions/Element.html.ini
testing/web-platform/meta/custom-elements/reactions/ShadowRoot.html.ini
testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
testing/web-platform/meta/dom/events/event-global.html.ini
testing/web-platform/meta/dom/events/relatedTarget.window.js.ini
testing/web-platform/meta/dom/interfaces.html.ini
testing/web-platform/meta/html/dom/reflection-misc.html.ini
testing/web-platform/meta/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/not-in-shadow-tree.html.ini
testing/web-platform/meta/html/semantics/forms/the-form-element/form-elements-filter.html.ini
testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.sub.html.ini
testing/web-platform/meta/html/semantics/interfaces.html.ini
testing/web-platform/meta/intersection-observer/shadow-content.html.ini
testing/web-platform/meta/payment-request/interfaces.https.html.ini
testing/web-platform/meta/shadow-dom/__dir__.ini
testing/web-platform/meta/shadow-dom/untriaged/events/event-dispatch/test-003.html.ini
testing/web-platform/meta/touch-events/touch-retargeting.html.ini
toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
toolkit/components/tooltiptext/tests/browser_shadow_dom_tooltip.js
toolkit/content/tests/widgets/chrome.ini
toolkit/content/tests/widgets/mochitest.ini
--- a/accessible/tests/mochitest/elm/test_shadowroot.html
+++ b/accessible/tests/mochitest/elm/test_shadowroot.html
@@ -18,19 +18,23 @@
   </a><br/>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <script>
     SimpleTest.waitForExplicitFinish();
-
-    window.onload = () => {
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.shadowdom.enabled", true],
+      ],
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
       var iframe = document.createElement("iframe");
       iframe.src = "test_shadowroot_subframe.html";
       document.body.appendChild(iframe);
-    };
-
+    });
   </script>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/hittest/test_shadowroot.html
+++ b/accessible/tests/mochitest/hittest/test_shadowroot.html
@@ -18,19 +18,23 @@
   </a><br/>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <script>
     SimpleTest.waitForExplicitFinish();
-
-    window.onload = () => {
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.shadowdom.enabled", true],
+      ],
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
       var iframe = document.createElement("iframe");
       iframe.src = "test_shadowroot_subframe.html";
       document.body.appendChild(iframe);
-    };
-
+    });
   </script>
 
 </body>
 </html>
--- a/accessible/tests/mochitest/relations/test_shadowdom.html
+++ b/accessible/tests/mochitest/relations/test_shadowdom.html
@@ -12,48 +12,64 @@
           src="../common.js"></script>
   <script type="application/javascript"
           src="../relations.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
 
   <script type="application/javascript">
     function doTest() {
+      let iframeDoc = document.getElementById("iframe").contentDocument;
+
       // explicit content
-      let label = document.getElementById("label");
-      let element = document.getElementById("element");
+      let label = iframeDoc.getElementById("label");
+      let element = iframeDoc.getElementById("element");
       testRelation(label, RELATION_LABEL_FOR, element);
       testRelation(element, RELATION_LABELLED_BY, label);
 
       // shadow DOM content
-      let shadowRoot = document.getElementById("shadowcontainer").shadowRoot;
+      let shadowRoot = iframeDoc.getElementById("shadowcontainer").shadowRoot;
       let shadowLabel = shadowRoot.getElementById("label");
       let shadowElement = shadowRoot.getElementById("element");
 
       testRelation(shadowLabel, RELATION_LABEL_FOR, shadowElement);
       testRelation(shadowElement, RELATION_LABELLED_BY, shadowLabel);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
+    SpecialPowers.pushPrefEnv({
+      set: [
+        [ "dom.webcomponents.shadowdom.enabled", true ],
+      ],
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
+      let sc = "script";
+      let iframe = document.createElement("iframe");
+      iframe.id = "iframe";
+      iframe.src = `data:text/html,<html>
+        <body>
+          <div id='label'></div><div id='element' aria-labelledby='label'></div>
+          <div id='shadowcontainer'></div>
+          <${sc}>
+            let shadowRoot = document.getElementById('shadowcontainer').
+              attachShadow({mode: 'open'});
+            shadowRoot.innerHTML =
+              "<div id='label'></div><div id='element' aria-labelledby='label'></div>";
+          </${sc}>
+        </body>
+      </html>`;
 
-    addA11yLoadEvent(doTest, window);
+      addA11yLoadEvent(doTest, iframe.contentWindow);
+      document.body.appendChild(iframe);
+    });
   </script>
 </head>
 
 <body>
   <p id="display"></p>
-  <div id="content">
-    <div id="label"></div>
-    <div id="element" aria-labelledby="label"></div>
-    <div id="shadowcontainer"></div>
-    <script>
-      let shadowRoot = document.getElementById("shadowcontainer").
-        attachShadow({mode: "open"});
-      shadowRoot.innerHTML =
-        `<div id="label"></div><div id="element" aria-labelledby="label"></div>`;
-    </script>
-  </div>
+  <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 </body>
 </html>
--- a/accessible/tests/mochitest/treeupdate/test_bug1276857.html
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
@@ -109,22 +109,27 @@
     function doTest() {
       gQueue = new eventQueue();
       gQueue.push(new runTest());
       gQueue.push(new runShadowTest());
       gQueue.invoke(); // will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-
-    window.onload = () => {
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.shadowdom.enabled", true],
+      ],
+    }, function() {
+      // This test loads in an iframe, to ensure that the element instance is
+      // loaded with the correct value of the preference.
       let iframe = document.createElement("iframe");
       iframe.id = "iframe";
       iframe.src = "test_bug1276857_subframe.html";
       addA11yLoadEvent(doTest, iframe.contentWindow);
       document.body.appendChild(iframe);
-    };
+    });
   </script>
 
 </head>
 <body>
 </body>
 </html>
--- a/browser/base/content/test/general/browser_contentAltClick.js
+++ b/browser/base/content/test/general/browser_contentAltClick.js
@@ -12,16 +12,17 @@
   */
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "Downloads",
                                "resource://gre/modules/Downloads.jsm");
 
 function setup() {
   Services.prefs.setBoolPref("browser.altClickSave", true);
+  Services.prefs.setBoolPref("dom.webcomponents.shadowdom.enabled", true);
 
   let testPage =
     "data:text/html," +
     '<p><a id="commonlink" href="http://mochi.test/moz/">Common link</a></p>' +
     '<p><math id="mathxlink" xmlns="http://www.w3.org/1998/Math/MathML" xlink:type="simple" xlink:href="http://mochi.test/moz/"><mtext>MathML XLink</mtext></math></p>' +
     '<p><svg id="svgxlink" xmlns="http://www.w3.org/2000/svg" width="100px" height="50px" version="1.1"><a xlink:type="simple" xlink:href="http://mochi.test/moz/"><text transform="translate(10, 25)">SVG XLink</text></a></svg></p><br>' +
     '<span id="host"></span><script>document.getElementById("host").attachShadow({mode: "closed"}).appendChild(document.getElementById("commonlink").cloneNode(true));</script>';
 
@@ -35,16 +36,17 @@ async function clean_up() {
   for (let download of downloads) {
     await downloadList.remove(download);
     await download.finalize(true);
   }
   // Remove download history.
   await PlacesUtils.history.clear();
 
   Services.prefs.clearUserPref("browser.altClickSave");
+  Services.prefs.clearUserPref("dom.webcomponents.shadowdom.enabled");
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 }
 
 add_task(async function test_alt_click() {
   await setup();
 
   let downloadList = await Downloads.getList(Downloads.ALL);
   let downloads = [];
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -58,16 +58,18 @@ add_task(async function test_xul_text_li
   // Clean up so won't affect HTML element test cases
   lastElementSelector = null;
   gBrowser.removeCurrentTab();
 });
 
 // Below are test cases for HTML element
 
 add_task(async function test_setup_html() {
+  await pushPrefs(["dom.webcomponents.shadowdom.enabled", true]);
+
   let url = example_base + "subtst_contextmenu.html";
 
   await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
 
   await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     let doc = content.document;
     let audioIframe = doc.querySelector("#test-audio-in-iframe");
     // media documents always use a <video> tag.
--- a/browser/components/extensions/test/browser/browser_ext_menus_replace_menu.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_replace_menu.js
@@ -21,17 +21,18 @@ function checkIsDefaultMenuItemVisible(v
 // - The usual extension filtering behavior (e.g. documentUrlPatterns and
 //   targetUrlPatterns) is still applied; some menu items are therefore hidden.
 // - Calling overrideContext({showDefaults:true}) causes the default menu items
 //   to be shown, but only after the extension's.
 // - overrideContext expires after the menu is opened once.
 // - overrideContext can be called from shadow DOM.
 add_task(async function overrideContext_in_extension_tab() {
   await SpecialPowers.pushPrefEnv({
-    set: [["security.allow_eval_with_system_principal", true]]});
+    set: [["dom.webcomponents.shadowdom.enabled", true],
+          ["security.allow_eval_with_system_principal", true]]});
 
   function extensionTabScript() {
     document.addEventListener("contextmenu", () => {
       browser.menus.overrideContext({});
       browser.test.sendMessage("oncontextmenu_in_dom_part_1");
     }, {once: true});
 
     let shadowRoot = document.getElementById("shadowHost").attachShadow({mode: "open"});
--- a/browser/components/extensions/test/browser/browser_ext_menus_targetElement_shadow.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_targetElement_shadow.js
@@ -1,18 +1,20 @@
 /* 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";
 
 const PAGE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html";
 
 add_task(async function menuInShadowDOM() {
+  Services.prefs.setBoolPref("dom.webcomponents.shadowdom.enabled", true);
   Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
   registerCleanupFunction(() => {
+    Services.prefs.clearUserPref("dom.webcomponents.shadowdom.enabled");
     Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
   });
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
   gBrowser.selectedTab = tab;
 
   async function background() {
     browser.menus.onShown.addListener(async (info, tab) => {
--- a/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
+++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
@@ -5,16 +5,18 @@ http://creativecommons.org/publicdomain/
 "use strict";
 
 // Test shadow DOM content in the markupview.
 // Note that many features are not yet enabled, but basic listing
 // of elements should be working.
 const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
 
 add_task(async function() {
+  Services.prefs.setBoolPref("dom.webcomponents.shadowdom.enabled", true);
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   const shadowHostFront = await getNodeFront("#shadow", inspector.markup);
   is(shadowHostFront.numChildren, 3, "Children of the shadow host are correct");
 
   await inspector.markup.expandNode(shadowHostFront);
   await waitForMultipleChildrenUpdates(inspector);
 
--- a/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
+++ b/devtools/client/inspector/markup/test/browser_markup_dragdrop_invalidNodes.js
@@ -5,16 +5,18 @@
 "use strict";
 
 // Check that pseudo-elements, anonymous nodes and slotted nodes are not draggable.
 
 const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
 
 add_task(async function() {
   await pushPref("devtools.inspector.showAllAnonymousContent", true);
+  await pushPref("dom.webcomponents.shadowdom.enabled", true);
+  await pushPref("dom.webcomponents.customelements.enabled", true);
 
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   info("Expanding nodes below #test");
   const parentFront = await getNodeFront("#test", inspector);
   await inspector.markup.expandNode(parentFront);
   await waitForMultipleChildrenUpdates(inspector);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom.js
@@ -195,17 +195,19 @@ const TEST_DATA = [
         #shadow-root`,
   },
 ];
 
 for (const {url, tree, title} of TEST_DATA) {
   // Test each configuration in both open and closed modes
   add_task(async function() {
     info(`Testing: [${title}] in OPEN mode`);
+    await enableWebComponents();
     const {inspector} = await openInspectorForURL(url.replace("#MODE#", "open"));
     await assertMarkupViewAsTree(tree, "test-component", inspector);
   });
   add_task(async function() {
     info(`Testing: [${title}] in CLOSED mode`);
+    await enableWebComponents();
     const {inspector} = await openInspectorForURL(url.replace("#MODE#", "closed"));
     await assertMarkupViewAsTree(tree, "test-component", inspector);
   });
 }
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_clickreveal.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_clickreveal.js
@@ -36,16 +36,18 @@ add_task(async function() {
     keydownOnRevealLink.bind(null, "KEY_Enter"));
   const checkWithSpacebar = checkRevealLink.bind(null,
     keydownOnRevealLink.bind(null, " "));
 
   await testRevealLink(checkWithEnter, checkWithSpacebar);
 });
 
 async function testRevealLink(revealFnFirst, revealFnSecond) {
+  await enableWebComponents();
+
   const { inspector } = await openInspectorForURL(TEST_URL);
   const { markup } = inspector;
 
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   const hostContainer = markup.getContainer(hostFront);
   await expandContainer(inspector, hostContainer);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_clickreveal_scroll.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_clickreveal_scroll.js
@@ -27,16 +27,18 @@ const TEST_URL = `data:text/html;charset
           <!-- adding some nodes to make sure the slotted container and the real container
            require scrolling -->
         \`;
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
   const {markup} = inspector;
 
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   const hostContainer = markup.getContainer(hostFront);
   await expandContainer(inspector, hostContainer);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_copy_paths.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_copy_paths.js
@@ -23,16 +23,18 @@ const TEST_URL = `data:text/html;charset
             <span></span>
             <span></span>
           </div>\`;
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
   const {markup} = inspector;
 
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   const hostContainer = markup.getContainer(hostFront);
   await expandContainer(inspector, hostContainer);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_delete.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_delete.js
@@ -20,16 +20,18 @@ const TEST_URL = `data:text/html;charset
         super();
         let shadowRoot = this.attachShadow({mode: 'open'});
         shadowRoot.innerHTML = '<slot name="slot1"><div>default</div></slot>';
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   // <test-component> is a shadow host.
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   await inspector.markup.expandNode(hostFront);
   await waitForMultipleChildrenUpdates(inspector);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_dynamic.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_dynamic.js
@@ -55,16 +55,18 @@ const TEST_URL = `data:text/html;charset
                                      <div>some-inline-content</div>
                                    </div>\`;
         }
       });
     }
   </script>`);
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   const tree = `
     div
       test-component
         slot1-1
         slot1-2
       inline text`;
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_hover.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_hover.js
@@ -31,16 +31,18 @@ const TEST_URL = `data:text/html;charset
           </div>
         \`;
       }
     });
   })();
   </script>`);
 
 add_task(async function() {
+  await enableWebComponents();
+
   const { inspector, toolbox, testActor } = await openInspectorForURL(TEST_URL);
 
   info("Waiting for element picker to become active.");
   await startPicker(toolbox);
 
   info("Move mouse over the padding of the test-component");
   await hoverElement(inspector, testActor, "test-component", 10, 10);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_maxchildren.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_maxchildren.js
@@ -22,16 +22,17 @@ const TEST_URL = `data:text/html;charset
       shadowRoot.innerHTML = "<slot>some default content</slot>";
     }
     connectedCallback() {}
     disconnectedCallback() {}
   });
 </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
   await pushPref("devtools.markup.pagesize", 5);
 
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   // <test-component> is a shadow host.
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   await inspector.markup.expandNode(hostFront);
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_mutations_shadow.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_mutations_shadow.js
@@ -23,16 +23,18 @@ const TEST_URL = `data:text/html;charset
                                    <slot name="slot1"></slot>
                                  </div>
                                  <div id="another-div"></div>\`;
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   const tree = `
     test-component
       #shadow-root
         slot1-container
           slot
             div!slotted
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_navigation.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_navigation.js
@@ -40,16 +40,19 @@ const TEST_DATA = [
   ["KEY_ArrowDown", "div", "slotted2"],
   ["KEY_ArrowDown", "slotted1"],
   ["KEY_ArrowRight", "slotted1"],
   ["KEY_ArrowDown", "slot1-child"],
   ["KEY_ArrowDown", "slotted2"],
 ];
 
 add_task(async function() {
+  await pushPref("dom.webcomponents.shadowdom.enabled", true);
+  await pushPref("dom.webcomponents.customelements.enabled", true);
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   info("Making sure the markup-view frame is focused");
   inspector.markup._frame.focus();
 
   info("Starting to iterate through the test data");
   for (const [key, expected, slottedClassName] of TEST_DATA) {
     info("Testing step: " + key + " to navigate to " + expected);
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_nested_pick_inspect.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_nested_pick_inspect.js
@@ -41,16 +41,18 @@ const TEST_URL = `data:text/html;charset
       </div>\`);
 
     defineComponent('test-image',
       \`<div style="display:block; height: 200px; width: 100%; background:red"></div>\`);
   })();
   </script>`);
 
 add_task(async function() {
+  await enableWebComponents();
+
   const { inspector, toolbox, tab, testActor } = await openInspectorForURL(TEST_URL);
 
   info("Waiting for element picker to become active");
   await startPicker(toolbox);
   info("Click and pick the pick-target");
   await pickElement(inspector, testActor, "test-outer", 10, 10);
   info("Check that the markup view is displayed as expected");
   await assertMarkupView(inspector);
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_noslot.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_noslot.js
@@ -42,16 +42,18 @@ const TEST_URL = `data:text/html;charset
         super();
         let shadowRoot = this.attachShadow({mode: 'open'});
         shadowRoot.innerHTML = '<slot></slot>';
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   // We expect that host children are correctly displayed when no slots are defined.
   const beforeTree = `
   class="root"
     no-slot-component
       #shadow-root
         no-slot-div
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_open_debugger.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_open_debugger.js
@@ -43,16 +43,18 @@ const TEST_URL = `data:text/html;charset
       constructor() {
         super();
       }
     }, { extends: 'p' });
   }
 </script>`);
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector, toolbox} = await openInspectorForURL(TEST_URL);
 
   // Test with an element to which we attach a shadow.
   await runTest(inspector, toolbox, "test-component", "attachTestComponent");
 
   // Test with an element to which we only add a custom element definition.
   await runTest(inspector, toolbox, "other-component", "defineOtherComponent");
 });
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_shadowroot_mode.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_shadowroot_mode.js
@@ -24,16 +24,18 @@ const TEST_URL = `data:text/html;charset
         super();
         this.attachShadow({ mode: "open" });
       }
     });
   </script>
 `;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const { inspector } = await openInspectorForURL(TEST_URL);
   const {markup} = inspector;
 
   info("Find and expand the closed-component shadow DOM host.");
   const closedHostFront = await getNodeFront("closed-component", inspector);
   const closedHostContainer = markup.getContainer(closedHostFront);
   await expandContainer(inspector, closedHostContainer);
 
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_show_nodes_button.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_show_nodes_button.js
@@ -22,16 +22,18 @@ const TEST_URL = `data:text/html;charset
         super();
         let shadowRoot = this.attachShadow({mode: 'open'});
         shadowRoot.innerHTML = '<slot></slot>';
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const { inspector } = await openInspectorForURL(TEST_URL);
   const { markup } = inspector;
 
   info("Find and expand the component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   const hostContainer = markup.getContainer(hostFront);
   await expandContainer(inspector, hostContainer);
   const shadowRootContainer = hostContainer.getChildContainers()[0];
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_slotted_keyboard_focus.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_slotted_keyboard_focus.js
@@ -18,16 +18,18 @@ const TEST_URL = `data:text/html;charset
         super();
         let shadowRoot = this.attachShadow({mode: 'open'});
         shadowRoot.innerHTML = '<slot name="slot1"></slot>';
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const { inspector } = await openInspectorForURL(TEST_URL);
   const { markup } = inspector;
   const win = inspector.markup.doc.defaultView;
 
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   const hostContainer = markup.getContainer(hostFront);
   await expandContainer(inspector, hostContainer);
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom_slotupdate.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom_slotupdate.js
@@ -22,16 +22,18 @@ const TEST_URL = `data:text/html;charset
         super();
         let shadowRoot = this.attachShadow({mode: 'open'});
         shadowRoot.innerHTML = '<slot name="slot1"></slot><slot name="slot2"></slot>';
       }
     });
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   const {inspector} = await openInspectorForURL(TEST_URL);
 
   const tree = `
     test-component
       #shadow-root
         name="slot1"
           div!slotted
           div!slotted
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -27,16 +27,17 @@ SimpleTest.requestCompleteLog();
 // Services.prefs.setBoolPref("devtools.dump.emit", true);
 
 // Clear preferences that may be set during the course of tests.
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.dump.emit");
   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
   Services.prefs.clearUserPref("devtools.markup.pagesize");
+  Services.prefs.clearUserPref("dom.webcomponents.shadowdom.enabled");
   Services.prefs.clearUserPref("devtools.inspector.showAllAnonymousContent");
 });
 
 /**
  * Some tests may need to import one or more of the test helper scripts.
  * A test helper script is simply a js file that contains common test code that
  * is either not common-enough to be in head.js, or that is located in a
  * separate directory.
--- a/devtools/client/inspector/rules/test/browser_rules_keyframes-rule-shadowdom.js
+++ b/devtools/client/inspector/rules/test/browser_rules_keyframes-rule-shadowdom.js
@@ -22,16 +22,18 @@ const TEST_URI = `data:text/html;charset
         }
         span {
           animation: blink .5s 0s infinite;
         }
       </style>\`;
   </script>`;
 
 add_task(async function() {
+  await enableWebComponents();
+
   await addTab(TEST_URI);
 
   const {inspector, view} = await openRuleView();
 
   info("Expand the shadow-root parent");
   const divFront = await getNodeFront("div", inspector);
   await inspector.markup.expandNode(divFront);
   await waitForMultipleChildrenUpdates(inspector);
--- a/devtools/client/inspector/rules/test/browser_rules_shadowdom_slot_rules.js
+++ b/devtools/client/inspector/rules/test/browser_rules_shadowdom_slot_rules.js
@@ -31,16 +31,19 @@ const TEST_URL = `data:text/html;charset
       }
     });
   </script>
   </body>
   </html>
 `);
 
 add_task(async function() {
+  await pushPref("dom.webcomponents.shadowdom.enabled", true);
+  await pushPref("dom.webcomponents.customelements.enabled", true);
+
   const {inspector} = await openInspectorForURL(TEST_URL);
   const {markup} = inspector;
   const ruleview = inspector.getPanel("ruleview").view;
 
   // <test-component> is a shadow host.
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
 
--- a/devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js
+++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js
@@ -18,16 +18,19 @@ const TEST_URL = `data:text/html;charset
         super();
         let shadowRoot = this.attachShadow({mode: 'open'});
         shadowRoot.innerHTML = '<slot class="slot-class" name="slot1"></slot>';
       }
     });
   </script>`;
 
 add_task(async function() {
+  await pushPref("dom.webcomponents.shadowdom.enabled", true);
+  await pushPref("dom.webcomponents.customelements.enabled", true);
+
   const {inspector} = await openInspectorForURL(TEST_URL);
   const {markup} = inspector;
   const breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
 
   info("Find and expand the test-component shadow DOM host.");
   const hostFront = await getNodeFront("test-component", inspector);
   const hostContainer = markup.getContainer(hostFront);
   await expandContainer(inspector, hostContainer);
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -87,16 +87,17 @@ markupView.custom.tooltiptext=Show custo
 # This is used to speak the New Attribute button when editing a tag
 # and a screen reader user tabs to it. This string is not visible onscreen.
 markupView.newAttribute.label=New attribute
 
 # LOCALIZATION NOTE (markupView.revealLink.tooltip)
 # Used as a tooltip for an icon in the markup view when displaying elements inserted in
 # <slot> nodes in a custom  component. When clicking on the icon, the corresponding
 # non-slotted container will be selected
+# (test with dom.webcomponents.shadowdom.enabled set to true)
 markupView.revealLink.tooltip=Reveal
 
 #LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
 previewTooltip.image.brokenImage=Could not load the image
 
 # LOCALIZATION NOTE: Used in color picker tooltip when the eyedropper is disabled for
 # non-HTML documents
 eyedropper.disabled.title=Unavailable in non-HTML documents
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -715,16 +715,24 @@ async function injectEventUtilsInContent
         { type: "mouseup", isSynthesized: false }, content);
     });
 
     Services.scriptloader.loadSubScript(
       "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
   });
 }
 
+/**
+ * Temporarily flip all the preferences needed to enable web components.
+ */
+async function enableWebComponents() {
+  await pushPref("dom.webcomponents.shadowdom.enabled", true);
+  await pushPref("dom.webcomponents.customelements.enabled", true);
+}
+
 /*
  * Register an actor in the content process of the current tab.
  *
  * Calling ActorRegistry.registerModule only registers the actor in the current process.
  * As all test scripts are ran in the parent process, it is only registered here.
  * This function helps register them in the content process used for the current tab.
  *
  * @param {string} url
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_syntax_highlight_output.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_syntax_highlight_output.js
@@ -2,16 +2,18 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const TEST_URI =
   "data:text/html;charset=utf-8,Test syntax highlighted output";
 
 add_task(async function() {
+  await pushPref("dom.webcomponents.customelements.enabled", true);
+
   await pushPref("devtools.webconsole.jsterm.codeMirror", true);
   await performTests();
 
   await pushPref("devtools.webconsole.jsterm.codeMirror", false);
   await performTests();
 });
 
 async function performTests() {
--- a/devtools/server/tests/mochitest/test_inspector-anonymous.html
+++ b/devtools/server/tests/mochitest/test_inspector-anonymous.html
@@ -16,16 +16,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 window.onload = function() {
   const {DocumentWalker: _documentWalker} =
     require("devtools/server/actors/inspector/document-walker");
 
   const nodeFilterConstants =
     require("devtools/shared/dom-node-filter-constants");
   const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.webcomponents.shadowdom.enabled", true],
+  ]});
   SimpleTest.waitForExplicitFinish();
 
   let gWalker = null;
   let gInspectee = null;
 
   addTest(async function setup() {
     info("Setting up inspector and walker actors.");
 
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -20,17 +20,18 @@ namespace dom {
 ExplicitChildIterator::ExplicitChildIterator(const nsIContent* aParent,
                                              bool aStartAtBeginning)
   : mParent(aParent),
     mChild(nullptr),
     mDefaultChild(nullptr),
     mIsFirst(aStartAtBeginning),
     mIndexInInserted(0)
 {
-  mParentAsSlot = HTMLSlotElement::FromNode(mParent);
+  mParentAsSlot = nsDocument::IsShadowDOMEnabled(mParent) ?
+    HTMLSlotElement::FromNode(mParent) : nullptr;
 }
 
 nsIContent*
 ExplicitChildIterator::GetNextChild()
 {
   // If we're already in the inserted-children array, look there first
   if (mIndexInInserted) {
     MOZ_ASSERT(mChild);
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -348,16 +348,42 @@ CustomElementRegistry::CustomElementRegi
   mozilla::HoldJSObjects(this);
 }
 
 CustomElementRegistry::~CustomElementRegistry()
 {
   mozilla::DropJSObjects(this);
 }
 
+bool
+CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject)
+{
+  if (nsContentUtils::IsCustomElementsEnabled()) {
+    return true;
+  }
+
+  if (!XRE_IsParentProcess()) {
+    return false;
+  }
+
+  nsIPrincipal* principal =
+    nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject));
+  return nsContentUtils::AllowXULXBLForPrincipal(principal);
+}
+
+bool
+CustomElementRegistry::IsCustomElementEnabled(nsIDocument* aDoc)
+{
+  if (nsContentUtils::IsCustomElementsEnabled()) {
+    return true;
+  }
+
+  return XRE_IsParentProcess() && aDoc->AllowXULXBL();
+}
+
 NS_IMETHODIMP
 CustomElementRegistry::RunCustomElementCreationCallback::Run()
 {
   ErrorResult er;
   nsDependentAtomString value(mAtom);
   mCallback->Call(value, er);
   MOZ_ASSERT(NS_SUCCEEDED(er.StealNSResult()),
     "chrome JavaScript error in the callback.");
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -387,16 +387,19 @@ class CustomElementRegistry final : publ
   // Allow nsDocument to access mCustomDefinitions and mCandidatesMap.
   friend class ::nsDocument;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry)
 
 public:
+  static bool IsCustomElementEnabled(JSContext* aCx, JSObject* aObject);
+  static bool IsCustomElementEnabled(nsIDocument* aDoc);
+
   explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow);
 
 private:
   class RunCustomElementCreationCallback : public mozilla::Runnable
   {
   public:
     NS_DECL_NSIRUNNABLE
     explicit RunCustomElementCreationCallback(CustomElementRegistry* aRegistry,
--- a/dom/base/DocumentOrShadowRoot.cpp
+++ b/dom/base/DocumentOrShadowRoot.cpp
@@ -177,16 +177,20 @@ DocumentOrShadowRoot::GetRetargetedFocus
                                            getter_AddRefs(focusedWindow));
     // be safe and make sure the element is from this document
     if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
       if (focusedContent->ChromeOnlyAccess()) {
         focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
       }
 
       if (focusedContent) {
+        if (!nsDocument::IsShadowDOMEnabled(focusedContent)) {
+          return focusedContent->AsElement();
+        }
+
         if (nsIContent* retarget = Retarget(focusedContent)) {
           return retarget->AsElement();
         }
       }
     }
   }
 
   return nullptr;
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1773,17 +1773,17 @@ Element::BindToTree(nsIDocument* aDocume
     // See the comment about the restyle bits above, it also applies.
     UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
   } else {
     // If we're not in the doc and not in a shadow tree,
     // update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
-  if (IsInComposedDoc()) {
+  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc()) && IsInComposedDoc()) {
     // Connected callback must be enqueued whenever a custom element becomes
     // connected.
     CustomElementData* data = GetCustomElementData();
     if (data) {
       if (data->mState == CustomElementData::State::eCustom) {
         nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eConnected, this);
       } else {
         // Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
@@ -2100,25 +2100,27 @@ Element::UnbindFromTree(bool aDeep, bool
           /* aNullParent */ false);
       }
     }
 
     document->ClearBoxObjectFor(this);
 
      // Disconnected must be enqueued whenever a connected custom element becomes
      // disconnected.
-    CustomElementData* data  = GetCustomElementData();
-    if (data) {
-      if (data->mState == CustomElementData::State::eCustom) {
-        nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eDisconnected,
-                                                 this);
-      } else {
-        // Remove an unresolved custom element that is a candidate for upgrade
-        // when a custom element is disconnected.
-        nsContentUtils::UnregisterUnresolvedElement(this);
+    if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
+      CustomElementData* data  = GetCustomElementData();
+      if (data) {
+        if (data->mState == CustomElementData::State::eCustom) {
+          nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eDisconnected,
+                                                   this);
+        } else {
+          // Remove an unresolved custom element that is a candidate for upgrade
+          // when a custom element is disconnected.
+          nsContentUtils::UnregisterUnresolvedElement(this);
+        }
       }
     }
   }
 
   // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
   //  because it has to happen after unsetting the parent pointer, but before
   //  recursively unbinding the kids.
   if (IsHTMLElement()) {
@@ -2752,42 +2754,44 @@ Element::SetAttrAndNotify(int32_t aNames
 
   if (aComposedDocument) {
     RefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
     }
   }
 
-  CustomElementDefinition* definition = GetCustomElementDefinition();
-  // Only custom element which is in `custom` state could get the
-  // CustomElementDefinition.
-  if (definition && definition->IsInObservedAttributeList(aName)) {
-    RefPtr<nsAtom> oldValueAtom;
-    if (oldValue) {
-      oldValueAtom = oldValue->GetAsAtom();
-    } else {
-      // If there is no old value, get the value of the uninitialized
-      // attribute that was swapped with aParsedValue.
-      oldValueAtom = aParsedValue.GetAsAtom();
+  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
+    CustomElementDefinition* definition = GetCustomElementDefinition();
+    // Only custom element which is in `custom` state could get the
+    // CustomElementDefinition.
+    if (definition && definition->IsInObservedAttributeList(aName)) {
+      RefPtr<nsAtom> oldValueAtom;
+      if (oldValue) {
+        oldValueAtom = oldValue->GetAsAtom();
+      } else {
+        // If there is no old value, get the value of the uninitialized
+        // attribute that was swapped with aParsedValue.
+        oldValueAtom = aParsedValue.GetAsAtom();
+      }
+      RefPtr<nsAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
+      nsAutoString ns;
+      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
+
+      LifecycleCallbackArgs args = {
+        nsDependentAtomString(aName),
+        aModType == MutationEvent_Binding::ADDITION ?
+          VoidString() : nsDependentAtomString(oldValueAtom),
+        nsDependentAtomString(newValueAtom),
+        (ns.IsEmpty() ? VoidString() : ns)
+      };
+
+      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+        this, &args, nullptr, definition);
     }
-    RefPtr<nsAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
-    nsAutoString ns;
-    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
-
-    LifecycleCallbackArgs args = {
-      nsDependentAtomString(aName),
-      aModType == MutationEvent_Binding::ADDITION ?
-        VoidString() : nsDependentAtomString(oldValueAtom),
-      nsDependentAtomString(newValueAtom),
-      (ns.IsEmpty() ? VoidString() : ns)
-    };
-
-    nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
-      this, &args, nullptr, definition);
   }
 
   if (aCallAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
                       aSubjectPrincipal, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
@@ -2924,33 +2928,35 @@ Element::PostIdMaybeChange(int32_t aName
   }
 }
 
 nsresult
 Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
                                 const nsAttrValueOrString& aValue,
                                 bool aNotify)
 {
-  // Only custom element which is in `custom` state could get the
-  // CustomElementDefinition.
-  CustomElementDefinition* definition = GetCustomElementDefinition();
-  if (definition && definition->IsInObservedAttributeList(aName)) {
-    nsAutoString ns;
-    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
-
-    nsAutoString value(aValue.String());
-    LifecycleCallbackArgs args = {
-      nsDependentAtomString(aName),
-      value,
-      value,
-      (ns.IsEmpty() ? VoidString() : ns)
-    };
-
-    nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
-      this, &args, nullptr, definition);
+  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
+    // Only custom element which is in `custom` state could get the
+    // CustomElementDefinition.
+    CustomElementDefinition* definition = GetCustomElementDefinition();
+    if (definition && definition->IsInObservedAttributeList(aName)) {
+      nsAutoString ns;
+      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
+
+      nsAutoString value(aValue.String());
+      LifecycleCallbackArgs args = {
+        nsDependentAtomString(aName),
+        value,
+        value,
+        (ns.IsEmpty() ? VoidString() : ns)
+      };
+
+      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+        this, &args, nullptr, definition);
+    }
   }
 
   return NS_OK;
 }
 
 EventListenerManager*
 Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,
                                         bool* aDefer)
@@ -3055,33 +3061,35 @@ Element::UnsetAttr(int32_t aNameSpaceID,
 
   if (document) {
     RefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
     }
   }
 
-  CustomElementDefinition* definition = GetCustomElementDefinition();
-  // Only custom element which is in `custom` state could get the
-  // CustomElementDefinition.
-  if (definition && definition->IsInObservedAttributeList(aName)) {
-    nsAutoString ns;
-    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
-
-    RefPtr<nsAtom> oldValueAtom = oldValue.GetAsAtom();
-    LifecycleCallbackArgs args = {
-      nsDependentAtomString(aName),
-      nsDependentAtomString(oldValueAtom),
-      VoidString(),
-      (ns.IsEmpty() ? VoidString() : ns)
-    };
-
-    nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
-      this, &args, nullptr, definition);
+  if (CustomElementRegistry::IsCustomElementEnabled(OwnerDoc())) {
+    CustomElementDefinition* definition = GetCustomElementDefinition();
+    // Only custom element which is in `custom` state could get the
+    // CustomElementDefinition.
+    if (definition && definition->IsInObservedAttributeList(aName)) {
+      nsAutoString ns;
+      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
+
+      RefPtr<nsAtom> oldValueAtom = oldValue.GetAsAtom();
+      LifecycleCallbackArgs args = {
+        nsDependentAtomString(aName),
+        nsDependentAtomString(oldValueAtom),
+        VoidString(),
+        (ns.IsEmpty() ? VoidString() : ns)
+      };
+
+      nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+        this, &args, nullptr, definition);
+    }
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, nullptr, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   UpdateState(aNotify);
 
   if (aNotify) {
--- a/dom/base/crashtests/1324463.html
+++ b/dom/base/crashtests/1324463.html
@@ -1,11 +1,12 @@
 <!DOCTYPE html>
 <html>
 <script>
+// requires: user_pref("dom.webcomponents.shadowdom.enabled", true);
 addEventListener("DOMContentLoaded", function(){
   let o_0 = document.createElement("span").attachShadow({ mode: "open" });
   let o_1 = document.createElementNS("http://www.mozilla.org/xbl", "binding");
   let o_2 = document.createElementNS("http://www.mozilla.org/xbl", "children");
   let o_3 = document.createTextNode("");
   o_0.appendChild(o_1);
   o_1.appendChild(o_2);
   o_2.appendChild(o_3);
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/1422931.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<!-- Testing slot element with "dom.webcomponents.shadowdom.enabled" set to false -->
+<slot><div></div></slot>
+</html>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -186,34 +186,34 @@ load 852381.html
 load 863950.html
 load 864448.html
 load 886213.html
 load 898906.html
 load 930250.html
 load 942979.html
 load 973401.html
 load 978646.html
-load 1024428-1.html
-load 1027461-1.html
-load 1029710.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1024428-1.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1027461-1.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1029710.html
 load 1154598.xhtml
 load 1157995.html
 load 1158412.html
 load 1181619.html
 load 1230422.html
 load 1251361.html
-load 1281715.html
-load 1281745.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1281715.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1281745.html
 load 1304437.html
 pref(dom.IntersectionObserver.enabled,true) load 1324209.html
 load 1324500.html
 pref(dom.IntersectionObserver.enabled,true) load 1326194-1.html
 pref(dom.IntersectionObserver.enabled,true) load 1326194-2.html
 pref(dom.IntersectionObserver.enabled,true) load 1332939.html
-load 1341693.html
+pref(dom.webcomponents.customelements.enabled,true) load 1341693.html
 load 1352453.html
 pref(dom.IntersectionObserver.enabled,true) load 1353529.xul
 load 1368327.html
 pref(dom.IntersectionObserver.enabled,true) load 1369363.xul
 load 1370072.html
 pref(clipboard.autocopy,true) load 1370737.html
 pref(dom.IntersectionObserver.enabled,true) load 1370968.html
 load 1373750.html
@@ -226,23 +226,24 @@ load 1383780.html
 pref(clipboard.autocopy,true) load 1385272-1.html
 load 1393806.html
 load 1396466.html
 load 1397795.html
 load 1400701.html
 load 1403377.html
 load 1405771.html
 load 1406109-1.html
-load 1324463.html
-load 1413815.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1324463.html
+pref(dom.webcomponents.customelements.enabled,true) load 1413815.html
 load 1411473.html
-load 1419799.html
-skip-if(!browserIsRemote) pref(dom.disable_open_during_load,false) load 1419902.html # skip on non e10s loads, Bug 1419902
-load 1422883.html
-load 1428053.html
-load 1441029.html
+pref(dom.webcomponents.shadowdom.enabled,false) load 1422931.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1419799.html
+skip-if(!browserIsRemote) pref(dom.webcomponents.customelements.enabled,true) pref(dom.disable_open_during_load,false) load 1419902.html # skip on non e10s loads, Bug 1419902
+pref(dom.webcomponents.shadowdom.enabled,true) load 1422883.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1428053.html
+pref(dom.webcomponents.customelements.enabled,true) load 1441029.html
 load 1449601.html
 load 1445670.html
 load 1458016.html
-load 1459688.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1459688.html
 load 1460794.html
-load 1505875.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1505875.html
 pref(dom.webcomponents.customelements.enabled,true) load 1505811.html
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -298,16 +298,18 @@ bool nsContentUtils::sTrustedFullscreenO
 bool nsContentUtils::sIsCutCopyAllowed = true;
 bool nsContentUtils::sIsUpgradableDisplayContentPrefEnabled = false;
 bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false;
 bool nsContentUtils::sIsFormAutofillAutocompleteEnabled = false;
 bool nsContentUtils::sIsUAWidgetEnabled = false;
+bool nsContentUtils::sIsShadowDOMEnabled = false;
+bool nsContentUtils::sIsCustomElementsEnabled = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
 bool nsContentUtils::sUseActivityCursor = false;
 bool nsContentUtils::sAnimationsAPICoreEnabled = false;
 bool nsContentUtils::sGetBoxQuadsEnabled = false;
 bool nsContentUtils::sSkipCursorMoveForSameValueSet = false;
 bool nsContentUtils::sRequestIdleCallbackEnabled = false;
 bool nsContentUtils::sLowerNetworkPriority = false;
 bool nsContentUtils::sTailingEnabled = false;
@@ -667,16 +669,22 @@ nsContentUtils::Init()
                                "dom.enable_frame_timing", false);
 
   Preferences::AddBoolVarCache(&sIsFormAutofillAutocompleteEnabled,
                                "dom.forms.autocomplete.formautofill", false);
 
   Preferences::AddBoolVarCache(&sIsUAWidgetEnabled,
                                "dom.ua_widget.enabled", false);
 
+  Preferences::AddBoolVarCache(&sIsShadowDOMEnabled,
+                               "dom.webcomponents.shadowdom.enabled", false);
+
+  Preferences::AddBoolVarCache(&sIsCustomElementsEnabled,
+                               "dom.webcomponents.customelements.enabled", false);
+
   Preferences::AddIntVarCache(&sPrivacyMaxInnerWidth,
                               "privacy.window.maxInnerWidth",
                               1000);
 
   Preferences::AddIntVarCache(&sPrivacyMaxInnerHeight,
                               "privacy.window.maxInnerHeight",
                               1000);
 
@@ -10056,22 +10064,24 @@ nsContentUtils::NewXULOrHTMLElement(Elem
   nsAtom* typeAtom = nullptr;
   bool isCustomElement = isCustomElementName || aIsAtom;
   if (isCustomElement) {
     typeAtom = isCustomElementName ? tagAtom : aIsAtom;
   }
 
   MOZ_ASSERT_IF(aDefinition, isCustomElement);
 
+  bool customElementEnabled = CustomElementRegistry::IsCustomElementEnabled(nodeInfo->GetDocument());
+
   // https://dom.spec.whatwg.org/#concept-create-element
   // We only handle the "synchronous custom elements flag is set" now.
   // For the unset case (e.g. cloning a node), see bug 1319342 for that.
   // Step 4.
   CustomElementDefinition* definition = aDefinition;
-  if (isCustomElement && !definition) {
+  if (customElementEnabled && isCustomElement && !definition) {
     MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
     definition =
       nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
                                                     nodeInfo->NameAtom(),
                                                     nodeInfo->NamespaceID(),
                                                     typeAtom);
   }
 
@@ -10182,17 +10192,17 @@ nsContentUtils::NewXULOrHTMLElement(Elem
   } else {
     NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
   }
 
   if (!*aResult) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  if (isCustomElement) {
+  if (customElementEnabled && isCustomElement) {
     (*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
     nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
   }
 
   return NS_OK;
 }
 
 CustomElementRegistry*
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -3234,17 +3234,20 @@ public:
    * dom.ua_widget.enabled.
    *
    * When enabled, UA Widget will replace legacy XBL when rendering JS-implemented
    * web content widgets (videocontrols/datetimebox/etc.)
    *
    * It is really enabled only if Shadow DOM is also enabled.
    */
   static bool
-  IsUAWidgetEnabled() { return sIsUAWidgetEnabled; }
+  IsUAWidgetEnabled() { return sIsShadowDOMEnabled && sIsUAWidgetEnabled; }
+
+  static bool
+  IsShadowDOMEnabled() { return sIsShadowDOMEnabled; }
 
   /**
    * Returns true if reserved key events should be prevented from being sent
    * to their target. Instead, the key event should be handled by chrome only.
    */
   static bool ShouldBlockReservedKeys(mozilla::WidgetKeyboardEvent* aKeyEvent);
 
   /**
@@ -3281,16 +3284,19 @@ public:
 
   /**
    * Detect whether a string is a local-url.
    * https://drafts.csswg.org/css-values/#local-urls
    */
   static bool
   IsLocalRefURL(const nsString& aString);
 
+  static bool
+  IsCustomElementsEnabled() { return sIsCustomElementsEnabled; }
+
   /**
    * Compose a tab id with process id and a serial number.
    */
   static uint64_t GenerateTabId();
 
   /**
    * Generate an id for a BrowsingContext using a range of serial
    * numbers reserved for the current process.
@@ -3544,16 +3550,17 @@ private:
   static uint32_t sHandlingInputTimeout;
   static bool sIsPerformanceTimingEnabled;
   static bool sIsResourceTimingEnabled;
   static bool sIsPerformanceNavigationTimingEnabled;
   static bool sIsUpgradableDisplayContentPrefEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsFormAutofillAutocompleteEnabled;
   static bool sIsUAWidgetEnabled;
+  static bool sIsShadowDOMEnabled;
   static bool sIsCustomElementsEnabled;
   static bool sSendPerformanceTimingNotifications;
   static bool sUseActivityCursor;
   static bool sAnimationsAPICoreEnabled;
   static bool sGetBoxQuadsEnabled;
   static bool sSkipCursorMoveForSameValueSet;
   static bool sRequestIdleCallbackEnabled;
   static bool sLowerNetworkPriority;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1410,16 +1410,17 @@ nsIDocument::nsIDocument()
     mDidFireDOMContentLoaded(true),
     mHasScrollLinkedEffect(false),
     mFrameRequestCallbacksScheduled(false),
     mIsTopLevelContentDocument(false),
     mIsContentDocument(false),
     mDidCallBeginLoad(false),
     mAllowPaymentRequest(false),
     mEncodingMenuDisabled(false),
+    mIsShadowDOMEnabled(false),
     mIsSVGGlyphsDocument(false),
     mInDestructor(false),
     mIsGoingAway(false),
     mInXBLUpdate(false),
     mNeedsReleaseAfterStackRefCntRelease(false),
     mStyleSetFilled(false),
     mSSApplicableStateNotificationPending(false),
     mMayHaveTitleElement(false),
@@ -2131,16 +2132,21 @@ nsDocument::Init()
   // mNodeInfo keeps NodeInfoManager alive!
   mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
   NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
   MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
              "Bad NodeType in aNodeInfo");
 
   NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
 
+  // Set this when document is initialized and value stays the same for the
+  // lifetime of the document.
+  mIsShadowDOMEnabled = nsContentUtils::IsShadowDOMEnabled() ||
+    (XRE_IsParentProcess() && AllowXULXBL());
+
   // If after creation the owner js global is not set for a document
   // we use the default compartment for this document, instead of creating
   // wrapper in some random compartment when the document is exposed to js
   // via some events.
   nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
   mScopeObject = do_GetWeakReference(global);
   MOZ_ASSERT(mScopeObject);
@@ -2645,24 +2651,49 @@ WarnIfSandboxIneffective(nsIDocShell* aD
 }
 
 bool
 nsIDocument::IsSynthesized() {
   nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr;
   return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized();
 }
 
+bool
+nsDocument::IsShadowDOMEnabled(JSContext* aCx, JSObject* aGlobal)
+{
+  MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
+  nsCOMPtr<nsPIDOMWindowInner> window = xpc::WindowOrNull(aGlobal);
+
+  nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
+  if (!doc) {
+    return false;
+  }
+
+  return doc->IsShadowDOMEnabled();
+}
+
 // static
 bool
-nsDocument::IsCallerChromeOrAddon(JSContext* aCx, JSObject* aObject)
-{
-  nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
-  return principal &&
-    (nsContentUtils::IsSystemPrincipal(principal) ||
-     principal->GetIsAddonOrExpandedAddonPrincipal());
+nsDocument::IsShadowDOMEnabledAndCallerIsChromeOrAddon(JSContext* aCx,
+                                                       JSObject* aObject)
+{
+  if (IsShadowDOMEnabled(aCx, aObject)) {
+    nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
+    return principal &&
+      (nsContentUtils::IsSystemPrincipal(principal) ||
+       principal->GetIsAddonOrExpandedAddonPrincipal());
+  }
+
+  return false;
+}
+
+bool
+nsDocument::IsShadowDOMEnabled(const nsINode* aNode)
+{
+  return aNode->OwnerDoc()->IsShadowDOMEnabled();
 }
 
 nsresult
 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
                               nsILoadGroup* aLoadGroup,
                               nsISupports* aContainer,
                               nsIStreamListener **aDocListener,
                               bool aReset, nsIContentSink* aSink)
@@ -5738,17 +5769,18 @@ nsIDocument::CreateElement(const nsAStri
   }
 
   const nsString* is = nullptr;
   CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
   if (aOptions.IsElementCreationOptions()) {
     const ElementCreationOptions& options =
       aOptions.GetAsElementCreationOptions();
 
-    if (options.mIs.WasPassed()) {
+    if (CustomElementRegistry::IsCustomElementEnabled(this) &&
+        options.mIs.WasPassed()) {
       is = &options.mIs.Value();
     }
 
     // Check 'pseudo' and throw an exception if it's not one allowed
     // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
     if (options.mPseudo.WasPassed()) {
       pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
       if (rv.Failed() ||
@@ -5782,17 +5814,18 @@ nsIDocument::CreateElementNS(const nsASt
                                             mNodeInfoManager,
                                             ELEMENT_NODE,
                                             getter_AddRefs(nodeInfo));
   if (rv.Failed()) {
     return nullptr;
   }
 
   const nsString* is = nullptr;
-  if (aOptions.IsElementCreationOptions()) {
+  if (CustomElementRegistry::IsCustomElementEnabled(this) &&
+      aOptions.IsElementCreationOptions()) {
     const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions();
     if (options.mIs.WasPassed()) {
       is = &options.mIs.Value();
     }
   }
 
   nsCOMPtr<Element> element;
   rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
@@ -5810,17 +5843,18 @@ nsIDocument::CreateXULElement(const nsAS
                               ErrorResult& aRv)
 {
   aRv = nsContentUtils::CheckQName(aTagName, false);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   const nsString* is = nullptr;
-  if (aOptions.IsElementCreationOptions()) {
+  if (CustomElementRegistry::IsCustomElementEnabled(this) &&
+      aOptions.IsElementCreationOptions()) {
     const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions();
     if (options.mIs.WasPassed()) {
       is = &options.mIs.Value();
     }
   }
 
   RefPtr<Element> elem = CreateElem(aTagName, nullptr, kNameSpaceID_XUL, is);
   if (!elem) {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -191,19 +191,22 @@ public:
   {
     return DocumentOrShadowRoot::GetValueMissingState(aName);
   }
   virtual void SetValueMissingState(const nsAString& aName, bool aValue) override
   {
     return DocumentOrShadowRoot::SetValueMissingState(aName, aValue);
   }
 
+  // Check whether shadow DOM is enabled for aGlobal.
+  static bool IsShadowDOMEnabled(JSContext* aCx, JSObject* aGlobal);
   // Check whether shadow DOM is enabled for the document this node belongs to.
   // Same as above, but also checks that the caller is either chrome or some addon.
-  static bool IsCallerChromeOrAddon(JSContext* aCx, JSObject* aObject);
+  static bool IsShadowDOMEnabledAndCallerIsChromeOrAddon(JSContext* aCx, JSObject* aObject);
+  static bool IsShadowDOMEnabled(const nsINode* aNode);
 
 public:
   using mozilla::dom::DocumentOrShadowRoot::GetElementById;
   using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagName;
   using mozilla::dom::DocumentOrShadowRoot::GetElementsByTagNameNS;
   using mozilla::dom::DocumentOrShadowRoot::GetElementsByClassName;
 
   // EventTarget
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -3514,55 +3514,57 @@ nsFocusManager::GetNextTabbableContent(n
   if (!startContent)
     return NS_OK;
 
   nsIContent* currentTopLevelHost = GetTopLevelHost(aStartContent);
 
   LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
   LOGFOCUSNAVIGATION(("  tabindex: %d", aCurrentTabIndex));
 
-  // If aStartContent is a shadow host or slot in forward navigation,
-  // search in scope owned by aStartContent
-  if (aForward && IsHostOrSlot(aStartContent)) {
-    nsIContent* contentToFocus =
-      GetNextTabbableContentInScope(aStartContent, aStartContent,
-                                    aOriginalStartContent, aForward,
-                                    aForward ? 1 : 0, aIgnoreTabIndex,
-                                    aForDocumentNavigation,
-                                    true /* aSkipOwner */);
-    if (contentToFocus) {
-      NS_ADDREF(*aResultContent = contentToFocus);
-      return NS_OK;
+  if (nsDocument::IsShadowDOMEnabled(aRootContent)) {
+    // If aStartContent is a shadow host or slot in forward navigation,
+    // search in scope owned by aStartContent
+    if (aForward && IsHostOrSlot(aStartContent)) {
+      nsIContent* contentToFocus =
+        GetNextTabbableContentInScope(aStartContent, aStartContent,
+                                      aOriginalStartContent, aForward,
+                                      aForward ? 1 : 0, aIgnoreTabIndex,
+                                      aForDocumentNavigation,
+                                      true /* aSkipOwner */);
+      if (contentToFocus) {
+        NS_ADDREF(*aResultContent = contentToFocus);
+        return NS_OK;
+      }
     }
+
+    // If aStartContent is not in a scope owned by the root element
+    // (i.e. aStartContent is already in shadow DOM),
+    // search from scope including aStartContent
+    nsIContent* rootElement = aRootContent->OwnerDoc()->GetRootElement();
+    nsIContent* owner = FindOwner(aStartContent);
+    if (owner && rootElement != owner) {
+      nsIContent* contentToFocus =
+        GetNextTabbableContentInAncestorScopes(&aStartContent,
+                                               aOriginalStartContent,
+                                               aForward,
+                                               &aCurrentTabIndex,
+                                               aIgnoreTabIndex,
+                                               aForDocumentNavigation);
+      if (contentToFocus) {
+        NS_ADDREF(*aResultContent = contentToFocus);
+        return NS_OK;
+      }
+    }
+
+    // If we reach here, it means no next tabbable content in shadow DOM.
+    // We need to continue searching in light DOM, starting at the shadow host
+    // in light DOM (updated aStartContent) and its tabindex
+    // (updated aCurrentTabIndex).
   }
 
-  // If aStartContent is not in a scope owned by the root element
-  // (i.e. aStartContent is already in shadow DOM),
-  // search from scope including aStartContent
-  nsIContent* rootElement = aRootContent->OwnerDoc()->GetRootElement();
-  nsIContent* owner = FindOwner(aStartContent);
-  if (owner && rootElement != owner) {
-    nsIContent* contentToFocus =
-      GetNextTabbableContentInAncestorScopes(&aStartContent,
-                                             aOriginalStartContent,
-                                             aForward,
-                                             &aCurrentTabIndex,
-                                             aIgnoreTabIndex,
-                                             aForDocumentNavigation);
-    if (contentToFocus) {
-      NS_ADDREF(*aResultContent = contentToFocus);
-      return NS_OK;
-    }
-  }
-
-  // If we reach here, it means no next tabbable content in shadow DOM.
-  // We need to continue searching in light DOM, starting at the shadow host
-  // in light DOM (updated aStartContent) and its tabindex
-  // (updated aCurrentTabIndex).
-
   nsPresContext* presContext = aPresShell->GetPresContext();
 
   bool getNextFrame = true;
   nsCOMPtr<nsIContent> iterStartContent = aStartContent;
   while (1) {
     nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
     // if there is no frame, look for another content node that has a frame
     if (!startFrame) {
@@ -3690,17 +3692,18 @@ nsFocusManager::GetNextTabbableContent(n
 
       // As of now, 2018/04/12, sequential focus navigation is still
       // in the obsolete Shadow DOM specification.
       // http://w3c.github.io/webcomponents/spec/shadow/#sequential-focus-navigation
       // "if ELEMENT is focusable, a shadow host, or a slot element,
       //  append ELEMENT to NAVIGATION-ORDER."
       // and later in "For each element ELEMENT in NAVIGATION-ORDER: "
       // hosts and slots are handled before other elements.
-      if (currentContent && IsHostOrSlot(currentContent)) {
+      if (currentContent && nsDocument::IsShadowDOMEnabled(currentContent) &&
+          IsHostOrSlot(currentContent)) {
         bool focusableHostSlot;
         int32_t tabIndex = HostOrSlotTabIndexValue(currentContent,
                                                    &focusableHostSlot);
         // Host or slot itself isn't focusable, enter its scope.
         if (!focusableHostSlot &&
             tabIndex >= 0 &&
             (aIgnoreTabIndex || aCurrentTabIndex == tabIndex)) {
           nsIContent* contentToFocus =
@@ -3790,30 +3793,32 @@ nsFocusManager::GetNextTabbableContent(n
             // Also, when going backwards, check to ensure that the focus
             // wouldn't be redirected. Otherwise, for example, when an input in
             // a textbox is focused, the enclosing textbox would be found and
             // the same inner input would be returned again.
             else if (currentContent == aRootContent ||
                      (currentContent != startContent &&
                       (aForward || !GetRedirectedFocus(currentContent)))) {
 
-              // If currentContent is a shadow host in backward
-              // navigation, search in scope owned by currentContent
-              if (!aForward && currentContent->GetShadowRoot()) {
-                nsIContent* contentToFocus =
-                  GetNextTabbableContentInScope(currentContent,
-                                                currentContent,
-                                                aOriginalStartContent,
-                                                aForward, aForward ? 1 : 0,
-                                                aIgnoreTabIndex,
-                                                aForDocumentNavigation,
-                                                true /* aSkipOwner */);
-                if (contentToFocus) {
-                  NS_ADDREF(*aResultContent = contentToFocus);
-                  return NS_OK;
+              if (nsDocument::IsShadowDOMEnabled(aRootContent)) {
+                // If currentContent is a shadow host in backward
+                // navigation, search in scope owned by currentContent
+                if (!aForward && currentContent->GetShadowRoot()) {
+                  nsIContent* contentToFocus =
+                    GetNextTabbableContentInScope(currentContent,
+                                                  currentContent,
+                                                  aOriginalStartContent,
+                                                  aForward, aForward ? 1 : 0,
+                                                  aIgnoreTabIndex,
+                                                  aForDocumentNavigation,
+                                                  true /* aSkipOwner */);
+                  if (contentToFocus) {
+                    NS_ADDREF(*aResultContent = contentToFocus);
+                    return NS_OK;
+                  }
                 }
               }
 
               NS_ADDREF(*aResultContent = currentContent);
               return NS_OK;
             }
           }
         }
@@ -3985,17 +3990,17 @@ nsFocusManager::GetNextTabIndex(nsIConte
 
   if (aForward) {
     tabIndex = 0;
     for (nsIContent* child = iter.GetNextChild();
          child;
          child = iter.GetNextChild()) {
       // Skip child's descendants if child is a shadow host or slot, as they are
       // in the focus navigation scope owned by child's shadow root
-      if (!IsHostOrSlot(child)) {
+      if (!(nsDocument::IsShadowDOMEnabled(aParent) && IsHostOrSlot(child))) {
         childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
         if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
           tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
         }
       }
 
       nsAutoString tabIndexStr;
       if (child->IsElement()) {
@@ -4010,17 +4015,17 @@ nsFocusManager::GetNextTabIndex(nsIConte
   }
   else { /* !aForward */
     tabIndex = 1;
     for (nsIContent* child = iter.GetNextChild();
          child;
          child = iter.GetNextChild()) {
       // Skip child's descendants if child is a shadow host or slot, as they are
       // in the focus navigation scope owned by child's shadow root
-      if (!IsHostOrSlot(child)) {
+      if (!(nsDocument::IsShadowDOMEnabled(aParent) && IsHostOrSlot(child))) {
         childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
         if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
             (childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
           tabIndex = childTabIndex;
         }
       }
 
       nsAutoString tabIndexStr;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3794,16 +3794,21 @@ public:
   void SetAllowPaymentRequest(bool aAllowPaymentRequest)
   {
     mAllowPaymentRequest = aAllowPaymentRequest;
   }
 
   mozilla::dom::FeaturePolicy*
   Policy() const;
 
+  bool IsShadowDOMEnabled() const
+  {
+    return mIsShadowDOMEnabled;
+  }
+
   bool ModuleScriptsEnabled();
 
   /**
    * Find the (non-anonymous) content in this document for aFrame. It will
    * be aFrame's content node if that content is in this document and not
    * anonymous. Otherwise, when aFrame is in a subdocument, we use the frame
    * element containing the subdocument containing aFrame, and/or find the
    * nearest non-anonymous ancestor in this document.
@@ -4261,16 +4266,20 @@ protected:
   bool mDidCallBeginLoad : 1;
 
   // True if the document is allowed to use PaymentRequest.
   bool mAllowPaymentRequest : 1;
 
   // True if the encoding menu should be disabled.
   bool mEncodingMenuDisabled : 1;
 
+  // True if dom.webcomponents.shadowdom.enabled pref is set when document is
+  // created.
+  bool mIsShadowDOMEnabled : 1;
+
   // True if this document is for an SVG-in-OpenType font.
   bool mIsSVGGlyphsDocument : 1;
 
   // True if the document is being destroyed.
   bool mInDestructor: 1;
 
   // True if the document has been detached from its content viewer.
   bool mIsGoingAway: 1;
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -430,17 +430,18 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
   nsCOMPtr<nsINode> clone;
   if (aClone) {
     nsresult rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aError.Throw(rv);
       return nullptr;
     }
 
-    if (clone->IsHTMLElement() || clone->IsXULElement()) {
+    if (CustomElementRegistry::IsCustomElementEnabled(nodeInfo->GetDocument()) &&
+        (clone->IsHTMLElement() || clone->IsXULElement())) {
       // The cloned node may be a custom element that may require
       // enqueing upgrade reaction.
       Element* cloneElem = clone->AsElement();
       CustomElementData* data = elem->GetCustomElementData();
       RefPtr<nsAtom> typeAtom = data ? data->GetCustomElementType() : nullptr;
 
       if (typeAtom) {
         cloneElem->SetCustomElementData(new CustomElementData(typeAtom));
@@ -484,17 +485,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
 
     aNode->mNodeInfo.swap(newNodeInfo);
     if (elem) {
       elem->NodeInfoChanged(oldDoc);
     }
 
     nsIDocument* newDoc = aNode->OwnerDoc();
     if (newDoc) {
-      if (elem) {
+      if (elem && CustomElementRegistry::IsCustomElementEnabled(newDoc)) {
         // Adopted callback must be enqueued whenever a node’s
         // shadow-including inclusive descendants that is custom.
         CustomElementData* data = elem->GetCustomElementData();
         if (data && data->mState == CustomElementData::State::eCustom) {
           LifecycleAdoptedCallbackArgs args = {
             oldDoc,
             newDoc
           };
--- a/dom/base/nsTextNode.cpp
+++ b/dom/base/nsTextNode.cpp
@@ -146,16 +146,22 @@ nsTextNode::BindToTree(nsIDocument* aDoc
 
 void nsTextNode::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   ResetDirectionSetByTextNode(this);
 
   CharacterData::UnbindFromTree(aDeep, aNullParent);
 }
 
+bool
+nsTextNode::IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject)
+{
+  return nsDocument::IsShadowDOMEnabled(aCx, aObject);
+}
+
 #ifdef DEBUG
 void
 nsTextNode::List(FILE* out, int32_t aIndent) const
 {
   int32_t index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
--- a/dom/base/nsTextNode.h
+++ b/dom/base/nsTextNode.h
@@ -55,16 +55,20 @@ public:
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent) override;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) override;
 
   nsresult AppendTextForNormalize(const char16_t* aBuffer, uint32_t aLength,
                                   bool aNotify, nsIContent* aNextSibling);
 
+  // Need to have a copy here because including nsDocument.h in this file will
+  // fail to build on Windows.
+  static bool IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject);
+
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const override;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
 #endif
 
 protected:
   virtual ~nsTextNode();
 
--- a/dom/base/test/test_bug1025933.html
+++ b/dom/base/test/test_bug1025933.html
@@ -10,29 +10,35 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1025933 **/
 
   SimpleTest.waitForExplicitFinish();
 
   function test() {
-    var iframe = document.createElement('iframe');
-    iframe.srcdoc = '<div id="content"> <div id="host"></div </div>';
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.shadowdom.enabled", true]
+      ]
+    }, function() {
+      var iframe = document.createElement('iframe');
+      iframe.srcdoc = '<div id="content"> <div id="host"></div </div>';
 
-    iframe.onload = function() {
-      var s = iframe.contentDocument.getElementById("host").attachShadow({mode: 'open'});
-      s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
-      var el = s.firstElementChild;
-      is(el.clientWidth, 100);
-      is(el.clientHeight, 100);
-      SimpleTest.finish();
-    }
+      iframe.onload = function() {
+        var s = iframe.contentDocument.getElementById("host").attachShadow({mode: 'open'});
+        s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
+        var el = s.firstElementChild;
+        is(el.clientWidth, 100);
+        is(el.clientHeight, 100);
+        SimpleTest.finish();
+      }
 
-    document.body.appendChild(iframe);
+      document.body.appendChild(iframe);
+    });
   }
 
   </script>
 </head>
 <body onload="test()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1025933">Mozilla Bug 1025933</a>
 <p id="display"></p>
 <pre id="test">
--- a/dom/base/test/test_bug1037687.html
+++ b/dom/base/test/test_bug1037687.html
@@ -15,18 +15,22 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="application/javascript">
   /** Test for Bug 1037687 **/
   SimpleTest.waitForExplicitFinish();
-  window.onload = function() {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.shadowdom.enabled", true]
+    ]
+  }, function() {
     // This test loads in an iframe, to ensure that the element instance is
     // loaded with the correct value of the preference.
     let iframe = document.createElement("iframe");
     iframe.src = "test_bug1037687_subframe.html";
     document.body.appendChild(iframe);
-  };
+  });
 </script>
 </body>
 </html>
--- a/dom/base/test/test_bug1100912.html
+++ b/dom/base/test/test_bug1100912.html
@@ -8,27 +8,37 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 1100912</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
   /** Test for Bug 1100912 **/
 
   SimpleTest.waitForExplicitFinish();
 
+  function init() {
+    SpecialPowers.pushPrefEnv(
+      {
+        "set": [["dom.webcomponents.shadowdom.enabled", true]]
+      },
+      runTests);
+  }
+
   function runTests() {
     win = window.open("file_bug1100912.html", "");
   }
 
   function didRunTests() {
     setTimeout("SimpleTest.finish()");
   }
 
+  SimpleTest.waitForFocus(init);
+
   </script>
 </head>
-<body onload="SimpleTest.waitForFocus(runTests)">
+<body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100912">Mozilla Bug 1100912</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 </body>
--- a/dom/base/test/test_bug1421568.html
+++ b/dom/base/test/test_bug1421568.html
@@ -10,18 +10,23 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript"><!--
 
   /** Test for Bug 1421568 **/
 
   SimpleTest.waitForExplicitFinish();
 
   function init() {
-    document.getElementById("content").innerHTML =
-      "<iframe src='about:blank' onload='test(this)'></iframe>";
+    SpecialPowers.pushPrefEnv({
+      set: [["dom.webcomponents.shadowdom.enabled", true]]
+      },
+      function () {
+        document.getElementById("content").innerHTML =
+          "<iframe src='about:blank' onload='test(this)'></iframe>";
+      });
   }
 
   function test(iframe) {
     var d = iframe.contentDocument;
     d.body.innerHTML = "<div>";
     var div = d.body.firstChild;
     var sr = div.attachShadow({mode: "closed"});
     is(sr.mode, "closed", "Shadow root should be closed.");
--- a/dom/base/test/test_bug1453693.html
+++ b/dom/base/test/test_bug1453693.html
@@ -9,24 +9,32 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1453693 **/
 
   SimpleTest.waitForExplicitFinish();
 
+  function init() {
+    SpecialPowers.pushPrefEnv(
+      {
+        "set": [["dom.webcomponents.shadowdom.enabled", true]]
+      },
+      runTests);
+  }
+
   function runTests() {
     win = window.open("file_bug1453693.html", "", "width=300, height=300");
   }
 
   function didRunTests() {
     setTimeout("SimpleTest.finish()");
   }
 
-  ;
+  SimpleTest.waitForFocus(init);
 
   </script>
 </head>
-<body onload="SimpleTest.waitForFocus(runTests);">
+<body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1453693">Mozilla Bug 1453693</a>
 </body>
 </html>
--- a/dom/base/test/test_bug1472427.html
+++ b/dom/base/test/test_bug1472427.html
@@ -72,17 +72,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     expectedTarget = img;
     synthesizeMouse(img, 50, 50, {}, ifr.contentWindow);
     ok(gotClick, "Should have got a click event.");
     SimpleTest.finish();
   }
 
   </script>
 </head>
-<body onload="initPage()">
+<body onload="SpecialPowers.pushPrefEnv({'set':[['dom.webcomponents.shadowdom.enabled', true]]}, initPage);">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1472427">Mozilla Bug 1472427</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 </body>
--- a/dom/base/test/test_find.html
+++ b/dom/base/test/test_find.html
@@ -108,16 +108,18 @@ let runTests = t.step_func_done(function
   testFindable(true, "Shadow text", function(document) {
     let div = document.createElement("div");
     div.appendChild(document.createTextNode("text, yay!"));
     div.attachShadow({ mode: "open" }).innerHTML = `This is Shadow <slot></slot>`;
     document.documentElement.appendChild(div);
   }, "Mixed shadow and non-shadow text");
 });
 
-window.onload = function() {
-  let iframe = document.createElement("iframe");
-  iframe.onload = runTests;
-  iframe.srcdoc = "<!doctype html><html></html>";
-  document.body.appendChild(iframe);
-};
+SpecialPowers.pushPrefEnv(
+  {"set":[['dom.webcomponents.shadowdom.enabled', true]]},
+  t.step_func(function() {
+    let iframe = document.createElement("iframe");
+    iframe.onload = runTests;
+    iframe.srcdoc = "<!doctype html><html></html>";
+    document.body.appendChild(iframe);
+  }));
 </script>
 </body>
--- a/dom/base/test/test_range_bounds.html
+++ b/dom/base/test/test_range_bounds.html
@@ -113,56 +113,56 @@ function doTest(){
     spanInFirstDivRect = spanInFirstDiv.getBoundingClientRect(),
     secondPRect = secondP.getBoundingClientRect(),
     secondDivRect = secondDiv.getBoundingClientRect(),
     spanInSecondPRect = spanInSecondP.getBoundingClientRect(),
     spanInSecondDivRect = spanInSecondDiv.getBoundingClientRect(),
     spanInSecondDivRectList = spanInSecondDiv.getClientRects();
   var widthPerchar = spanInSecondPRect.width / spanInSecondP.firstChild.length;
   var testcases = [
-    {name:'nodesNotInDocument', range:[document.createTextNode('abc'), 1],
+    {name:'nodesNotInDocument', range:[document.createTextNode('abc'), 1], 
       rect:null},
     {name:'collapsedInBlockNode', range:[firstP, 2], rect:null},
     {name:'collapsedAtBeginningOfTextNode', range:[firstP.firstChild, 0],
-      rect:[spanInFirstPRect.left - 6 * widthPerchar,
-      spanInFirstPRect.left - 6 * widthPerchar, spanInFirstPRect.top,
+      rect:[spanInFirstPRect.left - 6 * widthPerchar, 
+      spanInFirstPRect.left - 6 * widthPerchar, spanInFirstPRect.top, 
       spanInFirstPRect.bottom, 0, spanInFirstPRect.height]},
-    {name:'collapsedWithinTextNode', range:[firstP.firstChild, 1],
-      rect:[spanInFirstPRect.left  - 5 * widthPerchar,
+    {name:'collapsedWithinTextNode', range:[firstP.firstChild, 1], 
+      rect:[spanInFirstPRect.left  - 5 * widthPerchar, 
         spanInFirstPRect.left  - 5 * widthPerchar,
         spanInFirstPRect.top, spanInFirstPRect.bottom, 0, spanInFirstPRect.height]},
-    {name:'collapsedAtEndOfTextNode', range:[firstP.firstChild, 6],
+    {name:'collapsedAtEndOfTextNode', range:[firstP.firstChild, 6], 
       rect:[spanInFirstPRect.left, spanInFirstPRect.left,
         spanInFirstPRect.top, spanInFirstPRect.bottom, 0, spanInFirstPRect.height]},
     {name:'singleBlockNode', range:[root, 1, root, 2], rect:firstPRect},
     {name:'twoBlockNodes', range:[root, 1, root, 3],
       rect:[firstPRect.left, firstPRect.right, firstPRect.top,
         firstDivRect.bottom, firstPRect.width,
         firstDivRect.bottom - firstPRect.top],
       rectList:[firstPRect, firstDivRect]},
     {name:'endOfTextNodeToEndOfAnotherTextNodeInAnotherBlock',
       range:[spanInFirstP.firstChild, 1, firstDiv.firstChild, 5],
       rect:[spanInFirstDivRect.left - 5*widthPerchar, spanInFirstDivRect.left,
-        spanInFirstDivRect.top, spanInFirstDivRect.bottom, 5 * widthPerchar,
+        spanInFirstDivRect.top, spanInFirstDivRect.bottom, 5 * widthPerchar, 
         spanInFirstDivRect.height]},
-    {name:'startOfTextNodeToStartOfAnotherTextNodeInAnotherBlock',
+    {name:'startOfTextNodeToStartOfAnotherTextNodeInAnotherBlock', 
       range:[spanInFirstP.firstChild, 0, firstDiv.firstChild, 0],
       rect:[spanInFirstPRect.left, spanInFirstPRect.left + widthPerchar, spanInFirstPRect.top,
         spanInFirstPRect.bottom, widthPerchar, spanInFirstPRect.height]},
-    {name:'endPortionOfATextNode', range:[firstP.firstChild, 3,
+    {name:'endPortionOfATextNode', range:[firstP.firstChild, 3, 
         firstP.firstChild, 6],
       rect:[spanInFirstPRect.left - 3*widthPerchar, spanInFirstPRect.left,
         spanInFirstPRect.top, spanInFirstPRect.bottom, 3*widthPerchar, spanInFirstPRect.height]},
-    {name:'startPortionOfATextNode', range:[firstP.firstChild, 0,
+    {name:'startPortionOfATextNode', range:[firstP.firstChild, 0, 
         firstP.firstChild, 3],
-      rect:[spanInFirstPRect.left - 6*widthPerchar,
+      rect:[spanInFirstPRect.left - 6*widthPerchar, 
         spanInFirstPRect.left - 3*widthPerchar, spanInFirstPRect.top,
         spanInFirstPRect.bottom, 3 * widthPerchar, spanInFirstPRect.height]},
     {name:'spanTextNodes', range:[secondP.firstChild, 1, secondP.lastChild, 1],
-      rect:[spanInSecondPRect.left - 3*widthPerchar, spanInSecondPRect.right +
+      rect:[spanInSecondPRect.left - 3*widthPerchar, spanInSecondPRect.right + 
         widthPerchar, spanInSecondPRect.top, spanInSecondPRect.bottom,
         spanInSecondPRect.width + 4*widthPerchar, spanInSecondPRect.height],
       rectList:[[spanInSecondPRect.left - 3*widthPerchar, spanInSecondPRect.left,
         spanInSecondPRect.top, spanInSecondPRect.bottom, 3 * widthPerchar,
         spanInSecondPRect.height],
 	spanInSecondPRect,
 	[spanInSecondPRect.right, spanInSecondPRect.right + widthPerchar,
           spanInSecondPRect.top, spanInSecondPRect.bottom, widthPerchar,
@@ -178,22 +178,22 @@ function doTest(){
        rect: spanInSecondDivRect,
        rectList:[[spanInSecondDivRectList[0].left+widthPerchar,
         spanInSecondDivRectList[0].right, spanInSecondDivRectList[0].top,
 	spanInSecondDivRectList[0].bottom, spanInSecondDivRectList[0].width - widthPerchar,
 	spanInSecondDivRectList[0].height],
 	spanInSecondDivRectList[1],
 	[spanInSecondDivRectList[2].left,
 	spanInSecondDivRectList[2].right - 4 * widthPerchar, spanInSecondDivRectList[2].top,
-	spanInSecondDivRectList[2].bottom,
+	spanInSecondDivRectList[2].bottom, 
 	spanInSecondDivRectList[2].width - 4 * widthPerchar,
 	spanInSecondDivRectList[2].height]]},
       {name:'textAcrossLines',range:[thirdDiv.firstChild, 13, thirdDiv.firstChild, 28],
         rect: [spanInSecondDivRectList[1].left, spanInSecondDivRectList[1].right,
-          spanInSecondDivRectList[1].top + secondDivRect.height,
+          spanInSecondDivRectList[1].top + secondDivRect.height, 
           spanInSecondDivRectList[1].bottom + secondDivRect.height,
           spanInSecondDivRectList[1].width, spanInSecondDivRectList[1].height]}
     ];
   } else {
     directionDependentTestcases = [
       {name:'spanAcrossLines',range:[spanInSecondDiv.firstChild, 1, spanInSecondDiv.firstChild, 30],
        rect: spanInSecondDivRect,
        rectList:[[spanInSecondDivRectList[0].left+widthPerchar,
@@ -201,30 +201,30 @@ function doTest(){
 	spanInSecondDivRectList[0].bottom, spanInSecondDivRectList[0].width - widthPerchar,
 	spanInSecondDivRectList[0].height],
 	spanInSecondDivRectList[1],
 	spanInSecondDivRectList[2],
 	spanInSecondDivRectList[3],
 	[spanInSecondDivRectList[4].left,
 	spanInSecondDivRectList[4].right - 4 * widthPerchar,
         spanInSecondDivRectList[4].top,
-	spanInSecondDivRectList[4].bottom,
+	spanInSecondDivRectList[4].bottom, 
 	spanInSecondDivRectList[4].width - 4 * widthPerchar,
 	spanInSecondDivRectList[4].height]]},
       {name:'textAcrossLines',range:[thirdDiv.firstChild, 13, thirdDiv.firstChild, 28],
         rect: [spanInSecondDivRectList[2].left, spanInSecondDivRectList[2].right,
-          spanInSecondDivRectList[2].top + secondDivRect.height,
+          spanInSecondDivRectList[2].top + secondDivRect.height, 
           spanInSecondDivRectList[2].bottom + secondDivRect.height,
 	       spanInSecondDivRectList[2].width, spanInSecondDivRectList[2].height],
        rectList:[[spanInSecondDivRectList[2].left, spanInSecondDivRectList[2].right,
-          spanInSecondDivRectList[2].top + secondDivRect.height,
+          spanInSecondDivRectList[2].top + secondDivRect.height, 
           spanInSecondDivRectList[2].bottom + secondDivRect.height,
           spanInSecondDivRectList[2].width, spanInSecondDivRectList[2].height],
           [spanInSecondDivRectList[2].left, spanInSecondDivRectList[2].left,
-          spanInSecondDivRectList[2].top + secondDivRect.height,
+          spanInSecondDivRectList[2].top + secondDivRect.height, 
           spanInSecondDivRectList[2].bottom + secondDivRect.height,
           0, spanInSecondDivRectList[2].height]]}
      ];
   }
   directionDependentTestcases.forEach(runATest);
 }
 function testMixedDir(){
   var root = document.getElementById('mixeddir');
@@ -234,17 +234,17 @@ function testMixedDir(){
              rect: firstSpanRect, rectList:firstSpanRectList});
 
   root = document.getElementById('mixeddir2');
   firstSpan = root.firstElementChild;
   firstSpanRect = firstSpan.getBoundingClientRect();
   bdo = document.getElementById('bdo2');
   bdoRect=bdo.getBoundingClientRect();
   var widthPerChar = bdoRect.width / bdo.firstChild.length;
-  runATest({name:'mixeddirPartial', range:[firstSpan.firstChild, 3,
+  runATest({name:'mixeddirPartial', range:[firstSpan.firstChild, 3, 
 					   bdo.firstChild, 7],
 	rect: [firstSpanRect.left + 3*widthPerChar, bdoRect.right,
 	       bdoRect.top, bdoRect.bottom,
 	       (firstSpan.firstChild.length + bdo.firstChild.length - 3) *
 	        widthPerChar,
 	       bdoRect.height],
 	rectList:[[firstSpanRect.left + 3*widthPerChar,
 		   bdoRect.left,
@@ -285,18 +285,21 @@ function test(){
 
   testMixedDir();
 
   //test transforms
   isTransformed = true;
   root.style.transform = "translate(30px,50px)";
   doTest();
 
-  testShadowDOM();
-  SimpleTest.finish();
+  SpecialPowers.pushPrefEnv({"set":[["dom.webcomponents.shadowdom.enabled", true]]},
+                            () => {
+                              testShadowDOM();
+                              SimpleTest.finish();
+                            });
 }
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
   setTimeout(test, 0);
 };
 
 </script>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7798,24 +7798,27 @@ class CGPerSignatureCall(CGThing):
                     for arg, argname in self.getArguments())
 
             cgThings.append(
                 CGIfWrapper(CGList(xraySteps),
                             "objIsXray"))
 
         if (idlNode.getExtendedAttribute('CEReactions') is not None and
             not getter):
-            cgThings.append(CGGeneric(dedent(
+            cgThings.append(CGGeneric(fill(
                 """
                 Maybe<AutoCEReaction> ceReaction;
-                DocGroup* docGroup = self->GetDocGroup();
-                if (docGroup) {
-                  ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
-                }
-                """)))
+                if (CustomElementRegistry::IsCustomElementEnabled(cx, ${obj})) {
+                  DocGroup* docGroup = self->GetDocGroup();
+                  if (docGroup) {
+                    ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
+                  }
+                }
+                """,
+                obj=objectName)))
 
         # If this is a method that was generated by a maplike/setlike
         # interface, use the maplike/setlike generator to fill in the body.
         # Otherwise, use CGCallGenerator to call the native method.
         if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
             if (idlNode.maplikeOrSetlikeOrIterable.isMaplike() or
                 idlNode.maplikeOrSetlikeOrIterable.isSetlike()):
                 cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
--- a/dom/events/test/test_bug1079236.html
+++ b/dom/events/test/test_bug1079236.html
@@ -43,19 +43,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     }
 
     var r = file.getBoundingClientRect();
     synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"}, iframe.contentWindow);
     iframe.contentDocument.body.onmousemove = null;
   }
 
   SimpleTest.waitForExplicitFinish();
-  window.onload = () => {
-    SimpleTest.waitForFocus(runTests);
-  };
+  SimpleTest.waitForFocus(() => {
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.shadowdom.enabled", true]
+      ]
+    }, runTests);
+  });
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1079236">Mozilla Bug 1079236</a>
 <p id="display"></p>
 <div id="content">
 
--- a/dom/events/test/test_bug1145910.html
+++ b/dom/events/test/test_bug1145910.html
@@ -40,15 +40,19 @@ function runTests() {
 
   is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
   is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
 
   SimpleTest.finish();
 };
 
 SimpleTest.waitForExplicitFinish();
-window.onload = () => {
-  SimpleTest.waitForFocus(runTests);
-};
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.shadowdom.enabled", true]
+    ]
+  }, runTests);
+});
 
 </script>
 </body>
 </html>
--- a/dom/events/test/test_bug1150308.html
+++ b/dom/events/test/test_bug1150308.html
@@ -36,14 +36,18 @@ function runTests() {
   synthesizeMouseAtCenter(distributed, { type: "mouseup" }, iframeWin);
 
   is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
 
   SimpleTest.finish();
 };
 
 SimpleTest.waitForExplicitFinish();
-window.onload = () => {
-  SimpleTest.waitForFocus(runTests);
-};
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.shadowdom.enabled", true]
+    ]
+  }, runTests);
+});
 </script>
 </body>
 </html>
--- a/dom/events/test/test_bug1264380.html
+++ b/dom/events/test/test_bug1264380.html
@@ -44,17 +44,21 @@ function runTests()
   ok(dragService.getCurrentSession(), "Drag session is available.");
   dragService.endDragSession(false);
   ok(!dragService.getCurrentSession(), "There shouldn't be a drag session anymore!");
   SimpleTest.finish();
 }
 
 
 SimpleTest.waitForExplicitFinish();
-window.onload = () => {
-  SimpleTest.waitForFocus(runTests);
-};
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.shadowdom.enabled", true]
+    ]
+  }, runTests);
+});
 
 </script>
 
 <body>
 </body>
 </html>
--- a/dom/events/test/test_bug1429572.html
+++ b/dom/events/test/test_bug1429572.html
@@ -10,17 +10,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1429572 **/
   SimpleTest.waitForExplicitFinish();
 
   var win;
   function start() {
-    SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", 1]]},
+    SpecialPowers.pushPrefEnv({"set": [["dom.webcomponents.shadowdom.enabled", true],
+                                       ["dom.w3c_touch_events.enabled", 1]]},
     function() {
       ok(true, "Starting the test.");
       win = window.open("window_bug1429572.html", "testwindow",
                         "width=" + window.screen.width +
                         ",height=" + window.screen.height);
     });
   }
 
--- a/dom/events/test/test_bug1446834.html
+++ b/dom/events/test/test_bug1446834.html
@@ -10,17 +10,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1446834 **/
 
   SimpleTest.waitForExplicitFinish();
 
   window.onload = function() {
-    document.getElementById("iframe").src = "file_bug1446834.html";
+    SpecialPowers.pushPrefEnv({"set": [["dom.webcomponents.shadowdom.enabled", true]]},
+      function() {
+        document.getElementById("iframe").src = "file_bug1446834.html";
+      });
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1446834">Mozilla Bug 1446834</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/dom/events/test/test_bug1484371.html
+++ b/dom/events/test/test_bug1484371.html
@@ -10,17 +10,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1484371 **/
 
   SimpleTest.waitForExplicitFinish();
 
   window.onload = function() {
-    document.getElementById("iframe").src = "file_bug1484371.html";
+    SpecialPowers.pushPrefEnv({"set": [["dom.webcomponents.shadowdom.enabled", true]]},
+      function() {
+        document.getElementById("iframe").src = "file_bug1484371.html";
+      });
   }
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1484371">Mozilla Bug 1484371</a>
 <iframe id="iframe"></iframe>
 </body>
--- a/dom/events/test/test_slotted_mouse_event.html
+++ b/dom/events/test/test_slotted_mouse_event.html
@@ -50,12 +50,16 @@ function frameLoaded(iframe) {
     }, 0);
   });
 
   synthesizeMouseAtCenter(host, { type: "mousemove" });
   synthesizeMouseAtCenter(target, { type: "mousemove" });
 }
 
 SimpleTest.waitForExplicitFinish();
-window.onload = () => {
-  SimpleTest.waitForFocus(runTests);
-};
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.shadowdom.enabled", true]
+    ]
+  }, runTests);
+});
 </script>
--- a/dom/events/test/test_slotted_text_click.html
+++ b/dom/events/test/test_slotted_text_click.html
@@ -61,12 +61,16 @@ function frameLoaded(iframe) {
     requestAnimationFrame(() => {
       synthesizeMouseAtPoint(150, 150, { type: "mousedown" });
       synthesizeMouseAtPoint(150, 150, { type: "mouseup" });
     });
   });
 }
 
 SimpleTest.waitForExplicitFinish();
-window.onload = () => {
-  SimpleTest.waitForFocus(runTests);
-};
+SimpleTest.waitForFocus(() => {
+  SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.webcomponents.shadowdom.enabled", true]
+    ]
+  }, runTests);
+});
 </script>
--- a/dom/html/HTMLSlotElement.cpp
+++ b/dom/html/HTMLSlotElement.cpp
@@ -12,17 +12,21 @@
 #include "nsGkAtoms.h"
 #include "nsDocument.h"
 
 nsGenericHTMLElement*
 NS_NewHTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                       mozilla::dom::FromParser aFromParser)
 {
   RefPtr<mozilla::dom::NodeInfo> nodeInfo(std::move(aNodeInfo));
-  return new mozilla::dom::HTMLSlotElement(nodeInfo.forget());
+  if (nsDocument::IsShadowDOMEnabled(nodeInfo->GetDocument())) {
+    return new mozilla::dom::HTMLSlotElement(nodeInfo.forget());
+  }
+
+  return new mozilla::dom::HTMLUnknownElement(nodeInfo.forget());
 }
 
 namespace mozilla {
 namespace dom {
 
 HTMLSlotElement::HTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
   : nsGenericHTMLElement(std::move(aNodeInfo))
 {
--- a/dom/html/test/test_bug1472426.html
+++ b/dom/html/test/test_bug1472426.html
@@ -94,17 +94,20 @@ https://bugzilla.mozilla.org/show_bug.cg
        { name: "textarea", value: "textareavalue" }
       ];
     checkMPSubmission(submission, expected, "form submission inside shadow DOM");
     SimpleTest.finish();
   }
 
   window.onload = function() {
     SimpleTest.waitForExplicitFinish();
-    testFormSubmissionInShadowDOM();
+    SpecialPowers.pushPrefEnv({"set":[["dom.webcomponents.shadowdom.enabled", true]]},
+                              () => {
+                                testFormSubmissionInShadowDOM();
+                              });
   }
 
   </script>
   <template id="template">
     <form action="form_submit_server.sjs" target="target_iframe" id="form"
           method="POST" enctype="multipart/form-data">
       <input name="text" value="textvalue">
       <input name="hidden" value="hiddenvalue" type="hidden">
--- a/dom/html/test/test_fullscreen-api.html
+++ b/dom/html/test/test_fullscreen-api.html
@@ -169,16 +169,17 @@ is(window.fullScreen, false, "Shouldn't 
 // to write
 addLoadEvent(function() {
   SpecialPowers.pushPrefEnv({
       "set": [
         ["full-screen-api.enabled", true],
         ["full-screen-api.unprefix.enabled", true],
         ["full-screen-api.allow-trusted-requests-only", false],
         ["full-screen-api.transition-duration.enter", "0 0"],
-        ["full-screen-api.transition-duration.leave", "0 0"]
+        ["full-screen-api.transition-duration.leave", "0 0"],
+        ["dom.webcomponents.shadowdom.enabled", true]
       ]}, nextTest);
 });
 SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/pointerlock/test_pointerlock-api.html
+++ b/dom/tests/mochitest/pointerlock/test_pointerlock-api.html
@@ -28,17 +28,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 
         SimpleTest.waitForExplicitFinish();
 
         SpecialPowers.pushPrefEnv({"set": [
           ["full-screen-api.enabled", true],
           ["full-screen-api.unprefix.enabled", true],
           ["full-screen-api.allow-trusted-requests-only", false],
           ["full-screen-api.transition-duration.enter", "0 0"],
-          ["full-screen-api.transition-duration.leave", "0 0"]
+          ["full-screen-api.transition-duration.leave", "0 0"],
+          ["dom.webcomponents.shadowdom.enabled", true]
         ]}, nextTest);
 
         // Run the tests which go full-screen in new window, as Mochitests
         // normally run in an iframe, which by default will not have the
         // allowfullscreen attribute set, so full-screen won't work.
         var gTestFiles = [
           "file_screenClientXYConst.html",
           "file_childIframe.html",
--- a/dom/tests/mochitest/webcomponents/chrome.ini
+++ b/dom/tests/mochitest/webcomponents/chrome.ini
@@ -2,16 +2,12 @@
 support-files =
   dummy_page.html
 
 [test_custom_element_htmlconstructor_chrome.html]
 skip-if = os == 'android' # bug 1323645
 support-files =
   htmlconstructor_autonomous_tests.js
   htmlconstructor_builtin_tests.js
-[test_custom_element_namespace.html]
-[test_custom_element_namespace.xhtml]
-[test_custom_element_namespace.xul]
 [test_custom_element_upgrade_chrome.html]
 support-files =
   test_upgrade_page.html
   upgrade_tests.js
-[test_xul_custom_element.xul]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/chrome_disabled.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+prefs =
+  dom.webcomponents.customelements.enabled=false
+
+[test_xul_custom_element.xul]
+[test_custom_element_namespace.html]
+[test_custom_element_namespace.xhtml]
+[test_custom_element_namespace.xul]
--- a/dom/tests/mochitest/webcomponents/head.js
+++ b/dom/tests/mochitest/webcomponents/head.js
@@ -1,22 +1,30 @@
 /* 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";
 
 /**
- * Loads an iframe.
+ * Set dom.webcomponents.shadowdom.enabled pref to true and loads an iframe, to
+ * ensure that the Element instance is loaded with the correct value of the
+ * preference.
  *
  * @return {Promise} promise that resolves when iframe is loaded.
  */
-function createIframe(aSrcDoc) {
+function setShadowDOMPrefAndCreateIframe(aSrcDoc) {
   return new Promise(function (aResolve, aReject) {
-    let iframe = document.createElement("iframe");
-    iframe.onload = function () { aResolve(iframe.contentDocument); }
-    iframe.onerror = function () { aReject('Failed to load iframe'); }
-    if (aSrcDoc) {
-      iframe.srcdoc = aSrcDoc;
-    }
-    document.body.appendChild(iframe);
+    SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.webcomponents.shadowdom.enabled", true]
+      ]
+    }, () => {
+      let iframe = document.createElement("iframe");
+      iframe.onload = function () { aResolve(iframe.contentDocument); }
+      iframe.onerror = function () { aReject('Failed to load iframe'); }
+      if (aSrcDoc) {
+        iframe.srcdoc = aSrcDoc;
+      }
+      document.body.appendChild(iframe);
+    });
   });
 }
--- a/dom/tests/mochitest/webcomponents/test_bug1269155.html
+++ b/dom/tests/mochitest/webcomponents/test_bug1269155.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 1269155 **/
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="content" style="display: none"> </div>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
     var host = aDocument.querySelector('#content');
     var root = host.attachShadow({mode: "open"});
 
     var header1 = aDocument.createElement('h1');
     header1.textContent = 'Shadow Header1';
 
     var paragraph1 = aDocument.createElement('p');
--- a/dom/tests/mochitest/webcomponents/test_bug1276240.html
+++ b/dom/tests/mochitest/webcomponents/test_bug1276240.html
@@ -31,14 +31,25 @@ function test() {
 
   e = document.createElementNS("http://www.w3.org/1999/xhtml", "p", null);
   is(e.getAttribute("is"), null);
 
   e = document.createElementNS("http://www.w3.org/1999/xhtml", "p", undefined);
   is(e.getAttribute("is"), null);
 }
 
+function runTest() {
+  test();
+  SimpleTest.finish();
+}
+
+// test with webcomponents enabled
 test();
 
+// test with webcomponents disabled
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+  { 'set': [["dom.webcomponents.customelements.enabled", false]]}, runTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a>
 
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="container"></div>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
 
     // Test callback for custom element when used after registration.
 
     var iframeWin = aDocument.defaultView;
     var connectedCallbackCount = 0;
     var disconnectedCallbackCount = 0;
     var attributeChangedCallbackCount = 0;
--- a/dom/tests/mochitest/webcomponents/test_detached_style.html
+++ b/dom/tests/mochitest/webcomponents/test_detached_style.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1062578">Bug 1062578</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="grabme"></div>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
     var host = aDocument.createElement("div");
     var shadow = host.attachShadow({mode: "open"});
     shadow.innerHTML = '<style> #inner { height: 200px; } </style><div id="inner">Hello</div>';
 
     var iframeWin = aDocument.defaultView;
     iframeWin.grabme.appendChild(host);
 
--- a/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
+++ b/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
-createIframe()
+setShadowDOMPrefAndCreateIframe()
   .then((aDocument) => {
     var thrownException = false;
     var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
 
     try {
       aDocument.adoptNode(shadowRoot);
     } catch(err) {
       thrownException = err;
--- a/dom/tests/mochitest/webcomponents/test_document_importnode.html
+++ b/dom/tests/mochitest/webcomponents/test_document_importnode.html
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
-createIframe()
+setShadowDOMPrefAndCreateIframe()
   .then((aDocument) => {
     var thrownException = false;
     var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
 
     try {
       aDocument.importNode(shadowRoot);
     } catch(err) {
       thrownException = err;
--- a/dom/tests/mochitest/webcomponents/test_event_retarget.html
+++ b/dom/tests/mochitest/webcomponents/test_event_retarget.html
@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
-createIframe()
+setShadowDOMPrefAndCreateIframe()
   .then((aDocument) => {
     /*
      * Creates an event listener with an expected event target.
      */
     function createEventListener(expectedTarget, msg) {
       return function(e) {
         is(e.target, expectedTarget, msg);
       };
--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html
+++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
 <script>
 
 var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
             .getService(SpecialPowers.Ci.nsIEventListenerService);
 
 SimpleTest.waitForExplicitFinish();
-createIframe()
+setShadowDOMPrefAndCreateIframe()
   .then((aDocument) => {
     function eventListener(e) {
       eventChain.push(this);
     }
 
     function isEventChain(actual, expected, msg) {
       is(actual.length, expected.length, msg);
       for (var i = 0; i < expected.length; i++) {
--- a/dom/tests/mochitest/webcomponents/test_shadowroot.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot.html
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="movedtoshadow" class="testclass"></div>' +
               '<svg id="svgmovedtoshadow"></svg>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
     // Create ShadowRoot.
     var element = aDocument.createElement("div");
     ok(!element.shadowRoot, "div element should not have a shadow root.");
     var shadow = element.attachShadow({mode: "open"});
     is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
 
     // Move an element from the document to the ShadowRoot.
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 1429982 **/
 SimpleTest.waitForExplicitFinish();
-createIframe()
+setShadowDOMPrefAndCreateIframe()
   .then((aDocument) => {
     var element = aDocument.createElement("div");
     var shadowRoot = element.attachShadow({mode: "open"});
     var thrownException = false;
 
     try {
       shadowRoot.cloneNode();
     } catch(err) {
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="grabme"></div>';
-createIframe(content).then(aDocument => {
+setShadowDOMPrefAndCreateIframe(content).then(aDocument => {
   var element = aDocument.getElementById("grabme");
   var shadow = element.attachShadow({mode: "open"});
 
   // Check that <base> is inert.
   shadow.innerHTML = '<base href="http://www.example.org/" />';
   isnot(aDocument.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
   SimpleTest.finish();
 });
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div class="tall" id="bodydiv"></div>' +
               '<div id="container"></div>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
     var iframeWin = aDocument.defaultView;
 
     // Create ShadowRoot.
     var container = aDocument.getElementById("container");
     var elem = aDocument.createElement("div");
     container.appendChild(elem); // Put ShadowRoot host in document.
     var root = elem.attachShadow({mode: "open"});
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="container"></div>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
     var iframeWin = aDocument.defaultView;
 
     // Create ShadowRoot.
     var container = aDocument.getElementById("container");
     var elem = aDocument.createElement("div");
     container.appendChild(elem); // Put ShadowRoot host in document.
     var root = elem.attachShadow({mode: "open"});
--- a/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
+++ b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="grabme"></div>';
-createIframe(content)
+setShadowDOMPrefAndCreateIframe(content)
   .then((aDocument) => {
     var iframeWin = aDocument.defaultView;
 
     var host = aDocument.getElementById("grabme");
     var shadow = host.attachShadow({mode: "open"});
     shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><slot><span id="innerspan">Hello</span></slot></span>';
     var innerStyle = shadow.getElementById("innerstyle");
 
--- a/dom/tests/moz.build
+++ b/dom/tests/moz.build
@@ -177,16 +177,17 @@ MOCHITEST_CHROME_MANIFESTS += [
     'mochitest/beacon/chrome.ini',
     'mochitest/chrome/chrome.ini',
     'mochitest/general/chrome.ini',
     'mochitest/geolocation/chrome.ini',
     'mochitest/keyhandling/chrome.ini',
     'mochitest/localstorage/chrome.ini',
     'mochitest/sessionstorage/chrome.ini',
     'mochitest/webcomponents/chrome.ini',
+    'mochitest/webcomponents/chrome_disabled.ini',
     'mochitest/whatwg/chrome.ini',
 ]
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['browser/browser.ini', 'browser/perfmetrics/browser.ini',
                              'mochitest/ajax/offline/browser.ini']
 
 TEST_HARNESS_FILES.testing.mochitest.tests.dom.tests.mochitest.ajax.lib += [
--- a/dom/webidl/CustomElementRegistry.webidl
+++ b/dom/webidl/CustomElementRegistry.webidl
@@ -1,13 +1,14 @@
 /* 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/. */
 
 // https://html.spec.whatwg.org/#dom-window-customelements
+[Func="CustomElementRegistry::IsCustomElementEnabled"]
 interface CustomElementRegistry {
   [CEReactions, Throws]
   void define(DOMString name, Function functionConstructor,
               optional ElementDefinitionOptions options);
   [ChromeOnly, Throws]
   void setElementCreationCallback(DOMString name, CustomElementCreationCallback callback);
   any get(DOMString name);
   [Throws]
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -230,31 +230,31 @@ partial interface Element {
 // https://dom.spec.whatwg.org/#dictdef-shadowrootinit
 dictionary ShadowRootInit {
   required ShadowRootMode mode;
 };
 
 // https://dom.spec.whatwg.org/#element
 partial interface Element {
   // Shadow DOM v1
-  [Throws]
+  [Throws, Func="nsDocument::IsShadowDOMEnabled"]
   ShadowRoot attachShadow(ShadowRootInit shadowRootInitDict);
-  [BinaryName="shadowRootByMode"]
+  [BinaryName="shadowRootByMode", Func="nsDocument::IsShadowDOMEnabled"]
   readonly attribute ShadowRoot? shadowRoot;
 
-  [Func="nsDocument::IsCallerChromeOrAddon", BinaryName="shadowRoot"]
+  [Func="nsDocument::IsShadowDOMEnabledAndCallerIsChromeOrAddon", BinaryName="shadowRoot"]
   readonly attribute ShadowRoot? openOrClosedShadowRoot;
 
-  [BinaryName="assignedSlotByMode"]
+  [BinaryName="assignedSlotByMode", Func="nsDocument::IsShadowDOMEnabled"]
   readonly attribute HTMLSlotElement? assignedSlot;
 
-  [ChromeOnly, BinaryName="assignedSlot"]
+  [ChromeOnly, BinaryName="assignedSlot", Func="nsDocument::IsShadowDOMEnabled"]
   readonly attribute HTMLSlotElement? openOrClosedAssignedSlot;
 
-  [CEReactions, Unscopable, SetterThrows]
+  [CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsShadowDOMEnabled"]
            attribute DOMString slot;
 };
 
 Element implements ChildNode;
 Element implements NonDocumentTypeChildNode;
 Element implements ParentNode;
 Element implements Animatable;
 Element implements GeometryUtils;
--- a/dom/webidl/HTMLSlotElement.webidl
+++ b/dom/webidl/HTMLSlotElement.webidl
@@ -6,17 +6,17 @@
  * The origin of this IDL file is
  * https://html.spec.whatwg.org/multipage/forms.html#the-dialog-element
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-[Exposed=Window, HTMLConstructor]
+[Func="nsDocument::IsShadowDOMEnabled", Exposed=Window, HTMLConstructor]
 interface HTMLSlotElement : HTMLElement {
   [CEReactions, SetterThrows] attribute DOMString name;
   sequence<Node> assignedNodes(optional AssignedNodesOptions options);
 };
 
 dictionary AssignedNodesOptions {
   boolean flatten = false;
 };
--- a/dom/webidl/ShadowRoot.webidl
+++ b/dom/webidl/ShadowRoot.webidl
@@ -12,16 +12,17 @@
 
 // https://dom.spec.whatwg.org/#enumdef-shadowrootmode
 enum ShadowRootMode {
   "open",
   "closed"
 };
 
 // https://dom.spec.whatwg.org/#shadowroot
+[Func="nsDocument::IsShadowDOMEnabled"]
 interface ShadowRoot : DocumentFragment
 {
   // Shadow DOM v1
   readonly attribute ShadowRootMode mode;
   readonly attribute Element host;
 
   // [deprecated] Shadow DOM v0
   Element? getElementById(DOMString elementId);
--- a/dom/webidl/Text.webidl
+++ b/dom/webidl/Text.webidl
@@ -14,16 +14,16 @@
 interface Text : CharacterData {
   [Throws]
   Text splitText(unsigned long offset);
   [Throws]
   readonly attribute DOMString wholeText;
 };
 
 partial interface Text {
-  [BinaryName="assignedSlotByMode"]
+  [BinaryName="assignedSlotByMode", Func="nsTextNode::IsShadowDOMEnabled"]
   readonly attribute HTMLSlotElement? assignedSlot;
 
-  [ChromeOnly, BinaryName="assignedSlot"]
+  [ChromeOnly, BinaryName="assignedSlot", Func="nsTextNode::IsShadowDOMEnabled"]
   readonly attribute HTMLSlotElement? openOrClosedAssignedSlot;
 };
 
 Text implements GeometryUtils;
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -34,16 +34,17 @@ typedef OfflineResourceList ApplicationC
    CrossOriginReadable] readonly attribute Window window;
   [Replaceable, Constant, StoreInSlot,
    CrossOriginReadable] readonly attribute Window self;
   [Unforgeable, StoreInSlot, Pure] readonly attribute Document? document;
   [Throws] attribute DOMString name;
   [PutForwards=href, Unforgeable, BinaryName="getLocation",
    CrossOriginReadable, CrossOriginWritable] readonly attribute Location location;
   [Throws] readonly attribute History history;
+  [Func="CustomElementRegistry::IsCustomElementEnabled"]
   readonly attribute CustomElementRegistry customElements;
   [Replaceable, Throws] readonly attribute BarProp locationbar;
   [Replaceable, Throws] readonly attribute BarProp menubar;
   [Replaceable, Throws] readonly attribute BarProp personalbar;
   [Replaceable, Throws] readonly attribute BarProp scrollbars;
   [Replaceable, Throws] readonly attribute BarProp statusbar;
   [Replaceable, Throws] readonly attribute BarProp toolbar;
   [Throws] attribute DOMString status;
--- a/dom/xml/nsXMLPrettyPrinter.cpp
+++ b/dom/xml/nsXMLPrettyPrinter.cpp
@@ -7,16 +7,18 @@
 #include "nsContentUtils.h"
 #include "nsICSSDeclaration.h"
 #include "nsIObserver.h"
 #include "nsSyncLoadService.h"
 #include "nsPIDOMWindow.h"
 #include "nsIServiceManager.h"
 #include "nsNetUtil.h"
 #include "mozilla/dom/Element.h"
+#include "nsBindingManager.h"
+#include "nsXBLService.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozilla/Preferences.h"
 #include "nsIDocument.h"
 #include "nsVariant.h"
 #include "mozilla/dom/CustomEvent.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ToJSValue.h"
@@ -121,41 +123,104 @@ nsXMLPrettyPrinter::PrettyPrint(nsIDocum
     if (NS_WARN_IF(err.Failed())) {
         return err.StealNSResult();
     }
 
     // Find the root element
     RefPtr<Element> rootElement = aDocument->GetRootElement();
     NS_ENSURE_TRUE(rootElement, NS_ERROR_UNEXPECTED);
 
-    // Attach a closed shadow root on it.
-    RefPtr<ShadowRoot> shadowRoot =
-        rootElement->AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
+    if (nsContentUtils::IsShadowDOMEnabled()) {
+        // Attach a closed shadow root on it.
+        RefPtr<ShadowRoot> shadowRoot =
+            rootElement->AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
+
+        // Append the document fragment to the shadow dom.
+        shadowRoot->AppendChild(*resultFragment, err);
+        if (NS_WARN_IF(err.Failed())) {
+            return err.StealNSResult();
+        }
+    } else {
+        //
+        // Apply the prettprint XBL binding.
+        //
+        // We take some shortcuts here. In particular, we don't bother invoking the
+        // contstructor (since the binding has no constructor), and we don't bother
+        // calling LoadBindingDocument because it's a chrome:// URI and thus will get
+        // sync loaded no matter what.
+        //
+
+        // Grab the XBL service.
+        nsXBLService* xblService = nsXBLService::GetInstance();
+        NS_ENSURE_TRUE(xblService, NS_ERROR_NOT_AVAILABLE);
+
+        // Compute the binding URI.
+        nsCOMPtr<nsIURI> bindingUri;
+        rv = NS_NewURI(getter_AddRefs(bindingUri),
+            NS_LITERAL_STRING("chrome://global/content/xml/XMLPrettyPrint.xml#prettyprint"));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // Grab the system principal.
+        nsCOMPtr<nsIPrincipal> sysPrincipal;
+        nsContentUtils::GetSecurityManager()->
+            GetSystemPrincipal(getter_AddRefs(sysPrincipal));
 
-    // Append the document fragment to the shadow dom.
-    shadowRoot->AppendChild(*resultFragment, err);
-    if (NS_WARN_IF(err.Failed())) {
-        return err.StealNSResult();
+        // Destroy any existing frames before we unbind anonymous content.
+        // Note that the shell might be Destroy'ed by now (see bug 1415541).
+        if (!shell->IsDestroying()) {
+            shell->DestroyFramesForAndRestyle(rootElement);
+        }
+
+        // Load the bindings.
+        RefPtr<nsXBLBinding> unused;
+        bool ignored;
+        rv = xblService->LoadBindings(rootElement, bindingUri, sysPrincipal,
+                                      getter_AddRefs(unused), &ignored);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        // Fire an event at the bound element to pass it |resultFragment|.
+        RefPtr<CustomEvent> event =
+          NS_NewDOMCustomEvent(rootElement, nullptr, nullptr);
+        MOZ_ASSERT(event);
+        AutoJSAPI jsapi;
+        if (!jsapi.Init(event->GetParentObject())) {
+            return NS_ERROR_UNEXPECTED;
+        }
+        JSContext* cx = jsapi.cx();
+        JS::Rooted<JS::Value> detail(cx);
+        if (!ToJSValue(cx, resultFragment, &detail)) {
+            return NS_ERROR_UNEXPECTED;
+        }
+        event->InitCustomEvent(cx, NS_LITERAL_STRING("prettyprint-dom-created"),
+                               /* bubbles = */ false, /* cancelable = */ false,
+                               detail);
+
+        event->SetTrusted(true);
+        rootElement->DispatchEvent(*event, err);
+        if (NS_WARN_IF(err.Failed())) {
+            return err.StealNSResult();
+        }
     }
 
     // Observe the document so we know when to switch to "normal" view
     aDocument->AddObserver(this);
     mDocument = aDocument;
 
     NS_ADDREF_THIS();
 
     return NS_OK;
 }
 
 void
 nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent)
 {
     // If aContent is null, the document-node was modified.
-    // If it is not null but in the shadow tree or the <scrollbar> NACs,
-    // the change was in the generated content, and it should be ignored.
+    // If it is not null but in the shadow tree, the <scrollbar> NACs,
+    // or the XBL binding, the change was in the generated content, and
+    // it should be ignored.
     bool isGeneratedContent = !aContent ?
         false :
         aContent->GetBindingParent() || aContent->IsInShadowTree();
 
     if (!isGeneratedContent && !mUnhookPending) {
         // Can't blindly to mUnhookPending after AddScriptRunner,
         // since AddScriptRunner _could_ in theory run us
         // synchronously
@@ -169,16 +234,19 @@ void
 nsXMLPrettyPrinter::Unhook()
 {
     mDocument->RemoveObserver(this);
     nsCOMPtr<Element> element = mDocument->GetDocumentElement();
 
     if (element) {
         // Remove the shadow root
         element->UnattachShadow();
+
+        // Remove the bound XBL binding
+        mDocument->BindingManager()->ClearBinding(element);
     }
 
     mDocument = nullptr;
 
     NS_RELEASE_THIS();
 }
 
 void
new file mode 100644
--- /dev/null
+++ b/dom/xml/resources/XMLPrettyPrint.xml
@@ -0,0 +1,30 @@
+<?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/. -->
+
+<bindings xmlns="http://www.mozilla.org/xbl"
+          xmlns:html="http://www.w3.org/1999/xhtml">
+
+  <binding id="prettyprint" bindToUntrustedContent="true">
+
+    <content><html:div id="top"/>
+      <html:span style="display: none;"><children/></html:span>
+    </content>
+
+    <handlers>
+      <handler event="prettyprint-dom-created" allowuntrusted="false">
+        <![CDATA[
+          let container = document.getAnonymousNodes(this).item(0);
+          // Take the child nodes from the passed <div id="top">
+          // and append them to our own.
+          for (let el of event.detail.childNodes) {
+            container.appendChild(el);
+          }
+        ]]>
+      </handler>
+    </handlers>
+
+  </binding>
+
+</bindings>
--- a/dom/xml/resources/jar.mn
+++ b/dom/xml/resources/jar.mn
@@ -1,7 +1,8 @@
 # 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/.
 
 toolkit.jar:
     content/global/xml/XMLPrettyPrint.css                 (XMLPrettyPrint.css)
     content/global/xml/XMLPrettyPrint.xsl                 (XMLPrettyPrint.xsl)
+    content/global/xml/XMLPrettyPrint.xml                 (XMLPrettyPrint.xml)
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -462,17 +462,17 @@ load 1127198-1.html
 load 1140198.html
 load 1143535.html
 load 1153716.html
 load 1156588.html
 load 1162813.xul
 load 1163583.html
 load 1234622-1.html
 load 1235467-1.html
-load 1261351.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1261351.html
 load 1270797-1.html
 load 1278455-1.html
 load 1286889.html
 load 1288608.html
 load 1288946-1.html
 load 1288946-2a.html
 load 1288946-2b.html
 load 1297835.html
@@ -502,44 +502,44 @@ load 1400438-1.html
 load 1400599-1.html
 load 1401739.html
 load 1401840.html
 load 1402476.html
 load 1404789-2.html
 load 1406562.html
 load 1409147.html
 load 1411138.html
-load 1414100.html
-load 1414303.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1414100.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1414303.html
 load 1419762.html
 load 1419802.html
 load 1420533.html
 load 1423216.html
 load 1425893.html
 load 1425959.html
 load 1428353.html
-load 1429088.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1429088.html
 load 1429961.html
 load 1429962.html
 load 1435015.html
 load 1437155.html
-load 1439016.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1439016.html
 load 1442506.html
 load 1443027-1.html
 load 1448841-1.html
 load 1452839.html
 load 1453196.html
 load 1453342.html
 load 1453702.html
 load 1458121.html
-load 1461749.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1461749.html
 load 1461812.html
 load 1462412.html
 load 1463940.html
-HTTP load 1464641.html
+pref(dom.webcomponents.shadowdom.enabled,true) HTTP load 1464641.html
 load 1464737.html
 load 1466638.html
 load 1467688.html
 load 1467964.html
 load 1469354.html
 pref(layout.accessiblecaret.enabled,true) load 1472020.html
 load 1472027.html
 load 1477847.html
--- a/layout/base/tests/chrome/test_printpreview.xul
+++ b/layout/base/tests/chrome/test_printpreview.xul
@@ -6,11 +6,13 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 <body xmlns="http://www.w3.org/1999/xhtml">
 </body>
   <!-- test code goes here -->
 <script type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
-window.open("printpreview_helper.xul", "printpreview", "chrome,width=100,height=100");
+SpecialPowers.pushPrefEnv({"set":[["dom.webcomponents.shadowdom.enabled", true]]}, function() {
+  window.open("printpreview_helper.xul", "printpreview", "chrome,width=100,height=100");
+});
 ]]></script>
 </window>
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -588,17 +588,17 @@ asserts(11) asserts-if(Android,274) load
 pref(font.size.inflation.minTwips,200) load 1032450.html
 load 1032613-1.svg
 load 1032613-2.html
 load 1037903.html
 load 1039454-1.html
 load 1042489.html
 load 1054010-1.html
 load 1058954-1.html
-skip-if(verify&&isDebugBuild&&(gtkWidget||OSX)) load 1059138-1.html
+skip-if(verify&&isDebugBuild&&(gtkWidget||OSX)) pref(dom.webcomponents.shadowdom.enabled,true) pref(dom.webcomponents.customelements.enabled,true) load 1059138-1.html
 load 1134531.html
 load 1134667.html
 load 1137723-1.html
 load 1137723-2.html
 load 1140268-1.html
 load 1145768.html
 load 1145931.html
 load 1146103.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1859,17 +1859,17 @@ pref(layout.css.moz-document.content.ena
 == 1053035-1-grid.html 1053035-1-ref.html
 == 1059167-1.html 1059167-1-ref.html
 fails-if(webrender) == 1059498-1.html 1059498-1-ref.html # WebRender: see bug 1504290
 fails-if(webrender) == 1059498-2.html 1059498-1-ref.html # WebRender: see bug 1504290
 fails-if(webrender) == 1059498-3.html 1059498-1-ref.html # WebRender: see bug 1499113
 == 1062108-1.html 1062108-1-ref.html
 == 1062792-1.html 1062792-1-ref.html
 == 1062963-floatmanager-reflow.html 1062963-floatmanager-reflow-ref.html
-== 1066554-1.html 1066554-1-ref.html
+test-pref(dom.webcomponents.shadowdom.enabled,true) == 1066554-1.html 1066554-1-ref.html
 == 1069716-1.html 1069716-1-ref.html
 == 1078262-1.html about:blank
 test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html
 fuzzy-if(webrender,64-64,485-486) == 1081185-1.html 1081185-1-ref.html
 == 1097437-1.html 1097437-1-ref.html
 == 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test
 == 1105137-1.html 1105137-1-ref.html
 fuzzy-if(d2d,0-36,0-304) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&d2d,0-139,0-701) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -362,17 +362,17 @@ fuzzy-if(OSX,0-1,0-100) fuzzy-if(skiaCon
 == mfrac-C-2.html mfrac-C-2-ref.html
 == mfrac-C-3.html mfrac-C-3-ref.html
 == mfrac-C-4.html mfrac-C-4-ref.html
 fuzzy-if(OSX,0-1,0-100) fuzzy-if(skiaContent,0-1,0-14) == mfrac-D-1.html mfrac-D-1-ref.html
 == mfrac-D-2.html mfrac-D-2-ref.html
 == mfrac-D-3.html mfrac-D-3-ref.html
 == mfrac-D-4.html mfrac-D-4-ref.html
 == mfrac-E-1.html mfrac-E-1-ref.html
-== shadow-dom-1.html shadow-dom-1-ref.html
+test-pref(dom.webcomponents.shadowdom.enabled,true) == shadow-dom-1.html shadow-dom-1-ref.html
 pref(dom.meta-viewport.enabled,true) pref(font.size.inflation.emPerLine,25) fuzzy-if(webrender&&!gtkWidget,0-255,0-244) == font-inflation-1.html font-inflation-1-ref.html
 test-pref(font.minimum-size.x-math,40) == default-font.html default-font-ref.html
 != radicalbar-1.html about:blank
 != radicalbar-1a.html about:blank
 != radicalbar-1b.html about:blank
 != radicalbar-1c.html about:blank
 != radicalbar-1d.html about:blank
 != radicalbar-2.html about:blank
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -552,18 +552,18 @@ fuzzy-if(skiaContent,0-1,0-100) == tspan
 # currentColor override by color attribute
 == currentColor-override-flood.svg pass.svg
 == currentColor-override-lighting.svg currentColor-override-lighting-ref.svg
 == currentColor-override-stop.svg pass.svg
 
 == mask-invalidation.html mask-invalidation-ref.html
 
 # Shadow DOM id tracking.
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-1.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-2.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-3.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-4.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-5.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-6.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-7.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-8.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-9.html fragid-shadow-ref.html # Bug 1392106
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-10.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-1.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-2.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-3.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-4.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-5.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-6.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-7.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-8.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-9.html fragid-shadow-ref.html # Bug 1392106
+pref(dom.webcomponents.shadowdom.enabled,true) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == fragid-shadow-10.html fragid-shadow-ref.html # Bug 1392106
--- a/layout/reftests/webcomponents/reftest.list
+++ b/layout/reftests/webcomponents/reftest.list
@@ -1,26 +1,26 @@
-== cross-tree-selection-1.html cross-tree-selection-1-ref.html
-== basic-shadow-1.html basic-shadow-1-ref.html
-== basic-shadow-2.html basic-shadow-2-ref.html
-== basic-shadow-3.html basic-shadow-3-ref.html
-== basic-shadow-4.html basic-shadow-4-ref.html
-== fallback-content-1.html fallback-content-1-ref.html
-== remove-insertion-point-1.html remove-insertion-point-1-ref.html
-== nested-insertion-point-1.html nested-insertion-point-1-ref.html
-== update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
-fuzzy-if(Android,0-2,0-7) == input-transition-1.html input-transition-1-ref.html
-== dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
-== dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
-== remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
-== reframe-shadow-child-1.html reframe-shadow-child-ref.html
-== reframe-shadow-child-2.html reframe-shadow-child-ref.html
-== style-sharing.html style-sharing-ref.html
-== style-sharing-across-shadow.html style-sharing-ref.html # bug 1412400
-== basic-slot-1.html basic-slot-1-ref.html
-== basic-slot-2.html basic-slot-2-ref.html
-== basic-slot-3.html basic-slot-3-ref.html
-== basic-slot-4.html basic-slot-3-ref.html
-== basic-slot-5.html basic-slot-5-ref.html
-== basic-slot-6.html basic-slot-6-ref.html
-== shadow-style-1.html shadow-style-1-ref.html
-== shadow-style-2.html shadow-style-2-ref.html
-== shadow-style-3.html shadow-style-3-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) fuzzy-if(Android,0-2,0-7) == input-transition-1.html input-transition-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == reframe-shadow-child-1.html reframe-shadow-child-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == reframe-shadow-child-2.html reframe-shadow-child-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == style-sharing.html style-sharing-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == style-sharing-across-shadow.html style-sharing-ref.html # bug 1412400
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-1.html basic-slot-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-2.html basic-slot-2-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-3.html basic-slot-3-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-4.html basic-slot-3-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-5.html basic-slot-5-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-6.html basic-slot-6-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == shadow-style-1.html shadow-style-1-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == shadow-style-2.html shadow-style-2-ref.html
+pref(dom.webcomponents.shadowdom.enabled,true) == shadow-style-3.html shadow-style-3-ref.html
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -113,22 +113,22 @@ load 894245-1.html
 load 915440.html
 load 927734-1.html
 load 930270-1.html
 load 930270-2.html
 load 945048-1.html
 load 972199-1.html
 load 989965-1.html
 load 992333-1.html
-load 1017798-1.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1017798-1.html
 load 1028514-1.html
 load 1066089-1.html
 load 1074651-1.html
 load 1135534.html
-load 1089463-1.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1089463-1.html
 load 1136010-1.html
 load 1146101-1.html
 load 1153693-1.html
 load 1156969.svg
 load 1161320-1.html
 pref(dom.animations-api.getAnimations.enabled,true) load 1161320-2.html
 load 1161366-1.html
 load 1163446-1.html
@@ -268,30 +268,30 @@ load 1409931.html
 load 1410226-1.html
 load 1410226-2.html
 load 1411008.html
 load 1411143.html
 load 1411478.html
 load 1413288.html
 load 1413361.html
 load 1413670.html
-load 1415353.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1415353.html
 load 1418059.html
 test-pref(dom.animations-api.core.enabled,true) test-pref(dom.animations-api.implicit-keyframes.enabled,true) pref(dom.animations-api.getAnimations.enabled,true) load 1418867.html
-load 1419554.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1419554.html
 load 1426312.html
 load 1439793.html
 load 1409183.html
-load 1445682.html
-load 1449243.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1445682.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1449243.html
 load 1450691.html
-load 1453206.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1453206.html
 load 1454140.html
 load 1455108.html
 load 1457288.html
 load 1457985.html
-load 1468640.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1468640.html
 load 1469076.html
 load 1475003.html
 load 1479681.html
 load 1488817.html
 load 1490012.html
 load 1502893.html
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -200,17 +200,17 @@ load 1223281-1.svg
 load 1322537-1.html
 load 1322537-2.html
 load 1322852.html
 load 1348564.svg
 load 1402109.html
 load 1402124.html
 load 1402486.html
 load 1421807-1.html
-load 1421807-2.html
+pref(dom.webcomponents.shadowdom.enabled,true) load 1421807-2.html
 load 1422226.html
 load 1443092.html
 load 1467552-1.html
 load 1474982.html
 load conditional-outer-svg-nondirty-reflow-assert.xhtml
 load extref-test-1.xhtml
 load blob-merging-and-retained-display-list.html
 load empty-blob-merging.html
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1420,16 +1420,19 @@ pref("dom.event.highrestimestamp.enabled
 pref("dom.event.coalesce_mouse_move",       true);
 
 #if defined(NIGHTLY_BUILD)
 pref("dom.ua_widget.enabled", true);
 #else
 pref("dom.ua_widget.enabled", false);
 #endif
 
+pref("dom.webcomponents.shadowdom.enabled", true);
+pref("dom.webcomponents.customelements.enabled", true);
+
 pref("javascript.enabled",                  true);
 pref("javascript.options.strict",           false);
 #ifdef DEBUG
 pref("javascript.options.strict.debug",     false);
 #endif
 pref("javascript.options.baselinejit",      true);
 //Duplicated in JitOptions - ensure both match.
 pref("javascript.options.baselinejit.threshold", 10);
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -379,37 +379,40 @@ nsHtml5TreeOperation::CreateHTMLElement(
 
   dom::Element* newContent = nullptr;
   nsIDocument* document = nodeInfo->GetDocument();
   bool willExecuteScript = false;
   bool isCustomElement = false;
   RefPtr<nsAtom> isAtom;
   dom::CustomElementDefinition* definition = nullptr;
 
-  if (aAttributes) {
-    nsHtml5String is = aAttributes->getValue(nsHtml5AttributeName::ATTR_IS);
-    if (is) {
-      nsAutoString isValue;
-      is.ToString(isValue);
-      isAtom = NS_Atomize(isValue);
+  // Avoid overhead by checking if custom elements pref is enabled or not.
+  if (dom::CustomElementRegistry::IsCustomElementEnabled(document)) {
+    if (aAttributes) {
+      nsHtml5String is = aAttributes->getValue(nsHtml5AttributeName::ATTR_IS);
+      if (is) {
+        nsAutoString isValue;
+        is.ToString(isValue);
+        isAtom = NS_Atomize(isValue);
+      }
     }
-  }
 
-  isCustomElement = (aCreator == NS_NewCustomElement || isAtom);
-  if (isCustomElement && aFromParser != dom::FROM_PARSER_FRAGMENT) {
-    RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
-    RefPtr<nsAtom> typeAtom =
-      (aCreator == NS_NewCustomElement) ? tagAtom : isAtom;
+    isCustomElement = (aCreator == NS_NewCustomElement || isAtom);
+    if (isCustomElement && aFromParser != dom::FROM_PARSER_FRAGMENT) {
+      RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
+      RefPtr<nsAtom> typeAtom =
+        (aCreator == NS_NewCustomElement) ? tagAtom : isAtom;
 
-    MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
-    definition = nsContentUtils::LookupCustomElementDefinition(
-      document, nodeInfo->NameAtom(), nodeInfo->NamespaceID(), typeAtom);
+      MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
+      definition = nsContentUtils::LookupCustomElementDefinition(
+        document, nodeInfo->NameAtom(), nodeInfo->NamespaceID(), typeAtom);
 
-    if (definition) {
-      willExecuteScript = true;
+      if (definition) {
+        willExecuteScript = true;
+      }
     }
   }
 
   if (willExecuteScript) { // This will cause custom element constructors to run
     AutoSetThrowOnDynamicMarkupInsertionCounter
       throwOnDynamicMarkupInsertionCounter(document);
     nsHtml5AutoPauseUpdate autoPauseContentUpdate(aBuilder);
     {
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -32,17 +32,17 @@ macro_rules! apply_non_ts_list {
             [
                 ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
                 ("link", Link, link, IN_UNVISITED_STATE, _),
                 ("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _),
                 ("visited", Visited, visited, IN_VISITED_STATE, _),
                 ("active", Active, active, IN_ACTIVE_STATE, _),
                 ("checked", Checked, checked, IN_CHECKED_STATE, _),
-                ("defined", Defined, defined, IN_DEFINED_STATE, _),
+                ("defined", Defined, defined, IN_DEFINED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
                 ("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
                 ("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
                 ("focus", Focus, focus, IN_FOCUS_STATE, _),
                 ("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _),
                 ("hover", Hover, hover, IN_HOVER_STATE, _),
                 ("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _),
                 ("target", Target, target, IN_TARGET_STATE, _),
                 ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _),
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -173,16 +173,19 @@ impl NonTSPseudoClass {
     fn is_enabled_in_content(&self) -> bool {
         use crate::gecko_bindings::structs::mozilla;
         match *self {
             // For pseudo-classes with pref, the availability in content
             // depends on the pref.
             NonTSPseudoClass::Fullscreen => unsafe {
                 mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
             },
+            NonTSPseudoClass::Defined => unsafe {
+                structs::nsContentUtils_sIsCustomElementsEnabled
+            },
             // Otherwise, a pseudo-class is enabled in content when it
             // doesn't have any enabled flag.
             _ => !self
                 .has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
         }
     }
 
     /// <https://drafts.csswg.org/selectors-4/#useraction-pseudos>
@@ -339,17 +342,20 @@ impl<'a> SelectorParser<'a> {
 }
 
 impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
     type Impl = SelectorImpl;
     type Error = StyleParseErrorKind<'i>;
 
     #[inline]
     fn parse_slotted(&self) -> bool {
-        true
+        // NOTE(emilio): Slot assignment and such works per-document, but
+        // getting a document around here is not trivial, and it's not worth
+        // anyway to handle this in a per-doc basis.
+        unsafe { structs::nsContentUtils_sIsShadowDOMEnabled }
     }
 
     #[inline]
     fn parse_host(&self) -> bool {
         self.parse_slotted()
     }
 
     fn pseudo_element_allows_single_colon(name: &str) -> bool {
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
@@ -13,23 +13,25 @@ from marionette_driver.errors import (
 
 from marionette_harness import MarionetteTestCase
 
 
 class TestShadowDom(MarionetteTestCase):
 
     def setUp(self):
         super(TestShadowDom, self).setUp()
+        self.marionette.set_pref("dom.webcomponents.shadowdom.enabled", True)
         self.marionette.navigate(self.marionette.absolute_url("test_shadow_dom.html"))
 
         self.host = self.marionette.find_element(By.ID, "host")
         self.marionette.switch_to_shadow_root(self.host)
         self.button = self.marionette.find_element(By.ID, "button")
 
     def tearDown(self):
+        self.marionette.clear_pref("dom.webcomponents.shadowdom.enabled")
         super(TestShadowDom, self).tearDown()
 
     def test_chrome_error(self):
         with self.marionette.using_context("chrome"):
             self.assertRaises(UnsupportedOperationException,
                               self.marionette.switch_to_shadow_root)
 
     def test_shadow_dom(self):
--- a/testing/profiles/unittest/user.js
+++ b/testing/profiles/unittest/user.js
@@ -115,16 +115,18 @@ user_pref("dom.successive_dialog_time_li
 // In the default configuration, we bypass XBL scopes (a security feature) for
 // domains whitelisted for remote XUL, so that intranet apps and such continue
 // to work without major rewrites. However, we also use the whitelist mechanism
 // to run our XBL tests in automation, in which case we really want to be testing
 // the configuration that we ship to users without special whitelisting. So we
 // use an additional pref here to allow automation to use the "normal" behavior.
 user_pref("dom.use_xbl_scopes_for_remote_xul", true);
 user_pref("dom.w3c_touch_events.enabled", 1);
+user_pref("dom.webcomponents.customelements.enabled", true);
+user_pref("dom.webcomponents.shadowdom.enabled", true);
 user_pref("extensions.autoDisableScopes", 0);
 user_pref("extensions.blocklist.detailsURL", "http://{server}/extensions-dummy/blocklistDetailsURL");
 user_pref("extensions.blocklist.itemURL", "http://{server}/extensions-dummy/blocklistItemURL");
 user_pref("extensions.blocklist.url", "http://{server}/extensions-dummy/blocklistURL");
 // XPI extensions are required for test harnesses to load
 user_pref("extensions.defaultProviders.enabled", true);
 // Enable form autofill feature testing.
 user_pref("extensions.formautofill.available", "on");
--- a/testing/web-platform/meta/content-security-policy/securitypolicyviolation/targeting.html.ini
+++ b/testing/web-platform/meta/content-security-policy/securitypolicyviolation/targeting.html.ini
@@ -1,9 +1,10 @@
 [targeting.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   expected: TIMEOUT
   [Correct targeting inside shadow tree (inline handler).]
     disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1404842
 
   [Correct targeting inside shadow tree (style).]
     disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1404842
 
   [Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.]
--- a/testing/web-platform/meta/css/css-backgrounds/background-size-027.html.ini
+++ b/testing/web-platform/meta/css/css-backgrounds/background-size-027.html.ini
@@ -1,3 +1,4 @@
 [background-size-027.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   disabled:
     if webrender: bug 1425588
--- a/testing/web-platform/meta/css/css-scoping/__dir__.ini
+++ b/testing/web-platform/meta/css/css-scoping/__dir__.ini
@@ -1,1 +1,1 @@
-prefs: [dom.animations-api.getAnimations.enabled:true]
+prefs: [dom.animations-api.getAnimations.enabled:true, dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/cssom-view/elementsFromPoint-shadowroot.html.ini
@@ -0,0 +1,2 @@
+[elementsFromPoint-shadowroot.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/cssom-view/scrollIntoView-shadow.html.ini
@@ -0,0 +1,2 @@
+[scrollIntoView-shadow.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/cssom/getComputedStyle-display-none-001.html.ini
@@ -0,0 +1,2 @@
+[getComputedStyle-display-none-001.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/selectors/__dir__.ini
@@ -0,0 +1,1 @@
+prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/custom-elements/CustomElementRegistry.html.ini
+++ b/testing/web-platform/meta/custom-elements/CustomElementRegistry.html.ini
@@ -1,9 +1,10 @@
 [CustomElementRegistry.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   [customElements.define must get callbacks of the constructor prototype]
     expected: FAIL
 
   [customElements.define must rethrow an exception thrown while getting callbacks on the constructor prototype]
     expected: FAIL
 
   [customElements.define must rethrow an exception thrown while converting a callback value to Function callback type]
     expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/adopted-callback.html.ini
@@ -0,0 +1,2 @@
+[adopted-callback.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/connected-callbacks.html.ini
@@ -0,0 +1,2 @@
+[connected-callbacks.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/custom-element-registry/upgrade.html.ini
@@ -0,0 +1,2 @@
+[upgrade.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/disconnected-callbacks.html.ini
@@ -0,0 +1,2 @@
+[disconnected-callbacks.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/custom-elements/reactions/Element.html.ini
+++ b/testing/web-platform/meta/custom-elements/reactions/Element.html.ini
@@ -1,9 +1,10 @@
 [Element.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   [removeAttribute on Element must not enqueue an attributeChanged reaction when removing an attribute that does not exist]
     expected: FAIL
 
   [removeAttributeNS on Element must not enqueue an attributeChanged reaction when removing an attribute that does not exist]
     expected: FAIL
 
   [removeAttributeNode on Element must not enqueue an attributeChanged reaction when removing an attribute that does not exist]
     expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/reactions/ShadowRoot.html.ini
@@ -0,0 +1,2 @@
+[ShadowRoot.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
+++ b/testing/web-platform/meta/dom/events/event-global-extra.window.js.ini
@@ -1,5 +1,5 @@
 [event-global-extra.window.html]
-  prefs: [dom.window.event.enabled:true]
+  prefs: [dom.webcomponents.shadowdom.enabled:true, dom.window.event.enabled:true]
   [Listener from a different global]
     expected: FAIL
 
--- a/testing/web-platform/meta/dom/events/event-global.html.ini
+++ b/testing/web-platform/meta/dom/events/event-global.html.ini
@@ -1,2 +1,2 @@
 [event-global.html]
-  prefs: [dom.window.event.enabled:true]
+  prefs: [dom.webcomponents.shadowdom.enabled:true, dom.window.event.enabled:true]
--- a/testing/web-platform/meta/dom/events/relatedTarget.window.js.ini
+++ b/testing/web-platform/meta/dom/events/relatedTarget.window.js.ini
@@ -1,7 +1,8 @@
 [relatedTarget.window.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   [Reset targets before activation behavior]
     expected: FAIL
 
   [Retarget a shadow-tree relatedTarget]
     expected: FAIL
 
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -1,9 +1,10 @@
 [interfaces.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   [Document interface: attribute origin]
     expected: FAIL
     bug: 931884
 
   [Document interface: xmlDoc must inherit property "origin" with the proper type (3)]
     expected: FAIL
     bug: 931884
 
--- a/testing/web-platform/meta/html/dom/reflection-misc.html.ini
+++ b/testing/web-platform/meta/html/dom/reflection-misc.html.ini
@@ -1,10 +1,10 @@
 [reflection-misc.html]
-  prefs: [dom.dialog_element.enabled: true]
+  prefs: [dom.dialog_element.enabled: true, dom.webcomponents.shadowdom.enabled:true]
   [html.tabIndex: setAttribute() to object "3" followed by getAttribute()]
     expected: FAIL
 
   [html.tabIndex: setAttribute() to object "3" followed by IDL get]
     expected: FAIL
 
   [script.tabIndex: setAttribute() to object "3" followed by getAttribute()]
     expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/not-in-shadow-tree.html.ini
@@ -0,0 +1,2 @@
+[not-in-shadow-tree.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/html/semantics/forms/the-form-element/form-elements-filter.html.ini
+++ b/testing/web-platform/meta/html/semantics/forms/the-form-element/form-elements-filter.html.ini
@@ -1,2 +1,3 @@
 [form-elements-filter.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   max-asserts: 3
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/semantics/forms/the-label-element/label-attributes.sub.html.ini
@@ -0,0 +1,2 @@
+[label-attributes.sub.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/html/semantics/interfaces.html.ini
+++ b/testing/web-platform/meta/html/semantics/interfaces.html.ini
@@ -1,10 +1,10 @@
 [interfaces.html]
-  prefs: [dom.dialog_element.enabled: true]
+  prefs: [dom.dialog_element.enabled: true, dom.webcomponents.shadowdom.enabled:true]
   [Interfaces for image]
     expected: FAIL
 
   [Interfaces for keygen]
     expected: FAIL
 
   [Interfaces for IMAGE]
     expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/intersection-observer/shadow-content.html.ini
@@ -0,0 +1,2 @@
+[shadow-content.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/payment-request/interfaces.https.html.ini
+++ b/testing/web-platform/meta/payment-request/interfaces.https.html.ini
@@ -1,9 +1,10 @@
 [interfaces.https.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   [PaymentRequest interface: existence and properties of interface object]
     expected:
       if not e10s: FAIL
 
   [PaymentRequest interface object length]
     expected:
       if not e10s: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/shadow-dom/__dir__.ini
@@ -0,0 +1,1 @@
+prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/shadow-dom/untriaged/events/event-dispatch/test-003.html.ini
@@ -0,0 +1,2 @@
+[test-003.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
--- a/testing/web-platform/meta/touch-events/touch-retargeting.html.ini
+++ b/testing/web-platform/meta/touch-events/touch-retargeting.html.ini
@@ -1,4 +1,5 @@
 [touch-retargeting.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
   [TouchEvent's touches, targetTouches, and changedTouches should be retargeted.]
     expected: FAIL
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_shadowdom.js
@@ -1,20 +1,39 @@
 "use strict";
 
+ChromeUtils.defineModuleGetter(this, "Preferences",
+                               "resource://gre/modules/Preferences.jsm");
+
 // ExtensionContent.jsm needs to know when it's running from xpcshell,
 // to use the right timeout for content scripts executed at document_idle.
 ExtensionTestUtils.mockAppInfo();
 
 const server = createHttpServer();
 server.registerDirectory("/data/", do_get_file("data"));
 
 const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
 
 add_task(async function test_contentscript_shadowDOM() {
+  const PREFS = {
+    "dom.webcomponents.shadowdom.enabled": true,
+  };
+
+  // Set prefs to our initial values.
+  for (let pref in PREFS) {
+    Preferences.set(pref, PREFS[pref]);
+  }
+
+  registerCleanupFunction(() => {
+    // Reset the prefs.
+    for (let pref in PREFS) {
+      Preferences.reset(pref);
+    }
+  });
+
   function backgroundScript() {
     browser.test.assertTrue("openOrClosedShadowRoot" in document.documentElement,
                             "Should have openOrClosedShadowRoot in Element in background script.");
   }
 
   function contentScript() {
     let host = document.getElementById("host");
     browser.test.assertTrue("openOrClosedShadowRoot" in host, "Should have openOrClosedShadowRoot in Element.");
--- a/toolkit/components/tooltiptext/tests/browser_shadow_dom_tooltip.js
+++ b/toolkit/components/tooltiptext/tests/browser_shadow_dom_tooltip.js
@@ -1,13 +1,14 @@
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv(
-    {"set": [["ui.tooltipDelay", 0]]});
+    {"set": [["ui.tooltipDelay", 0],
+             ["dom.webcomponents.shadowdom.enabled", true]]});
 });
 
 add_task(async function test_title_in_shadow_dom() {
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
 
   info("Moving mouse out of the way.");
   await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 300, 300);
 
--- a/toolkit/content/tests/widgets/chrome.ini
+++ b/toolkit/content/tests/widgets/chrome.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 prefs =
   dom.ua_widget.enabled=true
+  dom.webcomponents.shadowdom.enabled=true
 skip-if = os == 'android'
 support-files =
   tree_shared.js
   popup_shared.js
   window_label_checkbox.xul
   window_menubar.xul
   seek_with_sound.ogg
 
--- a/toolkit/content/tests/widgets/mochitest.ini
+++ b/toolkit/content/tests/widgets/mochitest.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 prefs =
   dom.ua_widget.enabled=true
+  dom.webcomponents.shadowdom.enabled=true
 support-files =
   audio.wav
   audio.ogg
   file_videocontrols_jsdisabled.html
   seek_with_sound.ogg
   video.ogg
   head.js
   tree_shared.js