Commit graph

759 commits

Author SHA1 Message Date
Andreas Kling
a1bc2a6223 LibWeb: Use Utf16StringBuilder for UTF-16 text assembly
Replace the remaining UTF-16-mode StringBuilder instances in LibWeb
with Utf16StringBuilder when they assemble Utf16String values or UTF-16
text views. This keeps those paths in UTF-16 throughout and uses
explicit ASCII append helpers for ASCII-only pieces.

Keep StringBuilder in place for JSON, markup, CSS serialization, and
other byte-oriented string construction paths.
2026-06-22 16:10:40 +02:00
Jelle Raaijmakers
d819152b4e LibGfx+LibWeb: Preserve text color for selections
Selecting text without custom ::selection styling changed the
foreground color of the selected content. This was especially visible
for links, where the text changed color but the underline did not.

The default selection style supplied both a selection background and a
foreground color from the palette or HighlightText system color. That
made ordinary selections behave as if the page had explicitly styled
::selection color.

Only provide a default selection background, so selected content keeps
its own foreground color unless CSS overrides it. Remove the now-unused
SelectionText palette role.
2026-06-18 22:43:26 +02:00
Tim Ledbetter
91d3d135eb LibWeb: Paint emoji glyph text-shadows in the shadow color 2026-06-18 22:42:29 +02:00
Jelle Raaijmakers
d642f7d85a LibWeb: Do not dump number of children for StackingContexts
The number of children a SC has is not that useful in dumps, when all
its children are dumped anyway.
2026-06-18 10:50:08 +02:00
Callum Law
8ebdaeab69 LibWeb: Transfer animation ownership to AnimatedBitmapDecodedImageData
Previously animation ownership was a messy split between
`AnimatedBitmapDecodedImageData` and the consumers (i.e.
`ImageStyleValueResource`, `HTMLImageElement`, and `SVGImageElement`)
with `AnimatedBitmapDecodedImageData` owning the frames and a current
frame index, and the consumers owning the rest of the state (e.g. loop
count, timers to drive the animation forward, their own current index).

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

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

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

The tests to ensure animations are cancelled when consumers are removed
(e.g. `animated-background-image-timer-stops-when-hidden.html`) have
been updated to assert the inverse since animation state is now per
resource not per consumer.
2026-06-18 10:44:25 +02:00
Callum Law
2335941e43 LibWeb: Register ImageObservers for all mask-images
As of 114c8a7 we paint all mask layers, not just the first, so we should
likewise observe all `mask-image` values, not just the first.
2026-06-18 10:44:25 +02:00
Callum Law
d98c5d1b03 LibWeb: Inline image scaling mode computation
This allows us to remove the `frame_rect` accessor.

This also fixes a bug where we computed the scaling mode based on the
clipping rect rather than the draw rect for `ImagePaintable`
2026-06-18 10:44:25 +02:00
Callum Law
d3c0cfdc71 LibWeb: Respect SVG intrinsic sizing during object fitting
Previously we used the `frame_rect` size, this is the same as the
intrinsic size for bitmap images but is `OptionalNone` for SVG which
caused us to always fall back to the `image_rect` and thus not apply
any scaling for SVGs regardless of whether they had intrinsic sizing.
2026-06-18 10:44:25 +02:00
Aliaksandr Kalenik
d96dc6535f LibWeb+Compositor+WebContent: Simplify nested context composition
Nested navigables were represented through compositor surface ids owned
by the parent context. That forced CompositorState and ContextState to
maintain bidirectional attach/detach bookkeeping, publish child
snapshots into a surface map, and keep presentation mode variants just
to distinguish UI presentation from parent composition.

Record the child compositor context id directly in the display list and
let the compositor resolve it against the painting parent at playback
time. Child contexts now keep their parent context id and latest
rendered surface, while parents no longer track child maps or compositor
surface ids. UI presentation is represented separately from parent
composition, so closing a page only stops client presentation and nested
contexts keep using set_parent_context.
2026-06-18 07:08:34 +02:00
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
Aliaksandr Kalenik
7ca410c66c LibWeb: Stop storing compositor surfaces as display list resources
Compositor surfaces are only used for nested navigables now. The
display list command already carries the CompositorSurfaceId, but
playback still resolved that id through DisplayListResourceStorage and
WebContent exposed IPC for direct surface updates and clears.

