Commit graph

560 commits

Author SHA1 Message Date
Shannon Booth
45a87b1dbe LibWeb: Update document URL to the entry document URL in document.open()
When document.open() is called from another window, the opened
document's URL should be updated to match the calling document's URL.
2026-03-23 09:00:39 +01:00
Shannon Booth
84b41f6270 LibWeb: Use entry realm in Document.open
While they are aiming to remove entry realm in the HTML spec,
apparently it is still needed for Document.open for web
compatability at this stage.
2026-03-23 09:00:39 +01:00
Andreas Kling
522683cf91 LibWeb: Use binary search for IntersectionObserver threshold index 2026-03-22 14:09:22 -05:00
Andreas Kling
e2d5d72f55 LibWeb: Reduce allocations in IntersectionObserver hot path
Pre-compute per-observer values (root node, root paintable, root
bounds, is_implicit_root, root_is_element) once before the per-target
loop instead of recomputing them for each target.

Add intersection_root_node() which returns GC::Ref<DOM::Node> without
creating GC::Root wrappers. Previously, intersection_root() was called
multiple times per target (in the skip condition check, in
compute_intersection, and again in root_intersection_rectangle inside
compute_intersection), each call creating GC::Root objects that require
heap allocation via new RootImpl.

Pass the pre-computed root bounds and root paintable into
compute_intersection instead of having it call
root_intersection_rectangle() and intersection_root() internally.

For an observer watching N targets, this eliminates roughly 4N heap
allocations per frame.
2026-03-22 14:09:22 -05:00
Andreas Kling
9abb7e4517 LibWeb: Apply scroll margin in IntersectionObserver compute_intersection
Per the spec, the observer's [[scrollMargin]] should be applied to
each scroll container's scrollport when walking the containing block
chain. This expands the effective clip rect, allowing targets to be
detected as intersecting before they actually enter the visible area
of the scroll container.

Add a scroll_margin_values() accessor to IntersectionObserver so the
raw LengthPercentage values can be used during intersection computation.
The scroll margin is applied by inflating the scroll container's
padding box rect before clipping the intersection rect against it.
2026-03-22 14:09:22 -05:00
Andreas Kling
229eba9a06 LibWeb: Clip against scroll containers in IntersectionObserver
Implement the containing block chain traversal in compute_intersection
(steps 2-3 of the spec algorithm). Walk from the target's paintable box
up through the containing block chain, clipping the intersection rect
against each ancestor that has a content clip (overflow != visible).

This fixes the case where a target is inside an overflow:hidden or
overflow:clip container and pushed below the visible area. Previously,
the intersection ratio was incorrectly non-zero because we only
intersected with the root bounds (viewport), not intermediate
scroll containers.

Also update the scroll container clipping tests to verify the
intersection ratio (which is what compute_intersection affects)
rather than isIntersecting (which per spec is based on targetRect
vs rootBounds, not the clipped intersection rect).
2026-03-22 14:09:22 -05:00
Andreas Kling
bbadb38881 LibWeb: Fix IntersectionObserver skip condition for explicit roots
The boolean logic in steps 2-3 of "run the update intersection
observations steps" was incorrect. The code used || between the two
negated skip conditions instead of &&, and the first sub-expression
had its negation inverted.

This caused intersection geometry to be computed (and callbacks fired)
for targets that are not descendants of an explicit element root,
as long as they spatially overlapped the root's bounding rect.

The spec says:
- Step 2: Skip if root is not implicit AND target not in same document.
- Step 3: Skip if root is Element AND target not descendant of root.

The enter condition is NOT(skip2) AND NOT(skip3), but we had
NOT(skip2) OR NOT(skip3) with a wrong negation in the first part.
2026-03-22 14:09:22 -05:00
Andreas Kling
194f17928f LibWeb: Cache parsed selectors for querySelectorAll/querySelector
Add a per-Document cache that maps selector strings to their parsed
SelectorList results. This avoids re-tokenizing and re-parsing the
same selector strings on every call, which is a huge win for pages
that repeatedly call querySelectorAll with the same selectors.

On microsoft.com, where they spam querySelectorAll with the same two
selectors, profiling showed 62% of main thread time was spent
re-parsing selectors. This cache eliminates that entirely for
repeated queries.
2026-03-20 18:32:32 -05:00
Jelle Raaijmakers
c8baa6e179 LibWeb: Remove tasks for destroyed documents instead of running them
Previously, destroyed-document tasks were forced to be runnable to
prevent them from leaking in the task queue. Instead, discard them
during task selection so their callbacks never run with stale state.

This used to cause issues with a couple of `spin_until()`s in the past,
but since we've removed some of them that had to do with the document
lifecycle, let's see if we can stick closer to the spec now.
2026-03-19 15:24:46 -05:00
Callum Law
915fc4602b LibWeb: Implement CSS inherit() function
The remaining failing imported tests are due to wider issues which are
covered by FIXMEs (both existing and added in this commit)
2026-03-19 10:25:37 +01:00
Zaggy1024
2e54c18fb3 LibWeb: Use a queue to process fullscreen request completions
Instead of immediately firing fullscreenchange, defer that until
WebContent's client has confirmed that it is in fullscreen for the
content. The fullscreenchange is fired by the viewport change, so in
cases where the fullscreen transition is instantaneous (i.e. the
fullscreen state is entered at the exact moment the viewport expands),
the resize event should precede the fullscreenchange event, as the spec
requires.

