Chapter 5

Scripting Mozilla

In Mozilla, scripting plays important roles in the XPFE.
Whether developers refer to script access and security, user
interface logic, XPCOM object invocation, or script execution
in element event handlers, scripting is so integral to
application development that Mozilla, as a development
platform, would be inconceivable without it.

The core scripting language used in Mozilla is JavaScript.
Although it has had a reputation as an unsophisticated language
used mostly in web pages, JavaScript is more like a first-tier
programming language. Modularity, good exception handing,
regular expression enhancement, and number formatting are just
some features of the new JavaScript 1.5,[*] which is based on the ECMA-262 standard.[*] JavaScript 2.0, due
sometime late in 2002, promises to be an even bigger promotion
of the language.

Three distinct levels of JavaScript are identified in this
chapter. A user interface level manipulates content through the
DOM, a client layer calls on the services provided by XPCOM,
and, finally, an application layer is available in which
JavaScript can create an XPCOM component. The following section
describes these levels in detail.

Faces of JavaScript in Mozilla

As you have already
seen in some examples in this
book, the user interface uses JavaScript extensively to create
behavior and to glue various widgets together into a coherent
whole. When you add code to the event handler of one element to
manipulate another-for example, when you update the value of a
textbox using a XUL button-you take advantage of this first
"level" of scriptability. In this role, JavaScript uses the
Document Object Model (DOM) to access parts of the user
interface as a hierarchical collection of objects. The section
"Adding Scripts to the UI," later in this
chapter, discusses this highest level of scripting.

At a second level, JavaScript glues the entire user
interface to the XPCOM libraries beneath, which create the
application core. At this level, XPConnect (see the section "What Is XPConnect?" later in this chapter)
provides a bridge that makes these components "scriptable,"
which means that they can be invoked from JavaScript and used
from the user interface layer. When JavaScript calls methods
and gets data from scriptable components, it uses this second
layer of scriptability.

Finally, at the third and ultimate level of Mozilla
scripting, JavaScript can be used as a "first-order" language
for creating the application core itself, for writing software
components or libraries whose services are called. We discuss
this third level of scripting and provide a long example in the
section "Creating a JavaScript XPCOM
Component" in Chapter 8.

When you use JavaScript in these contexts, the application
architecture looks something like Figure
5-1, in which scripting binds the user interface to the
application core through XPConnect and can reside as a software
component using such technologies as XPIDL and XPCOM.

Figure 5-1: Scripting in
Mozilla

JavaScript and the DOM

In the application
layer of
Mozilla, there is little distinction between a web page and the
graphical user interface. Mozilla's implementation of the DOM
is fundamentally the same for both XUL and HTML. In both cases,
state changes and events are propagated through various DOM
calls, meaning that the UI itself is content-not unlike that of
a web page. In application development, where the difference
between application "chrome" and rendered content is typically
big, this uniformity is a significant step forward.

What Is the DOM?

The DOM is an API used to access HTML and XML documents. It
does two things for web developers: provides a structural
representation of the document and defines the way the
structure should be accessed from script. In the Mozilla XPFE
framework, this functionality allows you to manipulate the user
interface as a structured group of nodes, create new UI and
content, and remove elements as needed.

Because it is designed to access arbitrary HTML and XML, the
DOM applies not only to XUL, but also to MathML, SVG, and other
XML markup. By connecting web pages and XML documents to
scripts or programming languages, the DOM is not a particular
application, product, or proprietary ordering of web pages.
Rather, it is an API-an interface that vendors must
implement if their products are to conform to the W3C DOM
standard. Mozilla's commitment to standards ensures that its
applications and tools do just that.

When you use JavaScript to create new elements in an HTML
file or change the attributes of a XUL button, you
access an object model in which these structures are organized.
This model is the DOM for that document or data. The DOM
provides a context for the scripting language to operate in.
The specific context for web and XML documents-the top-level
window object, the elements that make up a web
document, and the data stored in those elements as children-is
standardized in several different specifications, the most
recent of which is the upcoming DOM Level 3 standard.

The DOM Standards and Mozilla

The DOM
specifications are split
into different levels overseen by the W3C. Each level provides
its own features and Mozilla has varying, but nearly complete,
levels of support for each. Currently, Mozilla's support for
the DOM can be summarized as follows:

DOM Level 1: Excellent

DOM Level 2: Good

DOM Level 3: Poor; under construction

Mozilla strives to be standards-compliant, but typically
reaches full support only when those standards have become
recommendations rather than working drafts. Currently, Level 1
and Level 2 are recommendations and Level 3 is a working
draft.

Standards like the DOM make Mozilla an especially attractive
software development kit (SDK) for web developers. The same
layout engine that renders web content also draws the GUI and
pushes web development out of the web page into the application
chrome. The DOM provides a consistent, unified interface for
accessing all the documents you develop, making the content and
chrome accessible for easy cross-platform development and
deployment.

DOM Methods and Properties

Methods in
the DOM allow you to access and
manipulate any element in the user interface or in the content
of a web page. Getting and setting attributes, creating
elements, hiding elements, and appending children all involve
direct manipulation of the DOM. The DOM mediates all
interaction between scripts and the interface itself, so even
when you do something as simple as changing an image when the
user clicks a button, you use the DOM to register an event
handler with the button and DOM attributes on the
image element to change its source.

The DOM Level 1 and Level 2 Core specifications contain
multiple interfaces, including Node, NodeList,
Element, and Document. The following sections
describe some interface methods used to manipulate the object
model of application chrome, documents, or metadata in Mozilla.
The Document and Element interfaces, in
particular, contain useful methods for XUL developers.

Using dump( ) to print to STDOUT

The code
samples in this
chapter use a method called dump( ) to print data to
STDOUT. This method is primarily used for debugging your code
and is turned on using a PREF. You can turn this
PREF on using the following code:

This code is necessary only if you are doing development
with a release distribution build of Mozilla. If you are using
a debug or nightly build, this PREF can be set from
the preferences panel by selecting Edit > Preferences >
Debug > Enable JavaScript dump( ) output.

getElementById

getElementById(aId) is
perhaps the most
commonly used DOM method in any programming domain. This is a
convenient way to get a reference to an element object by
passing that element's id as an argument, where the
id acts as a unique identifier for that element.

DOM calls like this are at the heart of Mozilla UI
functionality. getElementById is the main programmatic
entry point into the chrome and is essential for any dynamic
manipulation of XUL elements. For example, to get a box element
in script (i.e., to get a reference to it so you can call its
methods or read data from it), you must refer to it by using
the box id:

<box id="my-id" />

Since the return value of getElementById is a
reference to the specified element object, you usually assign
it to a variable like this:

Once you have the box element available as boxEl,
you can use other DOM methods like getAttribute and
setAttribute to change its layout, its position, its
state, or other features.

getAttribute

Attributes
are properties
that are defined directly on an element. XUL elements have
attributes such as disabled, height,
style, orient, and label.

<box id="my-id" foo="hello 1" bar="hello 2" />

In the snippet above, the strings "my-id," "hello 1," and
"hello 2" are values of the box element attributes. Note that
Gecko does not enforce a set of attributes for XUL elements.
XUL documents must be well-formed, but they are not validated
against any particular XUL DTD or schema. This lack of
enforcement means that attributes can be placed on elements ad
hoc. Although this placement can be confusing, particularly
when you look at the source code for the Mozilla browser
itself, it can be very helpful when you create your own
applications and want to track the data that interests you.

Once you have an object assigned to a variable, you can use
the DOM method getAttribute to get a reference to any
attribute in that object. The getAttribute method
takes the name of the desired attribute as a string. For
example, if you add an attribute called foo to a box
element, you can access that attribute's value and assign it to
a variable:

The dump method outputs the string "this is the foo
attribute," which is the value of the attribute foo.
You can also add or change existing attributes with the
setAttribute DOM method.

setAttribute

The setAttribute
method
changes an existing attribute value. This method is useful for
changing the state of an element-its visibility, size, order
within a parent, layout and position, style, etc. It takes two
arguments: the attribute name and the new value.

By setting an attribute that doesn't already exist, you
create it dynamically, adding a value to the hierarchical
representation of nodes that form the current document object.
After this code is executed, the boxEl element is the
same as an element whose bar attribute was hardcoded
into the XUL:

<box id="my-id" bar="this is the new attribute bar" />

These sorts of ad hoc changes give you complete control over
the state of the application interface.

createElement

If you
need to dynamically
create an element that doesn't already exist-for example, to
add a new row to a table displaying rows of information, you
can use the method createElement. To create and add a
text element to your box example, for example, you can use the
following code:

Once you create the new element and assign it to the
textEl variable, you can use appendChild to
insert it into the object tree. In this case, it is appended to
boxEl, which becomes the insertion point.

NOTE

For mixed namespace documents like XUL and HTML, you can
use a variation of createElement called
createElementNS. To create a mixed namespace
element, use this code:

Notice the use of appendChild. This method,
discussed next, is used to insert the new element or text node
into the DOM tree after it is created. Create-and-append is a
common two-step process for adding new elements to the object
model.

appendChild

To dynamically
add an
element to a document, you need to use the method
appendChild( ). This method adds a newly created
element to an existing parent node by appending to it. If a
visible widget is added, this change is visible in the
interface immediately.

This example creates a new element, gets an existing parent
element from the document, and then uses appendChild(
) to insert that new element into the document. It also
uses setAttribute to add an attribute value and some
CSS style rules, which can highlight the new element in the
existing interface.

cloneNode

For
elements that already
exist, a copy method allows you to duplicate elements to avoid
having to recreate them from scratch. cloneNode, which
is a method on the element object rather than the
document, returns a copy of the given node.

The method takes a Boolean-optional parameter that specifies
whether the copy is "deep." Deep copies duplicate all
descendants of a node as well as the node itself.

getElementsByTagName

Another
very useful
method is getElementsByTagName. This method returns an
array of elements of the specified type. The argument used is
the string element type. "box," for example, could be
used to obtain an array of all boxes in a document. The array
is zero-based, so the elements start at 0 and end with the last
occurrence of the element in the document. If you have three
boxes in a document and want to reference each box, you can do
it as follows:

getElementsByTagName is a handy way to obtain DOM
elements without using getElementById. Not all
elements have id attributes, so other means of getting
at the elements must be used occasionally.[*]

Getting an element object and its
properties

In addition
to a basic set of
attributes, an element may have many properties. These
properties don't typically appear in the markup for the
element, so they can be harder to learn and remember. To see
the properties of an element object node, however, you can use
a JavaScript forin loop to iterate through
the list, as shown in Example 5-1.

Note the implicit functionality in the el object
itself: when you iterate over the object reference, you ask for
all members of the class of which that object is an instance.
This simple example "spells" the object out to the console.
Since the DOM recognizes the window as another element (albeit
the root element) in the Document Object Model, you can use a
similar script in Example 5-2 to get the
properties of the window itself.

The output in Example 5-2 is a small
subset of all the DOM properties associated with a XUL window
and the other XUL elements, but you can see all of them if you
run the example. Analyzing output like this can familiarize you
with the interfaces available from window and other
DOM objects.

Retrieving elements by property

You can also use a
DOM method to
access elements with specific properties by using
getElementsByAttribute. This method takes the name and
value of the attribute as arguments and returns an array of
nodes that contain these attribute values:

One interesting use of this method is to toggle the state of
elements in an interface, as when you get all menu items whose
disabled attribute is set to true and set them to
false. In the xFly sample, you can add this functionality with
a few simple updates. In the xfly.js file in the xFly
package, add the function defined in Example
5-3.

Although this example doesn't update elements whose
disabled attribute is not specified, you can call this
function from a new menu item and have it update all menus
whose checked state you do monitor, as shown in Example 5-4.

When you add this to the xFly application window (from Example 2-10, for example, above the
basic vbox structure), you get an application menu bar
with a menu item, Toggle, that reverses the checked state of
the three items in the "Fly Types" menu, as seen in Figure 5-2.

