Firefox add-on programming

De Wikijoan
Dreceres ràpides: navegació, cerca

Contingut

Introducció

Per què vull programar un add-on per al Firefox? Es tracta d'automatitzar la còpia d'informació de la web (Ctrl-A, Ctrl-C) i volcar-ho sobre un fitxer. Tot això amb la menor combinació de tecles possibles i sense haver de canviar de pantalla.

Notes prèvies

El meu objectiu és que tinc el plugin AppLauncher, que va molt bé, i el que vull és poder associar una drecera del teclat per executar aquest plugin. Si ho aconsegueixo serà una bona manera de guanyar rapidesa en la web data extraction.

...Doncs al final he trobat la solució de programar el meu propi add-on per executar un bash script i associar-li una drecera del teclat, per tant no necessito el add-on AppLauncher

Add-on SDK

Hi ha vàries aproximacions per progrmar add-ons, jo utilitzaré el SDK (que no és el XUL...)

Instal.lació:

El primer que faig és actualitzar el Firefox. Tenia la versió 11.0, i ara ja tinc la versió 19.0.

Em descarrego addon-sdk-1.13.2

$ source bin/activate

Canvia el prompt a:

(addon-sdk-1.13.2)joan@joanillo:~/Downloads/addon-sdk-1.13.2$
$ cfx testall

per córrer aquest test és necessari que el Firefox estigui actualitzat, i que la versió del Firefox es correpongui amb la versió del SDK.

Llegeixo la documentació bàsica, cfx init, cfx run.

Faig els exemples senzills: add-on clickme (Add a toolbar button) i l'exemple menuitems (Add a menu item to Firefox)

Em centro ja en l'aspecte que m'interessa: executar un bash script des del Firefox:

i obtinc un resultat satisfactori. Ho ajunto amb l'exemple del Hotkeys per tal de programar les dreceres del teclat:

Associo l'execució del script a la combinació Alt-D, i això significa que amb el add-on customizable shortcuts que tinc instal.lat he d'alliberar la combinació Alt-D.


El add-on executar_script (~/Downloads/addon-sdk-1.13.2/packages/executar_script) queda de la següent forma:

main.js v1

main.js:

//http://stackoverflow.com/questions/11956753/how-to-get-set-working-directory-for-nsiprocess-runasync
var { Hotkey } = require("sdk/hotkeys");
 
var showHotKey = Hotkey({
  combo: "alt-d",
  onPress: function() {
    runBashScript("/home/joan/hola.sh");
  }
});

function runBashScript(scriptFile) {
	const {Cc,Ci} = require("chrome");
    var localFile = Cc["@mozilla.org/file/local;1"]
        .createInstance(Ci.nsILocalFile)
    localFile.initWithPath('/bin/bash');

    var process = Cc["@mozilla.org/process/util;1"]
        .createInstance(Ci.nsIProcess);
    process.init(localFile);

    var args = [ scriptFile ];
    var rc = process.runAsync(args, args.length, 
        function(subject, topic, data) {
            console.log('subject=' + subject + ', topic=' + topic + ', data=' + data);
            console.log('bash script finished executing, returned ' + process.exitValue);
    });

    return rc;
}

Estic utilitzant per provar-lo l'entorn SDK. Ara falta que funcioni en el Firefox de vertitat. S'ha de publicar.

$ cfx xpi

ara ja tinc el fitxer executar_script.xpi

El directori on s'han d'instal.lar és efectivament /home/joan/.mozilla/firefox/mevel791.default/extensions/, però no funciona copiar tal qual el fitxer xpi en aquest directori. S'ha d'instal.lar. Hi ha vàries maneres de fer-ho manualment, però a mi el que m'ha funcionat és fer-ho de forma gràfica. Amb el Nautilus, des del directori packages/executar_script on tinc el executar_script.xpi, arrossego el fitxer fins al Firefox, i aleshores apareix el quadre de diàleg de la instal.lació dels add-ons. Efectivament queda instal.lat, però amb un nom diferent:

~/.mozilla/firefox/mevel791.default/extensions$ ls -la
...
-rw-r--r--  1 joan joan   90166 2013-02-26 01:19 jid1-pVzByfENzpRwGA@jetpack.xpi
-rw-r--r--  1 joan joan  100568 2013-02-26 01:19 jid1-ROwR0HGQBmUjKQ@jetpack.xpi

Funciona correctament.

main.js v2

Millora, ara podem cridar a un binari en comptes d'un script bash.

main.js:

//http://stackoverflow.com/questions/11956753/how-to-get-set-working-directory-for-nsiprocess-runasync
var { Hotkey } = require("sdk/hotkeys");
 
var showHotKey = Hotkey({
  combo: "alt-d",
  onPress: function() {
    //runBashScript("/home/joan/hola.sh");
    runBinaryFile("/home/joan/jmining/bd/juridica/granollers/src/xclip_automation");
    //runBashScript("/home/joan/hola2.sh");
  }
});

function runBashScript(scriptFile) {
	const {Cc,Ci} = require("chrome");
    var localFile = Cc["@mozilla.org/file/local;1"]
        .createInstance(Ci.nsILocalFile)
    localFile.initWithPath('/bin/bash');

    var process = Cc["@mozilla.org/process/util;1"]
        .createInstance(Ci.nsIProcess);
    process.init(localFile);

    var args = [ scriptFile ];
    var rc = process.runAsync(args, args.length, 
        function(subject, topic, data) {
            console.log('subject=' + subject + ', topic=' + topic + ', data=' + data);
            console.log('bash script finished executing, returned ' + process.exitValue);
    });

    return rc;
}

function runBinaryFile(scriptFile) {
	const {Cc,Ci} = require("chrome");
    var localFile = Cc["@mozilla.org/file/local;1"]
        .createInstance(Ci.nsILocalFile)
    localFile.initWithPath(scriptFile);

    var process = Cc["@mozilla.org/process/util;1"]
        .createInstance(Ci.nsIProcess);
    process.init(localFile);

    var args = [ ];
    var rc = process.runAsync(args, args.length, 
        function(subject, topic, data) {
            console.log('subject=' + subject + ', topic=' + topic + ', data=' + data);
            console.log('bash script finished executing, returned ' + process.exitValue);
    });

    return rc;
}

Ha costat bastant tenir un funcionament correcte. El binari C crida al script todo_granollers.sh:

#!/bin/bash
win=$(xdotool search --title Wikijoan | head -1)
xdotool windowactivate $win
sleep 1
xdotool windowfocus $win
xdotool key ctrl+a
xdotool key ctrl+c
xclip -o >> /home/joan/jmining/bd/juridica/granollers/src/todo_granollers.txt

i hem hagut d'introduir la comanda windowfocus i un sleep per tal de què funcioni correctament, o sigui que és una mica art fer-ho funcionar. Executar directament el script bash en comptes de fer-ho a través del binari C crec que no dóna tants de problemes.

Si el que vull és copiar el codi font de la pàgina web faré servir el scrpt todo_granollers2.sh:

#!/bin/bash
win=$(xdotool search --title Wikijoan | head -1)
xdotool windowactivate $win
sleep 0.3
xdotool windowfocus $win
xdotool key ctrl+u #codi font
win=$(xdotool search --title Source | head -1)
xdotool windowactivate $win
sleep 0.3
xdotool windowfocus $win
xdotool key ctrl+a
xdotool key ctrl+c
xclip -o >> /home/joan/jmining/bd/juridica/granollers/src/todo_granollers.txt
sleep 0.3
xdotool key ctrl+w

Resum

Mode comanda

cd ~/Downloads/addon-sdk-1.13.2/

Primer de tot, he d'entrar en mode SDK

$ source bin/activate
Welcome to the Add-on SDK. Run 'cfx docs' for assistance

Vaig a modificar el add-on que m'interessa:

cd packages/executar_script/
joe lib/main.js #modificar-lo
cfx run #testejar-lo si vull comprovar el funcionament.
cfx xpi #generar el xpi

Mode gràfic:

En la shell ja puc sortir del mode SDK:

