Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Interfaces XPCOM

 

En esta sección, vamos a echar un pequeño vistazo a XPCOM (Cross-platform Component Object Model o Modelo de Objeto de Componentes Multiplataforma), el sistema de objetos que utiliza Mozilla.

Llamando a Objetos Nativos

Usando XUL podemos construir una interfaz de usuario compleja. Podemos asociarle scripts que modificarán la interfaz y ejecutarán tareas. Pero hay bastantes cosas que no podemos hacer directamente con JavaScript. Por ejemplo, si quisieramos crear una aplicación de correo, necesitaríamos escribir scripts que se conectaran con los servidores de correo para obtener el correo y mandarlo. Pero JavaScript no puede hacer esta clase de cosas.

La única forma de llevar a cabo estas tareas sería escribiendo código nativo. También necesitaríamos una forma de que nuestros scripts pudieran comunicarse con el código nativo de forma sencilla. Mozilla nos proporciona todo esto con XPCOM (Modelo de Objeto de Componentes Multiplataforma).

Acerca de XPCOM

Mozilla está construido mediante una colección de componentes, cada uno de ellos responsable de ejecutar una cierta tarea. Por ejemplo, hay un componente para cada menú, botón y elemento. Estos componentes se construyen a partir de un conjunto de definiciones llamadas interfaces.

Una interfaz en Mozilla es una definición de un conjunto de funcionalidades que pueden ser implementadas mediante componentes. Los componentes son los encargados de implementar el código que ejecuta las distintas tareas en Mozilla. Cada componente implementa la funcionalidad tal como está descrita en la interfaz. Un único componente puede implementar múltiples interfaces. Y varios componentes pueden implementar la misma interfaz.

Tomemos el ejemplo de un supuesto componente archivo. Se necesitaría crear una interfaz que describiera las propiedades y funciones que pueden realizarse sobre los archivos. Un archivo necesitaría propiedades para almacenar su nombre, la fecha de modificación y su tamaño. Entre las funciones del archivo podríamos incluir movernos por el archivo, copiarlo o borrarlo.

La interfaz Archivo solo describe las características de un archivo, no las implementa. La implementación de la interfaz Archivo es tarea del componente. El componente tendrá código que será capaz de obtener el nombre del archivo, la fecha y su tamaño. Además, tendrá código que permita copiarlo y renombrarlo.

Mientras implemente la interfaz correctamente, no nos preocupa cómo hace todas estas cosas. Evidentemente, necesitaremos diferentes implementaciones, una para cada plataforma. Las versiones de un componente archivo en Windows y Macintosh serán bastante diferentes. De todas formas, ambas implementarán la misma interfaz. Por lo tanto, podemos utilizar un componente accediendo a las funciones que conocemos de la interfaz.

En Mozilla, el nombre de las interfaces viene precedido de ‘nsI’ de forma que sean facilmente reconocibles. Por ejemplo, nsIAddressBook es la interfaz que se utiliza para interaccionar con una libreta de direcciones (address book), nsISound se utiliza para reproducir sonidos y nsILocalFile se utiliza para manejo de archivos.

Los componentes XPCOM suelen implementarse con código nativo, lo que significa que normalmente pueden hacer cosas que JavaScript no puede por sí mismo. Existe una forma sencilla de llamar a este código nativo, que veremos dentro de poco. Podemos ejecutar cualquiera de las funciones proporcionadas por el componente tal como se describe en las interfaces que implementa. Por ejemplo, una vez que tenemos un componente, podemos comprobar si implementa nsISound, y, si es el caso, podemos reproducir sonidos a través de él.

El proceso de llamar a XPCOM desde un script se realiza a través de XPConnect, una capa que traduce los objetos de script en objetos nativos.

Creando Objetos XPCOM

Hay tres pasos a seguir a la hora de llamar a un componente XPCOM.

  1. Obtener el componente
  2. Obtener la parte del componente que implementa la interfaz que queremos utilizar.
  3. Llamar a la función que necesitamos

Una vez que se han llevado a cabo los dos primeros pasos, podemos repetir el último paso tantas veces como sea necesario. Digamos que queremos renombrar un archivo. Para eso podemos utilizar la interfaz nsILocalFile. El primer paso es obtener un componente archivo. Después, hacemos una petición al componente y obtenemos la porción que implementa la interfaz nsILocalFile. Por último, llamamos a las funciones proporcionadas por la interfaz. Esta interfaz se utiliza para representar un único archivo.

Hemos visto que las interfaces siempre se nombran comenzando con ‘nsI’. Los componentes, por otra parte, se referencian utilizando una sintaxis URI. Mozilla almacena una lista de todos los componentes disponibles en su propio registro. Un usuario puede instalar nuevos componentes si lo desea. Todo esto funciona de forma muy parecida a los plug-ins.

