xpcom:create js xpcom


8.2. Creating XPCOM Components

As we mentioned, oneadvantageof using XPCOM is that it separates the implementation from theinterface so you can write a component in a language-agnostic manner.The services your component provides are available to all othercomponents despite the language used to implement it. This means, forexample, that you can use JavaScript not only to access the servicesof an XPCOM component, but also to create those services. Asdescribed in Chapter 5, using JavaScript as amodularized application programming language provides the deepestlevel of scripting in Mozilla.

In your Mozilla buildordistribution, you will find a subdirectory namedcomponents. Inside this directory, you will seemany compiled components. You will also see a number of JavaScriptcomponents. If you look at the source of these components, you canget an idea of howaJavaScript component is created. For example, look at the filesnsFilePicker.js andnsSidebar.js. These JavaScript components areused in the Mozilla distribution.

JavaScript XPCOM components have the advantage over regular scriptsof being fast, reusable, and globally accessible to any caller. Theyalso have the advantage over C++-based XPCOM components of beingeasier to write and maintain. The next few sections describe thecreation of a JavaScript-based XPCOM component. If you would ratherdo your work in C++, then skip to the C++ implementation section inthis chapter.

8.2.1. Creating a JavaScript XPCOM Component

To create a JavaScript component, youneed to create an IDL interface source file and a JavaScriptimplementation source file. In the Mozilla sources, naming sourcefiles with an ns prefix is common practice, sothe implementation file should be called something likensSimple.js. The interface source file, or IDLfile, uses a similar convention: it is typical for interfaces tobegin with nsI, using an Ito distinguish them as interfaces rather than implementations. Callthe IDL source file nsISimple.idl.

In addition to these two source files(nsSimple.js andnsISimple.idl), you will compile a crossplatform binary interface file, or type library, with the XPIDLcompiler, calling it nsISimple.xpt. This.xpt file tells Mozilla that the interface isavailable and scriptable. You can use it on any platform that Mozillasupports. In other words, you can pick upnsISimple.xpt, which may have been compiled onUnix, drop it into Windows or Mac OS, and use it.

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

8.2.1.1. The XPIDL interface source file

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

The complete source code for the nsISimple.idlinterface 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 filensISupports.idl, which defines thisinterface's base class. The [scriptable,uuid..] line declares the interface scriptable and assignsa UUID to the interface. You can use the UUIDprovided, but creating your own using one of the UUID generationtools described earlier is usually better. The third line, next tothe interface keyword, declares theinterface's name, nsISimple,and says that it derives from nsISupports.

Various attributes and methods are defined within the definition ofthe nsISimple interface. Attributes are propertiesof interface objects. They may be read-only or read/write variables.In nsISimple, an attribute calledyourName is of the type string.In this implementation, you may get and set thisattribute's value. Of the methods defined in thisinterface, the write( ) method takes no argumentsand the change( ) method takes an argument of typestring called aValue. The parameteraValue will be a new value that replaces thecurrent value held by yourName. The completeinterface 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);
};

8.2.1.2. JavaScript implementation file

Once you have created an interface file thatpublicly defines the component's methods andattributes, the next step is to implement those methods andattributes in a separate source file. The listings below walk throughthe implementation of nsISimple step by step.

First, you must declare an empty functioncalledSimpleComponent, which is a standard constructorfor a JavaScript object prototype. It's a good ideato name the component in a way that clearly describes both thecomponent and the interface, as SimpleComponentdoes (i.e., SimpleComponent is an implementationof the nsISimple interface):

function SimpleComponent( ) {}

With the function declared, we start defining the JavaScript classprototype.

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

In the prototype, we first create a member variable calledmName that will be the string placeholder forthe IDL attribute yourName. The variable assignsthe string a default value. Remember to place commas after alldefinitions in a prototype. IDL attributes are always implemented asgetter functions. Methods marked with [noscript]will not be available for use with scripting languages.

Next we implement the functions below for our definition ofattribute stringyourName in our filensISimple.idl.

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

When someone calls an IDL attribute in an interface, getters andsetters 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 attributeyourName and then use the getter function toobtain the currently set value of yourName.

The first function defined in nsISimple is calledvoid write( ). For this method, the implementationcan 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 variablemName to stdout. The codeuses the this keyword to indicate that you arecalling to the component's own member variablemName.

