As I’ve been investigating XBL caching, I’ve been looking into some additional performance characteristics. Here are some tips on improving your use of XBL in Mozilla, relying on specific implementation details to gain performance benefits. You probably won’t notice much difference, but lots of minor changes can add up.
Properties and methods, that is the code in <property> and method <body> elements are compiled once per session. The code is compiled once when the first element that uses the binding is added into a document. For elements included in a XUL document, this means that they will be compiled while the window is being created and opened. Constructors and destructors are treated similarly.
Fields and handlers (<field> and <handler> elements) however are not compiled when the element is added. Instead, they are compiled on demand when they are used. This means that both fields and handlers do not directly affect the time to open a window, but can instead, in the case of handlers, affect the time to respond to an event.
The field element defines a value on a element. The code for it is compiled when the field is first accessed, not when it is defined. For example, assume a field exists such as that below:
<field name="someValue">10 * 50</field>
The code ’10 * 50′ will be compiled and evaluated when code that checks ‘element.someValue’ is encountered. Naturally, if this happens during the binding constructor, this will affect creation time. Although I haven’t investigated this, if this is the case, it might be better to just initialize the field directly in the constructor to save the overhead of having to evaluate the field separately.
However, for fields that are rarely accessed, initialization within the field itself is generally more optimal.
A very important note however, is that the field value is compiled and evaluated not once per session or even once per window. The code in a field body is compiled and evaluated once for every element that uses it. Sometimes, people write large blocks of initialization code directly in the field element. If this field isn’t accessed often, or the binding is only used once or twice in a window, this won’t matter too much. But if the binding is used frequently, this can have notable impact on performance. Instead, call a method from the field initializer such that at least the compilation is only performed once.
It also goes without saying that enumerating an object with a binding will invoke all of the fields and evaluate all of their code.
Event handlers defined in XBL are compiled the first time the event occurs. So, for example, a handler for a keydown event will get compiled when the element is focused and a key is pressed down. This saves time when creating the element for rarely used event handlers.
Unlike fields, handlers are only compiled once per window, even when multiple elements exist within the same window. This means that putting all of your code in a method and having handlers that simply wrap the method requires more time to create as methods are compiled when created. If improved creation or startup time is more important to you, then place the code in the handler instead.
It helps to use the event filtering attributes on the <handler> element when possible. These will filter out events that aren’t what the handler code is interested in, and avoids the need to compile and call the script code at all, as these filters are implemented entirely in native code. Attributes for filtering available include: modifiers, button, clickcount, keycode, charcode, and phase.
The description above mainly applies to compile time of the various XBL parts. In all cases, for fields, properties, methods and handlers, the compiled code must still be attached to the content each time the binding is used.
In summary, the choice of how to structure the specific parts of the XBL depends on where better performance is needed.
The display and extends attributes
This isn’t a entirely a performance tip but sometimes the display attribute (or the extends attribute) on a binding is used when it doesn’t need to be, for example:
This particular example doesn’t generally do anything for XUL elements as they are already boxes. The example above might do something if you had changed the display type in a some other manner, but that is a very rare occurrence. The colon-form syntax used here does not mean that it extends some other binding or element. In fact, where it does have a use doesn’t relate to XBL at all. Instead, it specifies which internal layout objects get created for the element.
Although in the past any element could be used for the display/extends colon-syntax, today there are only sixteen tags that are allowed, only half of which are actually useful. (The list of tags to allow was generated by searching the Mozilla source for usage rather than fixing up the cases where it was used and wasn’t necessary).
One such tag that might be worth investigating for performance is using display=”xul:spacer” on a binding. It creates a layout object that will never have children. If you know your binding will have no children, either regular or anonymous, it saves a very tiny amount of extra time and storage (60 bytes or so per element).
Many specific additional tips could be made but it is hard to measure the exact benefit in many cases. I’ve investigated some cases with xbl inheritance, but haven’t found any specific tips there yet. I’m continuing to look into other areas.