Tuesday, May 22, 2007

Make a Javascript XPCOM component in five minutes!

You don’t really have to make XPCOM components if you prefer not to when you are developing a Firefox extension, but there are times when it’s the right way of doing things, and I just happen to be a guy who really love to do the things right. ;-)

Again, even though XPCOM is not necessary, and might be evil in some cases such as causing memory leaks if used without care, it does provide a lot of benefits, such as providing a clean way to store temporary data, sepating the development concerns between multiple developers by making a clear contract, also enabling developers to use different languages(C, C++, Java, Python, Javascript) to program the underlying implementations!

Since reading XPCOM document isn’t the easist thing for me, I am going to give you an even quicker and dirtier kick start:

  • Go to http://developer.mozilla.org/en/docs/Gecko_SDK and download the right version of gecko sdk, for me, I am downloading the linux one on the bottom right side, since I am using ubuntu.
  • Unzip it using “tar -xjf gecko-sdk-i686-pc-linux-gnu-1.8.0.4.tar.bz2”, and move it into your home directory “move gecko-sdk ~/”
  • Go to your XOCOM component development dir and “touch HelloWorld.idl”
  • Inside “HelloWorld.idl”, paste

#include "nsISupports.idl"

[scriptable, uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]
interface nsIHelloWorld : nsISupports
{
string hello();
};
  • Go to http://www.famkruithof.net/uuid/uuidgen, generate a bunch of UUID, and replace the xxx’s of “[scriptable, uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)]” with your favorite UUID that you want to use for your component.
  • Compile the typelib interface

~/gecko-sdk/bin/xpidl -m typelib -w -v -I ~/gecko-sdk/idl -e nsIFireCruncher.xpt nsIFireCruncher.idl
  • Paste the following code into “HelloWorld.js”.

/***********************************************************
constants
***********************************************************/


// reference to the interface defined in nsIHelloWorld.idl
const nsIHelloWorld = Components.interfaces.nsIHelloWorld;

// reference to the required base interface that all components must support
const nsISupports = Components.interfaces.nsISupports;

// UUID uniquely identifying our component
// You can get from: http://kruithof.xs4all.nl/uuid/uuidgen here
const CLASS_ID = Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}");

// description
const CLASS_NAME = "My Hello World Javascript XPCOM Component";

// textual unique identifier
const CONTRACT_ID = "@hungryfools.com/helloworld;1";

/***********************************************************
class definition
***********************************************************/


//class constructor
function HelloWorld() {
};

// class definition
HelloWorld.prototype = {

// define the function we want to expose in our interface
hello: function() {
return "Hello World!";
},

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

/***********************************************************
class factory

This object is a member of the global-scope Components.classes.
It is keyed off of the contract ID. Eg:

myHelloWorld = Components.classes["@dietrich.ganx4.com/helloworld;1"].
createInstance(Components.interfaces.nsIHelloWorld);

***********************************************************/

var HelloWorldFactory = {
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return (new HelloWorld()).QueryInterface(aIID);
}
};

/***********************************************************
module definition (xpcom registration)
***********************************************************/

var HelloWorldModule = {
registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
{
aCompMgr = aCompMgr.
QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME,
CONTRACT_ID, aFileSpec, aLocation, aType);
},

unregisterSelf: function(aCompMgr, aLocation, aType)
{
aCompMgr = aCompMgr.
QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
},

getClassObject: function(aCompMgr, aCID, aIID)
{
if (!aIID.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

if (aCID.equals(CLASS_ID))
return HelloWorldFactory;

throw Components.results.NS_ERROR_NO_INTERFACE;
},

canUnload: function(aCompMgr) { return true; }
};

/***********************************************************
module initialization

When the application registers the component, this function
is called.
***********************************************************/

function NSGetModule(aCompMgr, aFileSpec) { return HelloWorldModule; }
  • To install the Component that you’ve just created into your extension,
    • Copy HelloWorld.js and HelloWorld.xpt to {extensiondir}/components/
    • Delete compreg.dat and xpti.dat from your profile directory.
    • Restart application

Original Reference: http://developer.mozilla.org/en/docs/How_to_Build_an_XPCOM_Component_in_Javascript

No comments: