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

UUID Generator

If you want to make an XPCOM component, the first thing that comes to do is to get a UUID for your component.

Here are the two online ones that would just do this job for you free of charge ;-)

  1. http://www.famkruithof.net/uuid/uuidgen
  2. http://www.somacon.com/p113.php

I like the first one better, as it allows you to make a whole bunch of UUIDs, and let you pick the number that you think is right for your dream component!

Oh, one more freebie ;-)

If you are in a really good mood and have a couple of minutes to spare, why not try to generate a UUID from Firefox itself? It’s got an XPCOM component to do this! How fun would that be? ;-)

Firefox Extension: XPCOMViewer

After many attempts to kick start my XPCOM adventure, here is one of the many useful plugins that you don’t want to miss in your Firefox extension development environment, XPCOMViewer.

With XPCOMViewer, you wouldn’t have to search all over the xulplanet.com or developer.mozilla.org for the XPCOM interfaces any more. Just type in the keywords or key phrases into the filter on the top, and you will find all about the available interfaces just by a couple of clicks!

Another wonderful addition to my tools collection. ;-)

Monday, May 21, 2007

Firefox extension in sidebar

To date, other than the first “Hello World” extension I created for Firefox, almost all of my other works makes use of the Sidebar. After trying to search for the instruction of building the extension overlay into the Firefox sidebar for countless times, I am now deciding to add the original article link into my collection.

Three ways to log errors and messages in firefox

After making a couple of simple firefox plugins, I decided to write a really good Javascript logger. With the previous experience and a little more dig around, I am thinking about providing three primary logging target implementations which firefox supports out of the box.

  1. Dump
  2. Components.utils.reportError
  3. nsIConsoleService

To use dump() function, you have to make sure that your firefox application is started with the command line option ‘-console’ after you set the setting ‘browser.dom.window.dump.enabled’ to true. This is good for really any logging, but it’s limited to command line logging.

‘Components.utils.reportError’ on the other hand, is really great for reporting errors, and it’s usually used as the exception catching block like below:

try {
this.could.raise.an.exception;
} catch(e) {
Components.utils.reportError(e); // report the error and continue execution
}
My favorite one would be the ‘nsIConsoleService’, as it is logging directly into the console, and you can easily create a wrapper into it to make it really powerful.

function LOG(msg) {
var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
.getService(Components.interfaces.nsIConsoleService);
consoleService.logStringMessage(msg);
}
You can even specify line numbers, flags and category ;-) through scripterror component class that comes with the firefox!

var scriptError = Components.classes["@mozilla.org/scripterror;1"]
.createInstance(Components.interfaces.nsIScriptError);
scriptError.init(aMessage, aSourceName, aSourceLine, aLineNumber,
aColumnNumber, aFlags, aCategory);
consoleService.logMessage(scriptError);

Monday, May 14, 2007

A thought on Firefox extension development

ust for fun, I started my own firefox extension project some time ago, and as the code base grows, I found out that the current extension architecture is very easy to get smaller extensions to work, but once you hit 5k to 10k lines of code, you need to really do something about the refactoring of the javascript. As my extension goes, it has a base command set, and a base execution environment. The key would be to enable the other developers to develop the extension of my extension, so it will have an ever richer command set! ;-) Organization and loading of the code without restarting firefox or reloading the chrome is crucial at this stage!

I am currently looking for advice. If you have an idea, please post a comment or email me. (You can find my email address at the end of the about page.) I will also post any updates as I receive them.

Wednesday, May 2, 2007

A good tutorial site for web development

I came across many sites that gives tutorials for web development. The sad thing is, not many of them are actually good for real programming tasks.

Here is one that I found recently that’s pretty good: http://www.howtocreate.co.uk from Mark Wilton-Jones – usually known as “Tarquin”.

Mark’s got a lot of good materials on Javascript, CSS, Design and cross browser compatibility stuff. A lot of examples and code that you can see in action and download, too!

Consider his site the UK version of http://www.quirksmode.com/.

Javascript Closure

Even though there are alternative technologies such as Flash and Flex, Javascript is still being considered as the mainstream. I started my interested in Javascript by working with one of my early mentors, who worked for Netscape back in the first browser war. ;-) He uses Javascript to create prototypes of the code piece, and created many everyday programmer’s utilities by writing a small piece of Javascript. It was one of the most fascinating thing I’ve seen then. Now I am pretty much as in love of Javascript as my mentor does.

There are many shortcomings in the language itself, but Prototypal inheritance, Closure and many other built-in features makes Javascript one of the most flexible and powerful computer language.

Here is the reference to one of my favorite Javascript Closure documentation: http://www.jibbering.com/faq/faq_notes/closures.html