Commit graph

30 commits

Author SHA1 Message Date
Andreas Kling
1b8072371b LibWeb: Preserve compatible visual context tree versions
Let accumulated visual context updates keep the previous tree version
when rebuilt with the same shape. Display lists reference visual
context tree versions, so keep compositor-only updates on the old
version unless the tree structure changes.

Add coverage for version reuse and incompatible tree shapes.
2026-06-18 00:12:42 +02:00
Andreas Kling
4e047ae97b LibWeb: Keep pinch zoom out of client rects
Keep the visual viewport transform out of Element client rects and
IntersectionObserver geometry. Pinch zoom should change the visual
viewport, but not the layout viewport coordinates exposed through DOM
geometry APIs.

Thread an opt-out through rectangle mapping so paint and hit testing
still use the full visual transform while web-observable geometry can
stay in layout viewport coordinates. This matches the Blink and WebKit
page scale model and keeps responsive script from treating pinch zoom
like a relayout.

Add coverage for getBoundingClientRect() under pinch zoom and visual
viewport IntersectionObserver geometry.
2026-06-16 02:03:59 +02:00
Shannon Booth
96aa4b3ad3 LibWeb: Only apply CSS transforms to transformable elements
Add Node::is_transformable() per the CSS Transforms spec and gate
transform handling on it, so non-replaced inline boxes are no longer
transformed.
2026-06-14 22:12:00 +02:00
Jelle Raaijmakers
db04e1b819 LibWeb: Annotate compute_transform() with CSS Transforms 2 steps
Quote the transformation matrix computation steps from the spec
verbatim, which documents that we do not yet apply the offset property
(step 6).
2026-06-12 01:10:24 +02:00
Jelle Raaijmakers
b06cbbf81e LibGfx+LibWeb: Use matrix helpers in TransformationStyleValue::to_matrix
Using the new perspective_matrix() helper resolves a FIXME in
AccumulatedVisualContext as well.
2026-06-12 01:10:24 +02:00
Jelle Raaijmakers
7390818b5a LibWeb: Apply the z component of transform-origin
compute_transform() resolved transform-origin to a 2D point, so 3D
rotations pivoted around the element's own plane. Build the conjugation
T(0, 0, z) * M * T(0, 0, -z) into the matrix instead; this composes
with the 2D origin at paint time into the full 3D conjugation.
2026-06-11 22:13:24 +02:00
Aliaksandr Kalenik
221219e00c LibWeb: Track out-of-flow descendant visual contexts
Accumulated visual contexts only recorded the state used by normal
descendants. Fixed descendants started from the visual viewport, and
absolute descendants rebuilt their state from the containing block. That
could drop CSS clip and clip-path nodes from ancestors between the
positioned box and descendant, even though those clips still apply.

Carry separate transient AVC states for normal descendants,
absolute-position descendants, and fixed-position descendants while
building the AVC tree. The top-down paintable walk adds effects and CSS
clipping to positioned descendant contexts, and containing blocks switch
those contexts back to the normal context.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/9909
2026-06-05 23:27:21 +01:00
Aliaksandr Kalenik
5595efd46b LibWeb: Avoid display list rebuilds for VV changes
Visual viewport scroll and pinch zoom used to invalidate the full
accumulated visual context tree and display list, even though those
changes only modify the root visual viewport transform. That forced a
full display list rerecord before sending updated compositor state.

Patch the reserved visual viewport AVC node in place instead.
VisualViewport marks the tree for compositor update without
invalidating the display list, and Navigable sends the replacement tree
through the new IPC path before updating scroll state.
2026-06-03 02:12:39 +01:00
Aliaksandr Kalenik
78cda6a97e LibWeb: Reserve visual viewport AVC root
The accumulated visual context tree used index 0 as a null sentinel, so
visual viewport transforms were only represented by adding a normal
transform node when the transform was non-identity. That made callers
treat index 0 as a special no-context value and kept the tree shape
dependent on the visual viewport state.

Reserve index 0 as the visual viewport transform node instead. AVC
traversal, display-list replay, hit testing, debug dumping, and root
paint state now treat that node as a real root. Rebaseline the affected
display-list and async-scrolling text expectations so the explicit root
node appears in AVC dumps.
2026-06-03 02:12:39 +01:00
Aliaksandr Kalenik
a4f0cec346 LibWeb: Move visual context tree construction to AVC module
ViewportPaintable owned both the current visual context tree and the
details for constructing it, keeping CSS transform, clip, and effect
derivation mixed into the viewport paint lifecycle.

Move the construction helpers into AccumulatedVisualContext.cpp and
leave ViewportPaintable responsible for owning the resulting snapshot.
The visual viewport context index is now local builder state instead of
viewport object state.
2026-05-27 18:29:42 +01: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
f68d6d2636 LibWeb: Add IPC serialization for display-list resource transactions
The compositor IPC path needs to send display-list resources across a
process boundary without losing the resource IDs referenced by
display-list commands. The previous transaction shape carried font and
image-frame objects whose IDs are process-local and would be regenerated
after decoding.

Store explicit IDs with font and image-frame transaction entries and add
IPC serializers that rebuild the resource objects while preserving those
IDs. Fonts now serialize through Gfx::Typeface IPC, image frames use
shareable bitmaps, and video frames keep their existing ID-bearing
transaction entry. This lets the receiver apply the regular transaction
directly.

This is preparatory work required to add IPC between the main and
compositor threads.
2026-05-21 11:45:06 +01:00
Tim Ledbetter
8ff38d13e3 LibWeb: Add ScrollCompensation to the visual context system
Add a new visual context node type that negates a scroll frame's offset
during display list replay. This is the inverse of ScrollData: where
ScrollData translates by the scroll offset, ScrollCompensation
translates by the negated offset.
2026-05-08 13:09:40 +02:00
Jelle Raaijmakers
f5bb5d0797 LibWeb: Don't tear down an active effect saveLayer during culling check
Before applying an effect context (filter, opacity, blend-mode), the
display list player runs a culling check by tentatively switching to the
effect's parent first. When the effect was already on the stack (i.e.
the current context was a descendant of the target) that walk restored
the saveLayer prematurely. The filter was then applied to a partial
batch of commands, and subsequent commands opened a fresh saveLayer for
a second, independent application, producing visibly wrong compositing.

Fold the culling check into switch_to_context: while walking down to
apply contexts, check the bounding rect right before each EffectsData
node and abort if the command is fully clipped. Walks that only go up
never traverse an EffectsData node, so an already-applied effect is
never torn down.

This regressed in cd0705334b.
2026-04-28 16:14:44 +02:00
Aliaksandr Kalenik
3012c0fe72 LibWeb: Store scroll frames by value in contiguous storage
Replace per-frame heap-allocated RefCounted ScrollFrame objects with a
single contiguous Vector<ScrollFrame> inside ScrollState. All frames for
a viewport are now stored in one allocation, using type-safe
ScrollFrameIndex instead of RefPtr pointers.

This reduces allocation churn, improves cache locality, and moves
parent-chain traversal (cumulative offset, nearest scrolling ancestor)
into ScrollState — similar to how visual context nodes were recently
consolidated into AccumulatedVisualContextTree.
2026-03-12 12:06:40 +01: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
499ec26f97 LibWeb: Remove CSS pixel offsets from ScrollState
This was arguably put in a worse place by #8162; we mostly need the
device pixel offsets from the scroll state so keep track of those and
convert back to CSS pixels when necessary (i.e. scrollbar data).
2026-02-27 08:29:29 +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
Tim Ledbetter
73d948a028 LibWeb: Pre-allocate visual context chain vectors for hit testing
Also use inline storage for typical hit testing depths.
2026-02-23 17:30:25 +01:00
Aliaksandr Kalenik
55f4009163 LibWeb: Walk linked list directly in transform_rect_to_viewport()
The chain is already in element→root order, which matches the linked
list's natural traversal via parent pointers. No need to collect into
a Vector first.
2026-02-22 16:09:15 +01:00
Psychpsyo
6ea528f0ec LibWeb: Do not create a layer when CSS isolation is set to isolate
This is entirely unnecessary. All that this property does is create a
stacking context.
2026-02-13 11:02:32 +00:00
Aliaksandr Kalenik
24c08b7d42 LibWeb: Fix mouse event offset computation with nested CSS transforms
compute_mouse_event_offset() only inverted the target element's own CSS
transform, ignoring ancestor transforms. This caused incorrect offsetX/
offsetY values when elements were nested inside transformed parents.

