layout: Avoid layout sometimes when stretching (#33967)

This is the second flexbox caching change. It seeks to detect when a
relayout can be avoided in the case of a stretching flex item. This
heuristic can be combined, because currently we still do relayout
sometimes when we do not need to.

For instance currently we always relayout when a flex child is itself a
column flex. This only needs to happen when the grandchildren themselves
grow or shrink. That optimization is perhaps a lower priority as
`flex-grow: 0 / flex-shrink: 1` is the default behavior for flex.

Since this change means we more consistenly zero out the percentage part
of `calc` expressions when they have circular dependencies, this causes one
test to start failing (`/css/css-values/calc-min-height-block-1.html`).
This is related to w3c/csswg-drafts#10969, which is pending on further
discussion in the working group.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2024-10-24 10:44:30 -07:00 committed by GitHub
parent 638b520186
commit 52db185568
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 490 additions and 257 deletions

View file

@ -179,12 +179,38 @@ impl AspectRatio {
}
}
#[derive(Clone)]
pub(crate) struct ContentBoxSizesAndPBM {
pub content_box_size: LogicalVec2<Size<Au>>,
pub content_min_box_size: LogicalVec2<Size<Au>>,
pub content_max_box_size: LogicalVec2<Size<Au>>,
pub pbm: PaddingBorderMargin,
pub depends_on_block_constraints: bool,
}
impl From<ContentBoxSizesAndPBM> for ContentBoxSizesAndPBMDeprecated {
fn from(sizes: ContentBoxSizesAndPBM) -> Self {
Self {
content_box_size: sizes.content_box_size.map(Size::to_auto_or),
content_min_box_size: sizes.content_min_box_size.map(Size::to_auto_or),
content_max_box_size: sizes.content_max_box_size.map(Size::to_numeric),
pbm: sizes.pbm.clone(),
depends_on_block_constraints: sizes.depends_on_block_constraints,
}
}
}
pub(crate) struct ContentBoxSizesAndPBMDeprecated {
pub content_box_size: LogicalVec2<AuOrAuto>,
pub content_min_box_size: LogicalVec2<AuOrAuto>,
pub content_max_box_size: LogicalVec2<Option<Au>>,
pub pbm: PaddingBorderMargin,
pub depends_on_block_constraints: bool,
}
pub(crate) trait ComputedValuesExt {
fn physical_box_offsets(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
fn box_offsets(
&self,
containing_block: &ContainingBlock,
) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>>;
fn box_size(
&self,
containing_block_writing_mode: WritingMode,
@ -242,27 +268,10 @@ pub(crate) trait ComputedValuesExt {
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
#[allow(clippy::type_complexity)]
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
);
fn content_box_sizes_and_padding_border_margin_deprecated(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
);
) -> ContentBoxSizesAndPBM;
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
fn padding_border_margin_for_intrinsic_size(
&self,
@ -313,6 +322,10 @@ pub(crate) trait ComputedValuesExt {
resolved_auto_value: AlignItems,
resolved_normal_value: AlignItems,
) -> AlignItems;
fn depends_on_block_constraints_due_to_relative_positioning(
&self,
writing_mode: WritingMode,
) -> bool;
}
impl ComputedValuesExt for ComputedValues {
@ -334,14 +347,8 @@ impl ComputedValuesExt for ComputedValues {
)
}
fn box_offsets(
&self,
containing_block: &ContainingBlock,
) -> LogicalSides<LengthPercentageOrAuto<'_>> {
LogicalSides::from_physical(
&self.physical_box_offsets(),
containing_block.style.writing_mode,
)
fn box_offsets(&self, writing_mode: WritingMode) -> LogicalSides<LengthPercentageOrAuto<'_>> {
LogicalSides::from_physical(&self.physical_box_offsets(), writing_mode)
}
fn box_size(
@ -500,13 +507,7 @@ impl ComputedValuesExt for ComputedValues {
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
) {
) -> ContentBoxSizesAndPBM {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
// If max size properties or preferred size properties are set to a value containing
// indefinite percentages, we treat the entire value as the initial value of the property.
@ -537,54 +538,31 @@ impl ComputedValuesExt for ComputedValues {
_ => false,
}
};
let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
depends_on_block_constraints(&min_size.block) ||
depends_on_block_constraints(&max_size.block);
depends_on_block_constraints(&max_size.block) ||
self.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
let box_size = box_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_box_size = self
.content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from));
let min_size = min_size.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
let content_min_size = self
let content_min_box_size = self
.content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from));
let max_size = max_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_max_size = self
let content_max_box_size = self
.content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from));
(
ContentBoxSizesAndPBM {
content_box_size,
content_min_size,
content_max_size,
content_min_box_size,
content_max_box_size,
pbm,
depends_on_block_constraints,
)
}
fn content_box_sizes_and_padding_border_margin_deprecated(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
bool, /* depends_on_block_constraints */
) {
let (
content_box_size,
content_min_size,
content_max_size,
pbm,
depends_on_block_constraints,
) = self.content_box_sizes_and_padding_border_margin(containing_block);
(
content_box_size.map(Size::to_auto_or),
content_min_size.map(Size::to_auto_or),
content_max_size.map(Size::to_numeric),
pbm,
depends_on_block_constraints,
)
}
}
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin {
@ -1031,6 +1009,25 @@ impl ComputedValuesExt for ComputedValues {
value => AlignItems(value),
}
}
fn depends_on_block_constraints_due_to_relative_positioning(
&self,
writing_mode: WritingMode,
) -> bool {
if !matches!(
self.get_box().position,
ComputedPosition::Relative | ComputedPosition::Sticky
) {
return false;
}
let box_offsets = self.box_offsets(writing_mode);
let has_percentage = |offset: LengthPercentageOrAuto<'_>| {
offset
.non_auto()
.is_some_and(LengthPercentage::has_percentage)
};
has_percentage(box_offsets.block_start) || has_percentage(box_offsets.block_end)
}
}
impl From<stylo::Display> for Display {