Commit Graph

181 Commits

Author SHA1 Message Date
Callum Law
cbc2bb7aa7 LibWeb: Don't pass layout node when resolving EdgeRect
We know that these lengths have already been absolutized at computed
value time
2026-03-30 14:05:10 +01:00
mikiubo
71c4bb1aa8 LibWeb: Support border-radius in clip-path: inset()
Implement support for the 'round' radii in 'clip-path: inset()'
by resolving and normalizing corner radii and generating a path
with elliptical arcs.

Add a screenshot test.
2026-03-21 02:44:49 +00:00
Zaggy1024
d94293cc25 LibWeb: Use an abstract class for scrollbar and resize handle input
This unifies the implementations of the element resize and the scroll
mouse inputs, so the actual code involved in handling the events can
be simplified, and it only should require one object per event.

The cursor override is now part of this ChromeWidget class as well, so
that the hit test's cursor doesn't need to be passed around as much.
2026-03-17 04:01:29 -05:00
Zaggy1024
9a39a8308b LibWeb: Prevent events on scrollbars when pointer-events is none
This prevents an assertion failure in EventHandler's mousemove and
mousedown handlers when hovering over a scrollbar with pointer-events
set to none.
2026-03-17 04:01:29 -05:00
Zaggy1024
2c2ad598a0 LibWeb: Use local coordinates for hit testing chrome
Without this fix, clicking a scrollbar nested within a scrolled box
would not register.
2026-03-17 04:01:29 -05: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
Aliaksandr Kalenik
834675aea3 LibWeb: Add PaintableBox::inverse_transform_point()
Extract the pattern of inverse-transforming a screen position (removing
the effect of CSS transforms) into a helper method. Used to compute
mouse event offsets relative to the target element.
2026-03-11 02:31:30 +01:00
Aliaksandr Kalenik
01a7c8e424 LibWeb: Add PaintableBox::transform_rect_to_viewport()
Extract the repeated pattern of transforming a rectangle from absolute
coordinates to viewport coordinates via the accumulated visual context
into a helper method.
2026-03-11 02:31:30 +01:00
Aliaksandr Kalenik
b217c5a496 LibWeb: Add PaintableBox::transform_point_to_local_for_descendants()
Similar to transform_point_to_local(), but uses the descendants' visual
context which accounts for the element's own scroll offset. Used during
fragment hit testing in PaintableWithLines.
2026-03-11 02:31:30 +01:00
Aliaksandr Kalenik
21876bd011 LibWeb: Add PaintableBox::transform_point_to_local()
Extract the repeated pattern of transforming a screen position to local
coordinates via the accumulated visual context into a helper method.
This returns an Optional, unlike the existing
transform_to_local_coordinates() which falls back to the original
position. The old method is now implemented in terms of the new one.
2026-03-11 02:31:30 +01:00
Jelle Raaijmakers
9814ea28e9 LibWeb: Propagate paint invalidation to anonymous child nodes
Elements that generate anonymous boxes such as `<button>` and
`<fieldset>` need to propagate paint invalidation to them in order to
properly invalidate the style of their inner contents.

Fixes #8347
2026-03-10 15:23:53 +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
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
Aliaksandr Kalenik
d7a8db671b LibWeb: Skip overflow clip generation for SVG inner elements
Per the CSS Overflow spec, overflow properties apply only to block
containers, flex containers, and grid containers — not SVG graphics
elements. Add an `is<SVGPaintable>` check in
`overflow_property_applies()` to return false for SVG inner elements
like `<g>`, `<rect>`, `<path>`.

This doesn't affect `<svg>` elements (which use `SVGSVGPaintable`, a
direct `PaintableBox` subclass) or `<foreignObject>` (which uses
`SVGForeignObjectPaintable`, a `PaintableWithLines` subclass) — both
correctly keep their overflow clips.
2026-02-24 12:28:55 +01:00
Aliaksandr Kalenik
7e3b49e583 LibWeb: Remove unused cumulative_offset_of_enclosing_scroll_frame() 2026-02-23 08:59:11 +01:00
Andreas Kling
057f11bf8f LibWeb: Cache absolute padding and border box rects on PaintableBox
These rects are computed from immutable layout data but were
recomputed from scratch on every call, with the CSSPixels saturating
arithmetic showing up as a significant cost in profiles.

Cache them lazily alongside the existing m_absolute_rect cache.