Use AccumulatedVisualContext::inverse_transform_point() to invert the
full ancestor transform chain instead.
2026-01-31 16:56:05 +01:00
Aliaksandr Kalenik
8b411ff8c6 LibWeb: Account for border-radius in hit testing
This change adds border-radius awareness to hit testing in two places:
1. ClipData::contains() now uses BorderRadiiData::contains() to properly
   check if a point is inside a rounded clip rect. This handles overflow
   clips from ancestor elements that have border-radius.
2. PaintableBox::hit_test() now directly checks the element's own
   border-radius before reporting a hit.
2026-01-22 11:30:17 +01:00
Aliaksandr Kalenik
96a39aeaa6 LibWeb: Move effects application into AccumulatedVisualContext
Effects (opacity, blend mode, filters) must be applied in the parent's
coordinate space, before the element's transform. Previously this was
handled by manually switching to the parent's visual context when
applying effects at paint time.

By adding EffectsData to AccumulatedVisualContext and positioning it
before TransformData in the chain, effects are now naturally applied in
the correct order during display list replay, eliminating the special
case in StackingContext::paint().

For SVG filters that can generate content from empty elements (feFlood,
feImage, feTurbulence), a transparent FillRect command is emitted to
trigger the filter through the same AVC pipeline.
2026-01-21 16:19:18 +01:00
Jelle Raaijmakers
f966750d2c LibWeb: Use local coordinates for overflow clip hit testing 2026-01-21 12:57:22 +01:00
Aliaksandr Kalenik
23a74ddc2a LibWeb: Use AccumulatedVisualContext in Element::get_client_rects()
The previous implementation had a bug: it composed all ancestor
transforms but applied them around only the innermost element's
transform origin. The correct behavior is to apply each transform
around its own origin.

AccumulatedVisualContext already tracks all visual transformations
(transforms, scroll offsets, perspective) correctly for hit testing.
This change adds a new transform_rect_to_viewport() method that performs
the forward transformation (element coordinates to viewport
coordinates), which is the inverse direction of
transform_point_for_hit_test().

This fixes getBoundingClientRect() returning incorrect coordinates for
elements inside transformed ancestors with non-default
transform-origins.
2026-01-19 08:53:30 +01:00
Aliaksandr Kalenik
a468d06289 LibWeb: Include path string in AccumulatedVisualContext::dump()
This adds the actual SVG path data to the ClipPathData dump output,
making it easier to debug clip-path issues.
2026-01-19 04:01:37 +01:00
Aliaksandr Kalenik
98afd82491 LibWeb: Integrate clip-path into AccumulatedVisualContext
Previously, clip-path was applied only during painting in
StackingContext::paint(), which meant hit testing did not respect
clip-path boundaries. Clicks outside the visible clipped region but
inside the element's bounding box would incorrectly register as hits.

By moving clip-path into AccumulatedVisualContext, it becomes part of
the same system that handles transforms, clips, and scroll offsets for
both painting and hit testing, ensuring consistent behavior.
2026-01-16 13:39:02 +01:00
Aliaksandr Kalenik
a87b5c722d LibWeb: Add AccumulatedVisualContext debugging infrastructure 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