-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Summary
Problem
There's high-level agreement on the policy, but the spec doesn't define what a subframe can do once it already has focus.
Proposal
To avoid breaking existing sites and ensure adoption, allow subframes to manage focus internally once they have focus.
Why This Matters
This change preserves compatibility for existing sites while maintaining the policy’s core goal: preventing focus theft from the parent frame.
Explanation
This is the current status on the focus-without-user-activation Permissions Policy: there's a satisfied TAG review and support from WebKit. There's also a merged spec PR on this repo. But the feature is blocked because there are still these behaviors that need to be agreed upon.
It was resolved in TPAC 2024 that "Focus delegation should also be allowed (allow parent frame programmatically set focus into child iframe)", this is not spec'd yet, but also these scenarios came up in different discussions:
Let's say we have three nested iframes: a top level frame A hosting iframe B hosting iframe C, with B and C having the policy denied, and we have the following cases:
| Focus Setter Frame | Target | Currently Focused Frame |
|---|---|---|
| B | Element in B | B |
| B | iframe C | B |
| B | Element in B | C |
- If B has focus,
a. Can B move focus around inside itself with let's sayelement.focus()? (row 1)
b. What about passing focus to a subframe C withiframe.focus()? (row 2) - If C has focus,
a. Can B take focus back and set it on any element of itself? (row 3)
As far as I understand after discussing with customers, all of the above examples (1a, 1b, 2a) should be possible because the idea behind the policy is to prevent stealing focus from the parent, not to restrict what a frame can do once it has focus.
The fact that some webpages count on behaviors like the cases discussed here is the original motivation for discussing this. This tries to avoid breaking existing sites that are embedded with this policy denied so the feature can be more easily adopted, while still fulfilling the need for preventing frames from stealing focus from a parent.
Current Pseudo-algorithm
Current allow focus steps algorithm as it's spec'd nowadays:
def allowFocus(focus_setter_frame, target):
if focus_setter_frame.hasPolicyAllowed():
return True
if target.frame.hasTransientActivation(): # the user initiated the action
return True
return FalseNew Pseudo-algorithm Proposed
def allowFocus(focus_setter_frame, target, currently_focused_frame):
if focus_setter_frame.hasPolicyAllowed():
return True
if target.frame.hasTransientActivation(): # the user initiated the action
return True
# This is the only new addition (inclusive descendant means same frame or nested child).
if currently_focused_frame.isInclusiveDescendantOf(focus_setter_frame):
return True
return FalseThis modification would allow the behaviors mentioned in the examples above, while the rest of the algorithm works like it's currently spec'd as of now.
Any feedback or alternative approaches are welcomed. This issue aims to provide a focused place for further discussion, following up on the conversation in this spec PR #11519.