(addon-sdk-1.13.2)joan@joanillo:~/Downloads/addon-sdk-1.13.2/packages/executar_script$ deactivate

pagweb2mail

Anem a fer el addon pagweb2mail com a compendi de tot allò vist. El problema que es planteja és que tinc una taula amb una llista de pagweb i sense mail. Per ex:

select pagweb from taula where (pagweb is not null or pagweb<>'') and (mail is NULL or mail='') and id > 300 limit 1

select pagweb, collegi, id from advocat where (pagweb is not null or pagweb<>'') and (mail is NULL or mail='') and cercar_mail is NULL and pagweb > '".$pagweb."' order by pagweb limit 1

La idea és que jo treballo amb el Firefox, i tinc definides per exemple dues hotkeys: alt-a i alt-d. Amb alt-a es pretén navegar a la següent pagweb. Quan estic a la pagweb cerco el mail (potser no està a index.htm sinó a contactar.htm, etc...). Quan veig que existeix el mail, aleshores amb alt-d faig una selecció del contingut (codi font). En qualsevol cas, s'ha d'alimentar un fitxer que serà processat a posteriori.

Primer de tot amb el SDK creem el projecte:

source /bin/activate
cd packages
mkdr pagweb2mail
cfx init

El fitxer lib/main.js és l'important. Està buit. El podem copiar d'un altre projecte que ens serveixi com a plantilla.

El meu fitxer queda, després de proves i desenvolupament: main.js:


var pagweb;

var tabs = require('sdk/tabs');
var tab = tabs[0];

var { Hotkey } = require("sdk/hotkeys");
var Request = require("sdk/request").Request;
seguentPagweb('');

var showHotKey = Hotkey({
  combo: "alt-d",
  onPress: function() {
    runBashScript("/home/joan/jmining/bd/juridica/general/pagweb2mail/pagweb2mail.sh", urlweb1);
  }
});

var showHotKey = Hotkey({
  combo: "alt-a",
  onPress: function() {
    seguentPagweb(toweb);
  }
});

function seguentPagweb(urlweb) {
	console.log('Anem a cercar la següent de: ' + urlweb);

	var pagweb = Request({
		url: 'http://localhost/jmining/automation/pagweb2mail.php?pagweb='+urlweb,
		overrideMimeType: "text/plain; charset=latin1",
		onComplete: function (response) {
		console.log(response.text);
		tab.url = response.text;
		toweb = response.text;
		urlweb1='#####' + toweb + '#####'; //enviaré aquesta informació al txt com a delimitador, juntament amb el codi font
	}
	});
	 
	pagweb.get();tab.url = response.text;
}

function runBashScript(scriptFile, urlweb) {
	const {Cc,Ci} = require("chrome");
    var localFile = Cc["@mozilla.org/file/local;1"]
        .createInstance(Ci.nsILocalFile)
    localFile.initWithPath('/bin/bash');

    var process = Cc["@mozilla.org/process/util;1"]
        .createInstance(Ci.nsIProcess);
    process.init(localFile);

    var args = [ scriptFile, urlweb];
    var rc = process.runAsync(args, args.length, 
        function(subject, topic, data) {
            console.log('subject=' + subject + ', topic=' + topic + ', data=' + data);
            console.log('bash script finished executing, returned ' + process.exitValue);
    });

    return rc;
}

La manera de funcionar és

cfx run

com sempre, i no val la pena instal.lar el plugin en el Firefox. Podem treballar directament en el SDK.

Explicació: quan iniciem el script cridem a la funció seguentPagweb();. Fa un request al servidor web i a la bd, pagweb2mail.php, buscant la següent pàgina web que tingui el camp cercar_mail is NULL (estic buscant possibles mails dins una llista de pagweb). El script php em retorna la següent pagweb, i naveguem a aquesta pagweb amb la sentència:

tab.url = response.text;

i a més fico aquesta pagweb en dues variables, toweb i urlweb1, aquesta última dins dels caràcters ##### que em servirà de delimitador (veure més endavant).

