Đảm bảo hoạt động kích hoạt của người dùng nhất quán trên các API

Mustaq Ahmed
Joe Medley
Joe Medley

Để ngăn các tập lệnh độc hại lạm dụng API nhạy cảm như cửa sổ bật lên, toàn màn hình, v.v., trình duyệt kiểm soát quyền truy cập vào các API đó thông qua người dùng để kích hoạt tài khoản. Kích hoạt người dùng là trạng thái của phiên duyệt web theo vào hành động của người dùng: "đang hoạt động" thường ngụ ý rằng người dùng hiện đang tương tác với trang hoặc đã hoàn tất một tương tác kể từ trang tải. Cử chỉ của người dùng là một cụm từ phổ biến nhưng gây hiểu lầm cho cùng một ý tưởng. Cho ví dụ: cử chỉ vuốt hoặc xoay của người dùng không kích hoạt trang và do đó, từ quan điểm của tập lệnh thì không phải là sự kích hoạt của người dùng.

Các trình duyệt chính hiện nay cho thấy hành vi rất khác nhau về cách người dùng kích hoạt kiểm soát API có tính năng kích hoạt. Trong Chrome, việc triển khai dựa trên dựa trên mô hình dựa trên mã thông báo hoá ra lại quá phức tạp để xác định tính nhất quán trên tất cả các API được kích hoạt. Ví dụ: Chrome đã được cho phép truy cập không đầy đủ vào các API có giới hạn kích hoạt thông qua postMessage()setTimeout() cuộc gọi; và thao tác kích hoạt người dùng được hỗ trợ bằng Promise (Lời hứa), XHR thân mến! Tương tác với tay điều khiển trò chơi, v.v. Hãy lưu ý rằng một vài trong số là những lỗi phổ biến nhưng tồn tại từ lâu.

Trong phiên bản 72, Chrome chuyển giao diện Kích hoạt người dùng 2, giúp người dùng tình trạng kích hoạt hoàn tất cho tất cả API được kiểm soát. Thao tác này sẽ giải quyết những điểm không thống nhất được đề cập ở trên (và một vài vấn đề khác, như MessageChannels), mà chúng tôi tin rằng sẽ giúp đơn giản hoá trang web phát triển xung quanh việc kích hoạt người dùng. Ngoài ra, việc triển khai mới cung cấp triển khai tham chiếu cho một đề xuất quy cách mới nhằm mục đích tập hợp tất cả cá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 2 bit ở 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 người dùng kích hoạt) và một bit tạm thời cho trạng thái hiện tại (nếu một khung hình đã thấy người dùng kích hoạt 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 hình sau khi được đặt. Bit tạm thời được thiết lập vào mỗi tương tác của người dùng và được đặt lại sau khi hết hạn khoảng thời gian (khoảng một giây) hoặc thông qua một lệnh gọi đến một API sử dụng kích hoạt (ví dụ: window.open()).

Lưu ý rằng các API có cổng kích hoạt khác nhau phụ thuộc vào kích hoạt của người dùng ở các cách; API mới không thay đổi bất kỳ hành vi cụ thể nào của API này. Ví dụ: chỉ cho phép một cửa sổ bật lên cho mỗi lần kích hoạt của người dùng vì window.open() sử dụng kích hoạt người dùng như trước đây, Navigator.prototype.vibrate() tiếp tục có hiệu lực nếu một khung hình (hoặc bất kỳ khung phụ nào của khung đó) đã từng thấy hành động của người dùng, và cứ tiếp tục như vậy.

Điều gì sẽ thay đổi?

  • Kích hoạt người dùng phiên bản 2 chính thức hoá khái niệm hiển thị kích hoạt người dùng qua ranh giới khung: hành động tương tác của người dùng với một khung cụ thể giờ đây sẽ kích hoạt tất cả các khung chứa (và chỉ những khung đó) bất kể máy chủ gốc. (Trong Chrome 72, chúng tôi có giải pháp tạm thời nhằm mở rộng chế độ 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 kích hoạt người dùng đến các khung hình phụ một cách rõ ràng.)
  • Khi một API có chế độ 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, mã này sẽ hoạt động miễn là người dùng kích hoạt trạng thái là "đang hoạt động" (ví dụ: chưa hết hạn và cũng chưa được tiêu thụ). Trước người dùng Kích hoạt phiên bản 2 sẽ không thành công vô điều kiện.
  • Nhiều hoạt động tương tác không dùng đến của người dùng trong cầu chì khoảng thời gian hết hạn thành một lượt kích hoạt tương ứng với lượt tương tác cuối cùng.

Ví dụ về tính nhất quán trong các API có chế độ kích hoạt

Dưới đây là 2 ví dụ về cửa sổ bật lên (mở bằng window.open()) cho thấy cách tính năng Kích hoạt người dùng phiên bản 2 tạo ra hành vi của các API được kiểm soát kích hoạt nhất quán.

Cuộc gọi setTimeout() theo chuỗi

Ví dụ này lấy từ bản minh hoạ setTimeout() của chúng tôi. Nếu trình xử lý click cố mở cửa sổ bật lên trong vòng một giây, trình xử lý dự kiến sẽ thành công bất kể mã "kết hợp" như thế nào sự chậm trễ. Trang Kích hoạt người dùng phiên bản 2 đáp ứng kỳ vọng 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);

Khi không có tính năng Kích hoạt người dùng phiên bản 2, trình xử lý sự kiện thứ hai sẽ gặp lỗi trong tất cả các trình duyệt mà chúng tôi đã kiểm tra. (Ngay cả bước đầu tiên không thành công trong một số trường hợp.)

Cuộc gọi postMessage() giữa nhiều miền

Dưới đây là ví dụ từ bản minh hoạ postMessage() của chúng tôi. Giả sử trình xử lý click trong một khung phụ nhiều nguồn gốc gửi trực tiếp 2 thông báo vào khung chính. Khung mẹ phải có thể mở cửa sổ bật lên khi nhận một trong hai thông báo sau (chứ 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 chính sẽ không thể mở cửa sổ bật lên khi nhận được tin nhắn thứ hai. Ngay cả tin nhắn đầu tiên cũng không thành công nếu được "chuỗi" sang một địa chỉ khác khung trên nhiều nguồn gốc (nói cách khác, nếu dịch vụ nhận đầu tiên chuyển tiếp thư sang thiết bị khác).

Tính năng 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à tạo chuỗi.