1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 22:57:44 +00:00

LibWeb: Forbid usage of indefinite width in calculate_min{max}_height

Changing `calculate_min_content_heigh()` and
`calculate_min_content_heigh()` to accept width as `CSSPixels`, instead
of `AvailableSize` that might be indefinite, makes it more explicit
that width is supposed to be known by the time height is measured.

This change has a bit of collateral damage which is rows height
calculation regression in `table/inline-table-width` that worked before
by accident.
This commit is contained in:
Aliaksandr Kalenik 2023-08-12 15:17:17 +02:00 committed by Andreas Kling
parent 9b61339261
commit e25b1f76e1
8 changed files with 52 additions and 80 deletions

View file

@ -550,16 +550,16 @@ CSSPixels BlockFormattingContext::compute_auto_height_for_block_level_element(Bo
if (display.is_flex_inside()) {
// https://drafts.csswg.org/css-flexbox-1/#algo-main-container
// NOTE: The automatic block size of a block-level flex container is its max-content size.
return calculate_max_content_height(box, available_space.width);
return calculate_max_content_height(box, available_space.width.to_px_or_zero());
}
if (display.is_grid_inside()) {
// https://www.w3.org/TR/css-grid-2/#intrinsic-sizes
// In both inline and block formatting contexts, the grid containers auto block size is its
// max-content size.
return calculate_max_content_height(box, available_space.width);
return calculate_max_content_height(box, available_space.width.to_px_or_zero());
}
if (display.is_table_inside()) {
return calculate_max_content_height(box, available_space.width);
return calculate_max_content_height(box, available_space.width.to_px_or_zero());
}
// https://www.w3.org/TR/CSS22/visudet.html#normal-block

View file

@ -834,7 +834,7 @@ void FlexFormattingContext::determine_main_size_of_flex_container()
}
} else {
if (!has_definite_main_size(flex_container()))
set_main_size(flex_container(), calculate_max_content_height(flex_container(), m_available_space_for_flex_container->space.width));
set_main_size(flex_container(), calculate_max_content_height(flex_container(), m_available_space_for_flex_container->space.width.to_px()));
}
}
@ -2012,7 +2012,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_main_size(FlexItem const&
if (available_space.width.is_indefinite()) {
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
}
return calculate_min_content_height(item.box, available_space.width);
return calculate_min_content_height(item.box, available_space.width.to_px_or_zero());
}
CSSPixels FlexFormattingContext::calculate_max_content_main_size(FlexItem const& item) const
@ -2024,7 +2024,7 @@ CSSPixels FlexFormattingContext::calculate_max_content_main_size(FlexItem const&
if (available_space.width.is_indefinite()) {
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
}
return calculate_max_content_height(item.box, available_space.width);
return calculate_max_content_height(item.box, available_space.width.to_px_or_zero());
}
CSSPixels FlexFormattingContext::calculate_fit_content_main_size(FlexItem const& item) const
@ -2048,7 +2048,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const
if (available_space.width.is_indefinite()) {
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
}
return calculate_min_content_height(item.box, available_space.width);
return calculate_min_content_height(item.box, available_space.width.to_px_or_zero());
}
return calculate_min_content_width(item.box);
}
@ -2060,7 +2060,7 @@ CSSPixels FlexFormattingContext::calculate_max_content_cross_size(FlexItem const
if (available_space.width.is_indefinite()) {
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
}
return calculate_max_content_height(item.box, available_space.width);
return calculate_max_content_height(item.box, available_space.width.to_px_or_zero());
}
return calculate_max_content_width(item.box);
}

View file

