Handle the delegatesFocus branch of get-the-focusable-area before
rejecting non-focusable focus targets. This lets host.focus() move focus
to the first focusable delegate in the shadow tree, or preserve an
already-focused descendant.
Treat delegatesFocus shadow hosts as focus delegates even when tabindex
would otherwise make the host focusable. Skip inert delegate candidates,
and reject inert shadow hosts before looking for a delegate. Check focus
inertness through shadow-host ancestry so direct focus cannot enter
inert shadow subtrees.
Rebaseline the disabled delegatesFocus WPT now that this behavior
passes. Cover delegated focus, hidden and inert delegate candidates,
tabindex hosts, inert hosts and direct delegates inside inert hosts, and
hosts without any focusable delegate.
Apply the focusing steps' get-the-focusable-area mapping before
rejecting a non-focusable target. This preserves documentElement.focus()
by mapping the non-focusable document element to the Document viewport.
Also map rendered navigable containers with content navigables to their
active document, while leaving hidden containers unfocused. Preserve
Window focus events for child document viewports reached through iframe
focus, while still suppressing the top-level viewport surrogate events.
Treat rendered object elements as focusable through their default
non-null tabindex, even when they show fallback or image content instead
of a child navigable.
Keep the spec focus-chain common-tail handling intact for viewport
focus. The Document object is only our surrogate for the viewport, so
designate viewport focus from the new focus target without dispatching
Window focus/focusin events for that top-level surrogate.
Pass that viewport surrogate as the fallback target for fragment
scrolling and NavigateEvent focus reset, so unfocusable body or fragment
targets still clear stale element focus.
Cover documentElement.focus() in both the activeElement and focus-chain
tests, including a tabindex document element that remains focused as an
element. Cover object focus with and without a child navigable, hidden
object focus attempts, iframe focus events, hidden iframe focus, and
blurring a focused iframe after it becomes hidden. Also cover viewport
fallback for intercepted navigation focus reset and fragment scrolling
to an unfocusable target.
Do not let elements inside display:none subtrees become focus targets.
The HTML focusable-area model only allows elements to be focusable when
they are rendered, delegate rendering to their children, or are relevant
canvas fallback content.
Preserve blur for the current focused area after script hides it, since
the unfocusing steps operate on the old focus target. Check display:none
through the flat-tree style parent chain, so slotted controls inside
hidden slot subtrees cannot become focused.
Cover hidden ancestors with materialized computed style, display:none
controls, display:contents, hidden focused controls, and slotted cases
inside hidden and visible shadow-tree subtrees.
Previously we were inconsistent by generating code for enum definitions
but not generating code for dictionaries. With future changes to the
IDL generator to expose helpers to convert to and from IDL values
this produced circular depdendencies. To solve this problem, also
generate the dictionary definitions in bindings headers.
Remove 11 heavy includes from Document.h that were only needed for
pointer/reference types (already forward-declared in Forward.h), and
extract the nested ViewportClient interface to a standalone header.
This reduces Document.h's recompilation cascade from ~1228 files to
~717 files (42% reduction). Headers like BrowsingContext.h that were
previously transitively included see even larger improvements (from
~1228 down to ~73 dependents).
This comment at the end of LibWeb/HTML/Focus.cpp:
```
// What? It already doesn't have system focus, what possible
// platform-specific conventions are there?
```
Originally followed and referred to this FIXME on line 319:
```
// FIXME: Otherwise, apply any relevant platform-specific conventions
// for removing system focus from topDocument's
// browsing context, and run the focus update steps with old chain,
// an empty list, and null respectively.
```
During the course of #7233, it was accidentally moved and attached
to a different context, following this comment below on line 325:
```
// NOTE: The unfocusing steps do not always result in the focus
// changing, even when applied to the currently focused
// area of a top-level traversable. For example, if the currently
// focused area of a top-level traversable is a
// viewport, then it will usually keep its focus regardless until
// another focusable area is explicitly focused
// with the focusing steps.
```
Rather than move it to the correct place and become its git blame
villain in the process, I once more seek to remove it.
Model "viewport focus" with Document::focused_area == nullptr.
Focus.cpp:
When a blur occurs, remove the document entry from the old chain
before running the focusing steps. This ensures the document atop the
new chain is not discarded. Focus::run_focus_update_steps will then
set focused_area to nullptr, indicating viewport focus.
EventHandler.cpp:
Split hit testing in handle_mousedown. Use an exact hit test for
focus/blur decisions, and a subsequent cursor hit test for
selection/caret.
Update a couple of focus-related spec steps and their implementations.
The most relevant change is that we no longer allow focusing on elements
that return false for `->is_focusable()`, which necessitates fixing a
broken test that tried to `.focus()` on `<div>`s that were not
focusable. That test's output now more accurately reflects the expected
outcome as seen in other browsers.
And make it a DOM::Node, not DOM::Element. This makes everything flow
much better, such as spec texts that explicitly mention "focused area"
as the fact that we don't necessarily need to traverse a tree of
elements, since a Node can be focusable as well.
Eventually this will need to be a struct with a separate "focused area"
and "DOM anchor", but this change will make it easier to achieve that.
The focus chain always consists of newly created GC::Root objects, so
the condition always produced `false`. The fix is to use GC::Root's
overloaded operator== method, which compares the pointers of the stored
type.
This fixes Figma dropdowns and context menus instantly disappearing
upon opening them. This is because they become focused when they insert
them. Because of this bug, it would fire blur events all the way up to
and including the window. Figma listens for the blur event on the
window, and when received, it will instantly hide dropdowns and context
menus. The intention behind this seems to be hiding them when the user
clicks off the browser window, or switches tab.
I believe this is an error in the UI Events spec, and it should be
updated to match the HTML spec (which uses WindowProxy everywhere).
This fixes a bunch of issues already covered by existing WPT tests.
Spec bug: https://github.com/w3c/uievents/issues/388
Note that WebKit has been using WindowProxy instead of Window in
UI Events IDL since 2018:
816158b4aa
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:
* JS::NonnullGCPtr -> GC::Ref
* JS::GCPtr -> GC::Ptr
* JS::HeapFunction -> GC::Function
* JS::CellImpl -> GC::Cell
* JS::Handle -> GC::Root