Figure 5-2: Toggling the
state of menu items in xFly

The following section explains more about hooking scripts up
to the interface. Needless to say, when you use a method like
getElementsByAttribute that operates on all elements
with a particular attribute value, you must be careful not to
grab elements you didn't intend (like a button elsewhere in the
application that gets disabled for other purpose).

Adding Scripts to the UI

Once you are comfortable with how JavaScript works in the
context of the user interface layer and are familiar with some
of the primary DOM methods used to manipulate the various
elements and attributes, you can add your own scripts to your
application. Though you can use other techniques to get scripts
into the UI, one of the most common methods is to use Mozilla's
event model, which is described in the next few sections.

Handling Events from a XUL
Element

Events are input
messages
that pass information from the user interface to the
application code. Capturing this information, or event
handling, is how you usually tell scripts when to start and
stop.

When the user clicks a XUL button, for instance, the button
"listens" for the click event, and may also handle that event.
If the button itself does not handle the event (e.g., by
supplying executable JavaScript in an event handler attribute),
then the event "bubbles," or travels further up into the
hierarchy of elements above the button. The event handlers in
Example 5-3 use simple inline JavaScript
to show that the given event (e.g., the window loading in the
first example, the button getting clicked in the second, and so
on) was fired and handled.

As in HTML, predefined event handlers are available as
attributes on a XUL element. These attributes are entry points
where you can hook in your JavaScript code, as these examples
show. Note that event handler attributes are technically a
shortcut, for which the alternative is to register event
listeners explicitly to specified elements. The value of these
on[event] event handler attributes is the inline JavaScript
that should be executed when that event is triggered. Example 5-5 shows some basic button activation
events.

While the window and button events in Example 5-5 carry out some inline script, there is
a variation with the onchange handler attached to the
menulist element. onchange contains a
JavaScript function call whose definition may live in the XUL
document itself or in an external file that is included by
using the src attribute on a script
element:

A large basic set of event handler attributes is available
for use on XUL elements (and HTML elements). Appendix C has a full listing of these
events along with explanations. The following subset
shows the
potential for script interaction when the UI uses event
handlers:

Some of these event handlers work only on particular
elements, such as window, which listens for the
load event, the paint event, and other
special events.

To see all event handler attributes on a particular element,
you can execute the short script in Example
5-6, which uses the forin loop in
JavaScript to iterate over the members of an object-in this
case, a XUL element.

The function you added in Example 5-4
is also an example of event handler code in an application's
interface.

Events and the Mozilla Event
Model

The event model in Mozilla is the general framework for how
events work and move around in the user interface. As you've
already seen, events tend to rise up through the DOM
hierarchy-a natural process referred to as event propagation or
event bubbling. The next two sections describe event
propagation and its complement, event capturing.

Event propagation and event
bubbling

This availability of events in
nodes above the
element of origin is known as event propagation or event
bubbling. Event bubbling means you can handle events anywhere
above the event-raising element in the hierarchy. When events
are handled by elements that did not initiate those events, you
must determine which element below actually raised the event.
For example, if an event handler in a menu element handles an
event raised by one of the menu items, then the menu should be
able to identify the raising element and take the appropriate
action, as shown in Example 5-7. In this
example, a JavaScript function determines which
menuitem was selected and responds appropriately.

The event handler in the parent node menu finds out which
child menuitem was actually clicked by using
event.target and takes action accordingly. Let's walk
through another possible scenario. If a user of an application
selects an item from a menu list, you could get the node of
that item by using event.target. Your script could
then abstract that item's value or other information, if
necessary.

Trapping events

When an
event is raised, it is typically
handled by any node interested in it as it continues its way up
the DOM hierarchy. In some cases, you may want to handle an
event and then prevent it from bubbling further up, which is
where the DOM Event
method
stopPropagation( ) comes in handy.

Example 5-8 demonstrates how event
bubbling can be arrested very simply. When the XUL document in
Example 5-8 loads, an event listener is
registered with a row in the tree. The event listener
handles the event by executing the function stopEvent(
). This function calls an event object method,
stopPropagation, which keeps the event from bubbling
further up into the DOM. Note that the tree itself has an
onclick event handler that should display a message
when clicked. However, the
stopEvent( ) method has stopped propagation, so after
the data in the table is updated, the event phase is
effectively ended. In this case, the function was used to trap
the event and handle it only there.

Capturing events

Event
capturing is the complement of
event bubbling. The DOM provides the addEventListener
method for creating event listeners on nodes that do not
otherwise supply them. When you register an event listener on
an ancestor of the event target (i.e., any node above the
event-raising element in the node hierarchy), you can use event
capturing to handle the event in the ancestor before it is
heard in the target itself or any intervening nodes.

To take advantage of event capturing (or event bubbling with
elements that do not already have event listeners), you must
add an event listener to the element that wants to capture
events occurring below it. Any XUL element may use the DOM
addEventListener
method to register
itself to capture events. The syntax for using this method in
XUL is shown here:

The event handler code argument can be inline code or the
name of a function. The
useCapture parameter specifies whether the event listener
wants to use event capturing or be registered to listen for
events that bubble up the hierarchy normally. In Figure 5-3, the alert dialog invoked by the
menuitem itself is not displayed, since the root
window element used event capture to handle the event
itself.

Figure 5-3: Event
capturing

An onload
event handler for a
XUL window can also register a box element to capture
all click events that are raised from its child elements:

Changing an Element's CSS Style Using
JavaScript

Much of
what makes the
Mozilla UI both flexible and programmable is its ability to
dynamically alter the CSS style rules for elements at runtime.
For example, if you have a button, you can toggle its
visibility by using a simple combination of JavaScript and CSS.
Given a basic set of buttons like this:

The previous code snippet makes a visible button disappear
by setting its hidden attribute to true. Adding a few
more lines, you can toggle the visibility of the button, also
making it appear if it is hidden:

Another useful application of this functionality is to
collapse elements such as toolbars, boxes, and iframes in your
application.

The setAttribute method can also be used to update
the element's class attribute with which style rules
are so often associated. toolbarbutton-1 and
button-toolbar are two different classes of button.
You can change a button from a toolbarbutton-1-the
large button used in the browser-to a standard toolbar button
using the following DOM code:

// get the Back button in the browser
b1 = document.getElementById("back-button");\
b1.setAttribute("class", "button-toolbar");

This dynamically demotes the Back button to an ordinary
toolbar button. Code such as this assumes, of course, that you
know the classes that are used to style the various widgets in
the interface.

Be aware, however, that when you set the style
attribute in this way, you are overwriting whatever style
properties may already have been defined in the style
attribute. If the document referenced in the snippet above by
the ID some-element has a style attribute in
which the font size is set to 18pc, for example, that
information is erased when the style attribute is manipulated
in this way.

Creating Elements Dynamically

Using the
createElement method in XUL lets you accomplish things
similar to document.write in HTML, with which you can
create new pages and parts of a web page. In Example 5-9,
createElement is used to generate a menu dynamically.

The JavaScript function
generate( ) in Example 5-9 gets the menupopup as the
parent element for the new elements, creates five
menuitems in an array called menuitems, and
stores five string ID names for those menuitems.

The variable l is the length of the array. The
variable newElement is a placeholder for elements
created by using the createElement method inside of
the for loop. generate( ) assigns
newElement on each iteration of the loop and creates a
new menuitem each time, providing a way to dynamically
generate a list of menu choices based on input data or user
feedback. Try this example and experiment with different
sources of data, such as a menu of different auto
manufacturers, different styles on group of boxes that come
from user selection, or tabular data in a tree.

Sharing Data Between Documents

As the scale of your application development increases and
your applications grow new windows and components, you may
become interested in passing data around and ensuring that the
data remains in scope. Misunderstanding that scope often leads
to problems when beginning Mozilla applications.

Scope in Mozilla

The general
rule is
that all scripts pulled in by the base XUL document and scripts
included in overlays of this document are in the same scope.
Therefore, any global variables you declare in any of these
scripts can be used by any other scripts in the same scope. The
decision to put a class structure or more sophisticated design
in place is up to you.

The relationship of a parent and child window indicates the
importance of storing data in language constructs that can be
passed around. This code shows a common way for a parent to
pass data to a window it spawns:

Using the window.arguments array

The previous
code snippet creates a new JavaScript object, obj, and
assigns the value of an empty string to that object's
res property. The object is then passed by reference
to the new window as the last parameter of the openDialog(
) method so it can be manipulated in the scope of the
child window:

In that child window, the object is available as an indexed
item in the special window.arguments array. This array
holds a list of the arguments passed to a window when it is
created. window.arguments[0] is a reference to the first
argument in the openDialog( ) parameter list that is
not a part of the input parameters for that method,
window.arguments[1] is the second argument, and so on. Using
window.arguments is the most common way to pass
objects and other data around between documents.

When the user clicks a button in the displayed dialog (i.e.,
the OK or Cancel button), one of the functions sets a value to
the res property of the passed-in object. The object
is in the scope of the newly created window. When control is
passed back to the script that launched the window, the return
value can be checked:

In this case, a simple dump statement prints the result, but
you can also test the result in your application code and fork
accordingly.

XPConnect and Scriptable
Components

At the second level of scripting, XPConnect binds JavaScript
and the user interface to the application core. Here,
JavaScript can access all XPCOM components that implement
scriptable libraries and services through a special global
object whose methods and properties can be used in JavaScript.
Consider these JavaScript snippets from the Mozilla source
code:

The filepicker, file, and
localfile components that these JavaScript objects
represent are a tiny fraction of the components available via
XPConnect to programmers in Mozilla. This section describes how
to find these components, create the corresponding JavaScript
objects, and use them in your application programming.

What Is XPConnect?