@ -1265,17 +1265,17 @@ CSSPixels FormattingContext::calculate_fit_content_height(Layout::Box const& box
// equal to clamp(min-content size, stretch-fit size, max-content size)
// (i.e. max(min-content size, min(max-content size, stretch-fit size))).
if (available_space.height.is_definite()) {
return max(calculate_min_content_height(box, available_space.width),
return max(calculate_min_content_height(box, available_space.width.to_px_or_zero()),
min(calculate_stretch_fit_height(box, available_space.height),
calculate_max_content_height(box, available_space.width)));
calculate_max_content_height(box, available_space.width.to_px_or_zero())));
}
// When sizing under a min-content constraint, equal to the min-content size.
if (available_space.height.is_min_content())
return calculate_min_content_height(box, available_space.width);
return calculate_min_content_height(box, available_space.width.to_px_or_zero());
// Otherwise, equal to the max-content size in that axis.
return calculate_max_content_height(box, available_space.width);
return calculate_max_content_height(box, available_space.width.to_px_or_zero());
}
CSSPixels FormattingContext::calculate_min_content_width(Layout::Box const& box) const
@ -1355,29 +1355,19 @@ CSSPixels FormattingContext::calculate_max_content_width(Layout::Box const& box)
}
// https://www.w3.org/TR/css-sizing-3/#min-content-block-size
CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box, AvailableSize const& available_width) const
CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box, CSSPixels width) const
{
// For block containers, tables, and inline boxes, this is equivalent to the max-content block size.
if (box.is_block_container() || box.display().is_table_inside())
return calculate_max_content_height(box, available_width);
return calculate_max_content_height(box, width);
if (box.has_natural_height())
return *box.natural_height();
bool is_cacheable = available_width.is_definite() || available_width.is_intrinsic_sizing_constraint();
auto get_cache_slot = [&]() -> Optional<CSSPixels>* {
if (!is_cacheable)
return {};
auto& root_state = m_state.m_root;
auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new LayoutState::IntrinsicSizes); });
if (available_width.is_definite())
return &cache.min_content_height_with_definite_available_width.ensure(available_width.to_px());
if (available_width.is_min_content())
return &cache.min_content_height_with_min_content_available_width;
if (available_width.is_max_content())
return &cache.min_content_height_with_max_content_available_width;
return {};
return &cache.min_content_height.ensure(width);
};
if (auto* cache_slot = get_cache_slot(); cache_slot && cache_slot->has_value())
@ -1388,15 +1378,14 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box
auto& box_state = throwaway_state.get_mutable(box);
box_state.height_constraint = SizeConstraint::MinContent;
box_state.set_indefinite_content_height();
if (available_width.is_definite())
box_state.set_content_width(available_width.to_px());
box_state.set_content_width(width);
auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
if (!context) {
context = make<BlockFormattingContext>(throwaway_state, verify_cast<BlockContainer>(box), nullptr);
}
context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(available_width, AvailableSize::make_min_content()));
context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_min_content()));
auto min_content_height = context->automatic_content_height();
if (!isfinite(min_content_height.to_double())) {
@ -1411,28 +1400,18 @@ CSSPixels FormattingContext::calculate_min_content_height(Layout::Box const& box
return min_content_height;
}
CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box, AvailableSize const& available_width) const
CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box, CSSPixels width) const
{
if (box.has_preferred_aspect_ratio() && available_width.is_definite())
return available_width.to_px() / static_cast<double>(*box.preferred_aspect_ratio());
if (box.has_preferred_aspect_ratio())
return width / static_cast<double>(*box.preferred_aspect_ratio());
if (box.has_natural_height())
return *box.natural_height();
bool is_cacheable = available_width.is_definite() || available_width.is_intrinsic_sizing_constraint();
auto get_cache_slot = [&]() -> Optional<CSSPixels>* {
if (!is_cacheable)
return {};
auto& root_state = m_state.m_root;
auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new LayoutState::IntrinsicSizes); });
if (available_width.is_definite())
return &cache.max_content_height_with_definite_available_width.ensure(available_width.to_px());
if (available_width.is_min_content())
return &cache.max_content_height_with_min_content_available_width;
if (available_width.is_max_content())
return &cache.max_content_height_with_max_content_available_width;
return {};
return &cache.max_content_height.ensure(width);
};
if (auto* cache_slot = get_cache_slot(); cache_slot && cache_slot->has_value())
@ -1443,15 +1422,14 @@ CSSPixels FormattingContext::calculate_max_content_height(Layout::Box const& box
auto& box_state = throwaway_state.get_mutable(box);
box_state.height_constraint = SizeConstraint::MaxContent;
box_state.set_indefinite_content_height();
if (available_width.is_definite())
box_state.set_content_width(available_width.to_px());
box_state.set_content_width(width);
auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
if (!context) {
context = make<BlockFormattingContext>(throwaway_state, verify_cast<BlockContainer>(box), nullptr);
}
context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(available_width, AvailableSize::make_max_content()));
context->run(box, LayoutMode::IntrinsicSizing, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_max_content()));
auto max_content_height = context->automatic_content_height();

