Post on 22-Feb-2019
transcript
Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica Elaborato finale in Reti di Calcolatori
Sviluppo di applicazioni Open Web Apps multipiattaforma
Anno Accademico 2012/2013 Candidato: Marco Castelluccio matr. N46/1112
III
Indice
Capitolo 1. Open Web Apps 4
1.1 Vantaggi per gli utenti 15
1.2 Vantaggi per gli sviluppatori 23
1.3 App Manifest 25
1.4 Integrazione con le diverse piattaforme 32
1.5 Hosted apps 35
1.6 Packaged apps 36
1.7 Il sistema di sicurezza 38
Capitolo 2. Sviluppo di applicazioni 42
2.1 Presentazione di alcune API interessanti 42
2.1.1 TCPSocket API 44
2.1.2 Geolocation API 46
2.1.3 Apps API 49
2.1.4 Indexed Database API 53
2.1.5 Payments API 62
2.1.6 WebRTC 70
2.1.7 WebGL, FullScreen API, PointerLock API, Gamepad API, WebAudio API 72
2.2 Presentazione di alcuni tool di sviluppo interessanti 49
2.2.1 Remote debugger 53
2.2.2 Emscripten 62
Capitolo 3. Il desktop WebRuntime (WebRT) 42
3.1 Installazione 42
3.2 Architettura del WebRT 44
3.3 Funzionamento del WebRT 46
Bibliografia 199
Sviluppo di applicazioni Open Web Apps multipiattaforma
4
Capitolo 1
Open Web Apps
Open Web Apps è un progetto di Mozilla per la creazione di una piattaforma per
applicazioni basata completamente sul web.
Una webapp è semplicemente un sito, a cui è associato un insieme di metadati per la sua
descrizione, che può essere installato su un dispositivo e utilizzato come un'applicazione
nativa.
Una webapp è un'applicazione sviluppata con i linguaggi tipici del web (JavaScript,
HTML, CSS), che utilizza le API web standard.
La creazione di una piattaforma per applicazioni web fornisce svariati vantaggi sia per gli
sviluppatori di applicazioni, sia per gli utenti.
Nel seguito, per evitare confusione, si utilizzerà il termine “applicazione” per riferirsi al
software sviluppato da Mozilla (Firefox, Thunderbird, etc.) e il termine “webapp” per
riferirsi alle applicazioni web (salvo quando è ovvio a cosa ci si stia riferendo).
1.1 Vantaggi per gli utenti
1. Installazione locale e funzionamento offline: Rispetto a normali siti web, le
webapp possono in maniera più semplice essere eseguite offline.
2. Assenza di walled garden: Le webapp sono di loro natura cross-platform, gli utenti
non sono forzati ad utilizzare una singola piattaforma ma hanno libertà di scelta e
Sviluppo di applicazioni Open Web Apps multipiattaforma
5
libertà di passare da un sistema all'altro senza perdere le webapp che hanno
acquistato in precedenza.
3. Disponibilità di differenti sorgenti di distribuzione: Le API che permettono di
installare webapp permettono la presenza di più sorgenti di distribuzione che l'utente
può utilizzare. Esiste un Marketplace sviluppato da Mozilla, che però non è
avvantaggiato in alcun modo rispetto agli altri.
4. Sistema di permessi web-like che dà il pieno controllo agli utenti.
1.2 Vantaggi per gli sviluppatori
Uno degli obiettivi di questo progetto è quello di dare agli sviluppatori il pieno controllo
su tutti gli aspetti riguardanti le loro applicazioni, dalla semplicità di sviluppo fino alle
modalità di distribuzione.
1. API cross-platform: Gli sviluppatori possono creare un unico prodotto software
che può essere distribuito su tutte le piattaforme che dispongono di un moderno
browser web.
2. Accesso all'hardware: Sono state introdotte numerose nuove API per rendere le
webapp potenti quanto le applicazioni native.
3. Riutilizzare l'esperienza acquisita per lo sviluppo di siti web.
4. Userbase enorme: Le webapp possono essere eseguite su una qualsiasi piattaforma
disponga di un moderno browser web. Questo significa praticamente tutti i sistemi
operativi desktop e mobile esistenti (con alcune limitazioni in alcuni casi).
5. Libertà di distribuzione della propria applicazione: Gli sviluppatori sono liberi di
distribuire le proprie applicazioni come preferiscono. Nulla vieta la creazione di
nuove sorgenti di distribuzione (che funzionano esattamente alla pari di quelle
“ufficiali”)
Sviluppo di applicazioni Open Web Apps multipiattaforma
6
1.3 App Manifest
I metadati associati ad un'applicazione sono raccolti in un file manifest in formato JSON.
{
"version": "0.2",
"name": "Ceferino",
"description": "Ceferino, a clone of Super Pang",
"icons": {
"16": "/ceferino-16.png",
"48": "/ceferino-48.png",
"128": "/ceferino-128.png"
},
"developer": {
"name": "Marco Castelluccio",
"url": "http://marco-c.github.com/ceferinoweb/"
},
"installs_allowed_from": ["*"],
"appcache_path": "/ceferino.appcache",
"locales": {
"it": {
"description": "Ceferino, un clone di Super Pang",
"developer": {
"url": "http://marco-c.github.com/ceferinoweb/"
}
}
},
"default_locale": "en"
}
Campi del manifest:
• activities: Specifica le WebActivities che l'applicazione supporta.
• appcache_path: Specifica l'indirizzo del manifest per l'AppCache (per il supporto
allo storage offline).
• chrome: Specifica se l'applicazione deve essere eseguita con un'interfaccia di
navigazione (ad esempio, un menù di default, o i bottoni per la navigazione).
• csp: Specifica la Content Security Policy per l'applicazione.
• default_locale: Tag ([1]) che specifica il linguaggio di default dell'applicazione.
• description: Specifica la descrizione dell'applicazione.
• developer: Specifica i dati riguardanti lo sviluppatore dell'applicazione (è un
dizionario che contiene le proprietà nome e url).
• fullscreen: Specifica se l'applicazione dev'essere eseguita in modalità fullscreen.
• icons: Specifica l'insieme di icone da utilizzare per l'applicazione (è possibile
Sviluppo di applicazioni Open Web Apps multipiattaforma
7
specificare diverse icone di diverse dimensioni, così da potersi adattare a diversi
form factor).
• installs_allowed_from: Specifica i siti da cui è possibile installare l'applicazione.
• launch_path: Specifica l'indirizzo (relativo all'origine dell'applicazione) che viene
caricato al lancio della webapp.
• locales: Specifica le lingue supportate dall'applicazione. E' un dizionario che può
comprendere (e quindi sovrascrivere) gli stessi elementi presenti nell'App Manifest
(tranne default_locale, locales e installs_allowed_from).
• messages: Specifica i messaggi di sistema per i quali l'applicazione verrà notificata.
• name: Specifica il nome dell'applicazione.
• orientation: Specifica l'orientamento dell'applicazione (portrait, landscape, etc.).
• permissions: Specifica l'insieme di permessi che l'applicazione richiede.
"permissions": {
"contacts": {
"description": "Required for autocompletion in the share screen",
"access": "readcreate"
},
"alarms": {
"description": "Required to schedule notifications"
}
}
Il campo description è obbligatorio e serve per specificare il motivo per cui
l'applicazione richiede un determinato permesso.
• redirects: Nel caso di applicazioni packaged, che non sono ospitate su un server, è a
volte necessario specificare alcuni URL che vanno rediretti a pagine
dell'applicazione. Ad esempio può servire per il login con il protocollo OAuth:
"redirects": [
{"from": "http://mydomain.com/oauth2/flow.html",
"to": "/redirects/redirect.html"},
{"from": "http://mydomain.com/oauth2/dialogs_end.html",
"to": "/redirects/dialogs_end.html"}
]
I redirects sono ovviamente locali all'applicazione per questioni di sicurezza.
Sviluppo di applicazioni Open Web Apps multipiattaforma
8
• type: Specifica il tipo di applicazione, web, privileged o certified. Un diverso tipo
corrisponde a un diverso livello di privilegi (per l'accesso ad API più sensibili).
• version: Specifica la versione dell'applicazione.
Nel caso delle applicazioni hosted, l'App Manifest è servito tramite un server web (con
mimetype application/x-web-app-manifest+json, per questioni di sicurezza).
Nel caso delle applicazioni packaged, l'App Manifest è incluso nell'archivio compresso.
Esiste invece un altro manifest (Mini Manifest, per supportare installazioni/aggiornamenti)
con i seguenti campi:
• name
• package_path: Specifica l'indirizzo dell'archivio compresso che contiene
l'applicazione vera e propria.
• version
• size: Specifica la dimensione dell'archivio compresso.
• release_notes: Specifica le novità tra una versione dell'applicazione e l'altra.
• developer
• locales
• icons
I campi che compaiono sia nel Mini Manifest che nell'App Manifest devono essere uguali.
1.4 Integrazione con le diverse piattaforme
Sulle piattaforme desktop, le applicazioni vengono eseguite in maniera completamente
indipendente. Ogni applicazione ha il suo processo e viene eseguita in una o più finestre
separate.
Su Windows le applicazioni vengono installate nel menù Start e sul desktop. Possono
essere disinstallate tramite il pannello di controllo.
Sviluppo di applicazioni Open Web Apps multipiattaforma
9
Su Linux le applicazioni vengono installate nella home directory dell'utente. Vengono
utilizzati gli standard FreeDesktop (Desktop Entry Specification e Desktop Menu
Specification) per l'integrazione dell'applicazione con tutti gli ambienti desktop presenti su
Linux (GNOME, KDE, etc.).
Sviluppo di applicazioni Open Web Apps multipiattaforma
10
Su Mac le applicazioni vengono installate nella cartella /Applications, vengono
visualizzate nella dashboard e sulla barra delle applicazioni.
Su Android, le applicazioni vengono automaticamente pacchettizzate in APK e firmate
digitalmente per essere poi installate come normali applicazioni Android.
Su Firefox OS, tutte le applicazioni sono applicazioni web.
Sviluppo di applicazioni Open Web Apps multipiattaforma
11
1.5 Hosted apps
Le applicazioni hosted sono applicazioni servite tramite un normale server web. Tramite
l'App Manifest è possibile specificare i dati relativi all'applicazione.
Utilizzando determinate API (AppCache), è possibile supportare lo storage offline dei dati
dell'applicazione.
La gestione degli aggiornamenti è completamente demandata allo sviluppatore, la
piattaforma di distribuzione non ha alcun controllo sull'applicazione stessa. Per questo le
applicazioni hosted non possono accedere a determinate API che necessitano di un livello
di sicurezza particolare.
1.6 Packaged apps
Le applicazioni packaged sono applicazioni le cui risorse (codice JavaScript, HTML, CSS,
etc.) vengono incluse in un archivio compresso.
L'App Manifest che specifica i dati dell'applicazione è incluso nell'archivio ed è presente
un Mini Manifest per la gestione dell'installazione/aggiornamento della webapp.
L'aggiornamento delle applicazioni packaged può essere gestito dalla piattaforma di
distribuzione, quindi possono essere recensite e di conseguenza accedere ad API più
sensibili.
Le webapp accedono alle risorse contenute nell'archivio compresso tramite uno speciale
protocollo (“app://”).
1.7 Il sistema di sicurezza
Web: Le applicazioni di tipo web (tutte le hosted e le packaged che non richiedono
l'utilizzo di particolari API) sono applicazioni che non necessitano di particolari privilegi.
Sono trattate, dal punto di vista della sicurezza, esattamente come normale contenuto web.
Privileged: Le applicazioni di tipo privileged (solo packaged, perché per utilizzare
particolari API c'è bisogno che l'applicazione venga recensita e firmata digitalmente) sono
Sviluppo di applicazioni Open Web Apps multipiattaforma
12
applicazioni che richiedono particolari privilegi. Rispetto a normale contenuto web
godono di queste caratteristiche:
• Il codice dell'applicazione viene recensito da una piattaforma di
distribuzione.
• L'archivio compresso che contiene le risorse dell'applicazione viene firmato
digitalmente (un archivio JAR firmato con lo standard CMS/PKCS7, lo
stesso utilizzato per S/MIME).
• Possono utilizzare alcune API a cui normalmente i siti web non possono
accedere.
• Utilizza una particolare CSP (Content Security Policy) per mitigare attacchi
di code injection (per evitare che del codice estraneo possa utilizzare le API
sensibili). La politica CSP utilizzata è: default-src *; script-src 'self';
object-src 'none'; style-src 'self'
In sostanza, soltanto il codice compreso nell'archivio può essere eseguito (ad esempio, non
è possibile utilizzare la funzione “eval”, non è possibile utilizzare plugin, etc.).
E' possibile utilizzare elementi <iframe> (che però non possono accedere alle stesse API a
cui può accedere l'applicazione stessa, perché i permessi sono basati sull'origine), caricare
immagini e altri contenuti multimediali da una qualsiasi sorgente, creare connessioni di
rete tramite le API che lo supportano (XMLHttpRequest, WebSocket, TCPSocket,
WebRTC).
Per quanto riguarda i permessi, si possono verificare tre casi:
• Il permesso non viene concesso perché non era specificato nell'App
Manifest.
• Il permesso viene concesso dopo che l'utente ha esplicitamente accettato.
• Il permesso viene concesso senza chiedere all'utente. Questo è necessario
quando è difficile spiegare le implicazioni di un permesso agli utenti (ad
esempio, il permesso di utilizzare i socket).
Sviluppo di applicazioni Open Web Apps multipiattaforma
13
Come accade per i normali siti web, i permessi vengono richiesti a runtime e non all'atto
dell'installazione. Questo permette agli utenti un controllo fine-grained, perché le
applicazioni sono installabili ed eseguibili anche se non tutti i permessi vengono concessi
(al contrario di quanto avviene in altri sistemi, dove l'utente deve concedere tutti i
permessi prima di poter installare l'applicazione).
Le applicazioni vengono eseguite in processi separati e non condividono dati (compreso
cookie, dati localStorage, indexedDB e permessi).
Questo significa che uno stesso sito potrebbe essere visualizzato in maniera differente da
applicazioni differenti e che un'applicazione non può “aprirne” un'altra perché, se creasse
un elemento <iframe> che punta ad un'altra applicazione, semplicemente aprirebbe il sito
localizzato a quell'URL.
Questo approccio garantisce che le applicazioni non interagiscano tra loro in maniere
impreviste.
Nel dettaglio, a ogni Principal (che rappresenta un contesto di sicurezza) viene associato,
oltre alla semplice origine, un'ID univoco della webapp. Questo significa che tutti i
controlli di sicurezza, che vengono effettuati tramite il Principal, danno risultati diversi a
seconda che ci si trovi in un elemento che fa parte di una webapp o meno.
Sviluppo di applicazioni Open Web Apps multipiattaforma
14
Capitolo 2
Sviluppo di applicazioni
In questo capitolo si presenta alcune API interessanti per lo sviluppo di applicazioni web e
alcuni tool che semplificano la vita agli sviluppatori di questo tipo di applicazioni.
2.1 Presentazione di alcune API interessanti
2.1.1 TCPSocket API
L'API TCPSocket permette alle applicazioni privileged di creare connessioni TCP.
La definizione dell’interfaccia WebIDL è autoesplicativa:
interface TCPSocket{
readonly attribute DOMString host;
readonly attribute unsigned short port;
readonly attribute boolean ssl;
readonly attribute unsigned long bufferedAmount;
readonly attribute DOMString binaryType;
readonly attribute DOMString readyState;
TCPSocket open(DOMString host, unsigned short port, [object options]);
TCPServerSocket listen(unsigned short port, [object options, [unsigned short
backlog]])
void upgradeToSecure();
void suspend();
void resume();
void close();
boolean send(in jsval data);
attribute onopen;
attribute ondrain;
attribute ondata;
attribute onerror;
attribute onclose;
};
Sviluppo di applicazioni Open Web Apps multipiattaforma
15
Segue un estratto di codice da una webapp che supporta il download di files tramite il
protocollo BitTorrent:
connect: function() {
this._socket = navigator.mozTCPSocket.open(this.ip, this.port, { binaryType:
"arraybuffer" });
this._socket.onopen = () => {
this._connected = true;
this._socket.ondata = (event) => this.receiveData(event.data);
this._socket.onerror = (event) => console.log("onerror: " + this.ip);
this._socket.onclose = (event) => console.log("onclose: " + this.ip);
// Handshake message
let handShake = Connection.buildHandshake(this.torrent.infoHash,
myClient.peerID);
this._socket.send(handShake.buffer, handShake.byteOffset,
handShake.byteLength);
};
},
2.1.2 Geolocation API
L'API di geolocalizzazione permette alle applicazioni (senza particolari privilegi) di
accedere alla posizione dell'utente (dopo averne richiesto il permesso).
La funzione getCurrentPosition richiede in maniera asincrona la posizione e, quando la
posizione è stata acquisita, esegue una funzione di callback.
navigator.geolocation.getCurrentPosition(function(position) {
do_something(position.coords.latitude, position.coords.longitude);
});
La funzione watchPosition permette un’aquisizione continua della posizione. Funziona in
maniera simile alla getCurrentPosition. La funzione di callback viene chiamata ad
intervalli periodici, mentre la posizione sta cambiando.
var watchID = navigator.geolocation.watchPosition(function(position) {
do_something(position.coords.latitude, position.coords.longitude);
});
La funzione clearWatch permette di smettere di osservazione la posizione.
navigator.geolocation.clearWatch(watchID);
Sviluppo di applicazioni Open Web Apps multipiattaforma
16
watchPosition e getCurrentPosition permettono di specificare anche una funzione di
callback eseguita in caso di errore e un insieme di opzioni (ad esempio, l'accuracy).
function geo_success(position) {
do_something(position.coords.latitude, position.coords.longitude);
}
function geo_error() {
alert("Sorry, no position available.");
}
var geo_options = {
enableHighAccuracy: true,
maximumAge : 30000,
timeout : 27000
};
var wpid = navigator.geolocation.watchPosition(geo_success, geo_error,
geo_options);
2.1.1 Apps API
Segue un estratto del codice IDL che descrive l'interfaccia della API mozApps. Le
funzioni più importanti sono install e installPackage, che permettono di installare
un'applicazione dato l'URL al suo file manifest.
interface mozIDOMApplication : nsISupports
{
readonly attribute jsval manifest;
readonly attribute jsval updateManifest;
readonly attribute DOMString manifestURL;
readonly attribute jsval receipts; /* an array of strings */
readonly attribute DOMString origin;
readonly attribute DOMString installOrigin;
readonly attribute unsigned long long installTime;
readonly attribute boolean removable;
/**
* The current progress when downloading an offline cache.
*/
readonly attribute double progress;
/**
* The application installation state :
* "pending" : The application is being installed (eg, we're downloading the
* offline cache or the package).
* "installed" : The application is installed and ready to be launched.
* "updating" : We are updating the offline-cache or the package.
*/
readonly attribute DOMString installState;
/**
* fires a nsIDOMApplicationEvent when a change in appcache download or
* package download happens.
*/
attribute nsIDOMEventListener onprogress;
/**
Sviluppo di applicazioni Open Web Apps multipiattaforma
17
* The date of the last update check.
*/
readonly attribute unsigned long long lastUpdateCheck;
/**
* The date of the last updated manifest.
*/
readonly attribute unsigned long long updateTime;
/**
* Starts the process of looking for an update.
*/
nsIDOMDOMRequest checkForUpdate();
readonly attribute boolean downloadAvailable;
readonly attribute boolean downloading;
readonly attribute boolean readyToApplyDownload;
readonly attribute long downloadSize;
// This is a DOMError
readonly attribute nsISupports downloadError;
attribute nsIDOMEventListener ondownloadsuccess;
attribute nsIDOMEventListener ondownloaderror;
attribute nsIDOMEventListener ondownloadavailable;
/**
* Will fire once the mgmt.applyDownload() call succeeds.
*/
attribute nsIDOMEventListener ondownloadapplied;
/**
* Starts to download an update. If |downloading| is true, this
* is a no-op.
*/
void download();
/**
* Cancels an ongoing update download.
*/
void cancelDownload();
/* startPoint will be used when several launch_path exists for an app */
nsIDOMDOMRequest launch([optional] in DOMString startPoint);
/**
* Clear data that has been collected through mozbrowser elements.
* onsuccess will be called once data is actually cleared.
*/
nsIDOMDOMRequest clearBrowserData();
/**
* Inter-App Communication APIs.
*
* https://wiki.mozilla.org/WebAPI/Inter_App_Communication_Alt_proposal
*/
nsISupports connect(in DOMString keyword,
[optional] in jsval rules); // nsISupports is a Promise.
nsISupports getConnections(); // nsISupports is a Promise.
};
interface mozIDOMApplicationMgmt : nsISupports
{
/**
* the request will return the all the applications installed. Only accessible
* to privileged callers.
*/
Sviluppo di applicazioni Open Web Apps multipiattaforma
18
nsIDOMDOMRequest getAll();
/**
* the request will return the applications acquired from all origins but
* which are not launchable (e.g. by not being natively installed), or null.
*/
nsIDOMDOMRequest getNotInstalled();
/**
* event listener to get notified of application installs. Only settable by
* privileged callers.
* the event will be a mozIDOMApplicationEvent
*/
attribute nsIDOMEventListener oninstall;
/**
* event listener to get notified of application uninstalls. Only settable by
* privileged callers.
* the event will be a mozIDOMApplicationEvent
*/
attribute nsIDOMEventListener onuninstall;
/**
* Applies a downloaded update.
* This function is a no-op if it's passed an app object which doesn't have
* |readyToApplyDownload| set to true.
*/
void applyDownload(in mozIDOMApplication app);
/**
* Uninstall a web app.
*
* @param app : the app object of the web app to be uninstalled.
* @returns : A DOMRequest object, returning the app's origin in |result|
* if uninstall succeeds; returning "NOT_INSTALLED" error
otherwise.
*/
nsIDOMDOMRequest uninstall(in mozIDOMApplication app);
};
interface mozIDOMApplicationRegistry : nsISupports
{
/**
* Install a web app.
*
* @param manifestUrl : the URL of the webapps manifest.
* @param parameters : A structure with optional information.
* {
* receipts: ... Will be used to specify the payment
receipts for this installation.
* categories: ... Will be used to specify the
categories of the webapp.
* }
* @returns : A DOMRequest object, returning the app object in
|result| if install succeeds.
*/
nsIDOMDOMRequest install(in DOMString manifestUrl, [optional] in jsval
parameters);
/**
* the request will return the application currently installed, or null.
*/
nsIDOMDOMRequest getSelf();
/**
* the request will return the application if the app from that origin is
installed
*/
Sviluppo di applicazioni Open Web Apps multipiattaforma
19
nsIDOMDOMRequest checkInstalled(in DOMString manifestUrl);
/**
* the request will return the applications installed from this origin, or
null.
*/
nsIDOMDOMRequest getInstalled();
/**
* Install a packaged web app.
*
* @param packageUrl : the URL of the webapps manifest.
* @param parameters : A structure with optional information.
* {
* receipts: ... Will be used to specify the payment
receipts for this installation.
* categories: ... Will be used to specify the
categories of the webapp.
* }
* @returns : A DOMRequest object, returning the app object in
|result| if install succeeds.
*/
nsIDOMDOMRequest installPackage(in DOMString packageUrl, [optional] in jsval
parameters);
readonly attribute mozIDOMApplicationMgmt mgmt;
};
2.1.4 Indexed Database API
IndexedDB è un'API che permette lo storage di una larga quantità di dati in maniera
offline.
• I dati sono salvati in coppie chiave-valore. I valori possono essere oggetti
qualsiasi e la chiave può essere una qualsiasi delle proprietà di questi oggetti.
• Tutte le operazioni sono eseguite all'interno di una transazione. Questo
garantisce l'atomicità anche nel caso l'utente apra due pagine dello stesso
sito.
• L'API è asincrona. Come le altre API del genere, ci sono dei callback che
vengono eseguiti quando le operazioni sul database sono terminate.
• Non è una database relazionale, ma object-oriented. E' un database NoSQL,
non utilizza SQL.
• Ogni origine (e quindi ogni webapp) ha il suo insieme di basi di dati,
separato dagli altri.
Sviluppo di applicazioni Open Web Apps multipiattaforma
20
2.1.5 Payments API
interface nsINavigatorPayment : nsISupports
{
// The 'jwts' parameter can be either a single DOMString or an array of
// DOMStrings. In both cases, it represents the base64url encoded and
// digitally signed payment information. Each payment provider should
// define its supported JWT format.
nsIDOMDOMRequest pay(in jsval jwts);
};
Il formato JWT (JSON Web Token) è utilizzato per rappresentare le “ricevute”.
Le ricevute sono codificate in un oggetto JSON che viene poi firmato digitalmente
utilizzando lo standard JWS (JSON Web Signature) e criptate utilizzando lo standard JWE
(JSON Web Encryption).
Segue un esempio di oggetto JSON che specifica una “ricevuta”:
{
"iss": APPLICATION_KEY,
"aud": "marketplace.firefox.com",
...
"request": {
"name": "Magical Unicorn",
"pricePoint": 1,
"postbackURL": "https://yourapp.com/postback",
"chargebackURL": "https://yourapp.com/chargeback"
}
}
Lo sviluppatore deve occuparsi di creare (e firmare digitalmente) dei file JWT per ogni
prodotto che vende. Un sito di terze parti (specificato nel capo “aud”) si occuperà del
pagamento vero e proprio e, una volta che il pagamento sarà stato effettuato, notificherà
l'avvenuto acquisto all'indirizzo specificato in “request.postbackURL”.
var request = navigator.mozPay([signedJWT]);
request.onsuccess = function() {
// Attende che arrivi la notifica dal server dei pagamenti.
};
request.onerror = function() {
console.log('navigator.mozPay() error: ' + this.error.name);
}
2.1.6 WebRTC
WebRTC (Web Real-Time Communication) è un'insieme di API che permette lo sviluppo
Sviluppo di applicazioni Open Web Apps multipiattaforma
21
di applicazioni multimediali che comunicano in maniera P2P (senza l'utilizzo di plugin di
terze parti).
Tra le altre sono presenti le seguenti API:
getUserMedia – Permette di accedere ai dispositivi multimediali dell'utente (microfono,
videocamera, etc.).
MediaStream – Permette di creare e manipolare stream multimediali
Sono inoltre presenti API per creare connessioni P2P e condividere dati o stream
multimediali.
2.1.7 WebGL, Fullscreen API, Pointer Lock API, Gamepad API, WebAudio
API
Uno degli ambiti applicativi più interessanti per le applicazioni web è quello dei giochi.
WebGL – Quest'API permette il rendering di grafica 3D in un elemento <canvas> tramite
la GPU e senza l'utilizzo di plugin. E' molto simile a OpenGL ES 2.0 (e infatti entrambi gli
standard sono mantenuti dal Khronos Group).
FullScreen API - Quest'API permette alle applicazioni web di essere eseguite in modalità
fullscreen. L'API permette di effettuare il rendering in fullscreen di un elemento (e
eventualmente i suoi figli), eliminando dallo schermo l'interfaccia utente del browser e
delle altre applicazioni.
Si utilizza in maniera molto semplice tramite la funzione requestFullscreen di un elemento
del DOM.
PointerLock API – Quest'API fornisce la possibilità di accedere ai movimenti del mouse
in maniera dettagliata, di accedere agli eventi del mouse anche quando raggiunge i lati del
browser o dello schermo, di eliminare il cursore dallo schermo.
Pur non sembrando un'API così importante, è in realtà fondamentale per i giochi e per i
Sviluppo di applicazioni Open Web Apps multipiattaforma
22
software di modellazione 3D.
Si utilizza in maniera simile alla FullScreen API, tramite la funzione requestPointerLock
di un elemento del DOM.
La Gamepad API fornisce un'interfaccia per i controller gamepad, la WebAudio API
permette di creare, manipolare e riprodurre risorse audio su una pagina web (in maniera
molto più dettagliata rispetto a quanto era possibile fare prima).
2.2 Presentazione di alcuni tool di sviluppo interessanti
2.2.1 Remote Debugger
Il debugger remoto permette di utilizzare alcuni dei tool di sviluppo web presenti in
Firefox con una webapp (profiling del codice, profiling delle risorse di rete utilizzate,
inspector, debugger vero e proprio, console, etc.).
Il WebRT, che si occupa di lanciare la webapp, implementa un DebuggerServer a cui dei
DebuggerClient possono connettersi tramite socket TCP. La soluzione è molto flessibile
perché il DebuggerServer e il DebuggerClient possono ovviamente anche risiedere su
macchine diverse.
Sviluppo di applicazioni Open Web Apps multipiattaforma
23
2.2.2 Emscripten
Emscripten è un compilatore che trasforma bytecode LLVM in codice JavaScript. Il
bytecode LLVM può essere generato da vari linguaggi, in particolare da C/C++ usando
Clang.
In questa maniera si può compilare un software già esistente scritto in C/C++ in JavaScript
ed eseguirlo in un browser web.
Ovviamente è anche possibile compilare degli interpreti scritti in C/C++ (ad esempio,
CPython), e quindi eseguirli sul web (ad esempio, interpretare codice Python in una pagina
web).
Recentemente è stato anche definito un subset di JavaScript (asm.js) che rappresenta il
target di Emscripten e che può essere compilato ahead-of-time garantendo performance
molto vicine a quelle del C/C++.
Sviluppo di applicazioni Open Web Apps multipiattaforma
24
Capitolo 3
Il desktop WebRuntime (WebRT)
Il supporto alle Open Web Apps sulle piattaforme desktop comprende:
Il codice per l'installazione delle webapp e la loro integrazione col sistema operativo, che è
incluso nel browser.
Il software eseguibile che permette di lanciare le webapp e che fornisce API per
l'integrazione col sistema operativo, indipendente dal browser stesso.
3.1 Installazione
Il codice relativo all'installazione è diviso in due parti. Una parte, cross-platform,
implementa l'API mozApps, l'altra implementa l'installazione vera e propria ed è differente
per ogni sistema.
L'implementazione cross-platform dell'API mozApps si occupa di gestire un registro delle
webapp installate e di fornire, appunto, le funzioni mozApps viste in precedenza.
E' inoltre presente un “protocol handler” che gestisce il protocollo app://, che permette ad
applicazioni packaged di accedere alle proprie risorse dall'archivio compresso.
L'installazione provvede a creare i file necessari per l'esecuzione delle webapp e per
l'integrazione della stessa nei meccanismi di gestione software propri della piattaforma
sottostante.
Sviluppo di applicazioni Open Web Apps multipiattaforma
25
Su Windows, i dati sono salvati in una cartella locale ad ogni utente e nel registro di
sistema (per l'integrazione col pannello di controllo). Vengono creati due collegamenti sul
desktop e nel menu di start. Viene fornito un software per la disinstallazione (creato
tramite NSIS) come tipico per le applicazioni Windows.
Su Mac, i dati sono salvati nella cartella /Applications. Nella cartella d'installazione è
presente un file di descrizione del bundle, che Mac utilizza per registrare l'applicazione nel
sistema.
Su Linux, i dati sono salvati nella home dell'utente. Si utilizzano gli standard definiti da
FreeDesktop.org per fornire l'integrazione dell'applicazione con tutti i Desktop
Environment conformi agli standard (GNOME, KDE, etc.).
I file creati nella cartella d'installazione sono i seguenti:
• webapp.ini – Comprende alcuni dati per il WebRT (ad esempio, dove trovare la
libreria libxul)
• webapp.json – Comprende alcuni dati riguardanti la webapp, come il suo App
Manifest e il profilo da cui è stata installata.
• L'eseguibile che si occupa di lanciare la webapp.
• Directory del profilo – Directory che contiene i dati dell'applicazione (AppCache,
cache, IndexedDB, permessi granted dall'utente, etc.). L'AppCache viene precaricata
all'atto dell'installazione, se specificato nell'App Manifest (questo permette alla
webapp di essere eseguita offline).
• L'icona per la webapp.
L'installazione dell'applicazione viene eseguita tramite il browser o il WebRT (quando
vengono utilizzate le chiamate install o installPackage dell'API mozApps):
Sviluppo di applicazioni Open Web Apps multipiattaforma
26
A titolo di esempio, si mostra una parte dell'implementazione dell'installazione per Linux
(e BSD):
LinuxNativeApp.prototype = {
__proto__: NativeApp.prototype,
install: function(aZipPath) {
return Task.spawn(function() {
this._getInstallDir();
try {
this._createDirectoryStructure();
this._copyPrebuiltFiles();
this._createConfigFiles();
if (aZipPath) {
yield OS.File.move(aZipPath, OS.Path.join(this.tmpInstallDir,
"application.zip"));
}
yield this.getIcon();
// Remove previously installed app (for update purposes)
this._removeInstallation(true);
} catch (ex) {
removeFiles([this.tmpInstallDir]);
throw(ex);
}
try {
yield moveDirectory(this.tmpInstallDir, this.installDir);
this._createSystemFiles();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
}.bind(this));
},
3.2 Architettura del WebRT
Il webruntime è un semplice eseguibile che utilizza le librerie dinamiche del browser
engine (Gecko) per fornire una shell XUL in cui lanciare le applicazioni.
Sviluppo di applicazioni Open Web Apps multipiattaforma
27
Gecko – Gecko è il sottosistema che contiene il browser engine e il rendering engine.
Gecko si occupa del rendering del contenuto web. Il rendering engine si occupa anche del
rendering di interfacce utente definite tramite XUL.
Il browser engine è un'interfaccia ad alto livello per il rendering engine.
XPCOM (Cross Platform Component Object Model) – Fornisce le funzionalità per creare
applicazioni modulari in maniera cross-platform (è simile a Microsoft COM). Gestisce il
passaggio di parametri tra differenti componenti, la gestione della memoria, etc.
XPConnect è un “ponte” tra componenti scritti in JavaScript e componenti scritti in C++.
NSPR (Netscape Portable Runtime) – E' una libreria che fornisce un insieme di API
system-level e cross-platform (ad esempio le primitive per il locking e alcune funzioni
simili a quelle presenti in libc).
Toolkit – Un insieme di servizi cross-platform (gestione degli aggiornamenti, gestione dei
profili, etc.). Nel Toolkit è presente anche il codice per l'installazione delle webapp.
XUL (XML User Interface Language) è un linguaggio XML per definire interfacce
grafiche. Non è uno standard, ma è un linguaggio utilizzato internamente dai progetti
Sviluppo di applicazioni Open Web Apps multipiattaforma
28
basati sulla piattaforma Mozilla. XUL viene utilizzato nel WebRT per definire l'interfaccia
(la finestra, i menù, etc.) della shell che contiene l'applicazione web (non viene utilizzato
in alcun modo dalle applicazioni stesse).
Firefox, Thunderbird e tutti gli altri prodotti Mozilla (compreso il WebRT) sono tutte
applicazioni che fanno uso dell'engine Gecko.
Il WebRT non dipende da Firefox, ma dalle librerie condivise di Gecko (libxul, il nome
della libreria è misleading perché in realtà fa molto più che semplicemente effettuare il
parsing di file XUL).
3.3 Funzionamento del WebRT
In questa sezione si presentano alcune parti dell'implementazione del WebRT,
volutamente semplificate e con alcuni commenti esplicativi in più.
Segue un estratto di codice dell'eseguibile principale del WebRT, che non fa altro che
caricare la libreria libxul dalla directory d'installazione e poi, dopo alcuni passi di
inizializzazione, caricare la shell XUL e lanciare la webapp.
bool GRELoadAndLaunch(const char* firefoxDir)
{
char xpcomDllPath[MAXPATHLEN];
snprintf(xpcomDllPath, MAXPATHLEN, "%s/%s", firefoxDir, XPCOM_DLL);
if (NS_FAILED(XPCOMGlueStartup(xpcomDllPath))) {
ErrorDialog("Couldn't load the XPCOM library");
return false;
}
if (NS_FAILED(XPCOMGlueLoadXULFunctions(kXULFuncs))) {
ErrorDialog("Couldn't load libxul");
return false;
}
// NOTE: The GRE has successfully loaded, so we can use XPCOM now
{ // Scope for any XPCOM stuff we create
ScopedLogging log;
// Get the path to the runtime
char rtPath[MAXPATHLEN];
snprintf(rtPath, MAXPATHLEN, "%s/%s", firefoxDir, kWEBAPPRT_PATH);
// Get the path to the runtime's INI file
char rtIniPath[MAXPATHLEN];
snprintf(rtIniPath, MAXPATHLEN, "%s/%s", rtPath, kWEBAPPRT_INI);
Sviluppo di applicazioni Open Web Apps multipiattaforma
29
// Load the runtime's INI from its path
nsCOMPtr<nsIFile> rtINI;
if (NS_FAILED(XRE_GetFileFromPath(rtIniPath, getter_AddRefs(rtINI)))) {
ErrorDialog("Couldn't load the runtime INI");
return false;
}
bool exists;
nsresult rv = rtINI->Exists(&exists);
if (NS_FAILED(rv) || !exists) {
ErrorDialog("The runtime INI doesn't exist");
return false;
}
nsXREAppData *webShellAppData;
if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
ErrorDialog("Couldn't read WebappRT application.ini");
return false;
}
SetAllocatedString(webShellAppData->profile, profile);
// nsXREAppData::name is used for the class name part of the WM_CLASS
// property. Set it so that the DE can match our window to the correct
// launcher.
char programClass[MAXPATHLEN];
snprintf(programClass, MAXPATHLEN, "owa-%s", profile);
SetAllocatedString(webShellAppData->name, programClass);
nsCOMPtr<nsIFile> directory;
if (NS_FAILED(XRE_GetFileFromPath(rtPath, getter_AddRefs(directory)))) {
ErrorDialog("Couldn't open runtime directory");
return false;
}
nsCOMPtr<nsIFile> xreDir;
if (NS_FAILED(XRE_GetFileFromPath(firefoxDir, getter_AddRefs(xreDir)))) {
ErrorDialog("Couldn't open XRE directory");
return false;
}
xreDir.forget(&webShellAppData->xreDirectory);
NS_IF_RELEASE(webShellAppData->directory);
directory.forget(&webShellAppData->directory);
XRE_main(*pargc, *pargv, webShellAppData, 0);
XRE_FreeAppData(webShellAppData);
}
return true;
}
void CopyAndRelaunch(const char* firefoxDir, const char* curExePath)
{
char newExePath[MAXPATHLEN];
snprintf(newExePath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_RT);
if (unlink(curExePath) == -1) {
ErrorDialog("Couldn't remove the old webapprt-stub executable");
return;
}
if (!CopyFile(newExePath, curExePath)) {
ErrorDialog("Couldn't copy the new webapprt-stub executable");
return;
}
Sviluppo di applicazioni Open Web Apps multipiattaforma
30
execv(curExePath, *pargv);
ErrorDialog("Couldn't execute the new webapprt-stub executable");
}
void RemoveApplication(nsINIParser& parser, const char* curExeDir, const char*
profile) {
if (!isProfileOverridden) {
// Remove the desktop entry file.
char desktopEntryFilePath[MAXPATHLEN];
char* dataDir = getenv("XDG_DATA_HOME");
if (dataDir && *dataDir) {
snprintf(desktopEntryFilePath, MAXPATHLEN, "%s/applications/owa-
%s.desktop", dataDir, profile);
} else {
char* home = getenv("HOME");
snprintf(desktopEntryFilePath, MAXPATHLEN,
"%s/.local/share/applications/owa-%s.desktop", home, profile);
}
unlink(desktopEntryFilePath);
}
// Remove the files from the installation directory.
char webAppIniPath[MAXPATHLEN];
snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
unlink(webAppIniPath);
char curExePath[MAXPATHLEN];
snprintf(curExePath, MAXPATHLEN, "%s/%s", curExeDir, kAPP_RT);
unlink(curExePath);
char webAppJsonPath[MAXPATHLEN];
snprintf(webAppJsonPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_JSON);
unlink(webAppJsonPath);
char iconPath[MAXPATHLEN];
snprintf(iconPath, MAXPATHLEN, "%s/icon.png", curExeDir);
unlink(iconPath);
char appName[MAXPATHLEN];
if (NS_FAILED(parser.GetString("Webapp", "Name", appName, MAXPATHLEN))) {
strcpy(appName, profile);
}
char uninstallMsg[MAXPATHLEN];
if (NS_SUCCEEDED(parser.GetString("Webapp", "UninstallMsg", uninstallMsg,
MAXPATHLEN))) {
/**
* The only difference between libnotify.so.4 and libnotify.so.1 for these
symbols
* is that notify_notification_new takes three arguments in libnotify.so.4
and
* four in libnotify.so.1.
* Passing the fourth argument as nullptr is binary compatible.
*/
typedef void (*notify_init_t)(const char*);
typedef void* (*notify_notification_new_t)(const char*, const char*, const
char*, const char*);
typedef void (*notify_notification_show_t)(void*, void**);
void *handle = dlopen("libnotify.so.4", RTLD_LAZY);
if (!handle) {
handle = dlopen("libnotify.so.1", RTLD_LAZY);
if (!handle)
return;
Sviluppo di applicazioni Open Web Apps multipiattaforma
31
}
notify_init_t nn_init = (notify_init_t)(uintptr_t)dlsym(handle,
"notify_init");
notify_notification_new_t nn_new =
(notify_notification_new_t)(uintptr_t)dlsym(handle, "notify_notification_new");
notify_notification_show_t nn_show =
(notify_notification_show_t)(uintptr_t)dlsym(handle,
"notify_notification_show");
if (!nn_init || !nn_new || !nn_show) {
dlclose(handle);
return;
}
nn_init(appName);
void* n = nn_new(uninstallMsg, nullptr, "dialog-information", nullptr);
nn_show(n, nullptr);
dlclose(handle);
}
}
int main(int argc, char *argv[])
{
pargc = &argc;
pargv = &argv;
// Get current executable path
char curExePath[MAXPATHLEN];
if (NS_FAILED(mozilla::BinaryPath::Get(argv[0], curExePath))) {
ErrorDialog("Couldn't read current executable path");
return 255;
}
char curExeDir[MAXPATHLEN];
GetDirFromPath(curExeDir, curExePath);
bool removeApp = false;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-remove")) {
removeApp = true;
}
}
char firefoxDir[MAXPATHLEN];
// Check if Firefox is in the same directory as the webapp runtime.
// This is the case for webapprt chrome and content tests.
if (GRELoadAndLaunch(curExeDir, true)) {
return 0;
}
// Set up webAppIniPath with path to webapp.ini
char webAppIniPath[MAXPATHLEN];
snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
// Open webapp.ini as an INI file
nsINIParser parser;
if (NS_FAILED(parser.Init(webAppIniPath))) {
ErrorDialog("Couldn't open webapp.ini");
return 255;
}
// Set up our environment to know where webapp.ini was loaded from
if (setenv(kAPP_ENV_VAR, webAppIniPath, 1) == -1) {
ErrorDialog("Couldn't set up app environment");
return 255;
Sviluppo di applicazioni Open Web Apps multipiattaforma
32
}
// Get profile dir from webapp.ini
if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) {
ErrorDialog("Couldn't retrieve profile from web app INI file");
return 255;
}
if (removeApp) {
RemoveApplication(parser, curExeDir, profile);
return 0;
}
// Get the location of Firefox from our webapp.ini
if (NS_FAILED(parser.GetString("WebappRT", "InstallDir", firefoxDir,
MAXPATHLEN))) {
ErrorDialog("Couldn't find your Firefox install directory.");
return 255;
}
// Set up appIniPath with path to application.ini.
// This is in the Firefox installation directory.
char appIniPath[MAXPATHLEN];
snprintf(appIniPath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_INI);
if (NS_FAILED(parser.Init(appIniPath))) {
return 255;
}
// Get buildid of Firefox we're trying to load (MAXPATHLEN only for
convenience)
char buildid[MAXPATHLEN];
if (NS_FAILED(parser.GetString("App", "BuildID", buildid, MAXPATHLEN))) {
ErrorDialog("Couldn't read BuildID from Firefox application.ini");
return 255;
}
// If WebAppRT version == Firefox version, load XUL and execute the
application
if (!strcmp(buildid, NS_STRINGIFY(GRE_BUILDID))) {
if (GRELoadAndLaunch(firefoxDir, false))
return 0;
}
// Else, copy WebAppRT from Firefox installation and re-execute the process
else
CopyAndRelaunch(firefoxDir, curExePath);
return 255;
}
Segue un estratto del codice XUL e del codice JavaScript che definiscono l'interfaccia
della finestra principale del WebRT. La webapp viene caricata nell'elemento XUL
<browser> (che si comporta in maniera molto simile ad un elemento <iframe>, con alcune
proprietà e funzioni in più).
webapp.xul: <?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
Sviluppo di applicazioni Open Web Apps multipiattaforma
33
<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
<!DOCTYPE window [
<!ENTITY % webappDTD SYSTEM "chrome://webapprt/locale/webapp.dtd">
%webappDTD;
]>
<window windowtype="webapprt:webapp"
id="default"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
width="1024" height="768"
fullscreenbutton="true"
persist="screenX screenY width height sizemode"
>
<script type="application/javascript"
src="chrome://global/content/globalOverlay.js"/>
<script type="application/javascript"
src="chrome://webapprt/content/webapp.js"/>
<commandset id="mainCommandSet">
...
</commandset>
<keyset id="mainKeyset">
...
</keyset>
<menubar id="main-menubar">
<menu id="file-menu" label="&fileMenu.label;"
accesskey="&fileMenu.accesskey;">
<menupopup id="menu_FilePopup">
<menuitem id="menu_FileQuitItem"
label="&quitApplicationCmd.label;"
accesskey="&quitApplicationCmd.accesskey;"
key="key_quitApplication"
command="cmd_quitApplication"/>
</menupopup>
</menu>
<menu id="edit-menu" label="&editMenu.label;"
accesskey="&editMenu.accesskey;">
<menupopup id="menu_EditPopup"
onpopupshowing="updateEditUIVisibility()"
onpopuphidden="updateEditUIVisibility()">
<menuitem id="menu_undo"
label="&undoCmd.label;"
key="key_undo"
accesskey="&undoCmd.accesskey;"
command="cmd_undo"/>
...
</menupopup>
</menu>
</menubar>
<browser type="content-primary" id="content" flex="1"
context="contentAreaContextMenu" />
<popupset>
<menupopup id="contentAreaContextMenu" pagemenu="start"
onpopupshowing="return showContextMenu(event, this)"
onpopuphiding="hideContextMenu(event, this)">
</menupopup>
</popupset>
</window>
Sviluppo di applicazioni Open Web Apps multipiattaforma
34
webapp.js: const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://webapprt/modules/WebappRT.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "gAppBrowser",
function() document.getElementById("content"));
#ifdef MOZ_CRASHREPORTER
XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
"@mozilla.org/toolkit/crash-reporter;1",
"nsICrashReporter");
#endif
let progressListener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
onLocationChange: function onLocationChange(progress, request, location,
flags) {
// Set the title of the window to the name of the webapp, adding the origin
// of the page being loaded if it's from a different origin than the app
// (per security bug 741955, which specifies that other-origin pages loaded
// in runtime windows must be identified in chrome).
let title = WebappRT.config.app.manifest.name;
let origin = location.prePath;
if (origin != WebappRT.config.app.origin) {
title = origin + " - " + title;
// We should exit fullscreen mode if the user navigates off the app
// origin.
document.mozCancelFullScreen();
}
document.documentElement.setAttribute("title", title);
},
onStateChange: function onStateChange(aProgress, aRequest, aFlags, aStatus) {
if (aRequest instanceof Ci.nsIChannel &&
aFlags & Ci.nsIWebProgressListener.STATE_START &&
aFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
updateCrashReportURL(aRequest.URI);
}
}
};
function onLoad() {
window.removeEventListener("load", onLoad, false);
gAppBrowser.addProgressListener(progressListener,
Ci.nsIWebProgress.NOTIFY_LOCATION |
Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
updateMenuItems();
// Listen for clicks to redirect <a target="_blank"> to the browser.
// This doesn't capture clicks so content can capture them itself and do
// something different if it doesn't want the default behavior.
gAppBrowser.addEventListener("click", onContentClick, false, true);
if (WebappRT.config.app.manifest.fullscreen) {
enterFullScreen();
}
Sviluppo di applicazioni Open Web Apps multipiattaforma
35
}
window.addEventListener("load", onLoad, false);
function onUnload() {
gAppBrowser.removeProgressListener(progressListener);
}
window.addEventListener("unload", onUnload, false);
// Fullscreen handling.
function enterFullScreen() {
// We call mozRequestFullScreen here so that the app window goes in
// fullscreen mode as soon as it's loaded and not after the <browser>
// content is loaded.
gAppBrowser.mozRequestFullScreen();
// We need to call mozRequestFullScreen on the document element too,
// otherwise the app isn't aware of the fullscreen status.
gAppBrowser.addEventListener("load", function onLoad() {
gAppBrowser.removeEventListener("load", onLoad, true);
gAppBrowser.contentDocument.
documentElement.wrappedJSObject.mozRequestFullScreen();
}, true);
}
#ifndef XP_MACOSX
document.addEventListener('mozfullscreenchange', function() {
if (document.mozFullScreenElement) {
document.getElementById("main-menubar").style.display = "none";
} else {
document.getElementById("main-menubar").style.display = "";
}
}, false);
#endif
/**
* Direct a click on <a target="_blank"> to the user's default browser.
*
* In the long run, it might be cleaner to move this to an extension of
* nsIWebBrowserChrome3::onBeforeLinkTraversal.
*
* @param {DOMEvent} event the DOM event
**/
function onContentClick(event) {
let target = event.target;
if (!(target instanceof HTMLAnchorElement) ||
target.getAttribute("target") != "_blank") {
return;
}
let uri = Services.io.newURI(target.href,
target.ownerDocument.characterSet,
null);
// Direct the URL to the browser.
Cc["@mozilla.org/uriloader/external-protocol-service;1"].
getService(Ci.nsIExternalProtocolService).
getProtocolHandlerInfo(uri.scheme).
launchWithURI(uri);
// Prevent the runtime from loading the URL. We do this after directing it
// to the browser to give the runtime a shot at handling the URL if we fail
// to direct it to the browser for some reason.
event.preventDefault();
}
function updateCrashReportURL(aURI) {
Sviluppo di applicazioni Open Web Apps multipiattaforma
36
#ifdef MOZ_CRASHREPORTER
if (!gCrashReporter.enabled)
return;
let uri = aURI.clone();
// uri.userPass throws on protocols without the concept of authentication,
// like about:, which tests can load, so we catch and ignore an exception.
try {
if (uri.userPass != "") {
uri.userPass = "";
}
} catch (e) {}
gCrashReporter.annotateCrashReport("URL", uri.spec);
#endif
}
// Context menu handling code.
// At the moment there isn't any built-in menu, we only support HTML5 custom
// menus.
let gContextMenu = null;
XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
let tmp = {};
Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
return new tmp.PageMenu();
});
function showContextMenu(aEvent, aXULMenu) {
if (aEvent.target != aXULMenu) {
return true;
}
gContextMenu = new nsContextMenu(aXULMenu);
if (gContextMenu.shouldDisplay) {
updateEditUIVisibility();
}
return gContextMenu.shouldDisplay;
}
function hideContextMenu(aEvent, aXULMenu) {
if (aEvent.target != aXULMenu) {
return;
}
gContextMenu = null;
updateEditUIVisibility();
}
function nsContextMenu(aXULMenu) {
this.initMenu(aXULMenu);
}
nsContextMenu.prototype = {
initMenu: function(aXULMenu) {
this.hasPageMenu = PageMenu.maybeBuildAndAttachMenu(document.popupNode,
aXULMenu);
this.shouldDisplay = this.hasPageMenu;
},
};
Sviluppo di applicazioni Open Web Apps multipiattaforma
37
Bibliografia
[1] Tags for Identifying Languages - http://www.ietf.org/rfc/rfc4646.txt
[2] Desktop Menu Specification - http://standards.freedesktop.org/menu-spec/menu-spec-
latest.html
[3] Desktop Entry Specification - http://standards.freedesktop.org/desktop-entry-
spec/latest/
[4] Content Security Policy 1.1 - https://dvcs.w3.org/hg/content-security-policy/raw-
file/tip/csp-specification.dev.html
[5] AppCache - http://www.w3.org/TR/offline-webapps/ e
http://www.whatwg.org/specs/web-apps/current-work/multipage/offline.html#appcache
[6] CMS (Cryptographic Message Syntax) - http://tools.ietf.org/html/rfc5652I
[7] JAR File Specification -
http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#Signed_JAR_File
[8] Raw Socket API - http://www.w3.org/2012/sysapps/raw-sockets/
[9] Geolocation API - http://www.w3.org/TR/geolocation-API/
[10] XUL documentation - https://developer.mozilla.org/en-US/docs/XUL
[11] XPCOM documentation - https://developer.mozilla.org/en/docs/XPCOM
[12] A case study in architectural analysis: The evolution of the modern web browser -
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.68.52
[13] JWT (JSON Web Token) - http://openid.net/specs/draft-jones-json-web-token-
07.html
[14] Indexed Database API - http://www.w3.org/TR/IndexedDB/
[15] Interface DOMError - http://www.w3.org/TR/dom/#interface-domerror
Sviluppo di applicazioni Open Web Apps multipiattaforma
38
[16] WebRTC 1.0: Real-time Communication Between Browsers -
http://dev.w3.org/2011/webrtc/editor/webrtc.html
[17] WebGL (OpenGL ES 2.0 for the Web) - http://www.khronos.org/webgl/
[18] Fullscreen - https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#api
[19] Pointer Lock - https://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html
[20] Gamepad - https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html
[21] Web Audio API - https://dvcs.w3.org/hg/audio/raw-
file/tip/webaudio/specification.html
[22] Emscripten: An LLVM-to-JavaScript Compiler -
https://github.com/kripken/emscripten/blob/master/docs/paper.pdf?raw=true
[23] Gecko security model - https://developer.mozilla.org/en-
US/docs/Security_check_basics
[24] nsIPrincipal interface - https://developer.mozilla.org/en-
US/docs/XPCOM_Interface_Reference/nsIPrincipal
[25] XPIDL - https://developer.mozilla.org/en-US/docs/Mozilla/XPIDL
[26] The Web Origin Concept - http://www.ietf.org/rfc/rfc6454.txt
[27] T. Berners-Lee; R. Fielding; L. Masinter. Uniform Resource Identifiers (URI):
generic syntax. January 2005. Internet RFC 3986. URL:
http://www.ietf.org/rfc/rfc3986.txt
[28] The application/json Media Type for JavaScript Object Notation (JSON) -
http://www.ietf.org/rfc/rfc4627.txt
[29] Il termine “plugin” e l’elemento <iframe> sono descritti nella specifica HTML5 -
http://www.w3.org/TR/html5/
[30] The XMLHttpRequest Object - http://www.w3.org/TR/2008/WD-XMLHttpRequest-
20080415/
Tutto il codice incluso in questa trattazione è rilasciato sotto i termini della licenza MPL
(Mozilla Public License) 2.0. E’ possibile ottenerne una copia da
http://mozilla.org/MPL/2.0/.