The void change( ) method is then implemented asfollows:

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

change( ) is a method used to change the valuevariable.

8.2.1.3. Implementing the required XPCOM methods in JavaScript

Once the definitions in the nsISimple interfaceare implemented, you needto implement required methods and factories that make this JavaScriptimplementation class an XPCOM component. Recall that all XPCOMcomponents must implement the nsISupportsinterface.

Example 8-3 shows an implementation ofQueryInterface specific to our new component.QueryInterface ensures that the correct interface(nsISimple) is used by matching theiid with the nsISimpleinterface that this component implements. If the interfacedoesn't match, then the argument is invalid. In thiscase, the exceptionComponents.results.NS_ERROR_NO_INTERFACE isthrown, which maps to the error code number 2147500034, and codeexecution is stopped. If the interface identifier parameter matchesthe interface, then an instance of the implementation class objectSimpleComponent with its interface is returned asa ready-to-use XPCOM component. In XPCOM, every component youimplement must have aQueryInterface 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 calledModule. This module implements the methods neededfor autoregistration and component return type objects.

var Module = {
    firstTime  : true,

The Boolean firstTime is a flag used only when thecomponent 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, butyou have to add some logic for first time registration so theComponent Manager has the information it needs.RegisterSelf is called at registration time(component installation) and is responsible for notifying thecomponent manager of all components implemented in this module. ThefileSpec, location, andtype parameters can be passed on to theregisterComponent method unmolested. Next,register the component with the Component Manager using code like thefollowing example. The parameters include the CID, a description, aprogID, and the other parameters you can passwithout changing:

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

The GetClassObjectmethodproduces Factory andSingletonFactory objects. Singleton objects arespecialized for services that allow only one instance of the object.Upon success, the method returns an instance of the componentsfactory, 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 myCIDand myProgID are the class ID and thehuman-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 componentsfactory, which through its own member function,createInstance( ), constructs and returns aninstance of the complete component (if the iidparameter is specified and is the correct interface). Otherwise, ifno iid parameter is used, theiid of nsISupports is usedand an instance of the module is created that will then need asubsequent call to QueryInterface to instantiatethe 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 whenshutdown occurs and is the last function in the module. ThecomponentManager calls the methodNSGetModule to initialize these required XPCOMmethods 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 implementationfor the nsISimple interface in its entirety.

Example 8-4. JavaScript implementation of nsISimple

function SimpleComponent(){}

SimpleComponent.prototype = {

    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; },
    mName: "a default value",

    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("*** Deferring registration of simple JS components\n");
            this.firstTime = false;
            throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
        }
        debug("*** Registering sample JS components\n");
        compMgr =
compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(this.myCID,
                                        "Simple JS Component",
                                        this.myProgID,
                                        fileSpec,
                                        location,
                                        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)
            throw Components.results.NS_ERROR_NO_AGGREGATION;
            return (new SimpleComponent()).QueryInterface(iid);
        }
    },

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

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

8.2.2. Compiling the Component

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

8.2.2.1. Compiling the type library

To compile the XPIDL interface filensISimple.idl, you need to add the path of theXPIDL compiler to your environment. As mentioned earlier, the XPIDLcompiler is located atmozilla/xpcom/typelib/xpidl. Here is the outputof a Unix/cygwin/OSX session showing thecompilation starting with the source file(nsISimple.idl) created earlier in the chapter.Afterwards, nsISimple.xpt andnsSimple.js are copied to thecomponents 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 thensISimple.idl source file into thensISimple.xpt typelib file. The newly compiledtypelib file and the JavaScript implementationfile are then copied to the Mozilla distribution components directorywhere component registration will occur automatically when Mozilla islaunched.

8.2.2.2. Creating a Makefile for your component project

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

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 usingcygwin, or Mac OS X, theTOP_SRC environment variable points to the Mozillasource tree's top-level directory, theINST_DIR points to the directory where thecomponent should be installed, and the XPIDL variables drive theXPIDL executable and its environment and compiler flags. The"all" Makefile target compiles andcreates the type library nsISimple.xpt.

Note that in addition to the type libraries, the XPIDL compiler compilesheader files, Java class files, and special HTML documentation, ifnecessary.

8.2.3. Testing the Component

When you start up xpcshell, the ComponentManager finds the new nsISimple component andregisters 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, youcan use JavaScript from a local web page or from the chrome to createan nsISimple object and use it as you would anyordinary 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 implementXPCOM components in C++ and Python. The next sections cover the C++implementation of the nsISimple interface.

8.2.4. Useful C++ Macros and Types

Before you begin working on anactual implementation of a C++ component, familiarize yourself withsome of the tools that make C++ programming for XPCOM a littleeasier. Templates, special types, and macros can ease some of theextra housekeeping that programming XPCOM requires.

More tools than we can cover in this introduction are available, butthis section reviews some of the most common, including a macro thatimplements the nsISupports methodsQueryInterface, AddRef, andRelease, macros for testingnsresults, smartpointers, and special types.

8.2.4.1. The NS_IMPL_ISUPPORTS1_CI macro

Rather than having to implementQueryInterface, AddRef, and theRelease methods like we did in our JavaScriptcomponent, the NS_IMPL_ISUPPORTS macro inserts theimplementation 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 implementbasic methods of the nsISupports interface. Unlessyou need to modify these macros, they should be left as is. Thismacro is used later on when we create our C++ component.

Example 8-6 shows a referenceimplementationof 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;
}

8.2.4.2. The results macros

Since all XPCOM methods return result codes callednsresults, another useful macro is theNS_SUCCEEDED macro. Thisindicateswhether an XPCOM accessor has returned a successful result. It isdefined in nsError.h:

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

A related macro, NS_FAILED, is indicates whetheran XPCOM accessor returned a failure code result. It too is definedin nsError.h. The following code demonstratesthe 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 identifierrv as the type nsresult.nsresult is a 32-bit unsigned integer declared innscore.h:

  typedef PRUint32 nsresult;

We assign an nsCOMPtr or smart pointer namedfile to a newly created instance of thensILocalFile component. Using theNS_FAILED and NS_SUCCEEDEDmacros, we test for the nsresult to see if ourattempt to create an instance of the component failed. If it did,rv would be assigned an integer with a specificerror return code. Return codes are defined innsError.h. Alternatively, you can test yourresults 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 returnsNS_OK, which is 0.

Return codes are used in XPCOM instead of exceptions. Exceptions arenot allowed because of their inconsistent implementation acrossdifferent compilers. All error code numbers equate to a specific typeof error. For example NS_ERROR_FAILURE andNS_ERROR_NULL_POINTER are common types of errorcode return values used throughout the Mozilla code base. If a valuereturned to rv wasNS_ERROR_NULL_POINTER, the test for failure wouldbe true and the code would return the numerical result code forNS_ERROR_NULL_POINTER.

8.2.4.3. 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 touse null. The following code shows how to usensnull:

  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;
  }

8.2.4.4. The NS_IMETHODIMP macro

If you look in the Mozilla C++ sourcecode, you will see the macro NS_IMETHODIMP usedfrequently. This macro identifies the type of your interfaceimplementation method. It is also defined innscore.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 theNS_IMETHODIMP macro. All methods that implement aninterface 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 methodGetSomeString as an XPCOM implementation.

8.2.4.5. nsCOMPtr smart pointer

As described earlier, XPCOM provides a C++ tool called a smart pointer tomanage reference counting. A smart pointer is a template class thatacts syntactically, just like an ordinary pointer in C or C++. Youcan 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 foryou, 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 anyother pointers that point to it must be"refcounted." Example 8-9 uses anotherPtrand oneMorePtr to point torefp, and manually managesAddRef 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 someConditionis false, anotherPtr is released and the functionthen returns (NS_OK). But what aboutoneMorePtr? In this instance, it is neverreleased; if you remember, an object cannot be released from memoryuntil our refcount is at zero. Therefcount is out of sync,oneMorePtr is never decremented before the return,and the object is thus left dangling in memory. With therefcount off, the object leaks. Remember thatRelease( ) calls the C++ deleteoperator to free up the allocated XPCOM object only when the count isdecremented to 0. If Release thinks there arestill references to the object because therefcount hasn't been properlydecremented, delete is never called. The correctcode 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 toerror. To alleviate this burden and extra code bloat,nsCOMPtr implements AddRef andRelease for you and makes life much easier. Beforethe nsCOMPtr class is removed from the stack, itcalls Release in its destructor. After allreferences are properly released, delete is calledand the object is freed from memory. Example 8-10shows 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 thensLocalFile XPCOM object are releasedautomatically in the nsCOMPtr class destructorbefore the instructions are removed from the stack. By lettingnsCOMPtr manage AddRef andRelease for you, you remove a margin for error,code complexity, and bloat.

