We are planning to deprecate the use, in Firefox, of the techniques described in this document.
Don't use these techniques to develop new add-ons. Use WebExtensions or the Add-on SDK instead.
If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions or the SDK instead.
Add-ons developed using these techniques might not work with multiprocess Firefox (e10s), which is already the default in Firefox Nightly and Firefox Developer Edition, and will soon be the default in Beta and Release versions of Firefox. We have documentation on making your add-ons multiprocess-compatible, but it will be more future-proof for you to migrate to WebExtensions or the Add-on SDK.
A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.
Event handlers
Just like with HTML, most JavaScript code execution is triggered by event handlers attached to DOM elements. The most commonly used event is the onload event, which is used in overlays and other windows to detect when the window has loaded and then run initialization code:
// rest of overlay code goes here. window.addEventListener( "load", function() { XulSchoolChrome.BrowserOverlay.init(); }, false);
You can do something similar with the onunload event, to do any cleanup you may need.
Another way to attach event handlers, just like HTML, is to place the handler in the XUL code:
<overlay id="xulschoolhello-browser-overlay" onload="XulSchoolChrome.BrowserOverlay.init();" xmlns="https://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
We prefer the first method because it keeps a better separation of content and behavior. Also, note that the addEventListener method receives the event name without the "on" prefix, while element attributes do have the prefix. There's a long list of events you can listen to, and which you use depend on the situation. Elements only implement the events that are relevant to them, but there are several events that are implemented for most elements. These are some notable events you should keep in mind:
- oncommand. This is one of the most important and commonly used events in XUL. It's very useful because it represents the most common action for input controls such as menu items, buttons and checkboxes. For a button, it represents the action of the user clicking on it, or focusing it with the keyboard and then pressing the ENTER key. It's an abstraction of the main way to interact with a control element.
- onselect. Fired when the selection in a tree or listbox changes.
- onclick. Triggered when the user clicks on the element, including right clicks. You shouldn't normally use this event to trigger actions on input controls such as buttons. Use oncommand instead.
- onfocus and onblur. Used when an element receives or loses focus when the user is navigating with the keyboard. You can combine these with -moz-user-focus to add custom focus behavior to elements that normally wouldn't have it.
- Drag and drop. Drag and drop operations involve several events. Since drag and drop is a complicated thing to manage, there are some high level wrappers that facilitate working with it. Also keep in mind that there are 2 drag and drop APIs, the newest (and preferred) one introduced in Firefox 3.5.
Event handlers can take an event argument, which is an Event object that holds information on the event. You can get information on key modifiers (in case the user was holding a modifier key like Alt while performing the event), screen coordinates for mouse events, and most importantly, the target element for the event. For example:
<button label="&xulschoolhello.defaultGreeting.label;" oncommand="XulSchoolChrome.BrowserOverlay.changeGreeting(event);" />
Then on the Javascript code you would have something like this:
changeGreeting : function(aEvent) { // more stuff aEvent.target.setAttribute("label", someNewGreeting); }
The target in this example is the button element, so clicking on it will change its text. The advantage of using the event argument is that the method is not dependent of the specific button, so it can also be used for other elements.
For more advanced event handling, you should read about Event Propagation. In a nutshell, events propagate from the root of the DOM tree all the way down to the target element and then all the way up back to the root, in the capture and bubble phases, respectively. You can capture and cancel events during any of these phases, provided that they aren't canceled before they reach the point where you intended to capture them. The addEventListener method allows you to control the phase where you want to handle an event, with the last argument of the function.
Custom events
This is a very powerful tool that you should know, even if it isn't that frequently used. The DOM createEvent function allows you to create custom events that you can dispatch and capture.
Custom events serve as a good communication mechanism, specially when dealing with a somewhat common problem: communication between window XUL and web page content. It isn't hard for XUL code to control the content on pages being loaded or displayed, as we will see later on, but it can be hard for your extension XUL code to receive information from pages in a secure manner. This is because it would be very insecure to have a website JS controlling the behavior of Firefox and running JavaScript code with chrome privileges.
Suppose your extension interacts with pages from a website, and you want some actions on this site to trigger actions in your extension. One way to solve this is to have the actions on the site to generate a custom event that can be easily recognized by your extension. You can capture the events in the XUL overlay, since they'll bubble all the way up:
// in the overlay code. document.addEventListener( "XSHelloGreetingEvent", function(aEvent) { /* do stuff*/ }, false);
Be careful when doing this! You should at least validate the URL of the page that is generating the custom event, so that you know that it's coming from the right place. You should also avoid this kind of events to trigger actions that could be destructive to the user's data, because a malicious site could try to trigger these events and cause damage. There's a reason for the division between remote content and local chrome, so make sure you respect it.
There's a section further ahead on Intercepting Page Loads which complements this section very well. This should give you a solid foundation to handle interaction between web content and XUL. Additional information on custom events and how they can be used to effect communication between web content and XUL can be found in the Interaction between privileged and non-privileged pages code snippets, which describe and provide examples of this sort of communication.
Broadcasters
Keeping a consistent UI is another important aspect of extension behavior. Maybe your extension needs to disable or enable a series of controls when the user logs in or out of a service, or when Firefox detects it's online or offline. It's common that you need to change several elements at the same time, and this can be difficult to manage through JavaScript. The broadcaster element can help you out in these cases.
First you need to add a broadcaster element to your XUL code, as a child of a broadcasterset element.
<broadcasterset id="xulschoolhello-broadcasterset"> <broadcaster id="xulschoolhello-online-broadcaster" /> </broadcasterset>
These elements are completely invisible, so you can put them anywhere. It is recommended that you have them at the top of the XUL code, along with script declarations and other invisible elements with as popupset and commandset.
Then you need to identify which of your XUL elements will be linked to this broadcaster, using the observes attribute:
<menuitem id="xulschoolhello-hello-menu-item" label="&xulschoolhello.hello.label;" accesskey="&xulschoolhello.helloItem.accesskey;" observes="xulschoolhello-online-broadcaster" oncommand="XULSchoolChrome.BrowserOverlay.sayHello(event);" />
The attribute value is set to be the id of the broadcaster element, indicating that this element will observe all attribute changes that happen in the broadcaster. You can have as many elements as you want observing a broadcaster.
With that set, all you need to do now is set or remove attributes in the broadcaster using JavaScript. All nodes observing it will automatically have those attribute values set or removed as well. You can override pre-existing values, such as the label attribute value in the example.
let onlineBroadcaster = document.getElementById("xulschoolhello-online-broadcaster"); onlineBroadcaster.setAttribute("label", "Something");
You can also have finer-grained control to this behavior by adding the observes element as a child to your observer node. This allows you to choose which attributes you want it to observe.
Broadcasters allow you to easily maintain consistency among numerous elements without having to add much code. They also save you the trouble of having to know if a given element is present in the DOM or not. For example, if you have a customizable toolbar, you can't be sure if a given button is present or not, so it's easier to use a broadcaster. This way you only need to set values to the broadcaster instead of having to check if the button is there or not.
Commands
The command element is a specialized type of broadcaster, meant to be used with the oncommand event. This is the recommended way of centralizing common UI behavior in Firefox and extensions. Commands are heavily used in Firefox, as a quick look into the DOM Inspector should show.
Their behavior is identical as broadcaster elements, but they should be used when oncommand is one of the shared attributes. Our menu example is in fact better suited for a command.
<commandset id="xulschoolhello-commandset"> <command id="xulschoolhello-hello-command" oncommand="XULSchoolChrome.BrowserOverlay.sayHello(event);" /> <!-- More commands. --> </commandset> <!-- More code here... --> <menuitem id="xulschoolhello-hello-menu-item" label="&xulschoolhello.hello.label;" accesskey="&xulschoolhello.helloItem.accesskey;" command="xulschoolhello-hello-command" />
Commands allow you to keep your JavaScript calls in a single place, avoiding code repetition and possible bugs. Your UI can easily scale this way. You can create an extension that adds toolbar buttons, statusbar buttons and menu items, all with equivalent behavior, and without having to repeat lots of XUL code in the process. Commands and broadcasters also facilitate working with complex form windows and dialogs. You should always keep them in mind when adding the event-driven code for your extension.
This tutorial was kindly donated to Mozilla by Appcoast.