View file

@ -59,8 +59,8 @@ public:
CSSPixels calculate_min_content_width(Layout::Box const&) const;
CSSPixels calculate_max_content_width(Layout::Box const&) const;
CSSPixels calculate_min_content_height(Layout::Box const&, AvailableSize const& available_width) const;
CSSPixels calculate_max_content_height(Layout::Box const&, AvailableSize const& available_width) const;
CSSPixels calculate_min_content_height(Layout::Box const&, CSSPixels width) const;
CSSPixels calculate_max_content_height(Layout::Box const&, CSSPixels width) const;
CSSPixels calculate_fit_content_height(Layout::Box const&, AvailableSpace const&) const;
CSSPixels calculate_fit_content_width(Layout::Box const&, AvailableSpace const&) const;

View file

@ -1954,7 +1954,7 @@ CSSPixels GridFormattingContext::calculate_min_content_size(GridItem const& item
if (dimension == GridDimension::Column) {
return calculate_min_content_width(item.box);
} else {
return calculate_min_content_height(item.box, get_available_space_for_item(item).width);
return calculate_min_content_height(item.box, get_available_space_for_item(item).width.to_px_or_zero());
}
}
@ -1963,7 +1963,7 @@ CSSPixels GridFormattingContext::calculate_max_content_size(GridItem const& item
if (dimension == GridDimension::Column) {
return calculate_max_content_width(item.box);
} else {
return calculate_max_content_height(item.box, get_available_space_for_item(item).width);
return calculate_max_content_height(item.box, get_available_space_for_item(item).width.to_px_or_zero());
}
}

View file

@ -164,14 +164,8 @@ struct LayoutState {
Optional<CSSPixels> min_content_width;
Optional<CSSPixels> max_content_width;
// NOTE: Since intrinsic heights depend on the amount of available width, we have to cache
// three separate kinds of results, depending on the available width at the time of calculation.
HashMap<CSSPixels, Optional<CSSPixels>> min_content_height_with_definite_available_width;
HashMap<CSSPixels, Optional<CSSPixels>> max_content_height_with_definite_available_width;
Optional<CSSPixels> min_content_height_with_min_content_available_width;
Optional<CSSPixels> max_content_height_with_min_content_available_width;
Optional<CSSPixels> min_content_height_with_max_content_available_width;
Optional<CSSPixels> max_content_height_with_max_content_available_width;
HashMap<CSSPixels, Optional<CSSPixels>> min_content_height;
HashMap<CSSPixels, Optional<CSSPixels>> max_content_height;
};
HashMap<JS::GCPtr<NodeWithStyleAndBoxModelMetrics const>, NonnullOwnPtr<IntrinsicSizes>> mutable intrinsic_sizes;

View file

@ -241,8 +241,8 @@ void TableFormattingContext::compute_cell_measures(AvailableSpace const& availab
CSSPixels border_left = use_collapsing_borders_model ? round(cell_state.border_left / 2) : computed_values.border_left().width;
CSSPixels border_right = use_collapsing_borders_model ? round(cell_state.border_right / 2) : computed_values.border_right().width;
auto min_content_height = calculate_min_content_height(cell.box, available_space.width);
auto max_content_height = calculate_max_content_height(cell.box, available_space.width);
auto min_content_height = calculate_min_content_height(cell.box, available_space.width.to_px_or_zero());
auto max_content_height = calculate_max_content_height(cell.box, available_space.width.to_px_or_zero());
auto min_content_width = calculate_min_content_width(cell.box);
auto max_content_width = calculate_max_content_width(cell.box);