Keep published child surfaces as PaintingSurface entries on the
compositor ContextState and pass that map into Skia display list
playback. Publishing and detaching nested contexts now update the parent
cache entirely inside the compositor, so WebContent no longer needs
update_compositor_surface or clear_compositor_surface messages.
2026-06-17 20:20:24 +02:00
Aliaksandr Kalenik
91bcd50224 LibGfx+LibWeb: Store Skia images with display list resources
DisplayListPlayerSkia kept a separate DecodedImageFrameSkiaImageCache
that was pruned during flushes. That made Skia image lifetime
independent of display list resource lifetime, even though resource
storage is what knows when image frames and compositor surfaces are no
longer needed.

Make DisplayListResourceStorage own an opaque stored image-frame
resource that holds the decoded frame and its lazily-created SkImage.
Removing image frames or compositor surfaces now drops the decoded frame
and Skia image together, while transactions still carry only Skia-free
decoded frames.
2026-06-17 19:17:10 +02:00
Aliaksandr Kalenik
a80babffb6 LibWeb+Compositor: Run canvas contexts in the Compositor
Canvas rendering is a major remaining path where WebContent directly
owns GPU-facing drawing state. Back 2D and WebGL canvas contexts with
remote Compositor transports, so WebContent talks to canvas surfaces
through IPC while the Compositor owns the rasterization resources.

This is a large step toward GPU sandboxing because canvas GPU work now
lives behind the Compositor boundary. It also gives OffscreenCanvas the
process-independent canvas plumbing that HTMLCanvasElement now uses,
making worker-owned canvases possible without another WebContent-local
rendering path.
2026-06-17 19:07:32 +02:00
Aliaksandr Kalenik
af8b41e1cb LibWeb+Compositor: Add canvas display-list surfaces
Canvas contexts need a compositor-owned surface path that can be shared
by 2D canvas and WebGL. Add CanvasId and a CanvasSurfaceRegistry, pass
the registry into display-list playback, and teach Skia playback how to
resolve and draw a registered canvas surface.

This only adds the shared display-list command and registry plumbing.
Existing canvas elements still publish their old compositor surfaces, so
the behavior change is left for the later canvas-host commits.
2026-06-17 19:07:32 +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
Andreas Kling
a11c281dc3 Compositor: Handle pinch zoom asynchronously
Apply pinch zoom deltas to the compositor's visual viewport transform
so the currently presented display list can respond without waiting for
the WebContent main thread. Keep the normal WebContent pinch event path
so the real VisualViewport state and DOM-visible events catch up after.

Only take the compositor path when async scrolling is enabled and there
are no blocking wheel listeners, since pinch zoom dispatches a synthetic
wheel event that script may cancel. Coalesce queued pinch events in
WebContent so main-thread catch-up can adopt multiple gesture deltas
together.

Use the compositor visual viewport transform for wheel hit testing and
consume wheel deltas as visual viewport pan while zoomed. Scale the
handoff to layout viewport scrolling by the inverse visual viewport
scale, so touchpad momentum does not jump when the visual viewport hits
an edge.
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
Tim Ledbetter
48f845b3b4 LibWeb: Record SVG hit-test geometry in absolute coordinates
Previously, SVG path hit geometry was recorded in the enclosing <svg>
element's local coordinate space rather than absolute page coordinates,
so SVG shapes were only hittable when the <svg> sat at the document
origin
2026-06-14 17:45:28 +02:00
Tim Ledbetter
5b1120d0f4 LibWeb: Hit-test SVG paths against their fill geometry
Previously, hit testing for SVG paths used the path's bounding box.
2026-06-14 17:45:28 +02:00
Aliaksandr Kalenik
255ca9d99d LibWeb: Unify display list command playback hooks
Switch DisplayListPlayer and DisplayListPlayerSkia to overloaded
play_command() methods generated from ENUMERATE_DISPLAY_LIST_COMMANDS.
2026-06-13 20:41:03 +02:00
Andreas Kling
956a2b96d5 LibWeb: Allocate layout and painting objects with mimalloc
Add class-local allocation macros for operator new/delete through AK's
malloc helpers. The macros can optionally choose a HeapPartition.
Add Layout and Painting partitions, plus basic partition stats helpers.

