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

Creare Estensioni personalizzate per Firefox con il sistema di sviluppo di Mozilla

 

Questa pagina è in fase di traduzione: contribuisci anche tu completando le parti mancanti. Il testo da tradurre potrebbe essere nascosto nella pagina: vai in modifica per visualizzarlo

 

Nota: Tutte le istruzioni contenute in questo articolo si applicano solo al ramo di Mozilla 1.8 (cioè Firefox 1.5). Cercheremo di tenere questo articolo aggiornato ai cambiamenti. Ciò che può sicuramente essere escluso è che funzioni con i rami 1.7 (firefox 1.0) o precedenti.

Esiste un'abbondanza di materiale su come creare un'estensione per Firefox. Tutta questa documentazione però presume che si stia sviluppando un'estensione utilizzando esclusivamente XUL e JavaScript. Per estensioni più complesse, potrebbe essere necessario creare dei componenti in C++ per fornire funzionalità aggiuntive. Le ragioni per la quale potrebbe essere necessario includere componenti C++ nell'estensione, possono essere:

  • Necessità di ottenere alte prestazioni al di là di quanto possa essere effettuato con codice JavaScript.
  • L'utilizzo di librerie di terze parti scritte in C o C++.
  • L'utilizzo di interfacce di Mozilla che non sono accessibile attraverso XPCOM (es. NSPR).

Questo articolo descrive come impostare l'ambiente di sviluppo per un'estensioni di Firefox grande e complessa con alcuni o tutti i requisiti menzionati più sotto. Il processo di raccolta di queste informazioni è stato un pò faticoso a causa della mancanza di informazioni pubblicate su questo argomento, ma è stato assistito da vari membri della comunità di sviluppo di Mozilla, che hanno mostrato una pazienza estrema nel rispondere alle domande di novizi impreparati. In quest'articolo potrebbero esserci molte cose inesatte, poco chiare o non accurate. L'obbiettivo è quello di limare queste istruzioni fino a farle divenire una guida definitiva per seri programmatori che intendano estendere la piattaforma di Firefox. Se sei uno dei tanti che ne sanno di più, il tuo aiuto per migliorare questo articolo sarà enormemente apprezzato.

Devo ricordare chenon è necessario compilare Mozilla o usare il Mozilla build system se si vogliono creare componenti C++ per Mozilla. Se si vuole solo creare un componente XPCOM o due, questa è probailmente una guida troppo elaborata, e si farebbe meglio a dare invece uno sguardo a questo documento. D'altra parte, si si è uno sviluppatore (o un team di sviluppo) con una certa esperienza, e si sa che si andrà a realizzare una estensione grande e complessa, si può tenere in considerazione l'approccio descritto in questo documento.

Un'ultima nota: ho provato queste tecniche solo con Firefox, ma probabilmente funzionano più o meno bene anche con altre piattaforme basate su Gecko, come Thunderbird o Seamonkey. Se qualcuno può confermarmelo e/o fornire linee guida su cosa differisce, Aggiorenrò l'articolo per incorporare tali informazioni.

Bambi Incontra Mozilla

Niente di questo doocumento è per i deboli di cuore, In particolare, i passi iniziali richiedono di effettuare il build di Mozilla, che è un enorme - no, gargantuesco! - progetto. Più di uno sviluppatore è stato portato sull'orlo della pazzia provando ad effettuare il build per la prima volta. Se non si è sviluppatori C++ esperti, non me ne preoccuperò. Passate a JavaScript.

Su piattaforme Windows

Per il mio primo build di Mozilla ho usato questa guida. Non riesco a ricordare perché, ma mi sono bloccato per diverse maniere, ed il tutto è terminato solo dopo aver preso molto più tempo di quanto avessi preventivato. Ecco una guida di insieme che ha ricevuto buone critiche. Seguite metodicamente ogni passo e probabilmente avrete la meglio. Tenete in mente che una volta fatto funzionare il build, probabilmente da lì in poi potrete lavorare senza sforzi. Forse.

I can’t even remember why anymore, but I got stuck in a number of places, and the whole affair ended up taking far longer than I originally expected. Much furniture was smashed, much hair torn out by the roots. Here’s a comprehensive looking guide that’s gotten good reviews. Follow every step methodically and you’ll probably be alright. Focus on the fact that once you get the build working, it’ll probably work effortlessly from then on. Maybe.

Su altre piattaforme

Non ho mai provato ad effettuare build su altre piattaforme, per cui non so nulla di questo. Immagino comunuque che nei sistemi Unix-like il procedimento sia più semplice. Si può consultare questa lista completa di piattaforme per cui sono disponibili istruzioni per il building.

Strutturare il proprio progetto

Mozilla include un insieme di estensioni complesse che sono integrate nel suo processo di build. Per questo motivo si rende necessario risolvere tutti i problemi correlati alla creazione e registrazione di componenti XPCOM, build di file JAR e manifests, installazione del tutto nella directory extensions/ di Firefox e così via. Si rende quindi necessario fare riferimento a questa struttura per costruire la nostra estensione.

Prima di tutto, pensiamo ad un nome accattivante per l'estensione e creiamo la directory con quel nome come sottodirectory di /mozilla/extensions/. Usare solo lettere minuscole. Ci dovrebbe essere un insieme di altre directory (inspector/, reporter/ e così via) nello stesso livello dell'albero di build.


Notare che prima di effettuare il build di qualcosa, il build system di Mozilla invoca un processo di configurazione che genera i makefile usati per effettuare il build, a partire dallo schema di makefile chiamato Makefile.in. Il makefile attuale tende ad essere molto simile o anche identico allo schema, ma la maggiore flessibilità data dalla sua generaziione automatica è uno dei punti che rendono il sistema di build così potente.

Anatomia di una semplice estensione C++

Assumiamo stiate usando C++ per scrivere componenti XPCOM che possono essere usati da altri componenti C++ o da JavaScript. Il processo di creazione di un componente è relativamente lineare, utilizzando il build system di Mozilla.

Nel caso più semplice, un componente consisterà di una singola directory principale con due sottodirectory, public/ e src/. La directory principale deve contenere un Makefile.in (da ora in poi ci riferiremo a questo come al makefile anche se sappiamo che viene usato per generare quello vero). Questo Makefile ci dà due informazioni. Primo, elenca le sotodirectory che compongono l'estensione, di modo che il build system sa dove cercare per makefile aggiuntivi. Secondo, istruisce il build system sul creare una nuova estensione, piuttosto che copiare i componenti direttamente nella directory binaria di Firefox. Il primo vantaggio di usare una estensione è che diventa veramente facile realizzare un pacchetto con tutto il necessario ed installarlo su un'altra macchina.

Detto ciò ecco il makefile di base, puro e semplice (Makefile.in nella directory principale dell'estensione):

DEPTH		= ../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
	
DIRS		= public src
	
XPI_NAME		= myextension
INSTALL_EXTENSION_ID	= [email protected]
XPI_PKGNAME		= myextension
	
DIST_FILES = install.rdf
	
include $(topsrcdir)/config/rules.mk

Una descrizione dettagliata del processo di build, che descrive le caratteristiche chiave di questo makefile, può essere trovata a questo indirizzo. MODULE e XPI_NAME sono entrambi impostati come il nome della propria estensione. sono ripetuti in tutti i makefile del progetto di modo che ogni file sia inserito nella stessa posizione all'interno dell'area operativa XPI (XPI staging area , v. sotto). INSTALL_EXTENSION_ID è l'ID univoco della propria estensione. Questo può essere un GUID, ma il formato visto sopra è più grazioso e, diciamocelo, molto più semplice da ricordare. Non è necessario fornire un XPI_PKGNAME, ma se si crea un file XPI, pronto per la distribuzione, viene automaticamente creato nella radice dell'area operativa XPI (/mozilla/$(MOZ_OBJDIR)/dist/xpi-stage/).

Ogni estensione deve includere un file install.rdf che indica a Firefox come deve essere installata. Questo file dovrebbe essere posizionato nella directory principale dell'estensione ed essere simile a questo:

<?xml version=\"1.0\"?>
	
<RDF xmlns=\"https://www.w3.org/1999/02/22-rdf-syntax-ns#\"
     xmlns:em=\"https://www.mozilla.org/2004/em-rdf#\">
  <Description about=\"urn:mozilla:install-manifest\">
    <em:id>[email protected]</em:id>
    <em:version>0.1</em:version>
	
    <em:targetApplication>
      <!-- Firefox -->
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>1.0+</em:minVersion>
        <em:maxVersion>1.0+</em:maxVersion>
      </Description>
    </em:targetApplication>
	
    <!-- front-end metadata -->
    <em:name>My First Extension</em:name>
    <em:description>Just an example.</em:description>
    <em:creator>allpeers.com</em:creator>
    <em:homepageURL>https://www.allpeers.com/blog/</em:homepageURL>
  </Description>
</RDF>

MozillaZine ha una descrizione dettagliata del formato del file install.rdf. Usare la variabile DIST_FILES nel makefile per dire a make di copiare il file nella directory dell'estensione e (opzionalmente) nel file XPI.

Interfacce pubbliche

La directory public/ contiene ogni interfaccia che deve essere accedibile da altri moduli. Questi possono essere file IDL che descrivono interfacce XPCOM, i quali vengono usati per generare file header C++ per l'inclusione all'interno del proprio file sorgente. Possono anche essere normali file header C++ che vengono usati direttamente da altri moduli. Il modo più facile per permettere questa ultimaoperazione è l'implementazione inline di tutti i metodi, così da non avere dipendenze aggiuntive in fase di linking. Si dovrà altrimenti eseguire illink statico al proprio modulo se si usano questi header pubblici in altri moduli. Personalmente scoraggerei qyesta pratica (tra l'altro, il linking static significa avere lo stesso codice caricato più di una volta in memoria, il quale non sarà poi disponibile per Javascript o altri linguaggi diversi dal C++) e incoraggerei l'uso di XPCOM dove possibile.

Il Makefile nella directory public/ dovrebbe seguire questo modello:

DEPTH		= ../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE		= myextension
XPIDL_MODULE	= myextension
	
XPI_NAME = myextension
	
EXPORTS = \
		myHeader.h \
		$(NULL)
	
XPIDLSRCS	= \
		myIFirstComponent.idl \
		myISecondComponent.idl \
		$(NULL)
	
include $(topsrcdir)/config/rules.mk

XPIDL_MODULE è il nome del file XPT generato che contine informazioni de tipo riguardanto le proprie interfacce IDL. Se si hanno più moduli, assicurarsi assolutamente di usare un valore differente di XPIDL_MODULE per ognuno. In caso contrario il primo modulo XPT verrà sovrascritto dal secondo e si otterrnno errori NS_ERROR_XPC_BAD_IID quando si proverà ad accedere alle interfacce IDL dal proprio codice. I file sotto EXPORTS vengono copiati direttamente nella directory /mozilla/$(MOZ_OBJDIR)/dist/include/myextension/ e sono quindi accedibili da altri moduli (il valore di MOZ_OBJDIR è definito in /mozilla/.mozconfig). XPIDLSRCS sono eseguiti attraverso il processore IDL, e gli header C++ generati vengono copiati nella stesa directory di inclusione. In aggiunta, viene generato un file XPT (tipo libreria) e posizionato nella sottodirectory components/ dell'estensione.

File sorgenti

Ora è il momento di creare il makefile ed i file sorgente nella sottodirectory src/. Se si stanno reimplementando interfacce descritte usando IDL, la maniera più facile di farlo è lasciare la directory src/ vuota ed eseguire make solo nella directory public/; spiegheremo tra breve il perché.

Si può poi aprire il file header generato per la propria interfaccia da /mozilla/$(MOZ_OBJDIR)/dist/include/myextension/. QUesta directory contiene i file .H e .CPP dei componenti che possono essere copiati ed incollati all'interno dei propri file di implementazione. Tutto ciò che c'è da fare è riempire gli abbozzi di implementazione presenti nel file C++ e si è pronti per partire.

Ecco un esempio del makefile da inserire all'interno della directory src:

DEPTH		= ../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@

include $(DEPTH)/config/autoconf.mk

IS_COMPONENT = 1
MODULE = myextension
LIBRARY_NAME =  myExtension

XPI_NAME = myextension

REQUIRES	= xpcom \
		  string \
		  $(NULL)

CPPSRCS		= \
		  myFirstComponent.cpp \
		  mySecondComponent.cpp \
		  myExtension.cpp \
		  $(NULL)

include $(topsrcdir)/config/rules.mk

EXTRA_DSO_LDOPTS += \
  $(XPCOM_GLUE_LDOPTS) \
  $(NSPR_LIBS) \
  $(NULL)

# NOTE: If you are coding against the 1.8.0 branch (not 1.8 branch or trunk), the
# above line won't work, due to linker flag issues. Use the following 
# variables instead:
#
# EXTRA_DSO_LDOPTS += \
#   $(MOZ_COMPONENT_LIBS) \
#   $(NULL)
#
# Unfortunately, using MOZ_COMPONENT_LIBS links against xpcom_core, which means
# your components will not work in future versions of Firefox.

La sezione REQUIRES dice a make quali moduli sono utilizzati dal proprio componente. Questo causa l'aggiunta delle sottodirectory di /mozilla/$(MOZ_OBJDIR)/dist/include/ interessate nel path di inclusione del compilatore C++. Se si stanno includendo header Mozilla ed il compilatore non riesce a trovarli, potrebbe benissimo significare che in REQUIRES non sono stati elencati tutti i moduli necessari. CPPSRCS i file sorgente che devono essere compilati.

