= The Simple Explanation = Templetized Trees are an issue because they don't copy their content elements into the DOM. That's pretty disastrous for a DOM based automation tool. This means mozmill can't inspect tree children elements and can't automate interaction with them through the UI interfaces. However, if the tree in question implements some of the standard tree object libraries, then we may have a solution for this. If you're interested in the gory details, read on.
= The Details =
This means that your standard, run of the mill XUL trees we can automate. It means that if you have subclassed a tree view implementation from nsITreeView AND you also have attached a nsITreeContentView to your created tree, we can automate you. However, if you did not add those to your tree implementation then we can't automate you. Specifically, we need the getItemAtIndex method which resides on the nsITreeContentView to get to you. It tried QI'ing the nsITreeView to an nsITreeContentView, but of course that didn't work (I was out of other ideas, I must admit).
So, the net of this is that in order to be testable by mozmill, we need your tree implementation to have both an nsITreeContentView object and a nsITreeView object implemented for it. Unfortunately not all custom-crafted trees implement both objects (places, I'm looking at you -- Dietrich, please tell me I'm wrong here). But for standard XUL trees (and trees that have these interfaces) we should be able to access the DOMElement interfaces of the children elements via this algorthim:
<standard recursive search for elements> if (element.tagName == "tree") { for (i=0 i < element.view.rowCount; ++i) { addInspectorListeners(element.contentView.getItemAtIndex(i)); }
The "addInspectorListener" is pseudocode for the listeners we attach to elements in order to make the inspector function work so that we can find these elements. Likely this means we'll have to either adapt the "Lookup" elementslib search or create a new elementslib search type for trees that we use once we have a tree.
We should also try to determine before doing this whether or not the tree in question implements the "view" and "viewContent" objects, because if it doesn't then our only option is to instruct the tester to use the backend XPCOM interfaces to that content to get the information there. Those nodes are not accessible outside of their trees (at least not as far as I can tell).
= Note this will make NO sense unless you've looked at the Songbird DOM = Let's take the songbird example (standard XUL trees) and use the mozmill shell to figure out the elements: // songbird window s = windows[1]; // grab the anon node right above the service pane XUL tree element: sbsvc = s.document.getElementById("sb_servicepane_treepane"); // get it's anon elements anons = s.document.getAnonymousNodes(sbsvc); // the first node is the XUL tree tree = anons[0]; // use the view to get the number of rows of tree children that exist numRows = tree.view.rowCount; // use contentView to grab the DOMElement you want from that specific row. e0 = tree.contentView.getItemAtIndex(0);
Mikeal, Adam, let me know what you think of this.
Cheers, Clint
PS this is now bug 477079, but let's discuss the issue here first to figure out a solution that will work for everyone. This list has wider distribution than the people that watch our bz component.
On Feb 5, 2009, at February 5, 200910:27 AM, Clint Talbert wrote:
> <standard recursive search for elements> > if (element.tagName == "tree") { > for (i=0 i < element.view.rowCount; ++i) { > addInspectorListeners(element.contentView.getItemAtIndex(i)); > }
So, I assume that getItemAtIndex returns a XULElement.
> The "addInspectorListener" is pseudocode for the listeners we attach > to > elements in order to make the inspector function work so that we can > find these elements. Likely this means we'll have to either adapt the > "Lookup" elementslib search or create a new elementslib search type > for > trees that we use once we have a tree.
We'll see, it depends on whether or not we can find a way to get all tree elements for a given document. If we can then we should create a new object, if we have to leverage all the other Lookup stuff to work our way down the tree then we'll want to add some syntax to Lookup. Adding to Lookup is more difficult so if at all possible I'd like to avoid it.
> We should also try to determine before doing this whether or not the > tree in question implements the "view" and "viewContent" objects, > because if it doesn't then our only option is to instruct the tester > to > use the backend XPCOM interfaces to that content to get the > information > there. Those nodes are not accessible outside of their trees (at > least > not as far as I can tell).
Can we just check if those attributes are undefined? Or do they exist even when they don't work?
> = Note this will make NO sense unless you've looked at the Songbird > DOM = > Let's take the songbird example (standard XUL trees) and use the > mozmill > shell to figure out the elements: > // songbird window > s = windows[1]; > // grab the anon node right above the service pane XUL tree element: > sbsvc = s.document.getElementById("sb_servicepane_treepane"); > // get it's anon elements > anons = s.document.getAnonymousNodes(sbsvc); > // the first node is the XUL tree > tree = anons[0]; > // use the view to get the number of rows of tree children that exist > numRows = tree.view.rowCount; > // use contentView to grab the DOMElement you want from that > specific row. > e0 = tree.contentView.getItemAtIndex(0);
This example concerns me.
Currently we add a listener to every window and all the elements, even anonymous ones, respond to that listener.
It's easy to pull out all the tree elements if they are not anonymous ( document.getElementsByTagName('tree') ), but I can't see a way to get all the tree elements behind anonymous nodes without parsing through every element in the dom and iterating over all of it's anonymous nodes recursively.
There is no way to get an anonymous node by tag name, and from what I can tell there is no common attribute either. Iterating over every element, then iterating over all it's anonymous nodes checking for tag name, all recursively, sounds INCREDIBLY expensive.
Adam can comment on this more accurately.
> Mikeal, Adam, let me know what you think of this.
As long as there are real elements that we can click on we can figure out a way to create expressions that can get at them, I'm not concerned about the viability of that, but it's a given that writing such expressions by hand will be close to impossible so my main concern is how we get listeners on all of these. Another issue we'll have is that new elements won't have listeners on them and we'll need to add an observer that goes though all the addListener stuff again every time a tree rebuild happens.
> On Feb 5, 2009, at February 5, 200910:27 AM, Clint Talbert wrote:
>> <standard recursive search for elements> >> if (element.tagName == "tree") { >> for (i=0 i< element.view.rowCount; ++i) { >> addInspectorListeners(element.contentView.getItemAtIndex(i)); >> }
> So, I assume that getItemAtIndex returns a XULElement.
yep, it does >> The "addInspectorListener" is pseudocode for the listeners we attach >> to >> elements in order to make the inspector function work so that we can >> find these elements. Likely this means we'll have to either adapt the >> "Lookup" elementslib search or create a new elementslib search type >> for >> trees that we use once we have a tree.
> We'll see, it depends on whether or not we can find a way to get all > tree elements for a given document. If we can then we should create a > new object, if we have to leverage all the other Lookup stuff to work > our way down the tree then we'll want to add some syntax to Lookup. > Adding to Lookup is more difficult so if at all possible I'd like to > avoid it.
>> We should also try to determine before doing this whether or not the >> tree in question implements the "view" and "viewContent" objects, >> because if it doesn't then our only option is to instruct the tester >> to >> use the backend XPCOM interfaces to that content to get the >> information >> there. Those nodes are not accessible outside of their trees (at >> least >> not as far as I can tell).
> Can we just check if those attributes are undefined? Or do they exist > even when they don't work?
they will either be undefined or null: either way: if(tree.viewContent) should do the right thing.
>> = Note this will make NO sense unless you've looked at the Songbird >> DOM = >> Let's take the songbird example (standard XUL trees) and use the >> mozmill >> shell to figure out the elements: >> // songbird window >> s = windows[1]; >> // grab the anon node right above the service pane XUL tree element: >> sbsvc = s.document.getElementById("sb_servicepane_treepane"); >> // get it's anon elements >> anons = s.document.getAnonymousNodes(sbsvc); >> // the first node is the XUL tree >> tree = anons[0]; >> // use the view to get the number of rows of tree children that exist >> numRows = tree.view.rowCount; >> // use contentView to grab the DOMElement you want from that >> specific row. >> e0 = tree.contentView.getItemAtIndex(0);
> This example concerns me.
> Currently we add a listener to every window and all the elements, even > anonymous ones, respond to that listener.
> It's easy to pull out all the tree elements if they are not anonymous > ( document.getElementsByTagName('tree') ), but I can't see a way to > get all the tree elements behind anonymous nodes without parsing > through every element in the dom and iterating over all of it's > anonymous nodes recursively.
> There is no way to get an anonymous node by tag name, and from what I > can tell there is no common attribute either. Iterating over every > element, then iterating over all it's anonymous nodes checking for tag > name, all recursively, sounds INCREDIBLY expensive.
> Adam can comment on this more accurately.
Well, this is the specific songbird case for their left hand menu. FWIW it's the first time I've ever seen a tree generated as an anonymous XBL node.
>> Mikeal, Adam, let me know what you think of this.
> As long as there are real elements that we can click on we can figure > out a way to create expressions that can get at them, I'm not > concerned about the viability of that, but it's a given that writing > such expressions by hand will be close to impossible so my main > concern is how we get listeners on all of these. Another issue we'll > have is that new elements won't have listeners on them and we'll need > to add an observer that goes though all the addListener stuff again > every time a tree rebuild happens.
Maybe I don't get it, but I think we already have this problem. If you have a page and you do something to element A that causes there to be an element B, then you'd have to reinpect (and add a listener) to element B in order to find out how to access it. Then in your test, you'd activate element A and do a waitForElement with the proper expression for element B. New tree elements wouldn't be any different as long as they are added into the same area of the tree on test run. Now if your run adds a bunch of stuff to the tree and resorts it three times, you're going to have a problem, but I'd argue you have that problem if you do that with some element that's not in a tree too, like a <select> drop down list.
> Well, this is the specific songbird case for their left hand menu. > FWIW > it's the first time I've ever seen a tree generated as an anonymous > XBL > node.
I don't think this will be the last time we see this considering how many other UIs are created by XBL.
>>> Mikeal, Adam, let me know what you think of this.
>> As long as there are real elements that we can click on we can figure >> out a way to create expressions that can get at them, I'm not >> concerned about the viability of that, but it's a given that writing >> such expressions by hand will be close to impossible so my main >> concern is how we get listeners on all of these. Another issue we'll >> have is that new elements won't have listeners on them and we'll need >> to add an observer that goes though all the addListener stuff again >> every time a tree rebuild happens.
> Maybe I don't get it, but I think we already have this problem. If you > have a page and you do something to element A that causes there to > be an > element B, then you'd have to reinpect (and add a listener) to > element B > in order to find out how to access it. Then in your test, you'd > activate element A and do a waitForElement with the proper expression > for element B. New tree elements wouldn't be any different as long as > they are added into the same area of the tree on test run. Now if > your > run adds a bunch of stuff to the tree and resorts it three times, > you're > going to have a problem, but I'd argue you have that problem if you do > that with some element that's not in a tree too, like a <select> drop > down list.
I'm not talking about test execution, I'm talking about during inspection. Currently the window listener works for all new elements, but we have to add individual listeners to every element in the tree view in order to inspect them. When a re-draw happens we'll need to find all the elements again and add listeners to any that don't already have them.
> I don't think this will be the last time we see this considering how
> many other UIs are created by XBL.
You are probably, unfortunately, right. :(
>>>> Mikeal, Adam, let me know what you think of this.
> I'm not talking about test execution, I'm talking about during
> inspection. Currently the window listener works for all new elements,
> but we have to add individual listeners to every element in the tree
> view in order to inspect them. When a re-draw happens we'll need to
> find all the elements again and add listeners to any that don't
> already have them.
I don't see how that's a problem during inspection. During inspection, the trees are not changing. But, there is a listener available on standard trees that will tell you when the tree is rebuilt.
tree.builder.addListener(). This takes a nsIXULTemplateBuilderListener object [1]. However, the builder object may or may not be available on all trees, as you have no doubt already guessed.
> = The Simple Explanation =
> Templetized Trees are an issue because they don't copy their content
> elements into the DOM. That's pretty disastrous for a DOM based
> automation tool. This means mozmill can't inspect tree children
> elements and can't automate interaction with them through the UI
> interfaces. However, if the tree in question implements some of the
> standard tree object libraries, then we may have a solution for this.
> If you're interested in the gory details, read on.
> = The Details =
> This means that your standard, run of the mill XUL trees we can
> automate. It means that if you have subclassed a tree view
> implementation from nsITreeView AND you also have attached a
> nsITreeContentView to your created tree, we can automate you. > However,
> if you did not add those to your tree implementation then we can't
> automate you. Specifically, we need the getItemAtIndex method which
> resides on the nsITreeContentView to get to you. It tried QI'ing the
> nsITreeView to an nsITreeContentView, but of course that didn't work
> (I
> was out of other ideas, I must admit).
> So, the net of this is that in order to be testable by mozmill, we
> need
> your tree implementation to have both an nsITreeContentView object
> and a
> nsITreeView object implemented for it. Unfortunately not all
> custom-crafted trees implement both objects (places, I'm looking at
> you
> -- Dietrich, please tell me I'm wrong here). But for standard XUL
> trees > (and trees that have these interfaces) we should be able to access the
> DOMElement interfaces of the children elements via this algorthim:
> <standard recursive search for elements>
> if (element.tagName == "tree") {
> for (i=0 i < element.view.rowCount; ++i) {
> addInspectorListeners(element.contentView.getItemAtIndex(i));
> }
> The "addInspectorListener" is pseudocode for the listeners we attach
> to
> elements in order to make the inspector function work so that we can
> find these elements. Likely this means we'll have to either adapt the
> "Lookup" elementslib search or create a new elementslib search type
> for
> trees that we use once we have a tree.
> We should also try to determine before doing this whether or not the
> tree in question implements the "view" and "viewContent" objects,
> because if it doesn't then our only option is to instruct the tester
> to
> use the backend XPCOM interfaces to that content to get the
> information
> there. Those nodes are not accessible outside of their trees (at
> least
> not as far as I can tell).
> = Note this will make NO sense unless you've looked at the Songbird
> DOM =
> Let's take the songbird example (standard XUL trees) and use the
> mozmill
> shell to figure out the elements:
> // songbird window
> s = windows[1];
> // grab the anon node right above the service pane XUL tree element:
> sbsvc = s.document.getElementById("sb_servicepane_treepane");
> // get it's anon elements
> anons = s.document.getAnonymousNodes(sbsvc);
> // the first node is the XUL tree
> tree = anons[0];
> // use the view to get the number of rows of tree children that exist
> numRows = tree.view.rowCount;
> // use contentView to grab the DOMElement you want from that
> specific row.
> e0 = tree.contentView.getItemAtIndex(0);
> Mikeal, Adam, let me know what you think of this.
> Cheers,
> Clint
> PS this is now bug 477079, but let's discuss the issue here first to
> figure out a solution that will work for everyone. This list has
> wider
> distribution than the people that watch our bz component.