diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/percentage-height-in-quirks-mode.txt b/Tests/LibWeb/Layout/expected/block-and-inline/percentage-height-in-quirks-mode.txt new file mode 100644 index 00000000000..7947568edfe --- /dev/null +++ b/Tests/LibWeb/Layout/expected/block-and-inline/percentage-height-in-quirks-mode.txt @@ -0,0 +1,9 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x616 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x600 children: not-inline + BlockContainer
at (8,8) content-size 784x600 children: not-inline + +ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x616] + PaintableWithLines (BlockContainer) [0,0 800x616] + PaintableWithLines (BlockContainer) [8,8 784x600] + PaintableWithLines (BlockContainer
) [8,8 784x600] diff --git a/Tests/LibWeb/Layout/input/block-and-inline/percentage-height-in-quirks-mode.html b/Tests/LibWeb/Layout/input/block-and-inline/percentage-height-in-quirks-mode.html new file mode 100644 index 00000000000..00f538e7688 --- /dev/null +++ b/Tests/LibWeb/Layout/input/block-and-inline/percentage-height-in-quirks-mode.html @@ -0,0 +1,6 @@ +
\ No newline at end of file diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 85dafe1d8f4..d0ffc1579af 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -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(*containing_block) || is(*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 , 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)) {