Canvas, kesako ?

Canvas est une balise HTML offrant une ardoise où dessiner. Créée par Apple pour Safari, la balise est en phase d'incorporation à HTML 5, et a été intégrée depuis longtemps à Firefox et Opera. Cette balise dispose d'une API avec des fonctions de dessin en deux dimensions. Bien qu'elle existe depuis longtemps, elle restait très peu utilisée, mais depuis quelques mois des annonces plus alléchantes les unes que les autres se succèdent. Par exemple, lorsque les Mozilla Labs ont lancé leur projet d'IDE en ligne, Bespin, ils ont choisi de se baser non sur une textarea enrichie mais sur Canvas pour développer l'éditeur de fichiers[1]. Il y a quelques semaines, Mozilla et Khronos ont annoncé la création d'un groupe de travail visant à proposer un standard ouvert pour faire de la 3D sur le Web. L'implémentation s'appuiera très probablement sur Canvas et JavaScript[2]. Les impressionnantes démonstrations mitonnées par Paul Rouget des nouvelles fonctionnalités de Firefox 3.5 utilisent elles aussi allègrement Canvas, et il ne se passe pas une semaine sans qu'Ajaxian ne cite de nouveaux projets utilisant cette technologie. Bref, si ce n'est déjà fait, vous devriez jeter un œil à la bête.

Canvas, copieur !

En 2005, Robert O'Callahan a ajouté à Canvas dans Gecko une nouvelle méthode, drawWindow qui permet de dessiner à l'intérieur d'un canvas une partie de la fenêtre du navigateur. A ma connaissance, aucun autre navigateur ne l'implémente. Sans doute à cause des nombreux risques qu'elle peut faire courir à l'utilisateur. Un site web pourrait tout à fait l'utiliser pour réaliser des copies d'écran des sites consultés par ses visiteurs. Les risques de vol de données et d'atteintes à la vie privée sont élevés. Pour cette raison, cette méthode n'est pas accessible dans le contexte des pages web, mais uniquement appelable depuis le chrome (grosso-modo, les extensions). Il existe cependant un moyen de contourner cette restriction, et de faire appel à la fonction depuis un contenu web: en utilisant du code signé.

Le cambouis, enfin

La procédure pour signer une application web est très bien décrite en détail sur XULfr, je ne vais pas la paraphraser. Il faut créer un certificat, empaqueter son application dans une archive jar, et la signer. Une application signée peut ensuite demander des droits supplémentaires.

Concrètement, pour utiliser le petit script ci-dessous, l'internaute devra charger dans son navigateur le certificat que vous avez utilisé pour signer l'application, puis, au moment de l'exécution de celle-ci, l'autoriser à gagner des droits. Le reste n'est que littérature, et si vous voulez tester, je vous propose ce petit bout de code:

 <html>
 <head>
 <title>Screenshot</title>
 <style type="text/css">
 body{
   background-color: #888;
 }
 </style>
 </head>
 <body>
 <iframe id="frame" width="1024" height="768" style="overflow: hidden; display: none;"></iframe>
 <form>
   <input type="text" id="url" />
   <input type="button" onclick="frame.src = document.getElementById('url').value" value="submit"/>
 </form>
 <img id="screenshot" style="display: none" />
 <canvas id="canvas" width="1024" height="768" style="display: none"></canvas>
 <script type="text/javascript">
 var canvas = document.getElementById("canvas");
 var frame  = document.getElementById('frame');
 var img    = document.getElementById('screenshot');
 var ctx    = canvas.getContext("2d");
 frame.addEventListener("load", function(){
   img.style.display = 'block';
   frame.style.display = 'block';
   ctx.scale(0.5, 0.5);
   // Sans cette ligne qui demande à l'utilisateur d'accorder certains
   // droits au script, l'appel à drawWindow renverra une erreur
   netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
   ctx.drawWindow(window,
                  frame.offsetLeft,
                  frame.offsetTop,
                  frame.offsetWidth,
                  frame.offsetHeight,
                  "rgb(0,0,0)");
   ctx.scale(2, 2);
   frame.style.display = 'none';
   img.src = canvas.toDataURL();
   img.style.display = 'block';
 }, true);
 </script>
 </body>
 </html>

Le code est simple: on ouvre le site distant dans une iframe masquée. Une fois la page chargée, on affiche brièvement l'iframe le temps de faire une copie d'écran avec drawWindow et on convertit l'image affichée dans le Canvas avec toDataURL qui va créer une url de type data:image/png;base64, contenant l'image encodée en Base64. J'affiche directement l'image, mais la poster pour la stocker sur un serveur ne demanderait qu'un appel Ajax.

Au final, on a là un moyen simple de réaliser par exemple une bookmarklet pour créer des marque-pages illustrés d'une copie du site. Mais avec 2 grosses restrictions:

  • ça ne fonctionnera qu'avec Firefox;
  • l'utilisateur doit vous faire suffisamment confiance pour accepter votre certificat et accorder les privilèges nécessaires à l'application;
  • on me souffle également que cette méthode ne copie que ce qui est affiché par le navigateur lui-même, et non par des plug-ins. Les contenus flash ne devraient donc pas être visibles;

Bref, ça ne peut être qu'un petit cadeau aux utilisateurs de Firefox, pour les autres il faudra trouver une solution côté serveur pour réaliser les copies d'écran.

Vous trouverez en pièce jointe le fichier jar contenant cette page, et le certificat ayant servi à signer le fichier. Pour les utiliser, installez-les sur un serveur et:

  • ajoutez si elles n'existent déjà les 2 lignes suivantes à la configuration d'Apache, afin qu'il envoie le bon type MIME lorsque vous demanderez les fichiers:
AddType application/x-x509-ca-cert .crt
AddType application/java-archive jar
  • appelez l'URL du certificat: Firefox devrait vous proposer de l'installer;
  • appelez l'application en utilisant le sheme jar. Par exemple, si elle est installée à la racine de votre serveur: jar:http://localhost/screen.jar!/screen.html

Oualou. Questions, commentaires ?

Notes

[1] Ben Galbraith a longuement expliqué les raisons de ce choix sur son blog, ici et

[2] et c'est au passage une très bonne nouvelle puisqu'avec la réalité augmentée dont j'ai déjà parlée, la 3D risque d'être une des principale technologies qui va enfin prendre son envol sur le Web. Or pour l'instant elle est essentiellement disponible via des technologies propriétaires comme Flash. Des tentatives sont en cours depuis longtemps pour faire de la 3D avec Canvas, si le sujet vous intéresse je vous conseille la lecture de cette introduction sur le site de Vladimir Vukićević. De son côté, Google vient d'annoncer O3D, également dans l'optique de développer un standard ouvert pour la 3D sur le web