Extension Web multi-navigateur pour les scripts personnalisés Partie 4

Dans cet article, je complète une série de publications dans lesquelles je voulais parler de mon expérience dans l'écriture d'une extension web pour les navigateurs. J'avais déjà de l'expérience dans la création d'une extension Web, qui a été installée par environ 100000 utilisateurs de Chrome, qui fonctionnait de manière autonome, mais dans cette série d'articles, j'ai décidé de me plonger dans le processus de développement de l'extension Web en l'intégrant étroitement avec le côté serveur.

imageimageimageimageimage

Partie 1 , partie 2 , partie 3

Tâches planifiées


L'extension Web vous permet de configurer un script personnalisé pour exécuter automatiquement son code une fois le chargement de la page terminé. Cela est pratique, par exemple, lorsque vous disposez d'une liste de pages du même type pour analyser et recevoir des données de l'exécution de script. Ou si vous devez supprimer les publicités gênantes sur n'importe quelle page sur Internet, un code pour suivre vos actions sur une ressource Web, changer l'arrière-plan de la page en sombre, etc.

Dans ce cas, il est souvent nécessaire de recevoir des données de la page en mode horaire quotidien. Par exemple, si le site a des taux de change et qu'une demande directe par URL protège l'argument haché. En tant que telle copie, vous pouvez consulter le site Web de la Banque centrale d'Europe . Dans ce cas, pour obtenir les taux de change actuels, vous devez connaître l'URL hachée pour obtenir les données correctes au format XML.

À l'aide de l'extension Web, vous pouvez définir la fréquence requise pour recevoir des données via un script pour demander des taux de change. L'extension Web accepte une chaîne au format des couronnes en entrée, par conséquent, pour obtenir des taux de change en mode quotidien, vous devez spécifier * * / 1 * * *.

D'un point de vue technique, la partie serveur démarre Puppeteer avec l'ajout d'un script utilisateur à la page du navigateur Chromium. Dans ce cas, nous sommes confrontés au problème de l'arrêt intentionnel du processus Chromium au fil du temps, car une augmentation du nombre de processus affectera négativement les performances globales du côté serveur. Si vous prévoyez d'exécuter le script utilisateur en mode périodique pour résoudre le problème ci-dessus, vous devez envoyer une demande du script utilisateur à Puppeteer pour terminer le processus Chromium.

Après de longs tests du processus d'implémentation du planificateur de tâches, il a été décidé de suivre la sortie de la console de script. Ainsi, pour arrêter le processus Chromium, le script utilisateur doit avoir une commande pour arrêter le processus dans la section de code requise:

console.log('script is ended'); 

À l'aide de cette commande, un script utilisateur peut fermer le processus du serveur Chromium généré par Puppeteer. Ceci est implémenté en suivant l'événement «console» de Puppeteer:

 //initialize browser and page page.on('console', async msg => { for (let i = 0; i < msg.args().length; ++i) { if(await msg.args()[i].jsonValue() == 'script is ended') { await browser.close(); } } }); 

Si le script utilisateur ne dispose pas d'une telle commande, pour terminer le processus d'exécution en mode planificateur de tâches, vous devez forcer la minuterie à se terminer. Ceci est implémenté de manière simple en utilisant setTimeout:

 const timeLimitCron = 30; const timeout = setTimeout(async () => { if(browser) { await browser.close(); } clearTimeout(timeout); }, timeLimitCron * 1000 ); //time limit for cron, then forced closing, default 30 seconds 

Étant donné que le planificateur de tâches s'exécute côté serveur et que l'utilisateur doit souvent utiliser une adresse IP spécifique, une option d'utilisation d'un serveur proxy a été ajoutée.



L'utilisateur peut également télécharger le dernier journal des messages à partir de la console Puppeteer. Par exemple, il est pratique pour vérifier le bon fonctionnement du serveur proxy.

Raccourcis clavier


Au cours des tests et des tests sur le terrain, il s'est avéré que pour certains types de scripts, il est utile d'avoir des touches de raccourci pour leur exécution sur les ressources Web. Un exemple d'un tel script est Redability.js. Ce script crée une «vue claire» pour lire le contenu du site. Autrement dit, la bibliothèque js analyse la structure de la page avec le contenu et vous permet d'obtenir avec une forte probabilité le titre, l'auteur et le contenu de la page. Après cela, le script utilisateur peut remplacer le html de la ressource Web et permettre à l'utilisateur de lire en «forme pure», sans balisage inutile, publicité, etc.

