From 0243278587016af98aa357127e692a183a498734 Mon Sep 17 00:00:00 2001 From: implicitfield <114500360+implicitfield@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:11:04 +0400 Subject: [PATCH] LibWeb: Account for absolutely positioned table wrappers Table wrappers don't quite behave the same as most elements, in that their computed height and width are not meant to be used for layout. Instead, we now calculate suitable widths and heights based on the contents of the table wrapper when performing absolute layout. Fixes the layout of http://wpt.live/css/css-position/position-absolute-center-007.html --- .../table/absolute-positioning-alignment.txt | 38 ++ .../table/absolute-positioning-alignment.html | 52 +++ .../LibWeb/Layout/BlockFormattingContext.cpp | 42 -- .../LibWeb/Layout/BlockFormattingContext.h | 2 - .../LibWeb/Layout/FormattingContext.cpp | 384 +++++++++++------- .../LibWeb/Layout/FormattingContext.h | 3 + 6 files changed, 333 insertions(+), 188 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/table/absolute-positioning-alignment.txt create mode 100644 Tests/LibWeb/Layout/input/table/absolute-positioning-alignment.html diff --git a/Tests/LibWeb/Layout/expected/table/absolute-positioning-alignment.txt b/Tests/LibWeb/Layout/expected/table/absolute-positioning-alignment.txt new file mode 100644 index 0000000000..f9b4b4d26f --- /dev/null +++ b/Tests/LibWeb/Layout/expected/table/absolute-positioning-alignment.txt @@ -0,0 +1,38 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (8,8) content-size 784x532 [BFC] children: not-inline + BlockContainer at (16,16) content-size 768x516 children: not-inline + Box at (24,24) content-size 500x500 positioned flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + TableWrapper <(anonymous)> at (24,199) content-size 100x150 positioned [BFC] children: not-inline + Box at (27,202) content-size 94x144 table-box [TFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + TableWrapper <(anonymous)> at (424,199) content-size 100x150 positioned [BFC] children: not-inline + Box at (427,202) content-size 94x144 table-box [TFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + TableWrapper <(anonymous)> at (199,24) content-size 150x100 positioned [BFC] children: not-inline + Box at (202,27) content-size 144x94 table-box [TFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + TableWrapper <(anonymous)> at (199,424) content-size 150x100 positioned [BFC] children: not-inline + Box at (202,427) content-size 144x94 table-box [TFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (16,532) content-size 768x0 children: inline + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [5,5 790x538] + PaintableWithLines (BlockContainer) [13,13 774x522] + PaintableBox (Box
.container) [21,21 506x506] + PaintableWithLines (TableWrapper(anonymous)) [24,199 100x150] + PaintableBox (Box
.table.left) [24,199 100x150] + PaintableWithLines (TableWrapper(anonymous)) [424,199 100x150] + PaintableBox (Box
.table.right) [424,199 100x150] + PaintableWithLines (TableWrapper(anonymous)) [199,24 150x100] + PaintableBox (Box
.table.top) [199,24 150x100] + PaintableWithLines (TableWrapper(anonymous)) [199,424 150x100] + PaintableBox (Box
.table.bottom) [199,424 150x100] + PaintableWithLines (BlockContainer(anonymous)) [16,532 768x0] diff --git a/Tests/LibWeb/Layout/input/table/absolute-positioning-alignment.html b/Tests/LibWeb/Layout/input/table/absolute-positioning-alignment.html new file mode 100644 index 0000000000..032f46c684 --- /dev/null +++ b/Tests/LibWeb/Layout/input/table/absolute-positioning-alignment.html @@ -0,0 +1,52 @@ + + +
+
+
+
+
+
diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 98c6651358..d24a2dc8d7 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -410,48 +410,6 @@ void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_n box_state.padding_right = padding_right; } -CSSPixels BlockFormattingContext::compute_table_box_width_inside_table_wrapper(Box const& box, AvailableSpace const& available_space) -{ - // 17.5.2 - // Table wrapper width should be equal to width of table box it contains - - auto const& computed_values = box.computed_values(); - - auto width_of_containing_block = available_space.width.to_px_or_zero(); - - auto zero_value = CSS::Length::make_px(0); - - auto margin_left = computed_values.margin().left().resolved(box, width_of_containing_block); - auto margin_right = computed_values.margin().right().resolved(box, width_of_containing_block); - - // If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'. - if (margin_left.is_auto()) - margin_left = zero_value; - if (margin_right.is_auto()) - margin_right = zero_value; - - // table-wrapper can't have borders or paddings but it might have margin taken from table-root. - auto available_width = width_of_containing_block - margin_left.to_px(box) - margin_right.to_px(box); - - LayoutState throwaway_state(&m_state); - auto context = create_independent_formatting_context_if_needed(throwaway_state, box); - VERIFY(context); - context->run(box, LayoutMode::IntrinsicSizing, m_state.get(box).available_inner_space_or_constraints_from(available_space)); - - Optional table_box; - box.for_each_in_subtree_of_type([&](Box const& child_box) { - if (child_box.display().is_table_inside()) { - table_box = child_box; - return IterationDecision::Break; - } - return IterationDecision::Continue; - }); - VERIFY(table_box.has_value()); - - auto table_used_width = throwaway_state.get(*table_box).border_box_width(); - return available_space.width.is_definite() ? min(table_used_width, available_width) : table_used_width; -} - void BlockFormattingContext::compute_height(Box const& box, AvailableSpace const& available_space) { auto const& computed_values = box.computed_values(); diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index 0fd1306b66..08045edde6 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -69,8 +69,6 @@ private: void compute_width_for_block_level_replaced_element_in_normal_flow(Box const&, AvailableSpace const&); - CSSPixels compute_table_box_width_inside_table_wrapper(Box const&, AvailableSpace const&); - void layout_viewport(LayoutMode, AvailableSpace const&); void layout_block_level_children(BlockContainer const&, LayoutMode, AvailableSpace const&); diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp index 074f069dc8..fad96bd837 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -381,6 +381,93 @@ CSSPixels FormattingContext::compute_auto_height_for_block_formatting_context_ro return max(CSSPixels(0.0f), bottom.value_or(0) - top.value_or(0)); } +// 17.5.2 Table width algorithms: the 'table-layout' property +// https://www.w3.org/TR/CSS22/tables.html#width-layout +CSSPixels FormattingContext::compute_table_box_width_inside_table_wrapper(Box const& box, AvailableSpace const& available_space) +{ + // Table wrapper width should be equal to width of table box it contains + + auto const& computed_values = box.computed_values(); + + auto width_of_containing_block = available_space.width.to_px_or_zero(); + + auto zero_value = CSS::Length::make_px(0); + + auto margin_left = computed_values.margin().left().resolved(box, width_of_containing_block); + auto margin_right = computed_values.margin().right().resolved(box, width_of_containing_block); + + // If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'. + if (margin_left.is_auto()) + margin_left = zero_value; + if (margin_right.is_auto()) + margin_right = zero_value; + + // table-wrapper can't have borders or paddings but it might have margin taken from table-root. + auto available_width = width_of_containing_block - margin_left.to_px(box) - margin_right.to_px(box); + + LayoutState throwaway_state(&m_state); + auto context = create_independent_formatting_context_if_needed(throwaway_state, box); + VERIFY(context); + context->run(box, LayoutMode::IntrinsicSizing, m_state.get(box).available_inner_space_or_constraints_from(available_space)); + + Optional table_box; + box.for_each_in_subtree_of_type([&](Box const& child_box) { + if (child_box.display().is_table_inside()) { + table_box = child_box; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + VERIFY(table_box.has_value()); + + auto table_used_width = throwaway_state.get(*table_box).border_box_width(); + return available_space.width.is_definite() ? min(table_used_width, available_width) : table_used_width; +} + +// 17.5.3 Table height algorithms +// https://www.w3.org/TR/CSS22/tables.html#height-layout +CSSPixels FormattingContext::compute_table_box_height_inside_table_wrapper(Box const& box, AvailableSpace const& available_space) +{ + // Table wrapper height should be equal to height of table box it contains + + auto const& computed_values = box.computed_values(); + + auto width_of_containing_block = available_space.width.to_px_or_zero(); + auto height_of_containing_block = available_space.height.to_px_or_zero(); + + auto zero_value = CSS::Length::make_px(0); + + auto margin_top = computed_values.margin().top().resolved(box, width_of_containing_block); + auto margin_bottom = computed_values.margin().bottom().resolved(box, width_of_containing_block); + + // If 'margin-top', or 'margin-top' are computed as 'auto', their used value is '0'. + if (margin_top.is_auto()) + margin_top = zero_value; + if (margin_bottom.is_auto()) + margin_bottom = zero_value; + + // table-wrapper can't have borders or paddings but it might have margin taken from table-root. + auto available_height = height_of_containing_block - margin_top.to_px(box) - margin_bottom.to_px(box); + + LayoutState throwaway_state(&m_state); + auto context = create_independent_formatting_context_if_needed(throwaway_state, box); + VERIFY(context); + context->run(box, LayoutMode::IntrinsicSizing, m_state.get(box).available_inner_space_or_constraints_from(available_space)); + + Optional table_box; + box.for_each_in_subtree_of_type([&](Box const& child_box) { + if (child_box.display().is_table_inside()) { + table_box = child_box; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + VERIFY(table_box.has_value()); + + auto table_used_height = throwaway_state.get(*table_box).border_box_height(); + return available_space.height.is_definite() ? min(table_used_height, available_height) : table_used_height; +} + // 10.3.2 Inline, replaced elements, https://www.w3.org/TR/CSS22/visudet.html#inline-replaced-width CSSPixels FormattingContext::tentative_width_for_replaced_element(Box const& box, CSS::Size const& computed_width, AvailableSpace const& available_space) const { @@ -705,6 +792,8 @@ void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_ele // 1. The tentative used width is calculated (without 'min-width' and 'max-width') auto used_width = try_compute_width([&] { + if (is(box)) + return CSS::Length::make_px(compute_table_box_width_inside_table_wrapper(box, available_space)); if (computed_values.width().is_auto()) return CSS::Length::make_auto(); return CSS::Length::make_px(calculate_inner_width(box, available_space.width, computed_values.width())); @@ -833,17 +922,19 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el // In the before pass, if it turns out we need the automatic height of the box, we abort these steps. // This allows the box to retain an indefinite height from the perspective of inside layout. - auto apply_min_max_height_constraints = [this, &box, &available_space](CSSPixels unconstrained_height) -> CSSPixels { + auto apply_min_max_height_constraints = [this, &box, &available_space](CSS::Length unconstrained_height) -> CSS::Length { auto const& computed_min_height = box.computed_values().min_height(); auto const& computed_max_height = box.computed_values().max_height(); auto constrained_height = unconstrained_height; if (!computed_max_height.is_none()) { auto inner_max_height = calculate_inner_height(box, available_space.height, computed_max_height); - constrained_height = min(constrained_height, inner_max_height); + if (inner_max_height < constrained_height.to_px(box)) + constrained_height = CSS::Length::make_px(inner_max_height); } if (!computed_min_height.is_auto()) { auto inner_min_height = calculate_inner_height(box, available_space.height, computed_min_height); - constrained_height = max(constrained_height, inner_min_height); + if (inner_min_height > constrained_height.to_px(box)) + constrained_height = CSS::Length::make_px(inner_min_height); } return constrained_height; }; @@ -852,7 +943,6 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el auto margin_bottom = box.computed_values().margin().bottom(); auto top = box.computed_values().inset().top(); auto bottom = box.computed_values().inset().bottom(); - auto height = box.computed_values().height(); auto width_of_containing_block = containing_block_width_for(box); auto height_of_containing_block = available_space.height.to_px_or_zero(); @@ -861,171 +951,177 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el No, Yes, }; - auto solve_for = [&](CSS::Length length, ClampToZero clamp_to_zero = ClampToZero::No) { - auto unclamped_value = height_of_containing_block - - top.to_px(box, height_of_containing_block) - - margin_top.to_px(box, width_of_containing_block) - - box.computed_values().border_top().width - - box.computed_values().padding().top().to_px(box, width_of_containing_block) - - height.to_px(box, height_of_containing_block) - - box.computed_values().padding().bottom().to_px(box, width_of_containing_block) - - box.computed_values().border_bottom().width - - margin_bottom.to_px(box, width_of_containing_block) - - bottom.to_px(box, height_of_containing_block) - + length.to_px(box); - if (clamp_to_zero == ClampToZero::Yes) - return CSS::Length::make_px(max(CSSPixels(0), unclamped_value)); - return CSS::Length::make_px(unclamped_value); - }; - auto solve_for_top = [&] { - top = solve_for(top.resolved(box, height_of_containing_block)); - }; + auto try_compute_height = [&](CSS::Length height) -> CSS::Length { + auto solve_for = [&](CSS::Length length, ClampToZero clamp_to_zero = ClampToZero::No) { + auto unclamped_value = height_of_containing_block + - top.to_px(box, height_of_containing_block) + - margin_top.to_px(box, width_of_containing_block) + - box.computed_values().border_top().width + - box.computed_values().padding().top().to_px(box, width_of_containing_block) + - height.to_px(box) + - box.computed_values().padding().bottom().to_px(box, width_of_containing_block) + - box.computed_values().border_bottom().width + - margin_bottom.to_px(box, width_of_containing_block) + - bottom.to_px(box, height_of_containing_block) + + length.to_px(box); + if (clamp_to_zero == ClampToZero::Yes) + return CSS::Length::make_px(max(CSSPixels(0), unclamped_value)); + return CSS::Length::make_px(unclamped_value); + }; - auto solve_for_bottom = [&] { - bottom = solve_for(bottom.resolved(box, height_of_containing_block)); - }; + auto solve_for_top = [&] { + top = solve_for(top.resolved(box, height_of_containing_block)); + }; - auto solve_for_height = [&] { - height = CSS::Size::make_length(solve_for(height.resolved(box, height_of_containing_block), ClampToZero::Yes)); - }; + auto solve_for_bottom = [&] { + bottom = solve_for(bottom.resolved(box, height_of_containing_block)); + }; - auto solve_for_margin_top = [&] { - margin_top = solve_for(margin_top.resolved(box, width_of_containing_block)); - }; + auto solve_for_height = [&] { + height = solve_for(height, ClampToZero::Yes); + }; - auto solve_for_margin_bottom = [&] { - margin_bottom = solve_for(margin_bottom.resolved(box, width_of_containing_block)); - }; + auto solve_for_margin_top = [&] { + margin_top = solve_for(margin_top.resolved(box, width_of_containing_block)); + }; - auto solve_for_margin_top_and_margin_bottom = [&] { - auto remainder = solve_for(CSS::Length::make_px(margin_top.to_px(box, width_of_containing_block) + margin_bottom.to_px(box, width_of_containing_block))).to_px(box); - margin_top = CSS::Length::make_px(remainder / 2); - margin_bottom = CSS::Length::make_px(remainder / 2); - }; + auto solve_for_margin_bottom = [&] { + margin_bottom = solve_for(margin_bottom.resolved(box, width_of_containing_block)); + }; - // If all three of top, height, and bottom are auto: - if (top.is_auto() && should_treat_height_as_auto(box, available_space) && bottom.is_auto()) { - // First set any auto values for margin-top and margin-bottom to 0, - if (margin_top.is_auto()) - margin_top = CSS::Length::make_px(0); - if (margin_bottom.is_auto()) - margin_bottom = CSS::Length::make_px(0); + auto solve_for_margin_top_and_margin_bottom = [&] { + auto remainder = solve_for(CSS::Length::make_px(margin_top.to_px(box, width_of_containing_block) + margin_bottom.to_px(box, width_of_containing_block))).to_px(box); + margin_top = CSS::Length::make_px(remainder / 2); + margin_bottom = CSS::Length::make_px(remainder / 2); + }; - // then set top to the static position, - // and finally apply rule number three below. - - // NOTE: We actually perform these two steps in the opposite order, - // because the static position may depend on the height of the box (due to alignment properties). - - auto maybe_height = compute_auto_height_for_absolutely_positioned_element(box, available_space, before_or_after_inside_layout); - if (!maybe_height.has_value()) - return; - height = CSS::Size::make_px(maybe_height.value()); - - auto constrained_height = apply_min_max_height_constraints(maybe_height.value()); - m_state.get_mutable(box).set_content_height(constrained_height); - - auto static_position = calculate_static_position(box); - top = CSS::Length::make_px(static_position.y()); - - solve_for_bottom(); - } - - // If none of the three are auto: - else if (!top.is_auto() && !should_treat_height_as_auto(box, available_space) && !bottom.is_auto()) { - // If both margin-top and margin-bottom are auto, - if (margin_top.is_auto() && margin_bottom.is_auto()) { - // solve the equation under the extra constraint that the two margins get equal values. - solve_for_margin_top_and_margin_bottom(); - } - - // If one of margin-top or margin-bottom is auto, - else if (margin_top.is_auto() || margin_bottom.is_auto()) { - // solve the equation for that value. + // If all three of top, height, and bottom are auto: + if (top.is_auto() && height.is_auto() && bottom.is_auto()) { + // First set any auto values for margin-top and margin-bottom to 0, if (margin_top.is_auto()) - solve_for_margin_top(); - else - solve_for_margin_bottom(); - } + margin_top = CSS::Length::make_px(0); + if (margin_bottom.is_auto()) + margin_bottom = CSS::Length::make_px(0); - // If the values are over-constrained, - else { - // ignore the value for bottom and solve for that value. - solve_for_bottom(); - } - } - - // Otherwise, - else { - // set auto values for margin-top and margin-bottom to 0, - if (margin_top.is_auto()) - margin_top = CSS::Length::make_px(0); - if (margin_bottom.is_auto()) - margin_bottom = CSS::Length::make_px(0); - - // and pick one of the following six rules that apply. - - // 1. If top and height are auto and bottom is not auto, - if (top.is_auto() && should_treat_height_as_auto(box, available_space) && !bottom.is_auto()) { - // then the height is based on the Auto heights for block formatting context roots, - auto maybe_height = compute_auto_height_for_absolutely_positioned_element(box, available_space, before_or_after_inside_layout); - if (!maybe_height.has_value()) - return; - height = CSS::Size::make_px(maybe_height.value()); - - // and solve for top. - solve_for_top(); - } - - // 2. If top and bottom are auto and height is not auto, - else if (top.is_auto() && bottom.is_auto() && !should_treat_height_as_auto(box, available_space)) { // then set top to the static position, - top = CSS::Length::make_px(calculate_static_position(box).y()); + // and finally apply rule number three below. - // then solve for bottom. - solve_for_bottom(); - } + // NOTE: We actually perform these two steps in the opposite order, + // because the static position may depend on the height of the box (due to alignment properties). - // 3. If height and bottom are auto and top is not auto, - else if (should_treat_height_as_auto(box, available_space) && bottom.is_auto() && !top.is_auto()) { - // then the height is based on the Auto heights for block formatting context roots, auto maybe_height = compute_auto_height_for_absolutely_positioned_element(box, available_space, before_or_after_inside_layout); if (!maybe_height.has_value()) - return; - height = CSS::Size::make_px(maybe_height.value()); + return height; + height = CSS::Length::make_px(maybe_height.value()); + + auto constrained_height = apply_min_max_height_constraints(height); + m_state.get_mutable(box).set_content_height(constrained_height.to_px(box)); + + auto static_position = calculate_static_position(box); + top = CSS::Length::make_px(static_position.y()); - // and solve for bottom. solve_for_bottom(); } - // 4. If top is auto, height and bottom are not auto, - else if (top.is_auto() && !should_treat_height_as_auto(box, available_space) && !bottom.is_auto()) { - // then solve for top. - solve_for_top(); + // If none of the three are auto: + else if (!top.is_auto() && !height.is_auto() && !bottom.is_auto()) { + // If both margin-top and margin-bottom are auto, + if (margin_top.is_auto() && margin_bottom.is_auto()) { + // solve the equation under the extra constraint that the two margins get equal values. + solve_for_margin_top_and_margin_bottom(); + } + + // If one of margin-top or margin-bottom is auto, + else if (margin_top.is_auto() || margin_bottom.is_auto()) { + // solve the equation for that value. + if (margin_top.is_auto()) + solve_for_margin_top(); + else + solve_for_margin_bottom(); + } + + // If the values are over-constrained, + else { + // ignore the value for bottom and solve for that value. + solve_for_bottom(); + } } - // 5. If height is auto, top and bottom are not auto, - else if (should_treat_height_as_auto(box, available_space) && !top.is_auto() && !bottom.is_auto()) { - // then solve for height. - solve_for_height(); + // Otherwise, + else { + // set auto values for margin-top and margin-bottom to 0, + if (margin_top.is_auto()) + margin_top = CSS::Length::make_px(0); + if (margin_bottom.is_auto()) + margin_bottom = CSS::Length::make_px(0); + + // and pick one of the following six rules that apply. + + // 1. If top and height are auto and bottom is not auto, + if (top.is_auto() && height.is_auto() && !bottom.is_auto()) { + // then the height is based on the Auto heights for block formatting context roots, + auto maybe_height = compute_auto_height_for_absolutely_positioned_element(box, available_space, before_or_after_inside_layout); + if (!maybe_height.has_value()) + return height; + height = CSS::Length::make_px(maybe_height.value()); + + // and solve for top. + solve_for_top(); + } + + // 2. If top and bottom are auto and height is not auto, + else if (top.is_auto() && bottom.is_auto() && !height.is_auto()) { + // then set top to the static position, + top = CSS::Length::make_px(calculate_static_position(box).y()); + + // then solve for bottom. + solve_for_bottom(); + } + + // 3. If height and bottom are auto and top is not auto, + else if (height.is_auto() && bottom.is_auto() && !top.is_auto()) { + // then the height is based on the Auto heights for block formatting context roots, + auto maybe_height = compute_auto_height_for_absolutely_positioned_element(box, available_space, before_or_after_inside_layout); + if (!maybe_height.has_value()) + return height; + height = CSS::Length::make_px(maybe_height.value()); + + // and solve for bottom. + solve_for_bottom(); + } + + // 4. If top is auto, height and bottom are not auto, + else if (top.is_auto() && !height.is_auto() && !bottom.is_auto()) { + // then solve for top. + solve_for_top(); + } + + // 5. If height is auto, top and bottom are not auto, + else if (height.is_auto() && !top.is_auto() && !bottom.is_auto()) { + // then solve for height. + solve_for_height(); + } + + // 6. If bottom is auto, top and height are not auto, + else if (bottom.is_auto() && !top.is_auto() && !height.is_auto()) { + // then solve for bottom. + solve_for_bottom(); + } } - // 6. If bottom is auto, top and height are not auto, - else if (bottom.is_auto() && !top.is_auto() && !should_treat_height_as_auto(box, available_space)) { - // then solve for bottom. - solve_for_bottom(); - } - } + return height; + }; // Compute the height based on box type and CSS properties: // https://www.w3.org/TR/css-sizing-3/#box-sizing - CSSPixels used_height = 0; - if (should_treat_height_as_auto(box, available_space)) { - used_height = height.to_px(box, height_of_containing_block); - } else { - used_height = calculate_inner_height(box, available_space.height, height); - } + auto used_height = try_compute_height([&] { + if (is(box)) + return CSS::Length::make_px(compute_table_box_height_inside_table_wrapper(box, available_space)); + if (should_treat_height_as_auto(box, available_space)) + return CSS::Length::make_auto(); + return CSS::Length::make_px(calculate_inner_height(box, available_space.height, box.computed_values().height())); + }()); used_height = apply_min_max_height_constraints(used_height); @@ -1033,7 +1129,7 @@ void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_el // the final used values for vertical margin/border/padding. auto& box_state = m_state.get_mutable(box); - box_state.set_content_height(used_height); + box_state.set_content_height(used_height.to_px(box)); // do not set calculated insets or margins on the first pass, there will be a second pass if (before_or_after_inside_layout == BeforeOrAfterInsideLayout::Before) diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h index 15c6c69ef7..f3caedb649 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h @@ -50,6 +50,9 @@ public: static bool creates_block_formatting_context(Box const&); + CSSPixels compute_table_box_width_inside_table_wrapper(Box const&, AvailableSpace const&); + CSSPixels compute_table_box_height_inside_table_wrapper(Box const&, AvailableSpace const&); + CSSPixels compute_width_for_replaced_element(Box const&, AvailableSpace const&) const; CSSPixels compute_height_for_replaced_element(Box const&, AvailableSpace const&) const;