← WebKit — 2026-W41

c413d63ffc923ece86a0e4df8967f8cb9065ced5  Tabbing to an invoker should focus its popover itself if the popover is focusable

severity low class LogicError confidence 0.3 signal score 8.07 Source/WebCore/dom
Primitive: cyclic popover invoker chain causing unbounded focus traversal
Triage note: Reworks popover focus navigation with invokerForOpenPopoverIgnoringCycles and invokerChainReachesPopover (bounded by topLayerElements().size()) to break invoker->popover cycles, plus descends into focusable popovers/subframes. The cycle guard suggests a potential infinite-loop/hang; otherwise focus-order correctness, no memory-safety bug.
Structural signals: smartptr_member_added (+4.87), null_guard (+1.2), area:Source/WebCore/dom (+2.0)

Commit message

Tabbing to an invoker should focus its popover itself if the popover is focusable
rdar://188267440
https://bugs.webkit.org/show_bug.cgi?id=325129

Reviewed by Ryosuke Niwa.

Before this change, an open popover with an invoker always passed focus on to its contents. The
popover was never keyboard-focusable itself, even with tabindex set. With this change, the popover
acts as the root of its own focus navigation scope. It can take focus itself, and its contents come
next in sequential navigation.

Popovers that are direct children of the document element, a shadow root or a shadow host no longer
get invoker-based focus scoping.

https://html.spec.whatwg.org/multipage/interaction.html#associated-focus-navigation-owner

* LayoutTests/imported/w3c/web-platform-tests/html/semantics/popovers/popover-focus-2_test1-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/popovers/popover-focus-2_test2-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/popovers/popover-focus-2_test3-expected.txt:
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/html/semantics/popovers/popover-focus-inside-slot-expected.txt:
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::isKeyboardFocusable const):
* Source/WebCore/page/FocusController.cpp:
(WebCore::invokerForOpenPopoverIgnoringCycles):
(WebCore::invokerChainReachesPopover):
(WebCore::invokerForOpenPopover):
(WebCore::openPopoverForInvoker):
(WebCore::FocusNavigationScope::firstNodeInScope const):
(WebCore::FocusNavigationScope::lastNodeInScope const):
(WebCore::FocusNavigationScope::scopeOwnedByScopeOwner):
(WebCore::isFocusableElementOrScopeOwner):
(WebCore::isNonFocusableScopeOwner):
(WebCore::FocusController::findFocusableElementAcrossFocusScope):
(WebCore::FocusController::nextFocusableElementWithinScope):
(WebCore::FocusController::previousFocusableElementWithinScope):

Canonical link: https://commits.webkit.org/323172@main

Before / after

Loading diff…