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 pengguna. Aktivasi 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 putar pergelangan oleh pengguna tidak akan mengaktifkan halaman, dan dari sudut pandang skrip, bukanlah aktivasi pengguna.

Browser utama saat ini menunjukkan perilaku yang sangat berbeda terkait cara aktivasi pengguna mengontrol API yang dibatasi aktivasi. Di Chrome, implementasinya 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 tidak lengkap ke API yang dibatasi aktivasi melalui postMessage() dan panggilan setTimeout(); dan aktivasi pengguna tidak didukung dengan Promise, XHR, interaksi Gamepad, dll. Perlu diperhatikan bahwa beberapa bug ini populer tetapi sudah lama ada.

Pada versi 72, Chrome mengirimkan Aktivasi Pengguna v2 yang membuat ketersediaan aktivasi pengguna selesai untuk semua API yang dibatasi aktivasi. Tindakan ini akan mengatasi inkonsistensi yang disebutkan di atas (dan beberapa kasus lainnya, 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 yang 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 melihat aktivasi pengguna dalam waktu sekitar satu detik). Bit lengket tidak pernah direset selama masa aktif frame setelah disetel. Bit sementara ditetapkan pada setiap interaksi pengguna, dan direset setelah interval masa berakhir (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 sebelumnya, Navigator.prototype.vibrate() akan terus efektif jika frame (atau salah satu subframenya) 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 sekarang akan mengaktifkan semua frame yang memuatnya (dan hanya frame tersebut) terlepas dari asalnya. (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 luar kode pengendali peristiwa, API tersebut akan berfungsi selama status aktivasi pengguna "aktif" (misalnya, belum habis masa berlakunya atau belum dipakai). Sebelum Aktivasi Pengguna v2, pengujian 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 dibatasi aktivasi

Berikut adalah dua contoh dengan jendela pop-up (dibuka menggunakan window.open()) yang menunjukkan bagaimana Aktivasi Pengguna v2 membuat perilaku API yang dibatasi aktivasi menjadi konsisten.

Panggilan setTimeout() berantai

Contoh ini dari demo setTimeout() kami. Jika pengendali click mencoba membuka pop-up dalam satu detik, pengendali tersebut diharapkan akan berhasil, terlepas dari cara kode "menulis" penundaan. Aktivasi Pengguna v2 memenuhi ekspektasi ini, sehingga setiap pengendali peristiwa berikut membuka pop-up 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 telah 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 "dirantai" ke frame lintas origin lainnya (dengan kata lain, jika penerima pertama meneruskan pesan ke frame lain).

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