In questo esempio, i primi due file contengono l'implementazione dei due componenti dell'estensione. Il fuile finale, myExtension.cpp, contiene il codice necessario per registrare questi componenti, come descritto nella prossima sezione.

Registrare i componenti

Pre poter utilizzare i propri componenti da altri moduli C++ e JavaScript, devono prima essere registrati. Per fare ciò, l'estensione deve implementare una classe che espone l'interfaccia nsIModule, che ha i metodi per accedere ai componenti definiti in un modulo. Fortunatamente, questo può essere fatto attraverso l'uso di semplici macro, in modo da non doversi preoccupare dei dettagli di funzionamento.

Il primo passo è la definizione di un CID, ID di contratto (contract ID ) ed un nome di classe per ogni componente. Inserire il codice seguente (adattando alle proprie esigenze i vari #defines) all'interno dell'header di ogni componente che si vuole istanziare tramite il component manager (gestore dei componenti ):

// {00000000-0000-0000-0000-000000000000}
#define MYFIRSTCOMPONENT_CID \
	{ 0x00000000, 0x0000, 0x0000, \
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
	
#define MYFIRSTCOMPONENT_CONTRACTID	\"@mycompany.com/myfirst;1\"
#define MYFIRSTCOMPONENT_CLASSNAME	\"My First Component\"

Ovviamente è necessario compilare il CID con GUID vero. In Windows, questo può essere fatto usando guidgen.exe. Gli utenti Unix possono usare uuidgen (fornito direttamente da molte distribuzioni unix e Linux).

Si deve poi creare il file myExtension.cpp in modo simile a questo:

#include \"nsXPCOM.h\"
	
#include \"nsIGenericFactory.h\"
	
/**
 * Componenti da registrare
 */
#include \"myFirstComponent.h\"
#include \"mySecondComponent.h\"
	
NS_GENERIC_FACTORY_CONSTRUCTOR(myFirstComponent)
NS_GENERIC_FACTORY_CONSTRUCTOR(mySecondComponent)
	
//----------------------------------------------------------
	
static const nsModuleComponentInfo components[] =
{
	{
		MYFIRSTCOMPONENT_CLASSNAME,
		MYFIRSTCOMPONENT_CID,
		MYFIRSTCOMPONENT_CONTRACTID,
		myFirstComponentConstructor
	},
	{
		MYSECONDCOMPONENT_CLASSNAME,
		MYSECONDCOMPONENT_CID,
		MYSECONDCOMPONENT_CONTRACTID,
		mySecondComponentConstructor
	},
};
	
NS_IMPL_NSGETMODULE(MyExtension, components)

La macro NS_IMPL_NSGETMODULE crea l'oggetto modulo appropriato fornendo l'accesso a tutti i componenti elencati nell'array nsModuleComponentInfo.

Effettuare il build

Come detto prima, probabilmente si vuole effettuare il build di una estensione immediatamente dopo aver creato i propri file IDL in modo da generare gli abbozzi C++ per le proprie implementazioni dei componenti. Assumo che abbiate già compilato con successo Firefox. Se non è così, andate all'inizio di questo articolo e non tornate finché non avrete un firefox.exe fuzionante. Non passate dal via. Non ritirate 20 Euro.

Ancora qui? Okay, ora va modificato il proprio .mozconfig (nella directory radice /mozilla/) di modo che la propria estensione venga compilata con insieme Mozilla. Aggiungere le seguenti linee alla fine del file:

ac_add_options --enable-extensions=default,myextension

Lanciare poi make dalla directory radice di Mozilla:

make -f client.mk build

Anche si si ha una versione compilata recentemente di Firefox, si dovrà attendere per un po' mentre il make visita ricorsivamente tutto l'albero di directory del codice sorgente di Mozilla cercando nuovo materiale (la mia macchina, che è abbastanza veloce, impiega 10-15 minuti). Raggiungerà anche la vostra estensione e genererà una serie di elementi all'interno di /mozilla/$(MOZ_OBJDIR)/:

  • File header file esportati e generati (da IDL) in dist/include/myextension/
  • Librerie statiche per il proprio modulo in dist/lib/ (in altri moduli vogliano effettuare il link static di questi moduli invece di usare XPCOM).
  • File XPI in dist/xpi-stage/myextension.xpi.
  • Makefile generati per il proprio prgetto in extensions/myextension/ (ricordiamo che ci troviamo in /mozilla/$(MOZ_OBJDIR)/).
  • Tutto il resto in dist/bin/extensions/[email protected]/.

Gran parte di questo materiale non è creato in questo primo passo finché make segnalerà di non trovare i file sorgenti per i componenti. Non c'è da preoccuparsi di questo: tutto ciò che serve sono i file header generati che contengono gli abbozzi delle implementazioni C++. Perché la compilazone possa terminare devono essere rimpolpate le implementazioni dei componenti. Va ricordato che non si dovrebbe mai modificare uno di questi file generati automaticamente. Vanno sempre modificati i file usati per generarli e riavviari make. Possono esserci delle eccezioni, a questa regola, ma se si stanno cambiando i file automaticamente generati, molto ptobabilmente si sta sbagliando qualcosa.

Il processo di esplorazione dell'intero albero di Mozilla dura molto tempo. Se si ha già una build di Mozilla, è possibile evitarlo creando un makefile direttamente per ogni estensione. Nella directory radice del proprio $(MOZ_OBJDIR) si digita (da una shell compatibile con bash):

../build/autoconf/make-makefile extensions/myextension

Se la propria $(MOZ_OBJDIR) è posizionata fuori da $(TOPSRCDIR), è necessario digitare:

$(TOPSRCDIR)/build/autoconf/make-makefile -t $(TOPSRCDIR) extensions/myextension

perché lo script sappia dove si trovano i sorgenti (it'll use the extension path you gave it relative to the current dir to figure out where you want your makefiles to go).

Questo genererà il makefile appropriato per l'estensoine. Che si effettui il build di tutto l'albero Mozilla o si prenda questa scorciatoia, da ora in poi è possibile compilare andando in /mozilla/$(MOZ_OBJDIR)/extensions/myextension/ e digitando "make" sulla linea di comando. Questo dovrebbe effettuare il build del proprio componente senza disturbare il resto di Mozilla. Se tutto funziona, si vedrà il proprio file XPI nell'area operativa XPI. Si vedrà anche la versione "esplosa" del pacchetto XPI (ad esempio la struttura decompressa delle directory) all'interno di /mozilla/$(MOZ_OBJDIR)/dist/bin/extensions. (se qualcosa va storto, cercate cosa, risolvetelo e poi tornate qui ed aggiungete la soluzione a questo articolo.)

Per assicurarsi che il processo di build sia veramente finito, lanciare Firefox e controllare che la propria estensione sia elencate selezionando Tools/Extensions. Se si usa Firefox come browser predefinto (e se no, perché?), potrebbe essere necessario chiudere la versione "regolare" di Frefox prima di eseguire quella modificata. Se questo accade, si può provare ad impostare la variabile d'ambiente MOZ_NO_REMOTE al valore "1" prima di eseguire la versione di Firefox di sviluppo. Si avràa bisogno inoltre di usare un profilo differente per questa versione:

firefox -Pdevelopment

Dove è sostituito dal nome del nuovo profilo creato. Questo permetterà di eseguire entrambe le versioni di Firefox simultaneamente, permettendo di risparmiare tempo durante il ciclo di build/test.

Chrome, dolce Chrome

Nota: il titolo originale e' "No Place Like Chrome", una parafrasi di "No Place Like Home", che non ha equivalenti validi in italiano ma che rassomiglia a "Casa dolce Casa" Yippee-ki-yay! Ora abbiamo una estensione che fa, be', assolutamente nulla. E' ora di fare qualcosa con questi fantastici componenti che sono stati implementati e registrati. Il modo più semplice per fare questo è scrivere un po' di codice JavaScript e XUL. A questo punto, sarebbe di aiuto avere un po' di esperienza nella scrittura di estensioni "regolari" (ad esempio, che non utilizzano componenti C++ personali). Se non si ha mai provato, il mio consiglio è di pensare ad una idea carina per qualcosa di semplice che avreste sempre voluto avere in Firefox e scriverla. Anche solo visualizzare un nuovo elemento di menu che apre una finestra di dialogo "Hello, World!" è pur sempre un grande esercizio di riscaldamento.

Assumiamo ora che sappiate scrivere estensioni in XUL/JavaScript, siete a conoscenza del fatto che molte delle parti importanti vanno a finire nella directory chrome/ della vostra estensione. Bene, il fatto che si stiano usando anche componenti C++ non cambia nulla. Per questo bisogna creare le solite directory content/, locale/ e skin/ in cui inserire i propri file chrome. Personalmente mi piace posizionare questi direttamente nella directory radice del modulo, ma suppongo non faccia alcuna differenza se si preferisce inserirli in delle sottodirectory chrome/ o altro ancora. Spazio alla libertà!

Una volta scritti i necessari file chrome (ad esempio un overlay che aggiunga una voce di menu per istanziare ed usare uno dei propri componenti), è necessario inserirli in un pacchetto come parte della propria estensione. Questo si fa utilizzando un Manifest JAR. Per il nostro semplice esempio di estensione, il file potrebbe somigliare a questo:

myextension.jar:
%  content myextension %content/
%  locale myextension en-US %locale/en-US/
%  skin myextension classic/1.0 %skin/classic/
%  overlay chrome://browser/content/browser.xul chrome://myextension/content/MyExtensionOverlay.xul
	content/MyExtensionOverlay.js		(content/MyExtensionOverlay.js)
	content/MyExtensionOverlay.xul		(content/MyExtensionOverlay.xul)
	locale/en-US/MyExtension.dtd		(locale/en-US/MyExtension.dtd)
	locale/en-US/MyExtension.properties	(locale/en-US/MyExtension.properties)
	skin/classic/MyExtension.css		(skin/classic/MyExtension.css)

Questo codice va inserito in un file chiamato jar.mn nella directory radice della propria estensione, assicurandosi che i percorsi tra parentesi puntino ai file attuali (se interpretati in relazione con la directory radice). Deve inoltre essere fatta una piccola modifica al makefile nella stessa directory, aggiungendo la linea:

USE_EXTENSION_MANIFEST = 1

Questo indica al make di creare un singolo file manifest chiamato chrome.manifest invece di creare file manifest con strani nomi per ogni pacchetto.

Dopo aver lanciato ancora make, si dovrebbe veder apparire una nuova sottodirectory chrome nella propria estensione (/mozilla/$(MOZ_OBJDIR)/dist/bin/extensions/[email protected]/). Da notare che la directory chrome contiene un file JAR (ad esempio ZIP) con tutti i file chrome elencati in jar.mn cos'ì come una completa struttura delle directory alla strgua di quella presente nel file JAR. La struttura, comunque, è vuota. Perché? Non lo so. Non preoccupatevi di questo, i file contenuti nel JAR gli unici che vengono utilizzati.

Complicare il tutto

Se si sta sviluppando una estensione veramente complessa con molti componenti XPCOM, probabilmente si vorrà dividere il proprio codice in moduli più piccoli.

Estensioni un po' complicate

Per una estensione moderatamente complicata, probabilmente è sufficiente dividere il codice in un livello singolo di moduli. Assumiamo che abbiate un modulo base/ che definisce un insieme base di componenti XPCOM ed un modulo advanced/ che definisce alcuni chrome così come altri componenti che utilizzano quelli di base. La struttura completa delle directory somiglierà a questa:

  • myextension
    • base
      • public
      • src
    • advanced
      • content
      • locale
        • en-US
        • ...other locales...
      • public
      • skin
        • classic
        • ...other skins...
      • src

Oltre a ciò, non cambia molto altro. I makefile in base/ e advanced/ dovrebbero essere più o meno simili al makefile originale nella directory radice, ricordando di cambiare la variabile DEPTH poiché questi sono stati spostati di un livello di directory rispetto alla radice di Mozilla.

Va inoltre rimossa la variabile DIST_FILES poiché il suo valore viene preso dal makefile di livello più alto. Ogni makefile che genera qualcosa dovrebbe definire la variabile XPI_NAME per assicurarsi che i file generati finiscano nella directory della propria estensione e non nella directory globale components/. Basta comunuque definire la variabile in ogni makefile per stare tranquilli. Si può usare lo stesso valore di MODULE sia in base/ che in advanced/ di modo che tutti i fileinclude vadano nella stessa directory, assicurandosi però di non usare lo stesso stesso valore XPIDL_MODULE nelle due directory public/, o una delle librerie di tipo componenti (ad es. file XPT) sovrascriverà l'altra e si scatenerà l'inferno.

Ogni modulo deve inoltre avere un valore differente per la variabile LIBRARY_NAME. Questo è il nome della libreria dinamica generata, per cui se devono essere richiamate le librerie "myBase" e "myAdvanced", useremo i nomi con estensione myBase.dll e myAdvanced.dll (almeno in Windows). Ognuno dei due moduli andrà ad avere un file C++ separato per la registrazione dei componenti. Per questo motivo ci saranno due file simili a myExtension.cpp visto nell'esempio orginale, diciamo Base.cpp e Advanced.cpp. Infine, ogni modulo avrà ovviamente il proprio jar.mn, anche se essi possono fare riferimento allo stesso nome di file e pacchetto JAR, se si vuole che tutti i file chrome siano organizzati in un singolo pacchetto/file JAR. L'unico file che resta immutato è install.rdf, che ancora esiste unicamente nella directory radice dell'estensione.

Il makefile di livello più alto sarà ora simile a questo:

DEPTH		= ../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
	
DIRS		= base advanced
	
XPI_NAME               = myextension
INSTALL_EXTENSION_ID   = [email protected]
XPI_PKGNAME		= myextension
	
DIST_FILES = install.rdf
	
include $(topsrcdir)/config/rules.mk
Estensioni veramente complicate

Ad un certo momento, anche un singolo modulo potrebbe crescere al punto da dover essere diviso in sottomoduli. La differenza tra avere moduli separati ed avere un modulo singolo con sottomoduli separati è che i sottomoduli condividono lo stesso file per la registrazione dei componenti (il famoso file myExtension.cpp), e se compilato creano una unica libreria dinamica. La decisione di dividere un modulo in sotomoduli riguarda solo l'organizzazione del codice; non avrà ripercussioni sul prodotto finale.

Per dividere un modulo in sottomoduli, va prima creata una sottodirectory per ognuno. Dopodiché va creata una directory build/. Ogni sottomodulo sarà configurato per creare una libreria static, che la directory build riunirà per creare una unica libreria dinamica dei componenti. Confusi? Ecco un esempio, che mostra il ramo advanced/ della directory myextension/:

  • advanced
    • build
    • intricate
      • public
      • src
    • multifarious
      • public
      • src
    • content
    • locale
      • en-US
      • ...other locales...
    • skin
      • classic
      • ...other skins...

Come si vede, abbiamo diviso advanced/ in due sottomoduli: intricate/ e multifarious/, ed abbiamo aggiunto una sottodirectory build/. Abbiamo lasciato le directory chrome direttamente in advanced/, dato che non appartengono a nessun sottomodulo in particolare. QUesto vuol dire che il file jar.mn sarà nello stesso posto.

I makefile di intricate/ and multifarious/ somiglieranno molto al makefile originale di advanced/ anche se devono essere un po' manipolati. COme al solito, va aggiustato il valore della variabile DEPTH dato che i makefile sono ora più in profondità nella struttura delle directory. Andrebbe inoltre cambiato il valore di LIBRARY_NAME per indicare che si sta generando una libreria static per ogni sottomodulo. Per convenzione viene usato il suffisso "_s" a questo scopo . Chiamiamo quindi le librerie "myIntricate_s" e "myMultifarious_s". Infine, definiamo la variabile FORCE_STATIC_LIB, ottenendo un makefile il cui inizio è simile a questo:

DEPTH		= ../../../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
LIBRARY_NAME = myIntricate_s
FORCE_STATIC_LIB = 1
	
XPI_NAME = myextension
	
...altre cose qui...

Il makefile di build compone insieme tutte le librerie statiche generate dai sottomoduli e crea una libreria (dinamica)dei componenti:

DEPTH		= ../../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
IS_COMPONENT = 1
MODULE = myextension
LIBRARY_NAME = myAdvanced
	
XPI_NAME = myextension
	
DEFINES += XPCOM_GLUE
	
SHARED_LIBRARY_LIBS = \
		$(DIST)/lib/$(LIB_PREFIX)myIntricate_s.$(LIB_SUFFIX) \
		$(DIST)/lib/$(LIB_PREFIX)myMultifarious_s.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)xpcom.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)nspr4.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)plds4.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)plc4.$(LIB_SUFFIX) \
		$(NULL)
	
