Intercepter et modifier DOM avant que la page soit affichée à l'utilisateur

J'essaie de créer un addon Firefox (à l'aide d'addon SDK) qui modifiera la façon dont la page s'affiche, principalement comme un exercice de formation / apprentissage.

Pour certaines tâches (comme augmenter les pages avec de nouvelles fonctionnalités), utiliser pageMod est parfaitement pageMod . Les charges de page et je cours certains JS pour afficher / masquer / ajouter des éléments.

Mon problème est: puis-je effectuer une modification sur DOM (donc: le document HTML renvoyé par le serveur) avant même que la page ne commence à s'afficher?

Par exemple: la page renvoyée par le serveur est:

 <html> <body> <table> <tr> <td>Item 1.1</td> <td>Item 1.2</td> <td>Item 1.3</td> </tr> <tr> <td>Item 2.1</td> <td>Item 2.2</td> <td>Item 2.3</td> </tr> </table> </body> </html> 

Mais j'aimerais que FF soit à la place:

 <html> <body> <ul> <li>Item 1.1, Item 1.2, Item 1.3</li> <li>Item 2.1, Item 2.2, Item 2.3</li> </ul> </body> </html> 

Le faire après la chargement de la page afficherait d'abord le tableau, puis il "clignoterait" rapidement dans une liste. Cela pourrait être assez rapide, mais si je devais changer les balises <img> en <a> , par exemple pour empêcher les charges d'image (non recherchées), ce n'est pas suffisant.

J'étais en train d'utiliser le contentScriptWhen: "start" dans la pageMod et essayer d'attacher les auditeurs, mais je ne peux pas voir comment je peux réellement modifier le DOM 'à la volée' (ou l'événement évite tout type d'affichage de la page avant que toute la page ne soit chargée ).

J'ai vérifié l'extension nuage-à-bout , car elle modifie la page à la volée, mais je n'ai même pas pu l'utiliser: lorsqu'elle est attachée en tant que pageMod au start le code a échoué:

  document.getElementById('appcontent').addEventListener('DOMContentLoaded', function(e) 

Parce que document.getElementById('appcontent') est document.getElementById('appcontent') nul.

Je serais immensément reconnaissant pour certains conseils: est-il possible, comment attacher le script, comment intercepter le HTML et le renvoyer sur la voie après quelques modifications.

EDIT: Ok, alors je pense que je peux intercepter les données:

 let { Ci,Cr,CC } = require('chrome'); let { on } = require('sdk/system/events'); let { newURI } = require('sdk/url/utils'); let ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", "init"); on('http-on-examine-response', function (event) { var httpChannel = event.subject.QueryInterface(Ci.nsIHttpChannel); var traceChannel = event.subject.QueryInterface(Ci.nsITraceableChannel); if (/example.com/.test(event.subject.URI.spec)) { traceChannel.setNewListener(new MyListener()); } }, true); function MyListener(downloader) { this.data = ""; } MyListener.prototype = { onStartRequest: function(request, ctx) { this.data = []; }, onDataAvailable : function(request, context, inputStream, offset, count) { var scriptStream = new ScriptableInputStream(inputStream); this.data.push(scriptStream.read(count)); scriptStream.close(); }, onStopRequest: function(request, ctx, status) { console.log(this.data.join('')); } } 

Maintenant, sur onStopRequest j'aimerais faire quelque chose aux données et les retourner à l'origine …

Notez que cela fonctionne sur des chaînes non DOM, donc ce n'est pas parfait, mais c'est un endroit pour commencer 🙂

EDIT2:

Huh, j'ai travaillé, mais j'ai l'impression que je ne suis pas vraiment supposé de cette façon:

 onStopRequest: function(request, ctx, status) { //var newPage = this.data.join(''); var newPage = "<html><body><h1>TEST!</h1></body></html>"; var stream = converter.convertToInputStream(newPage); var count = {}; converter.convertToByteArray(newPage, count); this.originalListener.onDataAvailable(request, ctx, stream, 0, count.value); this.originalListener.onStopRequest(request, ctx, status); }, 

Mon problème est: puis-je effectuer une modification sur DOM (donc: le document HTML renvoyé par le serveur) avant même que la page ne commence à s'afficher?

Oui, l'exécution javascript commence avant que la page ne soit rendue la première fois. Le Parser DOM notifie les observateurs de la mutation , afin que vous puissiez immédiatement supprimer les éléments dès qu'ils sont ajoutés par l'analyseur.

Vous pouvez enregistrer des observateurs de mutations même dans des scripts de contenu chargés de contentScriptWhen: "start" afin qu'ils soient informés de tous les éléments ajoutés à l'arbre avant qu'ils ne soient rendus puisque les notifications d'observateurs sont effectuées dans la file d'attente de micro-tâches alors que le rendu se produit sur la macro File d'attente des tâches.

Mais je ne pouvais même pas le faire fonctionner: lorsque je suis attaché en tant que pageMod au démarrage, le code a échoué: document.getElementById('appcontent').addEventListener('DOMContentLoaded', function(e)

Bien sûr. Vous ne devez pas supposer qu'un élément en particulier – pas même la <body> – est déjà disponible dès le chargement de la page. Vous devrez attendre qu'ils soient disponibles.

Et l'événement DOMContentLoaded peut simplement être enregistré sur l'objet du document . Je ne sais pas pourquoi vous l'enregistrez sur certains éléments.

(Ou l'événement évite tout type d'affichage de la page avant que toute la page ne soit chargée).

Vous ne voulez vraiment pas cela parce que cela augmenterait les temps de chargement des pages et réduirait ainsi la réactivité du site.

 /* * contentScriptWhen: "start" * * "start": Load content scripts immediately after the document * element is inserted into the DOM, but before the DOM content * itself has been loaded */ /* * use an empty HTMLElement both as a place_holder * and a way to prevent the DOM content from loading */ document.replaceChild( document.createElement("html"), document.children[0]); var rqst = new XMLHttpRequest(); rqst.open("GET", document.URL); rqst.responseType = 'document'; rqst.onload = function(){ if(this.status == 200) { /* edit the document */ this.response.children[0].children[1].querySelector( "#content-load + div + script").remove(); /* replace the place_holder */ document.replaceChild( document.adoptNode( this.response.children[0]), document.children[0]); // use_the_new_world(); } }; rqst.send(); 

Si vous souhaitez y accéder avant que n'importe quel script ne soit exécuté, il y a des observateurs de document ici: https://developer.mozilla.org/en-US/docs/Observer_Notifications#Documents tels que le content-document-global-created