Un cop carregada la pagweb he de navegar pel site buscant la pàgina que contingui el mail. Pot estar en la pàgina d'entrada (index.html o la que sigui), o pot estar a contactar.html... Quan la trobo s'ha definit el hotkey alt-d que executa el script /home/joan/jmining/bd/juridica/general/pagweb2mail/pagweb2mail.sh passant-li com a argument urlweb1.

Fitxer pagweb2mail.sh:

#!/bin/bash
echo >> /home/joan/jmining/bd/juridica/general/pagweb2mail.txt
echo $1 >> /home/joan/jmining/bd/juridica/general/pagweb2mail.txt
win=$(xdotool search --onlyvisible --name Firefox | head -1)
xdotool windowactivate $win
sleep 0.3
xdotool windowfocus $win
xdotool key ctrl+u #codi font
win=$(xdotool search --title Source | head -1)
xdotool windowactivate $win
sleep 0.3
xdotool windowfocus $win
xdotool key ctrl+a
sleep 0.3
xdotool key ctrl+c
xclip -o >> /home/joan/jmining/bd/juridica/general/pagweb2mail.txt
sleep 0.3
xdotool key ctrl+w

Veiem quina és el directori del projecte on està el script i on omplirem la informació: /home/joan/jmining/bd/juridica/general/. Aquest script s'encarrega de buscar el codi font (ctrl-u), i fer seleccionar tot (ctrl-a), copiar (ctrl-c), i omplir el fitxer pagweb2mail.txt que serà processat a posteriori. Finalment ctrl-w per tancar la pàgina del codi font.

Ja puc anar a la següent pàgina amb alt-a, que crida a la funció que havíem cridat al principi, seguentPagweb(toweb), però ara li passem la pagweb actual, i el response del script php ens donarà la pagweb següent, i tornem a començar.

A mida que anem processant les pàgines el script php fica el camp cercar_mail=1, indicant que aquesta pagweb ja l'hem processat i per tant no cal tornar-ho a fer. D'aquesta manera ens evitem repetir la feina.

El script pagweb2mail.php del que hem parlat queda de la següent manera: pagweb2mail.php:

<?php include("../open_db.php"); ?>
<?php
//select pagweb, collegi, id from advocat where (pagweb is not null or pagweb<>'') and (mail is NULL or mail='') and cercar_mail is NULL and pagweb > 'www.solermore.com' order by pagweb;

$pagweb = $_GET["pagweb"];

$sql = "select pagweb, collegi, id from advocat where (pagweb is not null or pagweb<>'') and (mail is NULL or mail='') and cercar_mail is NULL and pagweb > '".$pagweb."' order by pagweb limit 1";
//echo $sql;
$result = mysql_query($sql);

if (!$result) {
    $message  = 'Invalid query: ' . mysql_error() . "\n";
    die($message);
}

while ($row = mysql_fetch_assoc($result)) {
	echo $row['pagweb'];
//hauré de fer update advocat set cercar_mail=1 where ... per tal de marcar que aquesta pagweb ja l'he processada.
$sql = "update advocat set cercar_mail=1 where pagweb='".$row['pagweb']."'";
//echo $sql;
$result2 = mysql_query($sql);
}



?>
<?php include("../close_db.php"); ?>

Aquest és un mètode general per cercar mails allà on tenim llistes de pagwebs en una taula. Ara falta processar el fitxer pagweb2mail.txt amb el binari pagweb2mail.c per tal d'omplir el fitxer pagweb2mail.sql on hi haurà els updates per omplir la base de dades amb els mails trobats, del tipus:

update taula set mail='pepito@pepito.com' where pagweb='www.pepito.com'

És bo que el procés sigui semimanual/semiautomàtic, doncs tot el procés des del principi fins al final és procliu a errors.

Un altre exemple: cercar_mails

Tinc una taula amb uns registres que sé que tenen mail, però a la pàgina web no s'hi pot accedir pel codi font. La única manera és fent Ctrl-A, Ctrl-C de la pàgina, no del codi font (on no apareix el mail). Aquesta és una manera d'obtenir informació allà on AJAX no deixa cercar la informació.