REQUIRES	= \
		xpcom \
		string \
		$(NULL)
	
CPPSRCS		= \
		Advanced.cpp \
		$(NULL)
	
include $(topsrcdir)/config/rules.mk
	
LOCAL_INCLUDES += \
        -I$(srcdir)/../intricate/src \
        -I$(srcdir)/../multifarious/src \
        $(NULL)

Il makefile nella directory advanced/ dovrebbe elencare le directory intricate/, multifarious/ e build/ nel calore della sua variabile DIRS. CIs i deve assicurare che build/ sia elencato come ultimo, dato che non può essere creata la libreria di componenti fino a quando gli altri makefile non hanno terminato.

Altri argomenti

Aggiungere file di dati alle proprie estensioni

In alcuni casi, si potrebbe voler includere file aggiuntivi nella propria estensione che non appartengono alla sottodirectory chrome/. Alcuni esempi potrebbero essere file di database o schemas XML. A questo proposito è possibile aggiungere un passo all'interno del makefile, per copiare i file dall'albero sorgente alla directory di destinazione dell'estensione.

Copiare file di dati nella directory di destinazione

Diciamo che avete alcuni file di dati contenti informazioni statistiche che volete include nella vostra estensione e rendere disponibile ai vostri componenti. Avete inserito questi file, con estensione .TXT, in una sottodirectory stats/ nella directory sorgente dell'estensione. La seguente regola di makefile può essere usata per copiare tali file nella directory di destinazione finale dell'estensione:

libs::
	if test ! -d $(FINAL_TARGET)/stats; then \
		$(NSINSTALL) -D $(FINAL_TARGET)/stats; \
	fi
	$(INSTALL) $(srcdir)/*.txt $(FINAL_TARGET)/stats
Accedere ai file di dati tramite Componenti

Il trucco per accedere ai propri file di dati è immaginare dove si trova la home directory della propria estensione. Alcune voci sostengono che in un prossimo futuro, questo sarà possibile attraverso l'interfaccia nsIExtensionManager o qualcosa di simile. Nel frattempo, c'è un hack semplice ed affidabile che può essere usato per raggiungere lo scopo. Nell'implementazione di ogni compoente JavaScript XPCOM, è presente uno speciale simbolo __LOCATION__ (due caratteri di underscore all'inizio e alla fine del nome) che punta al file di implementazione del componente. Perciò si può scrivere un semplice componente che deduce la directory radice della propria estensione estrapolandola da lì.

Questo articolo spiega come creare un componente XPCOM in JavaScript. Si avrà bisogno di un file IDL per una interfaccia simile a questa:

interface myILocation : nsISupports
{
    readonly attribute nsIFile locationFile;
};

Il file IDL va posizionato nella directory public/ del progetto o sottoprogetto. Nella directory src/, va inserito il file JavaScript che implementa il componente. L'implementazione del componente includerà i metodi per ottenere il percorso o il file per la home directory dell'estensione:

myLocation.prototype =
{
  QueryInterface: function(iid)
  {
    if (iid.equals(nsISupports))
      return this;
    if (iid.equals(myILocation))
      return this;
	
    Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
    return null;
  },
	
  get locationFile()
  {
     return __LOCATION__.parent.parent;
  }
}

Questo metodo assume che il componente risieda in una sottodirectory di quella dell'estensione (per convenzione, questa directory viene chiamata components/). La proprietà parent di __LOCATION__ ritorna components/, ed il suo valore parent è la home directory dell'estensione.

L'ultimo passo è la modifica del makefile della directory sorgente dove si ha inserito il proprio file JavaScript di modo che venga copiato nel luogo appropriato all'interno dell'estensione:

libs::
	$(INSTALL) $(srcdir)/*.js $(FINAL_TARGET)/components

Ora è possibile istanziare il componente ed usare la proprietà locationFile per ottenere una interfaccia nsIFile che punti alla home directory dell'estensione.

Usare librerie di terze parti

Per estensioni più sofosticate, si potrebbe voler integrare librerie di terze parti che forniscono funzionalità specializzare per la connettività al database, processing di immagini, rete e simili. Se si vuole che la propria estensione funzioni su tutte le piattaforme Firefox, è necessario avere il codice sorgente della libreria in questione, quindi assumo che questo sia disponibile.

l'approccio migliore nell'ottica del ciclo di sviluppo è creare un makefile come quello di Mozilla per la libreria. Questa maniera funziona bene per librerie che hanno un processo di make lineare, senza necessità di configurazioni eccessive. Un buon esempio di questo è la libreria SQLite inclusa nell'albero di build di Mozilla in db/sqlite. Adattando il makefile in quetso modo, la libreria è creata come parte del processo standard di build di Mozilla, il che elimina eventuali altri passi di compilazione. Il difetto di questo procedimento è che il makefile va aggiornato ogni qualvolta viene rilasciata una nuova versione della libreria.

Per librerie che hanno un complesso procedimento di configurazione, usano un compilatore non standard o altre caratteristiche speciali. potrebbe non essere praticabile la creazione di un Makefile compatibile con Mozilla. In questo caso, raccomanderei di inserire l'intera distribuzione della libreria all'interno del progetto o del sottoprogetto che la usa. Per cui se la libreria acmelib viene usata all'interno del sottoprogetto multifarious/ visto nell'esempio sopra, dovrebbe essere inserita in una sottodirectory di quella del sottoprogetto (allo stesso livello di public/ e src/).

Ovviamente, questo significa che si dovrà compilare manualmente acmelib su tutte le piattaforme prima di lanciare il build di Mozilla. Ma alla fine ci si potrà riferire ai file include ed importare le librerie dal proprio componente usando percorsi dei path relativi.

Build per più piattaforme

TODO

 

Informazioni riguardo il documento originale

Tag del documento e collaboratori

 Hanno collaborato alla realizzazione di questa pagina: fotografi, teoli, Timetravel0, Fabiothebest89, TheMastion, SuZzO, Leofiore, Indigo
 Ultima modifica di: fotografi,