Tutorial de WebExtensions (II) – Popup y Tabs

Continuamos con el tutorial de WebExtensions, hoy veremos un elemento muy útil para muchas extensiones que es el popup.

¿Qué es el popup?

El popup es un panel desplegable que se acciona al pulsar un botón de acción. Este panel es una página HTML que puede contener código JavaScript.

popup

El popup es el elemento que permite al usuario un mayor control sobre la extensión y es ideal tanto para mostrar información como para contener acciones. De hecho, WebExtensions limita el número de botones de acción que puede crear una extensión a uno. Si queremos simular tener más botones deberemos usar un popup que despliegue un pequeño menú.

Definir el popup en manifest.json

Para definir el popup tenemos que indicar la ubicación del fichero HTML correspondiente al popup.

	"browser_action" : {
		"default_icon" : "icons/icono_guay.png",
		"default_title" : "Mi PopUp guay",
		"default_popup" : "popup/index.html"
},

Con eso ya tenemos definido el popup. Se mostrará el botón en la barra superior y al hacer click se abrirá el popup.

El PopUp en sí

El PopUp es un fichero HTML sin el mayor misterio. Para definir el tamaño del PopUp usaremos CSS:

body{
    width: 400px;
    height: 400px;
}

Y el navegador adaptará el PopUp a esas dimensiones. A ese archivo HTML le podemos añadir archivos JavaScript mediante el procedimiento habitual en la web, vía una etiqueta script. Lo interesante viene en estos archivos de JavaScript pues cuentan con más privilegios. Desde ellos podemos acceder a las APIs retringidas a extensiones así como librarnos de limitaciones impuestas a las webs (como la política que bloquea los XMLHttpRequests ejecutados a dominios ajenos a nuestra página).

Para usar las APIs restringidas debemos pedir permiso en el fichero manifest.json. Por ejemplo, vamos a jugar con las pestañas. Para ello usamos los permisos tabs y activeTab.

    "permissions" : ["tabs","activeTab"],

Tabs (pestañas) en WebExtensions

La API se encuentra en chrome.tabs y dispone de varios métodos y eventos. Vamos a ver los más importantes:

  • chrome.tabs.query – Obtener información de las pestañas
    • Este método es muy flexible, lo podemos comparar a hacer un SELECT en SQL, pues obtiene una lista de pestañas que cumplen los parámetros que hemos indicado. Por ejemplo para comprobar la pestaña actual, usaremos la propiedad active:
      chrome.tabs.query({"active": true},function(tabs){
          var tab = tabs[0];
          // tab es la pestaña actualmente activa 
      });
      

      Otra opción muy interesante es para averiguar que pestañas tienen abierta una URL que cumple un patrón. Además los parámetros de búsqueda se pueden combinar:

      chrome.tabs.query({"url" : "*://*.adrianistan.eu/*", "status" : "loading", "muted" : true},function(tabs){
        // tabs (que puede estar vacío) contiene todas las URL que estan en un adrianistan.eu (subdominios incluidos),
        // se encuentran cargando
        // y el usuario ha silenciado su sonido
      });
      
  • chrome.tabs.create – Permite crear nuevas pestañas
    • Su uso básico es muy simple
      chrome.tabs.create({"url" : "https://blog.adrianistan.eu/"},function(tab){
        // tab es la pestaña creada
      });
      
  • chrome.tabs.executeScript – Esta función permite inyectar a la pestaña un script de contenido (Content Script).
    • Ahora no vamos a entrar en como se realizaría la comunicación entre los scripts Background y los Content. El código se puede pasar por una cadena de texto o por un archivo, siendo preferible esta última opción. Se puede especificar además cuando queremos que se ejecute dentro del ciclo de carga de la pestaña.
      chrome.tabs.executeScript(tab.id (opcional), {"code" : "alert('Hola');"},function(){
      
      });
      
      O
      
      chrome.tabs.executeScript({"file" : "miarchivo.js", "runAt" : "document_start"},function(){
      
      });
      
  • chrome.tabs.insertCSS – Permite inyectar CSS en la pestaña
    • Su uso es exactamente igual al de chrome.tabs.executeScript
      chrome.tabs.insertCSS({"file" : "misestilos.css"},function(){
      
      });
      
  • chrome.tabs.sendMessage – Enviar un mensaje a la pestaña
    • Hasta ahora no hemos visto la separación entre background y content scripts. Esta función permite enviar datos desde los scripts background a los content, que tendrán una función de escucha. La menciono, pero lo veremos más adelante en profundidad.

Por último, veamos algunos de los eventos que puede generar la API.

  • chrome.tabs.onUpdated – Es llamado cada vez que una pestaña sufre una modificación en sus datos (cambio de URL, quitar el sonido, finalizar una carga, etc)
    • Es muy posible que necesitemos filtrar el contenido que nos da, para fijarnos si se ha producido en una URL que nos interesa o en alguna condición relevante. Eso no obstante, es responsabilidad del programador.
      chrome.tabs.onUpdated.addListener(function(tabId,changeInfo,tab){
        // el objeto changeInfo presenta una estructura similar al de búsqueda con chrome.tabs.query
        // este objeto solo contiene valores para aquellos parámetros que han cambiado
        // con tab podemos acceder a la pestaña al completo
      });
      

Con esto ya podemos hacer extensiones interesantes, la APIs de PopUp y de Tabs son de las más usadas en WebExtensions.

loading...

Tutorial de WebExtensions (I) – manifest.json y conceptos

WebExtensions es un sistema para desarrollar extensiones de navegador. Realmente se trata de la API original de Google Chrome y Chromium. Esta API ya había sido diseñada teniendo en cuenta las extensiones de Firefox basadas en XUL y XPCOM. Simplifica mucho el proceso de tareas comunes, no requiere reinicios y es más segura al no permitir tanto control sobre el navegador.