This was 3.3% of CPU time while playing a YouTube video.
2026-02-21 15:53:22 +01:00
Callum Law
9f7f623455 LibWeb: Store FilterOperation::DropShadow sub-values as StyleValues
This simplifies handling and brings it into line with other `StyleValue`
types
2026-02-16 12:09:23 +00:00
Callum Law
98e62cf86a LibWeb: Store FilterOperation::Blur::radius as StyleValue
This simplifies handling and brings it into line with other `StyleValue`
types
2026-02-16 12:09:23 +00:00
Callum Law
c529614e67 LibWeb: Store FilterOperation::HueRotate::angle as StyleValue
This simplifies handling and brings it into line with other `StyleValue`
types
2026-02-16 12:09:23 +00:00
Callum Law
cd799aa7e7 LibWeb: Forward declare CSS::FilterOperation structs
This means we don't need to include `FilterValueListStyleValue.h` in as
many places - reducing the rebuild from editing that file from 717 files
to 19.
2026-02-16 12:09:23 +00:00
Psychpsyo
a7267f711b LibWeb: Add overflow-clip-margin-* properties
The corner radius isn't quite right yet, but this gives us
another couple WPT passes for these.
2026-02-14 22:58:21 +01:00
Jelle Raaijmakers
6c2583eade LibWeb: Implement mouse auto scrolling of scrollable containers
When the mouse is dragged from inside a scrollable container to outside
of it, we now automatically scroll the container so the selection can be
extended. Scroll speed scales with the distance past the scrollport
edge, capped at a maximum. Edges close to the viewport boundary get a
wider activation zone so the speed ramp works predictably even when the
mouse has limited room to move.

The logic is encapsulated in AutoScrollHandler, which EventHandler
creates lazily on mouse selection start.
2026-02-11 11:04:53 +01:00
Jelle Raaijmakers
9d09b3ce34 LibWeb: Add Paintable method to scroll offset into view 2026-02-11 11:04:53 +01:00
Jelle Raaijmakers
abb0167cfa LibWeb: Return empty rect for scrollbar-width: none
If the scrollbar was made to be invisible, it should no longer have a
size. This actually disables the scrollbar functionality - previously it
was only invisible, but still worked - and allows hit testing to pass
through to elements and fragments behind it.
2026-02-11 11:04:53 +01:00
Jelle Raaijmakers
87ada9e887 LibWeb: Do not mark ScrollHandled as [[nodiscard]]
There's only one place where we don't `(void)` the result of these
methods, so let's not be too pedantic about it.
2026-02-11 11:04:53 +01:00
Aliaksandr Kalenik
3fc33f86dd Revert "LibWeb: Flatten non-3d transforms"
This reverts commit 63aeaa81e5.

The change broken image rendering on X.com and
https://browserbench.org/Speedometer3.1/
2026-02-10 19:19:14 +01:00
Psychpsyo
63aeaa81e5 LibWeb: Flatten non-3d transforms 2026-02-07 18:56:23 +01:00
Jelle Raaijmakers
1dd5409630 LibWeb: Add helpers to convert CSS::ShadowData to Painting::ShadowData 2026-02-06 10:47:50 +00:00
Tim Ledbetter
2cd30af4df LibWeb: Only apply overflow clipping to applicable element types
According to the CSS spec, overflow properties only apply to block
containers, grid containers and flex containers.
2026-02-04 21:23:20 +01:00
Aliaksandr Kalenik
d29b1bfaa9 LibWeb: Fix sticky positioning inside fixed-position ancestors
When a sticky element was inside a position:fixed ancestor,
nearest_scroll_frame() would stop at the fixed boundary and return
nullptr. This caused precompute_sticky_constraints() to bail out,
leaving the sticky element at its normal flow position with no offset.
2026-02-01 19:57:14 +01:00
Aliaksandr Kalenik
e2c6190e8c LibWeb: Use scroll state snapshot in compute_scrollbar_data()
Replace direct ScrollFrame access via own_scroll_frame_offset() with
ScrollStateSnapshot-based lookup. This aligns compute_scrollbar_data()
with the rest of the painting/hit-testing pipeline, which uses snapshots
for consistent, thread-safe scroll state.
2026-02-01 16:13:13 +01:00
Aliaksandr Kalenik
fc8b7bb7af LibWeb: Remove cached perspective matrix from PaintableBox
Compute the perspective matrix on the fly during visual context
assignment instead of caching it in PaintableBox. This reduces
PaintableBox's size and keeps the logic closer to where it's used.
2026-01-31 16:56:05 +01:00
Aliaksandr Kalenik
2e4eebe4e5 LibWeb: Move CSS transform resolution to visual context assignment
Instead of computing and caching the transform matrix and transform
origin on PaintableBox during resolve_paint_properties(), compute them
inline during assign_accumulated_visual_contexts() where they are
actually consumed. This makes PaintableBox smaller by not wasting space
for properties that are only consumed during AccumulatedVisualContext
tree construction.
2026-01-31 16:56:05 +01:00
Aliaksandr Kalenik
044d778c5c LibWeb: Precompute sticky constraints to avoid paintable tree lookups
Precompute the geometric data needed for sticky positioning during
scroll frame assignment. This avoids walking the paintable tree to query
containing blocks and ancestor geometry during scroll state refresh,
which runs on every scroll event.
2026-01-29 19:02:30 +01:00
Aliaksandr Kalenik
9e8ba8c411 LibWeb: Fix crash when scrolling pseudo-elements
In PaintableBox::set_scroll_offset(), the code unconditionally
dereferenced dom_node() which returns null for pseudo-elements,
causing a crash when processing scroll events.