This fixes the WPT element-request-fullscreen-timing.html test, which
was previously succeeding by accident because we were immediately
fullscreenchange upon requestFullscreen() being called, instead of
following spec and doing the viewport (window) resize in parallel. The
WPT test was actually initially intended to assert that the
fullscreenchange event follows the resize event, but the WPT runner
didn't actually have a different resolution for normal vs fullscreen
viewports, so the resize event doesn't actually fire in their setup. In
our headless mode, the default viewport is 800x600, and the fullscreen
viewport is 1920x1080, so we do fire a resize event when entering
fullscreen. Therefore, that imported test is reverted to assert that
the resize precedes the fullscreenchange.
2026-03-17 18:58:37 -05:00
Zaggy1024
63820376ef LibWeb: Rename the doc variable in exit_fullscreen() to match spec 2026-03-17 18:58:37 -05:00
Zaggy1024
a1c9fbacc6 LibWeb: Only fire resize at Window if the viewport size changed
...and similarly, only fire resize at VisualViewport if its state has
changed.

Before, we would fire both if either changed.
2026-03-17 18:58:37 -05:00
Zaggy1024
7a45b50e8d LibWeb: Record the last viewport state in Document::make_active()
This ensures that we fire a resize event if the viewport size changes
between the document becoming active and the first rendering
opportunity.
2026-03-17 18:58:37 -05:00
Zaggy1024
44ed698d4f LibWeb: Separate the active element and the element being activated
We were conflating elements being the active element and elements being
activated. The :active pseudo class is supposed to be based on whether
an element will have its activation behavior run upon a button being
released.

Store whether an element is being activated as a flag that is set/reset
by EventHandler.

Doing this allows label elements to visually activate their control
without doing a weird paintable hack, so the Labelable classes have
been yeeted.
2026-03-17 04:01:29 -05:00
Jelle Raaijmakers
a095e4cd58 LibWeb: Remove ::spin_until() from unloading a doc with descendants
Rename DocumentDestructionState into a more generic
DocumentLifecycleState so it can be reused for the document unloading
logic. Instead of using ::spin_until(), use a callback mechanism to run
code once all child navigables are unloaded.
2026-03-15 09:03:20 -04:00
Jelle Raaijmakers
0a81470bff LibWeb: Rewrite unload/destroy document lifecycle to follow the spec
1. unload_a_document_and_its_descendants() now follows the spec
   algorithm structure: recursively unload child navigables via queued
   tasks (step 4), wait for them (step 5), then queue this document's
   own unload as a separate task (step 6). Previously, this was
   flattened into a single spin that unloaded all descendants and the
   document together, followed by a non-spec call to
   destroy_a_document_and_its_descendants().

2. unload() step 19 now calls destroy() when the document is not
   salvageable, as the spec requires. Previously this was a no-op with a
   comment deferring to unload_a_document_and_its_descendants().

3. destroy_top_level_traversable() step 2 now calls
   destroy_a_document_and_its_descendants() instead of destroy().

The iframe-unloading-order test, which exercises named iframe access
during unload handlers (the scenario the previous logic was designed to
protect), still passes.

Fixes #7825
2026-03-15 09:03:20 -04:00
Jelle Raaijmakers
f2a199d321 LibWeb: Reformat and add spec steps
No functional changes.
2026-03-15 09:03:20 -04:00
Aliaksandr Kalenik
9d2ebe90ed LibWeb: Store visual context nodes in arena-based tree
Replace per-node heap-allocated AtomicRefCounted
AccumulatedVisualContext objects with a single contiguous Vector inside
AccumulatedVisualContextTree. All nodes for a frame are now stored in
one allocation, using type-safe VisualContextIndex instead of RefPtr
pointers.

This reduces allocation churn, improves cache locality, and opens the
door for future snapshotting of visual context state — similar to how
scroll offsets are snapshotted today.
2026-03-11 11:16:36 +01:00
Jelle Raaijmakers
f61528238e LibWeb: Replace is<T> + as<T> with as_if<T>
Doing so results in a single fast_if<T> or dynamic_cast<T> call instead
of two. No functional changes.
2026-03-10 15:17:51 +01:00
Callum Law
c47f226225 LibWeb: Support CSS if() function
We don't yet support style queries
2026-03-09 14:36:18 +00:00
Callum Law
33c0e55762 LibWeb: Check navigable container for whether element needs style update
The style of an element depends on it's navigable's viewport size which
in turn depends on the navigable's container's style - so if requires a
style update then so does the original element.
2026-03-09 14:36:18 +00:00
Callum Law
8a6d902d4c LibWeb: Rename for_each_counter_style_at_rule
This only iterates effective rules which should be reflected in the name
2026-03-07 12:37:10 +01:00
Aliaksandr Kalenik
8f7bb7dd2e LibWeb: Skip layout update for disconnected elements querying metrics
Introduce Document::update_layout_if_needed_for_node() which only calls
update_layout() when the node is connected. Use it at all call sites
that query layout metrics (offsets, client dimensions, image size, SVG
bounding box, etc.) so disconnected elements no longer trigger an
unnecessary layout.
2026-03-05 14:17:20 +01:00
Aliaksandr Kalenik
d49809cba3 LibWeb: Remove paint-only properties resolution phase
With per-paintable display list command caching now in place, the
separate paint-only properties resolution phase is no longer needed.
Resolution now happens inline during painting and its cost is amortized
since it only runs on cache miss.