Mozilla proporciona un componente archivo, es decir, un componente que implementa nsILocalFile. Este componente puede ser referenciado para ser utilizado usando la URI ‘@mozilla.org/file/local;1’. Para especificar un determinado componente se utiliza la forma componente: URI. Se puede referenciar otros componentes de forma similar.

Se puede utiliar esta URI para obtener el componente. Para obtener un componente podemos utilizar un código JavaScript similar al siguiente:

var unArchivo = Components.classes["@mozilla.org/file/local;1"].createInstance();

Se obtiene el componente archivo y éste se almacena en la variable unArchivo.

La palabra clave Components del ejemplo anterior hace referencia a un objeto general que proporciona algunas funciones relacionadas con componentes. En este caso, obtenemos la clase de un componente a partir de la propiedad classes. La propiedad classes es un vector de todos los componentes disponibles. Para obtener un componente diferente, simplemente tendrías que reemplazar la URI de este componente por la del componente que quieras utilizar. Por último, se crea una instancia con la función createInstance.

Deberías comprobar el valor de retorno de createInstance para asegurarte de que no sea null, lo que indicaría que el componente no existe.

Llegados a este punto tenemos solo una referencia al componente archivo en sí. Para poder llamar a sus funciones necesitamos obtener una de sus interfaces, en este caso nsILocalFile. Necesitamos añadir una segunda línea de código:

var unArchivo = Components.classes["@mozilla.org/file/local;1"].createInstance();
if (unArchivo) unArchivo.QueryInterface(Components.interfaces.nsILocalFile);

QueryInterface es una función proporcionada por todos los componentes que puede ser utilizada para obtener una interfaz específica de ese componente. Esta función toma un solo parámetro, la interfaz que queremos obtener. La propiedad interfaces del objeto Components contiene una lista de todas las interfaces disponibles. En este caso, utilizamos la interfaz nsILocalFile y la pasamos como parámetro a QueryInterface. El resultado es que unArchivo será una referencia a la parte del componente que implementa la interfaz nsILocalFile.

Las dos líneas de JavaScript anteriores pueden ser utilizadas para obtener cualquier interfaz de un componente. Simplemente reemplaza el nombre del componente con el nombre del componente que quieras utilizar y sustituye también el nombre de la interfaz. Por supuesto, también puedes utilizar cualquier nombre de variable. Por ejemplo, para obtener una interfaz sonido, puedes hacer lo siguiente:

var sonido = Components.classes["@mozilla.org/sound;1"].createInstance();
if (sonido) sonido.QueryInterface(Components.interfaces.nsISound);

Las interfaces XPCOM pueden heredar de otras interfaces. Las interfaces que heredan de otras tienen sus propias funciones y las funciones de todas las interfaces de las que heredan. Todas las interfaces heredan de una interfaz raíz llamada nsISupports. En esta interfaz se define la función QueryInterface, que ya hemos visto. Dado que todos los componentes implementan la interfaz nsISupports, la función QueryInterface está disponible en todos los componentes.

Varios componentes distintos pueden implementar la misma interfaz. Normalmente, suelen ser subclases de la original pero no es necesario. Cualquier componente puede implementar la funcionalidad de nsILocalFile. Además, un componente puede implementar varias interfaces. Por esto es por lo que se necesitan dos pasos a la hora de obtener una interfaz con la que llamar a las funciones.

De todas formas, existe un atajo que podemos utilizar dado que estas dos líneas se utilizan tan amenudo:

var aLocalFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);

Esto tiene el mismo efecto que las dos líneas anteriores pero en una sola línea de código, lo cual elimina la necesidad de crear la instancia y pedir una interfaz en dos pasos distintos.

Si llamas a QueryInterface sobre un objeto y la interfaz pedida no está soportada por el objeto, se lanza una excepción. Si no estas seguro de si un componente soporta una determinada interfaz, puedes utilizar el operador instanceof para comprobarlo:

var unArchivo = Components.classes["@mozilla.org/file/local;1"].createInstance();
if (unArchivo instanceof Components.interfaces.nsILocalFile){
  // hacer algo
}

El operador instanceof devuelve verdadero si unArchivo implementa la interfaz nsILocalFile. Esto tiene el efecto secundario de llamar a QueryInterface, de forma que unArchivo será un nsILocalFile después.

Llamando a las Funciones de una Interfaz

Ahora que tenemos un objeto que referencia un componente con la interfaz nsILocalFile, podemos llamar a las funciones de nsILocalFile a través de él. La tabla siguiente muestra alguna de las propiedades y métodos de la interfaz nsILocalFile.

Propiedad / método Descripción

