Commit graph

11 commits

Author SHA1 Message Date
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
4ccd59f3a9 LibWeb: Avoid copying inline iterator data
Reference cached TextNode chunk lists from InlineLevelIterator. This
avoids copying them into each text node context. The list already lives
in the text node layout cache, and the iterator only reads it.

Return generated inline items by reference from next(). This lets line
box generation consume each item directly, without copying its glyph run
reference on every iteration.
2026-05-30 13:43:42 +02:00
Tim Ledbetter
d49a2dcfa1 LibWeb: Implement word-break functionality using UAX#14 line breaking
We now use ICU's line break iterator to determine soft wrap
opportunities, enabling correct line breaking for CJK text.
2026-02-14 16:23:18 -05:00
Andreas Kling
3877026082 LibWeb: Pre-generate items in InlineLevelIterator
Instead of generating items lazily during iteration (and using a
lookahead queue for peeking), we now pre-generate all items upfront
in the constructor.

This allows next() and next_non_whitespace_sequence_width() to be
simple O(1) array accesses instead of doing generation work.

To handle tabs correctly (which need to know the accumulated width of
content before them on the line), we track m_accumulated_width_for_tabs
during pre-generation and reset it on forced breaks.
2026-01-11 11:10:19 +01:00
Andreas Kling
10737e22d1 LibWeb: Pre-generate text chunks in InlineLevelIterator
Instead of lazily generating text chunks via ChunkIterator with a peek
queue that used O(n) take_first() operations, we now pre-generate all
chunks upfront when entering a text node.

This makes chunk access O(1) via simple array indexing, which speeds up
both next() calls and forward lookups for bidi direction resolution.
2026-01-11 11:10:19 +01:00
Callum Law
bbb344d534 LibWeb: Compute font features in ComputedProperties
By doing this in computed properties rather than InlineLevelIterator we
only do it once per element rather than once per text fragment.

Reduces runtime of this process from ~15% to ~0.2% when loading
https://en.wikipedia.org/wiki/2023_in_American_television
2025-11-10 12:11:36 +01:00
Jelle Raaijmakers
9e9db9a9dd LibWeb: Store correct text offsets in PaintableFragment
Previously, we were collapsing whitespace in Layout::TextNode and then
passed the resulting string for further processing through ChunkIterator
-> InlineLevelIterator -> InlineFormattingContext -> LineBuilder ->
LineBoxFragment -> PaintableFragment. Our painting tree is where we deal
with things like range offsets into the underlying text nodes, but since
we modified the original string, the offsets were wrong.

This changes the way we generate fragments:

  * Layout::TextNode no longer collapses whitespace as part of its
    stored "text for rendering", but moves this logic to ChunkIterator
    which splits up this text into separate views whenever whitespace
    needs to be collapsed.

  * Layout::LineBox now only extends the last fragment if its end offset
    is equal to the new fragment's start offset. Otherwise, there's a
    gap caused by collapsing whitespace and we need to generate a
    separate fragment for that in order to have a correct start offset.

Some tests need new baselines because of the fixed start offsets.

Fixes #566.
2025-09-12 15:34:09 -04:00
Jelle Raaijmakers
4a514020e0 LibWeb: Remove unused include from InlineLevelIterator 2025-06-17 17:03:33 +02:00
Johan Dahlin
083f4f3d08 LibWeb: Layout/Shape font-variant-* css properties 2024-12-17 19:07:13 +01:00
Shannon Booth
f87041bf3a LibGC+Everywhere: Factor out a LibGC from LibJS
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:

 * JS::NonnullGCPtr -> GC::Ref
 * JS::GCPtr -> GC::Ptr
 * JS::HeapFunction -> GC::Function
 * JS::CellImpl -> GC::Cell
 * JS::Handle -> GC::Root
2024-11-15 14:49:20 +01:00
Timothy Flynn
93712b24bf Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Renamed from Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h (Browse further)