Como tornar a ativação do usuário consistente em todas as APIs

Mustaq Ahmed
Joe Medley
Joe Medley

Para evitar que scripts maliciosos abusem de APIs sensíveis, como pop-ups, tela cheia etc., os navegadores controlam o acesso a essas APIs por meio de controles ativação. A ativação do usuário é o estado de uma sessão de navegação em relação às ações do usuário: um "ativo" estado normalmente implica que o usuário está está interagindo com a página ou concluiu uma interação desde que a página carregar. Gesto do usuário é um termo conhecido, mas enganoso, para a mesma ideia. Para Por exemplo, um gesto de deslizar ou mover de um usuário não ativa a página e, portanto, não é, do ponto de vista do script, uma ativação do usuário.

Atualmente, os principais navegadores apresentam comportamentos divergentes em relação ao modo como a ativação do usuário controla as APIs com controle de ativação. No Chrome, a implementação foi baseada em um modelo baseado em token que acabou sendo complexo demais para definir em todas as APIs controladas por ativação. Por exemplo, o Chrome foi permitindo acesso incompleto a APIs com controle de ativação por meio de postMessage() e chamadas do setTimeout(); e a ativação do usuário não foi compatível com promessas, XHR, Interação com o Gamepad etc. Observe que alguns desses são bugs conhecidos, mas que existem há muito tempo.

Na versão 72, o Chrome fornece a ativação do usuário v2, que torna o usuário disponibilidade de ativação concluída para todas as APIs controladas por ativação. Isso resolve as inconsistências mencionadas acima (e algumas outras, como MessageChannels), que acreditamos facilitar o uso da desenvolvimento em torno da ativação do usuário. Além disso, a nova implementação oferece uma implementação de referência para um nova especificação que tem como objetivo reunir todos os navegadores em longo prazo.

Como a ativação do usuário v2 funciona?

A nova API mantém um estado de ativação do usuário de dois bits em cada objeto window. na hierarquia de frames: uma parte fixa do estado histórico de ativação do usuário (se um já viu uma ativação de usuário), e um bit temporário para o estado atual (se um frame tiver visto uma ativação do usuário em cerca de um segundo). A parte aderente nunca é redefinido durante a vida útil do frame após ele ser definido. A parte temporária é definido em cada interação do usuário e é redefinido após um vencimento (cerca de um segundo) ou com uma chamada para uma API que consome muita ativação (por exemplo, window.open()).

Observe que diferentes APIs controladas por ativação dependem da ativação do usuário em diferentes maneiras a nova API não altera nenhum desses comportamentos específicos da API. Por exemplo: apenas um pop-up é permitido por ativação do usuário porque o window.open() consome a ativação de usuários como antes, o Navigator.prototype.vibrate() continua ser eficaz se um frame (ou qualquer um de seus subframes) já tiver visto uma ação do usuário, e assim por diante.

O que muda?

  • A ativação do usuário v2 formaliza a noção de visibilidade da ativação do usuário além dos limites dos frames: uma interação do usuário com um frame específico ativar todos os frames contidos (e somente os frames) independentemente origem. No Chrome 72, temos uma solução temporária para expandir a visibilidade para todos os frames de mesma origem. Essa solução alternativa será removida ter uma maneira de transmita explicitamente a ativação do usuário para subframes.
  • quando uma API controlada por ativação é chamada de um frame ativado, mas fora de um código de manipulador de eventos, ele funcionará, desde que a ativação do usuário estado é "ativo" (por exemplo, não expirou nem foi consumido). Antes do usuário na ativação v2, ela falharia incondicionalmente.
  • Várias interações do usuário não utilizadas dentro do intervalo de tempo de expiração se fundem em uma única ativação correspondente à última interação.

Exemplos de consistência em APIs controladas por ativação

Confira dois exemplos com janelas pop-up (abertas usando window.open()) que mostre como a ativação do usuário v2 torna o comportamento de APIs controladas por ativação consistentes.

Chamadas setTimeout() encadeadas

Este exemplo é de nossa demonstração setTimeout(). Se um gerenciador click tentar abrir um pop-up em um segundo, ele será ter sucesso, não importa como o código "componha" o atraso. A ativação do usuário v2 atende essa expectativa, então cada um dos seguintes manipuladores de eventos abre um pop-up em uma click (com um atraso de 100 ms):

function popupAfter100ms() {
  setTimeout(callWindowOpen, 100);
}

function asyncPopupAfter100ms() {
  setTimeout(popupAfter100ms, 0);
}

someButton.addEventListener('click', popupAfter100ms);
someButton.addEventListener('click', asyncPopupAfter100ms);

Sem a ativação do usuário v2, o segundo manipulador de eventos falha em todos os navegadores é treinado e testado. (Até o primeiro não dá certo em alguns casos.

Chamadas postMessage() entre domínios

Aqui está um exemplo nossa demonstração postMessage(). Suponha que um gerenciador click em um subframe de origem cruzada envie duas mensagens diretamente. ao frame principal. O frame principal deve abrir um pop-up recebendo apenas uma destas mensagens (mas não ambas):

// Parent frame code
window.addEventListener('message', e => {
  if (e.data === 'open_popup' && e.origin === child_origin)
    window.open('about:blank');
});

// Child frame code:
someButton.addEventListener('click', () => {
  parent.postMessage('hi_there', parent_origin);
  parent.postMessage('open_popup', parent_origin);
});

Sem a ativação do usuário v2, o frame principal não pode abrir um pop-up ao receber a segunda mensagem. Até a primeira mensagem vai falhar se estiver "encadeada" para outro frame de origem cruzada (em outras palavras, se o primeiro receptor encaminhar a mensagem para outro).

Isso funciona com a ativação do usuário v2, tanto na forma original quanto com o encadeamento.