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
--- 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() {