LibWeb: Implement "The percentage height calculation quirk" in BFC

See https://quirks.spec.whatwg.org/#the-percentage-height-calculation-quirk

Basically, it means that in quirks mode, percentage height needs to be
resolved against nearest ancestor in the containing block chain that
does not have height=auto.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/365
This commit is contained in:
Aliaksandr Kalenik 2024-09-27 21:47:01 +02:00 committed by Andreas Kling
commit 3a5e78780e
Notes: github-actions[bot] 2024-09-28 12:42:22 +00:00
3 changed files with 69 additions and 3 deletions

View file

@ -600,6 +600,41 @@ CSSPixels BlockFormattingContext::compute_auto_height_for_block_level_element(Bo
return 0;
}
static CSSPixels containing_block_height_to_resolve_percentage_in_quirks_mode(Box const& box, LayoutState const& state)
{
// https://quirks.spec.whatwg.org/#the-percentage-height-calculation-quirk
auto const* containing_block = box.containing_block();
while (containing_block) {
// 1. Let element be the nearest ancestor containing block of element, if there is one.
// Otherwise, return the initial containing block.
if (containing_block->is_viewport()) {
return state.get(*containing_block).content_height();
}
// 2. If element has a computed value of the display property that is table-cell, then return a
// UA-defined value.
if (containing_block->display().is_table_cell()) {
// FIXME: Likely UA-defined value should not be 0.
return 0;
}
// 3. If element has a computed value of the height property that is not auto, then return element.
if (!containing_block->computed_values().height().is_auto()) {
return state.get(*containing_block).content_height();
}
// 4. If element has a computed value of the position property that is absolute, or if element is a
// not a block container or a table wrapper box, then return element.
if (containing_block->is_absolutely_positioned() || !is<BlockContainer>(*containing_block) || is<TableWrapper>(*containing_block)) {
return state.get(*containing_block).content_height();
}
// 5. Jump to the first step.
containing_block = containing_block->containing_block();
}
VERIFY_NOT_REACHED();
}
void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContainer const& block_container, CSSPixels& bottom_of_lowest_margin_box, AvailableSpace const& available_space)
{
auto& box_state = m_state.get_mutable(box);
@ -672,11 +707,27 @@ 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);
AvailableSpace available_space_for_height_resolution = available_space;
auto is_grid_or_flex_container = box.display().is_grid_inside() || box.display().is_flex_inside();
auto is_table_box = box.display().is_table_row() || box.display().is_table_row_group() || box.display().is_table_header_group() || box.display().is_table_footer_group() || box.display().is_table_cell() || box.display().is_table_caption();
// NOTE: Spec doesn't mention this but quirk application needs to be skipped for grid and flex containers.
// See https://github.com/w3c/csswg-drafts/issues/5545
if (box.document().in_quirks_mode() && box.computed_values().height().is_percentage() && !is_table_box && !is_grid_or_flex_container) {
// In quirks mode, for the purpose of calculating the height of an element, if the
// computed value of the position property of element is relative or static, the specified value
// for the height property of element is a <percentage>, and element does not have a computed
// value of the display property that is table-row, table-row-group, table-header-group,
// table-footer-group, table-cell or table-caption, the containing block of element must be
// calculated using the following algorithm, aborting on the first step that returns a value:
auto height = containing_block_height_to_resolve_percentage_in_quirks_mode(box, m_state);
available_space_for_height_resolution.height = AvailableSize::make_definite(height);
}
resolve_used_height_if_not_treated_as_auto(box, available_space_for_height_resolution);
// 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()) {
resolve_used_height_if_treated_as_auto(box, available_space);
resolve_used_height_if_treated_as_auto(box, available_space_for_height_resolution);
}
// Before we insert the children of a list item we need to know the location of the marker.
@ -726,7 +777,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()) {
resolve_used_height_if_treated_as_auto(box, available_space, independent_formatting_context);
resolve_used_height_if_treated_as_auto(box, available_space_for_height_resolution, independent_formatting_context);
}
if (independent_formatting_context || !margins_collapse_through(box, m_state)) {