Initialement, l'exécution d'un script utilisateur n'était possible qu'à partir d'une extension Web contextuelle en cliquant sur le bouton «Exécuter». Cette logique d'interaction avec l'interface est souvent justifiée par des considérations de sécurité. Mais dans l'exemple ci-dessus, il ne vous permet pas d'amener facilement le contenu d'une ressource Web à une «forme pure».

Comme décrit ci-dessus, l'extension Web vous permet de travailler avec le DOM via content.js, mais l'objet fenêtre ne peut pas être utilisé, par exemple, pour stocker des paramètres, car il ne s'agit pas d'un objet fenêtre d'un onglet ouvert. Cette condition limite le fonctionnement de l'extension Web en tant qu'objet de suivi des frappes.

Dans le problème à résoudre, il est nécessaire de transférer les touches de raccourci de l'interface d'extension Web vers le côté serveur pour le stockage. Ensuite, chaque fois que vous chargez une page de ressources Web, obtenez une liste de «touches de raccourci» et chargez un script utilisateur après avoir cliqué sur la bonne combinaison. Hotkeys.js est utilisé comme bibliothèque pour travailler avec des touches de raccourci.

Après avoir reçu la liste des touches de raccourci du côté serveur, le code suivant est exécuté:

 var hotKeysMap = {keys: [], id: []}; for(var i in data.response.data) { hotKeysMap.keys.push(data.response.data[i]['hotkeys']); hotKeysMap.id.push(data.response.data[i]["_id"]); } if(hotKeysMap.keys) { getScript("hotkeys.js", function() { var script = document.createElement("script"); script.setAttribute("class", "gCore-hotKeys"); script.setAttribute("data-endpoint", event.data.endPoint); script.setAttribute("data-token", event.data.token); script.setAttribute("data-userid", event.data.userId); script.innerHTML = "GChotKeys = JSON.parse(\"" + JSON.stringify(hotKeysMap).replace(/"/g, "\\\"") + "\");\n" + "hotkeys(GChotKeys.keys.join(','), function(event, handler) {" + " event.preventDefault();" + " localStorage.setItem('runHotKeysScript', GChotKeys.id[GChotKeys.keys.indexOf(handler.key)]);" + "});"; document.body.appendChild(script); }); } 

La fonction getScript génère du code html pour la balise de script et l'écrit sur la page de ressources Web. Ainsi, nous avons sur chaque page la possibilité de suivre les frappes. Nous devons également transmettre un tableau de raccourcis correspondant à l'ID du script à exécuter. Ceci est implémenté en ajoutant du code html pour la balise de script, dont le contenu lance une variable globale pour stocker le tableau correspondant au format JSON.

Il a déjà été mentionné ci-dessus qu'il y a un problème de communication entre la page ouverte de la ressource Web et le script d'extension de contenu extension.js de l'extension Web, qui est utilisé pour manipuler le DOM. Dans ce cas, vous pouvez recourir à une technique simple pour vérifier la valeur dans localStorage, qui est un objet commun pour les deux points d'interaction mentionnés ci-dessus.

Dans content.js, vous pouvez simplement vérifier la valeur localStorage une fois par seconde et effectuer les mêmes manipulations DOM que lorsque vous cliquez sur le bouton "Exécuter" dans l'extension web pop-up.

 setInterval(function() { if(localStorage.getItem('runHotKeysScript')) { // run user script localStorage.removeItem('runHotKeysScript'); } }, 1000); 

Ainsi, la technique des «touches de raccourci» est implémentée, ce qui vous permet de lancer rapidement des scripts utilisateur en utilisant la puissance de la bibliothèque interne pour résoudre des problèmes pratiques.

Conclusion


Au cours de la mise en œuvre de ce projet, les tâches pratiques d'écriture de l'intégration entre la partie serveur basée sur meteor.js et l'extension Web multi-navigateur ont été résolues. Les principales pierres d'achoppement étaient SCP et les processus d'interaction entre les trois composants de la partie client - la page ouverte dans le navigateur, les scripts content.js et background.js.

J'espère que mon expérience facilitera l'écriture d'extensions Web multi-navigateurs plus spécialisées.

À l'avenir, il est prévu de localiser l'extension Web et d'écrire des scripts utiles à la communauté. Si le lecteur a une idée ou souhaite aider à écrire de tels scripts utilisateur, veuillez alors écrire dans des messages privés.

Source: https://habr.com/ru/post/fr417391/


All Articles