Cómo crear un componente XPCOM en JavaScript

 

Introducción

Éste es un tutorial del tipo "Hola Mundo" para crear un componente XPCOM en JavaScript. No se describe cómo funciona XPCOM ni se explica lo que hace cada línea de código. Todo ello se detalla en otras páginas.

En este tutorial mostraremos qué es lo que debes hacer para conseguir un componente funcional en pocos pasos y de forma sencilla.

Advertencia: lo expuesto aquí ha sido desarrollado en un Mac. El comportamiento puede variar en otros sistemas operativos.

Implementación

Éste es un componente de ejemplo, con un único método que devuelve la cadena "Hola MozDev!".

Definir la interfaz

Si quieres usar tu componente desde JavaScript, o en otros componentes XPCOM, debes definir las interfaces que quieres mostrar (si quieres usar tu componente sólo desde Javascript, puedes usar el truco wrappedJSObject para que no necesites definir interfaces como se explica aquí. Mira un ejemplo aquí (en)).

Hay muchas interfaces ya definidas en las aplicaciones Mozilla, así que tal vez no necesites definir una nueva. Puedes mirar las interfaces XPCOM existentes de varias formas, en el código fuente Mozilla, o usando XPCOMViewer, un GUI para ver los componentes e interfaces registradas. Puedes descargar una versión que trabaje con Firefox 2 desde MozDev.

Si existe una interfaz que cumpla tus necesidades, no necesitas escribir un IDL, o compilar un typelib, y puedes saltar a la siguiente sección.

Si no encuentras una interfaz pre-existente que te satisfaga, debes definirla. XPCOM usa un dialecto de IDL para definir interfaces, llamado XPIDL. A continuación tienes la definición XPIDL para nuestro componente HolaMozDev:

HolaMozDev.idl

#include "nsISupports.idl"

[scriptable, uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx)]
interface nsIHolaMozDev : nsISupports
{
  string hola();
};  

Ten en cuenta que tienes que generar un UUID para cada componente XPCOM que crees. Mira Generating GUIDs para más información.

Los UUID no pueden ser escritos puesto que se debe garantizar su unicidad. Deben ser generados empleando un algoritmo que la garantice.

Compilar la biblioteca de tipos

La definición de tu interfaz debe compilarse a forma binaria (XPT) para poder ser registrada y usada dentro de las aplicaciones Mozilla. La compilación puede hacerse usando el SDK de Gecko. Puedes averiguar cómo obtener versiones Mac, Linux, y Windows del SDK de Gecko leyendo el artículo Gecko SDK.

Nota: la versión Mac del SDK proporcionado para su descarga es sólo para PowerPC. Si necesitas una versión Intel, tendrás que compilarlo tú mismo como se describe en esa página.

Ejecuta esta orden para compilar la biblioteca de tipos. Aquí, {sdk_dir} es el directorio del SDK de Gecko descomprimido.

{sdk_dir}/bin/xpidl -m typelib -w -v -I {sdk_dir}/idl -e HolaMozDev.xpt HolaMozDev.idl

(El modificador -I es una i mayúscula, no una L minúscula) Esta orden creará el archivo de la bilioteca de tipos HelloWorld.xpt en el directorio de trabajo actual.

Crear el componente

HolaMozDev.js

/***********************************************************
constantes
***********************************************************/

// referencia a la interfaz definida en nsIHolaMozDev.idl
const nsIHolaMozDev = Components.interfaces.nsIHolaMozDev;

// referencia requerida a la interfaz base que todos los componentes deben implementar
const nsISupports = Components.interfaces.nsISupports;

// UUID único identificando nuestro componente
// puedes obtener el tuyo desde : http://kruithof.xs4all.nl/uuid/uuidgen
const CLASS_ID = Components.ID("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}");

// descripción
const CLASS_NAME = "Un Componente XPCOM de muestra";

// identificador único textual
const CONTRACT_ID = "@ejemplo.evelio.net/holamozdev;1";

/***********************************************************
definición de la clase
***********************************************************/

// el constructor de clase
function HolaMozDev() {
};

// definición de la clase
HolaMozDev.prototype = {

  // define la función que declaramos en la interfaz
  hola: function() {
      return "Hola MozDev!";
  },
  //Desde nsISupports
  QueryInterface: function(aIID)
  {
    if (!aIID.equals(nsIHolaMozDev) &&    
        !aIID.equals(nsISupports))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};

/***********************************************************
la factoría de la clase

Este objeto es miembro del objeto global Components.classes.
Se deriva del ID de contrato, p.e.:

miHolaMozDev = Components.classes["@ejemplo.evelio.net/holamozdev;1"].
                          createInstance(Components.interfaces.nsIHolaMozDev);

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

/***********************************************************
definición del módulo (registro xpcom)
***********************************************************/
var HolaMozDevModulo = {
  _firstTime: true,
  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 FabricaHolaMozDev;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  canUnload: function(aCompMgr) { return true; }
};

/***********************************************************
Inicialización del módulo

Cuando la aplicación registra el componente, se llama a esta función.

***********************************************************/
function NSGetModule(aCompMgr, aFileSpec) { return HolaMozDevModulo; }

Instalación

Para extensiones:

  1. Copia HolaMozDev.js y HolaMozDev.xpt al directorio {extensiondir}/components/.
  2. Elimina compreg.dat y xpti.dat del directorio de tu perfil de usuario.
  3. Reinicia la aplicación.

Para Firefox:

  1. Copia HolaMozDev.js y HolaMozDev.xpt al directorio {objdir}/dist/bin/components, si está corriendo desde el código fuente.
  2. Elimina compreg.dat y xpti.dat del directorio components.
  3. Elimina compreg.dat y xpti.dat del directorio de tu perfil de usuario.
  4. Reinicia la aplicación.

Usar tu componente

try {
        // Esto es necesario para permitir el uso generalizado de componentes en JavaScript
        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

        var miComponente = Components.classes['@ejemplo.evelio.net/holamozdev;1']
                                    .createInstance(Components.interfaces.nsIHolaMozDev);

        alert(miComponente.hola());
} catch (unError) {
        alert("ERROR: " + unError);
}

Otros recursos

Etiquetas y colaboradores del documento

Contributors to this page: teoli, ibnkhaldun, RickieesES, Evelio, Fenomeno, Jorolo, Neoser
Última actualización por: teoli,