Commit graph

68 commits

Author SHA1 Message Date
Aliaksandr Kalenik
21245e7adb LibWeb: Follow paint order for async wheel hit testing
Async wheel routing used scroll node scrollports as the hit-test
surface. That works while the scrollable content is the topmost content
under the pointer, but it ignores the rest of the painted scene. A
sibling with a higher z-index can visually cover a nested scroller
without being part of that scroller's subtree. In that case the
compositor could still pick the covered scroller from its scrollport
rect and consume the wheel event, even though normal hit testing would
target the covering element and let the page or an ancestor handle the
scroll.

Record wheel hit-test targets as display-list metadata in paint order
and resolve each target to the scroll frame that should receive wheel
deltas. The compositor can then walk those targets from front to back,
preserve non-passive wheel regions as main-thread barriers, and only
scroll the node associated with the topmost hit-testable box.
2026-05-14 15:32:03 +02:00
Aliaksandr Kalenik
82b016ed19 LibWeb: Rebuild async scrolling from display list
Use compositor hit-test commands in the display list to rebuild async
wheel targets, and serialize the async scroll metadata needed to
reconstruct AsyncScrollingState from the same display-list snapshot.

Driving the async scroll tree off the display list rather than a
separately collected tree has a few benefits:

- No additional full paintable tree traversal is required, since the
  information needed by the compositor is gathered while recording
  the display list.
- The display list is already serializable, so the async scroll tree
  no longer needs its own serialization path.
- It is more debuggable, as the existing display list dump now also
  covers the data used to reconstruct the async scroll tree.
- In the future we will want to include other areas that can
  interfere with hit-testing; recording them during display list
  construction makes it straightforward to preserve a hit-testing
  order that matches painting order.
2026-05-13 18:36:07 +02:00
Andreas Kling
b57cc2bb7a LibWeb: Invalidate async wheel state on listener changes
Bump a page-level wheel listener generation whenever non-passive wheel
listeners are added or removed. Store that generation in async scrolling
snapshots, and mark compositor wheel admission stale when listener state
changes before a current snapshot arrives.

This keeps the UI-process compositor wheel bypass from trusting an old
no-listener snapshot after script has installed a cancelable wheel
listener. Add async-scrolling coverage for the listener generation bump
and the fresh routing state after a blocking wheel listener is added.
2026-05-12 20:57:08 +02:00
Andreas Kling
d36d336c82 LibWeb: Allow viewport async scroll with nested scrollers
Allow compositor wheel routing to stay enabled when the scroll tree also
contains non-viewport scroll nodes. The compositor still rejects an
individual wheel whose hit-test target is not the viewport, so element
scrollers continue to fall back to the main thread until their scroll
offsets can be adopted safely.

Move the routing admission helper next to the async scrolling state and
expose test-only internals for the routing result and wheel target. Add
coverage for a page that has both a viewport scroller and a nested
element scroller.
2026-05-12 20:57:08 +02:00
Andreas Kling
ce1521441f LibWeb: Reject async wheels over nested navigables
Add main-thread wheel regions to async scrolling snapshots for embedded
navigable containers. A viewport scroll node covers the iframe box, so
the compositor must explicitly treat that rectangle as a synchronous
routing boundary before it accepts a UI-process wheel bypass.

Hit-test those regions in both internals admission and the compositor's
async enqueue path. The enqueue path now stores the hit-tested scroll
target in the queued command, ensuring accepted input is applied to the
same target that passed admission instead of being reselected later.

Add a text test for iframe routing. A wheel over the nested navigable is
rejected for async scrolling, while a wheel over the parent viewport
remains admitted.
2026-05-12 20:57:08 +02:00
Andreas Kling
191bd46cb8 LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.

Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.

Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-12 20:57:08 +02:00
Callum Law
5e3028013c LibWeb: Store AnimationTimeline associated document as Ref
We always had an associated document for the spec defined timelines
(i.e. `DocumentTimeline` and `ScrollTimeline`) but not for the ad-hoc
`InternalAnimationTimeline` so lets store one for that as well and use a
`Ref` instead of a `Ptr`.

This requires `convert_a_timeline_time_to_an_origin_relative_time` to be
implemented for `InternalAnimationTimeline` since it can now be called
where it previously wasn't due to the absence of a document - we just
return an empty optional since it's not used anywhere.
2026-05-08 22:59:16 +02:00
Aliaksandr Kalenik
568b7ce7ea LibWeb: Make Paintable tree ref-counted
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.

We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
2026-05-07 15:03:44 +02:00
Tim Ledbetter
08dd93a8e0 LibWeb: Count previous-sibling visits during style invalidation
Add a `previousSiblingInvalidationWalkVisits` counter that increments
once per element examined during the previous-sibling walk in
`invalidate_style` on `NodeInsertBefore` and `NodeRemove`. This can be
expensive and the next commit introduces an optimization that prevents
this work being done unnecessarily
2026-04-26 16:14:43 +02:00
Andreas Kling
a870475f2b LibWeb: Expose style recomputation counters through Internals
We already had Internals counters for :has() invalidation work and
the high-level invalidation passes. Add four more that record how
often Element::recompute_style and recompute_inherited_style run,
and how often each bails out as a no-op. The new tests on this
branch use these counters to assert that mutations don't trigger
redundant style recomputation.
2026-04-26 10:40:58 +02:00
Andreas Kling
73aafc2ade LibWeb: Expose full-style-invalidation counter through Internals
Full-subtree style invalidations are cheap to count where they happen
(Node::invalidate_style on a document node) but previously invisible to
tests. Surface the counter as fullStyleInvalidations alongside the
existing style invalidation counters so text tests can assert that a
given mutation path stayed surgical instead of degrading to a broad
document restyle.
2026-04-23 16:45:22 +02:00
Shannon Booth
fd44da6829 LibWeb/Bindings: Emit one bindings header and cpp per IDL
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.

This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.

Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
2026-04-21 07:36:13 +02:00
Andreas Kling
029b4998e5 LibWeb: Skip useless sibling scans in generic :has() walks
Track whether any :has() relative selector in a style scope uses a
sibling combinator and let the generic ancestor walk consult that
before scanning ancestor siblings.

This keeps descendant-only :has() invalidations from walking unrelated
siblings while preserving the existing behavior for selectors that use
+ or ~. Add counter-based test coverage so the reduced sibling scans
stay visible through the invalidation counters.
2026-04-20 13:20:41 +02:00
Andreas Kling
e1d62eaf85 LibWeb: Bucket :has() invalidation metadata by feature
Record per-feature :has() invalidation metadata instead of only tracking
whether some selector somewhere mentions a class, id, attribute, tag,
or pseudo-class. The new buckets preserve the relative selector and a
coarse scope classification for each :has() argument, which gives the
next invalidation step enough information to route mutations more
precisely.

Keep this commit behavior-preserving for mutation handling by only
switching the lookup path over to the new metadata buckets. Expose a
test-only counter for the number of candidate :has() metadata entries a
mutation matched, and add coverage showing that one feature can map to
one or multiple :has() buckets without forcing a document-wide yes/no
answer.
2026-04-20 13:20:41 +02:00
Andreas Kling
a72fae8d36 LibWeb: Add test-only counters for :has() invalidation work
Introduce a small set of counters on Document that track the work done
while processing :has() invalidation: how often the upward walk runs,
how many elements it visits, how often matches_has_pseudo_class() is
invoked, how well the per-pass result cache performs, and how many
elements transition from clean to needs-style-update.

Expose the counters through internals so tests can assert precise bounds
on the invalidation work triggered by a mutation, which regular
reference tests cannot express.

Add a css-has-invalidation test suite that covers subject-position,
non-subject-position, sibling-combinator, and no-:has() cases. The
baseline tests share a helper script so later coverage can reuse the
same counter-printing path.

The counters are test-only observation; they do not affect style
computation itself.
2026-04-20 13:20:41 +02:00
Sam Atkins
7c1d359790 LibWeb: Clean-up more input state after running each test
The clean-up in 71c457c36e turns out to
not be enough for all cases. So be more thorough and clear up anything
that could affect the next test.

In particular this fixes flakiness in `Text/input/select-text.html` but
hopefully it solves the issue for good!
2026-04-17 14:13:18 +01:00
Sam Atkins
71c457c36e LibWeb: Clean up mouse-down state after running each test
If a test calls internals.mouseDown() without also calling mouseUp(),
the following test can start with the EventHandler thinking a mouse
button is still held, and this confuses drag events. This led to our
drag-and-drop.html test being flaky.

Usually the GC collects the EventHandler::m_mousedown_target, but
sometimes that is kept alive, probably because of the conservative GC
stack/register scan.

Manually clear this state at the end of each test so it can't interfere.

Verified with this command, which fails ~40% of the time without this
change, and fails 0% of the time with it. :^)

```sh
Meta/ladybird.py run test-web -j1
    -f 'Text/input/click-behind-user-select-none-fragment.html'
    -f 'Text/input/HTML/drag-and-drop.html' --repeat 50
```
2026-04-16 14:53:32 +02:00
Zaggy1024
ed8ba673de LibWeb: Add internals.gcAsync() to run collection with an emptier stack
This will allow us to have more predictable and effective testing of GC
than the synchronous version.
2026-04-08 03:03:38 +02:00
Aliaksandr Kalenik
0914cd07c3 LibWeb: Add internals.dumpSessionHistory() for navigation testing
Dumps the calling navigable's session history entries with relative step
numbers and filename-only URLs, making the output stable across test
runs. Each entry shows its step index, filename (with fragment if any),
and a "(current)" marker when it matches the traversable's current step.
2026-04-04 11:30:55 +02:00
Zaggy1024
a51967311e Everywhere: Consolidate double/triple click handling to use click count
This moves normal/double/triple click checking into WebContent, the
client only has to send a click count in order to activate a double
or triple click in the content. This means that the AppKit UI will no
longer fire multiple double clicks when clicking in place more than 3
times. This matches the behavior of other browsers on macOS.

We will now also fire the click event regardless of whether a dblclick
event will follow, as the spec requires.
2026-03-17 04:01:29 -05:00
Shannon Booth
d6d80e5d52 LibWeb: Load cookies test from HTTP server
Allowing us to remove the internals hook.
2026-02-21 23:00:57 +01:00
Aliaksandr Kalenik
0bec9a1e5d LibWeb: Add dumpPaintableTree() to Internals API
Expose the paintable tree dump through the internals object, analogous
to the existing dumpLayoutTree(). This is useful for testing and
debugging painting behavior.
2026-02-15 17:47:30 +01:00
Shannon Booth
bc93ba4530 LibWeb/Bindings: Implement [SecureContext] extended attribute
Unfortunately this is a bit of a pain to test as it is surprisingly
difficult to create a non secure context in our test harness.
This is because both file scheme URLs and localhost are considered
secure contexts.

To test this, add a very specific internals setter to change the
top level origin of the environment for the current realm.
2026-02-14 20:22:40 +01:00
Praise-Garfield
56287ca90f LibWeb: Clear textarea with empty string for WebDriver
The WebDriver clear handler for textarea elements sets the raw
value to child_text_content() instead of an empty string. This
is a copy-paste from the adjacent reset handler, which correctly
uses child_text_content() per its own spec. The clear spec says
"set the raw value of element to an empty string".
2026-02-13 15:29:02 -05:00
Aliaksandr Kalenik
901cc28272 LibWeb: Reduce recompilation impact of DOM/Document.h
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).
2026-02-11 20:02:28 +01:00
Aliaksandr Kalenik
5be98aaa07 LibWeb: Add internals.dumpLayoutTree(node) API
This new testing API dumps the layout subtree rooted at a given DOM
node. It will be useful for testing partial layout tree rebuilds, where
we need to verify the layout tree structure for specific subtrees rather
than the entire document.
2026-02-09 21:05:45 +01:00
Jelle Raaijmakers
2a2f8ef90b LibWeb+UI: Support triple clicking and dragging paragraphs
When triple clicking on text, we should select the entire paragraph, or
entire line in <input>s and <textarea>s. If the mouse button is held
down and the user starts dragging, the selection expands with additional
paragraphs or lines.

This expands on the work of Kai Wildberger (PR #7681) but was adjusted
for the work that happened previously to support double click + drag
moves and includes triple click support for our Qt UI.

Co-authored-by: Kai Wildberger <kiawildberger@gmail.com>
2026-02-06 14:18:10 +00:00
Jelle Raaijmakers
f55fe69d4d LibWeb: Rework Internals' mouse control
Instead of defining somewhat high level mouse actions, allow granular
control of mouse clicks and mouse down/up/move events. We will want to
simulate things like holding down a mouse button after double clicking
and then dragging the mouse to another position in the future, and this
enables that.
2026-02-06 14:18:10 +00:00
Jelle Raaijmakers
c56c933014 LibWeb+WebContent: Instantly update DPI for painting and media queries
If our UI informed the page of a DPI change, we would store the new
device pixel ratio and leave it at that. It would take a layout/style
update (e.g. by clicking the page) to actually render the page using the
new DPI. This is very visible on macOS when moving the Ladybird window
from a 1x resolution monitor to a HiDPI 2x monitor.

We now instantly update the backing stores and mark media queries for
reevaluation. Moving the Ladybird window on macOS now immediately
updates the page when dragging it to a HiDPI monitor.
2026-02-06 09:01:26 +01:00
InvalidUsernameException
ecce9f5b68 LibWeb: Add method to set highlighted nodes via internals-object
Upcoming commits will fix some regressions related to the highlighting
of nodes from within the inspector. Let's add the necessary
infrastructure so that we can write regression tests and avoid breaking
things again.
2026-02-01 14:29:59 +01:00
Aliaksandr Kalenik
1f6ee6c63a LibWeb: Add internals.dumpStackingContextTree() for testing
This new API allows tests to inspect the stacking context tree structure
which is useful for verifying that stacking context invalidation and
rebuilding work correctly.
2026-01-28 18:05:41 +01:00
Jelle Raaijmakers
746362e5de LibWeb: Handle WebDriver special keys and Mod_Keypad in keyboard events
Add webdriver_key_to_key_code() in Internals.cpp to properly translate
WebDriver special key codes (0xE000-0xE05D) to KeyCode values with
appropriate modifiers. This ensures keys like Enter, Backspace, and
arrow keys are handled correctly when sent via Internals::send_text().

In EventHandler::handle_keydown(), strip Mod_Keypad when determining
Enter key behavior since it only indicates key location (numpad vs
standard keyboard), not a behavior change. The modifier is still passed
through to KeyboardEvent for the location property.

This gains us 656 WPT subtest passes in `editing`.
2026-01-23 14:21:35 +01:00
Sam Atkins
5510867d58 LibWeb: Automatically disconnect all testing gamepads on test completion
Connected gamepads are stored on the Navigator, which means that when we
navigate to a new test, they stick around, including the virtual ones
we create for testing purposes. This caused issues such as the one
fixed in #7329 where failing to manually disconnect a virtual gamepad
meant that it would interfere with subsequent tests.

We don't want to rely on tests cleaning things up correctly, so this
commit introduces an automatic clean-up of these virtual gamepads.
InternalGamepad objects are now tracked in Internals, and all
disconnected in `Internals::perform_per_test_cleanup()`. It's named
generically as I'm expecting other systems (eg, localStorage or
cookies) to want similar cleanup between tests, but implementing those
is left as an exercise for a future person.

This method is called from signal_test_is_done(). This method is always
called in the case where the test completes. A test that crashes means
the WebContent isn't reused. A test that times out should also not have
its WebContent process reused, though it's not clear to me what we
currently do there.

Verified by commenting-out the `gamepad.disconnect()` line from the
Text/input/GamepadAPI/gamepad-is-available-in-new-navigables.html test
and then running:

```bash
for run in {1..20}; do
  Meta/ladybird.py run test-web -s -j1 -f GamepadAPI;
done
```
2026-01-20 06:58:16 -05:00
Jelle Raaijmakers
bf77aeb3dc Tests/LibWeb: Support WPT variant meta tags in test-web
Add support for WPT test variants, which allow a single test file to be
run multiple times with different URL query parameters. Tests declare
variants using `<meta name="variant" content="?param=value">` tags.

When test-web encounters a test with variants, it expands that test into
multiple runs, each with its own expectation file using the naming
convention `testname@variant.txt` (e.g., `test@run_type=uri.txt`).

Implementation details:
- WebContent observes variant meta tags and communicates them to the
  test runner via a new `did_receive_test_variant_metadata` IPC call
- test-web dynamically expands tests with variants during execution,
  waking idle views after each test completion to pick up new work
- Use index-based test tracking to avoid dangling references when the
  test vector grows during variant expansion
- Introduce TestRunContext to group test run state, and store a static
  pointer to it for signal handler access

This enables proper testing of WPT tests that use variants, such as the
html5lib parsing tests (which test uri, write, and write_single modes)
and the editing/bold tests (which split across multiple ranges).
2026-01-16 16:44:13 +00:00
Timothy Flynn
96806a3f90 LibWeb: Update cursor and tooltip UI state regardless of event handlers
If a script on the page cancels a mousemove event, we would return early
and neglect to update the cursor. This is seen regularly on "Diablo Web"
where they set the cursor to "none" on the main canvas, and also cancel
mousemove events.
2025-12-03 12:23:56 +01:00
Timothy Flynn
adcf5462af LibWeb+WebContent: Rename the http-cache flag to http-memory-cache
Rather than having http-cache and http-disk-cache, let's rename the
former to http-memory-cache to be extra clear what we are talking about.
2025-12-02 12:19:42 +01:00
Timothy Flynn
813986237e LibWeb: Add some tests that exercise the HTTP disk cache
Our HTTP disk cache is currently manually tested against various sites.
This patch adds some tests to cover various scenarios, including non-
cacheable responses, expired responses, and revalidation.

In order to ensure we hit the disk cache in RequestServer, we must
disable the in-memory cache in WebContent.
2025-11-20 09:33:49 +01:00
Tim Ledbetter
74940726d0 LibWeb: Add an Internals method to dump the GC graph 2025-11-04 10:34:43 +01:00
Luke Wilde
eeb5446c1b LibWeb: Avoid including Navigable.h in headers
This greatly reduces how much is recompiled when changing Navigable.h,
from >1000 to 82.
2025-10-20 10:16:55 +01:00
Dowsley
c6b289c3dc LibWeb: Derive implicit aria-level for h1–h6 from tag name 2025-10-10 17:18:02 +01:00
Aliaksandr Kalenik
0fbadba2e1 LibWeb: Add a test to ensure "resize" event on VV is fired on pinch-zoom 2025-10-10 15:37:45 +02:00
Timothy Flynn
efa9311527 LibWeb+WebContent+UI: Port text pasting to UTF-16 2025-09-19 06:38:52 -04:00
Luke Wilde
9adf27f009 LibWeb: Add tests for Gamepad API by utilising virtual SDL3 joysticks 2025-09-01 21:10:47 +02:00
Andreas Kling
1402c143a9 LibWeb: Add Internals.getShadowRoot(element)
This lets you access closed shadow roots from JavaScript, even though
they're not normally accessible to JavaScript. This can be used to poke
into UA shadow roots in tests.
2025-08-07 22:15:36 +02:00
Timothy Flynn
0efa98a57a LibJS+LibWeb+WebContent: Port JS::PropertyKey to UTF-16
This has quite a lot of fall out. But the majority of it is just type or
UDL substitution, where the changes just fall through to other function
calls.

By changing property key storage to UTF-16, the main affected areas are:
* NativeFunction names must now be UTF-16
* Bytecode identifiers must now be UTF-16
* Module/binding names must now be UTF-16
2025-08-05 07:07:15 -04:00
Luke Wilde
a2f3a5a6ce LibWeb: Send a beforeinput event for pasting
This allows us to paste text into Discord.
2025-07-23 22:04:45 +02:00
Jelle Raaijmakers
0d856a8fa7 Tests: Implement fuzzy screenshot comparisons in test-web
We now read WPT's `<meta name="fuzzy">` tags if present in tests and
apply them while comparing screenshots.
2025-07-17 12:59:11 +01:00
Jelle Raaijmakers
e4b2253b63 Tests: Replace load-reference-page debug action with internals method
WPT reference tests can add metadata to tests to instruct the test
runner how to interpret the results. Because of this, it is not enough
to have an action that starts loading the (mis)match reference: we need
the test runner to receive the metadata so it can act accordingly.

This sets our test runner up for potentially supporting multiple
(mis)match references, and fuzzy rendering matches - the latter will be
implemented in the following commit.
2025-07-17 12:59:11 +01:00
Aliaksandr Kalenik
8ae7417445 LibWeb: Add internals call to dump display list
It's useful to have tests that dump display list items, so we can more
easily see how changes to the display list recording process affect the
output. Even the small sample test added in this commit shows that we
currently record an unnecessary AddClipRect item for empty paint phases.

For now, the dump doesn't include every single property of an item, but
we can shape it to include more useful information as we iterate on it.
2025-07-13 19:15:05 +02:00
Timothy Flynn
3171d57639 LibWeb: Restore flags to prevent formatting timestamps as local time
The flag to stringify these timestamps as UTC was errantly dropped in
6fb2be96bf. This was causing test-web to
fail in time zones other than GMT+0.
2025-06-25 23:41:04 +02:00