Move all property resolution to point of consumption:
- is_visible() and visible_for_hit_testing() compute on the fly
- Filter resolution moved to assign_accumulated_visual_contexts()
- Border radii, outlines computed on access
- Box shadows, backdrop filter resolved inline during painting
- Background resolution moved into paint_background()
- Mask resolution moved to StackingContext::paint()
- Text fragment and SVG stroke properties resolved during painting
2026-03-04 19:35:45 +01:00
Aliaksandr Kalenik
5bfc4a3c41 LibWeb: Cache display list commands per paintable
Cache the display list commands produced by each PaintableBox's paint()
on a per-phase basis. On subsequent display list rebuilds, if a
paintable's cache is still valid, replay the recorded commands directly
— skipping paint() and all the property resolution it entails.

Besides saving time on property resolution, this also enables Skia to
reuse path tessellation results across frames — e.g. border paths are
preserved in the cache and don't need to be re-tessellated on every
repaint.
2026-03-04 19:35:45 +01:00
Aliaksandr Kalenik
eae94a8a46 LibWeb: Route repaint requests through paintables, not Document
Rename Document::set_needs_display() to set_needs_repaint() and make it
private. External callers must now go through Node/Paintable which
route the request to the document internally.

Fix one existing misuse in AnimationEffect that was calling
document-level set_needs_display() instead of routing through the
target element's paintable.

This is preparation for per-paintable display list command caching:
repaint requests must go through specific paintables so their cached
command lists can be invalidated.
2026-03-04 19:35:45 +01:00
Timothy Flynn
2282636f98 LibWeb: Clean up the element fullscreen APIs a bit
* Don't publicly expose methods that are only used internally.

* Modernize comment style (wrap at 120 chars, use NB for our notes).

* Change the stringifier for RequestFullscreenError to return a UTF-16
  string. We were allocating a String, then transcoding it to UTF-16.

* Give helper methods clearer names, e.g.:

  fullscreen_has_error_check -> is_element_allowed_to_enter_fullscreen
2026-03-01 15:41:43 -06:00
Aliaksandr Kalenik
edf1ca8f19 LibWeb: Restrict throwaway layout states to their subtree
Throwaway LayoutState instances used for intrinsic sizing should not
access nodes outside the laid-out subtree. Make this explicit by setting
a subtree root on each throwaway state, pre-populating only the
immediate containing block, and using try_get() for any ancestor
lookups — treating unavailable ancestors as indefinite rather than
silently populating them with incorrectly-resolved values.
2026-02-27 19:11:41 +01:00
Callum Law
d0eabada0b LibWeb: Support extended Korean counter styles 2026-02-27 12:10:44 +00:00
Callum Law
1877c20c7b LibWeb: Support extended Japanese counter styles 2026-02-27 12:10:44 +00:00
Callum Law
08a5ed7ec6 LibWeb: Support Chinese counter styles
We implement the extended version of this algorithm but don't take full
advantage of it since we are limited to an i32 for our counter values.
2026-02-27 12:10:44 +00:00
Callum Law
120cabd35c LibWeb: Support ethiopic-numeric counter style 2026-02-27 12:10:44 +00:00
Andreas Kling
a146225331 LibWeb: Use unsafe layout/paintable accessors where appropriate
Add unsafe_layout_node(), unsafe_paintable(), and unsafe_paintable_box()
accessors that skip layout-staleness verification. These are for use in
contexts where accessing layout/paintable data is legitimate despite
layout not being up to date: tree construction, style recalculation,
painting, animation interpolation, DOM mutation, and invalidation
propagation.

Also add wrapper APIs on Node to centralize common patterns:
- set_needs_display() wraps if (unsafe_paintable()) ...set_needs_display
- set_needs_paint_only_properties_update() wraps similar
- set_needs_layout_update() wraps if (unsafe_layout_node()) ...

And add Document::layout_is_up_to_date() which checks whether layout
tree update flags are all clear.
2026-02-26 21:09:08 +01:00
Andreas Kling
4a2d80b493 LibWeb: Update layout in dump_dom_tree_as_json()
Ensure layout is up to date before serializing the DOM tree as JSON for
the inspector, so that layout information is accurate.
2026-02-26 21:09:08 +01:00
Andreas Kling
787a83b50a LibWeb: Stop calling Window::scroll_by() during layout
Replace the Window::scroll_by(0, 0) call at the end of
Document::update_layout() with a dedicated
Navigable::clamp_viewport_scroll_offset() that directly clamps the
viewport scroll offset to valid bounds.