A més, com a novetat, aquest script és desatès, va fent la select sobre la taula. L'usuari no interacciona amb l'ús de les tecles com en el cas anterior. Es pot interrompre i es reinicia allà on es va acabar. I al final de tot quan ha acabat la feina tanca el script de forma educada. La novetat d'aquest script és l'ús de dos timers. Amb el primer es pretén esperar un temps prudencial per tal de què es carregui la pàgina. Quan s'ha acabat de carregar la pàgina cridem al script bash que farà la tasca de copiar i enganxar a un fitxer el text de la web; alhora que iniciem un altre timer que ens portarà a la següent web.

main.js:

var pagweb;

//https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsITimer?redirectlocale=en-US&redirectslug=nsITimer
const {Cc,Ci} = require("chrome");
var timer1 = Cc["@mozilla.org/timer;1"]
    .createInstance(Ci.nsITimer)
var timer2 = Cc["@mozilla.org/timer;1"]
    .createInstance(Ci.nsITimer)

var tabs = require('sdk/tabs');
var tab = tabs[0];

var system = require("sdk/system");
var Request = require("sdk/request").Request;

seguentPagweb('');


function seguentPagweb(urlweb) {
    console.log('Anem a cercar la següent de: ' + urlweb);

	var pagweb = Request({
		url: 'http://localhost/jmining/automation/cercar_mails_advocats_tenerife.php?extra='+urlweb,
		overrideMimeType: "text/plain; charset=latin1",
		onComplete: function (response) {
    		console.log(response.text);
    		tab.url = 'file:///home/joan/jmining/bd/juridica/tenerife/src/' + response.text;
    		toweb = response.text;

            if (strcmp(toweb,'reg')==1) {
                timer1.initWithCallback(event1,4000, Ci.nsITimer.TYPE_ONE_SHOT);
            } else {
                console.log('*** FI DEL SCRIPT ***');
                //quit the host application:
                //https://addons.mozilla.org/en-US/developers/docs/sdk/1.13/modules/sdk/system.html
                system.exit();
        	}
        }
	});

	pagweb.get();

}

function runBashScript(scriptFile) {
	const {Cc,Ci} = require("chrome");
    var localFile = Cc["@mozilla.org/file/local;1"]
        .createInstance(Ci.nsILocalFile)
    localFile.initWithPath('/bin/bash');

    var process = Cc["@mozilla.org/process/util;1"]
        .createInstance(Ci.nsIProcess);
    process.init(localFile);

    var args = [ scriptFile ];
    var rc = process.runAsync(args, args.length, 
        function(subject, topic, data) {
            console.log('subject=' + subject + ', topic=' + topic + ', data=' + data);
            console.log('bash script finished executing, returned ' + process.exitValue);
    });

    return rc;
}


var event1 = {
  notify: function(timer) {
    console.log('ja puc executar el script');
    runBashScript("/home/joan/jmining/bd/juridica/tenerife/src/cercar_mails_advocats_tenerife.sh");
    console.log('ja puc anar al timer2');
    timer2.initWithCallback(event2,4000, Ci.nsITimer.TYPE_ONE_SHOT);

  }
}

var event2 = {
  notify: function(timer) {
    console.log('ja puc anar a ' + toweb);
    seguentPagweb(toweb);
  }
}

function strcmp ( str1, str2 ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Waldo Malqui Silva
    // +      input by: Steve Hilder
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    revised by: gorthaur
    // *     example 1: strcmp( 'waldo', 'owald' );
    // *     returns 1: 1
    // *     example 2: strcmp( 'owald', 'waldo' );
    // *     returns 2: -1
    return ( ( str1 == str2 ) ? 0 : ( ( str1 > str2 ) ? 1 : -1 ) );
}

Per completar el codi falta el script php que fa la consulta a la base de dades; i el bash script. És un cas acadèmci que s'ha fet per trobar advocats de Tenerife.

Com a resultat es genera un fitxer txt que es processa per obtenir la informació. A diferència del cas de Granollers, ara no tenim el codi font, sinó senzillament el text de les pàgines. Amb això après es pot millorar el codi de Granollers per tal de fer-lo desatès.


creat per Joan Quintana Compte, febrer 2013

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
IES Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines