XUL vs. CPIA
I just started working at OSAF, after spending a good 6 years at Netscape mostly working on Mozilla. I worked all over the place at Mozilla from the front end back through the embedding architecture. I've observed and participated in a lot of the evolution of our user interface framework. One of my first tasks at OSAF is to ressurect
ZaoBao, an RSS reader for Chandler.
I've found the experience to be somewhat frustrating as I learn the way the "content model" for the user interface is built in Chandler. I believe that part of this frustration is due to a number of factors:
- Working in a new environment: lets face it, I've gotten used to the way XUL works. A new system is inherently unfamiliar.
- Working within a framework as it is being developed: documentation is constantly out of date or non-existent, and things change out from under you. This is just a natural side effect of the style of development going on here, and one that I went through at Netscape as well. Its life, life changes.
- The fundamental model of user interface construction is incredibly different: this is the biggie, and the one that I want to spent some time analyzing.
Below I will attempt to outline my analysis of the difference between XUL and CPIA, and discuss the merits and drawbacks of each framework. I'm doing this partly as a means to help myself and other XUL developers understand Chandler and CPIA, but also as a critique to understand how CPIA can be improved. This is not to say that XUL is automatically better than CPIA, but that CPIA is still immature, and we can learn from the mistakes and triumphs of other frameworks.
I've divided up my analysis into a few subsections:
Construction and representation
History and background
XUL is the XML User Interface Language. It is an XML schema used to define user interfaces similar to HTML/XHTML. It was designed to provide a cross-platform user interface language for the Mozilla project, for creating a web browser, a mail reader, and an HTML editor.
Since the creation of XUL, Microsoft and Macromedia have created competing XML-based UI languages called XAML and MXML respectively. XAML has become a popular language for .NET applications, and MXML is used for Macromedia's Flex server to provide Flash-based user interfaces.
The fundamental unit of widgets in XUL is called a
node or sometimes a
content node. The vocabulary of XUL follows that of the W3C
DOM as XUL is transformed into a DOM tree at runtime. XUL was at one point recommended to the W3C for standardization, but I believe that effort has died out.
CPIA is a framework created for the Chandler application. Its user interface is currently defined using XML, but this XML is only used as a boostrapping mechanism to load data into Chandler's repository. Once the data has been loaded into the repository, CPIA deals entirely with that, rather than any particular XML structures.
The basic unit in CPIA is the
Block. It sometimes, but not always, refers to a widget or a widget container. The Block API provides a simple cross platform way to manipulate a user interface.
Resource format
Both XUL and CPIA use XML to initially represent their data. Both use tags to represent widgets and attributes to define attributes about these widgets. The data is read either at startup or on demand, and structures are built internally that represent hierarchies of widgets that will be displayed. After the initial XML is read, a binary format is stored on disk.
XUL
With XUL, an XML file is a monolithic representation of a window. The window is described with an XML-based hierarchy starting with the <window> tag. The hierarchy is represented by placing new XML elements inside of existing elements, so that the hierarchy in the file very closely matches the hierarchy in the UI.
For instance, a window could be represented as follows:
<window align="horizontal">
<box align="vertical">
<button/>
<button/>
</box>
<textbox/>
<box>
<button/>
<button/>
</box>
</window>
CPIA
CPIA uses Chandler's Parcel Loader to load all of its resources. As a result, the hierarchy in the XML does not match the hierarchy in the user interface. This is mainly because the resource format is a more general format used to load any kind of data into the repository. The format needs to be flexible enough to describe any relationship between objects, not just the common "tree" pattern that is typical to UI resource formats.
Instead of representing parent/child relationships by containment within XML, all elements are thrown into a kind of soup (the Repository) and a hierarchy is represented by a series of references between items. These references can exist within the same file, or in other xml files.
For example:
<Parent itsName="parent">
<parentAttribute value="..."/>
<childBlocks itemref="doc:box1"/>
<childBlocks itemref="doc:text1"/>
<childBlocks itemref="doc:box2"/>
</Parent>
<BoxContainer itsName="box1">
<orientationEnum value="doc:Vertical"/>
<childrenBlocks itemref="doc:button1"/>
<childrenBlocks itemref="doc:button2"/>
</BoxContainer>
<BoxContainer itsName="box2">
<orientationEnum value="Horizontal"/>
<childrenBlocks itemref="doc:button3"/>
<childrenBlocks itemref="doc:button4"/>
</BoxContainer>
<EditText itsName="text1"/>
<Button itsName="button1"/>
<Button itsName="button2"/>
<Button itsName="button3"/>
<Button itsName="button4"/>
This more verbose syntax is because the parcel XML format is designed to represent a web of interconnected resources, rather than just UI elements. This gives CPIA some added flexibility, such as sharing of specific blocks between resource files or even within the same file, as well as referencing blocks in other files. It makes dividing up the blocks among multiple files easier.
Lisa has pointed out that the parcel XML's use of namespaces is really nasty. Namespaces are really designed to distinguish between specific element types, and should not be visible inside the actual content (attribute values or CDATA).
Object Identification
UI frameworks need easy ways to refer to specific objects in a user interface. Both CPIA and XUL use unique strings to refer to these objects but their use is different.
XUL
References in XUL are only unique within the scope of a document. (as you may recall from above, a single document represents a window in XUL) Elements are uniquely named using their "id" attribute. XUL does its best to ensure that there is only one element with a given id, and provides access to it via the DOM's document.getElementById() mechanism. At runtime you can call this on a given document and get a pointer to the node in question in more or less constant time.
id is also used for CSS rendering to apply specific elements, though that use is rare.
Its possible that other XML standards like XLink or XPointer could be used to find elements in a declarative manner.
CPIA
The parcel loader has a well defined manner for uniquely identifying elements and referring to them from the current and other documents. It uses XML namespaces to define uniqueness among documents, and allows XML namespace prefixes as a shortcut for referring to items within these documents.
For example, an item defined in the document parcels/osaf/parcel.xml is uniquely defined by its itsName attribute, such as
itsName="bar". To reference that object from any other document, that document needs to:
- Define an XML namespace prefix for the other document, such as:
<Parcel xmlns:foo="http://osafoundation.org/parcels/osaf" ...>
- Use this prefix as a shortened version with the prefix
itemref="foo:bar"
(to link the item directly) or <foo:bar>
(to use the item as a Kind)
The first mechanism, using
itemref really violates the spirit of XML namespaces. (this is the same thing I was referring to above)
Overlaying/Extending the user interface
When developing a platform-like widget framework, developers need a way to share parts of the interface across different windows. For example, a product needs to share the same Help menu across different windows used in an application, or the same set of Ok/Cancel/Apply buttons in a dialog box.
3rd developers need a mechanism to attach new UI to an existing interface. This may include adding new menu items, toolbar buttons, you name it.
This mechanism can actually be used to build an entire interface, or to composite a number of interfaces into one.
XUL
XUL has a system of
overlays. An overlay is a set of UI that gets sprinkled over an existing window. Overlays can be used in two ways.
In the first, they are explicitly pulled in by a parent document. For instance, a browser window could overlay the menus, then the toolbar, then the status bar, and so forth. This gives you the equivalent of an #include, and allows for modularization of the UI.
In the second mechanism, a manifest file accompanies an overlay in an extension. The overlay specifies which documents the overlay should, well, overlay.
In both cases the overlay consists of a set of UI hierarchies (or fragments), with annotations to specify where in the parent documents the new UI should live. They specify both a root node for the UI fragment to be, and where it should be relative to it (before/after/child item/etc)
CPIA
CPIA again uses the parcel loader to allow references between documents, and uses bidirectional references to allow 3rd party parcels to hook into existing UI. The loader aggregates all parcels into the repository, and establishes direct references between specific blocks.
An external module can declare a menu item, and then declare that the menu item is a child of menu. Bidirectional references make sure that the menu knows about this new child.
There is not currently any mechanism (that I know of) to indicate where within a parent/child relationship, a child should appear relative to other children. If the 3rd party menu item needs to be a the top of a menu, there is no way to indicate that in the parcel.xml file.
Data Management
A proper dynamic framework needs a mechanism to dynamically create user interface elements based on data stored somewhere. For instance, a mail application might need a menu generated automatically from the list of mail folders.
XUL
XUL uses RDF templates. These templates define a skeleton of content that is to be generated from an RDF data source. The templates define rules about different types of content that should be generated depending on the actual structure of the datasource.
For example, an RDF datasource might define single menu items for "leaf" mail folders (folders with no children) but define submenus for mail folders with children.
RDF datasources are either in-memory stores of native RDF data, or another type of datastore wrapped with an RDF datasource API. The latter case allows a sort of "lazy" query of the RDF data: the RDF data doesn't really exist until it is queried by the RDF template engine.
CPIA
CPIA is backed by the repository, so any and all data comes directly from the repository. There are no rules to generate CPIA blocks based on data. Instead, certain Block roots (usually TrunkParentBlocks) are told that they are responsible for a specific item. CPIA and the repository notification system then try to keep that block up to date when the data in question changes.
For example, the "Summary table" that appears in the middle of chandler is given an ItemCollection object. It knows how to "render" this object by inserting all of the items into the list widget.
Presentation model
Both XUL and CPIA keep models of their user interface in a hierarchy of objects (nodes or blocks, respectively)
However, the approachs to present these objects onto the screen are very different.
XUL
XUL uses the Gecko rendering engine to display all XUL. XUL nodes are displayed using more or less empty, presentation-free "frames" which just contain information about their location on the screen. These frames are arranged with a classic constraint-based layout with the primary unit being a "box" - simply a horizontal or vertical container.
Internally, Gecko renders the "Content Tree" (the nodes) into a seperate "Frame Tree" which provides an almost 1:1 mapping between nodes and frames. The frame tree is used to actually display user interface on the screen using the GFX graphics library. The content nodes are more or less unaware of their corresponding frames, but frames provide backreferences to their originating nodes. (
Note: "frames" in this context have nothing to do with HTML frames)
XUL frames and their layout information are generated by passing the nodes through the CSS engine. The CSS engine applies any and all CSS styles to the node, and creates the corresponding frame. Thus, any W3C property can be applies to any XUL element. For example, a XUL button can its color, border, image, padding, etc, all defined with CSS. This CSS engine is the same CSS engine that is used to render HTML to web pages, so standards compliance is exactly the same as Gecko's own HTML/CSS compliance.
The CSS engine acts as the controller in a sort of model-view-controller model. When the content model changes, the change is reflected through the CSS engine which in turn updates the relevant frames.
When XUL frames are rendered to screen, they do not use any "native controls" or "native windows" of any kind. The entire painting and display of a XUL window is managed by the GFX and supporting libraries. To achieve a native look and feel, the CSS engine and GFX libraries have been hooked up to the native "theme drawing" libraries on each platform. Special CSS properties are used to describe UI elements as "native looking"
CPIA
CPIA uses the wxWidgets library to access native widgets and lay them out on screen.
Each CPIA block has a corresponding root widget, and the widgets are also connected to each other in the standard widget-tree manner. This is similar to XUL's Frame Tree except that there is a bidirectional relationship between the blocks and the widgets. This is sort of a model-view relationship where each block is responsible for maintaining the state of its corresponding widgets.
wxWidgets provides a cross platform interface to native controls and a system for laying out these controls. The layout mechanism, called Sizers, is wrapped with CPIA's BoxContainer blocks. These blocks ensure that their children are appropriately connected to the Sizers.
Presentation and styling of the controls is done using the wxWidgets APIs. These have been somewhat abstracted into CPIA with a few different style abstractions, including colors and fonts. These colors and fonts are hooked directly up to the blocks that they mean to style. Sharing of color or style information between blocks is made possible by the ability of the repository to provide direct references between objects - so that multiple blocks can each contain a sort of "pointer" directly to the style/color/font blocks they will be using.
Each of these style blocks must be handled specifically by each block which wraps a native control. For example, the Button block must specifically look for color and style information and then use that information to call wxWidgets APIs to style the Button.
CPIA's Blocks are stored directly in the repository, which means changes in the repository are reflected automatically into the user interface. This also means that any state information stored in blocks (such as selection, loaded views, etc) are persistent across sessions.
Behavior
The behavior of a user interface is defined by both the inherent behavior of the individual controls (such as how a combo box behaves when clicked) as well as the application behavior that manages these controls. (such as the population of a list widget with the names of mail servers)
XUL
XUL uses XBL, or the Extensible Binding Language, to bind shared behavioral elements with specific xul nodes. When creating a frame to display a XUL node, the CSS engine also brings in any XBL bindings which may define the behavior of that node. For example, a "combobox" node in XUL would be bound to an XBL binding providing "combobox" behavior. Some of the additional control-specific pieces (such as the down-arrow button in a combo box) is also defined with XUL (and styled with CSS) and the behavior is written in JavaScript.
XBL attempts to mimic the platform-specific behavior wherever possible, and provides a mostly-complete accessibility story. (At this point the accessibility issues being addressed are interfaces with specialized accessibility hardware and software such as voice control and so forth)
At an application level, the
W3C DOM is the standard interface to manipulate nodes in a XUL window. This is the same API used by HTML pages, and is a key part of what is now being called AJAX (previously called DHTML)
Specific XBL bound controls have control-specific APIs. These control-specific APIs are similar to the the HTML form control APIs, but there is no expectation of API compatibility.
CPIA
CPIA widget behavior is very tightly bound to the wxWidgets library. Most behavior of most widgets is performed by the wxWidgets library. These widgets attempt to mimic the platform specific behavior. wxWidgets is written almost entirely in C++ and is an external library loaded into Python using wxPython.
Application level behavior is written to both the widget and the block APIs. The block API is the preferred API for general application level behavior, but some custom widgets have been written in Python or C++ to achieve behavior that isn't possible with the existing widgets. (For example, the mini and main calendars)
CPIA and the repository provide a very data-centric view of user interface when tools like AttributeEditors are used. These are special widgets that know how to manipulate data in the repository. One of the goals of CPIA is to free the user from worrying about some of the application-specific behavior of modifying some specific data, and instead abstract much of it out into the Chander application. The hope is that developers spend less time on application behavior and more time on data model. Chandler and CPIA would just make it all work.
Language
Both XUL and CPIA are built on dynamic languages. The common believe seems to be that basic application behavior is much easier to write and maintain using a dynamic language.
XUL
XUL's primary language is JavaScript. All event handlers and basic UI manipulation is done in JavaScript. The strength of this particular language is the wealth of knowledge out there, especially with respect to the DOM. Any DHTML book worth its salt will tell you 90% of what you need to know about the language and the DOM.
JavaScript is a fairly robust dynamic language. It was designed for web page manipulation, and not for fully fledged applications. It lacks a package management system and any significant 3rd party libraries. Its data model is straightforward and easy to understand and provides a certain minimal amount of introspection. Its primary users are web page developers who do not demand a lot out of the language.
XUL is built on the XPCOM object model. XPCOM provides very strong cross-language support between JavaScript and C++ using type libraries. Any interfaces written in IDL are completely accessible from both languages. This means an interface implemented in JavaScript is callable from C++, and a C++ object implementing an interfaces is callable from JavaScript, without any glue code or predefined wrappers.
3rd party libraries must be wrapped with XPCOM IDL-based interfaces in order to be callable from JavaScript.
CPIA
CPIA, as well of the rest of Chandler, is written almost entirely in Python.
Python is a very robust, very dynamic language. It was designed to simplify IT tasks but it has grown into a language used for web applications, end user applications, scientific tools, and more. It has a very strong package management system that Chandler leverages in its use of parcels, and to integrate 3rd party Python libraries. It is very dynamic and introspective, two key elements that have allowed the Repository database that Chandler uses.
Cross-language calls are done with the SWIG tool. SWIG generates wrapper C/C++ and Python which allows Python to call into any non-python library that has been wrapped with SWIG. SWIG is fairly automatic in its generation of code, reading C and C++ directly.
Summary
Both frameworks have a lot going for them. I've tried to keep my opinions out of the above sections but here is my opinion after completing the above analysis.
- Resource format: XUL's standards-based XML format is very easy to understand, and allows for very rapid application development. Chandler's parcel.xml format is incredibly obtuse and confusing. However, the resulting data in the repository is really powerful.
- Object identification: Undecided. This may be a real apples-and-oranges comparison. XUL is of course very UI centric, and thus you need things like
document.getElementById(). With CPIA you're often already working within the context of a particular block, and at least so far I haven't found the need to navigate the document tree as much as you would with XUL. That said, Chandler isn't as mature an application as Mozilla/FireFox/etc, so we may find we're doing more UI glue work as the application starts to be refined.
- Overlaying/Extending the user interface: Toss-up. Both very different, decent mechanisms. CPIA is by far more powerful in its ability to provide direct references to any block in any parcel file, even if it lacks minor things like placement of 3rd party blocks within child lists - something that could be fixed. That said, I don't remember needing anything more powerful than overlays in XUL. The overlays are reasonably straight forward (surely no more complex than CPIA) and the format is far easier to understand from a developer who "just wants to add a new toolbar button" or something.
- Data Management: Ultimately, XUL's RDF templates are stronger and more powerful. It provides a really dynamic system that can generate almost any XUL from almost any data source. The drawback is that it uses RDF, which can be really confusing at times.
CPIA has this whole "SetContents" mechanism used in conjunction with TrunkParentBlocks but ultimately it feels kludgy. The widget itself still has to intercept the SetContents event and then render the data itself.
- Presentation model: XUL wins this hands down. Interfaces are based on open standards, and infinitely stylable to achieve just about any look, using CSS up to level 3. Appearence is identical down to the pixel on different platforms if necessary, and native-like widgets look great. As for CPIA, wxWidgets simply sucks. Its hard to describe any significant appearance without writing some raw code to call wxWidgets APIs, and the appearence and behavior across platforms is notoriously bad.
- Behavior: Again, XUL really nails this. Because it isn't based on actual native widgets, the behavior is automatically cross-platform and platform-specific behavior is defined by exception. XBL allows very easy creation of new widgets, including embedding one widget inside another.
- Language: CPIA (or at least Python) wins this by a mile. Python is an incredibly powerful, mature language. JavaScript, while quite dynamic, works well mainly for glue code. (simple event handlers and such) Python is strong enough to supplant most uses of C++. If you're using JavaScript, any significant application logic will probably be written in C++. XPCOM gets points for providing smart cross-language calls at runtime though.
Looking Forward
There is promise of achieving some "best of both worlds" scenarios with the wonders of
PyXPCOM. This library allows Python to take a seat next to JavaScript in the world of Mozilla. Certain code still must be written in JavaScript (like XUL event handlers) but
PyXPCOM? opens up most of the Mozilla API to Python, and allows XPCOM components to be written in Python.