Bug 878379 - Change the walker's querySelector requests to provide any nodes needed to connect to the root node. r=jwalker

--HG--
extra : rebase_source : c234f32af28b5c137e679b1da1d9e5967fc08b3f
This commit is contained in:
Dave Camp 2013-06-06 16:09:16 -07:00
parent 85a97695bf
commit 9c3398edd6

View File

@ -111,8 +111,11 @@ var NodeActor = protocol.ActorClass({
// Returns the JSON representation of this object over the wire. // Returns the JSON representation of this object over the wire.
form: function(detail) { form: function(detail) {
let parentNode = this.walker.parentNode(this);
let form = { let form = {
actor: this.actorID, actor: this.actorID,
parent: parentNode ? parentNode.actorID : undefined,
nodeType: this.rawNode.nodeType, nodeType: this.rawNode.nodeType,
namespaceURI: this.namespaceURI, namespaceURI: this.namespaceURI,
nodeName: this.rawNode.nodeName, nodeName: this.rawNode.nodeName,
@ -263,6 +266,25 @@ let NodeFront = protocol.FrontClass(NodeActor, {
} }
}); });
/**
* Returned from any call that might return a node that isn't connected to root by
* nodes the child has seen, such as querySelector.
*/
types.addDictType("disconnectedNode", {
// The actual node to return
node: "domnode",
// Nodes that are needed to connect the node to a node the client has already seen
newNodes: "array:domnode"
});
types.addDictType("disconnectedNodeArray", {
// The actual node list to return
nodes: "array:domnode",
// Nodes that are needed to connect those nodes to the root.
newNodes: "array:domnode"
});
/** /**
* Server side of a node list as returned by querySelectorAll() * Server side of a node list as returned by querySelectorAll()
*/ */
@ -306,24 +328,36 @@ var NodeListActor = exports.NodeListActor = protocol.ActorClass({
* Get a single node from the node list. * Get a single node from the node list.
*/ */
item: method(function(index) { item: method(function(index) {
return this.walker._ref(this.nodeList[index]); let node = this.walker._ref(this.nodeList[index]);
let newNodes = [node for (node of this.walker.ensurePathToRoot(node))];
return {
node: node,
newNodes: newNodes
}
}, { }, {
request: { item: Arg(0) }, request: { item: Arg(0) },
response: { node: RetVal("domnode") } response: RetVal("disconnectedNode")
}), }),
/** /**
* Get a range of the items from the node list. * Get a range of the items from the node list.
*/ */
items: method(function(start=0, end=this.nodeList.length) { items: method(function(start=0, end=this.nodeList.length) {
return [this.walker._ref(item) let items = [this.walker._ref(item) for (item of Array.prototype.slice.call(this.nodeList, start, end))];
for (item of Array.prototype.slice.call(this.nodeList, start, end))]; let newNodes = new Set();
for (let item of items) {
this.walker.ensurePathToRoot(item, newNodes);
}
return {
nodes: items,
newNodes: [node for (node of newNodes)]
}
}, { }, {
request: { request: {
start: Arg(0, "number", { optional: true }), start: Arg(0, "number", { optional: true }),
end: Arg(1, "number", { optional: true }) end: Arg(1, "number", { optional: true })
}, },
response: { nodes: RetVal("array:domnode") } response: { nodes: RetVal("disconnectedNodeArray") }
}), }),
release: method(function() {}, { release: true }) release: method(function() {}, { release: true })
@ -348,7 +382,23 @@ var NodeListFront = exports.NodeLIstFront = protocol.FrontClass(NodeListActor, {
// Update the object given a form representation off the wire. // Update the object given a form representation off the wire.
form: function(json) { form: function(json) {
this.length = json.length; this.length = json.length;
} },
item: protocol.custom(function(index) {
return this._item(index).then(response => {
return response.node;
});
}, {
impl: "_item"
}),
items: protocol.custom(function(start, end) {
return this._items(start, end).then(response => {
return response.nodes;
});
}, {
impl: "_items"
})
}); });
// Some common request/response templates for the dom walker // Some common request/response templates for the dom walker
@ -389,16 +439,20 @@ var WalkerActor = protocol.ActorClass({
*/ */
initialize: function(conn, document, options) { initialize: function(conn, document, options) {
protocol.Actor.prototype.initialize.call(this, conn); protocol.Actor.prototype.initialize.call(this, conn);
this._doc = document; this._doc = document;
this._refMap = new Map(); this._refMap = new Map();
// Ensure that the root document node actor is ready and
// managed.
this.rootNode = this.document();
this.manage(this.rootNode);
}, },
// Returns the JSON representation of this object over the wire. // Returns the JSON representation of this object over the wire.
form: function() { form: function() {
return { return {
actor: this.actorID, actor: this.actorID,
root: this.document().form() root: this.rootNode.form()
} }
}, },
@ -487,6 +541,38 @@ var WalkerActor = protocol.ActorClass({
}, },
}), }),
parentNode: function(node) {
let walker = documentWalker(node.rawNode);
let parent = walker.parentNode();
if (parent) {
return this._ref(parent);
}
return null;
},
/**
* Add any nodes between `node` and the walker's root node that have not
* yet been seen by the client.
*/
ensurePathToRoot: function(node, newParents=new Set()) {
if (!node) {
return newParents;
}
let walker = documentWalker(node.rawNode);
let cur;
while ((cur = walker.parentNode())) {
let parent = this._refMap.get(cur);
if (!parent) {
// This parent didn't exist, so hasn't been seen by the client yet.
newParents.add(this._ref(cur));
} else {
// This parent did exist, so the client knows about it.
return newParents;
}
}
return newParents;
},
/** /**
* Return children of the given node. By default this method will return * 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 * all children of the node, but there are options that can restrict this
@ -690,15 +776,24 @@ var WalkerActor = protocol.ActorClass({
*/ */
querySelector: method(function(baseNode, selector) { querySelector: method(function(baseNode, selector) {
let node = baseNode.rawNode.querySelector(selector); let node = baseNode.rawNode.querySelector(selector);
return node ? this._ref(node) : null;
if (!node) {
return {
}
};
let node = this._ref(node);
let newParents = this.ensurePathToRoot(node);
return {
node: node,
newNodes: [parent for (parent of newParents)]
}
}, { }, {
request: { request: {
node: Arg(0, "domnode"), node: Arg(0, "domnode"),
selector: Arg(1) selector: Arg(1)
}, },
response: { response: RetVal("disconnectedNode")
node: RetVal("domnode", { optional: true })
}
}), }),
/** /**
@ -739,6 +834,14 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
this.rootNode = types.getType("domnode").read(json.root, this); this.rootNode = types.getType("domnode").read(json.root, this);
}, },
querySelector: protocol.custom(function(queryNode, selector) {
return this._querySelector(queryNode, selector).then(response => {
return response.node;
});
}, {
impl: "_querySelector"
}),
// XXX hack during transition to remote inspector: get a proper NodeFront // XXX hack during transition to remote inspector: get a proper NodeFront
// for a given local node. Only works locally. // for a given local node. Only works locally.
frontForRawNode: function(rawNode){ frontForRawNode: function(rawNode){