## Chapter 8

# XPCOM

This chapter provides a high-level introduction to XPCOM component technology. XPCOM can be difficult to master, but after reading this chapter, you should have a good sense of what it is and the important part it plays as Mozilla's core technology. You should be able to find and use existing scriptable components in your own applications and create a simple XPCOM component by using JavaScript or C++.

XPCOM permits a reusable code module to be globally accessible to a Mozilla-based application. You do not need to worry about including external source files in your application distribution and you can distribute components by using XPInstall. This type of architecture makes the development of core application services flexible and entirely modular.

The section "Creating a JavaScript XPCOM Component" lets you create an interface from start to finish-writing the implementation for that interface, compiling it into a type library, registering it with Mozilla, and then testing the new component. One advantage of using XPCOM is that you can create multiple implementations for a single interface; following the JavaScript component section, we will take the same `nsISimple` interface and implement it in C++ as well.

The section "C++ Implementation of nsISimple" includes some techniques and programming tasks that are particular to C++ components, such as handling return values and generating header files and useful macros. The section "Other Languages for XPCOM" introduces the XPCOM bindings for the Python language (pyXPCOM). First, it provides an overview of XPCOM and how it relates to other technologies used in Mozilla.

## What Is XPCOM?

XPCOM is Mozilla's cross-platform component object model. Although it is similar to Microsoft's COM technology, this chapter points out some important differences.

Essentially, when you program in a component-based environment, you do one of three things: you create a new component using existing components, write a component that implements other components, and establish interdependencies and a service network.

### What Is a Component?

You've already seen components used in this book. In some cases, you may have used the services of Mozilla components without knowing it-for example, when you created a XUL tree widget in the section "High Performance Trees" in Chapter 3, and used its built-in layout and view capabilities. Some of this functionality is defined in an interface called nsITreeView, which provides specific methods and properties for a XUL tree, persisting its state, row, cell, and column properties, navigation, and other object metadata used in a tree object. Behind the scenes, you'll find an XPCOM-instantiated tree view object where methods and properties associated with the XUL element are accessed via DOM > JavaScript > XPConnect > XPCOM layers.

A component is a reusable or modular piece of code that implements a clearly defined interface. In Mozilla, this code can exist as a singleton service or an object instance. A singleton service is an object instance that is created only once and then used by other code (usually called "callers," "clients," or "consumers"). An object instance is an object that is instantiated once or many times. Components are written as classes that typically have member variables and methods. The basic purpose of a component is to implement a clearly defined set of APIs that exist in a public interface. The interface exists separately so that the implementation is abstracted away, and it can be changed without affecting the interface or breaking binary compatibility. When interfaces are deployed in a production environment, they are frozen, which means they are held in an immutable state-theoretically for as long as the application exists. While MSCOM provides a component-based programming model on Microsoft platforms, XPCOM provides it on all platforms where Mozilla is available.

Example 8-1 shows how simple using XPCOM components can be. In two lines, an XPConnect-wrapped `nsIBookmarksService` object is instantiated, and one of its methods is called, providing easy access to this XPCOM component from JavaScript.

Example 8-1: Using an XPCOM object in script

     // create a bookmark service object in JS
     var bmks =
       Components.classes["@mozilla.org/browser/bookmarks-service;1"].
           getService(Components.interfaces.nsIBookmarksService);
     // call one of the object's methods:
     // flush the bookmarks to disk if they've been touched.
     bmks.Flush( );

As you can see, the assignment of an XPCOM object to the variable bmks takes only a single line. Once you are comfortable using XPCOM from JavaScript, you can use any of Mozilla's scriptable interfaces in your application. Once an object like `bmks` is created, as in Example 8-1, it can be used to call any method in the nsIBookmarksService interface, of which `Flush( )` is an example.

### XPConnect and the Component Object

As shown the previous example, the XPCOM object is called and instantiated from script. For an interpreted language like JavaScript to call and instantiate it, a bridge must bind JavaScript types to XPCOM types. These type bindings are part of a technology called XPConnect.

In XPConnect, XPCOM interfaces, `classIDs`, and `progIDs` are stored as global Java-Script objects and properties that can be manipulated directly through a top-level object called `Components`. This object accesses any component that is declared "scriptable" in an XPCOM IDL interface. Through the `Components` object, you can access and use the services that these interfaces provide. The `Component` object's top-level properties and methods include:

QueryInterface
  A method used to match an interface with a desired implementation. The implementation can be in C, C++, JavaScript, Python, and other languages for which appropriate bindings are created. You can have multiple implementations for an interface. Through `QueryInterface`, you can ask for and assign the desired interface to its implementation. Each XPCOM object needs to implement `QueryInterface` in order to return an instance of that object's class:

js> var clz = Components.classes['@mozilla.org/file/local;1']; js> var inst = clz.getService( ); js> inst.QueryInterface(C.interfaces.nsILocalFile); [xpconnect wrapped nsILocalFile @ 0x81b7040]

interfaces
  A read-only object array containing all the interfaces declared scriptable in the IDL file. The object name has the same name as the interface it represents.

Components.interfaces.nsILocalFile

  The source file for this particular interface, for example, is nsILocalFile.idl. This XPIDL compiler compiles this file to produce a cross-platform binary type library, nsILocalFile.xpt, which contains tokenized IDL in an efficiently parsed form.
classes
  A read-only array of all the XPCOM component classes indexed by the `ProgID` (or human-readable name) of the component class. The `classes` object has these properties associated with it:

toString Returns the string progID. QueryInterface Used to QI this interface. name Returns the string progid name. number Returns the string components uuid number. valid Boolean verifies if the instance is valid. equals The Boolean used to match identical instances. initialize I don't know what this does. createInstance Will create an instance of the component; you can have many instances. getService Will instantiate the component as a service; you can have only one instance of a service.

classesByID
  The same as classes, except this time the array is indexed by the "canonical" or "well-established" form of their CLSID:

Components.classesByID['{dea98e50-1dd1-11b2-9344-8902b4805a2e}'];

  The `classesByID` object has the same properties object associated with it as the `class` object. The properties are also used in the same way:

toString QueryInterface name number valid equals initialize createInstance getService

stack
  A read-only property that represents a snapshot of the current JavaScript call stack. JavaScript handles each code interpretation one call at a time and then places that code onto a call stack. This property can be used for recondite diagnostic purposes:

      js> var C=Components;
      js> C.stack; JS frame :: typein :: <TOP_LEVEL> :: line 2
      js> C.stack; JS frame :: typein :: <TOP_LEVEL> :: line 3

results
  An object array of `nserror` results:

Components.results.NS_ERROR_FILE_ACCESS_DENIED; 2152857621

manager
  A reflection of the XPCOM global native component manager service. Using the component manager is the only way for a component to actually be created. It uses the components factory to create an instance of the `class` object.
ID
  A constructor used for a component written in JavaScript This component needs to register itself with the component manager by using its own `nsID` (an ID that is not already registered and thus does not appear in Components.classes).
Exception
  A JavaScript constructor used to create exception objects. When implementing XPCOM interfaces in JavaScript, these exception objects are the preferred types of exceptions. When an XPCOM exception is thrown in your JS code, it takes the form of an `Exception` object that has properties associated with this object. Exceptions are usually caught in a "catch" block.
Constructor
  A JavaScript constructor object that constructs new instances of XPCOM components:

js> var File=new Components.Constructor( "@mozilla.org/file/local;1", "nsILocalFile", "initWithPath");

  The interface nsILocalFile and the method `initWithPath` are optional. This example creates and initializes the nsILocalFile component.
isSucessCode
  A function that determines if the results code argument is successful. It takes an argument of `nsresult` and returns the Boolean values true or false:

      js> Components.isSuccessCode(Components.results.NS_OK);   true
      js> Components.isSuccessCode(Components.results.NS_ERROR_FAILURE); false

The methods and properties of the `Components` object listed above provide the only means to instantiate and access XPCOM objects from JavaScript. They are found often in the Mozilla codebase. In the sections that follow, they will be used frequently.

### XPCOM Interfaces and the IDL

All XPCOM interfaces are defined with the Interface Definition Language (IDL). IDL provides a language-neutral way to describe the public methods and properties of a component. Mozilla actually uses a modified, cross-platform version of IDL called XPIDL to compile interface source files.

The separation of interface and implementation is a key distinction of COM programming. If the application programming interface (API) is abstracted from the implementation language and then frozen, consumers of that API will receive a guaranteed, established contract with the interface that ensures it will not be changed. This is perhaps the main reason why COM was invented: to maintain compatibility on a binary level so the client code can find and use the library it needs without worrying about linking to it. To make this sort of modularity possible at runtime, IDL interfaces are compiled into binary files called type libraries, which are described later in the section "XPCOM Type Libraries."

#### Interfaces versus components

It is important to understand that most XPCOM components implement at least two interfaces. Like COM, each component needs the `QueryInterface`, `AddRef`, and Release functions to be available as an XPCOM object. These methods are derived from a basic interface called nsISupports, which is the XPCOM equivalent to Microsoft COM's `IUnknown`, shown in Table 8-1.

Table 8-1: The IUnknown interface
Name | Type | Description | Parameters / return value
--- | --- | --- | ---
AddRef | `ULONG AddRef(void)` | Increments the reference count on the COM object. | Returns: `int`, which is the new incremented reference count on the object. This value may be useful for diagnostics or testing.

QueryInterface

`HRESULT QueryInterface( /*[in] */ REFIID riid, /* [iid_is][out] */ void **ppvObject )`