8.2.5. C++ Implementation of nsISimple

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

Earlier in this chapter, the section Section 8.2.1 showed you how to create aninterface and implement it in JavaScript. However, you may need a C++implementation to benefit from the better performance offered by acompiled language.

Most components used in Mozilla are written in C++. This sectiondiscusses how to create a C++ implementation for thensISimple interface. A few more steps areinvolved, but as you will see, they are generally similar to theprocesses described in the JavaScript component section, facilitatedto some extent by the available tools and templates discussedpreviously.

8.2.5.1. Creating a C++ component

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

First, create a new directory and call it simple:

$ mkdir simple 
$ cd simple

You can place the nsISimple interface you createdearlier in this new directory as a file callednsISimple.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 attributeyourName and the methods write() and change( ) are defined, you cancreate a header file for the implementation source file.

8.2.5.2. nsISimple C++ header file

Earlier, you created the type librarynsISimple.xpt for the JavaScript component andinstalled it in the components subdirectory. Sincewe've already covered those steps, we can moveforward to generating a C++ header file. To create a C++ header filefrom your original IDL, run your IDL file through thexpidl compiler:

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

The generated file is nsISimple.h and is shownin 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 lotof work for you. The code generated in Example 8-11is a C++ header file that declares the methods ofnsISimple. It provides the class definition,macros for using the interface, and a template for the classimplementation, which contains stubbed-out declaratory code that youcan paste into your implementation file to quickly get started.

8.2.5.3. Creating the implementation file

The implementation file actually contains the C++ code that implementsthe member functions and properties declared in your interface. FornsISimple, these members are theyourName attribute and the write() and change( ) methods.

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

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

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

To maintain clarity, the C++ implementation class is namednsSimpleImpl, where the default class namegenerated by the xpidl compiler isnsSimple 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 filensISimple.h, which holds the C++ declarationsfor the interface class nsISimple. It then takesthe new UUID and breaks it into a class ID struct defined asNS_SIMPLE_CID. Next, it defines the contract IDfor this implementation class.

The example uses a completely different class ID and contract ID thanthe one used for the JavaScript component becauseit's a different implementation class and needs tohave it's own unique identification (even though itimplements the same interface).

Now the example makes the class declaration of the implementation,called nsSimpleImpl, which inherits fromnsISimple, defining the class constructor andvirtual destructor. NS_DECL_ISUPPORTS is amacro that holds the declaration of ourrequired QueryInterface,AddRef, and Release methods.NS_DECL_NSISIMPLE is created in the generatedheader file nsISimple.h. It expands to the usedinterface method declarations. Finally Example 8-12shows the addition of the char* member variableidentified as mName. This variable is used tohold the value of the interface attributeyourName, just as it did earlier in the JavaScriptclass implementation.

Once you have the header file, you are ready to start theimplementation source file. With a text editor, create a new filecalled 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 anddestructor:

// 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. Asdiscussed earlier, this macro conveniently implementsQueryInterface, AddRef, andRelease:

NS_IMPL_ISUPPORTS1_CI(nsSimpleImpl, nsISimple);

Next you are ready to implement the actualnsISimple 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 typeNS_IMETHODIMP. The implementation starts with thegetter method GetYourName, which takes achar** parameter for the method'sreturn value. Return values in C++ XPCOM components are marshaled viamethod arguments because interface implementations must always returna numerical nsresult, as described earlier. Toensure that the aName parameter is a pointer, usethe macro NS_PRECONDITION towarn if null, follow with a null test inthe line below, and return the error result codeNS_ERROR_NULL_POINTER. Then test whether themember 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 valueto the parameter aName. Otherwise,mName is null and you can assign null intoaName 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, useNS_PRECONDITION and then a null test on theaName. If that parameter holds data, you can freeit by using PL_strfree and callingPL_strdup. Then assign the new value to classmember 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 andChange methods by using printfto write the value of mName tostdout and set a new value tomName. 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( ); // has been depricated use NS_INIT_ISUPPORTS() 
    NS_INIT_ISUPPORTS(); 
    mValue = 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);
}

