Bug 1465873 - part1: Implement walker::countChildren() to count children without updating refMap;r=bgrins
MozReview-Commit-ID: CvJFIvn7Kdr
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -221,17 +221,17 @@ const NodeActor = protocol.ActorClassWit
if (numChildren === 0 && (hasContentDocument || hasSVGDocument)) {
// This might be an iframe with virtual children.
numChildren = 1;
}
// Normal counting misses ::before/::after. Also, some anonymous children
// may ultimately be skipped, so we have to consult with the walker.
if (numChildren === 0 || hasAnonChildren || this.isShadowHost) {
- numChildren = this.walker.children(this).nodes.length;
+ numChildren = this.walker.countChildren(this);
}
return numChildren;
},
get computedStyle() {
if (!this._computedStyle) {
this._computedStyle = CssLogic.getComputedStyle(this.rawNode);
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -575,16 +575,29 @@ var WalkerActor = protocol.ActorClassWit
// This parent did exist, so the client knows about it.
return newParents;
}
}
return newParents;
},
/**
+ * Return the number of children under the provided NodeActor.
+ *
+ * @param NodeActor node
+ * See JSDoc for children()
+ * @param object options
+ * See JSDoc for children()
+ * @return Number the number of children
+ */
+ countChildren: function(node, options = {}) {
+ return this._getChildren(node, options).nodes.length;
+ },
+
+ /**
* Return children of the given node. By default this method will return
* all children of the node, but there are options that can restrict this
* to a more manageable subset.
*
* @param NodeActor node
* The node whose children you're curious about.
* @param object options
* Named options:
@@ -596,19 +609,44 @@ var WalkerActor = protocol.ActorClassWit
* as possible in the list, given how close to the ends of the child
* list it is. Mutually exclusive with `start`.
* `whatToShow`: A bitmask of node types that should be included. See
* https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
*
* @returns an object with three items:
* hasFirst: true if the first child of the node is included in the list.
* hasLast: true if the last child of the node is included in the list.
- * nodes: Child nodes returned by the request.
+ * nodes: Array of NodeActor representing the nodes returned by the request.
*/
children: function(node, options = {}) {
+ const { hasFirst, hasLast, nodes } = this._getChildren(node, options);
+ return {
+ hasFirst,
+ hasLast,
+ nodes: nodes.map(n => this._ref(n))
+ };
+ },
+
+ /**
+ * Return chidlren of the given node. Contrary to children children(), this method only
+ * returns DOMNodes. Therefore it will not create NodeActor wrappers and will not
+ * update the refMap for the discovered nodes either. This makes this method safe to
+ * call when you are not sure if the discovered nodes will be communicated to the
+ * client.
+ *
+ * @param NodeActor node
+ * See JSDoc for children()
+ * @param object options
+ * See JSDoc for children()
+ * @return an object with three items:
+ * hasFirst: true if the first child of the node is included in the list.
+ * hasLast: true if the last child of the node is included in the list.
+ * nodes: Array of DOMNodes.
+ */
+ _getChildren: function(node, options = {}) {
if (isNodeDead(node)) {
return { hasFirst: true, hasLast: true, nodes: [] };
}
if (options.center && options.start) {
throw Error("Can't specify both 'center' and 'start' options.");
}
let maxNodes = options.maxNodes || -1;
@@ -622,17 +660,17 @@ var WalkerActor = protocol.ActorClassWit
isShadowRoot,
isTemplateElement,
} = node;
if (isTemplateElement) {
// <template> tags should have a single child pointing to the element's template
// content.
const documentFragment = node.rawNode.content;
- const nodes = [this._ref(documentFragment)];
+ const nodes = [documentFragment];
return { hasFirst: true, hasLast: true, nodes };
}
// Detect special case of unslotted shadow host children that cannot rely on a
// regular anonymous walker.
let isUnslottedHostChild = false;
if (isDirectShadowHostChild) {
try {
@@ -714,18 +752,18 @@ var WalkerActor = protocol.ActorClassWit
nodes = firstNodes.concat(nodes);
}
}
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;
+ hasFirst = nodes[0] == firstChild;
+ hasLast = nodes[nodes.length - 1] == lastChild;
} else {
// If nodes is still an empty array, we are on a host element with a shadow root but
// no direct children.
hasFirst = hasLast = true;
}
if (isShadowHost) {
// Use anonymous walkers to fetch ::before / ::after pseudo elements
@@ -734,23 +772,23 @@ var WalkerActor = protocol.ActorClassWit
const hasBefore = first && this._ref(first).isBeforePseudoElement;
const lastChildWalker = this.getDocumentWalker(node.rawNode);
const last = lastChildWalker.lastChild();
const hasAfter = last && this._ref(last).isAfterPseudoElement;
nodes = [
// #shadow-root
- this._ref(node.rawNode.openOrClosedShadowRoot),
+ node.rawNode.openOrClosedShadowRoot,
// ::before
- ...(hasBefore ? [this._ref(first)] : []),
+ ...(hasBefore ? [first] : []),
// shadow host direct children
...nodes,
// ::after
- ...(hasAfter ? [this._ref(last)] : []),
+ ...(hasAfter ? [last] : []),
];
}
return { hasFirst, hasLast, nodes };
},
/**
* Get the next sibling of a given node. Getting nodes one at a time
@@ -797,17 +835,17 @@ var WalkerActor = protocol.ActorClassWit
_readForward: function(walker, count) {
const ret = [];
let node = walker.currentNode;
do {
if (!walker.isSkippedNode(node)) {
// The walker can be on a node that would be filtered out if it didn't find any
// other node to fallback to.
- ret.push(this._ref(node));
+ ret.push(node);
}
node = walker.nextSibling();
} while (node && --count);
return ret;
},
/**
* Helper function for the `children` method: Read backward in the sibling
@@ -816,17 +854,17 @@ var WalkerActor = protocol.ActorClassWit
_readBackward: function(walker, count) {
const ret = [];
let node = walker.currentNode;
do {
if (!walker.isSkippedNode(node)) {
// The walker can be on a node that would be filtered out if it didn't find any
// other node to fallback to.
- ret.push(this._ref(node));
+ ret.push(node);
}
node = walker.previousSibling();
} while (node && --count);
ret.reverse();
return ret;
},
/**