Bug 1504355 - Do not return inlineTextChild for shadow hosts in inspector walker;r=ladybenko
authorJulian Descottes <jdescottes@mozilla.com>
Thu, 15 Nov 2018 10:46:06 +0000
changeset 446576 acb7047f9492ce3d9fc299f0fbecf1f9c53a5ff9
parent 446575 85d893b168424ecc49fe597c45fd89dd53b8925f
child 446577 18eddae8a2682764bbc72f19b7644e892ffcfe6a
push id73060
push userjdescottes@mozilla.com
push dateThu, 15 Nov 2018 10:46:51 +0000
treeherderautoland@acb7047f9492 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersladybenko
bugs1504355
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1504355 - Do not return inlineTextChild for shadow hosts in inspector walker;r=ladybenko Differential Revision: https://phabricator.services.mozilla.com/D11742
devtools/client/inspector/markup/test/browser_markup_shadowdom.js
devtools/server/actors/inspector/walker.js
--- a/devtools/client/inspector/markup/test/browser_markup_shadowdom.js
+++ b/devtools/client/inspector/markup/test/browser_markup_shadowdom.js
@@ -188,24 +188,63 @@ const TEST_DATA = [
             let shadowRoot = this.attachShadow({mode: "#MODE#"});
             shadowRoot.innerHTML = "";
           }
         });
       </script>`,
     tree: `
       test-component
         #shadow-root`,
+  }, {
+    // Test shadow hosts show their shadow root even if they contain just a short text.
+    title: "shadow host with inline-text-child",
+    url: `data:text/html;charset=utf-8,
+      <test-component>
+        <inner-component>short-text-outside</inner-component>
+      </test-component>
+
+      <script>
+        "use strict";
+
+        customElements.define("test-component", class extends HTMLElement {
+          constructor() {
+            super();
+            let shadowRoot = this.attachShadow({mode: "#MODE#"});
+            shadowRoot.innerHTML = "<div><slot></slot></div>";
+          }
+        });
+
+        customElements.define("inner-component", class extends HTMLElement {
+          constructor() {
+            super();
+            let shadowRoot = this.attachShadow({mode: "#MODE#"});
+            shadowRoot.innerHTML = "short-text-inside";
+          }
+        });
+      </script>`,
+    tree: `
+      test-component
+        #shadow-root
+          div
+            slot
+              inner-component!slotted
+        inner-component
+          #shadow-root
+            short-text-inside
+          short-text-outside`,
   },
 ];
 
 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`);
-    const {inspector} = await openInspectorForURL(url.replace("#MODE#", "open"));
+    const {inspector, tab} = await openInspectorForURL(url.replace(/#MODE#/g, "open"));
     await assertMarkupViewAsTree(tree, "test-component", inspector);
+    await removeTab(tab);
   });
   add_task(async function() {
     info(`Testing: [${title}] in CLOSED mode`);
-    const {inspector} = await openInspectorForURL(url.replace("#MODE#", "closed"));
+    const {inspector, tab} = await openInspectorForURL(url.replace(/#MODE#/g, "closed"));
     await assertMarkupViewAsTree(tree, "test-component", inspector);
+    await removeTab(tab);
   });
 }
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -508,24 +508,34 @@ var WalkerActor = protocol.ActorClassWit
    * content small enough to be inlined, return that child's NodeActor.
    *
    * @param NodeActor node
    */
   inlineTextChild: function(node) {
     // Quick checks to prevent creating a new walker if possible.
     if (isBeforePseudoElement(node.rawNode) ||
         isAfterPseudoElement(node.rawNode) ||
+        isShadowHost(node.rawNode) ||
         node.rawNode.nodeType != Node.ELEMENT_NODE ||
         node.rawNode.children.length > 0) {
       return undefined;
     }
 
-    const walker = isDirectShadowHostChild(node.rawNode)
-      ? this.getNonAnonymousWalker(node.rawNode)
-      : this.getDocumentWalker(node.rawNode);
+    let walker;
+    try {
+      // By default always try to use an anonymous walker. Even for DirectShadowHostChild,
+      // children should be available through an anonymous walker (unless the child is not
+      // slotted, see catch block).
+      walker = this.getDocumentWalker(node.rawNode);
+    } catch (e) {
+      // Using an anonymous walker might throw, for instance on unslotted shadow host
+      // children.
+      walker = this.getNonAnonymousWalker(node.rawNode);
+    }
+
     const firstChild = walker.firstChild();
 
     // Bail out if:
     // - more than one child
     // - unique child is not a text node
     // - unique child is a text node, but is too long to be inlined
     // - we are a slot -> these are always represented on their own lines with
     //                    a link to the original node.