Use the new partitions for LibWeb layout and painting object hierarchies
and layout-state side data.
2026-06-13 19:08:08 +02:00
Jelle Raaijmakers
de664c4c12 LibWeb: Restore context after culled display list effects
Effect culling can stop a visual context switch after applying
some ancestor contexts. The player left those contexts on the painter
stack even though the target command was skipped.

Restore the painter stack back to the common ancestor when effect
culling aborts the switch.
2026-06-12 04:59:57 +01: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
f1ade8186a LibWeb: Drop unused include from PaintableBox.h 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
Tim Ledbetter
d19fb87441 LibWeb: Clip nested display lists to their destination rect
SVG image content drawn outside the image's viewport escaped the image
box, because `PaintNestedDisplayList` playback translated to the
destination rect without clipping to it. We now clip it during playback.
2026-06-08 21:17:02 +01:00
Andreas Kling
e18d78eb2d LibWeb: Handle degenerate repeating gradients
Normalize zero-length repeating gradients to a solid average color
before painting code expands or tiles their stops. CSS Images defines
this case as an average over the same color stops with an arbitrary
non-zero distance, avoiding division by a zero repeat length while
painting.

Add a reduced crash test covering zero-length repeating linear, radial,
and conic gradients.
2026-06-08 01:04:08 +02:00
Andreas Kling
ecb3080ac6 LibWeb: Tolerate unpainted SVG ancestors
SVG absolute rect computation walks from the containing SVG root up
through layout containing blocks. Some anonymous or otherwise unpainted
ancestors can appear in that chain, so skip those boxes instead of
assuming every ancestor has a paintable box.

Add crash coverage for SVG content under a foreignObject nested in SVG
font content, matching the reduced structure from the fuzzer bucket.
2026-06-08 01:04:08 +02:00
Andreas Kling
e23c657144 LibWeb: Avoid SVG getBBox paintable downcasts
SVG foreignObject elements expose SVGGraphicsElement methods, including
getBBox(), but their layout boxes create SVGForeignObjectPaintable,
rather than SVGGraphicsPaintable. Only SVGGraphicsPaintable stores the
computed SVG transform data, so use it only when that paintable kind is
present.

Add reduced crash coverage for calling getBBox() on a rendered
foreignObject element.
2026-06-08 01:04:08 +02:00
Andreas Kling
9815f0d94d LibWeb: Avoid zero border radius overlap divisors
The border-radius overlap algorithm scales by L_i / S_i for each side
whose adjacent radii exceed the side length. If layout produced a
negative border box dimension, a side with a zero radius sum could be
mistaken for an overflowing side and passed to CSSPixelFraction as a
zero denominator.

Use non-negative side lengths for the normalization step and only divide
by nonzero radius sums. Add reduced crash coverage for an output element
whose negative letter spacing produces this geometry during painting.
2026-06-08 01:04:08 +02:00
Andreas Kling
e359c03289 LibWeb: Only assign sticky frames with sticky insets
Some paintables can carry sticky positioning without receiving sticky
inset data from layout. They cannot produce sticky constraints for async
scrolling.

Only create sticky scroll frames when sticky inset data exists. Add a
crash test for a sticky ruby paintable.
2026-06-08 01:04:08 +02:00
Andreas Kling
6eff400823 LibWeb: Avoid scrollbar painting without overflow data
Sticky inline paintables can have overflow:scroll without owning
scrollable overflow data. They cannot produce scrollbar geometry.

Return no scrollbar data when there is no overflow rectangle. Add a
crash test for a sticky inline bdi element.
2026-06-08 01:04:08 +02:00
Andreas Kling
9340d2d1a3 LibWeb: Make layout nodes refcounted
Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.

Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.

Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.

Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.

Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
2026-06-07 20:52:49 +02:00
Aliaksandr Kalenik
317dca7aaa LibWeb: Skip compositor metadata during display list painting
Display-list replay used to feed compositor-only metadata commands
through the normal painting path. Scroll nodes, wheel hit-test regions,
main-thread wheel regions, and viewport scrollbar metadata do not draw
in the Skia player, but each command still paid visual-context
switching, clip rejection, and no-op dispatch overhead.

Keep those commands in the display list for async scrolling and dumps,
but skip them at the start of painting replay. This keeps the metadata
available to the compositor while removing thousands of no-op paint-side
commands from heavy scrolling pages.

With this change rasterization goes down from 13 ms to 1ms on my
computer for page in
https://github.com/LadybirdBrowser/ladybird/issues/9929
2026-06-07 00:35:36 +01:00
Andreas Kling
222c1f7044 LibWeb: Move CSS image loading state to Document
Move the SharedResourceRequest, animation timer, and current frame
state out of ImageStyleValue and into a Document-owned table keyed
by resolved image URL. ImageStyleValue now keeps only URL metadata
and its client list, so image style values no longer need to trace
GC edges themselves.

Thread the Document through AbstractImageStyleValue APIs that need
decoded image data. CSS image fetches snapshot the stylesheet base URL,
referrer behavior, and origin-clean state instead of retaining the
stylesheet.

Remember each client's registered resolved URL when unregistering. This
keeps a later document base change from leaving an animated image
resource alive.

Add text coverage for inline relative image base URLs, stylesheet
referrers, imported stylesheet origin-clean behavior, inline @import
initiator type, and unregistering an animated background image after a
base element change.
2026-06-06 23:29:48 +02:00
Aliaksandr Kalenik
852c7a10f3 LibWeb: Let image paint callers own clipping
DecodedImageData::paint() used to take both a destination and a
clip rectangle even though most callers passed the same value. SVG
image painting used that API to wrap every nested SVG display list in
save/add-clip/restore, which put an unbounded command in front of
the bounded nested-list command and made offscreen SVG image content
harder to cull.

Move clipping to ImagePaintable, where the object-fit destination can
be compared with the replaced element box. CSS image and marker
painting continue to draw into their destination rect, while repeated
background images keep their explicit tile clip. The scaled decoded
image display-list command now stores only its destination rect and
uses that as its bounds; playback still clips decoded images to that
rect so bitmap rendering stays unchanged.
2026-06-06 13:23:21 +01:00
Aliaksandr Kalenik
e9fe045de6 LibWeb: Do not copy nested display list command bytes
PaintNestedDisplayList carried both a display list resource id and an
inline copy of that resource's command buffer. Repeated SVG image paints
therefore duplicated the nested command stream in every parent display
list even though resource storage already owns and deduplicates it.

Drop the inline command byte span and replay nested display lists from
the resource table. Resource collection now follows the referenced
resource's command bytes as well, so cached paint data and compositor
transactions keep the same nested resource retention behavior.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/9929
2026-06-06 11:30:15 +01:00
sideshowbarker
e356fd8188 LibWeb: Paint heavily-tiled non-image backgrounds as a single pattern
Problem: On https://turbovision.in6-addr.net a tiny-background-size +
background-repeat tile over a large painted area crashed WebContent.

Cause: BackgroundPainting was recording one paint command per tile for
any repeating background that’s not a decoded image. A small tile over a
large area could produce literally millions of commands — resulting in a
display list exceeding MAX_MESSAGE_PAYLOAD_SIZE, and tripping an assert.

Fix: When a repeating AbstractImageStyleValue would emit more tiles than
a fixed threshold, record a single tile into a nested display list and,
reusing some existing SVG pattern machinery, fill the coverage rect with
a PatternPaintStyle. We render the tile once, and repeat it with a
tiling shader — so the display list holds a single command regardless of
tile count. Normal backgrounds keep their exact per-tile painting.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/9876
2026-06-06 09:34:09 +01: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
ef58631dd5 LibWeb: Skip inline paint phase without inline content
The inline and replaced paint phase walks every descendant even for
block-only contexts. Track whether a stacking context has inline or
replaced descendants while the stacking-context tree is built, then
skip the phase when it cannot produce display-list commands. This adds
one bool to StackingContext.
2026-06-05 19:45:50 +01:00
Aliaksandr Kalenik
9327ab670e LibWeb: Skip float paint phase without floats
Stacking contexts already collect non-positioned floating descendants
while building the paint tree. Use that list to avoid walking the
whole context for the float paint phase when no float can be painted.
2026-06-05 19:45:50 +01:00
Aliaksandr Kalenik
bab8138236 LibWeb: Skip box hit-test records in irrelevant phases
PaintableBox::record_hit_test_items() is called for every paint phase,
but it only emits box hit-test entries during the background and
overlay passes. Return before touching the hit-test display list during
the other phases so cached paint replay stops repeating no-op work.
The hyperfine delta is within noise.
2026-06-05 19:45:50 +01:00
Aliaksandr Kalenik
760e82d299 LibWeb: Skip empty display-list command cache replay
DisplayListRecorder::replay_cached_commands() always entered the
display-list decoder to update save nesting, even when a paintable had
no cached commands. Empty command caches are common on the
ViewportPaintable benchmark, so return before decoding when the cache
is empty.
2026-06-05 19:45:50 +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
Sam Atkins
8c60d20dfc LibWeb: Keep grid template used values outside DevTools data
Resolved grid-template-columns and grid-template-rows values are needed
for getComputedStyle(), even when DevTools is not connected. Store them
separately from GridLayoutData again so the later lazy DevTools data
work can skip inspector-only geometry without changing CSSOM behavior.

Partly reverts 540b53ac1a.
2026-06-01 17:39:32 +01:00
Sam Atkins
a08b05a8e8 LibWeb: Recompute container-unit dependent styles
When a query container changes size or type, descendants that use
container-relative units need the same post-layout style refresh as
elements matched by `@container` rules.

Drop the style-scope gate from the invalidation and getComputedStyle()
paths so any element flagged as depending on a size query container gets
restyled after layout.
2026-06-01 08:27:17 +01:00
Aliaksandr Kalenik
b5c7821b3b LibWeb: Shrink glyph display-list payloads
DrawGlyphRun display-list commands serialized Gfx::DrawGlyph values
though replay only needs each glyph's position and glyph id. This kept
text layout-only fields in the command stream for every glyph and made
glyph-heavy pages spend most of their display-list bytes on unused data.

Serialize a display-list-specific glyph payload containing only the
position and glyph id, and have the Skia player read that compact form.
The same helper is used for text shadows, which replay through
DrawGlyphRun as well. On the Hacker News item trace this reduces the
stored glyph array from 24 to 12 bytes per glyph.
2026-05-31 18:26:23 +01:00
Aliaksandr Kalenik
08e448b119 LibWeb: Shrink wheel hit-test display-list targets
Every compositor wheel hit-test target carried a Gfx::CornerRadii
payload even though most targets have square corners. On YouTube this
made wheel hit-test targets the largest display-list bucket, with
thousands of targets paying for empty radii.

Keep CompositorWheelHitTestTarget as the compact square-corner command
and add CompositorWheelHitTestTargetWithCornerRadii for the rounded
case. Painting emits the larger command only when the computed corner
radii are non-empty, and async scrolling normalizes both payloads back
into the same target state.

A YouTube recording with 5,483 square-corner wheel hit-test targets used
80-byte compact commands instead of the 112-byte rounded command,
reducing the top-level display list by about 175 KiB for that page.
2026-05-31 18:26:23 +01:00