This document is a working document within the XBL task force which discusses one of the outstanding issues in depth: what syntax to use of the 'includes' attribute on <content> elements. This document describes the issues, lists the various proposals under consideration, and discusses pros and cons for the various proposals relative to particular examples. The overall goal with the document is to help the XBL task force make decisions at a particular moment in time. Once the XBL task force has discussed and made decisions about the given issue, this document may become obsolete and may not get updated to reflect the decisions that were made.
Request for public feedback
The XBL task force has not yet decided the syntax to use for
includes
attribute.
Two syntax approaches are being considered:
The 'includes' attribute on <children> in Mozilla XBL uses XPath 1.0 syntax in the spec, but was actually implemented as only a list of tag names. In the four years since the specification was submitted to the W3C as a Note, the need for more power in the attribute has not been strong enough to cause anyone to change the implementation, even when other parts of Mozilla's XBL implementation were changed.
The 'select' attribute on <refContent> in RCC used the following subset of XPath 1.0:
While there is a consensus about the general capabilities of the 'includes' attribute, detailed analysis of the issue has generated questions about certain detailed feature requirements. Are the following candidate requirements facilities that must be supported (MUST), are highly desirable to support (SHOULD), are optional to support (MAY), or should not be supported (NOT):
includes="foo|bar"
.
Prefixes would be defined using the normal xmlns
method.
The compromise proposal suggests
restricting the 'includes' attribute to unqualified tagnames.
In a namespace-aware DOM, this would mean that tagname matching would ignore namespaces,
for example foo
would match an element whose local name was foo
whether the element was in the http://example.com/
namespace, no namespace, or
any other namespace.[n]
) and in W3C
Selectors (using :nth-child(n)
).includes
attribute be fast to evaluate?
One proposal is to use W3C Selectors, the selector language that originated from CSS in 1996. The normative reference would be one of the following:
In a first version, the exact syntax UAs would be required to
support could be limited, for example only allowing one simple
selector (no combinators), only allowing (namespaced) tag names and
pseudo-classes (not the .class
syntax or the
#id
syntax), and only allowing certain
pseudo-classes (such as :nth-child(n)
).
Namespace prefixes from the current scope (as defined by
xmlns
attributes) would be used for namespaces prefixes
in the selectors.
One feature that could offer XBL component developers important flexibility is the "*[attr=val]" option. With this feature, it is possible for a component to use script to set the 'includes' attribute to point to a particular child element without having to analyse the bound element's children. For example, suppose the custom element is allowed to look like this:
<myNS:SkinnableWidget> <myNS:DropShadowSkin state="normal">...</myNS:DropShadowSkin> <myNS:DropShadowSkin state="hover">...</myNS:DropShadowSkin> <myNS:DropShadowSkin state="mousedown">...</myNS:DropShadowSkin> <myNS:BackgroundSkin state="normal">...</myNS:BackgroundSkin> <myNS:BackgroundSkin state="hover">...</myNS:BackgroundSkin> <myNS:BackgroundSkin state="mousedown">...</myNS:BackgroundSkin> <myNS:ToolTipSkin state="normal">...</myNS:ToolTipSkin> <myNS:ToolTipSkin state="hover">...</myNS:ToolTipSkin> <myNS:ToolTipSkin state="mousedown">...</myNS:ToolTipSkin> </myNS:SkinnableWidget>
The xblShadowTree might contain the following:
<xbl:shadowTree> <xbl:content includes="myNS|DropShadowSkin[state=normal]" /> <xbl:content includes="myNS|BackgroundSkin[state=normal]" /> <g display="none"> <xbl:content includes="myNS|TooltipSkin[state=normal]" /> </g> </xbl:shadowTree>
The component would respond to events and change the value of the
'includes' attribute based on the current interaction state for the
given widget. When no tooltip is shown and the widget is in a "normal
view" state, then script would set the 'includes' attribute on the
<xbl:content> elements to point to the relevant child elements
without having to know the ordinal position within the custom element.
For example, to reference the "mousedown" child element, you could say
includes="myNS|DropShadowSkin[state=mousedown]"
.
(Of course, the above example mixes presentation and content in the same way that HTML3.2's <font> element did, and so would not be a good design. However, similar scenarios occur in purely content-based designs and so the feature is still relevant.)
Suppose you want to implement the following <xforms:input> element using XBL with a shadow tree of SVG:
<xforms:input ref="..."> <xforms:label>First name</xforms:label> </xforms:input>
You can have one binding for the input
element, with this shadow tree:
<svg:rect .../> <xbl:content includes="label"/>
And one binding for the label
element, with this shadow tree:
<svg:text ...><xbl:content/></svg:text>
This is how all the widgets in Firefox are implemented.
Arguments in favor of this proposal:
Arguments against using CSS selectors:
Another proposal is to use a highly restricted subset of XPath, the expression language that originated from XSL in 1999. The normative reference for all of XPath 1.0 is:
The proposed subset of XPath would match what was defined in RCC for its 'select' attribute on <refContent>:
A key feature that offers XBL component developers important flexibility is the "*[#]" option. With this feature, it is possible for a component to analyze the content model of the custom element and use script to set the 'includes' attribute to point to a particular child element which was identified by scripting logic. For example, suppose the custom element is allowed to look like this:
<myNS:SkinnableWidget> <myNS:DropShadowSkin state="normal">...</myNS:DropShadowSkin> <myNS:DropShadowSkin state="hover">...</myNS:DropShadowSkin> <myNS:DropShadowSkin state="mousedown">...</myNS:DropShadowSkin> <myNS:BackgroundSkin state="normal">...</myNS:BackgroundSkin> <myNS:BackgroundSkin state="hover">...</myNS:BackgroundSkin> <myNS:BackgroundSkin state="mousedown">...</myNS:BackgroundSkin> <myNS:ToolTipSkin state="normal">...</myNS:ToolTipSkin> <myNS:ToolTipSkin state="hover">...</myNS:ToolTipSkin> <myNS:ToolTipSkin state="mousedown">...</myNS:ToolTipSkin> </myNS:SkinnableWidget>
The xblShadowTree might contain the following:
<xbl:shadowTree> <xbl:content includes="...ordinal reference to drop shadow skin..." /> <xbl:content includes="...ordinal reference to background skin..." /> <g display="none"> <xbl:content includes="...ordinal reference to tooltip skin..." /> </g> </xbl:shadowTree>
The component would respond to events and change the value of the 'includes' attribute
based on the current interaction state for the given widget. When no tooltip is shown
and the widget is in a "normal view" state, then script would set the 'includes' attribute
on the <xbl:content> elements to point to the relevant child elements
by ordinal position within the custom element using "*[#]" syntax.
For example, to reference the first child element, you would say includes="*[1]"
.
This critical
bit of flexibility when using fully encapsulated components
is not available by tagname or ID matching.
Arguments for using a subset of XPath:
Counter-arguments against proposals involving XPath:
It must be emphasized that this has a major effect on future extensibility. In particular, extending beyond the XPath "Step" production seems highly difficult, whereas extending to full selectors support (given an implementation of selectors) is trivial. This is because the operation we want to perform (determining whether each child is included or not) matches the model of selectors perfectly, but does not match the model of XPath. In other words, we're looking for a function that, given a value for the includes attribute, maps an element (one of the children) to a boolean (included or not included). Selectors performs exactly that mapping (selector * node in tree → boolean). XPath performs a mapping (expression * node in tree → list of nodes), which is not the function we want.
Another proposal is a variation on the XPath proposal which would allow the XPath subset to reference any nodes under the bound element, not just child nodes. With this variation, allowable XPath expressions would be extended to allow the shorthand "/" syntax to represent a child axis and the "text()" syntax to allow selection of text nodes. For example, "a/b/text()" would be supported and would select all text nodes that are child nodes of b and grandchild nodes of a.
Additional rules would be added such that it would not be possible for one <xbl:content> element to reference a descendant node and then another <xbl:content> element to reference one of the ancestor elements of that descendant node. Here are proposed rules to achieve this:
This extra flexibility is particularly useful for implementing XForms user interface controls. For example, suppose you want to implement the following <xforms:input> element using XBL with a shadow tree of SVG:
<xforms:input ref="..."> <xforms:label>First name</xforms:label> </xforms:input>
This extra flexibility allows the binding to use <xbl:content> on the text node for the label element as follows:
<svg:text x=... y=...><xbl:content includes="xforms:label/text()"/></svg:text>
Note that in some cases there is a way to use recursive XBL to satisfy the ability to apply XBL to nested child elements (such as in the <xforms:label> example above). However, this recursive approach is not a generally robust facility and can fall apart when child elements appear in multiple different contexts and require different shadow trees for each context. In the particular case of <xforms:label>, the shadow trees may need to be different depending on whether the parent element is a button control (<xforms:trigger>), a text entry control (<xforms:input>), or whatever. In one case, it might be sufficient to do a simple <xbl:content> to the content of the label, but in another case it might be necessary to copy/transform the textual content.
Also, the various textstring-oriented common labels in XForms (<xforms:label>, <xforms:hint>, <xforms:help>) all have the ability to either include text inline or use the 'model' and 'ref' attributes to bind to instance data. A desirable feature in the future would be to reference indirectly through an XForms model item property (with a possible calculation expression) which is then itself bound to the instance data. One way to pull this off would be to offer an XForms-knowledgeable XPath extension function which takes an XPath pointer to one of <xforms:label>, <xforms:hint>, <xforms:help> as a parameter (e.g., xforms-bound-data(xforms:label)). Such a growth path is only possible if XPath syntax is used as the technology foundation today.
[Comment from member of XBL-TF: It should be noted that a significantly simpler way of handling these use cases is applying XBL to elements from CSS (the 'binding' property). This would be a lot simpler than extending XPath as is suggested above -- especially since extending XPath would require changing the UA's code, at which point why not just implement XForms natively.
For example, to handle different labels for <xforms:input>
and
<xforms:trigger>
you would do:
trigger > label { binding: url(xforms.xbl#triggerLabel) } input > label { binding: url(xforms.xbl#inputLabel) }
Similarly, to handle inline <xforms:hint>
vs <xforms:hint ref="">
you could easily do:
hint[ref] { binding: url(xforms.xbl#hintWithRef); } hint { binding: url(xforms.xbl#hintWithoutRef); }
To be perfectly honest, though, if I were implementing XForms myself I
wouldn't really be trying to do it declaratively. It's a nice
theoretical excercise, but at the end of the day it would just be
faster to have bindings for each element (with maybe subelements like
<label>
bound bound based on their parent, as in the example above),
and then handle attributes like "ref" using script.]
Arguments for using a subset of XPath:
A proposed compromise: Use only single unqualified tag names, with matches being determined purely by local name, ignoring namespaces altogether.
Kurt Cagle on 02 Sept 2004 argued for XPath. Here is a snippet:
2) I'm going to put my two cents worth in for binding with XPath, for several reasons -
a) One of the reasons for the XBL specification in the first place was to provide the implementation layer between SVG and XForms, and XForms supports XPath. Without the support in XBL (where I feel it most belongs) much of the more sophisticated actions inherent within XForms require become problematic, if not impossible.
b) Most current "complete" SVG implementations exist in an environment when such XPath support already exists (Java, COM, .NET, Rhino, Mozilla Seamonkey, libXML, etc.) and as such the necessity for vendors to build their own XPath implementations is somewhat reduced.
c) XPath significantly reduces the amount of imperative code
needed within applications (I'm thinking specifically of the
<xbl:content>
tag here), which I see as one of the major rationales for
SVG over a platform dependent implementation such as GDI. Moreover, even
in that imperative code, the preferred node selection capability would
probably be to use XPath rather than go through the pain of trying to
navigate the tree manually. I'd far rather see
<xbl:content src="bar[position() % 2 = 0]"/>then a complex block of procedural code to do the same thing - select every even child "bar" element.
the equivalent in CSS would be src="bar:nth-child(even)"
(level 3)
d) I have often used the complexity of XPath as one of the reasons for not incorporating it in SVG. However, it has been my experience in teaching classes on SVG and dealing with readers as a technical writer that if they are sufficiently savvy to master rudimentary SVG skills, they can easily master rudimentary XPath.
e) I would recommend that you mandate the full XPath solution for SVG Basic, and the reduced set XPath solution for SVG Tiny. I understand the memory requirements that SVGT targets have, making it difficult to implement the full set, but a simnple tree walker parser can readily be implemented (and indeed probably already exists).
f) A quick argument against using CSS Selectors - The CSS selector model beyond the simplest matching mechanisms is generally not well understood by more than a small proportion of the web development universe, in great part because the browser with the largest market share does not support that functionality properly. Again, this comes from experience dealing with readers and students when I teach CSS classes. Moreover, the CSS model requires the use of certain reserved characters (the ">" greater-than character) to facilitate its operation for the more sophisticated operations that will cause compilation errors with many XML parsers.
actually > is perfectly safe, it's < that causes issues (and that's not part of Selectors)
Tobias Reif on 02 Sept 2004 said the following:
Nigel McFarlane on 17 Oct 2004 gave lengthy feedback on various sXBL issues. Regarding this issue, here are some snippets:
I think also there is little support for complex include options. This choice could be put off until XBL 2.0... For me, option 5. or 6 is more than sufficient for includes= until experience points to clearly urgent needs. I would hope for an XHTML class= syntax solution at most. (This comment was probably in response to the sXBL spec talking about options for supporting full XPath or full CSS selectors)
includes= in the Mozilla codebase's XBL has been deprecated as a feature...
This does not actually appear to be the case.
A similar feature in the Mozilla codebase is a filtering system for command observers. There, the filtering system has never been completed, because there has never been a pressing need. All of the Mozilla work has managed very well without itElliotte Harold on 06 Dec 2004 said the following:
My feeling is that full XPath 1.0 is a minimum syntax for the includes attributes. I assume the context node for the XPath expression would be the matched node.
However, I really wonder if this goes far enough. I can foresee a lot of use cases that need something Turing complete. For instance, you might want to place each subsequent ten nodes in a different box. I can't see how to do that with just XPath when you don't know the number of nodes in advance.