Untuk mencegah skrip berbahaya menyalahgunakan API sensitif seperti pop-up, layar penuh, dll., browser mengontrol akses ke API tersebut melalui aktivasi pengguna. Pengaktifan pengguna adalah status sesi penjelajahan sehubungan dengan tindakan pengguna: status "aktif" biasanya menyiratkan bahwa pengguna saat ini berinteraksi dengan halaman, atau telah menyelesaikan interaksi sejak halaman dimuat. Gestur pengguna adalah istilah populer, tetapi menyesatkan untuk ide yang sama. Misalnya, gestur geser atau ayun oleh pengguna tidak mengaktifkan halaman, sehingga, dari sudut pandang skrip, bukan merupakan aktivasi pengguna.
Browser utama saat ini menunjukkan perilaku yang sangat berbeda terkait cara aktivasi pengguna
mengontrol API yang dibatasi aktivasi. Di Chrome, penerapannya didasarkan
pada model berbasis token yang ternyata terlalu kompleks untuk menentukan perilaku
yang konsisten di semua API yang dibatasi aktivasi. Misalnya, Chrome telah
mengizinkan akses yang tidak lengkap ke API yang dibatasi aktivasi melalui
panggilan postMessage()
dan
setTimeout()
; dan aktivasi pengguna tidak
didukung dengan Promises,
XHR,
interaksi Gamepad, dll. Perhatikan bahwa beberapa dari bug ini
adalah bug populer yang sudah lama ada.
Dalam versi 72, Chrome meluncurkan User Activation v2 yang membuat ketersediaan aktivasi pengguna lengkap untuk semua API yang dibatasi aktivasi. Hal ini akan menyelesaikan inkonsistensi yang disebutkan di atas (dan beberapa lagi, seperti MessageChannels), yang kami yakini akan memudahkan pengembangan web seputar aktivasi pengguna. Selain itu, penerapan baru ini menyediakan penerapan referensi untuk spesifikasi baru yang diusulkan yang bertujuan untuk menyatukan semua browser dalam jangka panjang.
Bagaimana cara kerja Aktivasi Pengguna v2?
API baru mempertahankan status aktivasi pengguna dua bit di setiap objek window
dalam hierarki frame: bit melekat untuk status aktivasi pengguna historis (jika
frame pernah melihat aktivasi pengguna), dan bit sementara untuk status saat ini
(jika frame telah melihat aktivasi pengguna dalam waktu sekitar satu detik). Sticky bit
tidak pernah direset selama masa aktif frame setelah ditetapkan. Bit sementara
ditetapkan pada setiap interaksi pengguna, dan direset setelah interval
masa berlaku (sekitar satu detik) atau melalui panggilan ke API yang menggunakan aktivasi
(misalnya, window.open()
).
Perlu diperhatikan bahwa API yang dibatasi aktivasi berbeda bergantung pada aktivasi pengguna dengan cara yang
berbeda; API baru tidak mengubah perilaku khusus API ini. Misalnya,
hanya satu pop-up yang diizinkan per aktivasi pengguna karena window.open()
menggunakan
aktivasi pengguna seperti biasa, Navigator.prototype.vibrate()
akan terus
efektif jika frame (atau subframe-nya) pernah melihat tindakan pengguna,
dan seterusnya.
Apa yang berubah?
- Aktivasi Pengguna v2 memformalkan gagasan visibilitas aktivasi pengguna di seluruh batas frame: interaksi pengguna dengan frame tertentu kini akan mengaktifkan semua frame yang berisi (dan hanya frame tersebut) terlepas dari asalnya. (Di Chrome 72, kami memiliki solusi sementara untuk memperluas visibilitas ke semua frame dengan origin yang sama. Kami akan menghapus solusi ini setelah memiliki cara untuk secara eksplisit meneruskan aktivasi pengguna ke sub-frame.)
- Saat API yang dibatasi aktivasi dipanggil dari frame yang diaktifkan, tetapi dari di luar kode pengendali peristiwa, API tersebut akan berfungsi selama status aktivasi pengguna "aktif" (misalnya, belum habis masa berlakunya atau belum digunakan). Sebelum Aktivasi Pengguna v2, tindakan ini akan gagal tanpa syarat.
- Beberapa interaksi pengguna yang tidak digunakan dalam interval waktu masa berakhir menyatu menjadi satu aktivasi yang sesuai dengan interaksi terakhir.
Contoh konsistensi dalam API yang diaktifkan
Berikut adalah dua contoh dengan jendela pop-up (dibuka menggunakan window.open()
) yang
menunjukkan bagaimana User Activation v2 membuat perilaku API yang dibatasi aktivasi
konsisten.
Panggilan setTimeout()
berantai
Contoh ini berasal dari
demo setTimeout()
kami.
Jika pengendali click
mencoba membuka pop-up dalam hitungan detik, pengendali tersebut diharapkan
berhasil, terlepas dari cara kode "membuat" penundaan. Aktivasi Pengguna v2 memenuhi
ekspektasi ini, sehingga setiap pengendali peristiwa berikut akan membuka pop-up di
click
(dengan penundaan 100 md):
function popupAfter100ms() {
setTimeout(callWindowOpen, 100);
}
function asyncPopupAfter100ms() {
setTimeout(popupAfter100ms, 0);
}
someButton.addEventListener('click', popupAfter100ms);
someButton.addEventListener('click', asyncPopupAfter100ms);
Tanpa Aktivasi Pengguna v2, pengendali peristiwa kedua akan gagal di semua browser yang kami uji. (Bahkan yang pertama gagal dalam beberapa kasus.)
Panggilan postMessage()
lintas-domain
Berikut adalah contoh dari
demo postMessage()
kami.
Misalkan pengendali click
dalam subframe lintas origin mengirim dua pesan langsung ke frame induk. Frame induk harus dapat membuka pop-up setelah
menerima salah satu pesan ini (tetapi tidak keduanya):
// 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);
});
Tanpa Aktivasi Pengguna v2, frame induk tidak dapat membuka pop-up setelah menerima pesan kedua. Bahkan pesan pertama akan gagal jika "dikaitkan" ke frame lintas origin lain (dengan kata lain, jika penerima pertama meneruskan pesan ke penerima lain).
Fitur ini berfungsi dengan Aktivasi Pengguna v2, baik dalam bentuk asli maupun dengan rantai.