Commit graph

84 commits

Author SHA1 Message Date
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
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
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
Andreas Kling
7c06c3fd14 LibWeb: Make computed style data refcounted
Move ComputedProperties and CascadedProperties out of the GC. They no
longer contain strong references to GC-managed data.

Keep computed styles alive from DOM elements and animation updates with
RefPtr. Pass style into layout constructors by reference, since layout
only copies the values it needs while building nodes.

Use GC::Weak for cascade source links, so entries no longer keep the
style declaration or shadow root alive.
2026-06-06 23:29:48 +02:00
sideshowbarker
4df6ecd176 LibWeb: Ignore vertical-align on flex and grid items in box_baseline
Problem: Borked layout on (flex-styled) “D&D Beyond” site.

Cause: FormattingContext::box_baseline() was applying the box’s
vertical-align unconditionally. But vertical-align shouldn’t be
consulted for flex items or grid items.

Fix: Skip vertical-align handling in box_baseline() when the box is a
flex or grid item.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/9840
2026-06-05 10:11:06 +01:00
Andreas Kling
17902b02ab LibWeb: Stop idle animated CSS image timers
Keep animated ImageStyleValue frame advancement owned by the
style value. The current frame and loop state live there, so a
separate document scheduler would duplicate ownership of that state.

Start the ImageStyleValue timer only while it has layout clients.
Stop it when the last client unregisters, or when a finite animation
completes. Expose a document-scoped active timer count through
internals for focused regression tests.

Clear image observers when layout nodes detach. Use current-node
cleanup for per-DOM-node clearing, and explicit subtree cleanup for
tree replacement, full tree teardown, and synthetic pseudo-elements.
This keeps large document clearing linear.

Unregister generated-content image providers during layout detach
instead of waiting for GC to finalize the provider.

Cover hidden animated background images, generated content images,
layout node replacement, full layout tree teardown, and document
scoping for the internals counter.
2026-05-23 11:36:53 +02:00
Tim Ledbetter
b67d73a661 LibWeb: Apply ::first-letter pseudo-element styles
We now apply first letter styles by splitting text with a first-letter
style applied into 2 `TextSliceNode` objects.  The
`DOM::Text` layout  node always points at the non first-letter slice
and the first-letter slice is  reachable via
`TextSliceNode::first_letter_slice()`.

First letter splitting works by `TreeBuilder` walking a block
container's inline descendants to find the first typographic letter
unit per the pattern given in  css-pseudo level 4, which is then
wrapped in an anonymous inline box styled with the `::first-letter`
computed properties.

Consumers that map between DOM offsets and layout geometry
are updated to visit all slices of a `DOM::Text` through
`TextOffsetMapping`.
2026-05-20 12:09:19 +01:00
Tim Ledbetter
785f2881f9 LibWeb: Add AttachToDOMNode parameter to Layout::Node constructor
Add an `AttachToDOMNode` parameter to the Layout::Node constructor that
controls whether the newly-constructed node calls `set_layout_node()`
on its backing DOM node.
2026-05-20 12:09:19 +01:00
Andreas Kling
b95b3ecd6d LibWeb: Unregister image observers when finalizing layout nodes
Clear NodeWithStyle image observers during finalization so pending image
loads cannot call back into observers owned by unreachable layout nodes.
Incremental sweep leaves finalized cells allocated until their block is
swept, so waiting for the C++ destructor is too late.
2026-05-10 10:58:11 +02:00
Aliaksandr Kalenik
568b7ce7ea LibWeb: Make Paintable tree ref-counted
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.

We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
2026-05-07 15:03:44 +02:00
Tim Ledbetter
e495db44d5 LibWeb: Notify only affected layout nodes when a CSS image loads
Previously, `Document::notify_css_background_image_loaded()` walked the
entire `PaintableBox` subtree and cleared each box's paintable cache
whenever any CSS image finished loading.

Replace this with per-image observers owned by the layout node. During
`apply_style`, each node registers as an `ImageStyleValue::Client` for
the images its style references. On load, only the affected layout
node's paintables are invalidated.
2026-04-29 04:33:35 +02:00
Callum Law
fedd1f8d55 LibWeb: Add method to get style scope from layout node 2026-04-15 11:07:38 +01:00
Jelle Raaijmakers
6068d94752 LibWeb: Update/remove unused includes
No functional changes.
2026-04-01 19:41:46 +01:00
Tim Ledbetter
a762f623bd LibWeb: Implement SVGPatternElement 2026-02-27 17:14:50 +01:00
Zaggy1024
dd0c821291 LibWeb: Allow replaced elements to host children
We add a new formatting context that simply runs layout for an
anonymous block formatting context within it. This allows replaced
elements to contain children, if the parent rewrites inline-flow to
inline-block.
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
1b41c9109d LibWeb: Cache is_body() on layout nodes
Cache the result of the body element check as a bool set once during
NodeWithStyle construction, instead of calling document().body() (which
walks the children of <html>) on every call. This is called from
PaintableBox::paint_background() for every box on every frame.

This was 0.9% of CPU time while playing a YouTube video.
2026-02-21 15:53:22 +01:00
Callum Law
379db7a42c LibWeb: Support animation-timeline scroll() value 2026-02-11 10:49:34 +01:00
Aliaksandr Kalenik
e76cf3e225 LibWeb: Remove Document.h include from Layout/Node.h
This reduces the recompilation cascade when Document.h is modified.
Add explicit includes to files that relied on the transitive dependency.
2026-02-08 18:51:13 +01:00
Jonathan Gamble
d3cdeb3ac5 LibWeb: Implement auto_content_box_size for textarea and input
This allows default and attribute-based sizing when an axis is auto,
without overriding extrinsic sizing.
2026-02-02 14:36:49 +00:00
Andreas Kling
dc79259970 LibWeb: Support inline elements as abspos containing blocks
CSS allows inline elements with `position: relative` (or other
containing-block-establishing properties) to serve as the containing
block for their absolutely positioned descendants. However, our layout
system stores containing blocks as `Box*`, which cannot represent
inline elements (they are `InlineNode`, not `Box`).

This patch adds a workaround: when computing containing blocks, we
also check if there's an inline element between the abspos element
and its Box containing block that should actually be the CSS
containing block. If found, we store it in a new member called
`m_inline_containing_block_if_applicable` and use it during abspos
layout to:

1. Compute the inline's fragment bounding box as the containing
   block rectangle (including padding, per CSS spec)
2. Resolve percentage-based insets against the inline's dimensions
3. Position the abspos element relative to the inline's location

Some details to be aware of:

- The inline containing block search happens in the function
  `recompute_containing_block()` by walking DOM ancestors (not layout
  tree ancestors, since the layout tree restructures blocks inside
  inlines as siblings)

- For pseudo-elements like `::after`, we start the search from the
  generating element itself, since it may be the inline containing
  block

- Fragment offsets are relative to their block container, so we
  translate the computed rect to the abspos element's containing
  block coordinate system by accumulating offsets up the ancestor
  chain

- When the abspos element uses static position (auto insets), we
  don't apply the inline rect translation since static position is
  already computed in the correct coordinate system

Long term, we want to refactor our "containing block" concept to
map more cleanly to the spec concept. That means turning it into
a rectangle instead of the box this rectangle was derived from.
That's an invasive change for another day though.
2026-01-19 17:34:46 +01:00
Sam Atkins
89def06fcc LibWeb/Layout: Implement fixed positioning containing blocks
Previously we only allowed the viewport itself to be the containing
block for fixed-position elements, but the specs give us a few other
situations. Many of these are the same as for absolute-positioned ones.
2026-01-06 15:54:57 +00:00
Sam Atkins
cf2520d6a4 LibWeb/Node: Rename Node::can_contain_boxes_with_position_absolute()
This method matches the spec concept of "establishing an absolute
positioning containing block", so let's use that name.
2026-01-06 15:54:57 +00:00
Sam Atkins
c446281844 LibWeb/CSS: Remove Transformation in favor of TransformationStyleValue
The Transformation class wasn't really accomplishing anything. It still
had to store StyleValues, so it was basically the same as
TransformationStyleValue, with extra steps to convert from one to the
other. So... let's just use TransformationStyleValue instead!

Apart from moving code around, the behavior has changed a bit. We now
actually acknowledge unresolvable parameters and return an error when
we try to produce a matrix from them. Previously we just skipped over
them, which was pretty wrong. This gets us an extra pass in the
typed-om test.

We also get some slightly different results with our transform
serialization, because we're not converting to CSSPixels and back.
2025-12-19 14:51:53 +01:00
Lorenz A
010b0b00ff LibWeb: Propagate style values in deep anonymous wrappers
The style propagation logic in `NodeWithStyle::apply_style()`
was incomplete for anonymous nodes created during layout
(e.g., within `wrap_in_button_layout_tree_if_needed`).

1.  **Non-inherited CSS values** were not being propagated to the
    anonymous wrappers.
2.  Propagation did not recurse into **nested anonymous wrappers**
    (descendants).

This fix adds calls to `propagate_non_inherit_values(child)` and
`child.propagate_style_to_anonymous_wrappers()`, ensuring all computed
styles reach the entire anonymous wrapper hierarchy.
2025-11-23 21:38:07 +01:00
Pascal Pomper
5b1eba7ac8 LibWeb: Position absolute block-level boxes below previous siblings
Whether an absbox is positioned below or to the right of its previous
sibling in an `InlineFormattingContext` is determined by the
display-outside value before blockification, so we store the
pre-blockification `display` value in `ComputedValues` to access it in
`InlineFormattingContext` and position the box accordingly.
2025-09-29 18:37:53 +02:00
Andreas Kling
989f6ddb42 LibWeb: Add fast_is<T> for hotly dynamic_cast'ed things on imdb.com 2025-09-25 21:42:52 +02:00
zac
191b026c9f LibWeb: Rename is_generated to is_generated_for_pseudo_element
While much longer, this better conveys the intent. Since this method
isn't used too often, I think the extra length is worth it.
2025-09-24 12:33:17 +01:00
Andreas Kling
e41cfb92e2 LibWeb: Add fast_is<T> optimization for Layout::InlineNode 2025-09-22 15:00:50 +02:00
Luke Wilde
b17783bb10 Everywhere: Change west consts caught by clang-format-21 to east consts 2025-08-29 18:18:55 +01:00
Callum Law
1052ee20b0 LibWeb: Move snap_a_length_as_a_border_width to StyleComputer.cpp
All users are in this file so it makes more sense to have it here
2025-08-28 09:29:46 +01:00
Callum Law
6eae92511f LibWeb: Store border-*-width in computed form in ComputedProperties
Gains us 112 new passes since we now interpolate keywords (thick, thin,
etc) correctly.

Also loses us 4 WPT tests as we longer clamp negative values produced by
interpolation from the point of view of getComputedStyle (although the
'used' value is still clamped).
2025-08-28 09:29:46 +01:00
Andreas Kling
0d2800e411 LibWeb: Don't relocate fragments across atomic inline boundary
All fragments inside an atomic inline box should stay within that box,
otherwise we'll screw up the paint order and paint them behind things
that they're supposed to be on top of.

This fixes an issue with inline-block content not appearing on sites
like Google Docs and Reddit, among others.
2025-08-24 21:00:08 +02:00
ayeteadoe
3df8e00d91 LibWeb: Enable EXPLICIT_SYMBOL_EXPORT 2025-08-23 16:04:36 -06:00
Aliaksandr Kalenik
61114f6d16 LibWeb: Rename PaintContext to DisplayListRecordingContext
PaintContext dates back to a time when display lists didn't exist and it
truly represented "paint context". Renaming it to better align with its
current role.
2025-08-01 05:25:56 -04:00
Andreas Kling
0cf9a4543a LibWeb: Make SVG foreignObject establish a new stacking context
This matches the behavior of Blink and Gecko, but not WebKit.
2025-07-09 14:36:08 +02:00
Sam Atkins
23520509cc LibWeb/Layout: Expose which pseudo-element a node was generated for 2025-06-19 12:35:31 +01:00
Sam Atkins
dfce9974b5 LibWeb: Remove GeneratedPseudoElement enum
This doesn't currently provide any value over just using PseudoElement,
and makes it harder to work with PseudoElement in other places.
2025-06-19 12:35:31 +01:00
Jelle Raaijmakers
1871843acb LibWeb: Reformat Node/PaintableBox::has_css_transform()
Slightly more visually pleasing. No functional changes.
2025-06-17 11:55:28 +02:00
Psychpsyo
a0be82b2cb LibWeb: Move containment checks to Layout::Node
It fits better there and avoids having to reach into the Element
all the time.
2025-05-13 15:30:14 +03:00
Aliaksandr Kalenik
8e2d1559ec LibGfx: Join ScaledFont into Font
Since ScaledFont is the only class inherited from Font we could simply
merge them.
2025-04-21 09:51:16 +02:00
Andreas Kling
3c15fec303 LibWeb: Move "needs layout update" flag from DOM to layout tree
This is in preparation for allowing anonymous boxes to retain their
intrinsic size cache across layouts.
2025-04-21 00:31:08 +02:00
Andreas Kling
28d564197c LibWeb: Compute containing block boxes once at start of layout
We now cache the containing block (box) once at the start of layout,
which allows Layout::Node::containing_block() to return instantly
instead of doing tree traversal.

Removes a 0.7% profile item on Speedometer 3.
2025-04-19 01:14:02 +02:00
Andreas Kling
877391d569 LibWeb: Add more fast_is<T> helpers for layout and paintable nodes 2025-04-18 14:45:56 +02:00
Gingeh
972547635f LibWeb: Dispatch pointer events to ::backdrop originating element 2025-04-09 12:10:42 +01:00
Sam Atkins
2c86fd539d LibWeb: Generate an enum for generated pseudo-elements 2025-03-24 09:49:50 +00:00
InvalidUsernameException
f7276bfab3 LibWeb: Reduced number of recompiled files for CSS property headers
This reduces the number of `.cpp` files that need to be recompiled when
one of the below header files changes as follows:

CSS/ComputedProperties.h: 1113 -> 49
CSS/ComputedValues.h: 1120 -> 209
2025-02-23 10:14:39 -05:00
Jelle Raaijmakers
0c58dad7a6 LibWeb: Factor out conditions for creation of inline continuation
Having one big `if` to determine whether or not we should restructure an
inline layout node became a bit unwieldy. This extracts the logic into a
separate method.
2025-02-19 13:49:24 +01:00
Andreas Kling
fb020a3c8f LibWeb: Store final box model metrics in paint tree, not layout tree
This was a weird case of layout results being stored in the layout tree
instead of in the paint tree like everything else.
2025-02-17 18:28:29 +01:00
Jelle Raaijmakers
336684bc5c LibWeb: Support inserting non-inline elements into inline elements
Our layout tree requires that all containers either have inline or
non-inline children. In order to support the layout of non-inline
elements inside inline elements, we need to do a bit of tree
restructuring. It effectively simulates temporarily closing all inline
nodes, appending the block element, and resumes appending to the last
open inline node.

The acid1.txt expectation needed to be updated to reflect the fact that
we now hoist its <p> elements out of the inline <form> they were in.
Visually, the before and after situations for acid1.html are identical.
2025-01-23 09:33:10 +01:00