Chapter 5. Scripting Mozilla- P1
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,[1] which is based on the
ECMA-262 standard.[2] 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.
5.1. 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 Section 5.3, 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 Section 5.4.1 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
Section 8.2.1 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

Notes
[1] This book does not pretend to give a complete overview of JavaScript. You can
view the full JavaScript 1.5 reference online at
http://developer.netscape.com/docs/manuals/index.html?content=javascript.html.
[2] The third edition of the EMCA-262 EMCAScript Language Specification can be
found at http://www.ecma.ch/ecma1/STAND/ECMA-262.HTM.
5.2. 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.
5.2.1. 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.

5.2.2. 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.
5.2.3. 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.
5.2.3.1. 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:
const PREFS_CID =
"@mozilla.org/preferences;1";
const PREFS_I_PREF = "nsIPref";
const PREF_STRING =
"browser.dom.window.dump.enabled";
try {
var Pref = new
Components.Constructor(PREFS_CID, PREFS_I_PREF);
var pref = new Pref( );
pref.SetBoolPref(PREF_STRING, true);

} catch(e) {}
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.
5.2.3.2. 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:
Since the return value of getElementById is a reference to the specified
element object, you usually assign it to a variable like this:
var boxEl = document.getElementById('my-id');
dump("boxEl="+boxEl+"\n");
console output: boxEl=[object XULElement]

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.
5.2.3.3. getAttribute
Attributes are properties that are defined directly on an element. XUL
elements have attributes such as disabled, height, style, orient,
and label.
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:
var boxEl = document.getElementById('my-id');

var foo = boxEl.getAttribute('foo');
dump(foo+'\n');
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.
5.2.3.4. 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.
boxEl=document.getElementById('my-id');
boxEl.setAttribute('foo', 'this is the foo
attribute changed');
var foo = boxEl.getAttribute('foo');
dump(foo+'\n');
The script above outputs the string "this is the foo attribute changed" to the
console. You can also use setAttribute to create a new attribute if it
does not already exist:

boxEl=document.getElementById('my-id');
boxEl.setAttribute('bar', 'this is the new
attribute bar');
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:
These sorts of ad hoc changes give you complete control over the state of the
application interface.
5.2.3.5. 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:
boxEl = document.getElementById('my-id');
var textEl =
document.createElement('description');

boxEl.appendChild(textEl);
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.
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:
var node =
document.createElementNS('http://www.w3.org/1999.xh
tml', 'html:div');
Namespace variations for other functions include setAttributeNS,
getElementsByTagNameNS, and hasAttributeNS.
5.2.3.6. createTextNode
In addition to setting the label attribute on an element, you can create new
text in the interface by using the DOM method createTextNode, as
shown in the following example:
var description =
document.getElementById("explain");
if (description) {

if (!description.childNodes.length) {
var textNode =
document.createTextNode("Newly text");
description.appendChild(textNode);
}
else if (description.childNodes.length == 1 )
{
description.childNodes[0].nodeValue =
"Replacement text";
}
}
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.
5.2.3.7. 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.

var existingEl = document.getElementById('my-
id');
var captionEl =
document.createElement('caption');
existingEl.appendChild(captionEl);
captionEl.setAttribute('label', 'This is a new
caption');
captionEl.setAttribute('style', 'color: blue;');
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.
5.2.3.8. 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.
// this is untested --pete
var element = document.getElementById('my-id');
var clone = element.cloneNode(false);

dump(`element='+element+'\n');
dump(`clone='+clone+'\n');
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.
5.2.3.9. 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:
document.getElementsByTagName('box')[0];
document.getElementsByTagName('box')[1];
document.getElementsByTagName('box')[2];
Or you can get the array and index into it like this:

var box = document.getElementsByTagName('box');
box[0], the first object in the returned array, is a XUL box.
To see the number of boxes on a page, you can use the length property of
an array:
var len =
document.getElementsByTagName('box').length;
dump(len+'\n');
console output: 3
To output the id of the box:
var el =
document.getElementsByTagName('box');
var tagId = el[0].id;
dump(tagId+"\n");
console output: box-one
To get to an attribute of the second box:

var el =
document.getElementsByTagName('box');
var att = el[1].getAttribute('foo');
dump(att +"\n");
console output: some attribute for the second box
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.[1]
5.2.3.10. 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 for in loop to
iterate through the list, as shown in Example 5-1.
Example 5-1. Printing element properties to the console
var el = document.getElementById('my-id');

for (var list in el)
dump("property = "+list+"\n");
console output(subset):
property = id
property = className
property = style
property = boxObject
property = tagName
property = nodeName
. . .
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.
Example 5-2. Printing the window properties
var el = document.getElementById('test-
win');
for(var list in el)
dump("property = "+list+"\n");

console output(subset):
property = nodeName
property = nodeValue
property = nodeType
property = parentNode
property = childNodes
property = firstChild
. . .
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.
5.2.3.11. 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:

var chcks =
document.getElementsByAttribute("checked", "true");
var count = chcks.length;
dump(count + " items checked \n");
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.
Example 5-3. Adding toggle functionality to xFly
function toggleCheck( ) {
// get the elements before you make any changes
var chex =
document.getElementsByAttribute("disabled",
"true");
var unchex =
document.getElementsByAttribute("disabled",
"false");
for (var i=0; i

unchex[i].setAttributte("disabled", "true");
}
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.
Example 5-4. Adding Toggle menus to xFly