Extensión web entre navegadores para scripts personalizados Parte 4

En este artículo, estoy completando una serie de publicaciones en las que quería hablar sobre mi experiencia escribiendo una extensión web para navegadores. Ya tenía experiencia en la creación de una extensión web, que fue instalada por unos 100,000 usuarios de Chrome, que funcionó de manera autónoma, pero en esta serie de artículos decidí profundizar en el proceso de desarrollo de la extensión web al integrarla estrechamente con el lado del servidor.

imagenimagenimagenimagenimagen

Parte 1 , Parte 2 , Parte 3

Tareas programadas


La extensión web le permite configurar un script personalizado para ejecutar automáticamente su código una vez que la página haya terminado de cargarse. Esto es conveniente, por ejemplo, cuando tiene una lista de páginas del mismo tipo para rastrear y recibir datos de la ejecución del script. O si necesita eliminar anuncios molestos en cualquier página de Internet, un código para rastrear sus acciones en un recurso web, cambiar el fondo de la página a oscuro, etc.

En este caso, a menudo es necesario recibir datos de la página en el modo de programación diaria. Por ejemplo, si el sitio tiene tasas de cambio y una solicitud directa por URL protege el argumento hash. Como tal copia, puede considerar el sitio web del Banco Central de Europa . En este caso, para obtener los tipos de cambio actuales, debe conocer la URL hash para obtener los datos correctos en formato XML.

Con la extensión web, puede establecer la frecuencia requerida para recibir datos a través de un script para solicitar tipos de cambio. La extensión web acepta una cadena en el formato de coronas como entrada, por lo tanto, para obtener los tipos de cambio en modo diario, debe especificar * * / 1 * * *.

Desde un punto de vista técnico, la parte del servidor inicia Puppeteer con la adición de un script de usuario a la página del navegador Chromium. En este caso, nos enfrentamos al problema del cierre intencional del proceso de Chromium con el tiempo, ya que un aumento en el número de procesos afectará negativamente el rendimiento general del lado del servidor. Si planea ejecutar el script de usuario en modo periódico para resolver el problema anterior, debe enviar una solicitud del script de usuario a Puppeteer para completar el proceso de Chromium.

Después de largas pruebas del proceso de implementación del planificador de tareas, se decidió rastrear el resultado de la consola de script. Por lo tanto, para detener el proceso de Chromium, el script del usuario debe tener un comando para detener el proceso en la sección de código requerida:

console.log('script is ended'); 

Con este comando, un script de usuario puede cerrar el proceso del servidor Chromium generado por Puppeteer. Esto se implementa mediante el seguimiento del evento "consola" 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 el script de usuario no tiene dicho comando, para completar el proceso de ejecución en el modo de planificador de tareas, debe hacer que el temporizador termine por la fuerza. Esto se implementa de manera simple usando 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 

Como el programador de tareas se ejecuta en el lado del servidor y el usuario a menudo necesita usar una dirección IP específica, se ha agregado una opción para usar un servidor proxy.



El usuario también puede descargar el último registro de mensajes desde la consola de Puppeteer. Por ejemplo, es conveniente para verificar el funcionamiento correcto del servidor proxy.

Atajos de teclado


Durante las pruebas y las pruebas de campo, resultó que para algunos tipos de scripts es útil tener teclas de acceso rápido para su ejecución en los recursos web. Un ejemplo de tal script es Redability.js. Este script crea una "vista limpia" para leer el contenido del sitio. Es decir, la biblioteca js analiza la estructura de la página con el contenido y le permite obtener con alta probabilidad el título, autor y contenido de la página. Después de eso, el script del usuario puede sobrescribir el html del recurso web y permitir que el usuario lea en "forma pura", sin marcas innecesarias, publicidad, etc.

Inicialmente, ejecutar un script de usuario solo era posible desde una extensión web emergente haciendo clic en el botón "Ejecutar". Esta lógica de interacción con la interfaz a menudo se justifica por consideraciones de seguridad. Pero en el ejemplo anterior, no le permite llevar fácilmente el contenido de un recurso web a una "forma pura".

Como se describió anteriormente, la extensión web le permite trabajar con el DOM a través de content.js, pero el objeto de ventana no se puede usar, por ejemplo, para almacenar parámetros, ya que no es un objeto de ventana de una pestaña abierta. Esta condición restringe el funcionamiento de la extensión web como un objeto para el seguimiento de las pulsaciones de teclas.

En el problema a resolver, es necesario transferir las "teclas de acceso rápido" desde la interfaz de extensión web al lado del servidor para su almacenamiento. Luego, cada vez que cargue una página de recursos web, obtenga una lista de "teclas de acceso rápido" y cargue un script de usuario después de hacer clic en la combinación correcta. Hotkeys.js se utiliza como biblioteca para trabajar con teclas de acceso rápido.

Después de recibir la lista de teclas de acceso rápido del lado del servidor, se ejecuta el siguiente código:

 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 función getScript genera código html para la etiqueta del script y lo escribe en la página de recursos web. Por lo tanto, tenemos en cada página la capacidad de rastrear las pulsaciones de teclas. También necesitamos pasar una matriz de teclas de acceso rápido que coincida con la identificación del script para ejecutar. Esto se implementa agregando código html para la etiqueta del script, cuyo contenido inicia una variable global para almacenar la matriz coincidente en formato JSON.

Ya se mencionó anteriormente que existe un problema de comunicación entre la página abierta del recurso web y el script de extensión de contenido extension.js de la extensión web, que se utiliza para manipular el DOM. En este caso, puede recurrir a una técnica simple para verificar el valor en localStorage, que es un objeto común para los dos puntos de interacción mencionados anteriormente.

En content.js, simplemente puede verificar el valor localStorage una vez por segundo y realizar las mismas manipulaciones DOM que cuando hizo clic en el botón "Ejecutar" en la extensión web emergente.

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

Por lo tanto, se implementa la técnica de "teclas de acceso rápido", que le permite iniciar rápidamente scripts de usuario utilizando el poder de la biblioteca interna para resolver problemas prácticos.

Conclusión


Durante la implementación de este proyecto, se resolvieron las tareas prácticas de integración de escritura entre la parte del servidor basada en meteor.js y la extensión web entre navegadores. Los escollos clave fueron SCP y los procesos de interacción entre los tres componentes de la parte del cliente: la página abierta en el navegador, los scripts content.js y background.js.

Espero que mi experiencia haga que sea más fácil escribir extensiones web más especializadas para varios navegadores.

En el futuro, se planea localizar la extensión web y escribir scripts útiles para la comunidad. Si el lector tiene una idea o desea ayudar a escribir dichos scripts de usuario, escriba mensajes privados.

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


All Articles