Closed Bug 206687 Opened 22 years ago Closed 8 years ago

Implement ElementXBL

Categories

(Core :: XBL, defect)

x86
All
defect
Not set
normal

Tracking

()

RESOLVED WONTFIX

People

(Reporter: WeirdAl, Unassigned)

References

()

Details

Attachments

(1 file)

A quick search of LXR shows we don't have a full implementation of DocumentXBL, or any implementation of ElementXBL. We don't even have IDL files for them. Bugzilla shows no open bugs against XBL for that purpose, so this bug is for tracking DocumentXBL and ElementXBL.
Dup of bug 206586? /be
I'm not cleared for that bug... ?
I'll let bsmedberg decide what's a dup. This may be the right single-issue (or dual-issue? Maybe we need a bug for each of DocumentXBL and ElementXBL?) bug that the other bug (which seems kind of "meta" to me) should depend on. I'm not sure the other bug should be security-sensitive any longer. Benjamin? /be
OK, this bug is going to be about the ElementXBL interface. Bug 206586 (now open) will be a tracking bug for the general architecture issues. I will open other dependent bugs (DocumentXBL and the security architecture) as needed. This is probably going to happen on a branch, because a security review may be required.
Assignee: hyatt → bsmedberg
Blocks: 206586
OS: Windows 98 → All
Summary: Implement DocumentXBL, ElementXBL → Implement ElementXBL
This is a first-crack at the ElementXBL interface. Note that this there are significant differences between what's here and the 1.0 specification on the mozilla.org website. I've kept addBinding() and removeBinding() even though they are currently (and probably permanently) unimplemented. Neil says they cannot be implemented until JS2.0 is avilable, because you need multiple-inheritence.
Hixie/hyatt please let me know what you think of this
This would be the interface on what? Anonymous nodes, or explicit nodes that are children of a bound element?
Ian, this interface would be implemented on every DOM node. e.g. on content nodes without XBL bindings, childNodesXBL would return the same NodeList as nsIDOMNode::childNodes returns. However, depending on the security discussions in bug 206586, the methods might throw exceptions for anonymous content that the page is not allowed to acccess.
Let me rephrase my question. If you have the following case: <a><b/></a> ...and: a { -moz-binding: url(...); } ...which creates: <a><foo:c><b/></foo:c></a> ...and: foo|c { -moz-binding: url(...); } ...which creates: <a><foo:c><b/><foo:bar/></foo:c></a> ...what would b.xblNextSibling return? In other words, what scopes does this interface cross?
This interface does not distinguish scope (it crosses all scope). So b.nextSiblingXBL returns the <foo:bar/> element.
Then I don't really like it. I'd rather have something like: IDL Definition: interface ElementXBL { readonly attribute NodeList xblChildNodes; readonly attribute Element bindingOwner; readonly attribute Element anonymousParent; void addBinding(in DOMString bindingURI); void removeBinding(in DOMString bindingURI); boolean hasBinding(in DOMString bindingURI); }; Properties xblChildNodes The xblChildNodes property is a NodeList that represents the true children of the element in the default view after insertion points and element tags have been applied. This property can be used to navigate the content model according to XBL after bindings have moved explicit and anonymous children using children tags. The property's value is the same as childNodes if the element is not bound in the default view. bindingOwner The bindingOwner property is used to obtain the bound element with the binding attached that is responsible for the generation of the specified anonymous node. This property enables an author to determine the scope of any content node. For content at the document-level scope, the property's value is null. anonymousParent The anonymousParent property's value is the anonymous parent in the default view for an element that was placed underneath an insertion point using the children element. The property's value is null if the element was not repositioned. Methods addBinding The addBinding method attaches the specified binding (and any bindings that the binding inherits from) to an element. This call is not necessarily synchronous. The binding may not be attached yet when the call completes. The binding is attached in all views. Parameters bindingURI of type DOMString A URI that specifies the location of a specific binding to attach. No Return Value No Exceptions removeBinding The removeBinding method detaches the specified binding (and any bindings that the binding inherits from explicitly using the extends attribute) from the element. This method can only detach bindings that were attached using addBinding. If the binding in question is not attached to this element (or was attached through CSS ), then the method does nothing. Parameters bindingURI of type DOMString A URI that specifies the location of a specific binding to detach. No Return Value No Exceptions hasBinding The hasBinding method walks up the bindings applied to the element in the default view and compares each binding's URI with the parameter passed. This can be used to check if an element has been bound to a particular binding in in order to ensure that the expected methods and properties are available. For example widgets may walk up their ancestors looking for an element that has been bound to the form-container binding in order to locate their scope (so that radio buttons may properly be mutually exclusive, or so that a submit button can properly submit a form). Parameters bindingURI of type DOMString A URI that specifies the location of a specific binding for which to look. Returns boolean true if any of the bindings match the parameter, false otherwise. No Exceptions This allows you to get from an element to its anonymous children, if you need to crawl them, while avoiding the problem of crossing scopes. The ElementXBL properties are thus all explicit scope-crossing properties. If you want to walk from an anonymous node to an explicit one you can do so using the Node properties nextSibling, etc.
Ian, see bug 206586, I'm looking for a interface that allows modifications of the anonymous content structure, not just read-walking.
I'm confused. How does the DOM Node interface not already provide this? e.g.: http://www.hixie.ch/tests/adhoc/xbl/019.html
Ian, your testcase is exactly what I'm trying to avoid. You are moving the explicit children, and they are becoming anonymous. If you try to submit that form, you won't get anything!
I think that's a bug (they shouldn't turn into anonymous nodes). I'll talk this over with hyatt and let you know what the conclusion is. If I haven't gotten back to you by the weekend, ping me again.
If I may offer my two bits: firstChildXBL, lastChildXBL, previousSiblingXBL, nextSiblingXBL seem like a touch of features not needed; ditto for insertBeforeXBL, replaceChildXBL, removeChildXBL. They aren't in the spec, and we should keep discussion of these to bug 206586. I'll comment more on the extensions in that bug; for now I propose this bug remain for implementing ElementXBL as given in the XBL 1.0 spec. (This bug is blocking that one, anyway.) Should I file a new bug for adding the bindingDocuments property of DocumentXBL to our current DocumentXBL implementation?
Ian, the elements should become anonymous. If you use normal DOM calls, you are inserting the nodes in a tree at the same scope level as the parent. Thus moving explicit children using normal DOM calls to a position underneath an anonymous element would result in them becoming anonymous. Having some sort of scope level invariant that is preserved across DOM operations is nonsensical and needlessly complex IMHO. I agree with Alex that a whole set of XBL-mirrored DOM operations is overkill. I also agree, though, that it should be possible to create and manipulate insertion points. I would, however, recommend that this manipulation be performed using a createInsertionPoint method on ElementXBL that hands back a new interface, XBLInsertionPoint. Similarly, you could expose an array of XBLInsertionPoints (e.g., insertionPoints) on the binding and have a removeInsertionPoint function. It is IMO not intuitive to have methods like xblAppendChild, because that really isn't how you should think of it. The insertion points have rules that must be matched dynamically, so objects could constantly be shifting around as they matched different XPath selectors on the <children>. The binding can even have its anonymous content dynamically destroyed if insertion points suddenly can't accommodate all children. Therefore any manipulation of insertion points needs to be done using specialized methods that manipulate only insertion points, and that don't attempt to actually explicitly place children under specific insertion points (in potential violation of the very rules that the insertion point is supposed to adhere to).
It is worth noting that I didn't specify insertion point routines on ElementXBL mainly because without rich XPath selection capabilities, it's not going to be all that useful yet. (Insertion points in general aren't terribly useful as implemented in XUL because they can only filter by tag name.)
In Ian's proposal anonymousParent could then be replaced with insertionParent, which could return an XBLInsertionPoint. I suppose this property could be settable and throw an exception if the element doesn't match the constraints of the insertion point. Elements could then stay locked to an insertion point as long as they continue to match that insertion point, so you could then create any number of totally generic insertion points (e.g., <children/>) and place kids under whichever one you choose by setting their insertionParent properties. So if you had some tab box with an anonymous |box|, and you then made that |box| contain row children, you could create a second row like this: var secondRowContainer = document.createElementNS("box", "xulns"); box.appendChild(secondRowContainer); var secondRowInsertionPoint = this.createInsertionPoint(secondRowContainer, 0, ""); // Parent, index, XPath selector for (var i = 11; i < 20; i++) // Put tabs 11-20 on second row. firstRowContainer.xblChildNodes[i].insertionParent = secondRowInsertionPoint;
BTW, addBinding and removeBinding are implementable without JS2.0. They're just hard.
interface XBLInsertionPoint { readonly Element element; readonly attribute unsigned int index; attribute DOMString includes; } and... interface ElementXBL { readonly attribute NodeList xblChildNodes; readonly attribute Element bindingOwner; attribute XBLInsertionPoint insertionParent; void addBinding(in DOMString bindingURL); void removeBinding(in DOMString bindingURL); XBLInsertionPoint createInsertionPoint(in Element parent, in unsigned int index, in DOMString includes); void removeInsertionPoint(in XBLInsertionPoint point); // Not sure how to represent a collection that isn't of Nodes. // Might have to link insertionPoints with next pointers. readonly Array(?) insertionPoints; };
D'oh. Insertion points also have a type (explicit vs. inherited), so add a fourth argument (type) to createInsertionPoint, and add a readonly boolean type field to XBLInsertionPoint.
David, I've again redrafted my proposal. Instead of "insertion points" (the <element> and <children> tags) I have documented a concept of "connection points". An insertion point is expanded into a connection point at binding-attachment. The problem with the interface you posted is that you cannot move explicit items around. http://bdsmedberg.no-ip.org/xbl-proposal.html (This DNS is blocked by some AOL DNS servers, so do a DNS lookup from an authoratative native server if that's a problem and use the IP address.)
You can move explicit items around with Hyatt's proposal simply by changing an element's insertionParent, as his example in comment 19.
One thing that isn't clear to me in hyatt's proposal is how the DOM would represent sibling insertion points, as in: <content> <children/> <divider xmlns=""/> <children/> </content> It seems it might be necessary to have the insertionPoints actually be Nodes that contain the explicit children, although then we'd have the interesting situation of the DOM not being what selectors match on.
OK, that makes some sense. Instead of the many Node-like methods, a single xblParent = someNode; call would work. I don't think you need the XBLInsertParent interface to make this work. If you keep my separation of "insertion points" and "connection points", you have an explicit place to insert new children, and you don't have to massage the DOM for CSS selectors and other evil things.
IMO we should not allow the same element to be inserted in more than one place, so <content> <children/> <xul:divider/> <children/> </content> would place the children in only one of the <children/> insertion points. Mozilla currently uses the last matching insertion point, and that seems good to me.
The <children/> elements could have different filters, the problem still stands.
I retract. xblParent = something still isn't enough. How do you rearrange nodes (or even insert them in the proper place)? Ian, I don't see what the problem is with the multiple children elements... the children are not children of the children element, they are merely connected at the point the <children/> element indicates... Do you mean that the <children/> element itself needs to be included in the xblChild* attribute.
XBLInsertionPoints have indices, so in this example: <content> <children/> <baz:foo/> <children/> </content> You have an insertion point with an index of 0, and a second insertion point with an index of 1. They are distinguishable. There is a problem with my proposal though in that an element can be shoved through an arbitrary number of insertion points before arriving at its final destination, which means that - rather than having a single "insertionParent", and element has a whole chain of insertion points that it potentially moved through. Therefore "insertionParent" should be removed. I think "xblParentNode" should come back as the way of getting your parent that you finally end up shoved under . It can return a Node. Instead of .insertionParent, we will need: XBLInsertionPoint getInsertionPointFor(Element childElement); and void setInsertionPointFor(Element childElement, XBLInsertionPoint insertionPoint);
You don't have to rearrange nodes. They stay in document order at a given insertion point. (There is some ambiguity regarding what happens when you mix different scope levels at the same insertion point, so it might be worthwhile to attempt to establish some total order of DOM nodes including scopes).
I oppose the ability to arrange children of an insertion point, because then you make it totally ambiguous how to order children when new DOM nodes implicitly arrive at an insertion point (e.g., if inserted dynamically into the document). If you scramble the order at all, then you make it impossible to determine an accurate position for new elements. I also haven't seen a use case yet for being able to rearrange elements at an insertion point.
David, Perhaps you don't like my proposal for separating "insertion points" and "connection points", but I believe that it handles arbitrary ordering of children without problem.
I think introducing the concept of connection points needlessly complicates matters. Insertion points can do content reordering, just insert an insertion point for each node if necessary. Since we say explicit children remember which of the insertion points that match it they are currently bound to, there isn't a problem.
The problem with your connection points proposal is that you're missing two key aspects of how insertion points work. (1) Insertion points are dynamic. Elements implicitly move as they match new filters. (2) An element can have an arbitrary number of insertion points due to nested bindings.
David, > (1) Insertion points are dynamic. Elements implicitly move as they match new > filters. My proposal constructs the tree using the <children>, but at that point only uses the <children> for new children, not existing children... which IMO is a benefit (but you wrote the spec, so in the end I'll bow to your decision). > (2) An element can have an arbitrary number of insertion points due to nested > bindings. Which my spec handles with ease. Each binding keeps its insertion points, which are used up the binding chain. My other problem is this... I am pretty certain I can implement my spec, because it does not involve an "extra" XBLInsertionPoint element. It may look like a sledgehammer, but I think that in terms of element parenting it's fairly streamlined. I'm afraid that an extra element will cause all sorts of DOM headaches.
interface ElementXBL { readonly attribute NodeList xblChildNodes; /* the true child list after all bindings are applied */ readonly attribute Node xblParentNode; /* the true parent list after all bindings are applied */ readonly attribute Element bindingOwner; /* the element that generated the scope for this anonymous node, if any */ void addBinding(in DOMString bindingURL); void removeBinding(in DOMString bindingURL); XBLInsertionPoint insertInsertionPoint( in Element parent, /* must be the element, or an anonymous node with bindingOwner set to this element */ in unsigned long index, /* the position in the given element at which to place the insertion point */ in DOMString includes, /* the filter (a CSS selector) that elements must match in order to be placed in this insertion point */ in boolean inherited /* the type of insertion point */ ); /* creates and inserts an insertion point */ void removeInsertionPoint(in XBLInsertionPoint point); /* removes an insertion point. Nodes that used to be in the insertion point are redistributed in the remaining ones. If some nodes no longer apply to any insertion point, the anonymous content is removed. */ readonly attribute XBLInsertionPointList insertionPoints; /* if the element is a bound element, returns the list of insertion points for this element. */ XBLInsertionPoint getInsertionPointFor(Element child); /* if the element is a bound element, and child is an element in the same scope, or a higher scope, than the element itself, this returns the XBLInsertionPoint in which the element was inserted. Otherwise, or if the bound element has no anonymous content, returns null. */ void changeInsertionPointFor(Element child, XBLInsertionPoint insertionPoint); /* changes the insertion point of the child element to the new insertion point. Raises an exception if the element is not a bound element, or if the child element is not in the same scope, or a higher scope, than the element itself, or if the element does not apply to the filter of that insertion point. */ }; interface XBLInsertionPoint { readonly Element element; readonly attribute unsigned long index; attribute DOMString includes; attribute boolean inherited; attribute readonly NodeList nodes; /* the list of nodes currently inserted in this insertion point */ } interface XBLInsertionPointList { XBLInsertionPoint item(in unsigned long index); readonly attribute unsigned long length; } If two insertion points have the same parent, index, and filter, then the order in which they were added determines the order of their elements.
oops, add the following after the removeBinding declaration: boolean hasBinding(in DOMString bindingURI);
...and change getInsertionPoint and changeInsertionPoint to use nodes instead of Elements when it comes to children (we need to be able to cope with text nodes).
Benjamin, so you're proposing that the filters at insertion points not be dynamic? What do you do then when a new element is added to a document and has to be placed at an insertion point? That case clearly has to be dynamic (it occurs all the time in XUL), and if you allow screwing around with the node order at insertion points, it then becomes ambiguous where to place the new child. I'm all for entertaining new ideas, but they can't regress/break existing behavior in XUL.
>> (1) Insertion points are dynamic. Elements implicitly move as they match new >> filters. > My proposal constructs the tree using the <children>, but at that point only > uses the <children> for new children, not existing children... which IMO is a > benefit If the <children/> elements have filters, e.g.: <children includes="value[checked]"/> <children includes="value:not([checked])"/> ...and you change an element so that it no longer matches one of the filters, it should change to the appropriate filter, whether or not it has been moved manually in the meantime. >> (2) An element can have an arbitrary number of insertion points due to nested >> bindings. > Which my spec handles with ease. Each binding keeps its insertion points, > which are used up the binding chain. The bindings in the middle can be removed and added dynamically, too. > an "extra" XBLInsertionPoint element The latest proposal (comment 37 to comment 39) doesn't either, the xbl insertion points do not appear in the DOM tree, only in separate mutable lists.
Another problem is that <children/> cannot be in the DOM as an element. You run into all sorts of problems if you leave that node in the DOM (which is what XBL used to do) and make life more complicated for scripts and bindings that are crawling around in the DOM. One idea might be to make connection points "sever" the insertion point relationship, i.e., to stop caring about filters/placement, but that gets really complicated in the presence of dynamic insertion points and nested bindings.
In the above proposal, forget everything that mentions inherited insertion points. We've got a better way of doing that that doesn't involve this DOM.
Yes, the child node order is the fatal flaw in my proposal. OK, I await Ian's next which doesn't use the DOM to manipulate insertion points.
How are you distinguishing between "active" insertion points (with or without filters; new nodes are inserted into these) and "passive" ones (manually inserted nodes, but no new child nodes go here)?
Ok, here's our current thinking: interface ElementXBL { readonly attribute NodeList xblChildNodes; /* the true child list after all bindings are applied */ readonly attribute Node xblParentNode; /* the true parent list after all bindings are applied */ readonly attribute Element bindingOwner; /* the element that generated the scope for this anonymous node, if any */ void addBinding(in DOMString bindingURL); void removeBinding(in DOMString bindingURL); boolean hasBinding(in DOMString bindingURI); XBLInsertionPoint insertInsertionPoint( in Element parent, /* must be the element, or an anonymous node with bindingOwner set to this element */ in unsigned long index, /* the position in the given element at which to place the insertion point */ in DOMString includes /* the filter (a CSS selector) that elements must match in order to be placed in this insertion point. Null values are treated like an empty string. */ ); /* creates and inserts an insertion point */ void removeInsertionPoint(in XBLInsertionPoint point); /* removes an insertion point. Nodes that used to be in the insertion point are redistributed in the remaining ones. If some nodes no longer apply to any insertion point, the anonymous content is removed. */ readonly attribute XBLInsertionPointList insertionPoints; /* if the element is a bound element, returns the list of insertion points for this element. */ XBLInsertionPoint getInsertionPointFor(Node child); /* if the element is a bound element, and child is a node in the same scope, or a higher scope, than the element itself, this returns the XBLInsertionPoint in which the node is inserted. Otherwise, or if the bound element has no anonymous content, returns null. */ void changeInsertionPointFor(Node child, XBLInsertionPoint insertionPoint); /* changes the insertion point of the child node to the new insertion point. Raises an exception if the element is not a bound element, or if the child node is not in the same scope, or a higher scope, than the element itself, or if the node does not apply to the filter of that insertion point. Nodes other than element nodes only apply when the filter string is empty (that's the "catch all" filter) */ }; interface XBLInsertionPoint { readonly Element element; readonly attribute unsigned long index; attribute DOMString includes; /* never null */ attribute readonly NodeList nodes; /* the list of nodes currently inserted in this insertion point */ } interface XBLInsertionPointList { XBLInsertionPoint item(in unsigned long index); readonly attribute unsigned long length; } If two insertion points have the same parent, index, and filter, then the order in which they were added determines the order of their elements. If an element is added to the DOM dynamically, its scope is that of its parent element. (There is thus no way to add an anonymous child to a bound element, although in practice that doesn't matter.) Changes to a bound element's anonymous content only affects that bound element. Other bindings are unaffected. A node is inserted into the first insertion point that it matches. If it is moved (using changeInsertionPoint), then it remains in the new insertion point while it matches it. If it stops matching that new insertion point, it gets placed into the first insertion point that it matches. (If it doesn't match any, then the entire anonymous content is removed.) In other words, changes to the insertion point are "sticky". If an element is inserted into an insertion point with an empty filter ("includes" list) then it will always match it while that insertion point exists, and will therefore never move to another insertion point unless the insertion point is dynamically removed.
> How are you distinguishing between "active" insertion points (with or without > filters; new nodes are inserted into these) and "passive" ones (manually > inserted nodes, but no new child nodes go here)? There is no distinction, except that if there are two identical insertion points, the second will never be used by new nodes.
OK, I like this and it looks implementable. A few questions... > There is no distinction, except that if there are two identical insertion > points, the second will never be used by new nodes. I would like to able to declare an insertion point which will never automatically receive children. Take an automatically-overflowing toolbar, for example: <binding id="overflowing-toolbar"> <content> <children id="tbMain" /> <xul:button type="menubutton"> <xul:menupopup> <children id="tbOverflow" disabled="true"/> </xul:menupopup> </xul:button> </content> </binding> While there is room on the toolbar, children should be placed in tbMain. But when there are too many children, the binding will set disabled="true" (or something similar) on tbMain, and set disabled="false" on tbOverflow. The same sort of thing applies to multi-row tabboxes and most of the other applications I can think of. > interface XBLInsertionPoint { > readonly attribute unsigned long index; What is this index? Is it needed?
Ian, I would use addInsertionPoint, since "insertInsertionPoint" just sounds clumsy.
Ben (do you go by Ben or Benjamin), index is an index into the childNodes array of the <children>'s parent. So for example: <content> <xul:box> <children/> <xul:separator/> <children/> </xul:box> </content> The first insertion point above has an index of 0. The second insertion point has an index of 1. Both have the same parent, a <xul:box>. I don't follow the logic in disabling an insertion point. What if someone uses elt.insertBefore to put an element at the beginning? Presumably that element would then need to be on the first row of e.g., a toolbar and not in the overflow. I believe some cleverness with CSS3 selectors could be used as filters on the insertion points, e.g., nth-child stuff, in order to make sure elements dynamically stay where they are supposed to. It's also worth noting that box should be able to wrap to multiple rows/columns in XUL, and that you shouldn't have to do a multi-row system using XBL. I will propose a new property, box-lines (with values of single/multiple) to cover this deficiency in the XUL box model.
I've never seen a bug gather so many comments in a twenty-four hour period. :) When you guys finish defining what ElementXBL really should be, I'll write up a bunch of testcases and/or documentation for you. Hixie: you probably understand better than I do the difference between a URL and a URI; is the difference between the respective arguments of addBinding, removeBinding and hasBinding intentional?
> I would like to able to declare an insertion point which will never > automatically receive children. Take an automatically-overflowing toolbar, > for example: > > <binding id="overflowing-toolbar"> > <content> > <children id="tbMain" /> > <xul:button type="menubutton"> > <xul:menupopup> > <children id="tbOverflow" disabled="true"/> > </xul:menupopup> > </xul:button> > </content> > </binding> You don't need the disabled attribute. The tbOverflow <children/> element above will never automatically receive children. (Note that IDs on <children/> elements aren't useful in the current model. We might want to introduce getInsertionPointById() or something, though.) > While there is room on the toolbar, children should be placed in tbMain. But > when there are too many children, the binding will set disabled="true" (or > something similar) on tbMain, and set disabled="false" on tbOverflow. The same > sort of thing applies to multi-row tabboxes and most of the other applications > I can think of. You wouldn't want to implement it that way anyway, since it doesn't cope with dynamic ordering changes, resizes, etc. > Hixie: you probably understand better than I do the difference between a URL > and a URI; is the difference between the respective arguments of addBinding, > removeBinding and hasBinding intentional? It was a mistake. It should be URI throughout. > When you guys finish defining what ElementXBL really should be, I'll write up > a bunch of testcases and/or documentation for you. Testcases would be great! Documentation should be already covered by a spec hyatt and I are writing.
Update: interface ElementXBL { readonly attribute NodeList xblChildNodes; /* the true child list after all bindings are applied */ readonly attribute Node xblParentNode; /* the true parent list after all bindings are applied */ readonly attribute Element bindingOwner; /* the element that generated the scope for this anonymous node, if any */ void addBinding(in DOMString bindingURI); void removeBinding(in DOMString bindingURI); boolean hasBinding(in DOMString bindingURI); XBLInsertionPoint addInsertionPoint( in Element parent, /* must be the element, or an anonymous node with bindingOwner set to this element */ in unsigned long index, /* the position in the given element at which to place the insertion point */ in DOMString includes /* the filter (a CSS selector) that elements must match in order to be placed in this insertion point. Null values are treated like an empty string. */ ); /* creates and inserts an insertion point */ void removeInsertionPoint(in XBLInsertionPoint point); /* removes an insertion point. Nodes that used to be in the insertion point are redistributed in the remaining ones. If some nodes no longer apply to any insertion point, the anonymous content is removed. */ readonly attribute XBLInsertionPointList insertionPoints; /* if the element is a bound element, returns the list of insertion points for this element. */ XBLInsertionPoint getInsertionPointFor(Node child); /* if the element is a bound element, and child is a node in the same scope, or a higher scope, than the element itself, this returns the XBLInsertionPoint in which the node is inserted. Otherwise, or if the bound element has no anonymous content, returns null. */ XBLInsertionPoint getInsertionPointById(DOMString id); /* if the element is a bound element, and it has an insertion point with the given ID, this returns the relevant XBLInsertionPoint. Otherwise, or if the bound element has no anonymous content, returns null. */ void changeInsertionPointFor(Node child, XBLInsertionPoint insertionPoint); /* changes the insertion point of the child node to the new insertion point. Raises an exception if the element is not a bound element, or if the child node is not in the same scope, or a higher scope, than the element itself, or if the node does not apply to the filter of that insertion point. Nodes other than element nodes only apply when the filter string is empty (that's the "catch all" filter) */ }; interface XBLInsertionPoint { readonly Element element; /* the parent of the insertion point */ readonly attribute unsigned long index; /* the index of the insertion point in the element */ attribute DOMString includes; /* never null */ /* the filter, a string representing a CSS selector */ attribute readonly NodeList nodes; /* the list of nodes currently inserted in this insertion point */ } interface XBLInsertionPointList { XBLInsertionPoint item(in unsigned long index); readonly attribute unsigned long length; } If two insertion points have the same parent, index, and filter, then the order in which they were added determines the order of their elements. If an element is added to the DOM dynamically, its scope is that of its parent element. (There is thus no way to add an anonymous child to a bound element, although in practice that doesn't matter.) Changes to a bound element's anonymous content only affects that bound element. Other bindings are unaffected. A node is inserted into the first insertion point that it matches. If it is moved (using changeInsertionPoint), then it remains in the new insertion point while it matches it. If it stops matching that new insertion point, it gets placed into the first insertion point that it matches. (If it doesn't match any, then the entire anonymous content is removed.) In other words, changes to the insertion point are "sticky". If an element is inserted into an insertion point with an empty filter ("includes" list) then it will always match it while that insertion point exists, and will therefore never move to another insertion point unless the insertion point is dynamically removed.
Rather than having a getInsertionPointById, you could allow the insertionPoints array to be accessed by id, e.g., elt.insertionPoints['someId'] I'm not sure how that's represented in IDL but we do that for other arrays.
That would be represented in the JS binding for the IDL. It isn't represented directly in the IDL, as I understand it, as some languages don't support an equivalent syntax. But yes, in JS, we should allow that.
I think it is represented in the IDL actually, usually with a namedItem method (like the item method but takes a string).
It's possible it might be represented in XPIDL, but not the IDL used in DOM specs? I'm not familiar with XPIDL. I haven't seen it ever mentioned in the IDL used by DOM specs.
http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/html.html#ID-75708506 HTMLCollection is what I was thinking of. IMO we could copy this.
I would prefer that to a getInsertionPointById method.
There's nothing special about namedItem(). The reason it can be accessed by subscript notation is that the ECMAScript binding says: # Note: This object can also be dereferenced using square bracket notation (e.g. # obj["foo"]). Dereferencing using a string index is equivalent to invoking the # namedItem function with that index. See the "Functions of objects that implement the HTMLCollection interface" section in: http://www.w3.org/TR/DOM-Level-2-HTML/ecma-script-binding.html We can easily say that getInsertionPointById() should be equivalent to using string based subscript notation.
If I may point out, multiple bindings may have elements with the same "ID", if they come from different source documents. So I think we should scrap the whole GetById/named-item-access unless there is a way to differentiate between (valid) multiple IDs.
There is never any ambiguity with multiple IDs as getInsertionPointById() always operates in a single scope (you have to call it on the bound element itself). The only possible problem would be inherited bindings, but that situation is unlikely to occur (authors usually have control over the various bindings, especially if they use <inherited/>) and in the unlikely case of a clash, the method would return the first matching ID and authors wanting more careful control over the insertion points can always walk the insertion point list.
a few proposed changes (in pseudo-patch format). childInsertionPoints makes programming much easier. XBLInsertionPoint::element was ambiguous as to whether it meant the immediate parent or the bound element... both are useful, so I clarified that. And added XBLInsertionPointList::namedItem a la HTMLCollection. interface ElementXBL { [snip] + readonly attribute XBLInsertionPointList childInsertionPoints; + /* if the element is a bound or binding element, returns the list of + insertion points that are children of this element. */ interface XBLInsertionPoint { - readonly Element element; + readonly Element parent; /* the parent of the insertion point */ + readonly Element bindingOwner; /* matches ElementXBL.bindingOwner */ interface XBLInsertionPointList { XBLInsertionPoint item(in unsigned long index); + XBLInsertionPoint namedItem(in DOMString name); readonly attribute unsigned long length; }
These make sense. I'd shorten "childInsertionPoints" to just "insertionPoints". I still think getInsertionPointById can just go once we have namedItem.
Dave, Actually "insertionPoints" and "childInsertionPoints" are two different attributes. * insertionPoints is all of the insertion points created by binding the current element, and is only usable on bound elements. * childInsertionPoints is all of the insertion points that are direct children of the current node, whether it is a client node or a binding node.
I don't think childInsertionPoints is necessary. It's the difference between: anonContent.childInsertionPoints['foo'] and anonContent.bindingOwner.insertionPoints['foo'] So I really don't think we need an extra property just for that.
The two are not equivalent... anonElem.childInsertionPoints[0] retrieves the (first or only) insertion point directly under this anonymous element. anonElement.bindingParent.insertionPoints[0] retrieves the first insertion point on the element, which could very well be a totally different insertion point. If I dynamically create insertion points (for a multi-row tabbox) they won't have IDs, so I can't used the "namedItem" method of accessing them. It's much easier to access them by their location in the anonymous DOM.
Just store the insertion points as returned from the addInsertionPoint method.
Yeah. Also, I still don't like the multi-row tabbox use case, since boxes should be able to wrap to multiple lines. You shouldn't have to build complex XBL just to get wrapping.
How soon can we finalize the ElementXBL interface, so I can write testcases?
Assignee: benjamin → nobody
QA Contact: ian → xbl
Blocks: XBL2
Web Components \o/
Status: NEW → RESOLVED
Closed: 8 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: