[focus-without-user-activation] Thread the focus initiator through focus-producing APIs - #1
Draft
ffiori wants to merge 3 commits into
Draft
[focus-without-user-activation] Thread the focus initiator through focus-producing APIs #1ffiori wants to merge 3 commits into
ffiori wants to merge 3 commits into
Conversation
Carry the initiating document through direct focus, dialog, and popover algorithms. Use explicit invoking documents for user-agent paths and block the focused-subtree exception while a focused descendant is fullscreen. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec
Prevent caller-based authorization from reaching dialog focusing steps that dereference a null node navigable for detached or synthetic documents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec
Keep the focus-without-user-activation change scoped to caller attribution and focused descendants. Fullscreen interaction can be specified separately once its exact boundary is agreed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This updates the
focus-without-user-activationchecks to use the document that initiates a focus-producing operation, instead of implicitly using the document that owns the focus target.It also allows a document with the policy denied to continue managing focus when that document, or one of its nested browsing contexts, already has focus. This preserves focus management in embedded applications without allowing an unfocused restricted frame to steal focus from its parent or a focused sibling.
This is a more complete version of whatwg#11519 and follows the design discussion in whatwg#11839, whatwg#12032, and whatwg#12470.
Motivation
The policy is intended to prevent unexpected cross-frame focus theft, rather than disable ordinary focus management after a host has already delegated focus into an embedded application.
Consider a top-level document A containing B, which in turn contains C. B and C have
focus-without-user-activationdenied.The first three cases are needed by applications that embed nested experiences, including Teams and other Microsoft 365 applications. The last case preserves the policy's anti-focus-theft behavior.
The current target-based checks also produce incorrect results for cross-document calls. For example, when B invokes
focus()on an element owned by C, B is responsible for moving focus. C's policy or transient activation should not authorize or reject B's request.Changes
This PR:
allow focus stepsto operate on afocusSetterDocument;Window.focus();HTMLOrSVGOrMathMLElement.focus();HTMLDialogElement.show()andshowModal();showPopover()andtogglePopover();Why use the incumbent global object?
The
current global objectcan describe the realm associated with the method or target object instead of the script that initiated a cross-document call. That does not distinguish cases such as B invokingfocus()on an element owned by C.The incumbent global object represents the author script responsible for the operation, following the same general caller-attribution model used by APIs such as
postMessage(). Capturing it at the API boundary also gives implementations an explicit source document to carry through cross-origin or cross-process dispatch, instead of trying to recover caller identity inside the target-side algorithm.The incumbent is captured before nested dialog or popover algorithms run, so callbacks such as
beforetoggledo not replace the original focus initiator.Permissions Policy and activation
Authorization is based on the focus setter:
focus-without-user-activation, allow the request.This means:
Dialogs, popovers, and autofocus
Dialogs and popovers can move focus as part of their algorithms. Threading the setter through those paths avoids making
element.focus()caller-based while leavingshowModal()orshowPopover()target-based.User-agent invocation paths use the document of the invoking element as their explicit source. Automatic document-load autofocus continues to use its target document because it can run from the event loop without an incumbent author script.
Fullscreen
The focused-subtree exception is not used when a focused document in the setter's subtree has a fullscreen element. This addresses the case where an ancestor attempts to retake focus from fullscreen embedded content after the activation used to enter fullscreen has expired.
Permissions Policy or fresh transient activation are evaluated before this fallback guard.
The exact fullscreen boundary, including whether focus movement entirely within the active fullscreen surface should remain allowed, is an area where further review and WPT coverage would be useful.
Tests
The intended direct-focus behavior is represented by the following WPTs:
focus-without-user-activation-focused-frame-descendant.htmlfocus-without-user-activation-focused-frame-descendant-cross-origin.https.sub.htmlfocus-without-user-activation-focused-frame-descendant-remote.https.sub.htmlfocus-without-user-activation-setter-policy.htmlfocus-without-user-activation-target-frame-state-ignored.htmlAdditional coverage is still needed for fullscreen, popup/opener behavior, cross-document dialogs and popovers, and automatic autofocus.
Review focus
The main policy questions for review are:
Related
focus-without-user-activationwhatwg/html#10672