Commit Graph

142 Commits

Author SHA1 Message Date
Oriol Brufau
d5c55eb87c layout: Fix bottom margin of block-in-inline getting ignored (#42889)
In order to know whether a block box can collapse its bottom margin with
the bottom margin of its last child, we need to check its tentative
block size. Most usually, this will be the block size of the containing
block that is used when laying out the children.

However, anonymous blocks do not establish a containing block. So using
the containing block for children meant that we were using the tentative
block size of its parent. If that parent had a definite size, this would
prevent the anonymous block from collapsing bottom margins with its
children. This situation could happen when placing a block-level inside
of an inline box.

This patch fixes the problem by using an indefinite tentative block size
for anonymous blocks, instead of the containing block for children.

Testing: Adding new test
Fixes: #42469

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-02-27 10:23:21 +00:00
Oriol Brufau
e0bd48f16a layout: Fix abspos position when it would be preceded by phantom line (#42586)
The static position of an abspos is the hypothetical position that it
would have had in the normal flow. If the abspos is inside inline layout
and it had a block-level original display, then the static position is
computed as if the abspos was a block-level that breaks the inline.

Usually this places the static position after the current line. However,
if the abspos is not preceded by any text, padding, border, margin,
preserved newline or atomic inline, then the static position should be
as if the abspos was preceded by a 0px tall phantom line. Previously we
weren't taking that into account.

Note that browsers aren't completely interoperable in corner cases.

Testing: Adding 3 new tests. Blink fails the 2nd one and Gecko fails the
3rd one. WebKit and Servo pass them all.
Fixes: #41990

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-02-26 18:20:46 +00:00
Oriol Brufau
80318ad25e layout: Let OutsideMarker store an IndependentFormattingContext (#42864)
`OutsideMarker` was storing the `BlockFormattingContext` and the
`LayoutBoxBase` in separate fields. Now it will store an entire
`IndependentFormattingContext` in a single field.

In particular, this fixes the issue that `OutsideMarker::repair_style()`
wasn't calling `BlockFormattingContext::repair_style()`. Now we can just
rely on `IndependentFormattingContext::repair_style()`, which correctly
repairs the style of both the `BlockFormattingContext` and the
`LayoutBoxBase`.

Also, the `BlockLevelBox::repair_style()` was repairing the style of the
`LayoutBoxBase` twice, for all kinds of block-level boxes. This removes
the duplication.

Testing: Adding a new test
Fixes: #42779

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-02-26 09:55:15 +00:00
Oriol Brufau
60c5977291 layout: Correctly repair OutsideMarker::list_item_style (#42825)
`OutsideMarker::repair_style()` was setting `list_item_style` to the
style of the marker, not the style of the list item.

This patch sets it to `node.parent_style(context)` instead, and it fixes
`ServoThreadSafeLayoutNode::parent_style()` to take the pseudo-element
chain into account. This requires modifying a bunch of logic to pass the
`SharedStyleContext` around.

Testing: Adding a new test

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-02-25 13:25:45 +00:00
Oriol Brufau
a2041d6263 layout: Reuse boxes for independent formatting contexts with self damage if compatible (#42783)
Previously, when a formatting context root had rebuild damage from its
own (not from ancestors), we weren't reusing it. Now we check if the
level of the box (i.e. whether it's inline-level, block-level, a table
cell, a table caption, or absolutely positioned) that will be generated
by the new style is compatible with the old box. And if so, we reuse it.

Testing: Not needed, there should be no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-02-24 17:19:24 +00:00
Martin Robinson
042d95220a layout: Replace dirty root approach with flexible box tree layout (#42700)
Replace the old dirty root box tree layout approach with one that works
based on independent formatting contexts. Instead of just allowing a
single dirty root to be the source of box tree reconstruction, allow box
tree reconstruction to happen anywhere in the tree that can isolate box
tree damage from ancestors. This essentially combines damage propagation
and box tree construction into a single step.

There is currently one downside to this approach compared to the dirty
root approach which is that we currently cannot detect and start box
tree layout when the dirty has a compatible `display` and `position`
value. This can mean the scope of box tree layout extends further up the
tree. We will address this in a followup -- but have not noticed any
major performance implications (currently fragment tree layout is much
more expensive than box tree layout).

Benefits:
 1. Damage propagation now only happens under the dirty root.
 2. Future changes can limit the scope of damage up the tree and perhaps
    preserve the inline content size cache between box tree rebuilds.

Testing: This should not change behavior in a testable way (we currently
do
not have robust performance tests), so WPT test results should not
change.

---------

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
2026-02-19 14:31:13 +00:00
Martin Robinson
59f2325ff1 layout: Clear all node damage during damage traversal (#42600)
Instead of waiting until the box tree construction phase of layout to
clear node damage, clear it during the damage traversal. This means that
damage is cleared in the same way in all types of situations, which
simplifies the code and will make it easier to implement future
optimizations.

Testing: This should not really change behavior (other than being
marginally more
efficient when there is a dirty root), so existing tests should suffice.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
2026-02-16 11:11:02 +00:00
Martin Robinson
150f767b19 layout: Store a fallback cache in the FontGroup (#42466)
Store a list of recently used fallbacks in the `FontGroups`, keyed by
`lang` property and the detected `UnicodeBlock` and `Script` of the
character in question. This should mean that less work is done when
switching between falling back and not falling back. In addition, this
will allow better caching of `FontRef`s that come directly from the
platform font backend -- something necessary for the next generation
font fallback code.

Testing: This should not change rendering output so is covered by
existing
tests. It might have a minor affect on performance.
Fixes: This is part of #41426.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-02-13 17:01:41 +00:00
Martin Robinson
0c182904f5 layout: Clear dirty box tree nodes during damage traversal (#42596)
Instead of keeping dirty box tree nodes around after the damage
traversal, clear them readily (like `display: none` nodes). This
simplifies the logic during box tree reconstruction and also prepares
the damage traversal for future work.

In addition, restructure the logic of the damage traversal to make it a
bit easier to follow.

Testing: This shouldn't change observable behavior so should be covered
by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
2026-02-13 13:43:13 +00:00
Martin Robinson
dca5250222 layout: Clear display: none boxes during damage traversal (#42584)
There is no need to wait until box tree layout to do this and we want to
make clearing boxes a thing that happens in the damage traversal.

Testing: This should not change behavior in an observable way, so should
be covered by existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
2026-02-12 18:54:58 +00:00
Martin Robinson
6d2960ab36 layout: Properly set parent of dirty root after box tree layout (#42535)
When laying out the box tree from a dirty root, we were replacing the
inner layout
box contents of the dirty root, but not updating its parent. This change
fixes
that. This is important because any sort of operation that will need the
parent
will subsequently fail unless it is properly set. We do not use the
parent now, but
we will in the future.

Testing: The parent is currently unused, but it will be used in the
future. This
shouldn't change the functioning of Servo currently.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-02-12 10:00:11 +00:00
Oriol Brufau
2a3f23c78d Upgrade Stylo to 2026-02-02 (#42361)
This continues #41916

Changelog:
- Upstream:
360787fff5...7cd2a178d3
- Servo fixups:
e4d50e905e...d9831d464b

Stylo tracking issue: https://github.com/servo/stylo/issues/305

In particular, this adds support for `alignment-baseline` and
`baseline-shift`, and turns `vertical-align` into a shorthand of them.
This also introduces `vertical-align: center`.

Testing: Various tests improve. Some internal tests are updated because
they were wrong. And some fail because we don't support presentation
attributes on SVG elements.

---------

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-02-09 11:16:42 +00:00
Martin Robinson
edbce7e854 layout: Properly count characters when segmenting IFC text (#42399)
There was a bug where characters were not properly counted when
segmeting IFC text. Immediately incrementing the
`current_character_index` meant that the count was always one character
off. This character count is mainly used for drawing selections and
really matters when multiple text segments are in a single IFC. This
change fixes that by counting characters in the same way we were
counting byte indices for the text.

Testing: This change adds a Servo-specific WPT-style test. As it is
quite
difficult to reproduce the correct display in a different way, a
mismatch test
is used. Since this is mainly about appearance and is very specific to
our
implementation the test is Servo-specific.
Fixes: #42354.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-02-06 13:37:19 +00:00
webbeef
86403fa4e5 layout: get rid of some collect() calls (#42327)
Profiling a speedometer run showed significant time spent in some layout
function in vector allocations. The main change is to switch the result
of `query_box_areas()` from a Vec<T> to and iterator.

Testing: refactoring with no expected test changes

Signed-off-by: webbeef <me@webbeef.org>
2026-02-06 09:38:49 +00:00
Martin Robinson
db2a7cbe2a layout: Add support for -webkit-text-security (#42181)
This change adds support for the unspecified `-webkit-text-security`
property. All major browsers support this property. The benefit of
adding this property is that the first addition of a prefixed CSS
property will allow us to start running MotionMark.

This depends on servo/stylo#295.

Testing: Three new Servo-specific tests are added for this change. There
are a few `-webkit-text-security` tests in WPT, but they do not seem
to test basic functionality.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-31 19:12:59 +00:00
Rocketjumper
e950e5c2ca layout: Suppress soft wrap opportunities between NU/AL/AI/ID characters to respect word-break: keep-all (#42088)
This change modifies `LineBreaker` to properly handle `word-break:
keep-all` by adjusting the arguments passed to
`icu_segmenter::LineSegmenter`.

- [CSS
specification](https://drafts.csswg.org/css-text/#valdef-word-break-keep-all)
- Stylo PR : servo/stylo#289

Testing: Created a WPT test for this change, specifically:
`tests/wpt/tests/css/css-text/word-break/word-break-keep-all-011.html`.
Fixes: #42047

---------

Signed-off-by: Richard Tjokroutomo <richard.tjokro2@gmail.com>
2026-01-28 08:11:33 +00:00
Martin Robinson
c639bb1a7b layout: Do not enable letter-spacing for cursive scripts (#42165)
This is specified in CSS Text 3.

Testing: This fixes a WPT test.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-26 21:34:39 +00:00
Martin Robinson
2a7d2780b3 fonts: Store shaping output per-character rather than per-code point (#42105)
This change is a reworking of the shaping code and simplification of the
`GlyphRun` data structure.

The shaper was written between 2012 and 2014 against an early version of
Rust. It was originally written to have a single glyph entry per UTF-8
code point. This is useful when you always need to iterate through
glyphs based on UTF-8 code points. Unfortunately, this required a
tri-level data structure to hold detailed glyphs and meant that CJK
characters took over 3x the memory usage of ASCII characters. In
addition, iterating through glyphs (the most common and basic operation
on shaped text) required doing a lookup involving a binary search for
detailed glyphs (ones that had large advances or mapped to more or less
than a single UTF-8 code point).

The new design of the `GlyphStore` is instead based on `chars` in the
input string. These are tracked during layout so that the resulting
glyph output can be interpreted relatively to its original character
offset in the containing IFC. We are already dealing with IFC text on a
per-character basis for a variety of reasons (such as text
transformation and whitespace collapse). In addition, we will now able
to
implement mapping between the character offsets before and after layout
transformations of the original DOM string.

Now the penalty of more complex glyph iteration is only paid when
transforming glyph offsets to character offsets. Currently this is only
done for selections and clicking in text boxes, both of which are much
less common than layout.

This change does not properly handle selections in RTL text, though
rendering and basic selection and visual movement works (though buggy).

It does not seem like this affects the performance of shaping based on
measurement using the text shaping performance counters. This likely
means that the performance of shaping is dominated on our machines by
HarfBuzz. We noticed no performance degradation in Speedometer when run
on a M3 Mac.

Followup work:
 - Properly handle selection in RTL text.
 - Support mapping from original DOM character offsets to offsets in
   layout after text transformation and whitespace collapse. This is now
   possible.

Testing: This causes some tests to pass and a few to fail. This is
likely
due to the fact that we are handling glyphs more consistently while
shaping. Of the new failures:
- `letter-spacing-bengali-yaphala-001.html`,
`letter-spacing-cursive-001.html`, `font-feature-settings-tibetan.html`
where passing before probably because we were not applying letter
spacing to detailed glyphs. These scripts should not have letter spacing
applied to them, because they are cursive -- which we never implemented
properly. It will be handled in a a followup.
- `shaping-arabic-diacritics-001.html`: This was a false pass. The tests
verifies that Arabic diacritics are applied to NBSP. This wasn't
happening before nor after this change, but the results matched anyway.
Now they don't, but before and after are equally broken.
 - 
Fixes: #216
Part of #35540.

---------

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-01-26 12:38:04 +00:00
Martin Robinson
9c9d9c863f Rename compositing and compositing_traits to paint and paint_api (#42066)
This change finishes the big rename associated with the old
`compositing` crates. Long ago, these crates managed a compositor, like
you might find in a traditional web engine. These days, compositing is
done in WebRender so the name has stopped making much sense. Various
structs inside the crates have already been renamed and this is the
final big change necessary for the rename

Testing: This is just a rename so existing tests should cover it.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-24 09:17:35 +00:00
batu_hoang
bd97204816 layout: Support style inheritance for display: contents and <slot> elements (#41855)
In DOM traversal, `display:contents `elements don't generate boxes, but
their styles still need to apply to children.
`ModernContainerBuilder` now keeps track of `display:contents` ancestors
during traversal then apply their style to text runs.

In `InlineFormattingContextBuilder::push_text` only use same style
parent if both selected and current inline style are identical.

Testing: 

> PASS [expected FAIL]
/css/css-display/display-contents-dynamic-before-after-001.html
> PASS [expected FAIL]
/css/css-display/display-contents-dynamic-inline-flex-001-inline.html
> PASS [expected FAIL]
/css/css-display/display-contents-dynamic-inline-flex-001-none.html
> FAIL [expected PASS] /css/css-display/display-contents-fieldset.html
> PASS [expected FAIL]
/css/css-display/display-contents-first-line-002.html
> PASS [expected FAIL] /css/css-display/display-contents-inline-001.html
> PASS [expected FAIL]
/css/css-display/display-contents-inline-flex-001.html
> PASS [expected FAIL]
/css/css-display/display-contents-line-height.html

Fixes: https://github.com/servo/servo/issues/41797

cc: @xiaochengh

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-01-22 03:04:28 +00:00
Martin Robinson
4a9c95ac8e layout: Handle selection during display list construction (#41963)
Instead of handling selection in text input during box tree / fragment
tree construction, fully handle it during display list construction.
This means that when the selection changes in script, it updates
automatically in the `FragmentTree` and only a new display list is
necessary. This avoids a layout while changing the selection in text
fields.

Testing: This fixes a few rendering issues, but these are very hard
to isolate and test for. It causes one test to start failing, but this
is
because a cursor that wasn't rendered properly now starts showing
up.
Fixes: #41920.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-01-21 08:04:15 +00:00
Oriol Brufau
d0dcdb5a1a layout: Reduce visibility in components/layout/flow/inline/mod.rs (#42015)
Various methods, structs and fields were unnecessarily public. Make them
private.

Testing: Not needed, no behavior change.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-20 00:09:05 +00:00
Martin Robinson
90f568f962 fonts: Store ByteIndex as a usize (#42012)
This value is supposed to represent a UTF-8 code point offset or
length. It should never be negative and we often need to convert it to a
`usize`, so we can store it as a `usize`.

Testing: This should not change behavior so existing tests should
suffice.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-19 18:30:04 +00:00
Oriol Brufau
bc167b0ff1 layout: Simplify process_soft_wrap_opportunity() (#42008)
Some of its logic can just be replaced with a call to the method
`unbreakable_segment_fits_on_line()`.

Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-19 13:16:17 +00:00
Oriol Brufau
e58bdc03f9 layout: Track parentage in the box tree (#41884)
This patch adds a weak reference to the parent box in `LayoutBoxBase`,
so that boxes can refer to their parent. This will help during future
changes for incremental layout.

Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-01-14 14:40:12 +00:00
Martin Robinson
d5dcdd6e06 script: Add support for setting the edit point with the mouse in <input> and <textarea> (#41906)
This change updates and implements the old `query_text_index` layout
query to properly look for the glyph index of a point within a node's
`Fragment`s. This should work properly with the shadow DOM of both
`<input>` and `<textarea>` elements. In particular, multiple lines are
supported.

Caveats:
 - `<input>` and `<textarea>` that are transformed are currently not
   supported. This will happen in a followup.
 - For multi-line inputs, we should be finding the text offset of the
   nearest line that is within the block range of the click. This will
   happen in a followup.

Testing: This change adds two Servo-specific WPT-style tests. These are
Servo-specific because the behavior of clicking in text fields isn't
fully specified.

Fixes: #35432
Fixes: #10083

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Josh Matthews <josh@joshmatthews.net>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-01-14 13:23:43 +00:00
Oriol Brufau
0aae90ffe8 layout: Let floats know that margins can collapse thru phantom lines (#41812)
Phantom line boxes allow margins to collapse through them. But in the
sequential layout state we were assuming that was not the case, so we
were placing floats incorrectly.

Testing: Adding 2 tests
Fixes: #41794
Fixes: #41734

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-13 19:25:53 +00:00
Oriol Brufau
bb74a756a7 layout: Don't wrap InlineItem with ArcRefCell in LayoutBox (#41877)
Changes `LayoutBox::InlineLevel()` to have a raw `InlineItem` instead of
an `ArcRefCell<InlineItem>`. `InlineItem` is an enum where all the
options already use `ArcRefCell`, so the outer `ArcRefCell` wasn't
really necessary.

Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-01-13 16:29:31 +00:00
Martin Robinson
30fa32714e Remove Servo's custom Range implementation (#41880)
Servo `Range` type probably predates the `Range` type in the standard
library. The fact that there are two `Range` types in Servo is often
very confusing. The internal type is only used for byte indices in glyph
runs. This change removes the internal generic `Range` in favor of a
small wrapper around a native Rust `Range`.

It's likely that there are more improvements that could be done here,
such as reusing `Utf8CodePointIndex`, but this code is going to change a
lot soon and these ranges might soon be replaced with actual glyph
ranges. We are going to be looking to remove `TextByteRange` entirely.
This is just an intermediate step.

Testing: This should not change behavior and is thus covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-13 15:31:47 +00:00
Martin Robinson
e44cfc9ea5 layout: Store FontMetrics in an Arc (#41876)
This decreases the size of `TextFragment` by 66 bits and also allows
sharing empty metrics for text runs without fonts. Many `TextFragments`
should share the same font so these 66 bits should reflect reality even
though the `FontMetrics` is now stored on the heap.

Testing: This should not change behavior so is covered by existing
tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-13 12:48:55 +00:00
Martin Robinson
e80ec57069 layout: Remove outer Option on BoxSlot (#41858)
This was used before for dummy `BoxSlot`s, but we don't have those any
longer, so this can just be removed now.

Testing: This should not change observable behavior so is covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-01-12 16:24:15 +00:00
Oriol Brufau
f50530f781 layout: Consider margin of preceding block when placing abspos inside inline (#41683)
Now that we may have block-level boxes inside an inline formatting
context, absolutely positioned boxes need to consider their margin.

Testing: Adding new test
Fixes: #41625

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-09 17:26:28 +00:00
Oriol Brufau
c9613a25ec layout: Fix intrinsic size of block-in-inline not marked as depending on block constraints (#41655)
When a block-level that depends on block constraints is inside an inline
formatting context, then the intrinsic size of the entire IFC needs to
be marked as depending on block constraints too.

Testing: Adding new test
Fixes: #41630

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-03 21:22:11 +00:00
Oriol Brufau
147d5656df layout: Handle block-in-inline dependency on block constraints (#41624)
When a block-level that depends on block constraints is inside an inline
formatting context, then the entire layout of the IFC needs to be marked
as depending on block constraints too.

Testing: Adding new test
Fixes: #41623

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-02 17:49:45 +00:00
Oriol Brufau
8aa611c281 layout: Fix size of anonymous not marked as depending on block constraints (#41632)
An anonymous block box doesn't establish a containing block for its
contents. Therefore, if its contents depend on block constraints, we
need to mark it as having a size that depends on block constraints.

Testing: Adding new test
Fixes: #41629

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-02 16:59:13 +00:00
Oriol Brufau
accda387b4 layout: Stop splitting inline boxes when they contain block-levels (#41492)
We now follow the same approach as Blink: sequences of block-levels in
an inline formatting context get wrapped in an anonymous block, which is
treated as a line item. Inline ancestors are no longer split.

This means that inline elements will now generate a single inline box,
and the box tree makes more sense in general. This will help for
incremental layout.

This also means that block-level elements will now be properly affected
by effects like opacity of filters set on inline ancestors.

Recently, WebKit has done some similar work, but without the anonymous
blocks. We might also consider removing them in the future.

Google's explainer:
https://docs.google.com/document/d/15kgdIHhb9EVNup6Ir5NWwJxpzY5GH0ED7Ld3iMW3HlA/

Testing: Several tests are now passing
Fixes: #39813

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-01-01 17:41:37 +00:00
Oriol Brufau
713f79fb0b layout: Don't omit empty fragments for inline boxes (#41477)
For an inline box that had already generated some fragment, we were
omitting additional fragments that would have had no content, padding,
border nor margin. This basically happened when the inline was split by
a block, e.g. `<span><div></div></span>`.

However, other browsers agree that JS APIs like `getClientRects()`
should include these empty fragments, so remove the optimization.

Testing: Adding WPT

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-12-29 19:51:18 +00:00
Oriol Brufau
b3aee67769 layout: Use LogicalSides1D in compute_inline_content_sizes_for_block_level_boxes() (#41525)
Instead of storing the sizes of the start and end floats separately, we
can just use a `LogicalSides1D`.

Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-12-27 00:45:03 +00:00
Oriol Brufau
cbe6b03c5b layout: Take floats into account for the intrinsic sizes of an inline FC (#41513)
The computation of the min-content and max-content sizes of an inline
formatting context was ignoring floated contents.

Now we will take them into account.

Testing: One test is now passing. Note there isn't much test coverage
because there is no spec, and browsers aren't fully interoperable. More
tests should be added as part of
https://github.com/w3c/csswg-drafts/issues/9120
Fixes: #3923

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-12-25 22:40:24 +00:00
Oriol Brufau
d4810b9f26 layout: Stop clearing floats on anonymous blocks for intrinsic sizes (#41510)
When computing the intrinsic sizes of a block container which doesn't
establish an inline formatting context, block-level children which don't
establish an independent formatting context clear floats. That's because
these blocks overlap the floats instead of being placed next to them.

However, we shouldn't do that if the block-level child is an anonymous
box that establishes an inline formatting context. This matches what
other browsers do.

Testing: 4 tests are now passing
Fixes: #41280

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-12-25 19:42:17 +00:00
Simon Wülker
c8ed7a7269 layout + script: Use Iterator::sum in more places (#41501)
This change replaces a couple calls to `Iterator::fold` with
`Iterator::sum`.

Testing: No tests, this is a small cleanup PR.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2025-12-24 07:58:58 +00:00
Martin Robinson
55c4f579c9 fonts: Refactor the font fallback code a little bit and remove RwLock around FontGroup (#41449)
This change reworks the logic for finding font fallbacks to make it
simpler. I was involved with writing the code for `FontGroupFamily` and
`FontGroupFamilyMember` and I still struggle a bit with understanding
it, so I've chosen to do this. In addition, the change is in preparation
for more flexible font fallback (#41426).

The main changes here are:
 1. Move the logic for creating a new descriptor with variations into
    the Font constructor.
 2. Add some more general methods to `FontGroupFamily` such as a
    template iterator.
 3. Use `OnceLock` to avoid a convoluted code structure because of
    mutability and also having boolean "loaded" members. This is what
    `OnceLock` and `OnceCell` are for!
 4. Rename `FontGroupFamilyMember` to `FontGroupFamilyTemplate` to
    stress that it is one template of a particular `FontGroupFamily`.

Testing: This should not change behavior so is covered by existing WPT
tests.

---------

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2025-12-22 15:06:25 +00:00
Freya Arbjerg
c2e7484ef7 layout: Fix caret never rendering following a newline (#41330)
Fixes caret rendering when the caret is placed a the start of a line

```
Hello,

{here}World!
```

Also adds a utility function to the `range` crate.

Note that the rendering of the caret on many non-empty lines is still
broken due to the presence of several other bugs

Testing: I ran unit tests. Add a new reftest
Fixes: Part of #40839

---------

Signed-off-by: Freya Arbjerg <git@arbjerg.dev>
2025-12-18 22:31:25 +00:00
minghuaw
7e665f6bb9 layout: Remove centralized FontKeyAndMetrics list from the InlineFormattingContext (#41208)
As previously described in #41150 ,
`InlineFormattingContext::font_metrics` contains info that are easily
accessible via `FontRef`, and the `FontInstanceKey` is already cached in
the `FontContext`. Instead of keeping an index and obtaining these info
using the index, one should be able to obtain the same info via a
`FontRef`.

Testing: This should not change the expected behavior and the current
unit tests and WPT testcases should be sufficient.
Fixes: #41150

---------

Signed-off-by: minghuaw <michael.wu1107@gmail.com>
2025-12-15 16:52:43 +00:00
Martin Robinson
e9f372ca41 layout: Add a BaseFragment that contains shared properties of all Fragment types (#41282)
This reverts commit f5ecb34577 and also
merges more shared properties into `BaseFragment`. The goal here is to
eventually also store a reference to the parent fragment in this
`BaseFragment` as well.

Testing: This should not change behavior in an observable way, so is
covered by WPT tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2025-12-15 15:17:13 +00:00
Oriol Brufau
b285923fdc layout: Preserve cached intrinsic inline sizes in more cases (#41160)
If a box has layout damage, but the intrinsic contribution of some
ancestor doesn't depend on its contents, then we don't need to clear the
cached intrinsic sizes of further ancestors.

Testing: No behavior change, just an optimization. It would be good to
have unit tests that check we don't do unnecessary work, but leaving
this for the future.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2025-12-11 00:31:27 +00:00
Oriol Brufau
f5ecb34577 layout: Merge BaseFragment and BaseFragmentInfo (#41177)
These structs used to be different when they were added in #29699:
`BaseFragment` had a `debug_id` field, while `BaseFragmentInfo` didn't.
But this field was later removed in #35001, so the structs became
identical.

Therefore, this patch just unifies them. I'm picking `BaseFragmentInfo`
as the name, since it was the most frequently used of the two.

Testing: Not needed, no change in behavior

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-12-10 13:48:43 +00:00
Anonmiraj
b207be05d9 change some allows to expects (#41040)
Changed some allow to expects and removed the unfulfilled expectations.

Testing: Refactor
Part of: #40383

Signed-off-by: anonmiraj <nabilmalek48@gmail.com>
2025-12-05 07:23:32 +00:00
Freya Arbjerg
adc16980c4 Fix duplicate carets in <textarea> (#40837)
This fixes a bug where <textarea> would render with many carets. Reports
vary, but in my case it would would be at the start and end of every
"word", as long as those cursors came before the actual cursor.

The actual cursor would render normally, except that it does not render
immediately after a line break. This appears to be a separate issue.

The issue occurs because `push_glyph_store_to_unbreakable_segment` gets
passed too long of a range. This bypasses the check here:
bbbfe6d299/components/layout/flow/inline/mod.rs (L1438-L1440)

Testing: Tested with `./mach test-unit`. No new tests were added.
Perhaps someone has an idea of how one could test this. Also tested
manually in multiple scenarios, including with a text input. I was not
able to get Servo to soft wrap in a textbox, maybe Servo does not
support that currently. Ultimately I am not super confident about this
solution as it interacts with line wrapping, and I would like to hear
some feedback
Fixes: https://github.com/servo/servo/issues/40075

Signed-off-by: Freya Arbjerg <git@arbjerg.dev>
2025-11-30 18:58:10 +00:00
Oriol Brufau
8b5333b38e layout: Cleanup for replaced elements with widgets (#40769)
This cleans up the changes done in #40578.

In particular, it removes the unnecessary `Contents::ReplacedWithWidget`
since we can handle that with `Contents::Replaced`. It also removes
`IndependentFormattingContextContents::ReplacedWithWidget` in favor of
`IndependentFormattingContextContents::Replaced`, by adding an optional
parameter for the widget.

That ensures that the behavior of replaced elements won't accidentally
diverge dependign on whether they have a widget. For example, #40578
forgot to handle `ReplacedWithWidget` in
`tentative_block_content_size()`.

Additionally, this removes the hardcoded especial behavior for `<video>`
that was added in `ServoThreadSafeLayoutElement::with_pseudo()`.

Testing: Adding a reftest
Fixes: #40708
Fixes: #40770

---------

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2025-11-20 20:52:39 +00:00