- 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>
* 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>
We were ignoring `table-layout: fixed` both for `inline-size: auto` and
`inline-size: max-content`. However, the CSSWG resolved that fixed table
layout should be triggered except when `inline-size` is `auto`.
https://github.com/w3c/csswg-drafts/issues/10937#issuecomment-2669150397
Blink has already adopted this change, and they modified the WPT
`/css/css-tables/fixed-layout-2.html` accordingly. Here I'm doing some
further cosmetic cleanups to the test.
Signed-off-by: Oriol Brufau <obrufau@igalia.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>
* Update to rust 1.85
This is needed for cargo-deny
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Upgrade crown
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Clippy fixes
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Re-upgrade cargo-deny to 0.18
Keeping it locked to 0.18 just in case they
update their required rustc version again
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
`Table::create_spanned_slot_based_on_cell_above()` was performing the
subtraction `self.slots.len() - 2`, which could theoretically result
in underflow if `self.slots.len()` is 0 or 1.
That shouldn't have been possible in practice, but it may be worth
addressing, to improve code robustness. So this patch:
- Switches to `self.current_y()?.checked_sub(1)?`, which is safe and
is easier to understand.
- Moves `create_spanned_slot_based_on_cell_above()` to `TableBuilder`,
since `current_y()` is there, and the method is only used when
building the table anyways.
- Ensures that both callers use `expect()` to assert that the method
returned a value.
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>
We were painting collapsed borders without taking into account that some
tracks might have been "removed" by `visibility: collapse`.
This just sets the sizes of these tracks to zero. Note this implies that
collapsed borders may overlap each other, or overlap cell contents, but
this seems to match Blink.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
The specification doesn't say how to deal with percentages when
determining the minimum and maximum size of a table grid, so follow the
approach that Chromium uses.
Essentially, figure out the "missing" percentage from the non-percentage
columns and then use that to work backwards to fine the size of the
percentage ones.
This change is larger than one might expect, because this percentage
approach shouldn't happen for tables that are descendants of a flex,
grid or table container (except when there is an interceding absolute).
We have to pass this information down when building the box tree. This
will also make it easier to improve propagated text decorations in the
future.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
If `width` is indefinite, treat the outer size as zero, instead of
treating the content size as zero and then adding padding and borders.
Also, we don't want a default minimum of zero to get added padding and
borders, and then defeat the point baove. So just ignore minimums and
maximums.
That seems to roughly match what other browsers do, but as usual, the
details are not interoperable, e.g. some browsers may obey min or max
sizing properties in some cases.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
For example, a cell with `rowspan="2"` can cross a collapsed border that
was set on the rows. Now the slice of this row border that is crossed
by the cell will be hidden.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This used to be a struct that had a list of `CollapsedBorder`s, and the
maximum border width among that list.
However, this cached maximum border width was only used when resolving
the borders of the table. Therefore, for all grid lines except the first
and last ones per axis, this data was useless.
Also, in order to address #35123 I plan to retroactively zero out some
collapsed borders, which could invalidate this cache.
So this patch just removes the field and turns `CollapsedBorderLine`
into an alias of `Vec<CollapsedBorder>`.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Even though when painting the collapsed borders we were using the right
size, when sizing the table we were treating cells as having a border
of half the maximum border size along the entire grid line.
Now we only take the maximum among the borders adjacent to the cell.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
For a table wrapper in collapsed-borders mode we were just halving the
border widths from the computed style. However, it needs to actually
receive half of the resulting collapsed border, which can be bigger.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
https://www.w3.org/TR/CSS21/tables.html#border-conflict-resolution
> If border styles differ only in color, then a style set on a cell wins
> over one on a row, which wins over a row group, column, column group
> and, lastly, table.
We were actually using the opposite order.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
We previously tried to implement the [table specification algorithm] for
distributing the inline size of cells with `rowspan` > 1. This algorithm
isn't great though, so this change starts switching Servo to using an
algorithm like the one used in LayoutNG from blink. This leads to
improvements in test results.
Limitations:
- Currently, non-fixed layout mode is handled, but a followup change will
very likely addressed fixed mode tables.
- Column merging is not handled at all.
Fixes#6578.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
We were previously splitting collapsed borders into two halves, and then
paint each one as part of the corresponding cell. This looked wrong when
the border style wasn't solid, or when a cell spanned multiple tracks
and the border wasn't the same for all of them.
Now the borders of a table wrapper, table grid or table cell aren't
painted in collapsed borders mode. Instead, the resulting collapsed
borders are painted on their own.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
`clientWidth` shouldn't include the borders of a box. The problem was
that we pretend that table wrapper boxes have the border specified on
the table element, even though this border actually applies to the
table grid box instead of the table wrapper box.
Therefore, `clientWidth` was wrong when it subtracted the borders.
This patch fixes it.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This will be useful for distributing colspan constraints to their
columns
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-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>
We were previously using the same style and color for two collapsed
borders sharing a coordinate. Now such a line of collapsed borders can
be piecewise and have different colors and styles.
This still doesn't add support for piecewise border widths.
Also, since we are currently painting borders as part of the table and
cell boxes, and a box side can't have a piecewise border, this patch
only really works when:
- There aren't spanning cells
- The table has no assigned border (only the cells and tracks have it)
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>
The containing block for children already has the size coming from the
style and the rules of the parent formatting context, so no need to try
to recompute it.
This allows removing a bunch of functions, and fixes some problems when
the table is a flex item.
Signed-off-by: Oriol Brufau <obrufau@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>
This is still not the right approach, because we are not painting
collapsed borders as a single thing. Instead, we are splitting them
into two halves and paint each half on a different cell. This only
looks good for solid borders.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Just use the cached `TableLayout::pbm`. Also, `TableLayout::pbm` is now
computed with the right writing mode, but no change in practice since
we don't support vertical writing modes.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
- Fix a typo in a comment.
- Get the writing mode of the table in a less convoluted way.
- Check `is_horizontal()` instead of `!is_vertical()`
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, 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>
In the past this didn't hold, so we had to floor GRIDMAX by GRIDMIN.
We must have fixed some bugs because now it's fine to just assert it.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
The new version of rust allows us to elide some lifetimes and clippy is
now complaining about this. This change elides them where possible and
removes the clippy exceptions.
Fixes#34804.
Signed-off-by: Martin Robinson <mrobinson@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>
Due to a typo, the containing block established by a table row for the
table cells had its block size set to the its inline size. However,
this block size is currently unused, so no change in behavior.
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 cells to cache their inline content size and will eventually
allow them 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>
It was only used for the style, but the containing block of the caption
is the table wrapper box, whose style is stored in `self.table.style`.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
* Clean up tracing instrumentation
Signed-off-by: Delan Azabani <dazabani@igalia.com>
* Set all tracing spans to trace level for now
Signed-off-by: Delan Azabani <dazabani@igalia.com>
---------
Signed-off-by: Delan Azabani <dazabani@igalia.com>
In order to support size keywords in block layout, we may need to call
`inline_content_sizes()` in order to compute the min/max-content sizes.
But this required a mutable reference in order the update the cache,
and in various places we already had mutable references.
So this switches the cache into a RwLock to avoid needing mutable refs.
Note OnceCell wouldn't work because it's not thread-safe.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>