Add memory reporter integration for the fragment and box trees that are
persisted in the layout thread.
Testing: Looked at the numbers for https://servo.org and
https://html.spec.whatwg.org/. The former was very small, but the latter
was 700mb.
---------
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
Start storing a link to laid-out `Fragment`s in `LayoutBoxBase`, so that
these are accessible for queries and eventually for incremental layout.
Some box tree data structures lacked a `LayoutBoxBase`, such as table
tracks and table track groups[^1].
In addition, start using these `Fragment`s for queries instead of
walking the entire `Fragment` tree. Currently, this isn't possible for
most queries as `Fragment`s do not cache their absolute offsets (which
are often necessary). This change uses the new box tree `Fragment`s for
most resolved style queries.
[^1]: Note that only rows and row groups store `Fragment`s as columsn
and
colgroups do not produce any.
Testing: This is covered by existing tests.
Fixes: This is part of #36525.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This reduces the size of `BoxFragment` by around 20 bytes.
Testing: This just changes the layout of a data structure, so is
covered by existing WPT tests.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This is a duplicate of the style stored on the marker's `LayoutBoxBase`.
Testing: This is covered by existings WPT tests.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Use `is_zero()` instead of comparing with `Au::Zero()` for zero checks.
Testing: This change does not cause behaviour change, a test is not
necessary.
Fixes: #36300
---------
Signed-off-by: Barigbue <barigbuenbira@gmail.com>
This starts to enable the fragment cache for all layout modes, except
grid. The main tricky bit here is that update points are absolutes and
these need to be laid out again in their containing blocks. We punt a
little bit on this, by forcing ancestors of update points to rebuild
their Fragments. This is just the first step.
Testing: We do not currently have layout performance tests, but will try
to run some tests manually later. Behavior is covered by the WPT.
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
We were allowing `align-self: stretch` to stretch flex items whose cross
size behaves as `auto`, including cyclic percentages.
However, https://github.com/w3c/csswg-drafts/issues/4525 resolved that
stretching should only happen when the cross size computes to `auto`.
So this patch exposes this information in `ContentBoxSizesAndPBM`, and
refactors the flexbox stretching logic.
Fixes: #36285
Testing:
- `/css/css-flexbox/quirks-auto-block-size-with-percentage-item.html`
- `/css/css-flexbox/stretch-requires-computed-auto-size.html`
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
According to CSS2, the bottom margin of a block container can collapse
with the bottom margin of its last in-flow child if `height` computes to
`auto`.
However, according to CSS Sizing, that was "legacy spec prose" and
should be interpreted as "behaves as `auto`".
Therefore, cyclic percentages and intrinsic keywords like `min-content`
shouldn't prevent margin collapse, because they behave as `auto`.
This change aligns Servo with Gecko and Blink, but diverges from WebKit.
https://www.w3.org/TR/CSS22/box.html#collapsing-marginshttps://www.w3.org/TR/css-sizing/#behave-auto
Testing:
-
`tests/wpt/tests/css/css-sizing/margin-collapse-with-indefinite-block-size-001.html`
-
`tests/wpt/tests/css/css-sizing/margin-collapse-with-indefinite-block-size-002.html`
-
`tests/wpt/tests/css/css-sizing/margin-collapse-with-indefinite-block-size-003.html`
-
`tests/wpt/tests/css/css-sizing/margin-collapse-with-indefinite-block-size-004.html`
-
`tests/wpt/tests/css/css-sizing/margin-collapse-with-indefinite-block-size-005.html`
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
- Remove the last remaining Servo-specific PseudoElement enum from
layout. This was made to select `::before` and `::after` (both eager
pseudo-elements), but now `traverse_pseudo_element` is called
`traverse_eager_pseudo_element` and should work on any eager pseudo
element.
- Expose a single way of getting psuedo-element variants of
ThreadSafeLayoutElement in the Layout DOM, which returns `None` when
the pseudo-element doesn't apply (not defined for eager
pseudo-elements or when trying to get `<details>` related
pseudo-elements on elements that they don't apply to).
- Ensure that NodeAndStyleInfo always refers to a node. This is done by
making sure that anonymous boxes are all associated with their
originating node.
These changes are prepatory work for implementation of the `::marker`
pseudo-element as well as ensuring that all anonymous boxes can be
cached into the box tree eventually.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This replaces `IndependentLayout` with `CacheableLayoutResult` and
stores it in `LayoutBoxBase` so it can be reused when we need to lay out
a box multiple times.
This is a generalization of the caching that we had for flexbox, which
is now removed in favor of the new one.
With this, the number of runs per second in the Chromium perf test
`flexbox-deeply-nested-column-flow.html` are multiplied by 3.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
Spec: https://drafts.csswg.org/css-sizing-3/#funcdef-width-fit-content
It's similar to the `fit-content` keyword but, instead of clamping the
stretch size between `min-content` and `max-content`, it clamps the
provided argument.
So now that we support `fit-content`, it's quite straightforward to add.
It's just not completely clear what should happen when the argument has
a cyclic percentage, so this may need some further adjustments depending
on the outcome of https://github.com/w3c/csswg-drafts/issues/11805
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
`block_size_is_zero_or_intrinsic()` was always returning true for
`stretch`. This function is used for the margin collapse heuristics
in block layout, so we were considering that an empty element with
`height: stretch` would self-collapse.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
`Size::resolve_for_min()` had an `Au` parameter, representing the value
to be used for an automatic minimum size. However, this amount isn't
trivial to compute in flexbox, so this patch changes the parameter to a
function that can be called lazily.
Note flexbox isn't currently using `Size::resolve_for_min()`, but it
will in #35961.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
* Migrate to 2024 edition
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Allow unsafe_op_in_unsafe_fn lint
This lint warns by default in the 2024
edition, but is *way* too noisy for servo.
We might enable it in the future, but not now.
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Compile using the 2024 edition
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
The CSSWG resolved that `block-size: stretch` on a block-level box
stretches the margin box to fill the parent. However, if the parent
doesn't have padding nor border, and doesn't establish an independent
formatting context, then we assume that the margins will collapse.
Therefore, we treat the margins as zero when resolving the stretch size,
regardless of whether they will actually end up collapsing.
https://github.com/w3c/csswg-drafts/issues/11044#issuecomment-2599101601https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This function showed up as a top producer of allocations
(around 10% of all allocations).
Allocating the vector once upfront and using
`collect_into_vec` removes any intermediate allocations.
This approach is also recommended by the rayon documentation:
https://docs.rs/rayon/1.10.0/rayon/iter/trait.ParallelIterator.html#method.collect
Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
* Use 2024 style edition
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Reformat all code
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
We were always treating an indefinite `stretch` as the automatic size.
This instead treats it as `0px` on min sizing properties, and as `none`
on max sizing properties, aligning with Blink and this recent CSSWG
resolution: https://github.com/w3c/csswg-drafts/issues/11006
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
A box is usually sized by the formatting context in which it participates.
However, tables have some special sizing behaviors that we implemented
with a `content_inline_size_for_table` override.
However, breaking the assumptions of the formatting context isn't great.
It was also bad for performance that we could try to layout a table
among floats even though it wouldn't en up fitting because of a larger
min-content size.
Therefore, this changes the logic so that formatting contexts use some
special sizing for tables, and then tables only override that amount
when there are collapsed columns. Eventually, we should try to remove
that case too, see https://github.com/w3c/csswg-drafts/issues/11408
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
When we try to place a table next to some floats, and it doesn't fit
vertically, then we try again considering more floats. And as an
optimization we were using the previous width of the table as a minimum.
However, this was wrong, because the table might accept a smaller width
when the available space is smaller than beforehand.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
We were just checking the computed `display` style, but a few HTML
elements can ignore some `display` values and generate a different
kind of box.
This uses the right check, though for now there is no difference in
behavior since we don't have special HTML layouts.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Some layouts like table need some style overrides. We were handling this
in `ComputedValuesExt`, but it was messy, unreliable and too limited.
For example, we were assuming that a style with `display: table` would
belong to a table wrapper box or table grid box. However, certain HTML
elements can ignore their `display` value and generate a different kind
of box. I think we aren't doing that yet, but we will need this.
Also, resolving the used border of a table needs layout information,
which we don't have in `ComputedValuesExt`. This patch will allow to
improve border collapsing in a follow-up.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
There were two kinds of layout tracing controlled by the same debugging
option:
- modern layout: Functionality that dumped a JSON serialization of the
layout tree before and after layout.
- legacy layout: A scope based tracing that reported the process of
layout in a structured way.
I don't think anyone working on layout is using either of these two
features. For modern layout requiring data structure to implement
`serde` serialization is incredibly inconvenient and also generates a
lot of extra code.
We also have a more modern tracing functionality based on perfetto that
we have started to use for layout and IMO it's actually being used and
more robust.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
It used to be an `AuOrAuto`, turning it into a `SizeConstraint` allows
passing the information about the min and max constraints when the
containing block doesn't have a definite block size.
This will be useful for table layout.
Note that in most cases we were already constructing the containing
block from a `SizeConstraint`, but we were calling `to_auto_or()` to
turn it into an `AuOrAuto`.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Push the interior mutability into enum variants of `Fragment`, so that
they can be cloned. This saves memory in the `Fragment` tree as the
`Fragment` enum is now a relatively wee 16 bytes and the interior parts
can be a variety of sizes. Before, every `Fragment` was the size of the
biggest kind (`BoxFragment` - 248 bytes).
This a step on the way toward incremental layout.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
A box is usually sized by the formatting context in which it participates.
However, tables have some special sizing behaviors, and these were in
conflict.
Instead of letting tables attempting to re-resolve their inline table,
which failed to e.g. take flex properties into account or resolve sizing
keywords correctly, now tables will trust the inline size determined by
the parent. They will only floor it by the min-content size, and maybe
shrink the final size due to collapsed columns.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
When laying out a block-level box that avoids floats, if we know that
its size doesn't depend on the available space, we can take a fast path
and only lay it out once. If its size depends on the available space,
we may have to lay it out multiple times, which can be slower.
This patch improves the check for this dependency on the available space.
For example, `min-width: 200px; width: 100px; max-width: stretch` was
previously considered to depend on the available space because of
`max-width`. However, `max-width` is irrelevant when the min size is
greater than the preferred size.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
There is an early return for independent formatting contexts, so at this
point we don't need to handle them.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
`width` and `max-width` typically treat expressions with percentages as
their initial value, but for the min-content contribution of replaced
elements, they should instead be treated as zero.
https://drafts.csswg.org/css-sizing-3/#replaced-percentage-min-contribution
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Block layout uses some heuristics to guess whether margins are separated
by clearance and then don't collapse. These heuristics now take the
min-content, max-content, fit-content and stretch sizing keywords into
account.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
In order to compute the inline min-content and max-content contributions
of an anonymous block, we were finding its min-content and max-content
inline size with a SizeConstraint coming from the block size of the box.
However, anonymous blocks do not establish a containing block for their
contents, so this patch uses a SizeConstraint from the block size of the
containing block.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
If a table element had e.g. `width: 0px`, we were assuming that this was
its intrinsic min-content and max-content contributions.
However, tables are always at least as big as its min-content size, so
this patch floors the intrinsic contributions by that amount.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
in each layout logic, in order to correctly resolve sizing keywords.
This patch adds a new `Sizes` struct which holds the preferred, min and
max sizing values for one axis, and unifies the logic to resolve the
final size into there.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Adds support for min-content, max-content, fit-content and stretch,
for the case that was missing from #34568: block-level elements that
establish an independent formatting context, when there are floats.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Manage `<iframe>` size updates in `Window`. In addition to removing
duplicated code, this will allow setting `<iframe>` sizes synchronously
on child `Pipeline`s of the same origin in the script process in a
followup change. The goal is remove flakiness from `<iframe>` sizing.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Adds support for min-content, max-content, fit-content and stretch,
for block-level elements that don't establish an independent formatting
context, and for block-level elements when there is no float.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Several structs and enums had a `inline_content_sizes()` method, but it
wasn't clear which ones would try to cache the result, and which ones
would always compute it.
Therefore, this performs some clarifying renaming:
- Cached ones stay as `inline_content_sizes()`
- Uncached ones become `compute_inline_content_sizes()`
Also, to simplify calls to `LayoutBoxBase::inline_content_sizes()`,
`compute_inline_content_sizes()` is moved into a new trait.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Block-level elements that establish an independent formatting context
(or are replaced) need to avoid overlapping floats.
In the non-replaced case, we have two different subcases, depending on
whether the inline size of the element is known. This patch makes them
share more logic.
Then `solve_clearance_and_inline_margins_avoiding_floats()` would only
be used in the replaced case, so it's removed, inlining its logic.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
The refactoring in 264c0f972f stopped
caching the `inline_content_sizes()` calls from:
- `FlexItemBox::layout_for_block_content_size()`
- `IndependentFormattingContext::layout_float_or_atomic_inline()`
- `TaffyContainerContext::compute_child_layout()`
Also, the call from `OutsideMarker::layout()` was never cached.
This patch caches all of them.
It's not clear at all which `inline_content_sizes()` are cached and
which aren't, so I plan to improve the situation in a follow-up.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This might make caching these values a bit easier in the future.
Correcting the visibility of `ContainingBlock` also exposed some new
rustc and clippy warnings that are fixed here.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This allows `SameFormattingContextBlock` to cache inline content sizes
and will eventually allow it to participate in incremental layout.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Add a new struct `LayoutBoxBase`, that will be used throughout the box
tree. The idea of this struct is that we have a place to consistently
store common layout information (style and node information) and also to
cache layout results such as content sizes (inline and maybe later box
sizes) and eventually layout results.
In addition to the addition of this struct,
`IndependentFormattingContext` is flattened slightly so that it directly
holds the contents of both replaced and non-replaced elements.
This is only added to independent formatting contexts, but will later be
added to all block containers as well.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
* Refactor computation of preferred aspect ratios
Computing min/max-content sizes required a ContainingBlock in order to
resolve the padding and border when determining the preferred aspect
ratio. However, all callers already knew the padding and border, so they
can compute the ratio themselves, and pass it directly instead of
the ContainingBlock.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
* Put preferred aspect ratio into ConstraintSpace
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
---------
Signed-off-by: Oriol Brufau <obrufau@igalia.com>