I was surprised to discover that a node.firstChild can be anonymous:
var nodelist = document.getAnonymousNodes(node); if (nodelist[0] == node.firstChild) // !! DOM API gives anonymous nodes??
Am I mistaken? Can this be true? All we know about node is that it is from a XUL document.
My goal is to enumerate over all of the nodes. I thought I just had figured it out: walk over the anonymous nodes from the list above, then the regular nodes. But I was assuming that the regular nodes start with node.firstChild, and that does not seem to be the case.
John J. Barton wrote: > var nodelist = document.getAnonymousNodes(node); > if (nodelist[0] == node.firstChild) > // !! DOM API gives anonymous nodes??
I can't see how that could happen, although it would be interesting to see what document.getBindingParent(node.firstChild) returns. I say that because when you start using XBL heavily things get confusing very quickly.
Consider <foo><bar/></foo>. Now foo has an XBL binding whose contents are <baz><children/></baz>. This means that an event targetted at <bar> bubbles up through <baz> and then <foo>, which is confusing for <baz>, as its hasChildNodes() returns false!
> I was surprised to discover that a node.firstChild can be anonymous:
It can't.
> var nodelist = document.getAnonymousNodes(node); > if (nodelist[0] == node.firstChild) > // !! DOM API gives anonymous nodes??
I think you were mislead by the name of the (sadly undocumented and possibly mis-named) getAnonymousNodes method. I do apologize for this mess of an API. I've documented getAnonymousNodes a bit, in hopes of making its behavior more clear.
getAnonymousNodes returns non-null only if the element passed to it has an XBL binding attached to it. In this case, it returns a nodelist representing the children of this element in the flattened tree. This list can include some of the element's actual DOM kids, as well as nodes that come from the element's binding or nodes that come from bindings on ancestors of the element.
For example, consider a binding with a <content> section that looks like this:
If this binding is attached to an element, the getAnonymousNodes for that element will be a nodelist containing the <span> followed by all the element's kids (both its DOM kids, and kids coming from <children> kids due to bindings on ancestors), in order.
> My goal is to enumerate over all of the nodes.
"all" in what sense?
> But I was assuming that the regular nodes start with node.firstChild
Boris Zbarsky wrote: > On 10/11/09 1:54 PM, John J. Barton wrote: >> I was surprised to discover that a node.firstChild can be anonymous:
> It can't.
>> var nodelist = document.getAnonymousNodes(node); >> if (nodelist[0] == node.firstChild) >> // !! DOM API gives anonymous nodes??
> I think you were mislead by the name of the (sadly undocumented and > possibly mis-named) getAnonymousNodes method. I do apologize for this > mess of an API. I've documented getAnonymousNodes a bit, in hopes of > making its behavior more clear.
Where is it documented?
> getAnonymousNodes returns non-null only if the element passed to it has > an XBL binding attached to it. In this case, it returns a nodelist > representing the children of this element in the flattened tree. This
What is a "flattened tree"? You chopped it down or what? ;-)
> list can include some of the element's actual DOM kids, as well as nodes
Hmm..."some" is not a very encouraging word for an API.
> that come from the element's binding or nodes that come from bindings on > ancestors of the element.
Huh?
> For example, consider a binding with a <content> section that looks like > this:
> If this binding is attached to an element, the getAnonymousNodes for > that element will be a nodelist containing the <span> followed by all > the element's kids (both its DOM kids, and kids coming from <children> > kids due to bindings on ancestors), in order.
Ok, so which of any of that would a xul developer want to see in Chromebug?
>> My goal is to enumerate over all of the nodes.
> "all" in what sense?
In the the sense of "every single one"? I didn't expect to have to also add "and not a single extra one".
>> But I was assuming that the regular nodes start with node.firstChild
> This assumption is correct.
Ok, well that is one would be the only thing that makes sense here then.
>> getAnonymousNodes returns non-null only if the element passed to it has an >> XBL binding attached to it. In this case, it returns a nodelist >> representing the children of this element in the flattened tree. This
> What is a "flattened tree"? You chopped it down or what? ;-)
Google it, the definition in the XBL2 spec ("final flattened tree") is accurate, although it uses XBL2 terminology. This is the tree with both anonymous and real nodes (with all bindings applied).
> list can include some of the element's actual DOM kids, as well as nodes
> Hmm..."some" is not a very encouraging word for an API.
> that come from the element's binding or nodes that come from bindings on >> ancestors of the element.
> Huh?
I guess bz hints at <children>'s behavior, it has interesting @include attr and can be non-direct child of the bound element.
>> If this binding is attached to an element, the getAnonymousNodes for that >> element will be a nodelist containing the <span> followed by all the >> element's kids (both its DOM kids, and kids coming from <children> kids due >> to bindings on ancestors), in order.
> Ok, so which of any of that would a xul developer want to see in Chromebug?
Obviously a rhetorical question that will be followed by bz trying to determine what does a "xul developer" want and so on... :)
>>> getAnonymousNodes returns non-null only if the element passed to it has an >>> XBL binding attached to it. In this case, it returns a nodelist >>> representing the children of this element in the flattened tree. This
>> What is a "flattened tree"? You chopped it down or what? ;-)
> Google it, the definition in the XBL2 spec ("final flattened tree") is > accurate, although it uses XBL2 terminology. This is the tree with both > anonymous and real nodes (with all bindings applied).
So, getAnonymousNodes returns all of the nodes, that is the regular nodes and the anonymous nodes, in one list, is that correct?
If I visit each node in the list returned by getAnonymousNodes, and call getAnonymousNodes() on them, recursively, I will visit all of the nodes once?
Specifically I would visit node.firstChild, node.firstChild.nextSibling, etc?
I gather that there is no XBL equivalent to nextSibling? So given an element that is a child, computing the sibling would require iterating over the parent's nodelist from getAnonymousNodes(). (Firebug's HTML panel uses nextSibling iteration).
John J. Barton wrote: > So, getAnonymousNodes returns all of the nodes, that is the regular > nodes and the anonymous nodes, in one list, is that correct?
For elements with their own XBL anonymous nodes, yes. (As per my previous post, elements can have anonymous children without necessarily having an XBL binding.)
> If I visit each node in the list returned by getAnonymousNodes, and > call getAnonymousNodes() on them, recursively, I will visit all of the > nodes once?
You would not visit nodes more than once. Unfortunately you might not visit some nodes at all.
> I gather that there is no XBL equivalent to nextSibling? So given an > element that is a child, computing the sibling would require iterating > over the parent's nodelist from getAnonymousNodes().
Worse, you would need to use the anonymous parent, which XBL1 doesn't expose.
>> getAnonymousNodes returns non-null only if the element passed to it >> has an XBL binding attached to it. In this case, it returns a nodelist >> representing the children of this element in the flattened tree. This
> What is a "flattened tree"? You chopped it down or what? ;-)
Nickolay mostly covered this too. Basically, the flattened tree is the dom-like tree one uses to actually construct CSS boxes in the presence of XBL. Its parent/child relationships have been modified by XBL binding insertion points.
>> list can include some of the element's actual DOM kids, as well as nodes
> Hmm..."some" is not a very encouraging word for an API.
Well, it depends on the binding's details. If your binding has:
then getAnonymousNodes will return the "span" children of the node, in order, then the foobar element. The other children of the node will be kids of the foobar in the flattened tree. This is really not a function of the getAnonymousNodes API but of the flattened tree construction model.
>> that come from the element's binding or nodes that come from bindings >> on ancestors of the element.
> Huh?
Consider this document:
<div> <span> </div>
And a binding that's applied to the <div> which has:
And a binding that's applied to the <hbox> which has:
<content> <bar/> <children/> </content>
Then getAnonymousNodes on the <hbox> will return a list containing the <bar>, <foo>, <span> in that order. The <bar> comes from the hbox binding, the <foo> is a DOM child of the hbox, the <span> comes from a binding on an ancestor (and the binding parent, in this case, but in general it would be some binding ancestor) of the hbox.
None of this is magic about getAnonymousNodes; all the magic is in the XBL flattened tree construction.
> Ok, so which of any of that would a xul developer want to see in Chromebug?
A XUL developer is most likely to want to see the flattened tree kids for an element. That means the contents of getAnonymousNodes if it's non-null. If its null, they'd want to see the kids with all insertion points under the element resolved. I don't believe there's a JS API for getting this nodelist directly. DOM Inspector uses the inIDeepTreeWalker component to walk over the flattened tree.
>> "all" in what sense?
> In the the sense of "every single one"? I didn't expect to have to also > add "and not a single extra one".
Yes, but all nodes in the document? In some subtree? In some child list? Something else?
You should probably seriously consider just doing what DOM Inspector does here.
> So, getAnonymousNodes returns all of the nodes, that is the regular > nodes and the anonymous nodes, in one list, is that correct?
No. It returns exactly what it returns. The nodes that should look like the kids of the given node while constructing the box model. And only if the node has a binding attached to it. Sometimes this list includes some of the element's DOM kids. Sometimes it does not.
> If I visit each node in the list returned by getAnonymousNodes, and call > getAnonymousNodes() on them, recursively, I will visit all of the nodes > once?
No. See my previous mail.
> Specifically I would visit node.firstChild, node.firstChild.nextSibling, > etc?
No.
> I gather that there is no XBL equivalent to nextSibling?
Not at the moment, though there was a bug on implementing flattened tree traversal methods at some point.
Boris Zbarsky wrote: > On 10/12/09 12:49 AM, John J. Barton wrote: ... >> I gather that there is no XBL equivalent to nextSibling?
> Not at the moment, though there was a bug on implementing flattened tree > traversal methods at some point.
> You should seriously look into inIDeepTreeWalker.
Thanks and thanks to Neil for all of your help. Unfortunately it sounds like supporting anonymous nodes would be a much bigger effort than it seemed when I started. I better re-assess the cost/benefit here.
> Thanks and thanks to Neil for all of your help. Unfortunately it sounds > like supporting anonymous nodes would be a much bigger effort than it > seemed when I started. I better re-assess the cost/benefit here.
To be clear, it's impossible to sanely debug chrome without this feature.
Boris Zbarsky wrote: > On 10/12/09 12:32 PM, Boris Zbarsky wrote: >> To be clear, it's impossible to sanely debug chrome without this feature.
> And to be even more clear, there are 672 non-anonymous nodes in the > Firefox UI. There are 5000-ish nodes total (anonymous and not).
Well there are 2M firebug users and 4 users of Chromebug. I have to be realistic about where to invest a week's time. Adding anonymous node support may or may not change that dynamic. We use Chromebug on Firebug all of the time and never found a need for the anonymous info. I have no way to tell what features are essential and what impact could result from adding stuff beyond what we need for Firebug debugging.
On the plus side, I did look into your suggestion. inIDeepTreeWalker extends nsIDOMTreeWalker. Neither of these have MDC pages. I found zero uses of inIDeepTreeWalker in mozilla-central. http://mxr.mozilla.org/mozilla-central/search?string=inIDeepTreeWalker The IDL for nsIDOMTreeWalker as a cryptic comment // Introduced in DOM Level 2: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/traversa... Eventually I discovered that the DOM Level 2 has an interface call TreeWalker, http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/tr... so that is what that comment must mean. The method calls on TreeWalker look to be identical to the properties used by Firebug for its HTML traversal. The only real difference is that the state of the traversal is not carried in the model (node). To switch means understanding the lifetime of the traversal in Firebug so that the lifetime of the walker matches it, and understanding which flags to set on the walker.
> On the plus side, I did look into your suggestion. inIDeepTreeWalker > extends nsIDOMTreeWalker. Neither of these have MDC pages. I found zero > uses of inIDeepTreeWalker in mozilla-central.
> On 10/12/09 10:32 AM, John J. Barton wrote: >> On the plus side, I did look into your suggestion. inIDeepTreeWalker >> extends nsIDOMTreeWalker. Neither of these have MDC pages. I found zero >> uses of inIDeepTreeWalker in mozilla-central.
And as far as "what would a XUL developer want", in my experience what DOM Inspector does is more than sufficient. So making Firebug use the same algorithm would be great...
Ah, hmm. It might only implement nextNode() and parentNode() traversal at the moment. That shouldn't be all that hard to fix. File bugs on any issues that block you?
As a note, this is a chicken and egg problem; I doubt you can get many more chromebug users without having this working, and you're not incentivised to get it working because chromebug is underused. In the end, it all depends on what your goals for chromebug are, I guess.
Boris Zbarsky wrote: > On 10/12/09 2:54 PM, John J. Barton wrote: >> I did. My reward: >> "[JavaScript Error: "uncaught exception: [Exception... "Component >> returned failure code: 0x80004001 (NS_ERROR_NOT_IMPLEMENTED) >> [inIDeepTreeWalker.firstChild]"
> Ah, hmm. It might only implement nextNode() and parentNode() traversal > at the moment. That shouldn't be all that hard to fix. File bugs on > any issues that block you?
The bit of code the implements nextNode(): http://mxr.mozilla.org/mozilla-central/source/layout/inspector/src/in... is a lot like the JS code I wrote when I thought I understood getAnonymousNodes(). Are there any JS equivalents to: (bindingManager = inLayoutUtils::GetBindingManagerFor(aNode))) { bindingManager->GetAnonymousNodesFor(content, getter_AddRefs(kids)); if (!kids) bindingManager->GetContentListFor(content, getter_AddRefs(kids)); ?
> As a note, this is a chicken and egg problem; I doubt you can get many > more chromebug users without having this working, and you're not > incentivised to get it working because chromebug is underused. In the > end, it all depends on what your goals for chromebug are, I guess.
Yes, though I think I've sorted out the issues with JS and with windows in Chromebug enough for it to be useful for most extensions. I realize that XBL binding is very important to mozilla application developers but not so much for extensions. As for goals, I want Chromebug to be Firebug for XUL, but that does not help me have enough time to achieve that goal.
> if (!kids) > bindingManager->GetContentListFor(content, > getter_AddRefs(kids));
No, and this is the key problem that prevents a JS implementation not using inIDeepTreeWalker from actually being able to walk all the nodes.
> Yes, though I think I've sorted out the issues with JS and with windows > in Chromebug enough for it to be useful for most extensions. I realize > that XBL binding is very important to mozilla application developers but > not so much for extensions.
I'm not sure why that would be the case, exactly... Extensions use a fair amount of XBL, not even including the built-in bindings they rely on.
Dan Mosedale wrote: > On 10/12/09 10:25 AM, Shawn Wilsher wrote: >> On 10/12/09 10:32 AM, John J. Barton wrote: >>> On the plus side, I did look into your suggestion. inIDeepTreeWalker >>> extends nsIDOMTreeWalker. Neither of these have MDC pages. I found zero >>> uses of inIDeepTreeWalker in mozilla-central.
> And as far as "what would a XUL developer want", in my experience what > DOM Inspector does is more than sufficient. So making Firebug use the > same algorithm would be great...
> Dan
I looked into the DOM Inspector code so see if this was feasible. As far as I can tell, the key bits, where we get the anonymous nodes, is down in inIDOMView, a XUL tree view thing implemented in C++. The thing that opens the view twisties to drill down seems to be selectElementInTree, http://mxr.mozilla.org/comm-central/source/mozilla/extensions/inspect... It uses the DOM Walker only to set up the selection. Then it uses operations on inIDOMView to fill in, esp. ToggleOpenState which calls ExpandNode, and that calls GetChildNodesFor(), then we arrive at code very similar to what Boris already said was not accessible to JS: http://mxr.mozilla.org/comm-central/source/mozilla/layout/inspector/s...
So I am afraid this too is a dead end, absent new C++ code.
> So I am afraid this too is a dead end, absent new C++ code.
OK. Could we get a bug filed with a description of what you need to make this work, whether it be more of the methods on inDeepTreeWalker implemented or whether it be a getter for the flattened tree child list? This should be trivial to fix on the Gecko end to give you the APIs you need to make your end work.
Boris Zbarsky wrote: > On 10/15/09 1:14 AM, John J. Barton wrote: >> So I am afraid this too is a dead end, absent new C++ code.
> OK. Could we get a bug filed with a description of what you need to > make this work, whether it be more of the methods on inDeepTreeWalker > implemented or whether it be a getter for the flattened tree child list? > This should be trivial to fix on the Gecko end to give you the APIs you > need to make your end work.