8.2.6. The nsSimple module code

As you needed to do with the JavaScript implementation,youmust create the code for the module. The module code abstracts theimplementation class and makes the implementation a componentlibrary. In your text editor, createafile called nsSimpleModule.cpp and enter thecode 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)

8.2.6.1. The final steps for a C++ component

Once you have an interface file nsISimple.idl, aC++ source file nsSimple.cpp with its headerfile nsSimple.h, and a module filensSimpleModule.cpp, you can create a Makefilelike the one shown in Example 8-15. This Makefile cancompile the sources into an XPCOM component.

A Makefile directs the Mozilla build system to build the sources andinstall them into the Mozilladist/bin/components directory. To use theMakefile, run gmake to compile and install the component libraryfile.

Example 8-15. Sample Makefile

DEPTH         = ../../..
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 usexpcshell like you did for the JavaScriptcomponent. Example 8-16 shows a session withxpcshell 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>

8.2.6.2. Creating an instance of an existing Mozilla component

Creating an instanceof acomponent and accessing methods and attributes is different in C++than it is in JavaScript. Using the nsILocalFileinterface lets you walk through the code to create an instance ofthis 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 newlyallocated instance of an nsLocalFile object.

Example 8-17 accesses the public methods availablefrom this component by using the pointer identifierfile.

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, andsetters. Failures can appear at any time, so be sure to use resultchecking in your implementations.

8.2.7. Other Languages for XPCOM

Although most components available from XPCOM are written in C++, theXPConnect/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, withother language bindings being developed. In this respect, the Mozillaframework dovetails with one of the main trends in applicationdevelopment, which is to mix different languages in the developmentenvironment.

8.2.7.1. PyXPCOM: the Python binding for XPCOM

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

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

from xpcom import components

The IDL for a Python implementation of an XPCOM component can beidentical to one for a JavaScript- or C++-based component (which isthe point of XPCOM, after all). As in any component, your IDL needsto include nsISupports.idl and declare itself asscriptable with a unique UUID:

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

JavaScript accesses the component in the same way, using classes andinterface members of the component's interfaces toset 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 Pythondistribution on your system that Mozilla can access, you are ready togo! Example 8-18 shows a complete implementation of aPyXPCOM component. This file needs to be saved with a.py extension and put in thecomponents directory and registered like anyother 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 inthe appSysUtilsclass correspond to the special identifiers you must use in XPCOM tomake your code a reusable component (see Section 8.1.5, earlier in this chapter).Table 8-3 describes these attributes.

Table 8-3. Special XPCOM attributes in Python

Attribute

Description

_com_interfaces_

The interface IDs supported by this component. This attribute isrequired. It can be a single IID or a list, but you do not have tolist base interfaces such as nsISupports.

_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 createa 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 mightbe used in script -- for example, in a function you define for anevent 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/writepermissions status of a file on the local filesystem. The JavaScriptuses it to display a visual notifier of the status in the UI. In thiscase, the DOM's rwcheck noderefers to a checkbox. It's easy to imagine thiscomponent being extended to do other things, such as gettinginformation about a file (the Stat stub is in theIDL). The source code, samples, and documentation for PyXPCOM arelocated in the Mozilla tree atmozilla/extensions/python.

8.2.8. XPCOM as an Open Cross-Platform Solution

XPCOM can be an entire bookinitself. This chapter has merely touched upon the role it plays inMozilla application development. Understanding the basics of thisframework is vital to understanding the very foundation ofMozilla's componentized architecture.

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

Above all, XPCOM is entirely open source, so there are no costsassociated with it, no proprietary secrets in howit's put together, and you have various softwarelicenses to choose from. Although XPCOM has become a solid framework,its developers are still making improvements and uncovering andfixing bugs. However, XPCOM offers tremendous flexibility as asoftware development framework and the Mozilla community is anexcellent technical support resource for all technologies covered inthis book.


PrevHomeNext
XPCOMUpXUL Templates
Creating Applications with Mozilla
PrevChapter 8. XPCOMNext
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值