Commit Graph

109 Commits

Author SHA1 Message Date
Martin Robinson
ac9f747509 layout: Remove some newline-related dead code from shaping (#44485)
In #44436, I neglected to remove some hard line break-related code from
the shaping code. This code is effectively dead code now that
hard line breaks never make it to this code path, so it can safely be
removed.

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

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-25 08:38:45 +00:00
Martin Robinson
25ef3121a7 layout: Split hard line breaks into their own TextRunItem (#44436)
This change makes it so that hard line breaks are not shaped during
inline formatting context creation. Instead they are a separate variant
of a new `TextRunItem` enum. This will make it easier to start
linebreaking later in the flow of inline layout.

Testing: This fixes three WPT tests, likely because we are now properly
using the BiDi level of BiDi control characters. One test starts
failing, but
it depends on tab rendering, which we do not currently support properly.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-23 19:29:20 +00:00
Martin Robinson
09ef444d63 layout: Add placeholders for text carets after finishing a line (#44370)
Instead of readily adding a strut for text carets when processing a line
break, wait until a line is finished. This allows placing a strut on the
final line. In addition to fixing #41338 this will also make it possible
to handle line breaks as a separate kind of text run item so that we can
preserve shaping results between layouts.

In addition, some changes are made in script to ensure that these
placeholders
are never placed for non-textual input elements.

Testing: This change adds a Servo-specific WPT test. Caret rendering
isn't specified.
Fixes: #41338

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-22 06:51:54 +00:00
Martin Robinson
bec5a6ce37 layout: Simplify the way characters are iterated when segmeting text (#44400)
This is a minor adjustment from #44276 that makes the way characters are
iterated easier to read.

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

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-21 21:11:48 +00:00
Narfinger
a908081352 chore: Remove some clippy complains for 1.95 (#44276)
This removes some complains that clippy will have in 1.95.
As this is mostly just match guards, it doesn't update MSRV.

Testing: This is equivalent exchanges, so current WPT would find
anything.

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2026-04-17 19:55:17 +00:00
Martin Robinson
789974975f layout: Remove dependency on xi-unicode (#44303)
This functionality already provided by `icu_properties` which is already
in our dependency graph, so this change allows us to remove one
dependency.

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

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-17 13:09:44 +00:00
Oriol Brufau
9ef8fe5e42 layout: Avoid unnecessary borrow in TextRun::segment_text_by_font() (#44279)
No need to borrow `self.inline_styles.style` when the result is already
available as a parameter of the function.

Also, avoid calling `parent_style.get_font()` multiple times.

Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-04-16 17:17:37 +00:00
Martin Robinson
8da073a80c layout: Cache SameFormattingContextBlock layout results (#42904)
Add a cache for `SameFormattingContextBlock`s: A new kind of cache
is added which allows reusing fragments for
`SameFormattingContextBlock` (the majority of block level elements).
This can reduce the amount of fragment tree construction during
incremental layout (although with a 40 byte overhead per
`SameFormattingContextBlock`).

Testing: This change adds a new Servo-specific WPT test that verifies
that
the minimal number of `Fragment`s are rebuilt after an incremental
layout.
Previously, when running `flexbox-deeply-nested-column-flow.html` from
`tests/blink_perf_tests/perf_tests/layout/`, performance would degrade
with successive test runs. This is due to how slow inline layout is.
This change
removes that progressive degradation, though we may be able to remove
this cache with improvements to inline layout.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-04-16 12:04:39 +00:00
Martin Robinson
45972e07ab script: Rewrite the layout DOM wrappers (#44114)
This change reworks the layout DOM wrappers so that they are simpler and
easier to reason about. The main changes here:

**Combine layout wrappers into one interface:**

 - `LayoutNode`/`ThreadSafeLayoutNode` is combined into `LayoutNode`:
   The idea here is that `LayoutNode` is always thread-safe when used in
   layout as long as no `unsafe` calls are used. These interfaces
   only expose what is necessary for layout.
 - `LayoutElement`/`ThreadSafeLayoutElement` is combined into
   `LayoutElement`: See above.

**Expose two new interfaces to be used *only* with `stylo` and
`selectors`:**

`DangerousStyleNode` and `DangerousStyleElement`. `stylo`
and `selectors` have a different way of ensuring safety that is
incompatible with Servo's layout (access all of the DOM tree anywhere,
but ensure that writing only happens from a single-thread). These types
only implement things like `TElement`, `TNode` and are not intended to
be used by layout at all.

All traits and implementations are moved to files that are named after
the struct or trait inside them, in order to better understand what one
is looking at.

The main goals here are:

 - Make it easier to reason about the safe use of the DOM APIs.
 - Remove the interdependencies between the `stylo` and `selectors`
   interface implementations and the layout interface. This helps
   with the first point as well and makes it simpler to know where
   a method is implemented.
 - Reduce the amount of code.
 - Make it possible to eliminate `TrustedNodeAddress` in the future.
 - Document and bring the method naming up to modern Rust conventions.

This is a lot of code changes, but is very well tested by the WPT tests.
Unfortunately, it is difficult to make a change like this iteratively.
In addition, this new design comes with new documentation at
servo/book#225.

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

Signed-off-by: Martin Robinson <mrobinson@fastmail.fm>
2026-04-12 04:22:06 +00:00
Abbas Olanrewaju Sarafa
fbb37acf15 layout: Resolve word-spacing percentages against computed font-size (#44031)
Changes ```word-spacing``` to resolve percentages against the computed
```font-size```, instead of the size of a space.

Testing:
```tests/wpt/tests/css/css-text/word-spacing/word-spacing-percent-001.html```
is now passing

Fixes: #43997

---------

Signed-off-by: Sabb <sarafaabbas@gmail.com>
2026-04-10 16:17:22 +00:00
Oriol Brufau
e9fbed1d74 fonts: Use ICU's Language instead of Stylo's XLang (#44057)
Testing: Not needed, no behavior change

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-04-09 10:53:44 +00:00
Martin Robinson
b8ef264268 layout: Integrate more details into FontAndScriptInfo (#43974)
This will be needed for ensuring that shaped text is valid during
relayout. It duplicates some information, but the hope is that when all
of these change are done, there will be many fewer individual shaped
segments of text.

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

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-08 21:37:44 +00:00
Abbas Olanrewaju Sarafa
02a350d864 layout: Rename confusing ``SequentialLayoutState::collapse_margins`` (#43978)
Renamed ```SequentialLayoutState::collapse_margins``` to
```commit_margin``` in ```float.rs```, ```mod.rs``` &
```inline/mod.rs```

Testing: No testing required - just renaming. Compiles successfully.
Fixes: #43941

Signed-off-by: Sabb <sarafaabbas@gmail.com>
2026-04-06 19:10:28 +00:00
Martin Robinson
82c2f1434e fonts: Clean up application of word-spacing (#43899)
Let `word_spacing` be optional in `ShapingOptions` like
`letter_spacing`. In addition, send `None` for Canvas rendering instead
of the width of the space character. It is supposed to represent *extra*
space added between words. Finally, remove duplicated code that applied
word and letter spacing for the fast shaper. This can just reuse the
code from the Harfbuzz shaper.

Testing: This causes some canvas tests to start passing.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
2026-04-06 09:20:34 +00:00
Sharan Poojari
57b86edb89 layout: Fix line breaking opportunities for Chinese and Japanese (#43744)
Enable `CJK-aware` line breaking in inline layout so Chinese/Japanese
text gets proper wrap opportunities instead of overflowing containers.

Testing: some WPT are now passing

---------

Signed-off-by: SharanRP <z8903830@gmail.com>
2026-04-05 23:15:54 +00:00
Martin Robinson
322dfd0115 layout: Combine some common text segment data into FontAndScriptInfo (#43653)
This starts splitting out commonly shared data into a new data structure
that will make it easier to compare old shaping results to new ones in
the future. This eliminates some memory usage during inline layout, but
adds 8 bytes per segment (usually one per inline box). We hope to
recover this memory by storing one `GlyphStore` per `TextRunSegment`,
which
should recover 16 bytes.

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

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-03-30 13:15:23 +00:00
Martin Robinson
17a7ee037b layout: Integrate ::first-letter support into the InlineFormattingContextBuilder (#43692)
This change merges the first letter implementation into the
`InlineFormattingContextBuilder` as its more associated with inline
layout. The downside is that, due to ownership issues in Rust, the
builder must be unwrapped after ensuring it. Additionally, ensure
that `::first-letter` boxes are properly stored in a box slot meaning
that restyles work properly.

This change also makes a few small cleanups to the `first_letter_range`
function in addition to moving it to the inline code.

Testing: This does not change behavior and so should be covered by
existing tests.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Luke Warlow <lwarlow@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-03-30 11:40:09 +00:00
Oriol Brufau
80c57f80d4 layout: Fix justify-self: auto on block-in-inline (#43625)
We were resolving `justify-self: auto` according to the `justify-items`
of the containing block. However, the CSSWG resolved to instead use the
parent box; and if the parent is an inline box, then `justify-items`
doesn't apply, so `justify-self: auto` behaves as `normal`.

Testing: Adding WPT test
Fixes: #43624

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-03-25 06:05:03 +00:00
Oriol Brufau
62b2eefc7c layout: Allow block-levels inside an inline directly, without wrapper (#43586)
When encountering a block-level inside an inline box, we would wrap it
(together with other block-level siblings, if any) inside an anonymous
block box.

That was complicating the logic for no real benefit, so just get rid of
these anonymous wrappers, and allow block-levels as direct children of
inlines.

This aligns Servo with WebKit, which did the same refactoring:
https://commits.webkit.org/304357@main

Blink still generates these wrappers, but its design doc acknowledges
that "the anonymous box is not strictly required".

Testing: No existing test fails. But note that, while minimal, this may
have some observable implications. For example, as an accidental
side-effect of #41492, Servo aligned with the CSSWG resolution in
https://github.com/w3c/csswg-drafts/issues/11462, but now we will revert
to the previous behavior. I will address it in a follow-up, and add a
test.

Fixes: #41636

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-03-24 14:31:42 +00:00
Vinayak Sharma
d47711e849 layout: Fix static position of abspos nested within a block-in-inline (#43084)
Fixes incorrect static positioning of absolutely positioned elements
nested within a block that has `margin-top` inside an inline element.
Added two lines of code to `AnonymousBlockBox::layout_into_line_items`
in components/layout/flow/inline/mod.rs:
- We snapshot the current positioning_context_length before laying out
the block.
- We call `adjust_static_position_of_hoisted_fragments` on the
positioning_context after the block fragment is placed by the state.

Testing: 
Tested the fix using the manual reproduction HTML provided in the issue,
Also ran the existing WPT tests in `tests/wpt/tests/css/css-position/`
to ensure there are no regressions.


Fixes: #42890

---------

Signed-off-by: vinayak sharma <vinayaks0111@gmail.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-03-23 17:27:44 +00:00
rtjkro
afa4b524ab font: Consider lang attribute when shaping (#43447)
Fonts: Added `language` field in the struct `ShapingOptions` so Harfbuzz
can also consider language when shaping glyphs.

Testing: Existing WPT. Most recent try run:
[link](https://github.com/RichardTjokroutomo/servo/actions/runs/23334049878)

2 new passes, 5 new fails.

Failures:
- `/css/css-text-decor/text-emphasis-punctuation-2.html` should be a
false positive since `text-emphasis` shorthand hasn't been supported on
stylo yet.
- Not quite sure about this, but
`/css/css-text/text-spacing-trim/text-spacing-trim-quote-001.html?class=halt,htb&amp;lang=ja`
tests `text-spacing-trim` default behavior on `JA` texts. Since this
property is not defined in Stylo, I believe that this property's
behavior (including default) hasn't been considered in Servo. So Servo
previously passing the test should be a false positive. As a side note,
this test also fails on Firefox.
- `/html/canvas/element/manual/text/canvas.2d.lang.dynamic.html`,
`/html/canvas/element/manual/text/canvas.2d.lang.html`,
`/html/canvas/element/manual/text/canvas.2d.lang.inherit.disconnected.canvas.html`
fail because canvas' experimental [`lang`
attribute](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lang)
hasn't been supported yet.

credits to @mrobinson for figuring out the reason for last 3 failing WPT
tests!

Fixes: #41825

---------

Signed-off-by: Richard Tjokroutomo <richard.tjokro2@gmail.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-03-23 08:39:53 +00:00
Euclid Ye
cae0752676 cargo: Rename workspace-local library starting with b to servo_* (#43552)
Follow up of #43526. This addresses Nico's comment:
https://github.com/servo/servo/pull/43526#issuecomment-4104953308

- `bluetooth_traits` -> `servo_bluetooth_traits`
- `base` -> `servo_base`
- `bluetooth` -> `servo_bluetooth`
- `background_hang_monitor` -> `servo_background_hang_monitor`

Testing: This should not change any behaviour.

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
2026-03-23 08:26:49 +00:00
Simon Wülker
c0ff7c1fc9 Everywhere: Remove instances of clippy::redundant-clone (#43212)
This change fixes all instances where
[`clippy::redundant-clone`](https://rust-lang.github.io/rust-clippy/master/index.html?groups=complexity%2Ccorrectness%2Cnursery%2Csuspicious&levels=allow#redundant_clone)
would trigger. It's allowed by default

I've also changed the lint to warn-by-default for servo.

Testing: Covered by WPT

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
2026-03-12 13:28:21 +00:00
minghuaw
3a50ff1c10 layout: Add initial support for first-letter pseudo element (#43027)
This and the accompanying stylo PR
(https://github.com/servo/stylo/pull/320) is a partial implementation of
the `first-letter` pseudo element.

Testing: Existing WPT tests. There is one WPT test
(fcp-typographic-pseudo.html) that servo currently fails would timeout
with this PR because opacity of `TextFragment` is taken from the
`InlineFormattingContext` instead of the pseudo element.
Fixes: #43008 
Part of: #15413

---------

Signed-off-by: Minghua Wu <michael.wu1107@gmail.com>
Signed-off-by: minghuaw <michael.wu1107@gmail.com>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Xiaocheng Hu <xiaochengh.work@gmail.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
2026-03-11 15:30:15 +00:00
Freya Arbjerg
4ec46aaadc layout: Support text carets on empty lines in <textarea> (#42218)
This PR modifies the text layout in such a way that text carets can
render on empty lines (other than the first if the textarea is empty).
Zero-length segments are added to support caret rendering. The last
line, if it is without content, does not currently render a caret
correctly

This corresponds to cases 0, 1, and 3 in #41338

Testing: WPT subset `tests/wpt/tests/css/css-text/` ran as expected, one
internal WPT test was added
Fixes: Partially addresses https://github.com/servo/servo/issues/41338

---------

Signed-off-by: Freya Arbjerg <git@arbjerg.dev>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
2026-03-09 07:06:27 +00:00
Oriol Brufau
42d228dcc7 Upgrade Stylo to 2026-03-01 (#43045)
This continues  #42361

Changelog:
- Upstream:
7cd2a178d3...74ddab4091
- Servo fixups:
a5eabbdfc2...ecf17b5425

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

In particular, this includes some performance optimizations, improves
`attr()`, and adds support for `none` keywords in `clamp()`.

Testing: Some WPT tests pass.

---------

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
2026-03-06 23:39:55 +00:00
Martin Robinson
027910e544 layout/fonts: Mitigate races related to quirks mode and fallback fonts (#42960)
This change includes two related fixes for flakiness revealed by #42847.

1. When caching fonts, try harder to not re-cache a font. This could
   happen due to a race condition with the read-write lock on the font
   cache. This change improves memory usage in some cases as it prevents
   a font from being loaded twice.
2. Incorporate both the strut size and the block size contribution of a
   previously unused font in an inline container. Before the code was
   incorporating one or the other. In addition, *only* incorporate the
   block contribution of a previously unused font if its metrics
   meaningfully differ from the container's default font. This was the
   case triggered by failing to have the fix in part one of the change.

Testing: This fixes flakiness in an internal WPT-style test.
Fixes: #42908.

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-03-03 21:10:05 +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
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
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
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
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
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
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