Extensões do Chrome: ampliação da API para oferecer suporte à navegação instantânea

Dave Tapuska
Dave Tapuska

Texto longo, leia o resumo: a API Extensions foi atualizada para oferecer suporte ao cache de avanço e retorno, pré-carregando navegações. Veja os detalhes abaixo.

O Chrome tem trabalhado muito para tornar a navegação mais rápida. As tecnologias de navegação instantânea, como cache de avanço e retorno (enviados para computadores no Chrome 96) e Regras de especulação (enviadas no Chrome 103), melhoram a experiência de voltar e avançar. Nesta postagem, vamos explorar as atualizações que fizemos nas APIs de extensões do navegador para acomodar esses novos fluxos de trabalho.

Noções básicas sobre os tipos de páginas

Antes da introdução do cache de avanço e retorno e da pré-renderização, uma guia individual tinha apenas uma página ativa. Sempre foi aquela que esteve visível. Se um usuário retornar à página anterior, a página ativa será destruída (página B) e a anterior no histórico será completamente reconstruída (página A). As extensões não precisavam se preocupar com a parte das páginas do ciclo de vida, porque havia apenas uma para cada guia: o estado ativo/visível.

Remoção da página ativa
Remoção da página ativa.

Com o cache de avanço e retorno e a pré-renderização, não há mais uma relação de um para um entre guias e páginas. Agora, cada guia armazena várias páginas e transições entre estados, em vez de serem destruídas e reconstruídas.

Por exemplo, uma página pode começar a vida como uma página pré-renderizada (não visível), fazer a transição para uma página ativa (visível) quando o usuário clicar em um link e, em seguida, ser armazenada no cache de avanço e retorno (não visível) quando o usuário navegar para outra página, tudo sem que a página seja destruída. Mais adiante neste artigo, analisaremos as novas propriedades expostas para ajudar as extensões a entender em que estado as páginas estão.

Tipos de páginas
Tipos de páginas.

Observe que uma guia pode ter uma série de páginas pré-renderizadas (não apenas uma), uma única página ativa (visível) e uma série de páginas de navegação em cache.

O que vai mudar para os desenvolvedores de extensões?

FrameId == 0

No Chromium, nos referimos ao frame principal/superior como o frame mais externo.

Os autores de extensão que presumem que o frameId do frame mais externo é 0 (uma prática recomendada anterior) podem ter problemas. Como uma guia agora pode ter vários frames externos (páginas pré-renderizadas e armazenadas em cache), a suposição de que há um único frame mais externo para uma guia é incorreta. frameId == 0 continuará a representar o frame mais externo da página ativa, mas os frames mais externos das outras páginas na mesma guia serão diferentes de zero. Um novo campo frameType foi adicionado para corrigir esse problema. Consulte a seção “Como determinar se um frame é o mais externo?” desta postagem.

Ciclo de vida de frames versus documentos

Outro conceito que é problemático com extensões é o ciclo de vida do frame. Um frame hospeda um documento (que está associado a um URL confirmado). O documento pode mudar (por exemplo, navegando), mas o frameId não. Por isso, é difícil associar o que aconteceu em um documento específico apenas aos frameIds. Estamos introduzindo o conceito de documentId, que é um identificador exclusivo para cada documento. Se um frame for navegado e abrir um novo documento, o identificador mudará. Esse campo é útil para determinar quando as páginas mudam o estado do ciclo de vida (entre pré-renderização/ativo/armazenado em cache) porque ele permanece o mesmo.

Eventos de navegação na Web

Os eventos no namespace chrome.webNavigation podem ser disparados várias vezes na mesma página, dependendo do ciclo de vida em que ela está. Consulte as seções Como faço para saber em qual ciclo de vida a página está? e Como determino quando uma página faz a transição?.

Como faço para saber em qual ciclo de vida a página está?

O tipo DocumentLifecycle foi adicionado a várias APIs de extensões em que o frameId estava disponível anteriormente. Se o tipo DocumentLifecycle estiver presente em um evento (como onCommitted), o valor dele será o estado em que o evento foi gerado. Você sempre pode consultar informações dos métodos WebNavigation getFrame() e getAllFrames(), mas é preferível usar o valor do evento. Se você usar qualquer um dos métodos, saiba que o estado do frame pode mudar entre o momento em que o evento foi gerado e o momento em que as promessas retornadas pelos dois métodos são resolvidas.

O DocumentLifecycle tem estes valores:

  • "prerender" : não é apresentado ao usuário no momento, mas está se preparando para possivelmente ser exibido.
  • "active": exibido ao usuário.
  • "cached": armazenado no cache de avanço e retorno.
  • "pending_deletion": o documento está sendo destruído.

Como determino se um frame é o mais externo?

Anteriormente, as extensões podiam verificar se frameId == 0 para determinar se o evento que ocorreva no frame mais externo ou não. Com várias páginas em uma guia, agora temos vários frames mais externos. Por isso, a definição de frameId é problemática. Você nunca receberá eventos sobre um frame em cache de avanço e retorno. No entanto, para frames pré-renderizados, o frameId será diferente de zero para o frame mais externo. Portanto, usar frameId == 0 como um sinal para determinar se é o frame mais externo está incorreto.

Para ajudar com isso, apresentamos um novo tipo chamado FrameType. Por isso, agora ficou fácil determinar se o frame é realmente o mais externo. FrameType tem os seguintes valores:

  • "outermost_frame": normalmente conhecido como o frame superior. Observe que há vários deles. Por exemplo, se você tiver páginas pré-renderizadas e armazenadas em cache, cada uma delas terá um frame mais externo que pode ser chamado de frame superior.
  • "fenced_frame": reservado para uso futuro.
  • "sub_frame": normalmente é um iframe.

É possível combinar DocumentLifecycle com FrameType e determinar se um frame é o mais externo ativo. Exemplo: js tab.documentLifecycle == “active” && frameType == “outermost_frame”

Como resolvo problemas de tempo de uso com frames?

Como dissemos acima, um frame hospeda um documento e pode navegar para um novo documento, mas o frameId não muda. Isso cria problemas quando você recebe um evento apenas com um frameId. Se você pesquisar o URL do frame, ele poderá ser diferente do momento em que o evento ocorreu. Isso é chamado de problema de horário de uso.

Para resolver isso, introduzimos documentId (e parentDocumentId). O método webNavigation.getFrame() agora torna o frameId opcional se um documentId é fornecido. O documentId vai mudar sempre que um frame for navegado.

Como determino quando uma página faz uma transição?

Há sinais explícitos para determinar quando uma página faz a transição entre estados.

Vamos dar uma olhada nos eventos WebNavigation.

Na primeira navegação de qualquer página, você verá quatro eventos na ordem listada abaixo. Esses quatro eventos podem ocorrer com o estado DocumentLifecycle sendo "prerender" ou "active".

onBeforeNavigate
onCommitted
onDOMContentLoaded
onCompleted

Isso é ilustrado no diagrama abaixo, que mostra a mudança de documentId para "xyz" quando a página pré-renderizada se torna a página ativa.

O documentId muda quando a página pré-renderizada se torna ativa.
O documentId muda quando a página pré-renderizada se torna a página ativa.

Quando uma página faz a transição do cache de avanço e retorno ou de pré-renderização para o estado ativo, haverá mais três eventos (mas com DocumentLifecyle sendo "active").

onBeforeNavigate
onCommitted
onCompleted

O evento documentId vai ser o mesmo dos eventos originais. Isso é ilustrado acima quando documentId == xyz é ativado. Os mesmos eventos de navegação são acionados, exceto o evento onDOMContentLoaded, porque a página já foi carregada.

Se você tiver comentários ou dúvidas, fique à vontade para perguntar no grupo chromium-extensions.