Cómo me deshice de mil pestañas ...

... y llegó 3 años tarde. Idealmente, debería ser así: el usuario inicia el navegador, y el navegador muestra lo que necesita el usuario. Pero si bien esto no está implementado, debe utilizar los motores de búsqueda. Idealmente, debería ser así: el usuario abre un motor de búsqueda, ingresa una consulta de búsqueda y muestra lo que necesita el usuario. Pero aunque el botón "Me siento afortunado" no funciona tan bien (aunque últimamente ha habido un movimiento notable en esta dirección), a veces hay que ir a varias direcciones desde la página de resultados de búsqueda.

El escenario de usar motores de búsqueda, aparentemente, se solucionó históricamente (cuando Internet era lento): al llegar a la página de resultados de búsqueda, abrí varias pestañas en segundo plano, y mientras el resto se cargaba, ya era posible leer la primera pestaña. En el caso en que encontré la información necesaria en una de las pestañas, el resto tuvo que cerrarse manualmente. Si no se cerró de inmediato, las pestañas permanecieron colgadas, lo que infló el número de pestañas abiertas en el navegador, que, por regla general, rara vez se cerró después de eso.

Además, si hace clic en la página con los enlaces que se abren en una nueva ventana, se crean varias pestañas con pestañas (lógicamente). Cuando encuentra la información que necesita, no siempre puede recordar qué pestañas están conectadas, no puede cerrar todo, lo que también lleva a inflar el número de pestañas abiertas.

Siempre necesité el botón "Encontrado" , que limpiaría después de mí las consecuencias de la búsqueda (llamémoslo "Tuve suerte" ). Después de sumergirme en el mundo de las extensiones de navegador, pensé que era algo que podría ayudar en este caso. Tan débilmente comenzó a aparecer el deseo de escribir una extensión que resolviera mis problemas.

Te contaré mi historia, dirigiré la historia en orden cronológico, las conclusiones pueden ser inesperadas.

Primer paso hacia


Lo primero que hice fue configurar la infraestructura: webpack + babel . Y de inmediato no me gustó ese código duplicado de babel para sus ayudantes en cada módulo. Era posible configurarlo para usar el objeto babelHelper, pero luego el archivo de código babelHelpertenía que estar conectado en la configuración del paquete web . Mantener un archivo de este tipo en el proyecto y señalarlo entryera feo, hice un complemento para el paquete web que lo hizo automáticamente por mí. Después de gastar mucho esfuerzo en el primer paso y escribir más código para la extensión en sí, disminuí un poco la velocidad.

Enchufar

Fundación


Pasó el tiempo y solo estaba disponible un complemento para webpack, que no resolvió mis problemas de ninguna manera. Y cada vez que buscaba algo y no cerraba las pestañas, había un pensamiento: "Sería bueno completar esa extensión ..." El deseo creció y creció, y ahora, un buen día, la cantidad se convirtió en calidad.

Es hora de decir cuál era la idea principal: el
usuario accede a la página de resultados de búsqueda: SICKLE , analizamos los resultados, guardamos las direcciones de enlace para nosotros, después de que el usuario hace clic en una de las direcciones, le muestra una notificación con el resto de las direcciones y el botón "Encontrado" para cerrar pestañas.

Cuando va a la página puede haber varias opciones. El más simple: una solicitud, una respuesta del servidor ( 200) Lo más difícil: una solicitud: varias redirecciones de servidor ( 3xx ), después de lo cual la redirección del cliente (usando <meta/>o javascript), la API de historial también está en la parte superior . Y las combinaciones entre ellos, como regla, la mayoría de los sitios entran en esta categoría.

Caso de transición simple:

El caso de una transición simple (respuesta 200)

Caso de transición complejo:

Caso de transición complejo (redirecciones de cliente 3xx +)

Es decir, guardar la dirección de la página y verificar solo no siempre es suficiente. Por lo tanto, debe crear una transición lógica, donde anote todas las direcciones encontradas en la ruta y luego verifique que la transición lógica contenga la dirección almacenada. La tarea es clara, pero no todo es tan sencillo en la ejecución.

En Chrome, hay dos API relacionadas con la navegación: webNavigation y webRequest , cada una con sus propios eventos. El primero, conecta las transiciones y la interfaz de usuario del navegador, el último, las solicitudes de red subyacentes. Por lo tanto, si el cambio de dirección en la página se produjo debido a la API de historial, no habrá eventos para este último, y si se producen redireccionamientos durante una solicitud de red, el primero no lo informa en absoluto. Por lo tanto, es necesario usar ambas API, recolectando una pizca de cada evento de cada API, para formar una transición lógica.

Algunos detalles
, webNavigation (wN) :

onBeforeNavigate -> onCommitted -> onDOMContentLoaded -> onCompleted

webRequest (wR):

onBeforeRequest -> [onBeforeRedirect -> onBeforeRequest]* -> onCompleted | onErrorOccurred

wR wN ( ), .. - wN.onBeforeNavigate wR.onBeforeRequest, - . .

, , .

Desarrollo


... Volvamos al momento en que la cantidad se ha convertido en calidad. Ha pasado una cantidad significativa de tiempo desde el comienzo del desarrollo hasta este punto: los navegadores comenzaron a admitir módulos es6 , shadow DOM y otras características modernas. Para construir el proyecto, me mudé a Rollup , esta vez no tuve que escribir un complemento. Después de construir la base, la capacidad de obtener información sobre cualquier transición en cualquier pestaña, queda por implementar la lógica de analizar SICKLES compatibles y mostrar notificaciones en páginas relacionadas.

La primera tarea es bastante primitiva: conocemos la dirección de la SICKLE, subimos al contenido de la página usando el script de contenido, obtenemos los datos que nos interesan, los guardamos, esperamos que el usuario vaya a una de las páginas para mostrarle una notificación con las otras páginas.

Para la segunda tarea, debe implementar la notificación en sí, qué mostrar al usuario en la página. Y aquí, también, los scripts de contenido no pueden hacer.

Inicialmente, solo había un controlador (también conocido como controlador) que era responsable de la lógica durante la interacción del usuario con los motores de búsqueda. Entonces surgió la idea, ¿por qué no mostrar notificaciones en pestañas relacionadas cuando el usuario simplemente hace clic en los enlaces que se abren en pestañas nuevas? Tuve que rehacer la lógica, haciéndola más universal. Similar al middleware React / Redux, puede conectar varios controladores de transición, lo que en el futuro le permitirá implementar la capacidad de deshabilitar / habilitar varios controladores en la configuración de la extensión.

Intimidad


Dado que la notificación es un panel en la parte inferior de la pantalla y se agrega al diseño de la página, el script en la página puede acceder a este elemento de la misma manera que cualquier otro elemento en esta página. Es decir, en teoría, la página podría averiguar qué consulta de búsqueda utilizó, en qué motor de búsqueda y qué otras páginas se le ofrecieron, lo cual no es muy bueno.

Una tecnología llamada shadow DOM viene al rescate . No se recomienda usarlo closed modeen la web al crearlo shadowRoot, ya que no tiene mucho sentido (aún debe almacenar el enlace al elemento en shadowRootalgún lugar si desea tener acceso a él mediante programación; también puede anular la función attachShadowpara crearshadowRooten modo abierto, y luego los scripts cargados después de la redefinición ya usarán la nueva versión de la función).

En el caso de la expansión, esto no es así. Los guiones de contenido y los guiones de página viven en mundos paralelos. Los scripts de la página no tienen acceso a los objetos definidos en los scripts de contenido, mientras que los scripts de contenido operan con la implementación nativa de las funciones DOM de los objetos (una función anulada por un script de la página no tiene efecto en la función con la que funciona el script de contenido). Combinando estas dos condiciones, vemos que es posible crear un elemento con shadowRootuno privado almacenando el enlace en una variable.

En este caso, el script de la página podrá acceder solo al elemento contenedor, que estará vacío para él. No podrá recibir el texto de la solicitud o las páginas propuestas. Se debe tener cuidado de no dar un enlace a ningún elemento dentro de la notificación o texto sin formato en los eventos generados. Por lo tanto, en la extensión, la identificación generada se usa en eventos, y el script de fondo ya comprende lo que requiere esta identificación. Para la página, esta identificación no tiene sentido.

Dificultades de traducción


Inicialmente, la extensión se desarrolló solo para Google Chrome , pero desde la API de WebExtensions , en algún lugar de mi cabeza se mantuvo la capacidad de portar a otros navegadores. Y la presencia de webextension-polyfill inspiró confianza. Pero no importa cómo. El polyphil para esta extensión solo trajo la capacidad de usar la API de Chrome con promesas.

Firefox se ha convertido en una decepción del año. La discrepancia de la API de Firefox Chrome ( Bug 1543647 , Bug 1595621 ) resultó ser crítica para que la extensión funcione, podemos decir que no funciona en este navegador (como se esperaba).

Vivaldi era el más cercano, pero tampoco sin costo. EventowN.onCreatedNavigationTargetNo ocurre cuando el usuario abre el enlace con el botón central del mouse o con el botón Shift|Ctrl+ izquierdo del mouse, en lugar del evento wN.onCommitted transitionType == 'start_page', que no está en la API de Chrome , debido a esto, la extensión no funciona correctamente en todos los casos. También en Vivaldi no funcionan las teclas de acceso rápido para extensiones. Lo que es una característica asesina en este caso en Chrome, le permite navegar rápidamente por las pestañas y cerrarlas, sin la necesidad de usar un mouse para esto.

Conclusión


Durante la escritura del código, la lógica para mostrar notificaciones cambió varias veces, simplificándose cada vez. Como resultado, resultó que no era posible cercar el jardín con transiciones lógicas, sino detectar las "transiciones relacionadas" del usuario (en caso de que wN.onCommittedhaya una bandera transitionTypeque indique para qué fue la transición, en muchos casos es "enlace", lo que significa que el usuario cambió por referencia), lo que simplificaría enormemente el código y funcionaría en muchos casos, pero no en todos.

Además, al no estar en el tema, esperaba más compatibilidad en términos de API de webExtensions. Como siempre, es bueno vivir en un mundo de navegadores modernos cuando no necesita soporte para versiones anteriores. Las animaciones CSS son algo maravilloso: para lo que solías usar la biblioteca js ahora se hace en unas pocas líneas en css. Los elementos personalizados no funcionan en extensiones, pero el DOM sombra funciona, lo que le permite aprovechar todas sus características.

Expansión
chrome web store: Handy Search

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


All Articles