Skip to content

Base focus permission on the initiating document - #4

Draft
ffiori wants to merge 4 commits into
mainfrom
user/ffiori/focus-setter-only
Draft

ffiori wants to merge 4 commits into
mainfrom
user/ffiori/focus-setter-only

Conversation

@ffiori

@ffiori ffiori commented Sep 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

Base focus-without-user-activation authorization on the document initiating the focus change,
rather than the document receiving focus.

This separates two questions: whether a document may initiate a focus move, and whether the
destination is valid for focusing. The permission check receives only focusSetterDocument;
destination-validity checks remain in the calling algorithms.

Related: whatwg#12032, whatwg#12470, and whatwg#11839.
This is an alternative to the approach in whatwg#11519.

Motivation

An allowed parent should be able to delegate focus into a child with the feature denied. Conversely,
a restricted initiator should not be authorized merely because its target has the feature enabled,
has transient activation, or already has focus.

Once an embedded application receives focus, it also needs to manage its controls, delegate to nested
content, and reclaim focus from that content. These are the compatibility cases described in
whatwg#11839, including the Teams and Microsoft 365 embedding scenarios.

The TPAC 2024 resolution
requires parent-to-child delegation. The focused-subtree behavior below is an additional proposal;
it is not presented as already-resolved consensus.

Allow focus steps

allow focus steps(focusSetterDocument):
  1. If focusSetterDocument is not fully active, return false.
  2. If focusSetterDocument is allowed to use the feature, return true.
  3. If focusSetterDocument's relevant global has transient activation,
     return true.
  4. Return has focus steps(focusSetterDocument).

The target's policy and activation no longer grant permission. There is also no target-containment
check: this feature controls the initiator, not whether a particular document may receive focus.

has focus steps includes focus in descendant navigables and requires the top-level traversable to
have system focus. A restricted document can therefore manage focus while it or one of its
descendants has focus. Once focus leaves that subtree, the focus-based allowance ends.

Passing the initiator explicitly

The six script entry points capture the incumbent global object's associated document:
Window.focus(), HTMLOrSVGOrMathMLElement.focus(), dialog.show(), dialog.showModal(),
showPopover(), and togglePopover(). The document is then passed through the dialog and popover
algorithms.

This uses the incumbent calling context rather than the current realm of a method obtained from
another window. The shared permission helper does not consult the JavaScript stack.

Button commands capture the invoking element's node document before dispatching command, so an
event handler adopting the invoker into another document cannot change the authority for the pending
command. That document is forwarded to the popover and dialog command handlers. Popover-target
activation and the select picker likewise supply their invoking element's document.

Automatic autofocus uses the element's node document, including when insertion is performed by the
parser without author script. Permission is checked both at insertion and when candidates are
flushed. A candidate authorized while its document had focus is discarded if that authorization no
longer holds at flush time, preventing queued autofocus from reclaiming focus after a handoff. This
also means that expired activation alone cannot authorize a pending candidate.

Fully-active destination checks are kept separate in the Window, element, dialog, and popover
focusing paths; autofocus retains its existing validity and sandbox checks.

Intended behavior

Let allowed A contain restricted B and D, with restricted C nested inside B. Unless noted, there is
no transient activation. These results concern permission to focus; normal focusability, access
control, and user-agent rules still apply.

Scenario Result
A focuses an element in restricted B Allow: A's policy authorizes it
Focused B focuses another element in B Allow
Focused B delegates to child C Allow
C is focused; B retakes focus into its own document Allow
Focused B hands focus to sibling D or back to A Allow
Focus has moved to D; B tries to reclaim it without activation Deny
Unfocused B targets allowed A Deny: A's policy does not authorize B
Only D has activation; B tries to focus D Deny: D's activation does not authorize B
Unfocused D targets B while focus is in B's child C Deny
B has transient activation and requests focus outside its subtree Allow

An allowed or already-focused setter can also hand focus across an opener/popup relationship, subject
to existing Window access and focusing rules. An unfocused, restricted popup cannot move its opener's
focus merely because the opener has the feature enabled.

Deliberate scope and compatibility changes

This is not an editorial refactoring or a claim that all behavior allowed by today's specification is
preserved. Target-authorized requests from an otherwise unauthorized caller become blocked. An
authorized caller can request focus outside its own subtree, including into a restricted document.

Same-origin access rules are unchanged. Declarative actions are attributed to their invoking document;
this does not attempt to restrict every focus effect achievable through same-origin DOM manipulation.

Sequential user focus navigation and focus restoration algorithms are not newly policy-gated here.
No fullscreen-specific restriction is introduced. The general authorization changes also apply to
fullscreen content; any additional fullscreen rule is deferred to a separate discussion.

Coverage and review

Existing WPTs express part of the proposed behavior:

ffiori and others added 4 commits September 10, 2026 17:24
Check policy and transient activation on the initiating document, and let
Snapshot the invoking document before firing a button's command event.
Forward that document through popover and dialog command handlers so
adopting the invoker cannot change the authority for a pending action.

Recheck an autofocus candidate's document when flushing the queue.
A candidate must not reclaim focus using an allowance that ended after
handoff or activation expiry. Clarify the current-focus and system-focus
requirements in the permission algorithm's note.

The proposal remains setter-only: a fully active initiator is authorized
by its permissions policy, transient activation, or current focus chain.
There is no destination-policy grant or destination-containment gate.
Destination validity is checked separately by the callers, and
fullscreen-specific policy remains deferred.

Related: whatwg#12032
Related: whatwg#12470
Related: whatwg#11839

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec
Show popover already checks destination validity after author callbacks.
Do not repeat its fully-active precondition in popover focusing steps.
Remove the explanatory note to keep the specification change concise.

Retain the setter, Window, element, and dialog guards: their other steps
do not guarantee that the relevant documents are fully active.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec
Capture the focus setter Document only in togglePopover()'s showing
branch, and after show()'s existing already-open checks.

Keep capture ahead of author callbacks and preserve the existing focus
permission checks and forwarded arguments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e74da64d-6b71-41bf-8b15-4e34d5f7a6ec
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant