Implementing XPCOM components in JavaScript
From MozillaZine Knowledge Base
About XPCOM
Mozilla applications are built from a collection of XPCOM (Cross-platform Component Object Model) objects. They are used to perform certain tasks or to get specific functionality. For example, there is a directory service component that can be used to access files on your file system. Components can be constructed using several programming languages, including: javascript, c++, and python. Mozilla then uses "interfaces" to describe what a component can do. The most basic interface nsISupports is used to get different interfaces that a component implements.
- Benifits of XPCOM
- XPCOM objects can be used by any programming language mozilla supports.
- Objects implemented as XPCOM are global to an application, and are not dependent on the scope of any one window.
- Programming logic can be encapsulated within an object.
- XPCOM Drawbacks
- Objects must be accessed from their defined interfaces. Javascript shortcuts such as the global window object cannot be accessed.
- The only exception to this rules, is if you set a magical property wrappedJSObject equal to your component, then the underlining javascript object can be accessed.
- It is easier to have memory leaks
- Objects must be accessed from their defined interfaces. Javascript shortcuts such as the global window object cannot be accessed.
Finished Code
Below is how the finished code may look.
// constants const nsISupportsPriority = Components.interfaces.nsISupportsPriority; const CLASS_ID = Components.ID("{1C0E8D86-B661-40d0-AE3D-CA012FADF170}"); const CLASS_NAME = "My Supports Priority Component"; const CONTRACT_ID = "@mozillazine.org/example/priority;1"; //class constructor function MyPriority() { this._priority = nsISupportsPriority.PRIORITY_LOWEST; }; //class definition MyPriority.prototype = { _priority: null, get priority() { return this._priority; }, set priority(aValue) { this._priority = aValue; }, adjustPriority: function(aDelta) { this._priority += aDelta; }, QueryInterface: function(aIID) { if (!aIID.equals(nsISupportsPriority) && !aIID.equals(Components.interfaces.nsISupports)) throw Components.results.NS_ERROR_NO_INTERFACE; return this; } }; //class factory var MyPriorityFactory = { createInstance: function (aOuter, aIID) { if (aOuter != null) throw Components.results.NS_ERROR_NO_AGGREGATION; return (new MyPriority()).QueryInterface(aIID); } }; //module definition (xpcom registration) var MyPriorityModule = { _firstTime: true, registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) { if (this._firstTime) { this._firstTime = false; throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN; }; 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 MyPriorityFactory; throw Components.results.NS_ERROR_NO_INTERFACE; }, canUnload: function(aCompMgr) { return true; } }; //module initialization function NSGetModule(aCompMgr, aFileSpec) { return MyPriorityModule; }