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.
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.
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 : https://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:
- Copia HolaMozDev.js y HolaMozDev.xpt al directorio {extensiondir}/components/.
- Elimina compreg.dat y xpti.dat del directorio de tu perfil de usuario.
- Reinicia la aplicación.
Para Firefox:
- Copia HolaMozDev.js y HolaMozDev.xpt al directorio {objdir}/dist/bin/components, si está corriendo desde el código fuente.
- Elimina compreg.dat y xpti.dat del directorio components.
- Elimina compreg.dat y xpti.dat del directorio de tu perfil de usuario.
- 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
- Dos temas en los foros de mozillazine sobre la implementación de componentes XPCOM en JS con algunas explicaciones, código de ejemplo, y trucos para resolver problemillas (en inglés):
- Implementing XPCOM components in JavaScript en kb.mozillazine.org
- Using XPCOM in JavaScript without leaking - De lectura obligatoria.
- Un componente de ejemplo
- Older JS+XPCOM notes - incluye información acerca de wrappedJSObject.