Implementing XPCOM components in JavaScript: Difference between revisions

From MozillaZine Knowledge Base
Jump to navigationJump to search
No edit summary
No edit summary
Line 3: Line 3:


==About XPCOM==
==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 class that can be used to access files on your file system.  Classes can be constructed using several programming languages, including: javascript, c++, and python.  Mozilla then uses "interfaces" to describe what a class can do.  The most basic interface [http://lxr.mozilla.org/mozilla/source/xpcom/base/nsISupports.idl nsISupports] is used to retrieve the different interfaces that a class implements.
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 class that can be used to access files on your file system.  Classes can be constructed using several programming languages, including: javascript, c++, and python.  Mozilla then uses "interfaces" to describe what a class can do.  The most basic interface [http://lxr.mozilla.org/mozilla/source/xpcom/base/nsISupports.idl nsISupports] is used to retrieve the different interfaces that a class implements.


Line 17: Line 16:




== Class Concepts ==
== XPCOM Concepts ==
There are basically 4 concepts you need to understand when developing xpcom objects.  They are classes, interfaces, modules, and factories.  I will give a brief summary of each, then go into more details later.
 
=== Classes ===
A class is simply an xpcom object that implements interfaces, and is registered with the component manager.  In our case we are going to implement a javascript object which will be our class.
 
=== Interfaces ===
Interfaces are used to define what functions and attributes an xpcom class provides.  [http://lxr.mozilla.org/mozilla/source/xpcom/base/nsISupports.idl nsISupports] is an interface that all classes must implement.  It provides a function to request the interfaces on a class.


To understand how to create XPCOM classes there are a few concepts that need to be discussed.  A javascript xpcom class is simply a javascript object that implements interfaces, and is registered with the component manager.  Interfaces are used to define what functions and attributes an xpcom class provides.  One or more classes can be defined in a fileOne or more javascript objects need to be created so Mozilla understands what is being registered, and how to create the class.  These objects need to implement the [http://lxr.mozilla.org/mozilla/source/xpcom/components/nsIModule.idl nsIModule], and [http://lxr.mozilla.org/mozilla/source/xpcom/components/nsIFactory.idl nsIFactory] interfaces.
=== Factories ===
Factories are objects used to instantiate classes.  it implements the [http://lxr.mozilla.org/mozilla/source/xpcom/components/nsIFactory.idl nsIFactory] interface.
 
=== Modules ===
Multiple classes can be defined in a file.  A module is an object that is used to register, unregister and provide factories for classes.  It implement the [http://lxr.mozilla.org/mozilla/source/xpcom/components/nsIModule.idl nsIModule] interface.




== Startup and Registration ==
== Startup and Registration ==
During the startup process if the file compreg.dat is missing or the file .autoreg exists, the application knows that it needs to register components.  It then loads files from each of the components folders, including the components folder in each extension directory.  It then calls the function NSGetModule on each file to get the object that implements the nsIModule interface.
During the startup process if the file compreg.dat is missing or the file .autoreg exists, the application knows that it needs to register components.  It then loads files from each of the components folders, including the components folder in each extension directory.  It then calls the function NSGetModule on each file to get the object that implements the nsIModule interface.


Line 30: Line 39:
function NSGetModule(aCompMgr, aFileSpec) { return MyPriorityModule; }
function NSGetModule(aCompMgr, aFileSpec) { return MyPriorityModule; }
</pre>
</pre>
=== Modules ===
Multiple classes can be defined within a single file (module).  The nsIModule interface is used to register what clases are in that file and to retrieve those classes through:
<pre>
Components.classes["@mozillazine.org/example/priority;1"].createInstance();
Components.classes["@mozillazine.org/example/priority;1"].getService();
</pre>


==Finished Code==
==Finished Code==

Revision as of 14:59, 3 February 2006

This page is part of the extension development documentation project.

Ask your questions in MozillaZine Forums. Also try browsing example code.

Note: development documentation is in process of being moved to Mozilla Development Center (MDC).


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 class that can be used to access files on your file system. Classes can be constructed using several programming languages, including: javascript, c++, and python. Mozilla then uses "interfaces" to describe what a class can do. The most basic interface nsISupports is used to retrieve the different interfaces that a class 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 class, then the underlining javascript object can be accessed.
    • It is easier to have memory leaks


XPCOM Concepts

There are basically 4 concepts you need to understand when developing xpcom objects. They are classes, interfaces, modules, and factories. I will give a brief summary of each, then go into more details later.

Classes

A class is simply an xpcom object that implements interfaces, and is registered with the component manager. In our case we are going to implement a javascript object which will be our class.

Interfaces

Interfaces are used to define what functions and attributes an xpcom class provides. nsISupports is an interface that all classes must implement. It provides a function to request the interfaces on a class.

Factories

Factories are objects used to instantiate classes. it implements the nsIFactory interface.

Modules

Multiple classes can be defined in a file. A module is an object that is used to register, unregister and provide factories for classes. It implement the nsIModule interface.


Startup and Registration

During the startup process if the file compreg.dat is missing or the file .autoreg exists, the application knows that it needs to register components. It then loads files from each of the components folders, including the components folder in each extension directory. It then calls the function NSGetModule on each file to get the object that implements the nsIModule interface.

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

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