initWithPath 
Este método se utiliza para inicializar la ruta y el nombre de archivo para nsILocalFile. El primer parámetro debería ser la ruta del archivo, como ‘/usr/local/mozilla’
leafName  
El nombre de archivo sin la parte del directorio
fileSize 
El tamaño del directorio
isDirectory() 
Devuelve verdadero si nsILocalFile representa un directorio
delete(recursivo) 
Borra un archivo. Si el parámetro recursivo es true, también permite borrar un directorio y todos sus archivos y subdirectorios.
copyTo(directorio,nuevonombre) 
Copia un archivo a otro directorio, puede usarse también para renombrar el archivo. El parámetro directorio debería ser un nsILocalFile que sirva como apuntador del directorio donde se quiere copiar el archivo.
moveTo(directorio,nuevonombre) 
Permite mover un archivo a otro directorio, o renombrar el archivo. El parámetro directorio debería ser un nsILocalFile apuntando al directorio al que se quiere mover el archivo.

Para borrar un archivo primero tenemos que asignarlo al nsILocalFile. Podemos llamar al método initWithPath para indicar el archivo al que nos referimos. Simplemente asigna la ruta del archivo a su propiedad. Después, llamamos a la función delete. Esta función toma un parámetro que indica si debe borrar de forma recursiva. El siguiente código sirve como demostración de estos dos pasos:

var unArchivo = Components.classes["@mozilla.org/file/local;1"].createInstance();
if (unArchivo instanceof Components.interfaces.nsILocalFile){
  unArchivo.initWithPath("/mozilla/archivoprueba.txt");
  unArchivo.delete(false);
}

Este código obtiene el archivo de /mozilla/testfile.txt y lo borra. Prueba este ejemplo añadiendo este código a un manejador de evento. Deberías cambiar el nombre de archivo a un archivo existente en tu sistema de archivos y que quieras borrar.

En la tabla de funciones anterior, verás dos funciones copyTo y moveTo. Estas dos funciones puede ser utilizadas para copiar archivos y mover archivos respectivamente. Ten en cuenta que estos no toman una cadena de caracteres como parámetro indicando el directorio al que copiar o mover el archivo, si no un nsILocalFile. Esto significa que tendrás que obtener dos componentes archivo. El siguiente ejemplo muestra como copiar un archivo.

function copiarArchivo(archivofuente,dirdestino)
{
  // obtenemos un componente para el archivo a copiar
  var unArchivo = Components.classes["@mozilla.org/file/local;1"]
    .createInstance(Components.interfaces.nsILocalFile);
  if (!unArchivo) return false;
 
  // obtenemos un componente para el directorio donde queremos copiar
  var unDirectorio = Components.classes["@mozilla.org/file/local;1"]
    .createInstance(Components.interfaces.nsILocalFile);
  if (!unDirectorio) return false;
 
  // asignamos las URLs a los componentes archivo
  unArchivo.initWithPath(archivofuente);
  unDirectorio.initWithPath(dirdestino);
 
  // finalmente, copiamos el archivo, sin renombrarlo
  unArchivo.copyTo(unDirectorio,null);
}
 
copyFile("/mozilla/archivoprueba.txt","/etc");

Servicios XPCOM

Existen algunos componentes XPCOM especiales llamados servicios. No tienes que crear instancias de ellos porque solo puede existir uno. Los servicios proporcionan funciones de carácter general que pueden obtener o establecer el valor de información global o ejecutar operaciones en otros objetos. En lugar de llamar a createInstance, llamamos a getService para obtener una referencia al componente del servicio. A parte de eso, los servicios no son demasiado diferentes de los demás componentes.

Uno de los servicios que proporciona Mozilla es un servicio de marcadores. Este permite añadir marcadores a la lista de marcadores del usuario. A continuación podemos ver un ejemplo:

var marcadores = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService();
marcadores.QueryInterface(Components.interfaces.nsIBookmarksService);
marcadores.addBookmarkImmediately("https://www.mozilla.org","Mozilla",0,null);

Primero obtenemos el componente “@mozilla.org/browser/bookmarks-service;1�? y su servicio se coloca en una variable marcadores. Utilizamos QueryInterface para obtener la interfaz nsIBookmarksService. La función addBookmarkImmediately proporcionada por esta interfaz puede utilizarse para añadir marcadores. Los dos primeros parámetros de la función son la URL y el título del marcador. El tercer parámetro es el tipo del marcador que valdrá 0 normalmente, y el último parámetro es la codificación de caractéres del documento que estamos añadiendo a marcadores, que puede ser null.

Etiquetas y colaboradores del documento

Etiquetas: 
 Colaboradores en esta página: teoli, Javier crowsoft, Czar, Telco
 Última actualización por: teoli,