layout: Refine the check for dependending on available space (#34907)

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>
This commit is contained in:
Oriol Brufau 2025-01-08 10:22:01 -08:00 committed by GitHub
parent 51a1aeb535
commit 02c10fc502
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 11 additions and 16 deletions

View file

@ -5,7 +5,7 @@
//! Flow layout, also known as block-and-inline layout. //! Flow layout, also known as block-and-inline layout.
use app_units::Au; use app_units::{Au, MAX_AU};
use inline::InlineFormattingContext; use inline::InlineFormattingContext;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use serde::Serialize; use serde::Serialize;
@ -1239,11 +1239,18 @@ impl IndependentNonReplacedContents {
// The final inline size can depend on the available space, which depends on where // The final inline size can depend on the available space, which depends on where
// we are placing the box, since floats reduce the available space. // we are placing the box, since floats reduce the available space.
if !content_box_sizes.inline.depends_on_available_space() { // Here we assume that `compute_inline_size()` is a monotonically increasing function
// with respect to the available space. Therefore, if we get the same result for 0
// and for MAX_AU, it means that the function is constant.
// TODO: `compute_inline_size()` may not be monotonic with `calc-size()`. For example,
// `calc-size(stretch, (1px / (size + 1px) + sign(size)) * 1px)` would result in 1px
// both when the available space is zero and infinity, but it's not constant.
let inline_size_with_no_available_space = compute_inline_size(Au::zero());
if inline_size_with_no_available_space == compute_inline_size(MAX_AU) {
// If the inline size doesn't depend on the available inline space, we can just // If the inline size doesn't depend on the available inline space, we can just
// compute it with an available inline space of zero. Then, after layout we can // compute it with an available inline space of zero. Then, after layout we can
// compute the block size, and finally place among floats. // compute the block size, and finally place among floats.
let inline_size = compute_inline_size(Au::zero()); let inline_size = inline_size_with_no_available_space;
layout = self.layout( layout = self.layout(
layout_context, layout_context,
positioning_context, positioning_context,
@ -1286,7 +1293,7 @@ impl IndependentNonReplacedContents {
// For the lower bound of the inline size, simply assume no available space. // For the lower bound of the inline size, simply assume no available space.
// TODO: this won't work for things like `calc-size(stretch, 100px - size)`, // TODO: this won't work for things like `calc-size(stretch, 100px - size)`,
// which should result in a bigger size when the available space gets smaller. // which should result in a bigger size when the available space gets smaller.
inline: compute_inline_size(Au::zero()), inline: inline_size_with_no_available_space,
block: match tentative_block_size { block: match tentative_block_size {
// If we were able to resolve the preferred and maximum block sizes, // If we were able to resolve the preferred and maximum block sizes,
// use the tentative block size (it takes the 3 sizes into account). // use the tentative block size (it takes the 3 sizes into account).

View file

@ -899,18 +899,6 @@ impl Sizes {
} }
} }
#[inline]
pub(crate) fn depends_on_available_space(&self) -> bool {
// TODO: this logic could be refined further, since even if some of the 3 sizes
// depends on the available space, the resulting size might not. For example,
// `min-width: 200px; width: 100px; max-width: stretch`.
matches!(
self.preferred,
Size::Initial | Size::Stretch | Size::FitContent
) || matches!(self.min, Size::Stretch | Size::FitContent) ||
matches!(self.max, Size::Stretch | Size::FitContent)
}
/// Resolves the three sizes into a single numerical value. /// Resolves the three sizes into a single numerical value.
#[inline] #[inline]
pub(crate) fn resolve( pub(crate) fn resolve(