Rendere l'attivazione utente coerente tra le API

Mustaq Ahmed
Joe Medley
Joe Medley

Per impedire che script dannosi utilizzino in modo illecito API sensibili come popup, schermo intero e così via, i browser controllano l'accesso a queste API tramite l'attivazione dell'utente. L'attivazione utente è lo stato di una sessione di navigazione in relazione alle azioni dell'utente. Uno stato "attivo" in genere implica che l'utente sta sta interagendo con la pagina o ha completato un'interazione dal caricamento della pagina. Gesto dell'utente è un termine popolare ma fuorviante per la stessa idea. Ad esempio, un gesto di scorrimento o di scorrimento di un utente non attiva una pagina e, pertanto, dal punto di vista dello script, non si tratta di un'attivazione da parte dell'utente.

Oggi i principali browser mostrano comportamenti ampiamente divergenti in merito al modo in cui l'attivazione utente controlla le API basate sull'attivazione. In Chrome, l'implementazione si basava su un modello basato su token che si è rivelato troppo complesso per definire un comportamento coerente in tutte le API basate sull'attivazione. Ad esempio, Chrome ha consentito l'accesso incompleto alle API con attivazione limitata tramite le chiamate postMessage() e setTimeout() e l'attivazione degli utenti non era supportata con Promises, XHR, Interazione con il gamepad e così via. Tieni presente che alcuni di questi sono bug popolari ma da tempo.

Nella versione 72, Chrome spedisce User Activation v2, che rende completa la disponibilità dell'attivazione degli utenti per tutte le API basate sull'attivazione. Ciò risolve le incoerenze sopra menzionate (e alcune altre, come MessageChannels), che crediamo agevolare lo sviluppo web per l'attivazione da parte degli utenti. Inoltre, la nuova implementazione fornisce un'implementazione di riferimento per una proposta di nuova specifica che mira a riunire tutti i browser nel lungo periodo.

Come funziona Attivazione utente v2?

La nuova API mantiene uno stato di attivazione dell'utente a due bit in ogni oggetto window nella gerarchia dei frame: un bit fisso per lo stato storico di attivazione dell'utente (se in un frame è mai stata effettuata l'attivazione di un utente) e un bit temporaneo per lo stato corrente (se in un frame è stata rilevata un'attivazione utente in circa un secondo). La punta persistente non si reimposta mai durante il ciclo di vita del frame una volta impostato. Il bit temporaneo viene impostato a ogni interazione dell'utente e viene reimpostato dopo un intervallo di scadenza (circa un secondo) o tramite una chiamata a un'API che utilizza l'attivazione (ad es. window.open()).

Tieni presente che le diverse API basate sull'attivazione si basano sull'attivazione utente in modi diversi; la nuova API non modifica nessuno di questi comportamenti specifici dell'API. Ad esempio, è consentito un solo popup per attivazione utente perché window.open() utilizza l'attivazione dell'utente come prima, Navigator.prototype.vibrate() continua a essere efficace se un frame (o uno dei suoi frame secondari) ha mai registrato azioni da parte dell'utente e così via.

Cosa cambierà?

  • Attivazione utente v2 formalizza la nozione di visibilità dell'attivazione dell'utente oltre i confini dei frame: un'interazione dell'utente con un determinato frame ora attiverà tutti i frame contenenti (e solo quei frame) indipendentemente dalla loro origine. In Chrome 72, è stata implementata una soluzione alternativa temporanea per espandere la visibilità a tutti i frame della stessa origine. Rimuoveremo questa soluzione alternativa una volta che avremo un modo per trasferire esplicitamente l'attivazione degli utenti ai frame secondari.
  • Quando un'API con attivazione controllata viene chiamata da un frame attivato, ma dall'esterno del codice di un gestore di eventi, funziona purché lo stato di attivazione dell'utente sia "attivo" (ad esempio, non è scaduto né è stato utilizzato). Prima dell'attivazione dell'utente v2, l'attivazione non avrebbe avuto esito positivo.
  • Più interazioni utente inutilizzate nell'intervallo di tempo di scadenza si uniscono in un'unica attivazione corrispondente all'ultima interazione.

Esempi di coerenza nelle API basate sull'attivazione

Ecco due esempi di finestre popup (aperte utilizzando window.open()) che mostrano come User Activation v2 rende coerente il comportamento delle API basate sull'attivazione.

Chiamate setTimeout() concatenate

Questo esempio è tratto dalla nostra demo di setTimeout(). Se un gestore click tenta di aprire un popup entro un secondo, dovrebbe funzionare indipendentemente da come il codice "compone" il ritardo. Attivazione utente v2 soddisfa questa aspettativa, quindi ciascuno dei seguenti gestori di eventi apre un popup su click (con un ritardo di 100 ms):

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

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

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

Senza Attivazione utente v2, il secondo gestore di eventi ha esito negativo in tutti i browser testati. (In alcuni casi anche la prima non funziona).

Chiamate postMessage() interdominio

Ecco un esempio tratto dalla nostra demo di postMessage(). Supponiamo che un gestore click in un frame secondario multiorigine invii due messaggi direttamente al frame principale. Il frame principale deve essere in grado di aprire un popup alla ricezione di uno di questi messaggi (ma non entrambi):

// 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);
});

Senza Attivazione utente v2, il frame principale non può aprire un popup alla ricezione del secondo messaggio. Anche il primo messaggio restituisce un errore se è "collegato" a un altro frame multiorigine (in altre parole, se il primo destinatario inoltra il messaggio a un altro).

Questo vale per Attivazione utente v2, sia nel formato originale che con il concatenamento.