Implementing XPCOM components in JavaScript

From MozillaZine Knowledge Base
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


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

function MyPriority() {
  this._priority = nsISupportsPriority.PRIORITY_LOWEST;
};

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

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

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

function NSGetModule(aCompMgr, aFileSpec) { return MyPriorityModule; }