Bug 1053898 - Update NodeActor with new properties to detect slotted nodes in markup-view;r=bgrins draft
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 06 Mar 2018 18:31:46 +0100
changeset 773670 232795c1ee91a41ec667c8bcdc21eb73bcfcbf9a
parent 773669 31e8cb68519893a371504885fe34ee3e58372cfd
child 773671 95259e33e084038d7a71e89b90affb60c38894a3
push id104269
push userjdescottes@mozilla.com
push dateWed, 28 Mar 2018 08:16:27 +0000
reviewersbgrins
bugs1053898
milestone61.0a1
Bug 1053898 - Update NodeActor with new properties to detect slotted nodes in markup-view;r=bgrins Instead of filtering light DOM nodes in the actor, return enough information for the markup-view to filter out the nodes itself. The nodes will be displayed in a later changeset when the markup view can accommodate several containers for a single nodeFront. MozReview-Commit-ID: LFKYU24BLZB
devtools/client/inspector/markup/markup.js
devtools/server/actors/inspector/node.js
devtools/server/actors/inspector/walker.js
devtools/shared/fronts/node.js
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -1665,32 +1665,40 @@ MarkupView.prototype = {
     // We're going to issue a children request, make sure it includes the
     // centered node.
     let centered = this._checkSelectionVisible(container);
 
     // Children aren't updated yet, but clear the childrenDirty flag anyway.
     // If the dirty flag is re-set while we're fetching we'll need to fetch
     // again.
     container.childrenDirty = false;
+
+    let isShadowHost = container.node.isShadowHost;
     let updatePromise =
       this._getVisibleChildren(container, centered).then(children => {
         if (!this._containers) {
           return promise.reject("markup view destroyed");
         }
         this._queuedChildUpdates.delete(container);
 
         // If children are dirty, we got a change notification for this node
         // while the request was in progress, we need to do it again.
         if (container.childrenDirty) {
           return this._updateChildren(container, {expand: centered || expand});
         }
 
         let fragment = this.doc.createDocumentFragment();
 
         for (let child of children.nodes) {
+          let { isDirectShadowHostChild } = child;
+          if (!isShadowHost && isDirectShadowHostChild) {
+            // Temporarily skip light DOM nodes if the container's node is not a host
+            // element, which means that the node is a "slotted" node.
+            continue;
+          }
           let childContainer = this.importNode(child, flash);
           fragment.appendChild(childContainer.elt);
         }
 
         while (container.children.firstChild) {
           container.children.firstChild.remove();
         }
 
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -119,16 +119,18 @@ const NodeActor = protocol.ActorClassWit
       attrs: this.writeAttrs(),
       isBeforePseudoElement: this.isBeforePseudoElement,
       isAfterPseudoElement: this.isAfterPseudoElement,
       isAnonymous: isAnonymous(this.rawNode),
       isNativeAnonymous: isNativeAnonymous(this.rawNode),
       isXBLAnonymous: isXBLAnonymous(this.rawNode),
       isShadowAnonymous: isShadowAnonymous(this.rawNode),
       isShadowRoot: this.isShadowRoot,
+      isShadowHost: this.isShadowHost,
+      isDirectShadowHostChild: this.isDirectShadowHostChild,
       pseudoClassLocks: this.writePseudoClassLocks(),
 
       isDisplayed: this.isDisplayed,
       isInHTMLDocument: this.rawNode.ownerDocument &&
         this.rawNode.ownerDocument.contentType === "text/html",
       hasEventListeners: this._hasEventListeners,
     };
 
@@ -192,16 +194,21 @@ const NodeActor = protocol.ActorClassWit
     return this.rawNode.nodeName === "_moz_generated_content_after";
   },
 
   get isShadowRoot() {
     let isFragment = this.rawNode.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE;
     return isFragment && this.rawNode.host;
   },
 
+  get isShadowHost() {
+    let shadowRoot = this.rawNode.shadowRoot;
+    return shadowRoot && shadowRoot.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE;
+  },
+
   get isDirectShadowHostChild() {
     // Pseudo elements are always part of the anonymous tree.
     if (this.isBeforePseudoElement || this.isAfterPseudoElement) {
       return false;
     }
 
     let parentNode = this.rawNode.parentNode;
     return parentNode && parentNode.shadowRoot;
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -602,23 +602,22 @@ var WalkerActor = protocol.ActorClassWit
     if (options.center && options.start) {
       throw Error("Can't specify both 'center' and 'start' options.");
     }
     let maxNodes = options.maxNodes || -1;
     if (maxNodes == -1) {
       maxNodes = Number.MAX_VALUE;
     }
 
-    let isShadowHost = !!node.rawNode.shadowRoot;
-    let isShadowRoot = !!node.rawNode.host;
+    let { isShadowHost, isShadowRoot, isDirectShadowHostChild } = node;
 
     // Detect special case of unslotted shadow host children that cannot rely on a
     // regular anonymous walker.
     let isUnslottedHostChild = false;
-    if (node.isDirectShadowHostChild) {
+    if (isDirectShadowHostChild) {
       try {
         this.getDocumentWalker(node.rawNode, options.whatToShow, SKIP_TO_SIBLING);
       } catch (e) {
         isUnslottedHostChild = true;
       }
     }
 
     // We're going to create a few document walkers with the same filter,
@@ -689,22 +688,16 @@ var WalkerActor = protocol.ActorClassWit
       if (options.center && remaining > 0 && nodes[0].rawNode != firstChild) {
         let firstNodes = this._readBackward(backwardWalker, remaining);
 
         // Then put it all back together.
         nodes = firstNodes.concat(nodes);
       }
     }
 
-    // Temporarily filter out shadow host children when a walker returns them in a <slot>.
-    if (!isShadowHost) {
-      // Shadow host children should only be displayed under the host.
-      nodes = nodes.filter(n => !n.isDirectShadowHostChild);
-    }
-
     let hasFirst, hasLast;
     if (nodes.length > 0) {
       // Compare first/last with expected nodes before modifying the nodes array in case
       // this is a shadow host.
       hasFirst = nodes[0].rawNode == firstChild;
       hasLast = nodes[nodes.length - 1].rawNode == lastChild;
     } else {
       // If nodes is still an empty array, we are on a host element with a shadow root but
--- a/devtools/shared/fronts/node.js
+++ b/devtools/shared/fronts/node.js
@@ -294,16 +294,24 @@ const NodeFront = FrontClassWithSpec(nod
   get isDocumentElement() {
     return !!this._form.isDocumentElement;
   },
 
   get isShadowRoot() {
     return this._form.isShadowRoot;
   },
 
+  get isShadowHost() {
+    return this._form.isShadowHost;
+  },
+
+  get isDirectShadowHostChild() {
+    return this._form.isDirectShadowHostChild;
+  },
+
   // doctype properties
   get name() {
     return this._form.name;
   },
   get publicId() {
     return this._form.publicId;
   },
   get systemId() {