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 rispetto alle azioni dell'utente: uno stato "attivo" in genere implica che l'utente stia attualmente interagendo con la pagina o che abbia 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 un movimento brusco di un utente non attiva una pagina e quindi, dal punto di vista dello script, non è un'attivazione dell'utente.
I principali browser oggi mostrano un comportamento molto diverso per quanto riguarda il modo in cui l'attivazione dell'utente controlla le API con 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 con attivazione controllata. Ad esempio, Chrome consente da tempo accesso incompleto alle API con attivazione tramite chiamate postMessage()
e setTimeout()
; inoltre, l'attivazione utente non era supportata con Promise, XHR, interazione con il gamepad e così via. Tieni presente che alcuni di questi sono bug noti e di lunga data.
Nella versione 72, Chrome fornisce la versione 2 dell'attivazione utente, che rende completa la disponibilità dell'attivazione utente per tutte le API con attivazione obbligatoria. In questo modo vengono risolte le incoerenze sopra menzionate (e altre ancora, come MessageChannels), che riteniamo possano semplificare lo sviluppo web per l'attivazione 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 la versione 2 dell'attivazione utente?
La nuova API gestisce uno stato di attivazione utente a due bit in ogni oggetto window
nella gerarchia del frame: un bit fisso per lo stato di attivazione utente storico (se un
frame ha mai registrato un'attivazione utente) e un bit transitorio per lo stato corrente
(se un frame ha registrato 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 transitorio viene impostato a ogni interazione dell'utente e 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()
consuma
l'attivazione utente come in passato, Navigator.prototype.vibrate()
continua a essere
efficace se un frame (o uno dei relativi frame secondari) ha mai registrato un'azione dell'utente,
e così via.
Cosa cambierà?
- La versione 2 dell'attivazione utente formalizza il concetto di visibilità dell'attivazione utente tra i confini dei frame: ora un'interazione dell'utente con un determinato frame attiva tutti i frame contenenti (e solo quelli) indipendentemente dalla loro origine. In Chrome 72 è disponibile una soluzione temporanea per estendere la visibilità a tutti i frame dello stesso dominio. Rimuoveremo questa soluzione alternativa quando avremo un modo per trasmettere esplicitamente l'attivazione dell'utente ai frame secondari.
- Quando un'API con attivazione gated viene chiamata da un frame attivato, ma al di fuori di un codice di gestore eventi, funzionerà purché lo stato di attivazione dell'utente sia "attivo" (ad es. non sia scaduto né sia 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 fondono in un'unica attivazione corrispondente all'ultima interazione.
Esempi di coerenza nelle API basate sull'attivazione
Di seguito sono riportati due esempi con finestre popup (aperte utilizzando window.open()
) che mostrano come la versione 2 dell'attivazione utente rende coerente il comportamento delle API con attivazione obbligatoria.
Chiamate setTimeout()
concatenate
Questo esempio è tratto dalla
nostra demo di setTimeout()
.
Se un gestore click
tenta di aprire un popup entro un secondo, dovrebbe riuscire, indipendentemente da come il codice "compone" il ritardo. La versione 2 dell'attivazione utente soddisfa questa aspettativa, pertanto ciascuno dei seguenti gestori eventi apre un popup su un
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 la versione 2 di Attivazione utente, il secondo gestore eventi non funziona in tutti i browser che abbiamo provato. Anche il primo non va a buon fine in alcuni casi.
Chiamate postMessage()
interdominio
Ecco un esempio tratto dalla nostra demo di postMessage()
.
Supponiamo che un gestore click
in un frame secondario cross-origin invii due messaggi direttamente al frame principale. Il frame principale deve essere in grado di aprire un popup al
ricevimento di uno di questi messaggi (ma non di 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.