Retrieves a pointer to the requested interface.

Parameters:

- `[in] iid`, which is an identifier of the requested interface.

- `ppvObject`, which is an `[out]` pointer to the interface pointer identified by iid. If the object does not support this interface, `ppvObject` is set to `NULL`.

Returns:

`HRESULT`, which is the standard `HRESULT` value.

Release | `ULONG Release(void)` | Decrements the reference count on the COM object. | Returns: `int`, which is the new decremented reference count on the object. This value may be useful for diagnostics or testing.

Tables 8-1 and 8-2 illustrate the minor differences between Microsoft's nsIUnknown and Mozilla's nsISupports root interfaces. The usage is covered in detail throughout this chapter.

Table 8-2: The nsISupports interface
Name | Type | Description | Parameters / return value
--- | --- | --- | ---
AddRef | `NS_IMETHOD_(nsrefcnt) AddRef(void)` | Increases the reference count for this interface. The associated instance will not be deleted unless the reference count is returned to zero. | Returns: The resulting reference count.

QueryInterface

`NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr)`

A runtime mechanism for interface discovery.

Parameters:

- `aIID` [in], which is a requested interface IID.
- `aInstancePtr` [out], which is a pointer to an interface pointer that receives the result.

Returns:

`NS_OK` if the interface is supported by the associated instance; `NS_NOINTERFACE` if it is not; and `NS_ERROR_INVALID_POINTER` if aInstancePtr is `NULL`.

Release | `NS_IMETHOD_(nsrefcnt) Release(void) = 0;` | Decreases the reference count for this interface. Generally, if the reference count returns to zero, the associated instance is deleted. | Returns: The resulting reference count.

#### Root interfaces

`QueryInterface`, `Addref`, and `Release` are required methods that are implemented by every component. `QueryInterace` matches a specific interface with its implementation class module. `Addref` and `Release` are methods used for reference counting. When an instance of a component is created, one or more pointers may reference that object. For each reference, a count is incremented by one. When a reference is no longer used, `Release` is called to decrement the count. You must hold a reference count to ensure that no pointers reference an object after it is deleted. When pointers try to access objects that are deleted, the application core dumps. Reference counting can get tricky, which is why smart pointers manage `Addref` and `Release` for you, as described in the later section "Useful C++ Macros and Types."

Defining `QueryInterface`, `Addref`, and `Release` every time an interface is created is clearly not very efficient. Instead, these methods are defined in the base interface called nsISupports. All interfaces inherit from this mother of all interfaces and don't need to redefine these three basic functions.

XPIDL supports the C style syntax preparser directive #include to include other IDL files-not unlike MSCOM, which uses the import statement. At the top of any IDL file that you create, you need to include nsISupports:

    #include "nsISupports.idl"
    interface nsISimple : nsISupports {
    readonly attribute string value;
    };

Core IDL Types

The core types used in IDL interface files are listed in the file xpcom/base/nsrootidl.idl. This file is included in nsISupports. (This means it is included in every interface file, since all interfaces inherit from nsISupports.) All interfaces used in a system are valid IDL types. For example, nsISimple is a valid type to use as a method parameter or a method return type. Some of the main types listed in this interface are:

     typedef boolean PRBool;
     typedef octet PRUint8;
     typedef unsigned short PRUint16;
     typedef unsigned short PRUnichar;
     typedef unsigned long PRUint32;
     typedef unsigned long long PRUint64;
     typedef unsigned long long  PRTime;
     typedef short PRInt16;
     typedef long PRInt32;
     typedef long long PRInt64;
     typedef unsigned long nsrefcnt;
     typedef unsigned long nsresult;
     typedef unsigned long size_t;

#### The XPIDL compiler

An IDL compiler is a tool that creates a binary distribution file called a type library from an interface description source file. Since support for many different platforms is a requirement for Mozilla, a modified version of the libIDL compiler from the Gnome project is used. This variant is called the XPIDL compiler and is primarily used to compile Mozilla's own dialect of IDL, conveniently called XPIDL. The XPIDL compiler generates XPCOM interface information, headers for XPCOM objects, and XPT type libraries from which objects may be accessed dynamically through XPConnect. It can also generate HTML files for documentation and Java class stubs. Another feature of the XPIDL compiler is the option to generate C++ code stubs. This feature creates nearly all the declaratory C++ code you need when you start a new project, which makes XPIDL useful as a coding wizard that helps you get started. Code generation is covered later in this chapter in the section "C++ Implementation of nsISimple."

The XPIDL compiler is located in xpcom/typelib/xpidl/ in the Mozilla sources. If you built Mozilla, you can add this directory to your `PATH`:

    $ PATH=$PATH:/usr/src/mozilla/xpcom/typelib/xpidl

Using the compiler is fairly easy. If you use the help command, you can see the usage syntax and other basic information about the compiler:

    $ ./xpidl --help
    Usage: xpidl [-m mode] [-w] [-v] [-I path] [-o basename] filename.idl
    -a emit annotations to typelib
    -w turn on warnings (recommended)
    -v verbose mode (NYI)
    -I add entry to start of include path for ``#include "nsIThing.idl"''
    -o use basename (e.g. ``/tmp/nsIThing'') for output
    -m specify output mode:
    header        Generate C++ header            (.h)
    typelib       Generate XPConnect typelib     (.xpt)
    doc           Generate HTML documentation    (.html)
    java          Generate Java interface        (.java)

### XPCOM Type Libraries

The key to the component architecture of XPCOM is the presence of binary-independent interface files that are used uniformly across platforms, languages, and programming environments. These interface files are compiled into .xpt files by the XPIDL compiler. The Mozilla components subdirectory is where type libraries and modules are typically stored. If you create a cross-platform type library for your component, you must place it in this directory for it to be accessible to XPCOM.

#### Creating a type library file from an IDL interface

To create a (.xpt) typelib file, use the flag `-m typelib` with warning (`-w`) and verbose (`-v`) modes turned on. `-o` is used for the name of the output file and `-I` is used to specify paths to other IDL files you want to include. To successfully compile your interface, you must always point to the directory where nsISupports is located.

    # include path to nsISupports.idl
    $ $XPIDL_INC = /usr/src/mozilla/xpcom/base
    #compile nsISimple.idl
    $ xpidl -m typelib -w -v -I $XPIDL_INC \
    > -o nsISimple nsISimple.idl

The file created after compilation is nsISimple.xpt. It provides the necessary type information about your interface at runtime. Typelib files enumerate the methods of interfaces and provide detailed type information for each method parameter.

### XPCOM Identifiers

To simplify the process of dynamically finding, loading, and binding interfaces, all classes and interfaces are assigned IDs. An ID is a unique 128-bit number that is based on universally unique identifiers (UUIDs) generated by various tools such as uuidgen (which we will cover later in this chapter). They are stored in the structure format defined below:

    struct nsID {
    PRUint32 m0;
    PRUint16 m1, m2;
    PRUint8 m3[8];
    };

To initialize an ID struct, declare it like this:

    ID = {0x221ffe10, 0xae3c, 0x11d1,
    {0xb6, 0x6c, 0x00, 0x80, 0x5f, 0x8a, 0x26, 0x76}};

One thing that gives XPCOM its modularity is the dynamic allocation of objects through the use of unique identifiers at runtime. This system of canonical identifiers is used for interface querying and component instantiation. Having an interface is important because it ensures that an immutable binary holds a semantic contract defined for a specific interface class.

The two types of identifiers used in XPCOM are the contract ID and the class identifier. These identifiers are shuttled to the Component Manager's `createInstance( )` or the Service Manager's `getService( )` methods in order to instantiate a component.

#### The Contract ID

The program ID (`progID`), also known as the Contract ID, is a unique human-readable string. Example 8-2 shows various `progID`s for different components. This example can be used to instantiate an XPCOM component through the use of a Contract ID.

Example 8-2: progIDs

     // progID: @mozilla.org/file/local;1
     var f = Components.classes['@mozilla.org/file/local;1'];
     // progID: @mozilla.org/browser/bookmarks-service;1
     var bmks =
       Components.classes["@mozilla.org/browser/bookmarks-service;1"].
           getService(Components.interfaces.nsIBookmarksService);

#### The class identifier

The other type of identifier is the `classID`, or CLSID. The interface and implementation are identified by this 128-bit numerical identifier string:

    // clsid: {2e23e220-60be-11d3-8c4a-000064657374}
    var f = Components.classesByID["{2e23e220-60be-11d3-8c4a-000064657374}"];

Using XPConnect, XPCOM interfaces, `classID`s, and `progID`s are stored as global JavaScript objects and properties and can be manipulated directly through the top-level `Components` object discussed earlier.

#### Generating identifiers

To obtain a UUID on Unix, you can use a command-line program called uuidgen that generates a unique number for you:

    $ uuidgen
    ce32e3ff-36f8-425f-94be-d85b26e634ee

On Windows, a program called guidgen.exe does the same thing and also provides a graphical user interface if you'd rather point and click.

Or you can use one of the special "bots" on IRC at the irc.mozilla.org server.

    irc irc.mozilla.org
    /join #mozilla
    /msg mozbot uuid

This command makes the bot generate and return a uuid, which you can then copy into your component source code. The information can then be used to uniquely identify your component.

### Component Manager

One major goal of XPCOM modularization is the removal of link-time dependencies, or dependencies that arise when you link libraries during compilation. The achievement of this goal allows you to access and use modules at runtime. The trouble then becomes finding those modules and figuring out which of their interfaces you want to use. This problem is solved through the use of the Component Manager.