The old approach re-entered layout from within layout, since scroll_by()
would trigger another layout update. The new approach is called from the
event loop's rendering steps, after layout is complete.
2026-02-26 21:09:08 +01:00
Jelle Raaijmakers
90a211bf47 LibWeb: Use device-pixel coordinates in display list and AVC
Stop converting between CSS and device pixels as part of rendering - the
display list should be as simple as possible, so convert to DevicePixels
once when constructing the display list.
2026-02-26 07:43:00 +01:00
Aliaksandr Kalenik
9231b54d49 LibWeb: Pre-populate viewport in SVG subtree relayout
During SVG subtree relayout, position:fixed elements inside
<foreignObject> use the viewport as their containing block. Since the
viewport is outside the SVG subtree, it was not pre-populated in the
LayoutState, causing a VERIFY failure in ensure_used_values_for().

Fix this by unconditionally pre-populating the viewport node from its
paintable in relayout_svg_root().
2026-02-25 09:05:10 +01:00
Andreas Kling
73b0ecd89d LibWeb: Use GC::WeakHashSet for AnimationTimeline associated animations
Replace the unsafe HashTable<GC::Weak<Animation>> with
GC::WeakHashSet<Animation>, and update all callers to use reference
syntax instead of pointer syntax since the iterator now yields T&.
2026-02-24 22:35:03 +01:00
Sam Atkins
bca6f3e4b5 LibWeb/DOM: Always give Document a FontFaceSet
In practice, the event loop queries Document::fonts(), so we don't gain
anything by delaying the allocation, apart from making it unclear when
it happens.
2026-02-24 15:44:32 +00:00
Aliaksandr Kalenik
1fc4c69ad8 LibWeb: Expand PaintNestedDisplayList in internals.dumpDisplayList()
Previously, PaintNestedDisplayList was treated as an opaque command,
printing only its name and rect without showing the nested display
list's contents. This made it impossible to debug painting issues
involving SVG masks/clips, CSS background-clip: text, and iframe
content through display list dumps.

Refactor the command dump loop into a recursive lambda that expands
nested display lists inline with increased indentation.
2026-02-24 14:37:29 +01:00
Aliaksandr Kalenik
533228f8ad LibWeb: Invalidate stacking context tree after partial SVG relayout
relayout_svg_root() clears individual stacking contexts via
reset_for_relayout() but didn't call invalidate_stacking_context_tree().
The viewport's stacking context remained non-null, so
build_stacking_context_tree_if_needed() skipped the rebuild. This caused
foreignObject to lose its stacking context after relayout, breaking SVG
mask application.
2026-02-24 06:15:07 +01:00
Luke Wilde
0bdc5e52ba LibWeb: Allow document element to be in the top layer
This seems to be an old ad-hoc fix, removing it doesn't make the test
introduced with the commit crash.

Allows YouTube to enter fullscreen.
2026-02-23 18:44:26 +00:00
Simon Farre
4396e5b8d0 LibWeb: Unfullscreen when removing an element or unloading a document 2026-02-23 18:44:26 +00:00
Simon Farre
04d1e2bf3d LibWeb: Implement exitFullscreen algorithm
Exiting fullscreen from the UI will be added in future commits.
2026-02-23 18:44:26 +00:00
Simon Farre
bc17805b2b LibWeb: Implement requestFullscreen algorithm
The required functionality to exit fullscreen will be in a followup
commit.
2026-02-23 18:44:26 +00:00
Simon Farre
db076bab92 LibWeb: Add Fullscreen event handlers to Document and Element
This also adds a stub to the Permissions Policy checks.
2026-02-23 18:44:26 +00:00
Simon Farre
f5ac0ccf6c LibWeb: Add 'Run the fullscreen steps'
Future commits will populate the pending list with events.
2026-02-23 18:44:26 +00:00
Callum Law
2008c6be5a LibWeb: Add predefined @counter-styles to UA stylesheet
There are some predefined counter styles (such as the longhand east
asian ones) which are too complex to be defined here and will need to be
implemented ad-hoc, this remains as a FIXME for now.
2026-02-23 11:21:09 +00:00
Callum Law
73b07d25ac LibWeb: Resolve and register counter styles from @counter-style rules
The tricky bit of this is resolving cycles in extending rules and
ensuring that counter styles are registered in the required order for
extension (i.e. for any pair of extended/extending rules the extended
one should be registered first).
2026-02-23 11:21:09 +00:00
Callum Law
2cc3fbb017 LibWeb: Pass callback as lvalue ref in for_each_active_stylesheet
Taking the callback as an rvalue ref meant we couldn't use the same
callback more than once
2026-02-23 11:21:09 +00:00
Zaggy1024
83bc63ccb8 LibWeb: Traverse shadow roots when firing mouseenter/mouseleave 2026-02-23 07:27:31 +01:00
Aliaksandr Kalenik
eb210bb3af LibWeb: Replace OrderedHashMap with page-table Vector in LayoutState
Each NodeWithStyle is assigned a sequential layout index during the
pre-layout tree traversal. LayoutState stores UsedValues in a
PagedStore — a two-level page table indexed by layout_index that
gives O(1) lookup via two array accesses, with pages allocated
lazily on first write. UsedValues are stored directly in pages
(Optional<T>) rather than behind heap pointers, eliminating
per-entry malloc/free calls and improving cache locality.