Until now,
scripting has
referred to scripting the DOM, manipulating various elements in
the interface, and using methods available in Mozilla
JavaScript files. However, for real applications like the
Mozilla browser itself, this may be only the beginning. The UI
must be hooked up to the application code and services (i.e.,
the application's actual functionality) to be more than just a
visual interface. This is where XPConnect and XPCOM come
in.

Browsing the Web, reading email, and parsing XML files are
examples of application-level services in Mozilla. They are
part of Mozilla's lower-level functionality. This functionality
is usually written and compiled in platform-native code and
typically written in C++. This functionality is also most often
organized into modules, which take advantage of Mozilla's
cross-platform component object model (XPCOM), and are known as
XPCOM components. The
relationship of these components and the application services
they provide to the interface is shown in Figure 5-4.

Figure 5-4: How XPConnect
fits into the application model

In Mozilla, XPConnect is the bridge between JavaScript and
XPCOM components. The XPConnect technology wraps natively
compiled components with JavaScript objects. XPCOM, Mozilla's
own cross-platform component technology, is the framework on
top of which these scriptable components are built. Using
JavaScript and XPConnect, you can create instances of these
components and use their methods and properties as you do any
regular JavaScript object, as described here. You can access
any or all of the functionality in Mozilla in this way.

Chapter 8 describes more about
the XPConnect technology and how it connects components to the
interface. It also describes the components themselves and
their interfaces, the XPCOM technology, and how you can create
your own XPCOM components.

Creating XPCOM objects in script

Example 5-10 demonstrates
the creation and use of an
XPCOM component in JavaScript.
In this example, the script instantiates the
filepicker object and then uses it to display a file
picker dialog with all of the file filters selected. To run
this example, add the function to your xfly.js file and
call it from an event handler on the "New" menu item you added
in Example 3-5. Example 5-10Scriptable component example

Note the first two lines in the function and the way they
work together to create the fpfilepicker
object. The first line in the function assigns the name of the
nsFile-picker interface to the nsIFilePicker
variable in JavaScript. This variable is used in the second
line, where the instance is created from the component to
specify which interface on that component should be used.
Discovering and using library interfaces is an important aspect
of XPCOM, where components always implement at least two
interfaces.

In Example 5-11, an HTML file (stored
locally, since it wouldn't have the required XPConnect access
as a remote file because of security boundaries) loaded in
Mozilla instantiates a Mozilla sound component and plays a
sound with it. Go ahead and try it. Example 5-11Scripting components from HTML

Sound Service Play Example

As in Example 5-10, the classes[ ]
array on the special Mozilla Components object refers
to a particular component-in this case, the sound
component-by contract ID. All XPCOM objects must have a
contract ID that uniquely identifies them with the domain, the
component name, and a version number ["@mozilla.org/sound;1"],
respectively. See the "XPCOM
Identifiers" section in Chapter
8 for more information about this.

Finding components and interfaces

Most
components are scripted in Mozilla. In fact, the challenge is
not to find cases when this scripting occurs (which you can
learn by searching LXR for the Components), but to
find Mozilla components that don't use scriptable components.
Finding components and interfaces in Mozilla and seeing how
they are used can be useful when writing your own
application.

The Mozilla
Component Viewer is a great tool for discovering components and
provides a convenient UI for seeing components and looking at
their interfaces from within Mozilla. The Component Viewer can
be built as an extension to Mozilla (see "cview" in the
extensions directory of the Mozilla source), or it can be
downloaded and installed as a separate
XPI from http://www.hacksrus.com/~ginda/cview/.
Appendix B describes the
Component Viewer in more detail.

Commonly used XPCOM objects in the browser and other Mozilla
applications include file objects, RDF services, URL objects,
and category managers.

Selecting the appropriate interface
from the component

In all cases,
the way to
get the object into script is to instantiate it with the
special classes object and use the createInstance(
) method on the class to select the interface you want to
use. These two steps are often done together, as in the
following example, which gets the component with the contract
ID ldap-connection;1, instantiates an object from the
nsILDAPConnection interface, and then calls a method on
that object:

Mozilla constantly uses these processes. Wherever
functionality is organized into XPCOM objects (and most of it
is), these two statements bring that functionality into
JavaScript as high-level and user-friendly JavaScript
objects.

JavaScript Application Code

There are
two
ways to use JavaScript in the third, deepest level of
application programming. The first is to organize your
JavaScript into libraries so your functions can be reused,
distributed, and perhaps collaborated upon.

The second way is to write a JavaScript component, create a
separate interface for that component, and compile it as an
XPCOM component whose methods and data can be accessed from
XPConnect (using JavaScript). This kind of application
programming is described in Chapter
8, which includes examples of creating new interfaces,
implementing them in JavaScript or C++, and compiling, testing,
and using the resulting component in the Mozilla interface.

This section introduces the library organization method of
JavaScript application programming. The JSLib code discussed
here is a group of JavaScript libraries currently being
developed by Mozilla contributors and is especially useful for
working with the XPFE and other aspects of the Mozilla
application/package programming model. When you include the
right source files at the top of your JavaScript and/or XUL
file, you can use the functions defined in JSLib libraries as
you would use any third-party library or built-in functions.
You may even want to contribute to the JSLib project yourself
if you think functionality is missing and as your Mozilla
programming skills grow.

JavaScript Libraries

The open
source
JSLib project makes life easier for developers. The JSLib
package implements some of the key XPCOM components just
discussed and wraps them in simpler, JavaScript interfaces,
which means that you can use the services of common XPCOM
components without having to do any of the instantiation,
interface selection, or glue code yourself. Collectively, these
interfaces are intended to provide a general-purpose library
for Mozilla application developers. To understand what JSLib
does, consider the following short snippet from the JSLib
source file jslib/io/file.js, which implements a
close( ) function for open file objects and provides a
handy way to clean up things when you finish editing a file in
the filesystem.

To use the close method as it's defined here,
import the file.js source file into your JavaScript,
create a file object (as shown in the examples below), and call
its close( ) method.

xpcshell

Most
examples in this section
are in xpcshell, but using these libraries in your
user interface JavaScript is just as easy. You can access
these libraries from a XUL file, as the section "Using the DirUtils class," later in this
chapter, demonstrates.

xpcshell is the command-line interpreter to JavaScript and
XPConnect. This shell that uses XPConnect to call and
instantiate scriptable XPCOM interfaces. It is used primarily
for debugging and testing scripts.

To run xpcshell, you need to go to the Mozilla bin
directory or have that folder in your PATH. For each
platform, enter:

Windows:

xpcshell.exe

Unix:

./run-mozilla.sh ./xpcshell

To run xpcshell on Unix, you need to supply environment
variables that the interpreter needs. You can use the
run-mozilla.sh shell script that resides in the
Mozilla bin directory.

The source files for JSLib are well annotated and easy to
read. JSLib provide easy-to-use interfaces for creating
instances of components (e.g., File objects), performing
necessary error checking, and ensuring proper usage. To use a
function like the one just shown, simply include the source
file you need in your XUL:

Installing JSLib

To use the
JavaScript libraries, install the JSLib package in Mozilla. The
package is available as a tarball, a zip file, or as CVS
sources. The easiest way to obtain it is to install it from the
Web using Mozilla's XPInstall technology, described in Chapter 6.

Using your Mozilla browser, go to http://jslib.mozdev.org/installation.html
and click the installation hyperlink. The link uses XPInstall
to install JSLIB and make it available to you in Mozilla. To
test whether it is installed properly, type the following code
in your shell:

./mozilla -chrome chrome://jslib/content/

You should see a simple window that says "welcome to
jslib."

The JSLib libraries

Currently
available JavaScript functions in the JSLib package are divided
into different modules that, in turn, are divided into
different classes defined in source files such as
file.js, dir.js, and fileUtils.js. Table 5-1 describes the basic classes in the
JSLib package's I/O module and describes how they are used.

Table 5-1: JSLib
classes

Class / (filename)

Description

File / (file.js)

Contains most routines associated with the File
object (implementing nsIFile). The library is
part of the jslib I/O module.

Using the File class

The JSLib File
class exposes most local file routines from the nsIFile
interface. The File class is part of the JSLib I/O
module, and is defined in jslib/io/file.js. Here is how
you load the library from xpcshell:

These examples show some ways the JSLib File object
can manipulate local files. Using these interfaces can make
life a lot easier by letting you focus on creating your Mozilla
application without having to implement XPCOM nsIFile
objects manually from your script.

Using the FileUtils class

To create an
instance of the FileUtils class, use the
FileUtils constructor:

js> var fu = new FileUtils( );
js> fu;
[object Object]

Then look at the object by calling its help
method:

js> fu.help;

The difference between using the File and
FileUtils interfaces is that methods and properties on
the latter are singleton and require a path argument,
while the FileUtils utilities are general purpose and
not bound to any particular file. The FileUtils
interface has several handy I/O utilities for converting,
testing, and using URLs, of which this example shows a few:

Most methods on the FileUtils objects are identical
to the methods found in file.js, except they require a
path argument. Another handy method in the FileUtils
class is spawn, which spawns an external executable
from the operating system. It's used as follows:

js> fu.spawn('/usr/X11R6/bin/Eterm');

This command spawns a new Eterm with no argument. To open an
Eterm with vi, you could also use this code:

js> fu.spawn('/usr/X11R6/bin/Eterm', ['-e/usr/bin/vi']);

Checking to see if three different files exist would take
several lines when using the File class, but the
FileUtils class is optimized for this type of check,
as the following listing shows:

You need to initialize the FileUtils class only
once to use its members and handle local files robustly.

Using the Dir class

The Dir
class is custom-made for working with directory structures on a
local filesystem. To create an instance of the Dir
class, call its constructor and then its help method
to see the class properties:

js> var d = new Dir('/tmp');
js> d.help;

Dir inherits from the same base class as
File, which is why it looks similar, but it implements
methods used specifically for directory manipulation:

js> d.path;
/tmp
js> d.exists( );
true
js> d.isDir( );
true

The methods all work like those in the File and
FileUtils classes, so you can append a new directory
name to the object, see if it exists, and create it if (it does
not) by entering:

Using the DirUtils class

Note that
some methods in the DirUtils class cannot be called
from xpcshell and instead must be called from a XUL window into
which the proper JSLib source file was imported. The following
XUL file provides two buttons that display information in
textboxes about the system directories: