Commit graph

101 commits

Author SHA1 Message Date
Andreas Kling
b81269e78b Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.

Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-22 19:51:25 +02:00
Luke Wilde
8dc8835b64 LibWeb+LibWebView+WebContent: Allow muted media to autoplay by default
The autoplay setting was binary and its default blocked all media,
including muted video, leaving sites that rely on muted autoplay
visibly broken. Replace it with a tri-state user-agent autoplay
policy (allow audio and video, block audio, or block audio and video)
defaulting to allowing only inaudible media to autoplay.

This is enforced through the media element's "allowed to play" check,
so unmuting a muted autoplay or calling `play()` cannot slip audio
past the policy; audible playback is permitted once the document has
been activated by the user. The policy lives in a dedicated
AutoplaySettings consulted from HTMLMediaElement instead of the
Permissions Policy "allowed to use feature" check it was previously
conflated with.
2026-06-19 09:41:32 +02:00
Callum Law
8ebdaeab69 LibWeb: Transfer animation ownership to AnimatedBitmapDecodedImageData
Previously animation ownership was a messy split between
`AnimatedBitmapDecodedImageData` and the consumers (i.e.
`ImageStyleValueResource`, `HTMLImageElement`, and `SVGImageElement`)
with `AnimatedBitmapDecodedImageData` owning the frames and a current
frame index, and the consumers owning the rest of the state (e.g. loop
count, timers to drive the animation forward, their own current index).

This had a couple of main issues:
 - While `AnimatedDecodedImageData` partially synchronized animations by
   dropping unexpected advancement notifications, this didn't apply to
   other animation state which meant, for instance, that a later started
   consumer could drive the animation of an earlier one past the max
   loop count (albeit without invalidating the earlier consumer).
 - Multiple consumers didn't share frame timings, meaning animations
   could be up to a full frame out of sync visually.
 - Animations were paused depending on whether there were any consumers,
   this is different to the behavior in other browsers (where they
   continue regardless of whether there are any consumers).
 - It was an overgeneralization of how animations need to work - only
   `AnimatedBitmapDecodedImageData` works with an indexed frame model,
   with animated SVGs (although not yet implemented) relying on their
   internal event loop to be driven forward.

Given the above the new approach implemented in this commit is:
 - The API for `DecodedImageData` is animation system agnostic, only
   exposing `default_frame`, `current_frame`, and `restart_animation`
   methods not reliant on providing a specific frame index.
 - `AnimatedBitmapDecodedImageData` owns its own timer, loop count,
   etc. The animation starts when the first consumer registers and ends
   when the document is hidden or becomes inactive (or completes in the
   case of finite animations).
 - Consumers are invalidated by `AnimatedBitmapDecodedImageData` when
   required.

Tests have been added for:
 - Animations being paused when the document becomes inactive and
   restarted when it becomes active again.
 - Frame timings being synchronized across consumers.
 - Restarts triggered by `HTMLImageElement` applying to all consumers.
 - Processing ending once a non-infinite animation plays to completion.

The tests to ensure animations are cancelled when consumers are removed
(e.g. `animated-background-image-timer-stops-when-hidden.html`) have
been updated to assert the inverse since animation state is now per
resource not per consumer.
2026-06-18 10:44:25 +02:00
sideshowbarker
427a66d448 LibWeb: Add internals.loadURL() to start UI-process-style loads
This adds an internals.loadURL(url) that defers Page::load so it starts
outside the calling task and can land between session-history traversal-
queue steps — as a load requested by ConnectionFromClient::load_url in
the UI process can, but as a load started from script never does.

Use case: Some session-history races are reachable only when a load
request arrives from the UI process between event-loop pumps — while the
session history traversal queue is mid-drain. A load started from script
enters navigate() inside the calling task, and claims the ongoing
navigation up front — so it can never land in that window. And so,
without this function, we can’t write tests for those kinds of races.
2026-06-17 12:25:53 +02:00
sideshowbarker
85a4f2633c LibWeb: Add an Internals hook to interrupt a navigation with a traversal
clobberNextNavigationWithATraversal() arms a one-shot that, on the next
call to Navigable::begin_navigation, re-stamps the navigable’s ongoing
navigation with a synthetic session-history traversal during the unload
check, then clears it on a later turn — draining deferred navigations.

This lets a single-process test deterministically reproduce a race
between a cross-document navigation and a concurrent traversal, which
otherwise only surfaces under scheduling jitter in a multi-process run.
2026-06-16 11:43:09 +02:00
Andreas Kling
b06955277a LibWeb: Stabilize same-document history mirrors
Same-document navigations now commit synchronously in WebContent, while
the UI process mirror learns about them over asynchronous IPC. A stale
UI seed could be accepted back into a live non-initial document and
overwrite its latest entry, making queued traversals target unreachable
entries.

Share descriptor comparison helpers between LibWeb and LibWebView.
Reject stale top-level seeds against the active document latest entry,
and let the UI process adopt WebContent current snapshots when a seed is
rejected. Test-only session history dumps now first send WebContent
current state synchronously, so dumps observe the converged state.

Allow post-load UI seeds to carry UI-owned nested histories that the
freshly loaded top-level document has not reconstructed yet. Add unit
coverage for matching those seeds while still checking top-level state.
2026-06-16 00:00:38 +02:00
Andreas Kling
0aaae1ac76 Tests/LibWeb: Cover UI-owned session history
Teach test-web to expose the UI-process history dump. Add focused
navigation tests for same-document traversal, fallback traversal, and
cross-document browser back and forward behavior. The expectations
assert document state and the UI-owned history snapshot.
2026-06-14 17:38:44 +02:00
Andreas Kling
24f37c6732 LibWebView: Keep browser history in the UI process
Use the LibWebView history mirror to preserve traversable session
history across WebContent process swaps. WebContent reports snapshots to
the UI process, and new renderers can be seeded from the mirror.

Browser back and forward now resolve through the UI-owned used history
steps. WebContent still runs the spec traversal path when the current
renderer has enough matching state to do so.

Handle canceled and no-op UI navigations without leaving speculative
history entries or pending WebDriver waits behind. Preserve traversal
precheck state across synchronous IPC shutdown, and avoid overwriting a
restored target entry's persisted scroll state before the document has
adopted that entry.
2026-06-14 17:38:44 +02:00
Andreas Kling
49730156ae LibWeb: Avoid media rule reevaluation for matchMedia
Separate MediaQueryList change reporting from stylesheet media rule
invalidation. Creating matchMedia() objects evaluates their own baseline
state, but should not make the next style update walk all active
stylesheets when the media environment has not changed.

This avoids continuous stylesheet media query reevaluation during
YouTube video playback, where repeated matchMedia() creation can make
style flushes do unnecessary work.
2026-06-13 14:00:53 +02:00
Shannon Booth
790b9bd36a LibWebView: Add async cookie deletion internals
Add Internals.deleteAllCookies(), backed by an async WebContent to
browser request and ack pair. CookieJar can now clear transient and
persisted cookies. Note that we only delete all cookies associated
with the current URL so that tests are able to run in parallel with
one another without impacting shared cookie state.
2026-06-13 10:18:33 +02:00
Sam Atkins
c7015a8647 LibWeb: Remove unused test variant metadata API
Remove internals.loadTestVariants and the IPC forwarding that reported
variant metadata back to WebView. test-web now identifies WPT variants
during collection, so no loaded document needs to expose this test-only
hook.
2026-06-09 16:48:33 +02:00
sideshowbarker
a82c7939d9 LibWeb+UI/AppKit: Implement macOS IME support
This makes macOS IME input in web content work as expected.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/9712
2026-06-08 10:23:14 +09:00
Andreas Kling
d9d8c5ce89 LibWeb: Allow Option-generated text insertion
Track whether a keydown should perform text insertion separately from
the delivered code point. Native frontends and text-oriented test paths
can now mark events that came from text input, while shortcut-style key
events keep Alt-modified default insertion suppressed.

This lets macOS Option-generated text such as Option+A and Option+Space
insert into editable controls without making plain Alt shortcuts insert
their base character. Add coverage for Alt text, Ctrl+Alt text,
separator text, Ctrl-only shortcuts, and Alt shortcuts.
2026-06-03 13:51:32 +02:00
Sam Atkins
c4e8817246 LibWeb+Tests: Update layout before accessibility tree dumps
The accessibility tree builder consults layout nodes while applying
inclusion and exclusion rules. DevTools can request an accessibility
tree after style or layout has been dirtied, which made those
layout-node lookups trip the stale-layout verification.

Update layout before serializing the accessibility tree, matching the
DOM tree dump path. Add an internals regression test that dirties
layout before requesting an accessibility dump.
2026-06-01 08:28:45 +01:00
Aliaksandr Kalenik
d725c36129 LibWeb: Store cached display list commands without resources
Cached display list command sequences used to carry their own
DisplayListResourceStorage. That kept resource ID sets and referenced
fonts, images, video frames, and nested display lists alive on every
cached phase, even though the command bytes already contain enough
information to discover those references when they are needed.

This makes cached command sequences store only command bytes. Resource
references are collected transiently from those bytes when a cache entry
is installed or invalidated. The navigable's central display list
resource storage now keeps cache reference counts, so compositor pruning
retains resources used by live cached commands without duplicating
storage in each sequence.
2026-05-31 00:21:25 +01:00
Andreas Kling
7445cff5e8 LibWeb: Use retained data for hit testing
Build a hit-test display list while recording paint output. Use it as
source of truth for point hit testing instead of recursively walking the
paintable tree in reverse paint order.

The retained list records target paintables, visual context indices,
border radii, caret rects, and line metadata needed by hit testing. It
also keeps a spatial index so point queries inspect nearby items before
checking containment in paint order.

Refresh scroll state before hit testing so visual context transforms use
current scroll offsets. Add text tests for rounded hit regions and
selection across non-text content.
2026-05-30 13:50:48 +02:00
Luke Wilde
854d9c7da4 LibWeb+LibHTTP: Consult HSTS preload list before dynamic-store IPC
The preload list is static, immutable data compiled into LibHTTP.
ResourceLoader consults it in-process at the fetch layer before
falling back to the dynamic, per-profile store in the browser
process, so a preloaded host is upgraded without a synchronous IPC.
HSTSStore stays the dynamic store only; the preload list cannot be
unset by a max-age=0 response because it is consulted first, before
the store is ever queried.
2026-05-29 22:23:33 +02:00
Luke Wilde
f6e5af0cb5 Tests/LibWeb: Add HSTS tests with internals helpers
Add internals.setHSTSPolicy, internals.ingestHSTSHeader, and
internals.isKnownHSTSHost so tests can drive HSTS state without an
HTTPS server. Extract ResourceLoader::try_store_hsts_policy_for_url
so the ingest helper exercises the same code path the network layer
uses for Strict-Transport-Security response headers.
2026-05-29 22:23:33 +02:00
sideshowbarker
03f6f25b11 LibWeb: Skip user-select:none when extracting selected text
Problem: Select All (Ctrl+A or the context menu) followed by Ctrl+C
unexpectedly copies text from elements with user-select:none — breaking
compat with Chrome and Firefox, which both exclude user-select:none
content from the clipboard.

Cause: Navigable::selected_text() walks the selection range via
visible_text_in_range() and concatenates each text node’s data. The walk
filters out nodes without a layout, but not nodes whose used value of
user-select is ‘none’.

Fix: Add a user-select check at each visible_text_in_range() walk point.
The Selection range itself is left unchanged. Selection.toString() still
returns the full text per spec — but the clipboard-extraction path now
excludes user-select: none subtrees, per spec
2026-05-28 12:15:21 +01:00
Andreas Kling
6ca9214b4c LibWeb: Respect root color scheme for loading canvases
Only use the loading-time top-level canvas fallback when the root
color-scheme value is still normal. An explicit light scheme on the
root element already gives the canvas a light used scheme, so replacing
it with the preferred dark scheme makes the viewport disagree with the
computed system colors until readiness advances.

Add internals coverage for the document canvas scheme and a text test
that exercises the loading state with a dark preferred color scheme.
2026-05-27 20:57:38 +02:00
Aliaksandr Kalenik
b36e6c9b97 Compositor+LibWeb: Pass AVC trees separately from display lists
Display lists owned the accumulated visual context tree through a
ref-counted pointer. That tied visual-context state to display-list
lifetime and made compositor updates treat the two as one unit, even
though AVC trees need to become independently updateable compositor
state.

Make accumulated visual context trees plain versioned values, have each
display list store the compatible tree version, and pass the matching
tree alongside display-list updates and replay calls. Replay verifies
that the provided tree matches the display list before executing it.

This prepares the compositor for receiving AVC tree updates separately
from display-list updates: it now accepts the tree as a separate update
parameter, stores it next to the display list, and uses that stored tree
for replay and async-scroll hit testing. Nested display-list resources
carry their own tree snapshots for the same version check.
2026-05-27 18:29:42 +01:00
Aliaksandr Kalenik
9ab311507e LibWeb+UI: Synthesize ctrl-wheel events for pinches
Trackpad pinches applied directly as visual viewport zoom, so pages
could not observe the gesture or cancel the browser default. Canvas
apps such as maps expect the ctrl-wheel path instead.
2026-05-27 12:53:08 +02:00
Andreas Kling
212330205f LibWeb: Fix link drag cancellation state
Clear mousedown activation state whenever primary mousedown tracking is
reset. Also route local link drag cancellation through the drag-and-drop
end path when Escape, viewport leave, or mouseup outside the viewport
ends the drag.

These paths can otherwise leave drag input suppression active after the
user has ended or canceled the drag, or skip the required dragend event
for source cleanup.

Add an internals mouseLeave primitive and a focused text test. It covers
stale :active state, dragend dispatch for cancellations, viewport leave,
outside-viewport mouseup, Escape canceling, and later click delivery
after each canceled drag.
2026-05-27 09:52:15 +02:00
Andreas Kling
44a6da5165 LibWeb: Avoid cold rule cache builds for :has() checks
Cache lightweight selector insights on each stylesheet so cold style
scopes can answer whether :has() invalidation is relevant without
building a complete rule cache. This avoids forcing rule caches for
shadow roots whose active sheets do not contain :has() selectors.

Imported sheets contribute to their parent sheet's effective rules, so
an imported sheet load or CSSOM change also clears ancestor selector
insight caches.

Add test-only counters and regression coverage for cold shadow roots
with and without :has() selectors, plus delayed imported :has() rules.
2026-05-23 22:03:46 +02:00
Andreas Kling
f9ef74bb0a LibWeb: Stabilize style invalidation counter tests
Counter tests reset their counters before measured mutations, but setup
and cleanup could leave pending style invalidation work behind. A later
case could then pay for stale work in its measured counter window. That
made exact recomputation baselines depend on prior test order.

Add a test-only Internals hook that flushes pending style work without
forcing layout. Use it to make warm counter measurements start from a
clean style state. Cold paths keep using a plain counter reset so they
still exercise cold rule-cache behavior.

Rebaseline these counter tests now that they no longer include cleanup
work from previous cases.
2026-05-23 22:03:46 +02:00
Andreas Kling
17902b02ab LibWeb: Stop idle animated CSS image timers
Keep animated ImageStyleValue frame advancement owned by the
style value. The current frame and loop state live there, so a
separate document scheduler would duplicate ownership of that state.

Start the ImageStyleValue timer only while it has layout clients.
Stop it when the last client unregisters, or when a finite animation
completes. Expose a document-scoped active timer count through
internals for focused regression tests.

Clear image observers when layout nodes detach. Use current-node
cleanup for per-DOM-node clearing, and explicit subtree cleanup for
tree replacement, full tree teardown, and synthetic pseudo-elements.
This keeps large document clearing linear.

Unregister generated-content image providers during layout detach
instead of waiting for GC to finalize the provider.

Cover hidden animated background images, generated content images,
layout node replacement, full layout tree teardown, and document
scoping for the internals counter.
2026-05-23 11:36:53 +02:00
Andreas Kling
ddce686ed0 LibWeb: Propagate inherited style across slot boundaries
Make style updates reach a fixed point when slot invalidation dirties
assigned nodes after their traversal point. During inherited-style
cascades, only the topmost changed element scans for descendant slots.

Animation inherited-style updates now include the target slot and walk
shadow-including descendants, so host animations propagate inherited
values through shadow trees and assigned slottables. Animated inherited
longhands also carry the same inherited-style invalidation signal as
regular style changes.

Mark custom elements dirty when their :defined state flips, so upgraded
elements do not keep stale :not(:defined) computed style. Add coverage
for slotted menu invalidation, descendant-slot scan counts, target slot
animations, host shadow-tree animations, and explicit inherit from an
animated non-inherited longhand.
2026-05-22 09:38:59 +02:00
Andreas Kling
a289601eb0 LibWeb: Expose content blocker test controls
Add internals helpers so text tests can install local blocker rules and
toggle content blocking without browser-process IPC. Clear installed
rules after each non-crashed test-web case so blocker state cannot leak
into the next test.
2026-05-21 21:16:56 +02:00
Shannon Booth
74b76e21b9 LibCore: Keep ICU and libc time zones in sync
Add Core::TimeZone as the single entry point for changing the
current time zone. This updates both ICU and the process TZ/tzset
state so libc-backed helpers such as AK::UnixDateTime::to_string()
agree with JS/ICU time zone state. Previously only the ICU timezone
would be updated, which could result in inconsistent results being
returned.
2026-05-20 07:26:23 -04:00
Aliaksandr Kalenik
8906011a6b LibWeb: Synchronize display list resources via transactions
Display lists used to own the resource storage needed to replay their
command bytes. That kept the compositor tied to in-process object
ownership: sending a display list update also meant sharing the same
resource container with the recording side.

Move resource storage out of DisplayList and make display list updates
carry a transaction of resources to add and remove. Navigable now tracks
the resources referenced by the current display list, sends only the
delta to the compositor, and trims its recording-side storage to the
active set. The compositor applies those transactions to its own storage
before replacing the cached display list.

This still carries in-process resource objects, but it puts the
ownership boundary in the right place. Command bytes and resource
lifetime are now synchronized explicitly, which is the shape needed
before the compositor can receive serializable resource updates across a
process boundary.
2026-05-16 19:35:24 +02:00
Andreas Kling
a36f8aa36d LibWeb: Keep ResizeObserver targets weakly
Store ResizeObservation targets weakly, matching Blink and WebKit. A
ResizeObserver can be kept alive by the document while it has observed
targets, but the observation itself should not keep a removed target and
its subtree alive forever.

Prune dead observations before gathering active resize observations.
Snapshot the document observer list before pruning so unregistering an
observer cannot mutate the intrusive list being iterated. Keep gathered
active targets rooted while broadcasting callbacks, since an earlier
callback can remove a later target and trigger GC before delivery.

Expose an internals helper to force environment-bound test objects to be
treated as garbage. Add text coverage for both pruning a dead observer
during gather and GC during an earlier resize observer callback.
2026-05-16 18:48:52 +02:00
Aliaksandr Kalenik
f743263871 LibWeb: Let internals.wheel await async scroll adoption
Async scrolling tests used requestAnimationFrame() as a proxy for the
compositor thread to return pending scroll updates to the main thread.
That waited for a rendering opportunity, so tests could observe stale
DOM scroll offsets when the compositor update had not been adopted yet.

Make internals.wheel() return a promise that resolves after a tracked
async scroll operation has been applied by Navigable. Tracking is opt-in
from the internals test API, so regular page wheel input and compositor
IPC keep the boolean async-scroll path without allocating operation IDs.
Tracked test scrolls are the only operations that record completions.

Update async scrolling and wheel propagation tests to await the wheel
promise directly instead of relying on animation frame timing in tests.
2026-05-15 19:10:27 +02:00
Aliaksandr Kalenik
1687a955d3 LibWeb: Respect blocking wheel regions for async scrolling
Root-level blocking wheel listeners still need to disable async wheel
routing globally, but non-root listeners only block the regions where
their event path can cancel the wheel event. The previous routing
admission treated any blocking listener as global, which prevented
async scrolling in unaffected nested scrollports.

Base routing admission on viewport-covering blocking regions instead of
the presence of any blocking listener, leaving local blocker rejection
to the compositor hit test. Update internals so blocked positions report
no async wheel target. Update the admission test to cover the routing
distinction and add a nested scroller test where a non-passive wheel
listener calls preventDefault(), keeping both the scroller and the
viewport at their original offsets.
2026-05-14 19:41:32 +02:00
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