This cuts ensure_used_values_for() from ~14% to ~7% in profiles
on https://www.nyan.cat/.
2026-02-23 01:13:35 +01:00
Andreas Kling
cd364ea375 LibWeb: Don't require layout to toggle cursor blink state
The cursor blink timer fires every 500ms and only needs to toggle
the blink state and mark the paintable as needing display. If the
paintable doesn't exist yet, we can simply skip the blink -- the
cursor will appear after the next natural rendering update.

This avoids a potentially expensive synchronous layout every 500ms
for what is a purely cosmetic operation.
2026-02-22 13:24:05 +01:00
Andreas Kling
ef6368924e LibWeb: Add Document::update_style_if_needed_for_element()
This is a targeted version of update_style() that only performs a
style update if the given element (or its ancestors) actually have
dirty style. Useful when we only need up-to-date computed properties
for a specific element.
2026-02-22 12:43:01 +01: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
7cc973fa77 LibWeb: Lazify ElementByIdMap resolution and cache first element
Eliminate O(n) tree-order sorted insert on every add() by simply
appending elements and deferring order resolution to lookup time. Cache
the first-in-tree-order element in get() so repeated getElementById()
calls avoid repeated subtree traversal.

Improves performance on YouTube where add() was hot in profiles while
scrolling through comments.
2026-02-21 13:56:00 +01:00
Andreas Kling
5fd608b7cd LibWeb: Cache editability flag on DOM nodes for O(1) lookups
Add a cached m_in_editable_subtree flag to Node, updated on tree
mutations and contenteditable/designMode changes.

This replaces the recursive parent walk in is_editable() and
is_editing_host() with an O(1) flag check. The flag is recomputed
in inserted(), moved_from(), and cleared in removed_from(). Subtree
walks recompute the flag when contenteditable attributes change or
design mode is toggled.

This was 4% of CPU time while playing a YouTube video.
2026-02-21 03:51:28 +01:00
Aliaksandr Kalenik
b3231ea2a0 LibWeb: Make foreignObject establish a containing block for abspos
Absolutely positioned elements inside SVG foreignObject were being
positioned relative to an ancestor containing block outside the SVG,
instead of relative to the foreignObject itself. Per a W3C resolution
and the behavior of other browsers, foreignObject should establish a
containing block for absolutely and fixed positioned elements.

With this fix, the `has_abspos_with_external_containing_block` check
in `set_needs_layout_update()` and the abspos preservation loop in
`relayout_svg_root()` become dead code — remove both and simplify the
ancestor loops. Rename related tests to reflect the new behavior.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/3241
2026-02-17 15:59:59 +01:00
Jelle Raaijmakers
fa04c8db83 LibWeb: Move needs_layout_tree_rebuild to just before we use it
No functional changes.
2026-02-17 10:24:00 +01:00
Tim Ledbetter
8d0afda9f7 LibWeb: Dispatch pointer boundary events when hovered node changes 2026-02-15 02:36:01 +00:00
Tim Ledbetter
e4f37293fc LibWeb: Dispatch mouseenter events from ancestors to descendents
This is the opposite order to mouseleave and pointerleave events.
2026-02-15 02:36:01 +00:00
Tim Ledbetter
ae0f5ef9ce LibUnicode+LibWeb: Add infrastructure for line segmentation using ICU
No behavior change This is needed for correct UAX#14 line breaking.
2026-02-14 16:23:18 -05:00
Jelle Raaijmakers
83913fef84 LibWeb: Queue animation phase events as part of updating animations
Move the dispatch_events_for_animation_if_necessary() calls into step 1
of update_animations_and_send_events(), where the spec note says
updating timelines involves "Queueing animation events for any such
animations." Previously, these calls ran after step 7 (event dispatch),
causing newly queued events to be deferred by an extra rendering update.

This meant that e.g. a CSS transition triggered during an earlier
rendering step would not have its transitionrun event fired until the
next frame, instead of the current one.
2026-02-13 22:44:17 +01:00
Jelle Raaijmakers
3576f4a53a LibWeb: Don't throw away new scroll events after processing the old ones
Dispatching scroll events could cause new scroll events to get lined up
and added to `m_pending_scroll_events`. The spec then asks us to empty
out that list, removing those newly added events.

Prevent doing that by emptying out the list before iterating over the
events. Fixes part of the WPT test `html/webappapis/scripting/event-
loops/new-scroll-event-dispatched-at-next-updating-rendering-time.html`.
2026-02-13 22:44:17 +01:00
Jelle Raaijmakers
14b118c8ea LibWeb: Fire right scroll event type in ::run_the_scroll_steps() 2026-02-13 22:44:17 +01:00
Jelle Raaijmakers
919cdb5143 LibWeb: Tie auto scrolling into the rendering loop
We were using a separately fired timer for auto scrolling ticks, but it
makes more sense to tie this into the rendering steps of which
`::run_the_scroll_steps()` is a part. Should fix the flaky
`Text/input/viewport-auto-scroll.html` test.

Fixes #7939.
2026-02-13 22:44:17 +01:00
Niccolo Antonelli Dziri
bed56c676d LibWeb: Use enum instead of bool for CanUseCrossOriginIsolatedAPIs
Change the parameters types of the functions `coarsen_time` and
`coarsened_shared_current_time` from `bool` to
`CanUseCrossOriginIsolatedAPIs` for more coherence with the surrounding
code.
2026-02-13 16:47:42 +00:00
Andreas Kling
fb11732526 LibWeb: Fix style inheritance for slotted elements
Two issues prevented slotted elements from correctly inheriting
styles from their assigned slot:

1. Element::element_to_inherit_style_from() was skipping the slot
   element and returning the shadow host instead. This meant slotted
   elements inherited from the host, completely ignoring any styles
   on the slot itself.

2. When a slot element's style changed during the style tree walk,
   its assigned (slotted) nodes were never marked for recomputation.
   The tree walk follows the DOM tree, but slotted elements are DOM
   children of the shadow host, not the slot, so they were missed.

Fix (1) by returning the slot directly as the inheritance parent.
Fix (2) by marking assigned nodes dirty in update_style_recursively
when a slot's style changes.
2026-02-13 10:22:30 +01:00
Andreas Kling
4a7ca32af0 LibWeb: Skip full document style update in getComputedStyle if possible
Before calling update_style() for a getComputedStyle property access,
we now check whether the target element actually needs a style update
by walking the flat tree ancestor chain. If neither the element nor any
of its ancestors have dirty style bits, and there are no document-level
reasons to recalculate style, we skip the update_style() call entirely.

We walk the flat tree (not the DOM tree) because style inheritance
follows slot assignment -- slotted elements inherit from their assigned
slot, not their DOM parent.

This avoids unnecessary work when scripts access computed style
properties on elements whose styles are already up-to-date, which is a
common pattern on the web.
2026-02-13 10:22:30 +01:00
Aliaksandr Kalenik
30e4779acb AK+LibWeb: Reduce recompilation impact of DOM/Node.h
Remove includes from Node.h that are only needed for forward
declarations (AccessibilityTreeNode.h, XMLSerializer.h,
JsonObjectSerializer.h). Extract StyleInvalidationReason and
FragmentSerializationMode enums into standalone lightweight
headers so downstream headers (CSSStyleSheet.h, CSSStyleProperties.h,
HTMLParser.h) can include just the enum they need instead of all of
Node.h. Replace Node.h with forward declarations in headers that only
use Node by pointer/reference.

This breaks the circular dependency between Node.h and
AccessibilityTreeNode.h, reducing AccessibilityTreeNode.h's
recompilation footprint from ~1399 to ~25 files.
2026-02-11 20:02:28 +01: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
Jelle Raaijmakers
dbd09454c4 LibWeb: Reset cursor blink cycle when focus changes
This makes sure the caret starts blinking immediately on programmatic
focus changes to <input>s and <textarea>s, for example.
2026-02-11 11:17:27 +01:00
Jelle Raaijmakers
a69df4d25b LibWeb: Disable caret blinking in test mode
If we want to test whether or not we're drawing the caret, we need to
prevent it from blinking or otherwise all tests we're going to write
that look at the display list will turn out to be flaky.
2026-02-11 11:17:27 +01:00
Aliaksandr Kalenik
40429292fe LibWeb: Forward-declare RequiredInvalidationAfterStyleChange in Element
Replace the direct #include of StyleInvalidation.h in Element.h with a
forward declaration in Forward.h. Element.h only uses the type in
function declarations, so the complete type is not needed.

This reduces the recompilation impact of modifying StyleInvalidation.h
from ~1380 files to ~4 files, since Element.h is transitively included
by nearly every HTML and SVG element header.
2026-02-11 06:52:11 +01:00
Aliaksandr Kalenik
eea9837438 LibWeb: Skip :has() invalidation in update_style when nothing is pending
Add a document-level boolean flag that tracks whether any :has()
invalidations have been scheduled. This avoids iterating over all
shadow roots just to check is_empty() on each style scope when no
:has() invalidations are pending, which is the common case during
scrolling on complex pages like Reddit.

Results in ~10% reduction of is_empty() calls in profiles when
scrolling on Reddit.
2026-02-11 00:28:42 +01:00
Aliaksandr Kalenik
38e53b5600 LibWeb: Templatize Document::for_each_shadow_root()
Replace AK::Function parameter with a template parameter so the
compiler can inline the lambda at each call site. This eliminates
type-erasure overhead (vtable indirection + ScopeGuard) that was
showing up in profiles during Reddit scrolling, where this function
is called repeatedly from update_style() for every shadow root on
every style update.
2026-02-11 00:28:42 +01:00
Aliaksandr Kalenik
ad76ce6d90 LibWeb: Extract layout tree update check in Document::update_layout()
No behavior change.
2026-02-10 22:14:33 +01:00
Andreas Kling
bacd946721 LibWeb: Bail out of image callbacks when document becomes inactive
HTMLImageElement's "update the image data" algorithm checks
is_fully_active() at the start, but its async continuations
(microtasks, element tasks, batching dispatcher callbacks) skip
this check. When an iframe is removed or navigated, these
callbacks fire on an inactive document, causing crashes.

Fix this with two changes:

1) Add is_fully_active() bail-out checks at all async callback
   entry points in HTMLImageElement. Each bail-out also clears
   the DocumentLoadEventDelayer to prevent blocking the parent
   document's load event forever.

2) Create the DocumentObserver eagerly in initialize() (like
   HTMLMediaElement) with a document_became_inactive callback
   that clears the load event delayer and stops the animation
   timer. Fire document_became_inactive from Document::destroy()
   in addition to did_stop_being_active_document_in_navigable(),
   since iframe removal takes a different path than navigation.
   A guard flag prevents duplicate firing.
2026-02-10 21:19:35 +01:00
Andreas Kling
6ca01e124d LibWeb: Skip destroyed navigables when unloading a document
When unload_a_document_and_its_descendants() iterates all_navigables()
to find descendant navigables, skip any that have been destroyed.

When an iframe is removed, destroy_the_child_navigable() marks its
navigable as destroyed synchronously, but removal from all_navigables()
happens later in an async callback from destroy_a_document_and_its_
descendants(). If we count these destroyed navigables as descendants and
queue unload tasks for them, Document::destroy() may run during the
subsequent spin_until and remove those tasks (since it clears all tasks
for its document). This causes number_unloaded to never reach
unloaded_documents_count, hanging the event loop permanently.

This was the root cause of intermittent hangs when running crash tests
in batch mode: the previous test's iframe cleanup would leave destroyed
navigables in all_navigables(), and the about:blank navigation between
tests would get stuck in the unload spin_until.
2026-02-10 21:19:35 +01:00
Timothy Flynn
d050206fc3 LibWeb: Remove cookie source parameter from document cookie APIs
The source of any document cookie must be non-HTTP.
2026-02-10 12:21:20 +01:00
Timothy Flynn
8d97389038 LibHTTP+Everywhere: Move the cookie implementation to LibHTTP
This will allow parsing cookies outside of LibWeb.

LibHTTP is basically becoming the home of HTTP WG specs.
2026-02-10 12:21:20 +01:00
Aliaksandr Kalenik
ed0ce5c17f LibWeb: Unify three layout tree traversals into one in update_layout()
Merge the three consecutive for_each_in_inclusive_subtree traversals
into a single preorder walk. All three operations only depend on
ancestor state which is satisfied before descendants are visited in
preorder traversal.
2026-02-09 19:00:04 +01:00
Aliaksandr Kalenik
abecc746d7 LibWeb: Implement partial SVG relayout
Previously, any SVG geometry attribute change would mark the entire
document layout tree as dirty, triggering a full layout pass even though
only the SVG subtree was affected. This made SVG geometry animations
unnecessarily expensive.

Fix this by stopping `needs_layout_update` propagation at the SVGSVGBox
boundary and tracking dirty SVG roots separately on the Document. When
`update_layout()` finds that only SVG roots need relayout (and the
document layout root is clean), it runs SVGFormattingContext on each
dirty SVG root in a fresh LayoutState and commits the results directly,
bypassing the full document layout pass entirely.

This results in a substantial performance improvement on pages with
animated SVGs, such as https://www.cloudflare.com/,
https://www.duolingo.com/, and our GC graph explorer page.
2026-02-09 03:02:49 +01:00
Timothy Flynn
4a8ef68b90 LibWeb: Protect against null navigables in lineage chain more thoroughly
This extends the null navigable check added in commit
b118c99c27 to include all ancestor and
descendant list lookups. Fixes a crash in the following WPT test:

/cookies/schemeful-same-site/schemeful-navigation.tentative.html
2026-02-08 14:51:25 -05:00
Timothy Flynn
5cff8db44c LibWeb: Invoke Document::navigable() fewer times in a row
This is not necessarily a cheap accessor.
2026-02-08 14:51:25 -05:00
Callum Law
226b1ee46b LibWeb: Support percentage units for Animations::TimeValue
Non functional change for now since we don't yet have any progress-based
timelines which support percentage based time values
2026-02-05 16:45:34 +01:00
Timothy Flynn
0482b6bb57 LibWeb+LibWebView+WebContent: Implement versioning for document cookies
This patch introduces a cookie cache in the WebContent process to reduce
blocking IPC calls when JS accesses document.cookie. The UI process now
maintains a cookie version counter per-domain in shared memory. When JS
reads document.cookie, we check whether we have a valid cached cookie by
comparing the current shared version to the last used version. If they
match, the cached cookie is returned without IPC.

This optimization is based on Chromium's shared versioning, in which it
was observed that 87% of document.cookie accesses were redundant. See:
https://blog.chromium.org/2024/06/introducing-shared-memory-versioning-to.html

Note that this cache only supports document.cookie, not HTTP Cookie
headers. HTTP cookies are attached to requests with varying URLs and
paths. The cookies that match the document URL might not match the
request URL, which we wouldn't know from WebContent. So attaching the
cached document cookie would be incorrect.

On https://twinings.co.uk, we see approximately 600 document.cookie
requests while the page loads. This patch reduces the time spent in
the document.cookie getter from ~45ms to 2-3ms.
2026-02-05 07:28:07 -05:00
Tim Ledbetter
c44b30f0f1 LibWeb: Exclude UA internal shadow root elements in elementFromPoint()
When `elementFromPoint()` or `elementsFromPoint()` returns an element
that is inside a UA internal shadow root, we now return the shadow host
for that element.
2026-02-02 20:17:03 +00:00
Adam Colvin
ef3991f1ed LibWeb: Implement Document.hasFocus() per specification
Previously, Document::has_focus() always returned true, which was
incorrect. This caused documents without a browsing context (such as
those created via document.implementation.createHTMLDocument()) to
incorrectly report that they had focus.

The implementation now follows the spec:
1. Return false if the document has no navigable
2. Return false if the top-level traversable doesn't have system focus
3. Walk the focus chain from the top-level document to verify this
   document is actually focused

This fixes a WPT test: "The hasFocus() method must return false if the
Document has no browsing context"
2026-01-31 15:13:54 +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
Andreas Kling
6b9797f480 LibWeb: Scope pseudo-class invalidation to common ancestor
When a pseudo-class state changed, we always walked the entire
document (or shadow root) tree to find affected elements, even
though only the subtree rooted at the old/new common ancestor
can be affected.

Narrow the tree walk to start from old_new_common_ancestor
instead of the root. To ensure ancestor-dependent selectors are
still correctly evaluated, we seed the style computer's ancestor
filter by walking up from the common ancestor to the root before
the invalidation walk.

This reduces the work from O(total elements) to
O(subtree elements) + O(tree depth), which is a large improvement
on pages where pseudo-class changes (hover, focus, active, target)
occur deep in the DOM.

This was extremely hot (10%+) when hovering mailboxes on GMail.
2026-01-27 10:58:47 +01:00
Andreas Kling
3b90eb1d49 LibWeb: Recompute child style when parent's display changes
When a parent element's display property changes (e.g., to flex or
grid), children may need to be blockified or un-blockified.
Previously, children only received a recompute_inherited_style() call
which doesn't run the blockification logic.

This patch adds a parent_display_changed flag to the recursive style
update that forces children to get a full style recompute when their
parent's display change triggers a layout tree rebuild.
2026-01-26 12:40:36 +01:00
Aliaksandr Kalenik
3e54291813 LibWeb: Move VisualViewport transform to AccumulatedVisualContext tree
Move the visual viewport (pinch-to-zoom) transform from a reserved slot
in DisplayList to the AccumulatedVisualContext tree as a root transform
node. Fixed position elements now correctly inherit from this context.

This requires rebuilding the context tree and display list on each zoom
change, but this overhead will be eliminated by future partial context
tree rebuilds.
2026-01-23 18:56:24 +01:00
Colleirose
bf7fd80140 LibCrypto+AK: Merge LibCrypto/SecureRandom into AK/Random
AK/Random is already the same as SecureRandom. See PR for more details.

ProcessPrng is used on Windows for compatibility w/ sandboxing measures
See e.g. https://crbug.com/40277768
2026-01-23 15:53:27 +01:00
Aliaksandr Kalenik
44bfb32d47 LibWeb: Move AccumulatedVisualContext update in paint properties update
Integrate the AccumulatedVisualContext tree update into
update_paint_and_hit_testing_properties_if_needed() to consolidate
paint tree preparation into a single function.
2026-01-22 10:43:20 +01:00
Luke Wilde
babfd70ca7 LibGC: Enforce that a Cell type must declare the allocator to use
This ensures that we are explicitly declaring the allocator to use when
allocating a cell(-inheriting) type, instead of silently falling back
to size-based allocation.

Since this is done in allocate_cell, this will only be detected for
types that are actively being allocated. However, since that means
they're _not_ being allocated, that means it's safe to not declare
an allocator to use for those. For example, the base TypedArray<T>,
which is never directly allocated and only the defined specializations
are ever allocated.
2026-01-20 12:00:11 +01:00
Jelle Raaijmakers
f2f8f3ae57 LibWeb: Propagate <body>'s image-rendering to root element
Whenever we propagated a <body>'s background image to the root element,
we ignored any `image-rendering` property present.
2026-01-19 12:05:08 +01:00
Adam Colvin
3a6d82245b LibWeb: Implement Screen.isExtended attribute
- Add WindowManagement to PolicyControlledFeature enum
- Add screen_count() virtual method to PageClient
- Store all screen rects in WebContent::PageClient, derive both
  screen_rect() and screen_count() from stored data
- Implement screen_count() overrides in SVGPageClient and PageHost
- Replace FIXME stub in Screen.cpp with spec-compliant implementation
2026-01-16 20:34:58 +01:00
Aliaksandr Kalenik
bb4e29be5c LibWeb: Remove ClipFrame
Remove the now-obsolete ClipFrame infrastructure:
- Delete ClipFrame.h and ClipFrame.cpp
- Remove assign_clip_frames() from ViewportPaintable
- Remove enclosing_clip_frame and own_clip_frame from PaintableBox
- Remove m_clip_state HashMap from ViewportPaintable

Clip handling is now fully managed through AccumulatedVisualContext
nodes with ClipData.
2026-01-15 19:50:53 +01:00