The Component Manager is a special set of component management classes and implementation classes that reside in object libraries (.dll,.so,.js,.py, etc.). These classes also include factories, which let you create objects without having access to their class declarations. When you bind to objects at runtime, as you do in XPCOM, you need functionality like this to help you discover and use objects without looking at their code. The Component Manager also includes the Component Manager class itself, known as `nsComponentManager`, which is a mapping of class IDs to factories for the libraries they contain. The Component Manager is responsible for the autoregistration of all new or add-on modules located in the components directory. This autoregistration happens behind the scenes and allows you to use new components as they become available without having to register them yourself.

A component author first creates an interface file that defines all APIs that will be publicly available for a component. The component author then creates an implementation for the methods and attributes in a separate implementation class. For example, an nsILocalFile interface may have an `nsLocalFile` implementation class. Then a factory or module is needed to abstract the implementation class, and reduce compile and link-time dependencies. It then creates instances of the implementation class through its own implementation of `QueryInterface`. For example:

    // create an instance of the implementation class
    var f = Components.classes[`@mozilla.org/file/local;1'].createInstance( );

The variable f is assigned an instance of a nsLocalFile implementation class using the nsIFactory method `createInstance( )`. To match the correct interface (nsILocalFile in this case) to the implementation, you need a class instance to be created before you can call on its member method`QueryInterface( )`:

    // QI for nsILocalFile interface
    var f = f.QueryInterface(Components.interfaces.nsILocalFile);

Once you do this, the variable f is ready to use the nsILocalFile interface to access the newly created instance of the `nsLocalFile` class from script.

Simply put, a factory or module is a set of classes used by the Component Manager to register and create an instance of the component's implementation class. A factory can make its way into the Mozilla component repository in several ways. The most direct is through using the Component Manager method `RegisterFactory( )`, which supports two different registration mechanisms. The first mechanism, which takes a class ID and a pointer to a factory, can be used on factories that are actually linked into the executable. The second, which takes a class ID and the path to a dynamically loadable library, can be used both inside an executable at runtime and externally by using the `aPersist` flag to tell the repository to store the class ID/library relationship in its permanent store. The Component Manager discovers new factories or modules placed in the components directory and queries those modules for the XPCOM components they provide. The name, contract IDs, and class IDs are placed into a small component registry database for quick retrieval. The factory provides this information through a simple set of APIs required by every XPCOM module. Module creation, covered later in this chapter, describes the process through which all components contain an implementation of a module or factory.

### Getting and Using XPCOM

Mozilla is a client application that implements XPCOM, so everything you need to use or build new XPCOM components is already included in the source code and/or the binaries. Whenever you use the JavaScript `Components` object, as described earlier, you use XPCOM.

If you'd rather not build the entire Mozilla browser and you have no interest in existing Mozilla components or the large footprint that comes with an entire distribution, then standalone XPCOM is for you. To pull the XPCOM source on Unix using Mac OS X or cygwin on Windows, invoke the following commands:

    cvs -z 3 co mozilla/client.mk
    cd mozilla
    gmake -f client.mk pull_all BUILD_MODULES=xpcom

To build the XPCOM Stand Alone version, type:

    configure --enable-modules=xpcom
    gmake

When you build standalone XPCOM, the directory xpcom/sample contains the source code for a sample application and a `nsTestSample` component. The sample application built from these sources, also called `nsTestSample`, is installed in the Mozilla bin directory. libsample.so (Unix), which is the component that the sample application tries to instantiate, should have been installed in bin/components. To run the test that indicates whether standalone XPCOM is installed successfully, change to the mozilla/dist/bin directory and run the following commands:

    ./run-mozilla.sh ./nsTestSample

You should see the following output. If you do not, there is a problem with the installation of standalone XPCOM:

    Type Manifest File: /D/STAND_ALONE_XPCOM/mozilla/dist/bin/components/xpti.dat
    nsNativeComponentLoader: autoregistering begins.
    nsNativeComponentLoader: autoregistering succeeded
    nNCL: registering deferred (0)
    Inital print: initial value
    Set value to: XPCOM defies gravity
    Final print : XPCOM defies gravity
    Test passed.

Using standalone XPCOM is a powerful way to use the Mozilla framework of cross-platform COM. Even if you're just hacking on Mozilla, standalone XPCOM is a great way to learn about and use XPCOM for application development.

## Creating XPCOM Components

As we mentioned, one advantage of using XPCOM is that it separates the implementation from the interface so you can write a component in a language-agnostic manner. The services your component provides are available to all other components despite the language used to implement it. This means, for example, that you can use JavaScript not only to access the services of an XPCOM component, but also to create those services. As described in Chapter 5, using JavaScript as a modularized application programming language provides the deepest level of scripting in Mozilla.

In your Mozilla build or distribution, you will find a subdirectory named components. Inside this directory, you will see many compiled components. You will also see a number of JavaScript components. If you look at the source of these components, you can get an idea of how a JavaScript component is created. For example, look at the files nsFilePicker.js and nsSidebar.js. These JavaScript components are used in the Mozilla distribution.

JavaScript XPCOM components have the advantage over regular scripts of being fast, reusable, and globally accessible to any caller. They also have the advantage over C++-based XPCOM components of being easier to write and maintain. The next few sections describe the creation of a JavaScript-based XPCOM component. If you would rather do your work in C++, then skip to the C++ implementation section in this chapter.

### Creating a JavaScript XPCOM Component

To create a JavaScript component, you need to create an IDL interface source file and a JavaScript implementation source file. In the Mozilla sources, naming source files with an ns prefix is common practice, so the implementation file should be called something like nsSimple.js. The interface source file, or IDL file, uses a similar convention: it is typical for interfaces to begin with nsI, using an I to distinguish them as interfaces rather than implementations. Call the IDL source file nsISimple.idl.

In addition to these two source files (nsSimple.js and nsISimple.idl), you will compile a cross platform binary interface file, or type library, with the XPIDL compiler, calling it nsISimple.xpt. This .xpt file tells Mozilla that the interface is available and scriptable. You can use it on any platform that Mozilla supports. In other words, you can pick up nsISimple.xpt, which may have been compiled on Unix, drop it into Windows or Mac OS, and use it.

All .xpt interface files for Mozilla live in the components directory located in mozilla/dist/bin if you are developing with the Mozilla source code. Otherwise, for binary distributions of Mozilla, they are located in mozilla/components. Mozilla checks this directory upon start up, looking for any new components to register automatically.

#### The XPIDL interface source file

Usually, the first step in creating a new component is writing the interface. To begin, open up your favorite text editor and create a new file called nsISimple.idl.

The complete source code for the nsISimple.idl interface file is:

    #include "nsISupports.idl"
    [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)]
    interface nsISimple : nsISupports
    {
    attribute string yourName;
    void write( );
    void change(in string aValue);
    };

The `#include` line above includes the file nsISupports.idl, which defines this interface's base class. The [scriptable, uuid..] line declares the interface scriptable and assigns a UUID to the interface. You can use the UUID provided, but creating your own using one of the UUID generation tools described earlier is usually better. The third line, next to the `interface` keyword, declares the interface's name, nsISimple, and says that it derives from nsISupports.

Various attributes and methods are defined within the definition of the `nsISimple` interface. Attributes are properties of interface objects. They may be read-only or read/write variables. In nsISimple, an attribute called `yourName` is of the type `string`. In this implementation, you may get and set this attribute's value. Of the methods defined in this interface, the `write( )` method takes no arguments and the `change( )` method takes an argument of type string called `aValue`. The parameter `aValue` will be a new value that replaces the current value held by `yourName`. The complete interface IDL is:

    #include "nsISupports.idl"
    [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)]
    interface nsISimple : nsISupports
    {
    attribute string yourName;
    void write( );
    void change(in string aValue);
    };

#### JavaScript implementation file

Once you have created an interface file that publicly defines the component's methods and attributes, the next step is to implement those methods and attributes in a separate source file. The listings below walk through the implementation of `nsISimple` step by step.

First, you must declare an empty function called `SimpleComponent`, which is a standard constructor for a JavaScript object prototype. It's a good idea to name the component in a way that clearly describes both the component and the interface, as `SimpleComponent` does (i.e., `SimpleComponent` is an implementation of the `nsISimple` interface):

    function SimpleComponent( ) {}

With the function declared, we start defining the JavaScript class prototype.

    SimpleComponent.prototype = {
    mName : "a default value",

In the prototype, we first create a member variable called mName that will be the string placeholder for the IDL attribute `yourName`. The variable assigns the string a default value. Remember to place commas after all definitions in a prototype. IDL attributes are always implemented as getter functions. Methods marked with [noscript] will not be available for use with scripting languages.

Next we implement the functions below for our definition of `attribute` `string` `yourName` in our file nsISimple.idl.

    get yourName( )      { return this.mName; },
    set yourName(aName) { return this.mName = aName; },

When someone calls an IDL attribute in an interface, getters and setters are used to get or set values for the attribute:

    simple.yourName='foo';

Or similarly read values from the attribute:

    var foo = simple.yourName;

We first call on the setter function to set a value to the attribute `yourName` and then use the getter function to obtain the currently set value of `yourName`.

The first function defined in `nsISimple` is called `void write( )`. For this method, the implementation can be as simple as the following code:

    write : function ( ) { dump("Hello " + this.mName + "\n"); },

This example implements the declaration `void write( )` by dumping the current value of the variable mName to `stdout`. The code uses the `this` keyword to indicate that you are calling to the component's own member variable mName.

The `void change( )` method is then implemented as follows:

    change : function (aValue) { this.mName = aValue; },

`change( )` is a method used to change the value variable.

#### Implementing the required XPCOM methods in JavaScript

Once the definitions in the nsISimple interface are implemented, you need to implement required methods and factories that make this JavaScript implementation class an XPCOM component. Recall that all XPCOM components must implement the `nsISupports` interface.

Example 8-3 shows an implementation of `QueryInterface` specific to our new component. `QueryInterface` ensures that the correct interface (`nsISimple`) is used by matching the `iid` with the `nsISimple` interface that this component implements. If the interface doesn't match, then the argument is invalid. In this case, the exception Components.results.NS_ERROR_NO_INTERFACE is thrown, which maps to the error code number 2147500034, and code execution is stopped. If the interface identifier parameter matches the interface, then an instance of the implementation class object `SimpleComponent` with its interface is returned as a ready-to-use XPCOM component. In XPCOM, every component you implement must have a `QueryInterface` method.

Example 8-3: QueryInterface method for nsISimple interface

       QueryInterface: function (iid)
       {
        if(!iid.equals(Components.interfaces.nsISimple)
             && !iid.equals(Components.interfaces.nsISupports))
            throw Components.results.NS_ERROR_NO_INTERFACE;
        return this;
       }

The next requirement is to create a JavaScript object called `Module`. This module implements the methods needed for autoregistration and component return type objects.

    var Module = {
    firstTime  : true,

The Boolean `firstTime` is a flag used only when the component is initially registered:

    registerSelf: function (compMgr, fileSpec, location, type) {
    if (this.firstTime) {
    dump("*** first time registration of Simple JS component\n");
    this.firstTime = false;
    throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
    }

The Component Manager can do a lot in the registration process, but you have to add some logic for first time registration so the Component Manager has the information it needs. `RegisterSelf` is called at registration time (component installation) and is responsible for notifying the component manager of all components implemented in this module. The `fileSpec`, `location`, and `type` parameters can be passed on to the `registerComponent` method unmolested. Next, register the component with the Component Manager using code like the following example. The parameters include the CID, a description, a `progID`, and the other parameters you can pass without changing:

    dump(" ***** Registering: Simple JS component! ****\n");
    compMgr.registerComponentWithType(this.myCID,
    "My JS Component",
    this.myProgID, fileSpec,
    location, true, true,
    type);
    },

The `GetClassObject` method produces `Factory` and `SingletonFactory` objects. Singleton objects are specialized for services that allow only one instance of the object. Upon success, the method returns an instance of the components factory, which is the implementation class less its interface:

    getClassObject : function (compMgr, cid, iid) {
    if (!cid.equals(this.myCID))
    throw Components.results.NS_ERROR_NO_INTERFACE;
    if (!iid.equals(Components.interfaces.nsIFactory))
    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    return this.myFactory;
    },

In the previous list, the member variables myCID and myProgID are the class ID and the human-readable canonical program ID, respectively:

    myCID: Components.ID("{98aa9afd-8b08-415b-91ed-01916a130d16}"),
    myProgID: "@mozilla.org/js_simple_component;1",

The member object `myFactory` is the components factory, which through its own member function, `createInstance( )`, constructs and returns an instance of the complete component (if the `iid` parameter is specified and is the correct interface). Otherwise, if no `iid` parameter is used, the `iid` of nsISupports is used and an instance of the module is created that will then need a subsequent call to `QueryInterface` to instantiate the object as a component.

    myFactory: {
    createInstance: function (outer, iid) {
    dump("CI: " + iid + "\n");
    if (outer != null)
    throw Components.results.NS_ERROR_NO_AGGREGATION;
    return (new SimpleComponent( )).QueryInterface(iid);
    }
    },

The method `canUnload` unloads the module when shutdown occurs and is the last function in the module. The `componentManager` calls the method `NSGetModule` to initialize these required XPCOM methods and objects:

    canUnload: function(compMgr) {
    dump("****** Unloading: Simple JS component! ****** \n");
    return true;
    }
    function NSGetModule(compMgr, fileSpec) { return Module; }

The code in Example 8-4 shows the implementation for the nsISimple interface in its entirety.

Example 8-4: JavaScript implementation of nsISimple

     SimpleComponent.prototype = {
        mName: "a default value"
        get yourName()        { return this.mName; },
        set yourName(aName)   { return this.mName = aName; },
        write: function () { dump("Hello " + this.mName + "\n"); },
        change: function (aValue) { this.mName = aValue; },
        QueryInterface: function (iid) {
          if (!iid.equals(Components.interfaces.nsISimple)
              && !iid.equals(Components.interfaces.nsISupports))
            throw Components.results.NS_ERROR_NO_INTERFACE;
          return this;
        }
     }
     var Module = {
       firstTime: true,
       registerSelf: function (compMgr, fileSpec, location, type) {
       if (this.firstTime) {
         dump("*** first time registration of Simple JS component\n");
         this.firstTime = false;
         throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
       }
       dump(" ***** Registering: Simple JS component! ****\n");
       dump(" ***** Registering: Simple JS component! ****\n");
               this.myCID,
               "My JS Component",
               this.myProgID, fileSpec,
               location, true, true,
               type);
       },
       getClassObject : function (compMgr, cid, iid) {
         if (!cid.equals(this.myCID))
           throw Components.results.NS_ERROR_NO_INTERFACE
         if (!iid.equals(Components.interfaces.nsIFactory))
           throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
         return this.myFactory;
       },
       myCID: Components.ID("{98aa9afd-8b08-415b-91ed-01916a130d16}"),
       myProgID: "@mozilla.org/js_simple_component;1",
       myFactory: {
         createInstance: function (outer, iid) {
           dump("CI: " + iid + "\n");
           if (outer != null)
           if (outer != null)
           return (new SimpleComponent()).QueryInterface(iid);
         }
       },
       canUnload: function(compMgr) {
         dump("****** Unloading: Simple JS component! ****** \n");
         return true;
       }
     }; // END Module

     function NSGetModule(compMgr, fileSpec) { return Module; }

### Compiling the Component

Once you create an IDL source file and a JavaScript implementation file, you need to compile `nsISimple.idl` into a .xpt type library.

#### Compiling the type library

To compile the XPIDL interface file nsISimple.idl, you need to add the path of the XPIDL compiler to your environment. As mentioned earlier, the XPIDL compiler is located at mozilla/xpcom/typelib/xpidl. Here is the output of a Unix/cygwin/OSX session showing the compilation starting with the source file (nsISimple.idl) created earlier in the chapter. Afterwards, nsISimple.xpt and nsSimple.js are copied to the components directory:

    $ ls
    nsISimple.idl  nsSimple.js
    $ PATH=$PATH:/usr/src/mozilla/xpcom/typelib/xpidl
    $ echo $PATH
    /sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/bin:/usr/X11R6/bin:/root/bin:/usr/src/mozilla/xpcom/typelib/xpidl
    $ export XPIDL_INC=/usr/src/mozilla/xpcom/base
    $ echo $XPIDL_INC
    /usr/src/mozilla/xpcom/base
    $ xpidl -m typelib -w -v -I $XPIDL_INC \
    > -o nsISimple nsISimple.idl
    $ ls
    nsISimple.idl  nsISimple.xpt  nsSimple.js
    $ cp nsISimple.xpt nsSimple.js \
    > /usr/src/mozilla/dist/bin/components/

This output illustrates the compilation of the nsISimple.idl source file into the nsISimple.xpt typelib file. The newly compiled typelib file and the JavaScript implementation file are then copied to the Mozilla distribution components directory where component registration will occur automatically when Mozilla is launched.

#### Creating a Makefile for your component project

All previous steps were done manually. You can also create a Makefile to automate this process by using GNU make, in which case you would create a Makefile with the following variables and targets defined:

    TOP_SRC=/usr/src/mozilla
    INST_DIR=$(TOP_SRC)/dist/bin/components
    XPIDL=$(TOP_SRC)/xpcom/typelib/xpidl
    XPIDL_INC=$(TOP_SRC)/xpcom/base
    FLAGS=-m typelib -w -v -I $(XPIDL_INC) -o
    all:
    $(XPIDL)/xpidl $(FLAGS) \
    nsISimple nsISimple.idl
    install:
    cp nsISimple.xpt nsSimple.js $(INST_DIR)
    clean:
    rm -rf *.xpt
    uninstall:
    rm -f $(INST_DIR)/nsISimple.xpt
    rm -f $(INST_DIR)/nsSimple.js

Remember that you must indent after your targets with a `<tab>`.

In this file, which can be used on Unix, Windows using cygwin, or Mac OS X, the `TOP_SRC` environment variable points to the Mozilla source tree's top-level directory, the `INST_DIR` points to the directory where the component should be installed, and the XPIDL variables drive the XPIDL executable and its environment and compiler flags. The "all" Makefile target compiles and creates the type library nsISimple.xpt.

Note that in addition to the type libraries, the XPIDL compiler compiles header files, Java class files, and special HTML documentation, if necessary.

### Testing the Component

When you start up xpcshell, the Component Manager finds the new `nsISimple` component and registers it. The result of your test should look similar to Example 8-5.

Example 8-5: Scripting the "simple" component in xpcshell

     $ cd /usr/src/mozilla/dist/bin/
     $ ./run-mozilla.sh ./xpcshell
     Type Manifest File: /home/petejc/MOZILLA/mozilla/dist/bin/components/xpti.dat
     nsNativeComponentLoader: autoregistering begins.
     nsNativeComponentLoader: autoregistering succeeded
     *** first time registration of Simple JS component
     nNCL: registering deferred (0)
      ***** Registering: Simple JS component! ****
     nNCL: registering deferred (0)
     js>const Simple=new Components.Constructor("@mozilla.org/js_simple_component;1", "nsISimple");
     js> var simple=new Simple( );
     CI: {ce32e3ff-36f8-425f-94be-d85b26e634ee}
     js> for(var list in simple)
     print(list);
     QueryInterface
     yourName
     write
     change
     js> simple.yourName;
     a default value
     js> simple.yourName="Pete";
     Pete
     js> simple.write( );
     Hello Pete
     null
     js> simple.change("Brian");
     null
     js> simple.write( );
     Hello Brian
     null
     js> simple.yourName;
     Brian
     js> quit( );
     CanUnload_enumerate: skipping native
     ****** Unloading: Simple JS component! ******

Once the component is tested and registered as an XPCOM object, you can use Java-Script from a local web page or from the chrome to create an `nsISimple` object and use it as you would any ordinary JavaScript object:

    <script type="application/x-JavaScript">
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    var Simple=new Components.Constructor("@mozilla.org/js_simple_component;1", "nsISimple");
    var s = new Simple( );
    for(var list in s)
    document.write(list+"<br>\n");
    </script>

In addition to creating a component in JavaScript, you can implement XPCOM components in C++ and Python. The next sections cover the C++ implementation of the `nsISimple` interface.

### Useful C++ Macros and Types

Before you begin working on an actual implementation of a C++ component, familiarize yourself with some of the tools that make C++ programming for XPCOM a little easier. Templates, special types, and macros can ease some of the extra housekeeping that programming XPCOM requires.

More tools than we can cover in this introduction are available, but this section reviews some of the most common, including a macro that implements the `nsISupports` methods `QueryInterface`, `AddRef`, and `Release`, macros for testing `nsresults`, `smartpointers`, and special types.

#### The NS_IMPL_ISUPPORTS1_CI macro

Rather than having to implement `QueryInterface`, `AddRef`, and the `Release` methods like we did in our JavaScript component, the `NS_IMPL_ISUPPORTS` macro inserts the implementation code for you.

To use this macro for the `nsISimple` interface, type:

    NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple)

The following lines define this macro:

    #define NS_IMPL_ISUPPORTS1(_class, _interface) \
    NS_IMPL_ADDREF(_class)                         \
    NS_IMPL_RELEASE(_class)                        \
    NS_IMPL_QUERY_INTERFACE1(_class, _interface)

As you can see, the macro is made up of other macros that implement basic methods of the `nsISupports` interface. Unless you need to modify these macros, they should be left as is. This macro is used later on when we create our C++ component.

Example 8-6 shows a reference implementation of the `QueryInterface` method in C++.

Example 8-6: Reference implementation of QueryInterface

     NS_IMETHODIMP
     nsMyImplementation::QueryInterface( REFNSIID aIID, void** aInstancePtr )
     {
       NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
       if ( !aInstancePtr )
         return NS_ERROR_NULL_POINTER;
       nsISupports* foundInterface;
       if ( aIID.Equals(nsCOMTypeInfo<nsIX>::GetIID( )) )
         foundInterface = NS_STATIC_CAST(nsIX*, this);
       else if ( aIID.Equals(nsCOMTypeInfo<nsIY>::GetIID( )) )
         foundInterface = NS_STATIC_CAST(nsIY*, this);
       else if ( aIID.Equals(nsCOMTypeInfo<nsISupports>::GetIID( )) )
         foundInterface = NS_STATIC_CAST(nsISupports*, NS_STATIC_CAST(nsIX*, this));
       else
         foundInterface = 0;
       nsresult status;
       if ( !foundInterface ) {
         status = NS_NOINTERFACE;
       } else {
         NS_ADDREF(foundInterface);
         status = NS_OK;
       }
       *aInstancePtr = foundInterface;
       return status;
     }

#### The results macros

Since all XPCOM methods return result codes called nsresults, another useful macro is the `NS_SUCCEEDED` macro. This indicates whether an XPCOM accessor has returned a successful result. It is defined in nsError.h:

    #define NS_SUCCEEDED(_nsresult) (!((_nsresult) & 0x80000000))

A related macro, `NS_FAILED`, is indicates whether an XPCOM accessor returned a failure code result. It too is defined in nsError.h. The following code demonstrates the typical use of these two macros:

    nsresult rv;
    nsCOMPtr<nsILocalFile> file(do_CreateInstance("@mozilla.org/file/local;1", &rv));
    if (NS_FAILED(rv)) {
    printf("FAILED\n");
    return rv;
    }
    if (NS_SUCCEEDED(rv)) {
    printf(" SUCCEEDED \n");
    return rv;
    }

You may have noticed that the declaration of the identifier `rv` as the type `nsresult`. `nsresult` is a 32-bit unsigned integer declared in nscore.h:

    typedef PRUint32 nsresult;

We assign an nsCOMPtr or smart pointer named `file` to a newly created instance of the `nsILocalFile` component. Using the `NS_FAILED` and `NS_SUCCEEDED` macros, we test for the `nsresult` to see if our attempt to create an instance of the component failed. If it did, `rv` would be assigned an integer with a specific error return code. Return codes are defined in nsError.h. Alternatively, you can test your results for the success code:

    nsresult rv = nsComponentManager::CreateInstance("@mozilla.org/file/local;1",
    nsnull,
    NS_GET_IID(nsILocalFile),
    (void **)&refp);

If a result is successful, the value of `rv` returns `NS_OK`, which is 0.

Return codes are used in XPCOM instead of exceptions. Exceptions are not allowed because of their inconsistent implementation across different compilers. All error code numbers equate to a specific type of error. For example `NS_ERROR_FAILURE` and `NS_ERROR_NULL_POINTER` are common types of error code return values used throughout the Mozilla code base. If a value returned to `rv` was `NS_ERROR_NULL_POINTER`, the test for failure would be true and the code would return the numerical result code for `NS_ERROR_NULL_POINTER`.

#### The nsnull type

Another widely use type is `nsnull`, defined in nscore.h. Here is the definition:

    #define nsnull 0

This definition, `nsnull`, is the most common way to use `null`. The following code shows how to use `nsnull`:

    nsresult rv;
    nsCOMPtr<nsILocalFile> file =
    do_CreateInstance("@mozilla.org/file/local;1", &rv);
    if (NS_SUCCEEDED(rv)) {
    char* msg = "we successfully created an instance of file\n";
    *_retval = (char*) nsMemory::Alloc(PL_strlen(msg) + 1);
    if (!*_retval)
    return NS_ERROR_OUT_OF_MEMORY;
    PL_strcpy(*_retval, msg);
    } else {
    *_retval = nsnull;
    }

#### The NS_IMETHODIMP macro

If you look in the Mozilla C++ source code, you will see the macro `NS_IMETHODIMP` used frequently. This macro identifies the type of your interface implementation method. It is also defined in nscore.h, as shown in Example 8-7.

Example 8-7: Platform macros in xpcom/base/nscore.h

     #define NS_IMETHODIMP NS_IMETHODIMP_(nsresult)
     #ifdef NS_WIN32
       #define NS_IMETHODIMP_(type) type _ _stdcall
     #elif defined(XP_MAC)
       #define NS_IMETHODIMP_(type) type
     #elif defined(XP_OS2)
       #define NS_IMETHODIMP_(type) type
     #else
       #define NS_IMETHODIMP_(type) type
     #endif

Example 8-8 shows a typical use of the `NS_IMETHODIMP` macro. All methods that implement an interface are of the type `NS_IMETHODIMP`.

Example 8-8: NS_IMETHOD macro

     NS_IMETHODIMP
     nsMyImpl::GetSomeString(char** _retval)
     {
       nsresult rv;
       nsCOMPtr<nsILocalFile> file =
       do_CreateInstance("@mozilla.org/file/local;1", &rv);
       if (NS_SUCCEEDED(rv)) {
         char* msg = "we successfully created an instance of file\n";
         *_retval = (char*) nsMemory::Alloc(PL_strlen(msg) + 1);
         if (!*_retval)
           return NS_ERROR_OUT_OF_MEMORY;
         PL_strcpy(*_retval, msg);
       } else {
         *_retval = nsnull;
       }
       return NS_OK;
     }

The macro in Example 8-8 declares the method `GetSomeString` as an XPCOM implementation.

#### nsCOMPtr smart pointer

As described earlier, XPCOM provides a C++ tool called a smart pointer to manage reference counting. A smart pointer is a template class that acts syntactically, just like an ordinary pointer in C or C++. You can apply `*` to dereference the pointer, `->`, or access what the pointer refers to. Unlike a raw COM interface pointer, however, `nsCOMPtr` manages `AddRef`,`Release`, and `QueryInterface` for you, thereby preventing memory leaks.

Here is how to create a raw pointer:

    nsILocalFile *refp(nsnull);
    nsresult rv = nsComponentManager::CreateInstance("@mozilla.org/file/local;1",
    nsnull,
    NS_GET_IID(nsILocalFile),
    (void **)&refp);
    if (refp)
    printf("%p\n", (void*)refp);

After you create a new object that `refp` points to, `refp` is considered an owning reference, and any other pointers that point to it must be "refcounted." Example 8-9 uses `anotherPtr` andoneMorePtr to point to `refp`, and manually manages `AddRef` and `Release`.

Example 8-9: Manual reference counting using raw pointers

     nsILocalFile *refp(nsnull);
     nsresult rv = nsComponentManager::CreateInstance("@mozilla.org/file/local;1",
        nsnull,
        NS_GET_IID(nsILocalFile),
        (void **)&refp);
     nsILocalFile *anotherPtr = refp;
     NS_IF_ADDREF(anotherPtr); // increment refcount
     nsILocalFile *oneMorePtr = refp;
     NS_IF_ADDREF(oneMorePtr); // increment refcount
     if (!someCondition) {
       NS_RELEASE(anotherPtr); // decrement refcount
       return NS_OK;
     }
     . . .
     NS_RELEASE(anotherPtr); // decrement refcount
     NS_RELEASE(oneMorePtr); // decrement refcount
     return NS_OK;
     }

In Example 8-9, if `someCondition` is false, `anotherPtr` is released and the function then returns (`NS_OK`). But what about `oneMorePtr`? In this instance, it is never released; if you remember, an object cannot be released from memory until our `refcount` is at zero. The `refcount` is out of sync, `oneMorePtr` is never decremented before the return, and the object is thus left dangling in memory. With the `refcount` off, the object leaks. Remember that `Release( )` calls the C++ `delete` operator to free up the allocated XPCOM object only when the count is decremented to 0. If `Release` thinks there are still references to the object because the `refcount` hasn't been properly decremented, `delete` is never called. The correct code is shown below:

    if (!someCondition) {
    NS_RELEASE(anotherPtr); // decrement refcount
    NS_RELEASE(oneMorePtr); // decrement refcount
    return NS_OK;
    }

As you can see, manual management of reference counting is prone to error. To alleviate this burden and extra code bloat, `nsCOMPtr` implements `AddRef` and `Release` for you and makes life much easier. Before the `nsCOMPtr` class is removed from the stack, it calls `Release` in its destructor. After all references are properly released, `delete` is called and the object is freed from memory. Example 8-10 shows a typical use of `nsCOMPtr`.

Example 8-10 Using nsCOMPtr in your code

     nsCOMPtr<nsILocalFile> refp = do_CreateInstance("@mozilla.org/file/local;1");
     nsCOMPtr<nsILocalFile> anotherPtr = refp;
     nsCOMPtr<nsILocalFile> oneMorePtr = refp;
     nsresult rv;
     if (!someCondition)
       return NS_OK;
     . . .
     //no need to release here because nsCOMPtr smart pointer's destructor
     // will call release automatically and the above references will be
     // properly decremented.
     return NS_OK;

Wherever the code returns, all pointers holding references to the `nsLocalFile` XPCOM object are released automatically in the `nsCOMPtr` class destructor before the instructions are removed from the stack. By letting `nsCOMPtr` manage `AddRef` and `Release` for you, you remove a margin for error, code complexity, and bloat.

### C++ Implementation of nsISimple

Now that you have seen some of the C++ tools you need for XPCOM, you can turn to an actual implementation.

Earlier in this chapter, the section "Creating a JavaScript XPCOM Component" showed you how to create an interface and implement it in JavaScript. However, you may need a C++ implementation to benefit from the better performance offered by a compiled language.

Most components used in Mozilla are written in C++. This section discusses how to create a C++ implementation for the `nsISimple` interface. A few more steps are involved, but as you will see, they are generally similar to the processes described in the JavaScript component section, facilitated to some extent by the available tools and templates discussed previously.

#### Creating a C++ component

First, you must find a good place to put the source file you create for the component. In your local Mozilla source tree, mozilla/xpcom/sample/ is a great place to start because it's the directory in which the sample XPCOM interface and implementations already reside.

First, create a new directory and call it simple:

    $ mkdir simple
    $ cd simple

You can place the `nsISimple` interface you created earlier in this new directory as a file called nsISimple.idl:

    #include "nsISupports.idl"
    [scriptable, uuid(ce32e3ff-36f8-425f-94be-d85b26e634ee)]
    interface nsISimple : nsISupports
    {
    attribute string yourName;
    void write( );
    void change(in string aName);
    };

Once you have the interface source file in which the attribute `yourName` and the methods `write( )` and `change( )` are defined, you can create a header file for the implementation source file.

#### nsISimple C++ header file

Earlier, you created the type library nsISimple.xpt for the JavaScript component and installed it in the components subdirectory. Since we've already covered those steps, we can move forward to generating a C++ header file. To create a C++ header file from your original IDL, run your IDL file through the xpidl compiler:

    $ xpidl -m header -w -v -I $XPIDL_INC \
    > -o nsISimple nsISimple.idl

The generated file is nsISimple.h and is shown in Example 8-11.

Example 8-11 nsISimple header file generated by xpidl compiler

     /*
      * DO NOT EDIT.  THIS FILE IS GENERATED FROM nsISimple.idl
      */
     #ifndef _ _gen_nsISimple_h_ _
     #define _ _gen_nsISimple_h_ _
     #ifndef _ _gen_nsISupports_h_ _
     #include "nsISupports.h"
     #endif
     /* For IDL files that don't want to include root IDL files. */
     #ifndef NS_NO_VTABLE
     #define NS_NO_VTABLE
     #endif
     /* starting interface:    nsISimple */
     #define NS_ISIMPLE_IID_STR "ce32e3ff-36f8-425f-94be-d85b26e634ee"
     #define NS_ISIMPLE_IID \
       {0xce32e3ff, 0x36f8, 0x425f, \
         { 0x94, 0xbe, 0xd8, 0x5b, 0x26, 0xe6, 0x34, 0xee }}
     class NS_NO_VTABLE nsISimple : public nsISupports {
      public:
      NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISIMPLE_IID)
      /* attribute string yourName; */
      NS_IMETHOD GetYourName(char * *aYourName) = 0;
      NS_IMETHOD SetYourName(const char * aYourName) = 0;
      /* void write ( ); */
      NS_IMETHOD Write(void) = 0;
      /* void change (in string aName); */
      NS_IMETHOD Change(const char *aName) = 0;
     };
      /* Use this macro when declaring classes that implement this interface. */
     #define NS_DECL_NSISIMPLE \
      NS_IMETHOD GetYourName(char * *aYourName); \
      NS_IMETHOD SetYourName(const char * aYourName); \
      NS_IMETHOD Write(void); \
      NS_IMETHOD Change(const char *aName);
     /* Use this macro to declare functions that forward the behavior of this interface to another object. */
     #define NS_FORWARD_NSISIMPLE(_to) \
      NS_IMETHOD GetYourName(char * *aYourName) { return _to ## GetYourName(aYourName); } \
      NS_IMETHOD SetYourName(const char * aYourName) { return _to ## SetYourName(aYourName); } \
      NS_IMETHOD Write(void) { return _to ## Write( ); } \
      NS_IMETHOD Change(const char *aName) { return _to ## Change(aName); }
     /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
     #define NS_FORWARD_SAFE_NSISIMPLE(_to) \
      NS_IMETHOD GetYourName(char * *aYourName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##->GetYourName(aYourName); } \
      NS_IMETHOD SetYourName(const char * aYourName){return !_to ## ? NS_ERROR_NULL_POINTER : _to ##->SetYourName(aYourName);} \
      NS_IMETHOD Write(void) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-> Write( ); } \
      NS_IMETHOD Change(const char *aName) { return !_to ## ? NS_ERROR_NULL_POINTER : _to ##-> Change(aName); }
     #if 0
     /* Use the code below as a template for the implementation class for this interface. */
     /* Header file */
     class nsSimple : public nsISimple
     {
     public:
       NS_DECL_ISUPPORTS
       NS_DECL_NSISIMPLE
       nsSimple( );
       virtual ~nsSimple( );
       /* additional members */
     };
     /* Implementation file */
     NS_IMPL_ISUPPORTS1(nsSimple, nsISimple)
     nsSimple::nsSimple( )
     {
       NS_INIT_ISUPPORTS( );
       /* member initializers and constructor code */
     }
     nsSimple::~nsSimple( )
     {
       /* destructor code */
     }
     /* attribute string yourName; */
     NS_IMETHODIMP nsSimple::GetYourName(char * *aYourName)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     NS_IMETHODIMP nsSimple::SetYourName(const char * aYourName)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     /* void write ( ); */
     NS_IMETHODIMP nsSimple::Write( )
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     /* void change (in string aName); */
     NS_IMETHODIMP nsSimple::Change(const char *aName)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     /* End of implementation class template. */
     #endif
     #endif /* _ _gen_nsISimple_h_ _ */

As you can see, the xpidl compiler can do a lot of work for you. The code generated in Example 8-11 is a C++ header file that declares the methods of `nsISimple`. It provides the class definition, macros for using the interface, and a template for the class implementation, which contains stubbed-out declaratory code that you can paste into your implementation file to quickly get started.

#### Creating the implementation file

The implementation file actually contains the C++ code that implements the member functions and properties declared in your interface. For `nsISimple`, these members are the `yourName` attribute and the `write( )` and `change( )` methods.

First you need to generate a new UUID for the new implementation class you'll write. Every XPCOM implementation class must have its own UUID:

    $ uuidgen
    79e9424f-2c4d-4cae-a762-31b334079252

As part of the generated file nsISimple.h, all the code stubs you need to get started are ready to be copied and pasted into the C++ source files. You can use those stubs as a guide to implement the component. In a text editor, create a new file called nsSimple.h and enter the code shown in Example 8-12.

To maintain clarity, the C++ implementation class is named `nsSimpleImpl`, where the default class name generated by the xpidl compiler is `nsSimple` and the header file, nsSimple.h, is shown in Example 8-12.

Example 8-12 The component header file nsSimple.h

     #include "nsISimple.h"
     // 79e9424f-2c4d-4cae-a762-31b334079252
     #define NS_SIMPLE_CID \
     { 0x79e9424f, 0x2c4d, 0x4cae, { 0xa7, 0x62, 0x31, 0xb3, 0x34, 0x07, 0x92, 0x52 } }
     #define NS_SIMPLE_CONTRACTID "@mozilla.org/cpp_simple;1"
     class nsSimpleImpl : public nsISimple
     {
     public:
         nsSimpleImpl( );
         virtual ~nsSimpleImpl( );
         // nsISupports interface
         NS_DECL_ISUPPORTS
         NS_DECL_NSISIMPLE
     private:
         char* mName;
     };

Example 8-12 includes the ID-generated header file nsISimple.h, which holds the C++ declarations for the interface class `nsISimple`. It then takes the new UUID and breaks it into a class ID struct defined as `NS_SIMPLE_CID`. Next, it defines the contract ID for this implementation class.

The example uses a completely different class ID and contract ID than the one used for the JavaScript component because it's a different implementation class and needs to have it's own unique identification (even though it implements the same interface).

Now the example makes the class declaration of the implementation, called `nsSimpleImpl`, which inherits from `nsISimple`, defining the class constructor and virtual destructor. `NS_DECL_ISUPPORTS` is a macro that holds the declaration of our required `QueryInterface`,`AddRef`, and `Release` methods. `NS_DECL_NSISIMPLE` is created in the generated header file nsISimple.h. It expands to the used interface method declarations. Finally Example 8-12 shows the addition of the `char*` member variable identified as mName. This variable is used to hold the value of the interface attribute `yourName`, just as it did earlier in the JavaScript class implementation.

Once you have the header file, you are ready to start the implementation source file. With a text editor, create a new file called nsSimple.cpp. As in any C++ source file, you should add the header files required by the implementation:

    #include "plstr.h"
    #include "stdio.h"
    #include "nsCOMPtr.h"
    #include "nsMemory.h"
    #include "nsSimple.h"

Start by adding the implementation of our class constructor and destructor:

    // c++ constructor
    nsSimpleImpl::nsSimpleImpl( ) : mName(nsnull)
    {
    NS_INIT_REFCNT( );
    mName = PL_strdup("default value");
    }
    // c++ destructor
    nsSimpleImpl::~nsSimpleImpl( )
    {
    if (mName)
    PL_strfree(mName);
    }

Then add the macro `NS_IMPL_ISUPPORTS1_CI`. As discussed earlier, this macro conveniently implements `QueryInterface`, `AddRef`, and `Release`:

    NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple);

Next you are ready to implement the actual `nsISimple` interface methods:

    NS_IMETHODIMP
    nsSimpleImpl::GetYourName(char** aName)
    {
    NS_PRECONDITION(aName != nsnull, "null ptr");
    if (!aName)
    return NS_ERROR_NULL_POINTER;
    if (mName) {
    *aName = (char*) nsMemory::Alloc(PL_strlen(mName) + 1);
    if (! *aName)
    return NS_ERROR_NULL_POINTER;
    PL_strcpy(*aName, mName);
    }
    else {
    *aName = nsnull;
    }
    return NS_OK;
    }

A C++ implementation of an IDL method is declared as the type `NS_IMETHODIMP`. The implementation starts with the getter method `GetYourName`, which takes a `char**` parameter for the method's return value. Return values in C++ XPCOM components are marshaled via method arguments because interface implementations must always return a numerical `nsresult`, as described earlier. To ensure that the `aName` parameter is a pointer, use the macro `NS_PRECONDITION` to warn if null, follow with a null test in the line below, and return the error result code `NS_ERROR_NULL_POINTER`. Then test whether the member variable mName holds a value. If it does, allocate the necessary memory to accommodate the size of the copy. Then by using `PL_strcpy`, you can assign the value to the parameter `aName`. Otherwise, mName is null and you can assign null into `aName` and return:

    NS_IMETHODIMP
    nsSimpleImpl::SetYourName(const char* aName)
    {
    NS_PRECONDITION(aName != nsnull, "null ptr");
    if (!aName)
    return NS_ERROR_NULL_POINTER;
    if (mName) {
    PL_strfree(mName);
    }
    mName = PL_strdup(aName);
    return NS_OK;
    }

After implementing the getter, implement the setter. Again, use `NS_PRECONDITION` and then a null test on the `aName`. If that parameter holds data, you can free it by using `PL_strfree` and calling `PL_strdup`. Then assign the new value to class member `mName`:

    NS_IMETHODIMP
    nsSimpleImpl::Write( )
    {
    printf("%s\n", mName);
    return NS_OK;
    }
    NS_IMETHODIMP
    nsSimpleImpl::Change(const char* aName)
    {
    return SetYourName(aName);
    }

Finally, implement the `Write` and `Change` methods by using `printf` to write the value of `mName` to `stdout` and set a new value to `mName`. Example 8-13 shows the C++ source code in its entirety.

Example 8-13 nsSimple.cpp

     #include "plstr.h"
     #include "stdio.h"
     #include "nsSimple.h"
     #include "nsCOMPtr.h"
     #include "nsMemory.h"
     // c++ constructor
     nsSimpleImpl::nsSimpleImpl( ) : mName(nsnull)
     {
         NS_INIT_REFCNT( );
          = PL_strdup("default value");
     }
     // c++ destructor
     nsSimpleImpl::~nsSimpleImpl( )
     {
         if ( )
             PL_strfree( );
     }
     // This macro implements the nsISupports interface methods
     // QueryInterface, AddRef and Release
     NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple);
     NS_IMETHODIMP
     nsSimpleImpl::GetYourName(char** aName)
     {
         NS_PRECONDITION(aName != nsnull, "null ptr");
         if (!aName)
             return NS_ERROR_NULL_POINTER;
         if ( ) {
             *aName = (char*) nsMemory::Alloc(PL_strlen( ) + 1);
             if (! *aName)
                 return NS_ERROR_NULL_POINTER;
             PL_strcpy(*aName, );
         }
         else {
             *aName = nsnull;
         }
         return NS_OK;
     }
     NS_IMETHODIMP
     nsSimpleImpl::SetYourName(const char* aName)
     {
        NS_PRECONDITION(aName != nsnull, "null ptr");
         if (!aName)
             return NS_ERROR_NULL_POINTER;
         if ( ) {
             PL_strfree( );
         }
          = PL_strdup(aName);
         return NS_OK;
     }
     NS_IMETHODIMP
     nsSimpleImpl::Write( )
     {
       printf("%s\n", );
       return NS_OK;
     }
     NS_IMETHODIMP
     nsSimpleImpl::Change(const char* aName)
     {
       return SetYourName(aName);
     }

### The nsSimple module code

As you needed to do with the JavaScript implementation, you must create the code for the module. The module code abstracts the implementation class and makes the implementation a component library. In your text editor, create a file called nsSimpleModule.cpp and enter the code shown in Example 8-14.

Example 8-14 nsSimpleModule.cpp

     #include "nsIGenericFactory.h"
     #include "nsSimple.h"
     NS_GENERIC_FACTORY_CONSTRUCTOR(nsSimpleImpl)
     static NS_METHOD nsSimpleRegistrationProc(nsIComponentManager *aCompMgr,
                                               nsIFile *aPath,
                                               const char *registryLocation,
                                               const char *componentType,
                                               const nsModuleComponentInfo *info)
     {
         return NS_OK;
     }
     static NS_METHOD nsSimpleUnregistrationProc(nsIComponentManager *aCompMgr,
                                                 nsIFile *aPath,
                                                 const char *registryLocation,
                                                 const nsModuleComponentInfo *info)
     {
         return NS_OK;
     }
     // For each class that wishes to support nsIClassInfo, add a line like this
     NS_DECL_CLASSINFO(nsSimpleImpl)
     static nsModuleComponentInfo components[ ] =
     {
       { "A Simple Component",    // a message to display when component is loaded
         NS_SIMPLE_CID,           // our UUID
         NS_SIMPLE_CONTRACTID,    // our human readable PROGID or CLSID
         nsSimpleImplConstructor,
         nsSimpleRegistrationProc      /* NULL if you dont need one */,
         nsSimpleUnregistrationProc    /* NULL if you dont need one */,
         NULL /* no factory destructor */,
         NS_CI_INTERFACE_GETTER_NAME(nsSimpleImpl),
         NULL /* no language helper */,
         &NS_CLASSINFO_NAME(nsSimpleImpl)
       }
     };
     NS_IMPL_NSGETMODULE(nsSimpleModule, components)

#### The final steps for a C++ component

Once you have an interface file nsISimple.idl, a C++ source file nsSimple.cpp with its header file nsSimple.h, and a module file nsSimpleModule.cpp, you can create a Makefile like the one shown in Example 8-15. This Makefile can compile the sources into an XPCOM component.

A Makefile directs the Mozilla build system to build the sources and install them into the Mozilla dist/bin/components directory. To use the Makefile, run gmake to compile and install the component library file.

Example 8-15 Sample Makefile

     topsrcdir       = ../../..
     srcdir          = .
     VPATH           = .
     include $(DEPTH)/config/autoconf.mk
     MODULE          = xpcom
     XPIDL_MODULE    = simple
     LIBRARY_NAME    = simple
     IS_COMPONENT    = 1
     MODULE_NAME     = nsSimpleModule
     REQUIRES        = string \
                       xpcom  \
                       $(NULL)
     CPPSRCS         =
                     nsSimple.cpp \
                     nsSimpleModule.cpp
                     $(NULL)
     XPIDLSRCS       = nsISimple.idl
     include $(topsrcdir)/config/config.mk
     LIBS            +=            \
                     $(XPCOM_LIBS) \
                     $(NSPR_LIBS)  \
                     $(NULL)
     include $(topsrcdir)/config/rules.mk
     EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
     install:: $(TARGETS

To test the newly compiled component, you can use xpcshell like you did for the JavaScript component. Example 8-16 shows a session with xpcshell that tests the new component.

Example 8-16 Sample use of component in xpcshell

     $ ./run-mozilla.sh ./xpcshell
     Type Manifest File: /usr/src/commit_mozilla/mozilla/dist/bin/components/xpti.dat
     nsNativeComponentLoader: autoregistering begins.
     *** Registering nsSimpleModule components (all right -- a generic module!)
     nsNativeComponentLoader: autoregistering succeeded
     nNCL: registering deferred (0)
     js> var Simple = new Components.Constructor("@mozilla.org/cpp_simple;1", "nsISimple");
     js> var s = new Simple( );
     js> s.yourName;
     default value
     js> s.write( );
     default value
     js> s.change('pete');
     js> s.yourName;
     pete
     js> s.yourName = 'brian';
     brian
     js>

#### Creating an instance of an existing Mozilla component

Creating an instance of a component and accessing methods and attributes is different in C++ than it is in JavaScript. Using the nsILocalFile interface lets you walk through the code to create an instance of this component from C++:

    nsCOMPtr<nsILocalFile>
    file(do_CreateInstance("@mozilla.org/file/local;1"));

You can also instantiate the object as follows:

    nsresult rv;
    nsCOMPtr<nsILocalFile> file =
    do_CreateInstance("@mozilla.org/file/local;1", &rv);
    if (NS_FAILED(rv))
    return rv;

Both techniques assign an `nsCOMPtr` to a newly allocated instance of an `nsLocalFile` object.

Example 8-17 accesses the public methods available from this component by using the pointer identifier `file`. Example 8-17 Example 8-17: Testing for nsresults from component methods

     if (file) {
       nsresult rv;
       rv = file->InitWithPath(NS_LITERAL_STRING("/tmp"));
       if (NS_FAILED(rv))
         return rv;
       PRBool exists;
       rv = file->Exists(&exists);
       if (NS_FAILED(rv))
         return rv;
       if (exists)
         print("yep it exists!\n");
       nsAutoString leafName;
       rv = file->GetLeafName(leafName);
       if (NS_FAILED(rv))
         return rv;
       if (!leafName.IsEmpty( ))
         printf("leaf name is %s\n", NS_ConvertUCS2toUTF8(leafName).get( ));
     }

Always test accessors of all XPCOM public methods, getters, and setters. Failures can appear at any time, so be sure to use result checking in your implementations.

### Other Languages for XPCOM

Although most components available from XPCOM are written in C++, the XPConnect/XPCOM pairing can also accommodate other languages. Language independence is a goal of the XPCOM architecture. Currently, implementations for Python (PyXPCOM) and Ruby (rbXPCOM) exist, with other language bindings being developed. In this respect, the Mozilla framework dovetails with one of the main trends in application development, which is to mix different languages in the development environment.

#### PyXPCOM: the Python binding for XPCOM

Python has emerged as a very popular programming language in the last couple of years. It even does some of the application work and other heavy lifting that were the province of C++. Mozilla now offers a Python "binding" similar to the XPConnect binding for JavaScript that allows you to write application code in Python, compile it in XPCOM, and make it available like you would any C++ component in the Mozilla application framework. As with other XPCOM programming languages, you must create an implementation file (in Python) and an interface file (in IDL), as shown in Examples 8-18 and 8-19, respectively.

The terms and constructs for Python components are similar to those of C++. In the implementation, you need to import components from the XPCOM module to access the standard public members. The syntax is the same as that for importing any regular Python library:

    from xpcom import components

The IDL for a Python implementation of an XPCOM component can be identical to one for a JavaScript- or C++-based component (which is the point of XPCOM, after all). As in any component, your IDL needs to include nsISupports.idl and declare itself as scriptable with a unique UUID:

    [scriptable, uuid(6D9F47DE-ADC1-4a8e-8E7D-2F7B037239BF)]

JavaScript accesses the component in the same way, using classes and interface members of the component's interfaces to set up an instance of the component:

    Components.classes["@foo.com/appSysUtils;1"].
    getService(Components.interfaces.appISysUtils);

With these foundations, and assuming that you have to have a Python distribution on your system that Mozilla can access, you are ready to go! Example 8-18 shows a complete implementation of a PyXPCOM component. This file needs to be saved with a .py extension and put in the components directory and registered like any other component. Example 8-18 Sample Python component implementation

     import sys, os
     from xpcom import components, nsError, ServerException
     class appSysUtils:
         _com_interfaces_ = [components.interfaces.appISysUtils]
         _reg_clsid_ = "{56F686E0-A989-4714-A5D6-D77BC850C5C0}"
         _reg_contractid_ = "@foo.com/appSysUtils;1"
         _reg_desc_ = "System Utilities Service"
         def _ _init_ _(self):
             self.F_OK = os.F_OK
             self.R_OK = os.R_OK
             self.W_OK = os.W_OK
             self.X_OK = os.X_OK
         # ...
         def Access(self, filename, mode):
             return os.access(filename, mode)

The special attributes defined in the `appSysUtils` class correspond to the special identifiers you must use in XPCOM to make your code a reusable component (see "XPCOM Identifiers," earlier in this chapter). Table 8-3 describes these attributes.

Table 8-3: Special XPCOM attributes in Python nsISupports.
Attribute | Description
--- | ---
_com_interfaces_ | The interface IDs supported by this component. This attribute is required. It can be a single IID or a list, but you do not have to list base interfaces such as
_reg_contractid_ | The component's contract ID. Required.
_reg_clsid_ | The Class ID (CLSID) or progID of the component in the form: @domain/component;version.Required.
_reg_desc_ | A description of the component. Optional.

Example 8-19 is the IDL file you also need to create a Python component.

Example 8-19 IDL for the Python component

     #include "nsISupports.idl"
     // some useful system utilities
     [scriptable, uuid(6D9F47DE-ADC1-4a8e-8E7D-2F7B037239BF)]
     interface appSysUtils : nsISupports {
         boolean IsFile(in string filename);
         boolean IsDir(in string dirname);
         void Stat(in string filename,
                   out PRUint32 st_mode,
                   out PRUint32 st_ino,
                   out PRUint32 st_dev,
                   out PRUint32 st_nlink,
                   out PRUint32 st_uid,
                   out PRUint32 st_gid,
                   out PRUint32 st_size,
                   out PRUint32 st_atime,
                   out PRUint32 st_mtime,
                   out PRUint32 st_ctime);
         boolean Access(in string filename, in PRUint32 mode);
         readonly attribute PRUint32 F_OK;
         readonly attribute PRUint32 R_OK;
         readonly attribute PRUint32 W_OK;
         readonly attribute PRUint32 X_OK;
     };

Finally, Example 8-20 shows how this component might be used in script-for example, in a function you define for an event handler in the XUL interface.

Example 8-20 Using the Python component in script

     var appSysUtils = Components.classes["@foo.com/appSysUtils;1"].getService(Components
            interfaces.appISysUtils);
     // Read-write status
     var write = appSysUtils.Access(url, appSysUtils.W_OK);
     var read = appSysUtils.Access(url, appSysUtils.R_OK);
     var rwcheck = document.getElementById('rwCheckbox');
     if (read)  {
         if (write && read)
             ro = false;
         else
             ro = true;
         rwcheck.setAttribute('checked', ro);
     }

The component is a small system utility that checks the read/write permissions status of a file on the local filesystem. The JavaScript uses it to display a visual notifier of the status in the UI. In this case, the DOM's `rwcheck` node refers to a checkbox. It's easy to imagine this component being extended to do other things, such as getting information about a file (the `Stat` stub is in the IDL). The source code, samples, and documentation for PyXPCOM are located in the Mozilla tree at mozilla/extensions/python.

### XPCOM as an Open Cross-Platform Solution

XPCOM can be an entire book in itself. This chapter has merely touched upon the role it plays in Mozilla application development. Understanding the basics of this framework is vital to understanding the very foundation of Mozilla's componentized architecture.

Although other component-based systems exist on various platforms-MSCOM for Microsoft or a CORBA system for GNOME, for example-if you want to write truly cross-platform component-based applications, then XPCOM is the best tool for the job. It can be deployed on any platform Mozilla is ported to, and can be scripted by using JavaScript or Python.

Above all, XPCOM is entirely open source, so there are no costs associated with it, no proprietary secrets in how it's put together, and you have various software licenses to choose from. Although XPCOM has become a solid framework, its developers are still making improvements and uncovering and fixing bugs. However, XPCOM offers tremendous flexibility as a software development framework and the Mozilla community is an excellent technical support resource for all technologies covered in this book.