LibWeb: Resolve not auto height before child box layout in BFC

It's possible to resolve box's height without doing inner layout, when
computed value is not auto. Doing that fixes height resolution, when box
with percentage height has containing block with percentage height.

Before:
- resolve used width
- layout box's content
- resolve height

After:
- resolve used width
- resolve height if treated as not auto
- layout box's content
- resolve height if treated as auto
This commit is contained in:
Aliaksandr Kalenik 2024-09-27 17:49:02 +02:00 committed by Alexander Kalenik
parent fdcece2e88
commit 9098e39a43
Notes: github-actions[bot] 2024-09-27 18:26:33 +00:00
5 changed files with 74 additions and 25 deletions

View file

@ -0,0 +1,9 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x300 children: not-inline
BlockContainer <div> at (8,8) content-size 784x300 children: not-inline
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x300]
PaintableWithLines (BlockContainer<DIV>) [8,8 784x300]

View file

@ -0,0 +1,9 @@
<style>
div {
background-color: crimson;
height: 100%;
}
body {
height: 50%;
}
</style><div></div>

View file

@ -407,24 +407,48 @@ void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_n
box_state.padding_right = padding_right;
}
void BlockFormattingContext::compute_height(Box const& box, AvailableSpace const& available_space, FormattingContext const* box_formatting_context)
void BlockFormattingContext::resolve_used_height_if_not_treated_as_auto(Box const& box, AvailableSpace const& available_space)
{
auto const& computed_values = box.computed_values();
auto& box_used_values = m_state.get_mutable(box);
if (should_treat_height_as_auto(box, available_space)) {
return;
}
auto const& computed_values = box.computed_values();
auto& box_state = m_state.get_mutable(box);
auto height = calculate_inner_height(box, available_space.height, box.computed_values().height());
if (!should_treat_max_height_as_none(box, available_space.height)) {
if (!computed_values.max_height().is_auto()) {
auto max_height = calculate_inner_height(box, available_space.height, computed_values.max_height());
height = min(height, max_height);
}
}
if (!computed_values.min_height().is_auto()) {
height = max(height, calculate_inner_height(box, available_space.height, computed_values.min_height()));
}
box_state.set_content_height(height);
box_state.set_has_definite_height(true);
}
void BlockFormattingContext::resolve_used_height_if_treated_as_auto(Box const& box, AvailableSpace const& available_space, FormattingContext const* box_formatting_context)
{
if (!should_treat_height_as_auto(box, available_space)) {
return;
}
auto const& computed_values = box.computed_values();
auto& box_state = m_state.get_mutable(box);
// Then work out what the height is, based on box type and CSS properties.
CSSPixels height = 0;
if (box_is_sized_as_replaced_element(box)) {
height = compute_height_for_replaced_element(box, available_space);
} else {
if (should_treat_height_as_auto(box, available_space)) {
if (box_formatting_context) {
height = box_formatting_context->automatic_content_height();
} else {
height = compute_auto_height_for_block_level_element(box, m_state.get(box).available_inner_space_or_constraints_from(available_space));
}
if (box_formatting_context) {
height = box_formatting_context->automatic_content_height();
} else {
height = calculate_inner_height(box, available_space.height, computed_values.height());
height = compute_auto_height_for_block_level_element(box, m_state.get(box).available_inner_space_or_constraints_from(available_space));
}
}
@ -446,8 +470,6 @@ void BlockFormattingContext::compute_height(Box const& box, AvailableSpace const
// https://quirks.spec.whatwg.org/#the-html-element-fills-the-viewport-quirk
// FIXME: Handle vertical writing mode.
auto& box_state = m_state.get_mutable(box);
// 1. Let margins be sum of the used values of the margin-left and margin-right properties of element
// if element has a vertical writing mode, otherwise let margins be the sum of the used values of
// the margin-top and margin-bottom properties of element.
@ -461,10 +483,10 @@ void BlockFormattingContext::compute_height(Box const& box, AvailableSpace const
height = max(size, height);
// NOTE: The height of the root element when affected by this quirk is considered to be definite.
box_used_values.set_has_definite_height(true);
box_state.set_has_definite_height(true);
}
box_used_values.set_content_height(height);
box_state.set_content_height(height);
}
void BlockFormattingContext::layout_inline_children(BlockContainer const& block_container, AvailableSpace const& available_space)
@ -616,7 +638,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
// NOTE: In quirks mode, the html element's height matches the viewport so it can be treated as definite
if (box_state.has_definite_height() || box_is_html_element_in_quirks_mode) {
compute_height(box, available_space);
resolve_used_height_if_treated_as_auto(box, available_space);
}
auto independent_formatting_context = create_independent_formatting_context_if_needed(m_state, m_layout_mode, box);
@ -650,9 +672,12 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
place_block_level_element_in_normal_flow_horizontally(box, available_space);
resolve_used_height_if_not_treated_as_auto(box, available_space);
// NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
if (box.is_replaced_box() || box.display().is_flex_inside())
compute_height(box, available_space);
if (box.is_replaced_box() || box.display().is_flex_inside()) {
resolve_used_height_if_treated_as_auto(box, available_space);
}
// Before we insert the children of a list item we need to know the location of the marker.
// If we do not do this then left-floating elements inside the list item will push the marker to the right,
@ -701,7 +726,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
// Tables already set their height during the independent formatting context run. When multi-line text cells are involved, using different
// available space here than during the independent formatting context run can result in different line breaks and thus a different height.
if (!box.display().is_table_inside()) {
compute_height(box, available_space, independent_formatting_context);
resolve_used_height_if_treated_as_auto(box, available_space, independent_formatting_context);
}
if (independent_formatting_context || !margins_collapse_through(box, m_state)) {
@ -939,12 +964,15 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
compute_width(box, available_space);
resolve_used_height_if_not_treated_as_auto(box, available_space);
// NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
if (box.is_replaced_box() || box.display().is_flex_inside())
compute_height(box, available_space);
if (box.is_replaced_box() || box.display().is_flex_inside()) {
resolve_used_height_if_treated_as_auto(box, available_space);
}
auto independent_formatting_context = layout_inside(box, m_layout_mode, box_state.available_inner_space_or_constraints_from(available_space));
compute_height(box, available_space, independent_formatting_context);
resolve_used_height_if_treated_as_auto(box, available_space, independent_formatting_context);
// First we place the box normally (to get the right y coordinate.)
// If we have a LineBuilder, we're in the middle of inline layout, otherwise this is block layout.

View file

@ -37,7 +37,8 @@ public:
virtual void parent_context_did_dimension_child_root_box() override;
void compute_height(Box const&, AvailableSpace const&, FormattingContext const* box_formatting_context = nullptr);
void resolve_used_height_if_not_treated_as_auto(Box const&, AvailableSpace const&);
void resolve_used_height_if_treated_as_auto(Box const&, AvailableSpace const&, FormattingContext const* box_formatting_context = nullptr);
SpaceUsedAndContainingMarginForFloats space_used_and_containing_margin_for_floats(CSSPixels y) const;
[[nodiscard]] SpaceUsedByFloats intrusion_by_floats_into_box(Box const&, CSSPixels y_in_box) const;

View file

@ -177,16 +177,18 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l
box_state.set_content_width(width);
parent().resolve_used_height_if_not_treated_as_auto(box, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_indefinite()));
// NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
if (box_state.has_definite_height() || box.display().is_flex_inside())
parent().compute_height(box, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_indefinite()));
parent().resolve_used_height_if_treated_as_auto(box, AvailableSpace(AvailableSize::make_definite(width), AvailableSize::make_indefinite()));
auto independent_formatting_context = layout_inside(box, layout_mode, box_state.available_inner_space_or_constraints_from(*m_available_space));
auto const& height_value = box.computed_values().height();
if (should_treat_height_as_auto(box, *m_available_space)) {
// FIXME: (10.6.6) If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
parent().compute_height(box, AvailableSpace(AvailableSize::make_indefinite(), AvailableSize::make_indefinite()));
parent().resolve_used_height_if_treated_as_auto(box, AvailableSpace(AvailableSize::make_indefinite(), AvailableSize::make_indefinite()));
} else {
auto inner_height = calculate_inner_height(box, AvailableSize::make_definite(m_containing_block_used_values.content_height()), height_value);
box_state.set_content_height(inner_height);