Để ngăn các tập lệnh độc hại lợi dụng các API nhạy cảm như cửa sổ bật lên, chế độ toàn màn hình, v.v., trình duyệt sẽ kiểm soát quyền truy cập vào các API đó thông qua việc kích hoạt người dùng. Hoạt động kích hoạt người dùng là trạng thái của một phiên duyệt web liên quan đến các hành động của người dùng: trạng thái "đang hoạt động" thường ngụ ý rằng người dùng đang tương tác với trang hoặc đã hoàn tất một lượt tương tác kể từ khi tải trang. Cử chỉ của người dùng là một thuật ngữ phổ biến nhưng gây hiểu lầm về cùng một ý tưởng. Ví dụ: cử chỉ vuốt hoặc hất của người dùng không kích hoạt trang và do đó, không phải là hành động kích hoạt người dùng theo quan điểm của tập lệnh.
Các trình duyệt chính hiện nay cho thấy hành vi khác biệt đáng kể về cách hoạt động kích hoạt của người dùng kiểm soát các API có cổng kích hoạt. Trong Chrome, quá trình triển khai dựa trên mô hình dựa trên mã thông báo, nhưng mô hình này quá phức tạp để xác định hành vi nhất quán trên tất cả các API có cổng kích hoạt. Ví dụ: Chrome đã cho phép quyền truy cập không đầy đủ vào các API có cổng kích hoạt thông qua các lệnh gọi postMessage()
và setTimeout()
; đồng thời không hỗ trợ việc kích hoạt người dùng bằng Promises, XHR, Tương tác với tay điều khiển trò chơi, v.v. Xin lưu ý rằng một số lỗi trong số này là lỗi phổ biến nhưng đã tồn tại từ lâu.
Trong phiên bản 72, Chrome sẽ cung cấp User Activation v2 (Kích hoạt người dùng phiên bản 2) để hoàn tất việc cung cấp tính năng kích hoạt người dùng cho tất cả các API có cổng kích hoạt. Điều này giúp giải quyết các điểm không nhất quán được đề cập ở trên (và một số điểm khác, chẳng hạn như MessageChannels). Chúng tôi tin rằng điều này sẽ giúp việc phát triển web liên quan đến việc kích hoạt người dùng trở nên dễ dàng hơn. Hơn nữa, phương thức triển khai mới cung cấp một phương thức triển khai tham chiếu cho một thông số kỹ thuật mới được đề xuất nhằm mục đích kết hợp tất cả trình duyệt về lâu dài.
Tính năng Kích hoạt người dùng phiên bản 2 hoạt động như thế nào?
API mới duy trì trạng thái kích hoạt người dùng hai bit tại mọi đối tượng window
trong hệ phân cấp khung: một bit cố định cho trạng thái kích hoạt người dùng trước đây (nếu một khung đã từng thấy một lượt kích hoạt người dùng) và một bit tạm thời cho trạng thái hiện tại (nếu một khung đã thấy một lượt kích hoạt người dùng trong khoảng một giây). Bit cố định không bao giờ đặt lại trong suốt thời gian hoạt động của khung sau khi được đặt. Bit tạm thời được đặt trên mọi lượt tương tác của người dùng và được đặt lại sau khoảng thời gian hết hạn (khoảng một giây) hoặc thông qua lệnh gọi đến một API tiêu thụ hoạt động kích hoạt (ví dụ: window.open()
).
Xin lưu ý rằng các API có cổng kích hoạt khác nhau dựa vào việc kích hoạt người dùng theo nhiều cách; API mới không thay đổi bất kỳ hành vi nào dành riêng cho API này. Ví dụ: chỉ cho phép một cửa sổ bật lên cho mỗi lượt kích hoạt người dùng vì window.open()
sử dụng lượt kích hoạt người dùng như trước đây, Navigator.prototype.vibrate()
tiếp tục hiệu quả nếu một khung (hoặc bất kỳ khung con nào của khung đó) từng thấy hành động của người dùng, v.v.
Điều gì sẽ thay đổi?
- User Activation v2 (Kích hoạt người dùng phiên bản 2) chính thức hoá khái niệm về khả năng hiển thị của hoạt động kích hoạt người dùng trên các ranh giới khung: giờ đây, một lượt tương tác của người dùng với một khung cụ thể sẽ kích hoạt tất cả các khung chứa (và chỉ những khung đó) bất kể nguồn gốc của các khung đó. (Trong Chrome 72, chúng tôi đã có một giải pháp tạm thời để mở rộng phạm vi hiển thị cho tất cả các khung có cùng nguồn gốc. Chúng tôi sẽ xoá giải pháp này sau khi có cách chuyển rõ ràng hoạt động kích hoạt của người dùng đến các khung con.)
- Khi một API có cổng kích hoạt được gọi từ một khung đã kích hoạt nhưng từ bên ngoài mã trình xử lý sự kiện, API đó sẽ hoạt động miễn là trạng thái kích hoạt của người dùng là "đang hoạt động" (ví dụ: chưa hết hạn hoặc chưa được sử dụng). Trước khi có tính năng Kích hoạt người dùng phiên bản 2, thao tác này sẽ không thành công vô điều kiện.
- Nhiều lượt tương tác không dùng đến của người dùng trong khoảng thời gian hết hạn sẽ hợp nhất thành một lượt kích hoạt tương ứng với lượt tương tác gần đây nhất.
Ví dụ về tính nhất quán trong các API có cổng kích hoạt
Dưới đây là hai ví dụ về cửa sổ bật lên (mở bằng window.open()
) cho thấy cách User Activation v2 giúp hành vi của các API có cổng kích hoạt nhất quán.
Các lệnh gọi setTimeout()
theo chuỗi
Ví dụ này được lấy từ bản minh hoạ setTimeout()
của chúng tôi.
Nếu trình xử lý click
cố gắng mở một cửa sổ bật lên trong vòng một giây, thì trình xử lý này dự kiến sẽ thành công bất kể mã "sắp xếp" độ trễ như thế nào. User Activation v2 đáp ứng
mong đợi này, vì vậy, mỗi trình xử lý sự kiện sau đây sẽ mở một cửa sổ bật lên trên
click
(với độ trễ 100 mili giây):
function popupAfter100ms() {
setTimeout(callWindowOpen, 100);
}
function asyncPopupAfter100ms() {
setTimeout(popupAfter100ms, 0);
}
someButton.addEventListener('click', popupAfter100ms);
someButton.addEventListener('click', asyncPopupAfter100ms);
Nếu không có User Activation v2, trình xử lý sự kiện thứ hai sẽ không hoạt động trong tất cả trình duyệt mà chúng tôi đã thử nghiệm. (Thậm chí lần đầu tiên cũng không thành công trong một số trường hợp.)
Lệnh gọi postMessage()
trên nhiều miền
Sau đây là ví dụ về minh hoạ postMessage()
.
Giả sử một trình xử lý click
trong khung con nhiều nguồn gốc gửi trực tiếp hai thông báo đến khung mẹ. Khung mẹ phải có thể mở một cửa sổ bật lên khi nhận được một trong hai thông báo sau (nhưng không phải cả hai):
// 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);
});
Nếu không có tính năng Kích hoạt người dùng phiên bản 2, khung mẹ sẽ không thể mở cửa sổ bật lên khi nhận được thông báo thứ hai. Ngay cả thông báo đầu tiên cũng không thành công nếu thông báo đó được "chuỗi" với một khung chéo nguồn khác (nói cách khác, nếu trình nhận đầu tiên chuyển tiếp thông báo đến một trình nhận khác).
Phương thức này hoạt động với tính năng Kích hoạt người dùng phiên bản 2, cả ở dạng ban đầu và với tính năng tạo chuỗi.