WebExtensions
Tomada de http://tenfourfox.blogspot.com.es/2015/08/okay-you-want-webextensions-api.html

Posteriormente Firefox respondería con Jetpack y Addon SDK, un sistema similar pero escrito encima de XUL y XPCOM, incompatible pero que no necesitaba reinicios y tampoco perdía potencia pues las APIs de XPCOM seguían allí.

Luego Opera decide abandonar Presto y usar Blink, el motor de Chromium. Con respecto a su sistema de extensiones, planean ser 100% compatibles con Chrome.

Firefox también ha de realizar cambios internos, concretamente se inició el trabajo (electrolysis) de hacer Firefix multiproceso. Esto rompía gran parte de las APIs internas de Firefox. Todos los complementos necesitarían ser reescritos. Ya de paso, teniendo en cuenta que hay que diseñar una nueva API para extensiones, se toma la de Chrome y se renombra WebExtensions. Posteriormente Microsoft afirma que Edge será compatible con extensiones de Chrome o WebExtensions. Esta es la historia de como 4 navegadores se pusieron de acuerdo en estandarizar la API de desarrollo de extensiones para navegadores.

En esta serie de tutoriales vamos a ver como programar nuestra extensión usando WebExtensions.

manifest.json

El fichero manifest.json de una extensión es muy importante y es obligatorio. Contiene metadatos de la extensión así como permisos de los que dispone la extensión y las acciones que define.

En primer lugar vamos a distinguir entre distintos tipos de scripts distintos.

  • Background scripts: estos scripts se ejecutan en el fondo del navegador. Se ejecutan al iniciar el navegador. Podemos configurar eventos y entonces se mantendrán a la escucha durante toda la sesión. Estos scripts no pueden acceder a las páginas web.
  • Content scripts: estos scripts se añaden a determinadas páginas según un patrón en el archivo manifest.json o desde un background script. Estos scripts interactúan directamente con las páginas en los que han sido adjuntados.

Veamos ahora un fichero manifest.json

 

{
	"manifest_version" : 2,
	"name" : "Bc.vc shortener",
	"version" : "2.0.0",
	"description" : "Shorthener for Bc.vc",
	"homepage_url" : "http://adrianistan.eu/norax/",
	"icons" : {
		"16" : "icon-16.png",
		"48" : "icon-48.png",
		"64" : "icon-64.png",
		"96" : "icon-96.png",
		"128" : "icon-128.png"
	},
	"applications" : {
		"gecko" : {
			"id" : "@bc-vc",
			"strict_min_version" : "45.*"
		}
	},
	"permissions" : ["notifications","contextMenus","clipboardWrite","activeTab","storage","http://bc.vc/"],
	"browser_action" : {
		"default_title" : "Bc.vc shortener",
		"default_icon" : "icon-64.png"
	},
	"background" : {
		"scripts" : ["background/install.js","background/main.js"]
	},
	"options_ui" : {
		"page" : "options/options.html",
		"chrome_style" : true
	}
}

manifest_version siempre es 2. Al principio rellenamos metadatos sencillos, name, description, version, homepage_url,… Posteriormente debemos añadir los iconos. Dependiendo de lo que queramos no hacen falta todos los iconos. Por ejemplo, el icono de 16×16 solo es necesario si nuestra extensión define un menú contextual del botón derecho. La sección applications ha sido añadida por Mozilla y originalmente no está en Chrome. Define la compatibilidad entre distintos navegadores y versiones, sin embargo, por ahora solo Firefox tiene en cuenta esto.

A continuación se definen los permisos. Una extensión necesita pedir permiso para acceder a ciertas APIs más privilegiadas. Por ejemplo, para mostrar notificaciones o para realizar peticiones HTTP a servidores remotos.

A continuación vemos la acción del navegador. A diferencia de otros sistemas de extensiones, WebExtensiones solo permite un botón en la interfaz del navegador. Es una manera excelente de separar funcionalidades, aunque si vienes de Firefox verás como limita a muchas extensiones, algunas de las cuáles modificaban la interfaz por completo. La acción del navegador puede hacer dos cosas: abrir un panel popup o generar un evento en el background script. Dependiendo de como sea la experiencia de usuario que prefieras te convendrá una opción u otra (en este caso, como no hemos definido un popup html default_popup, tendremos que escuchar el evento browserAction en un background script). Similar a browser_action existe page_action, la única diferencia es el lugar donde aparece el botón.

Page Action a la izquierda
Browser Action a la derecha

Después podemos asignar archivos de background scripts. Por último options_ui es la manera adecuada de crear una página de opciones. Será una página HTML, pero de eso hablaremos en otro tutorial.

Por último vamos a ver los content scripts

"content_scripts": [
  {
    "matches": ["*://*.mozilla.org/*"],
    "js": ["borderify.js"],
    "css" : ["style.css"],
    "all_frames" : false,
    "run_at" : "document_end"
  }
]

Una manera de añadir content scripts es desde el manifest.json, especificando un patrón en matches. Podemos añadir JavaScript y CSS.

Otra opción es web_accesible_resources. Se trata de elementos de la extensión que pueden ser cargados desde una página web. Por ejemplo, si un content script quiere mostrar una imagen en una web, esta deberá estar indicada en web_accesible_resources.

"web_accesible_resources" : ["imagen/zorro.png"]

...

chrome.extension.getURL("imagen/zorro.png"); // desde el content script

Este fichero es el más importante de una WebExtension. En el siguiente tutorial veremos los background scripts combinados con browser actions.