Extensiones de Chrome: Extensión de la API para admitir la navegación instantánea

Dave Tapuska
Dave Tapuska

Resumen: La API de Extensions se actualizó para admitir la memoria caché atrás/adelante y la carga previa de navegaciones. Consulta la siguiente información para obtener más detalles.

Chrome se ha esforzado por hacer que la navegación sea rápida. Las tecnologías de navegación instantánea, como la memoria caché atrás/adelante (se envió a las computadoras de escritorio en Chrome 96) y las reglas de especulación (se enviaron en Chrome 103), mejoran la experiencia de ir hacia atrás y hacia adelante. En esta publicación, exploraremos las actualizaciones que realizamos en las APIs de extensiones de navegador para admitir estos nuevos flujos de trabajo.

Comprende los tipos de páginas

Antes de la introducción de la memoria caché de atrás/adelante y la renderización previa, una pestaña individual solo tenía una página activa. Este siempre era el que se veía. Si un usuario vuelve a la página anterior, la página activa se destruirá (página B) y la página anterior del historial se reconstruirá por completo (página A). Las extensiones no tenían que preocuparse por en qué parte del ciclo de vida se encontraban las páginas, ya que solo había una para una pestaña, el estado activo o visible.

Expulsión de la página activa
Expulsión de la página activa.

Con la memoria caché atrás/adelante y la renderización previa, ya no hay una relación uno a uno entre las pestañas y las páginas. Ahora, cada pestaña almacena varias páginas y las páginas transitan entre estados en lugar de destruirse y reconstruirse.

Por ejemplo, una página podría comenzar su vida como una página renderizada previamente (no visible), integrarse en una página activa (visible) cuando el usuario hace clic en un vínculo y, luego, almacenarse en la caché de Atrás/Adelante (no visible) cuando el usuario navega a otra página, todo sin que la página se destruya. Más adelante en este artículo, veremos las nuevas propiedades expuestas para ayudar a las extensiones a comprender en qué estado se encuentran las páginas.

Tipos de páginas
Tipos de páginas.

Ten en cuenta que una pestaña puede tener una serie de páginas renderizadas previamente (no solo una), una sola página activa (visible) y una serie de páginas almacenadas en caché de Atrás/Adelante.

¿Qué cambiará para los desarrolladores de extensiones?

FrameId == 0

En Chromium, nos referimos al marco superior o principal como el marco más externo.

Es posible que los autores de extensiones que suponen que el frameId del marco más externo es 0 (una práctica recomendada anterior) tengan problemas. Dado que una pestaña ahora puede tener varios marcos externos (páginas renderizadas previamente y almacenadas en caché), la suposición de que hay un solo marco externo para una pestaña es incorrecta. frameId == 0 seguirá representando el marco más externo de la página activa, pero los marcos más externos de otras páginas en la misma pestaña no serán cero. Se agregó un nuevo campo frameType para solucionar este problema. Consulta la sección "¿Cómo determino si un marco es el más externo?" de esta publicación.

Ciclo de vida de los fotogramas en comparación con los documentos

Otro concepto que es problemático con las extensiones es el ciclo de vida del marco. Un marco aloja un documento (que está asociado con una URL confirmada). El documento puede cambiar (por ejemplo, cuando se navega), pero el frameId no lo hará, por lo que es difícil asociar que algo sucedió en un documento específico con solo frameIds. Presentamos el concepto de documentId, que es un identificador único para cada documento. Si se navega por un marco y se abre un documento nuevo, el identificador cambiará. Este campo es útil para determinar cuándo las páginas cambian su estado de ciclo de vida (entre renderización previa, activa o almacenada en caché) porque permanece igual.

Eventos de navegación web

Los eventos del espacio de nombres chrome.webNavigation pueden activarse varias veces en la misma página según el ciclo de vida en el que se encuentren. Consulta las secciones "¿Cómo sé en qué ciclo de vida se encuentra la página?" y "¿Cómo determino cuándo una página realiza una transición?".

¿Cómo puedo saber en qué ciclo de vida se encuentra la página?

Se agregó el tipo DocumentLifecycle a varias APIs de extensiones en las que frameId estaba disponible anteriormente. Si el tipo DocumentLifecycle está presente en un evento (como onCommitted), su valor es el estado en el que se generó el evento. Siempre puedes consultar información de los métodos WebNavigation getFrame() y getAllFrames(), pero siempre se prefiere usar el valor del evento. Si usas cualquiera de los métodos, ten en cuenta que el estado de la trama puede cambiar entre el momento en que se generó el evento y el momento en que se resuelven las promesas que muestran ambos métodos.

DocumentLifecycle tiene los siguientes valores:

  • "prerender" : No se presenta al usuario en este momento, pero se prepara para mostrarse.
  • "active": Se muestra actualmente al usuario.
  • "cached": Se almacena en la memoria caché atrás/adelante.
  • "pending_deletion": Se está destruyendo el documento.

¿Cómo determino si un marco es el más externo?

Anteriormente, las extensiones podrían haber verificado si frameId == 0 determina si el evento que se produce es para el marco más externo o no. Con varias páginas en una pestaña, ahora tenemos varios marcos externos, por lo que la definición de frameId es problemática. Nunca recibirás eventos sobre un fotograma almacenado en caché de Atrás/Adelante. Sin embargo, para los fotogramas renderizados previamente, frameId no será cero para el fotograma más externo. Por lo tanto, usar frameId == 0 como indicador para determinar si es el marco más externo es incorrecto.

Para ayudar con esto, presentamos un tipo nuevo llamado FrameType, de modo que ahora es fácil determinar si el marco es realmente el más externo. FrameType tiene los siguientes valores:

  • "outermost_frame": Por lo general, se conoce como el marco superior. Ten en cuenta que hay varios de estos. Por ejemplo, si tienes páginas renderizadas previamente y almacenadas en caché, cada una tiene un marco más externo que podría llamarse su marco superior.
  • "fenced_frame": Se reserva para uso futuro.
  • "sub_frame": Por lo general, un iframe.

Podemos combinar DocumentLifecycle con FrameType y determinar si un fotograma es el activo más externo. Por ejemplo: tab.documentLifecycle === “active” && frameType === “outermost_frame”

¿Cómo soluciono los problemas de horario de uso con los marcos?

Como dijimos anteriormente, un marco aloja un documento y puede navegar a un nuevo documento, pero frameId no cambiará. Esto genera problemas cuando recibes un evento con solo un frameId. Si buscas la URL del marco, es posible que sea diferente de la que se registró cuando ocurrió el evento. Esto se denomina problema de horario de uso.

Para abordar este problema, presentamos documentId (y parentDocumentId). El método webNavigation.getFrame() ahora hace que frameId sea opcional si se proporciona un documentId. El documentId cambiará cada vez que se navegue por un marco.

¿Cómo determino cuándo se produce una transición de página?

Existen indicadores explícitos para determinar cuándo una página realiza la transición entre estados.

Veamos los eventos WebNavigation.

En la primera navegación de cualquier página, verás cuatro eventos en el orden que se indica a continuación. Ten en cuenta que estos cuatro eventos pueden ocurrir con el estado DocumentLifecycle como "prerender" o "active".

onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted

Esto se ilustra en el siguiente diagrama, que muestra cómo documentId cambia a "xyz" cuando la página renderizada previamente se convierte en la página activa.

El documentId cambia cuando la página renderizada previamente se convierte en la página activa.
El documentId cambia cuando la página renderizada previamente se convierte en la página activa.

Cuando una página realice la transición de la caché atrás/adelante o la renderización previa al estado activo, se producirán tres eventos más (pero con DocumentLifecyle como "active").

onBeforeNavigate
onCommitted
onCompleted

El documentId seguirá siendo el mismo que en los eventos originales. Esto se ilustra más arriba cuando se activa documentId == xyz. Ten en cuenta que se activan los mismos eventos de navegación, excepto el evento onDOMContentLoaded, porque la página ya se cargó.

Si tienes comentarios o preguntas, no dudes en hacerlos en el grupo chromium-extensions.