Commit graph

428 commits

Author SHA1 Message Date
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
Aliaksandr Kalenik
a87b5c722d LibWeb: Add AccumulatedVisualContext debugging infrastructure 2026-01-15 19:50:53 +01:00
Aliaksandr Kalenik
04c3ad669b LibWeb: Rebuild AccumulatedVisualContext on transform changes
Ensure AccumulatedVisualContext stays synchronized when CSS transform
properties change.

AccumulatedVisualContext copies transform and perspective matrices from
the paintable tree at assignment time. When CSS properties that affect
these matrices change (transform, rotate, scale, translate, perspective,
transform-origin, perspective-origin), we must rebuild the
AccumulatedVisualContext tree to reflect the new values.

This adds a rebuild_accumulated_visual_contexts flag to style
invalidation that triggers a full rebuild during the next paint.

Note: The current invalidation strategy is inefficient - it rebuilds
the entire tree even for single-element transform changes. This could
be improved by patching the AccumulatedVisualContext node in-place with
updated matrices, but only when the transform doesn't transition
from/to none (which would change the tree structure). This optimization
is left for future work.
2026-01-15 19:50:53 +01:00
Aliaksandr Kalenik
10833640b1 LibWeb: Introduce AccumulatedVisualContext
Introduce AccumulatedVisualContext, a tree structure that tracks the
cumulative visual state (scroll offsets, clip regions, transforms,
perspective) for each paintable box.

Motivation:

Before this change, visual state was fragmented across multiple
mechanisms:
- ClipFrame: Tracked clip rectangles, each storing its own
  enclosing_scroll_frame_id to handle scroll offset adjustments
- scroll_frame_id: Passed separately to each display list command
- PushStackingContext: Stored transform matrices directly in the command
- Every display list command implemented translate_by() (45 methods
  total) to allow scroll offset adjustment during playback

This fragmentation led to:
- Complex, error-prone coordinate transformation logic scattered
  throughout the codebase
- Commands being mutated during playback to apply scroll offsets
- Duplicate logic between painting and hit testing for coordinate
  transformations

Solution:

AccumulatedVisualContext builds a tree where each node represents a
single visual operation:
- ScrollData: A scroll frame with its ID
- ClipData: A clip rectangle with optional border radii
- TransformData: A 4x4 transform matrix with its origin
- PerspectiveData: A perspective projection matrix

Each PaintableBox stores a reference to its accumulated context node.
The tree structure naturally captures the parent-child relationships,
so traversing from any node to the root gives the complete chain of
visual transformations.

Benefits this enables (in subsequent commits):
- Display list commands become immutable - no more translate_by()
- Single RefPtr<AccumulatedVisualContext> replaces separate
  scroll_frame_id and ClipFrame on commands
- LCA-based tree traversal during playback for efficient save/restore
- transform_point_for_hit_test() provides coordinate transformation for
  hit testing using the same structure
2026-01-15 19:50:53 +01:00
Psychpsyo
fe2bc2bfe7 LibWeb: Improve scrollingElement handling
This change is currently entirely undetectable because of what the
added FIXME talks about. Currently, the HTML element's overflow is
always set to visible in both axes, so it getting set to "clip" in
the imported test ends up not mattering at all.
2026-01-13 11:47:13 +00:00
Jelle Raaijmakers
1bef8b259a LibWeb: Create selection range when enabling designMode
Previously, setting designMode to "on" would only modify the selection
range if one already existed. Since selections start empty, this meant
no range was created and no selectionchange event was fired.

Use Selection::collapse() instead, which creates a new range if needed
and properly triggers the selectionchange event.
2026-01-13 10:09:22 +01:00
Jonathan Gamble
8f1cb4cbb0 LibWeb: Implement resizing for eligible elements and update scrollbars
Add ElementResizeAction to Page (maybe there's a better place). It's
just a mousemove delegate that updates styles on the target element.

Add ChromeMetrics for zoom-invariant chrome like scrollbar thumb
thickness, resize gripper size, paddings, etc. It's not user-stylable
but separates basic concerns in a way that a visually gifted
designer unlike myself can adjust to taste.

These values are pre-divided by zoom factor so that PaintableBox can
continue using device_pixels_per_css_pixel calls as normal.

The adjusted metrics are computed on demand from Page multiple times
per paint cycle, which is not ideal but avoids lifetime management and
atomics. Maybe someone with more surety about the painting flow control
can improve this, but it won't be a huge win. If profiling shows
this slowing paints, then Ladybird is in good shape.

Update PaintableBox to draw the resize gripper and deconflict
the scrollbars. Set apropriate cursors for scrollbars and gripper in
mousemove. We override EventHandler's cursor handling because nothing
should ever come between a man and his resize gripper.

Chrome metrics use the CSSPixels class. This is good because it's
broadly compatible but bad because they're actually different units
when zoom is not 1.0. If that's a problem, we could make a new type
or just use double.
2026-01-12 11:00:14 +00:00
Jonathan Gamble
196be843a5 LibWeb: Add helpers for focus management of text editors 2026-01-09 18:09:09 +01:00
Jonathan Gamble
0120e3513f LibWeb: Dont scroll elements into view if focus is lost 2026-01-09 18:09:09 +01:00
Sam Atkins
960558f30a LibWeb: Register and unregister @property rules when added or removed
Previously, we registered `@property` rules during parsing, and treated
them the same as `CSS.registerProperty()` calls. This is not correct
for a couple of reasons: One, the spec wants us to distinguish between
those two sources of registered custom properties, with
`CSS.registerProperty()` calls taking precedence. Two, we never removed
the registered property when its `@property` was removed from the
document.

This commit deals with this by iterating active CSSPropertyRules to find
which ones currently apply, and storing those in a cache. This cache is
invalidated whenever the Document's style is invalidated, which happens
whenever a CSSRule is added or removed from the Document.

The attached test demonstrates this now working as it should.
2026-01-09 10:54:37 +00:00
Sam Atkins
02149a8032 LibWeb: Store registered custom properties as CustomPropertyRegistration
This brings us closer to the spec.
2026-01-09 10:54:37 +00:00
Sam Atkins
0a57e1e8ac LibWeb: Clarify some scrolling-related code
A lot of our scrolling code is quite old, and doesn't match the spec,
but does use some similar names. This is quite confusing. In particular
`perform_scroll_of_viewport()` is not the same as the spec algorithm.
That algorithm is actually almost implemented in
`scroll_viewport_by_delta()`.

To clarify things, this commit makes a few changes:
- Rename perform_scroll_of_viewport() to
  perform_scroll_of_viewport_scrolling_box(). This is a better match
  for how we use this method, even if it's not actually a match for the
  algorithm. (:yakbait:)
- Move `scroll_viewport_by_delta()`'s code into a new
  `perform_a_scroll_of_the_viewport()` method, and make it take a
  position like it should. `scroll_viewport_by_delta()` now calls it
  with a calculated position.

I've avoided reusing the original `perform_scroll_of_viewport()` name to
avoid accidents.
2026-01-08 14:50:09 +00:00
Aliaksandr Kalenik
5336c53171 LibWeb: Stop intrinsic size cache invalidation at abspos boundaries
When content changes inside a layout node, we now reset intrinsic size
caches only up to the nearest absolutely positioned ancestor, rather
than all the way to the document root.

This optimization is safe because absolutely positioned elements don't
contribute to their ancestors' intrinsic sizes - they are skipped in
min/max content width calculations.

The needs_layout_update flag still propagates to all ancestors so the
document knows layout is needed. Only the cache reset is bounded.
2026-01-05 23:00:06 +01:00
Aliaksandr Kalenik
3353ec9663 LibWeb: Add result caching for :has() pseudo-class matching
The `:has()` pseudo-class requires traversing descendants (or siblings)
to find matches.

With this change we cache results keyed by `(Selector*, Element*)`
pairs. The cache is stored in `StyleComputer` and cleared at the start
of each style computation pass in `Document::update_style()`.

When `:has()` uses a descendant combinator and we find a match, we also
cache that all ancestors between the matching descendant and the
anchor match. For example with `div:has(.target)`:

```html
<div id="A">  <!-- checking :has(.target) here -->
  <div id="B">
    <div id="C">
      <span class="target"/>
    </div>
  </div>
</div>
```

When we find `.target` while checking `div#A`, we also cache that
`div#B` and `div#C` match `:has(.target)` since they also contain
`.target`. Later when styling these elements, we get cache hits and skip
traversal.
2026-01-04 19:36:40 +01:00
Andreas Kling
418a243c04 LibWeb: Don't run tasks in documents that haven't been BC associated
Documents that have never been associated with a browsing context will
never become "fully active" so we shouldn't schedule tasks in them since
they'll never run.
2025-12-27 16:40:34 +01:00
Callum Law
1bad8e5a78 LibWeb: Update animations regardless of whether timeline time changed
There are times that we want to update an animation regardless of
whether it's timelines time has changed, for example if an animation
associated with a scroll timeline has a pending task we should run that
on the next update regardless of whether the user has scrolled
2025-12-23 14:54:22 +01:00
Callum Law
f235625670 LibWeb: Don't disassociate animations from timeline when target orphaned
An animation with an orphaned owning element should continue to be
ticked by the timeline.

Reverts c8b574e and instead avoids leaking animations by not visiting
`Animation`s from `AnimationTimeline`s.

Fixes a timeout in the imported test
2025-12-23 14:54:22 +01:00
Callum Law
d62e2e39a3 LibWeb: Avoid running microtasks too early when updating animations
There were a bunch of places that we created
`HTML::TemporaryExecutionContext`s when updating animations in order to
resolve various promises, this worked but as part of the destructor it
would perform a microtask checkpoint which would result in us executing
microtasks earlier than intended, this is solved by instead having a
single temporary execution context for the entire animation update
process which we then destruct at the intended time.
2025-12-23 14:54:22 +01:00
Sam Atkins
cb0c428b3a LibWeb/DOM: Use a single scroll queue for all events
Corresponds to:
36f05864a6
302490c80c
https://github.com/w3c/csswg-drafts/pull/13238
https://github.com/w3c/csswg-drafts/pull/13239

The latter two are my own corrections which haven't been merged yet.
2025-12-19 12:09:19 -06:00
Sam Atkins
e3c76d396f LibWeb/DOM: Prevent refreshing to a javascript URL
Corresponds to:
97e0693fb7

We do now pass the test added for this, but can't import it:
http://wpt.live/html/browsers/browsing-the-web/navigating-across-documents/refresh/javascript.window.html
2025-12-19 12:08:03 -06:00
Psychpsyo
5c67ea640a LibWeb: Propagate overflow to viewport more correctly 2025-12-15 09:47:25 +00:00
bingyuan.ng
6353118809 LibWeb: Update svg type for root element checks when set/get title 2025-12-14 23:22:38 +00:00
Callum Law
57b7d0bbe5 LibWeb: Expose AnimationPlaybackEvent time values as CSSNumberish 2025-12-12 10:49:18 +00:00
Callum Law
69f05bd45d LibWeb: Store animation time values in abstract type
In level 2 of the web animations spec, times are no longer always
measures in milliseconds, they can also be percents when dealing with
progress-based (i.e. scroll-based) timelines.

We don't actually support percent times yet but this change will make it
easier to implement when we do.
2025-12-12 10:49:18 +00:00
Callum Law
f9df1c4eea LibWeb: Implement distinct specified timing values for AnimationEffect
Web Animations Level 2 disallows setting some `AnimationEffect` timing
values (start delay, end delay, iteration duration) directly and instead
allows authors to set the specified values which are then normalized
into the actual used values taking into account the type of the
associated timeline (i.e. progress- vs time-based)
2025-12-12 10:49:18 +00:00
Callum Law
f58339de7f LibWeb: Split AnimationTimeline::set_current_time into two methods
This method did two things:
1) on the base class (`AnimationTimeline`) it was a setter for
   `m_current_time` and;
