Implementing XPCOM components in JavaScript

From MozillaZine Knowledge Base
Revision as of 21:52, 2 February 2006 by Richwklein (talk | contribs)
Jump to navigationJump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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


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