Now we check for pseudo-elements and use the generating element
as the scroll event target.
2026-01-27 20:32:12 +01:00
Andreas Kling
c550301a04 LibWeb: Allow hit testing visible children of hidden elements
Previously, hit testing would return early for elements with
visibility: hidden, which prevented their visible children from being
hit. Now we traverse children even for hidden elements, allowing visible
descendants to be hit while still preventing the hidden elements
themselves from being hit.

The key changes:
- PaintableBox::hit_test() and PaintableWithLines::hit_test() no longer
  return early for hidden elements, but still skip chrome hit testing
  and the final hit result for them
- hit_test_fragments() now checks is_visible() on each fragment's
  paintable to skip hidden text

This matches the CSS specification where visibility is inherited but
children can override it with visibility: visible.
2026-01-25 10:55:30 +01:00
Aliaksandr Kalenik
cb8ecb3c11 LibGfx+LibWeb: Move SVG mask/clip composition from CPU to GPU
Previously, both mask and clip-path were rendered to separate mutable
Gfx::Bitmap objects which forced CPU rasterization. They were then
combined using a CPU pixel-by-pixel operation before being returned
as an ImmutableBitmap.

Instead of including mask in the final bitmap as already rasterized
images, we now use display lists which opens opportunity to utilize
GPU if available.

Bitmap::apply_mask() and ApplyMaskBitmap display list command are no
longer used and have been removed.
2026-01-23 16:23:06 +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
660e1a62b2 LibWeb: Remove clip-path handling from PaintableBox::get_masking_area()
This is now handled by AccumulatedVisualContext.
2026-01-21 19:10:26 +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
Aliaksandr Kalenik
69d5fa8cb7 LibWeb: Cache resolved CSS filters as paint-only properties
This moves filter resolution from display list recording time to
resolve_paint_properties(), caching the resolved values in CSS pixels
on PaintableBox. Device pixel conversion is now deferred until paint
time via to_gfx_filter().

This follows the existing pattern used for other paint-only properties
like box shadows and border radii.
2026-01-21 16:19:18 +01:00
Aliaksandr Kalenik
fe2b3cdb7d LibWeb: Clear perspective matrix when perspective property is removed
When paintable trees are preserved across relayouts, the paint
properties are re-resolved. If an element previously had a perspective
property but no longer does, the m_perspective_matrix needs to be
explicitly cleared, otherwise the stale value persists and produces
incorrect rendering.
2026-01-21 10:00:17 +01:00
Aliaksandr Kalenik
d4feeb1cad LibWeb: Preserve paintable tree across relayouts
Reuse existing paintables during relayout to reduce GC allocation
pressure. Each paintable subclass implements reset_for_relayout()
to clear state before reuse.
2026-01-21 10:00:17 +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
Aliaksandr Kalenik
9b3abe9c95 LibWeb: Remove unused PaintableBox::absolute_paint_rect()
This method was initially introduced to calculate the total paint area
including effects like box-shadows that paint outside the border box.
It was used in StackingContext for sizing bitmaps when painting
elements with opacity or transforms, and later for clip-path bounds.

This functionality is no longer needed as stacking context painting
and clip-path handling have been refactored to use different
approaches (AccumulatedVisualContext now handles clip-path).
2026-01-19 09:21:48 +01:00
Andreas Kling
eb75220eb6 LibWeb: Add missing type-isolating GC allocators for some types
- Layout::BlockContainer
- Painting::PaintableBox
- Painting::SVGGraphicsPaintable
- Platform::TimerSerenity
2026-01-18 10:10:04 +01:00