2) on the child classes (e.g. `DocumentTimeline`) it updated the
   timeline's current time given a document timestamp

It makes more sense for theses to be distinct methods
2025-12-12 10:49:18 +00:00
Callum Law
b89b453c70 LibWeb: Make update_animations_and_send_events parameter non-optional
We always have a value for this so there is no need to have it as
optional
2025-12-12 10:49:18 +00:00
Sam Atkins
80787d3a9c LibWeb/DOM: Stub out Document.exitPointerLock()
This lets us leave the main menu on classic.minecraft.net by tapping
Escape again.
2025-12-09 12:11:21 +01:00
Jelle Raaijmakers
aa1abe778a LibWeb: Misc. code improvements
More usage of `as_if<T>`, fewer unnecessary `const_cast`s, etc. No
functional changes.
2025-12-08 20:12:23 +01:00
Callum Law
6c236d04d8 LibWeb: Separate font computation logic from StyleComputer
Font computation and loading is distinct enough from style computation
that it makes more sense to have this in it's own class.

This will be useful later when we move the font loading process to
`ComputedProperties` in order to respect animated values.
2025-12-05 10:03:15 +00:00
Sam Atkins
ed7a86b8cc LibWeb/DOM: Set the pseudo_element on created Animation/TransitionEvents
Also import a couple of WPT tests that now pass.
2025-12-03 13:29:51 +01:00
Sam Atkins
d717dd64b3 LibWeb: Make Animation's owning element an AbstractElement
From the spec:
> The owning element of a transition refers to the element or
  pseudo-element to which the transition-property property was applied
  that generated the animation.

https://drafts.csswg.org/css-transitions-2/#owning-element

Previously we only stored the element.
2025-12-03 13:29:51 +01:00
Sam Atkins
5179c57ca0 LibWeb/DOM: Use Document::make_unsalvageable()
Noticed because of this spec change:
e2974bd884

I then went and updated the other places it should be used.
2025-12-01 11:07:16 +00:00