Membuat aktivasi pengguna konsisten di seluruh API

Mustaq Ahmed
Joe Medley
Joe Medley

Untuk mencegah skrip berbahaya menyalahgunakan API sensitif seperti pop-up, layar penuh dll., browser mengontrol akses ke API tersebut melalui aktivasi. Aktivasi pengguna adalah status sesi penjelajahan yang terkait untuk tindakan pengguna: "aktif" biasanya menyiratkan bahwa pengguna sedang berinteraksi dengan halaman, atau telah menyelesaikan interaksi sejak halaman memuat halaman. Gestur pengguna adalah istilah populer, tetapi menyesatkan untuk ide yang sama. Sebagai contoh, gestur menggeser atau memutar oleh pengguna tidak mengaktifkan halaman dan karenanya dari sudut pandang skrip, bukanlah aktivasi pengguna.

Browser utama saat ini menunjukkan perilaku yang sangat berbeda tentang cara aktivasi pengguna mengontrol API yang dibatasi aktivasi. Di Chrome, implementasinya didasarkan model berbasis token yang ternyata terlalu kompleks untuk menentukan di semua API yang dibatasi aktivasi. Misalnya, Chrome telah mengizinkan akses tidak lengkap ke API yang dibatasi aktivasi melalui postMessage() dan Panggilan setTimeout(); dan aktivasi pengguna tidak didukung dengan Promise, XHR, Interaksi gamepad, dll. Perhatikan bahwa beberapa adalah bug populer tetapi sudah lama ada.

Pada versi 72, Chrome mengirimkan Aktivasi Pengguna v2 yang membuat pengguna ketersediaan aktivasi selesai untuk semua API yang dibatasi aktivasi. Ini mengatasi inkonsistensi yang disebutkan di atas (dan beberapa lagi, seperti MessageChannels), yang kami yakini akan memudahkan web pengembangan seputar aktivasi pengguna. Selain itu, implementasi baru ini menyediakan implementasi referensi untuk spesifikasi baru yang bertujuan untuk menyatukan semua {i>browser<i} bersama 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 pernah melihat aktivasi pengguna), dan bit sementara untuk status saat ini (jika sebuah frame melihat aktivasi pengguna dalam waktu sekitar satu detik). {i>Sticky Bit<i} tidak pernah direset selama masa aktif {i>frame<i} setelah ditetapkan. Bit sementara ditetapkan pada setiap interaksi pengguna, dan direset setelah masa berlaku habis interval (sekitar satu detik) atau melalui panggilan ke API yang menggunakan aktivasi (misalnya, window.open()).

Perlu diingat bahwa API dengan batasan aktivasi yang berbeda bergantung pada aktivasi pengguna cara; API baru tidak mengubah perilaku spesifik API tersebut. Mis. hanya satu pop-up yang diizinkan per aktivasi pengguna karena window.open() menggunakan aktivasi pengguna seperti sebelumnya, Navigator.prototype.vibrate() terus efektif jika {i>frame<i} (atau salah satu sub {i>frame<i}-nya) pernah melihat tindakan pengguna, dan seterusnya.

Apa yang berubah?

  • Aktivasi Pengguna v2 memformalkan gagasan visibilitas aktivasi pengguna lintas batas frame: interaksi pengguna dengan frame tertentu kini akan aktifkan semua {i>frame<i} yang memuatnya (dan hanya {i>frame<i} tersebut) terlepas dari tempat asal. (Di Chrome 72, kami memiliki solusi sementara untuk memperluas visibilitas ke semua frame asal 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, kode ini akan berfungsi selama aktivasi pengguna status adalah "aktif" (misalnya, belum habis masa berlakunya atau telah dikonsumsi). Sebelum Pengguna Aktivasi v2 akan gagal tanpa syarat.
  • Beberapa interaksi pengguna yang tidak digunakan dalam interval waktu masa berlaku habis menjadi satu aktivasi yang sesuai dengan interaksi terakhir.

Contoh konsistensi dalam API yang dibatasi aktivasi

Berikut dua contoh dengan jendela pop-up (dibuka menggunakan window.open()) yang tampilkan cara Aktivasi Pengguna 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 satu detik, pengendali tersebut akan berhasil, tidak peduli bagaimana kode "menulis" jika terjadi keterlambatan. Aktivasi Pengguna v2 memenuhi ekspektasi ini, jadi setiap pengendali peristiwa berikut membuka {i>pop-up<i} pada click (dengan keterlambatan 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 diuji. (Bahkan yang pertama gagal dalam beberapa kasus.)

Panggilan postMessage() lintas-domain

Berikut ini contoh dari demo postMessage() kami. Misalkan pengendali click dalam subframe lintas origin mengirim dua pesan secara langsung ke {i>frame<i} induk. {i>Frame<i} induk harus dapat membuka {i>pop-up<i} pada 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 "dirantai" ke yang lain frame lintas origin (dengan kata lain, jika penerima pertama meneruskan pesan ke pihak lain).

Fitur ini berfungsi dengan Aktivasi Pengguna v2, baik dalam bentuk asli maupun dengan perantaian.