|
Author: Dave
Hyatt
Change History:
- Jan 12, 2000 - Second Draft
- Jan 4, 2000 - Initial Draft
Contents:
Introduction
The current Mozilla infrastructure contains a number of
recent feature additions, specifically intended to
facilitate the design of user interface elements and the
processing of events and operations on those elements. To
date, this has been undertaken on a sort of "ad hoc" basis,
with features being invented, augmented and redesigned only
as needed.
These features include anonymous content, the
command dispatcher, the controller system, and
the configurable key binding system.
The above features have emerged as critical requirements
for the Mozilla application. Unfortunately the design of
these features has been haphazard at best. The design has
also been particularly difficult because it necessarily
involves non-standard extensions to CSS, the DOM, and
HTML.
The purpose of this document is to provide a concrete
proposal for a redesign of the current system in order to
enable the full range of functionality we need for the
Mozilla application. In the following sections, I will tie
together these different features into a new language, XBL,
that can exist apart from XUL and that can be used with
either XUL or HTML.
The Structure of an XBL Document
An XBL file is a well-formed XML file. The root element
of the file is a <bindings> tag. The
namespace of XBL is http://www.mozilla.org/xbl.
An XBL file consists of zero or more bindings, each
declared as a child of the root element with a
<binding> tag.
<bindings xmlns="http://www.mozilla.org/xbl">
<binding>
...
</binding>
<binding>
...
</binding>
...
</bindings>
|
CSS and XBL
An element in XML (in particular XUL and HTML elements)
can point to a specific binding in an XBL file. The location
of this file is specifiable using CSS. A new property,
behavior, can be used.
textarea {
behavior: url("chrome://global/content/htmlBindings.xml#textarea");
}
|
Bindings are identified using a
name attribute. In the CSS, the
# notation is used to reference a specific binding within
the XBL file. If a binding name is not specified, then the
first binding defined in the XBL file is used.
The binding found in the XBL file will be applied to any
elements that match the style rule. In the above example, a
binding is referenced that will apply to all HTML
textarea elements.
Note: There is currently a
working draft afoot
for adding behavioral extensions to CSS. This proposal
complements that draft through its support of the
behavior property. XBL is one
particular behavioral implementation that CSS can reference.
Another proposed implementation of behaviors is HTC (HTML
Components). This implementation, however, is not
generalizable to other namespaces, is not well-formed XML,
and does not support the extra anonymous content
capabilities that XBL offers.
The <binding> Element
The binding element is used to dynamically bind
new information to another XML element (e.g., a XUL or HTML
element). Each binding has three components.
- Content. A binding can specify new content
that is placed underneath the bound element. This content
can either be anonymous (hidden from the bound element)
or explicit (visible to the bound element).
This doesn't have to be
implemented in the 5.0 timeframe.
- Methods & Properties. A binding can
specify additional methods that can be invoked on the
element. It can also specify additional properties that
can be retrieved or set on the element. In this way the
functionality of the bound element becomes extensible.
This doesn't have to be implemented
in the 5.0 timeframe.
- Behavior. A binding can specify event handlers
for specific actions like key and mouse events that can
make use of the new functionality applied in the
binding.
The following sections cover each component of a binding
in more detail.
The <content> Element
The content element is used as a child of the
binding element to indicate additional content that
can potentially be built underneath the bound element. A
binding can have only one content element as a child.
This content is anonymous and does not alter the
bound element's document object model. The content itself is
declared normally, just as it would appear if it were really
declared underneath the bound element.
|
Example: Scrollbars frequently differ
from platform to platform. On the Windows operating
system a scrollbar can be implemented using an up
button, a scrollbar thumb, and a down button. On
the BeOS, however, a scrollbar consists of a pair
of up/down buttons, a scrollbar thumb, and a second
pair of up/down buttons.
By making the scrollbar's content anonymous, and
by moving the anonymous content itself into an XBL
file, CSS can then be used to point to two
different XBL files on the different operating
systems.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
<content>
<xul:titledbutton class="upButton" oncommand="parentNode.lineUp();"/>
<xul:thumb class="thumb" .../>
<xul:titledbutton class="downButton" oncommand="parentNode.lineDown();"/>
</content>
...
</binding>
...
</bindings>
|
Anonymous Content and the DOM
Although anonymous content is not really contained in the
document, it can be styled by style sheets loaded by the
document. As far as style resolution is concerned, the
anonymous content can be referenced as if it is contained in
the document. Scripts on anonymous content can also
reference JavaScript variables and functions defined within
the global script object that corresponds to the
document.
The formatting objects constructed from anonymous content
are placed on the end of the containing object's list of
children. In other words, if explicit content and anonymous
content are both specified for a bound element, then the
explicit content's formatting objects appear first in the
flow (with all the usual exceptions for absolutely
positioned, fixed positioned, floaters, etc.).
Events that occur on anonymous content are not visible to
the bound element or to its document. Within the anonymous
content tree itself, events bubble normally and are also
capturable by other anonymous content.
It is possible for anonymous content to be bound to
another XBL file that can itself contain anonymous content.
In this case, the nested anonymous content is invisible to
the outer anonymous content. This nesting can be taken to an
arbitrary level.
|
Example: The scrollbar thumb itself can
be implemented using two springs and a
titledbutton. This content could be defined in an
XBL file bound to the thumb element. It is
invisible to the thumb that it is bound to, and in
turn the thumb is invisible to the scrollbar that
it is bound to.
|
Exclusionary Construction of Content
By default, content is only built if the bound element
has no child content already. In certain situations,
however, it is desirable to build anonymous content even
when the element already has children. The
excludes attribute can be used
in this case. Its value is a comma-separated list of child
tags that should be ignored during the check regarding
whether or not anonymous content should be constructed.
|
Example:: The menu element in XUL can
contain template or observes
elements as children. It can also contain a
menupopup element as a child. If, however,
no content is specified other than those three
children, then the contents of the menu must still
be constructed anonymously.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
<content excludes="template,observes,menupopup">
<xul:titledbutton class="menu-left"/>
<xul:titledbutton class="menu-text"/>
<xul:spring flex="1" class="menu-spring"/>
<xul:titledbutton class="menu-accel"/>
<xul:titledbutton class="menu-right"/>
</content>
...
</binding>
...
</bindings>
|
Inheritance of Attributes
In some cases attributes that are set or changed on the
bound element should be reflected into the binding's
content. The inherits attribute
can be used on specific elements within the content binding.
This attribute is a global attribute, and therefore its
namespace must be explicitly qualified as XBL. The value of
the attribute is a comma-separated list of attributes that
should inherit from the bound element. These attributes
always remain in sync with the bound element; if the
attribute's value changes on the bound element, then the
anonymous content is also updated to reflect the new
value.
In some cases the attribute on the bound element is not
the same as the attribute on the anonymous content that
should be set. In this case, a colon-separated pair can be
specified in the inherits attribute. The first attribute in
the pair is mapped to the second attribute in the pair.
|
Example:: The menu element in XUL
supports the value attribute as a means of
specifying the menu item's text. It also supports
the acceltext attribute as a means of
specifying the accelerator text for a menu item.
Because titledbutton elements are used for
both, the value attribute must be
specified on both. The menu text button can inherit
the value from the bound element. The menu
accelerator button must inherit from acceltext and
map that attribute to its value attribute.
<xbl:bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<xbl:binding>
<xbl:content excludes="template,observes,menupopup">
<xul:titledbutton class="menu-left"/>
<xul:titledbutton class="menu-text" xbl:inherits="value"/>
<xul:spring flex="1" class="menu-spring"/>
<xul:titledbutton class="menu-accel"
xbl:inherits="acceltext:value"/>
<xul:titledbutton class="menu-right"/>
</xbl:content>
...
</xbl:binding>
...
</xbl:bindings>
|
The <children> Element
XBL can be used to interpose anonymous content in between
the bound element and its explicit children. This has no
effect on the bound element's document object model.
Instead, the formatting objects created from the anonymous
content are constructed as immediate children of the bound
element's formatting object. The formatting objects that are
built from the explicit child content of the bound element
are inserted underneath the anonymous formatting objects at
a location specified in the XBL.
The children tag within anonymous content
specifies the place at which to insert the explicit children
of the parent within the formatting object tree. When
styling these children, the anonymous content nodes are
visible, just as if they existed in the document itself.
Events that originate on explicit child content will
bubble through their parent anonymous content elements
before reaching the bound element. In effect, the event
begins in the bound element's DOM, bubbles through the
parent anonymous content, and then bubbles up to the bound
element. Capturing works in a similar fashion.
|
Example:: In XUL a
<scrollgroup> tag can be used to
specify a scrollable window. Its explicit children
are placed inside a scrollport object, and
a scrollbar is built as a sibling of the
scrollport in the formatting object tree.
This extra scrollport frame need not be
defined by the user, but its presence is required
in order for the scrollable window to work
properly.
In this example, an XBL binding for the
scrollgroup is made that ensures that any
of the scrollgroup's explicit children are placed
inside the scrollport so that they can be
scrolled.
<xbl:bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<xbl:binding>
<xbl:content excludes="template,observes,menupopup">
<xul:scrollport>
<xbl:children/>
</xul:scrollport>
<xul:scrollbar class="vertical"/>
</xbl:content>
...
</xbl:binding>
...
</xbl:bindings>
|
The <interface> Element
The interface element is used to specify
additional functions that can be invoked on the bound
element, as well as additional properties that can be set or
retrieved on the bound element. A binding element
can have only one interface element child.
Methods
An interface element defines zero or more
methods and zero or more properties. Each method is
specified using the method tag, and each property
is specified using the property tag. The
name attribute can be used on
either of these two elements to indicate the name of the
method or property that will be added to the bound
element.
|
Example: A scrollbar element in XUL can
have methods defined on it using XBL that allow for
scrolling up and down, either by a line or by a
page. It can also have a property that represents
the current index of the scrollbar.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
<content>
<xul:titledbutton class="upButton" oncommand="parentNode.lineUp();"/>
<xul:thumb class="thumb" .../>
<xul:titledbutton class="downButton" oncommand="parentNode.lineDown();"/>
</content>
<interface>
<method name="lineUp">
...
</method>
<method name="lineDown">
...
</method>
<property name="currentIndex"/>
</interface>
</binding>
...
</bindings>
|
Method and property names are case-sensitive. The
convention to use in Mozilla: the first letter of method and
property names should always be lowercase (just as with
JavaScript methods and properties).
Parameters
Arguments to methods can be specified using the
<argument> tag. The argument tag can also
take a name attribute. In the
implementation of the function the value of this attribute
can be used to refer to a variable that is bound to the
value of the argument passed in to the method.
The type of an argument can be specified using the
type attribute, although this
is not required. Method arguments in XBL can be of
polymorphic type, assuming the scripting language used by
the implementation supports it.
|
Example: A scrollbar can define a
scrollTo method that requires a single
argument, a number indicating the line to scroll
to.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
...
<interface>
<method name="scrollTo">
<argument name="index"/>
...
</method>
...
</interface>
...
</binding>
...
</bindings>
|
The return type of a method can be specified on the
method tag using a
returns attribute. Just as with
argument types, this value need not be specified if the
underlying scripting language supports polymorphic return
types.
Method Implementations
The body tag is used to specify the
implementation of a particular function. It contains a
script that is executed when the method is invoked. The
type attribute can be used to
specify the language of the script.
|
Example: The scrollbar defines an
implementation for the scrollTo
method.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
...
<interface>
<method name="scrollTo">
<argument name="index"/>
<body>
<![CDATA[
if (index == currentIndex) return; ...
]]>
</body>
</method>
...
</interface>
...
</binding>
...
</bindings>
|
The method body is evaluated in the context of the bound
element. Thus properties of the element can be referred to
by name (as is done for the currentIndex property
in the example above).
Properties
The property tag is used to specify a property
in XBL. By default the property can be set or retrieved on
the element. The name attribute
is used to specify the name of the property.
The readonly attribute if
present and set to true
indicates that the property can be inspected on the bound
element but not changed.
The value attribute on the
property element is a script that evaluates to an initial
value for the property.
|
Example: The scrollbar initializes its
current index to 0.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
...
<interface>
...
<property name="currentIndex" value="0"/>
...
</interface>
...
</binding>
...
</bindings>
|
Interfaces and XPCOM
An interface element can point to an XPCOM
interface and to a specific implementation of that
interface. When this is done, no methods or properties are
specified in the XBL. Instead they are picked up from the
XPIDL description that corresponds to the XPCOM
interface.
[Note: Only interfaces that
have had XPT files generated for their descriptions can be
used with XBL.]
The name attribute can be
used to point to a symbolic name for the interface. When
this is done, the interface is retrieved from the
interfaces object that is accessible from the
Components object. The
iid attribute can be used to
point directly to a specific interface ID.
Implementations can be referred to using the
progid attribute, which refers
to a progid, or a specific implementation can be directly
referred to using the classid
attribute.
It is permissible to specify an XPCOM interface and to
define the implementation inline using
<method> and <property> tags.
Note that it is not permissible to point to an XPCOM
implementation without also specifying either an interface
name or an IID.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
...
<interface name="nsIController" progid="component://netscape/editor/controller"/>
...
</binding>
...
</bindings>
|
The <handlers> Element
The final component of a binding is the
<handlers> element. This element is used to
define new event handlers that should be invoked when events
occur on the element. The handlers element contains
zero or more handler elements as children. Each
element corresponds to a single event handler that will be
invoked when the handler is matched to the event.
Handler Elements
The type attribute specifies
the type of event that the handler wishes to field. Its name
must corresponds to an event name, such as
click or
keypress. A comma-separated
list of event names can be given if, for example, the
handler should fire on more than one action, e.g., when the
return key is pressed or when the mouse is
clicked.
The button attribute can be
used to restrict a mouse event to a specific button. It has
values of left, middle or right.
There are several modifier keys supported. They each have
separate attributes. If set, then on a mouse or key event,
these modifier keys must be down for the event to fire. The
value of a modifier key attribute is true if the modifier
key must be down. The modifier key attributes include
shift,
control,
alt,
command, and
meta.
The key attribute can be
used to stipulate that on a key event the key must be the
primary key used. The keycode
attribute works in a similar fashion, but it allows for the
selection of unusual keys like space or
home. The virtual key codes can be found in the
Mozilla source. Note: Include link to
Mozilla source file.
The capturer attribute, if
set to true, indicates that the event should only fire
during the capturing stage of DOM event flow.
The value attribute contains
the event handler script that should be invoked when the
event element is matched.
|
Example: A list box in HTML moves the
selection up and down when the appropriate arrow
keys are pressed. XBL event elements can be used to
install this behavior on a list box.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
...
<handlers>
...
<handler type="keypress" keycode="vk_up"
value="moveDown()"/>
<handler type="keypress" keycode="vk_down"
value="moveUp()"/>
...
</handlers>
...
</binding>
...
</bindings>
|
Event Redirection
A handler element can be used to map one event to another
event. When this happens, a new event is synthesized from
the old event and fired into the DOM. The
<mapto> element is used to perform the
redirection. It is a child of the handler element,
and it can have all the attributes that an handler
element can. These attributes specify the values of the new
event and provide information regarding how the fields of
the event object should be
filled in.
The new event fires on the bound element and moves
through the capturing and bubbling stages just as the old
event does. An event element that redirects can still have a
value attribute specified. This
code executes prior to the firing of the new event. This can
be useful in cases where it is desirable to prevent the
continued processing of the old event.
Extensibility of Events
Events are extensible, and code can be used to invoke
events with arbitrary names. Handlers can be defined to deal
with any new events. For example, a new event called
close could be fired when a
window in XUL wishes to close itself up.
|
Example: Dialog boxes attempt to close
when the ESC key is pressed. An XBL file bound to
all dialog windows can be used to ensure that the
ESC key causes a close handler to fire.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding>
...
<events>
...
<event type="keypress" keycode="vk_escape">
<mapto type="close"/>
</event>
...
</events>
...
</binding>
...
</bindings>
|
This mapping capability coupled with extensibility of
events is quite powerful. It can be used to perform key
remapping. For example a Dvorak keyboard layout could be
implemented within Mozilla by supplying a set of redirects
on the outermost XUL window.
Other examples of event redirection include
platform-specific bindings. For example, CONTROL+click on
the Mac could be remapped to a right mouse button click.
Finally modifier keys can be mapped to other modifier keys,
thus allowing for platforms to choose modifier keys as
appropriate for specific bindings.
Binding Inheritance
A binding can inherit from another binding. When this
inheritance is specified, the binding picks up all the
content and event handlers defined in the base binding, and
it picks up the additional methods and properties defined in
the base binding.
The Extends Attribute
The extends attribute is
used to specify the URL of the base class binding. The URL
is just like the one specified in the
behavior CSS property; the #
notation can be used to point to a specific binding, either
in the same file or in another file all together.
Inheritance can be taken to an arbitrary level, i.e.,
binding A can inherit from binding B, which in turn inherits
from binding C. Only single inheritance is allowed.
This feature allows bindings to be reused by different
elements that also wish to define additional bindings of
their own. Most importantly, it can be used in conjunction
with chrome URLs to include platform-specific event
redirects and bindings unique to a given operating
system.
|
Example: By default, input text fields
have a certain set of bindings defined for simple
navigation. An autocompleting text field, e.g., the
URL bar in a browser window, wishes to inherit all
of the default behavior, but also to add support
for auto-completion. It can do this by inheriting
from the base binding.
<bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns="http://www.mozilla.org/xbl">
<binding name="autoCompleteBinding"
extends="resource:/chrome/htmlBindings#inputTextField">
...
</binding>
...
</bindings>
|
Inheritance of Content
By default when one binding inherits from another, the
content specified by both bindings is built. The derived
binding has its content built first, and then the base
binding has its content built. Content is always appended to
the end of the bound element's formatting object list, so
the derived class content shows up first in the flow.
Only one insertion point for explicit content can be
specified. If a base class attempts to redefine this
insertion point using the children tag, the tag
should be ignored.
A derived binding can prevent a base binding's content
from being built by placing an
inherits attribute on the
content element with a value of false.
Inheritance of Methods and Properties
Methods and properties defined in bindings are invoked by
making those calls directly on the bound element itself. For
example, if a binding defines a
doCommand function for a XUL tree widget, then the
function can be invoked directly from the tree element.
Methods
For methods, the IDL for the DOM element is first
checked. If there is no match, then the bindings are
searched in order (from derived bindings up to the root
binding) for any methods that match the invoked method. Note
that it is possible for multiple bindings to have matching
methods. When this occurs, only the derived binding's method
is called. The base binding's method can be invoked in
script by the derived binding using the
base keyword followed by the
method name, e.g.,
base.doCommand("delete").
Properties
When a property is inspected, the element itself is
checked first. If the property is not found, then the
bindings are searched in order, from derived binding up to
the root binding. The first binding that provides a match is
used to obtain the current value of the property.
When a new property is set on the element, the element is
checked first. If the property does not currently exist on
the element, then the bindings are searched in order. The
first binding that has the property defined gets the
property set. If no bindings match, then the property is set
on the element and not on any of the bindings.
There is no notion of "shadowing" of properties. Only one
binding is responsible for the value of a property at any
given time. It is not possible for the property to be
defined in more than one binding in the same inheritance
chain.
Inheritance of Handlers
If a derived binding and base binding both define
handlers for the same event, the derived binding's handler
is invoked first. This is true both in the bubbling or
capturing phases of event handling.
If preventBubble is called on the event during the
bubbling phase, then the base binding's event handler will
not fire. Similarly, if preventCapture is called on the
event during the capturing phase, then a base binding's
capturing handler also will not fire. These methods can be
used to selectively prevent an event from being fielded by a
base class binding.
Rule-Based Construction (Examples)
Sometimes different types of content should be
constructed based on the attributes found on the bound
element. XBL supports this capability through the usage of
inheritance and style rules. If the content varies in this
way, then multiple derived bindings can be defined that all
inherit from the same base binding.
|
Example:: A treecell in XUL constructs
different anonymous content if an
indent attribute is
set to true on the cell. In one case, a
<treeindentation> element needs to
be inserted into the cell. Otherwise it
doesn't.
<xbl:bindings xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<xbl:binding name="normalcells">
<xbl:content excludes="template,observes">
<xul:titledbutton class="tree-icon" flex="1" xbl:inherits="value,src">
</xbl:content>
...
</xbl:binding>
...
<xbl:binding name="indentedcells" extends="#normalcells">
<content>
<xul:treeindentation>
</content>
...
</xbl:binding>
...
</xbl:bindings>
|
Contact us at xptoolkitstaff@netscape.com.
Want to complain about the new documentation? Email
Dave Hyatt.
|