Skip to content

[focus-without-user-activation] Clarify focus behavior for subframes with denied policy #11839

@ffiori

Description

@ffiori

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
  1. If B has focus,
    a. Can B move focus around inside itself with let's say element.focus()? (row 1)
    b. What about passing focus to a subframe C with iframe.focus()? (row 2)
  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 False

New 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 False

This 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions