« Application web » en local
Ce qu'on peut faire avec une page web locale s'est considérablement réduit ces dernières années, essentiellement pour des questions de sécurité. Petit topo sur la manière de retrouver un peu de puissance avec des pages web non déployées sur un serveur.
Projet de travail
- projet
- data
- index.md
- js
- script.js
- index.html
- data
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Projet de test</title>
</head>
<body>
<script src="js/script.js"></script>
</body>
</html>
index.html
window.fetch('data/index.md').then(response => {
return response.text();
}).then(text => {
console.log(text);
}).catch(err => {
console.warn(err);
});
js/script.js
Rien de bien compliqué donc : une simple page web avec un fichier javascript qui essaye de lire un fichier texte.
Restrictions de sécurité
Rien de bien compliqué mais pourtant ce code ne fonctionne pas (plus) si on ouvre la page web directement depuis le système de fichier et pas depuis un serveur web. La faute à une faille de sécurité qu'il a fallu corriger. Ainsi, depuis Firefox 68 (juillet 2019), on ne peut plus faire de requête de ressources même si elles se trouvent dans le même répertoire que la page HTML. Cette protection se paramètre via la configuration privacy.file_unique_origin
. Si on passe sa valeur à false
tout rentre dans l'ordre.
Néanmoins elle n'est pas suffisante si votre projet utilise des ressources en dehors de son dossier, si vous voulez lire par exemple les images stockées dans le dossier dédié (XDG_PICTURES_DIR
), ou bien si vous faites références à des données ailleurs dans le système de fichiers via des liens symboliques. Dans ce cas, c'est la configuration Security.fileuri.strict_origin_policy
qui permet d'accéder à l'extérieur du dossier parent en la passant à false
.
On doit a priori faire sauter — a minima — deux configurations de sécurité pour retrouver la liberté de développement / utilisation d'applications web locales.
Il n'est pas souhaitable de supprimer ces configuration de sécurité par défaut. On va donc créer un profil Firefox dédié à l'ouverture des pages web locales.
Profil firefox pour fichiers locaux
Création du profil
On créé donc un profil Firefox dédié aux applications web locales :
firefox -CreateProfile web-apps
Dans ce profil, on va modifier les règles de sécurité liées à la lecture des fichiers locaux (about:config
) :
privacy.file_unique_origin
:false
Security.fileuri.strict_origin_policy
:false
On peut maintenant lancer une nouvelle instance de Firefox avec ce profil et y ouvrir des pages HTML locales qui fonctionneront parfaitement.
Lanceur firefox dédié au profil
On peut aller plus loin et créer un lanceur dédié à ce profil :
[Desktop Entry]
Type=Application
Encoding=UTF-8
Name=FF Web Apps
Comment=Firefox Web Apps
Exec=firefox -P "web-apps" --new-window --class webapps %u
Icon=/path/to/icons/firefox.png
Actions=new-window;
StartupNotify=True
StartupWMClass=webapps
Categories=Internet
Terminal=false
[Desktop Action new-window]
Name=Open a new window
Exec=firefox -P "web-apps" --new-window %u
~/.local/share/applications/ff-webapps.desktop
Maintenant, un clic droit sur une page HTML, puis « Ouvrir avec une autre application... » et choisir « FF Web Apps ».
Script de lancement
On peut aussi écrire un simple script bash de lancement à intégrer dans Nautilus :
#!/bin/bash
if [ $1 ]; then
firefox -P "web-apps" --new-window --class "webapps" $1
fi
~/.local/share/nautilus/scripts/webapp
Maintenant, clic droit sur une page HTML puis « scripts » puis « webapp ».
Application Gtk
Une autre solution consiste à créer une petite application gtk se charge d'afficher la page index.html
:
#!/usr/bin/gjs
imports.gi.versions.Gtk = '3.0';
imports.gi.versions.WebKit2 = '4.0';
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Webkit = imports.gi.WebKit2;
let window = null;
const app = new Gtk.Application();
app.connect('activate', () => window.present());
app.connect('startup', () => {
window = new Gtk.ApplicationWindow({
application: app,
title: "Web App",
default_height: 1080,
default_width: 1920,
window_position: Gtk.WindowPosition.CENTER
});
const webKitSettings = new Webkit.Settings({
allow_file_access_from_file_urls: true,
allow_top_navigation_to_data_urls: false,
allow_modal_dialogs: false,
enable_developer_extras: true,
enable_fullscreen: false,
enable_html5_database: false,
enable_html5_local_storage: false,
enable_hyperlink_auditing: false,
enable_offline_web_application_cache: false,
enable_java: false,
enable_javascript: true,
enable_plugins: false,
media_playback_requires_user_gesture: true,
enable_write_console_messages_to_stdout: true
});
const webView = new Webkit.WebView({
visible: true,
is_ephemeral: true,
settings: webKitSettings
});
webView.load_uri(GLib.filename_to_uri (`${GLib.get_current_dir()}/index.html`, null));
window.add(webView);
window.show_all();
});
app.run(ARGV);
app.gjs
à placer à côter de index.htmlOn peut ensuite rendre le fichier exécutable et on pourra le lancer avec la commande « Exécuter comme un programme » depuis le menu contextuel, mais ce n'est pas franchement pratique, surtout qu'une fenêtre de terminal s'ouvre en même temps.
Serveur python
On peut rapidement lancer un serveur http python dans le dossier local :
python3 -m http.server
La commande indique ensuite où les pages du dossier sont disponibles :
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/)
Adaptation du code javascript
Concernant les modules javascript : les spécifications EcmaScript permettent de déclarer un import sans préciser l'extension du fichier mais ils ne fonctionneront pas en local dans ce cas là (Ne pas les déclarer peut aussi provoquer d'autres soucis). Mieux vaut donc les déclarer.
De plus, si on choisit la technique de l'application GTK, il faut tenir compte du fait que Webkit ne supporte pas les composants web étendants les élements existants. Il faudra utiliser un polyfill.
Ressources et références
Historique
- 2021-06-25
- add: Création d'une application GTK
- 2020-12-27
- add : création de la note