diff --git a/Tests/LibWeb/Layout/expected/flex-item-with-cyclic-percentage-height.txt b/Tests/LibWeb/Layout/expected/flex-item-with-cyclic-percentage-height.txt index 99f3fbea1a..566ea4d27e 100644 --- a/Tests/LibWeb/Layout/expected/flex-item-with-cyclic-percentage-height.txt +++ b/Tests/LibWeb/Layout/expected/flex-item-with-cyclic-percentage-height.txt @@ -2,7 +2,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline BlockContainer at (1,1) content-size 798x39.46875 [BFC] children: not-inline BlockContainer at (10,10) content-size 780x21.46875 children: not-inline Box at (11,11) content-size 778x19.46875 flex-container(row) [FFC] children: not-inline - Box at (12,12) content-size 44.03125x17.46875 flex-container(column) flex-item [FFC] children: not-inline + Box at (12,12) content-size 44.03125x19.46875 flex-container(column) flex-item [FFC] children: not-inline BlockContainer <(anonymous)> at (12,12) content-size 44.03125x17.46875 flex-item [BFC] children: inline line 0 width: 44.03125, height: 17.46875, bottom: 17.46875, baseline: 13.53125 frag 0 from TextNode start: 0, length: 6, rect: [12,12 44.03125x17.46875] @@ -11,8 +11,8 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [0,0 800x41.46875] - PaintableWithLines (BlockContainer) [9,9 782x23.46875] - PaintableBox (Box
.flexrow) [10,10 780x21.46875] - PaintableBox (Box
.project) [11,11 46.03125x19.46875] + PaintableWithLines (BlockContainer) [9,9 782x23.46875] overflow: [10,10 780x22.46875] + PaintableBox (Box
.flexrow) [10,10 780x21.46875] overflow: [11,11 778x21.46875] + PaintableBox (Box
.project) [11,11 46.03125x21.46875] PaintableWithLines (BlockContainer(anonymous)) [12,12 44.03125x17.46875] TextPaintable (TextNode<#text>) diff --git a/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt b/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt index 5ce0f4dc07..99f6d4198d 100644 --- a/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt +++ b/Tests/LibWeb/Layout/expected/flex/flex-item-with-intrinsic-aspect-ratio-and-max-height.txt @@ -1,11 +1,11 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline - BlockContainer at (1,1) content-size 798x69.96875 [BFC] children: not-inline - Box at (10,10) content-size 780x51.96875 flex-container(row) [FFC] children: not-inline - ImageBox at (11,11) content-size 66.65625x49.984375 flex-item children: not-inline + BlockContainer at (1,1) content-size 798x69.984375 [BFC] children: not-inline + Box at (10,10) content-size 780x51.984375 flex-container(row) [FFC] children: not-inline + ImageBox at (11,11) content-size 64x49.984375 flex-item children: not-inline BlockContainer <(anonymous)> (not painted) [BFC] children: inline TextNode <#text> ViewportPaintable (Viewport<#document>) [0,0 800x600] - PaintableWithLines (BlockContainer) [0,0 800x71.96875] - PaintableBox (Box) [9,9 782x53.96875] overflow: [10,10 780x51.984375] - ImagePaintable (ImageBox) [10,10 68.65625x51.984375] + PaintableWithLines (BlockContainer) [0,0 800x71.984375] + PaintableBox (Box) [9,9 782x53.984375] + ImagePaintable (ImageBox) [10,10 66x51.984375] diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index f3940afe84..a5e57e88ee 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -680,7 +680,8 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain place_block_level_element_in_normal_flow_horizontally(box, available_space); - if (box.is_replaced_box()) + // 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 (independent_formatting_context) { @@ -1247,15 +1248,4 @@ CSSPixels BlockFormattingContext::greatest_child_width(Box const& box) const return max_width; } -void BlockFormattingContext::determine_width_of_child(Box const&, AvailableSpace const&) -{ - // NOTE: We don't do anything here, since we'll have already determined the width of the child - // before recursing into nested layout within the child. -} - -void BlockFormattingContext::determine_height_of_child(Box const& box, AvailableSpace const& available_space) -{ - compute_height(box, available_space); -} - } diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index eec02a897b..089d915789 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -50,10 +50,6 @@ public: void layout_block_level_box(Box const&, BlockContainer const&, LayoutMode, CSSPixels& bottom_of_lowest_margin_box, AvailableSpace const&); - virtual bool can_determine_size_of_child() const override { return true; } - virtual void determine_width_of_child(Box const&, AvailableSpace const&) override; - virtual void determine_height_of_child(Box const&, AvailableSpace const&) override; - void resolve_vertical_box_model_metrics(Box const&); enum class DidIntroduceClearance { diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index 7da85a1426..c05125b000 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023, Andreas Kling + * Copyright (c) 2021-2024, Andreas Kling * Copyright (c) 2021, Tobias Christiansen * * SPDX-License-Identifier: BSD-2-Clause @@ -56,35 +56,17 @@ CSSPixels FlexFormattingContext::automatic_content_height() const return m_flex_container_state.content_height(); } -void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace const& available_content_space) +void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace const& available_space) { VERIFY(&run_box == &flex_container()); - // NOTE: The available space provided by the parent context is basically our *content box*. - // FFC is currently written in a way that expects that to include padding and border as well, - // so we pad out the available space here to accommodate that. - // FIXME: Refactor the necessary parts of FFC so we don't need this hack! - - auto available_width = available_content_space.width; - if (available_width.is_definite()) - available_width = AvailableSize::make_definite(available_width.to_px_or_zero() + m_flex_container_state.border_box_left() + m_flex_container_state.border_box_right()); - auto available_height = available_content_space.height; - if (available_height.is_definite()) - available_height = AvailableSize::make_definite(available_height.to_px_or_zero() + m_flex_container_state.border_box_top() + m_flex_container_state.border_box_bottom()); - - m_available_space_for_flex_container = AxisAgnosticAvailableSpace { - .main = is_row_layout() ? available_width : available_height, - .cross = !is_row_layout() ? available_width : available_height, - .space = { available_width, available_height }, - }; - // This implements https://www.w3.org/TR/css-flexbox-1/#layout-algorithm // 1. Generate anonymous flex items generate_anonymous_flex_items(); // 2. Determine the available main and cross space for the flex items - determine_available_space_for_items(AvailableSpace(available_width, available_height)); + determine_available_space_for_items(available_space); { // https://drafts.csswg.org/css-flexbox-1/#definite-sizes @@ -114,12 +96,16 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace c determine_flex_base_size_and_hypothetical_main_size(item); } - if (available_width.is_intrinsic_sizing_constraint() || available_height.is_intrinsic_sizing_constraint()) { + if (available_space.width.is_intrinsic_sizing_constraint() || available_space.height.is_intrinsic_sizing_constraint()) { // We're computing intrinsic size for the flex container. This happens at the end of run(). } else { - // 4. Determine the main size of the flex container - determine_main_size_of_flex_container(); + // Determine the main size of the flex container using the rules of the formatting context in which it participates. + // NOTE: The automatic block size of a block-level flex container is its max-content size. + + // NOTE: We've already handled this in the parent formatting context. + // Specifically, all formatting contexts will have assigned width & height to the flex container + // before this formatting context runs. } // 5. Collect flex items into flex lines: @@ -186,7 +172,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace c // 16. Align all flex lines (per align-content) align_all_flex_lines(); - if (available_width.is_intrinsic_sizing_constraint() || available_height.is_intrinsic_sizing_constraint()) { + if (available_space.width.is_intrinsic_sizing_constraint() || available_space.height.is_intrinsic_sizing_constraint()) { // We're computing intrinsic size for the flex container. determine_intrinsic_size_of_flex_container(); } else { @@ -195,7 +181,7 @@ void FlexFormattingContext::run(Box const& run_box, LayoutMode, AvailableSpace c copy_dimensions_from_flex_items_to_boxes(); for (auto& item : m_flex_items) { auto& box_state = m_state.get(item.box); - if (auto independent_formatting_context = layout_inside(item.box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(m_available_space_for_flex_container->space))) + if (auto independent_formatting_context = layout_inside(item.box, LayoutMode::Normal, box_state.available_inner_space_or_constraints_from(m_available_space_for_items->space))) independent_formatting_context->parent_context_did_dimension_child_root_box(); compute_inset(item.box); @@ -443,66 +429,17 @@ void FlexFormattingContext::set_main_axis_second_margin(FlexItem& item, CSSPixel // https://drafts.csswg.org/css-flexbox-1/#algo-available void FlexFormattingContext::determine_available_space_for_items(AvailableSpace const& available_space) { - // For each dimension, if that dimension of the flex container’s content box is a definite size, use that; - // if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint; - // otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. - // This might result in an infinite value. - - Optional available_width_for_items; - if (m_flex_container_state.has_definite_width()) { - available_width_for_items = AvailableSize::make_definite(m_flex_container_state.content_width()); - } else { - if (available_space.width.is_intrinsic_sizing_constraint()) { - available_width_for_items = available_space.width; - } else { - if (available_space.width.is_definite()) { - auto remaining = available_space.width.to_px_or_zero() - - m_flex_container_state.margin_left - - m_flex_container_state.margin_right - - m_flex_container_state.border_left - - m_flex_container_state.padding_right - - m_flex_container_state.padding_left - - m_flex_container_state.padding_right; - available_width_for_items = AvailableSize::make_definite(remaining); - } else { - available_width_for_items = AvailableSize::make_indefinite(); - } - } - } - - Optional available_height_for_items; - if (m_flex_container_state.has_definite_height()) { - available_height_for_items = AvailableSize::make_definite(m_flex_container_state.content_height()); - } else { - if (available_space.height.is_intrinsic_sizing_constraint()) { - available_height_for_items = available_space.height; - } else { - if (available_space.height.is_definite()) { - auto remaining = available_space.height.to_px_or_zero() - - m_flex_container_state.margin_top - - m_flex_container_state.margin_bottom - - m_flex_container_state.border_top - - m_flex_container_state.padding_bottom - - m_flex_container_state.padding_top - - m_flex_container_state.padding_bottom; - available_height_for_items = AvailableSize::make_definite(remaining); - } else { - available_height_for_items = AvailableSize::make_indefinite(); - } - } - } - if (is_row_layout()) { m_available_space_for_items = AxisAgnosticAvailableSpace { - .main = *available_width_for_items, - .cross = *available_height_for_items, - .space = { *available_width_for_items, *available_height_for_items }, + .main = available_space.width, + .cross = available_space.height, + .space = { available_space.width, available_space.height }, }; } else { m_available_space_for_items = AxisAgnosticAvailableSpace { - .main = *available_height_for_items, - .cross = *available_width_for_items, - .space = { *available_width_for_items, *available_height_for_items }, + .main = available_space.height, + .cross = available_space.width, + .space = { available_space.width, available_space.height }, }; } } @@ -785,59 +722,6 @@ CSSPixels FlexFormattingContext::content_based_minimum_size(FlexItem const& item return unclamped_size; } -bool FlexFormattingContext::can_determine_size_of_child() const -{ - return true; -} - -void FlexFormattingContext::determine_width_of_child(Box const&, AvailableSpace const&) -{ - // NOTE: For now, we simply do nothing here. If a child context is calling up to us - // and asking us to determine its width, we've already done so as part of the - // flex layout algorithm. -} - -void FlexFormattingContext::determine_height_of_child(Box const&, AvailableSpace const&) -{ - // NOTE: For now, we simply do nothing here. If a child context is calling up to us - // and asking us to determine its height, we've already done so as part of the - // flex layout algorithm. -} - -// https://drafts.csswg.org/css-flexbox-1/#algo-main-container -void FlexFormattingContext::determine_main_size_of_flex_container() -{ - // Determine the main size of the flex container using the rules of the formatting context in which it participates. - // NOTE: The automatic block size of a block-level flex container is its max-content size. - - // FIXME: The code below doesn't know how to size absolutely positioned flex containers at all. - // We just leave it alone for now and let the parent context deal with it. - if (flex_container().is_absolutely_positioned()) - return; - - // FIXME: Once all parent contexts now how to size a given child, we can remove - // `can_determine_size_of_child()`. - if (parent()->can_determine_size_of_child()) { - if (is_row_layout()) { - parent()->determine_width_of_child(flex_container(), m_available_space_for_flex_container->space); - } else { - parent()->determine_height_of_child(flex_container(), m_available_space_for_flex_container->space); - } - return; - } - - if (is_row_layout()) { - if (!flex_container().is_out_of_flow(*parent()) && m_state.get(*flex_container().containing_block()).has_definite_width()) { - set_main_size(flex_container(), calculate_stretch_fit_width(flex_container(), m_available_space_for_flex_container->space.width)); - } else { - set_main_size(flex_container(), calculate_max_content_width(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.to_px_or_zero())); - } -} - // https://www.w3.org/TR/css-flexbox-1/#algo-line-break void FlexFormattingContext::collect_flex_items_into_flex_lines() { @@ -1229,7 +1113,7 @@ void FlexFormattingContext::calculate_cross_size_of_each_flex_line() // If the flex container is single-line, then clamp the line’s cross-size to be within the container’s computed min and max cross sizes. // Note that if CSS 2.1’s definition of min/max-width/height applied more generally, this behavior would fall out automatically. // AD-HOC: We don't do this when the flex container is being sized under a min-content or max-content constraint. - if (is_single_line() && !m_available_space_for_flex_container->cross.is_intrinsic_sizing_constraint()) { + if (is_single_line() && !m_available_space_for_items->cross.is_intrinsic_sizing_constraint()) { auto const& computed_min_size = this->computed_cross_min_size(flex_container()); auto const& computed_max_size = this->computed_cross_max_size(flex_container()); auto cross_min_size = (!computed_min_size.is_auto() && !computed_min_size.contains_percentage()) ? specified_cross_min_size(flex_container()) : 0; @@ -1553,7 +1437,7 @@ void FlexFormattingContext::determine_flex_container_used_cross_size() } // AD-HOC: We don't apply min/max cross size constraints when sizing the flex container under an intrinsic sizing constraint. - if (!m_available_space_for_flex_container->cross.is_intrinsic_sizing_constraint()) { + if (!m_available_space_for_items->cross.is_intrinsic_sizing_constraint()) { auto const& computed_min_size = this->computed_cross_min_size(flex_container()); auto const& computed_max_size = this->computed_cross_max_size(flex_container()); auto cross_min_size = (!computed_min_size.is_auto() && !computed_min_size.contains_percentage()) ? specified_cross_min_size(flex_container()) : 0; @@ -1689,7 +1573,7 @@ void FlexFormattingContext::copy_dimensions_from_flex_items_to_boxes() // https://drafts.csswg.org/css-flexbox-1/#intrinsic-sizes void FlexFormattingContext::determine_intrinsic_size_of_flex_container() { - if (m_available_space_for_flex_container->main.is_intrinsic_sizing_constraint()) { + if (m_available_space_for_items->main.is_intrinsic_sizing_constraint()) { CSSPixels main_size = calculate_intrinsic_main_size_of_flex_container(); set_main_size(flex_container(), main_size); } @@ -2051,7 +1935,7 @@ CSSPixels FlexFormattingContext::calculate_fit_content_cross_size(FlexItem const CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const& item) const { if (is_row_layout()) { - auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_flex_container->space); + auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_items->space); if (available_space.width.is_indefinite()) { available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item)); } @@ -2063,7 +1947,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const CSSPixels FlexFormattingContext::calculate_max_content_cross_size(FlexItem const& item) const { if (is_row_layout()) { - auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_flex_container->space); + auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_items->space); if (available_space.width.is_indefinite()) { available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item)); } diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h index 599300b32e..70408019db 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021-2024, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -24,10 +24,6 @@ public: Box const& flex_container() const { return context_box(); } - virtual bool can_determine_size_of_child() const override; - virtual void determine_width_of_child(Box const&, AvailableSpace const&) override; - virtual void determine_height_of_child(Box const&, AvailableSpace const&) override; - virtual CSSPixelPoint calculate_static_position(Box const&) const override; private: @@ -162,8 +158,6 @@ private: void determine_flex_base_size_and_hypothetical_main_size(FlexItem&); - void determine_main_size_of_flex_container(); - void collect_flex_items_into_flex_lines(); void resolve_flexible_lengths(); @@ -229,7 +223,6 @@ private: AvailableSpace space; }; Optional m_available_space_for_items; - Optional m_available_space_for_flex_container; }; } diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h index 5a94155ec0..3d5f8d695d 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h @@ -89,10 +89,6 @@ public: [[nodiscard]] CSSPixels calculate_stretch_fit_width(Box const&, AvailableSize const&) const; [[nodiscard]] CSSPixels calculate_stretch_fit_height(Box const&, AvailableSize const&) const; - virtual bool can_determine_size_of_child() const { return false; } - virtual void determine_width_of_child(Box const&, AvailableSpace const&) { } - virtual void determine_height_of_child(Box const&, AvailableSpace const&) { } - virtual CSSPixelPoint calculate_static_position(Box const&) const; bool can_skip_is_anonymous_text_run(Box&); diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index 534693991f..3e8093dc34 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -172,6 +172,10 @@ void InlineFormattingContext::dimension_box_on_line(Box const& box, LayoutMode l box_state.set_content_width(width); + // 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_definite(m_containing_block_state.content_height()))); + 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(); @@ -389,21 +393,6 @@ bool InlineFormattingContext::can_fit_new_line_at_y(CSSPixels y) const return true; } -bool InlineFormattingContext::can_determine_size_of_child() const -{ - return parent().can_determine_size_of_child(); -} - -void InlineFormattingContext::determine_width_of_child(Box const& box, AvailableSpace const& available_space) -{ - return parent().determine_width_of_child(box, available_space); -} - -void InlineFormattingContext::determine_height_of_child(Box const& box, AvailableSpace const& available_space) -{ - return parent().determine_height_of_child(box, available_space); -} - CSSPixels InlineFormattingContext::vertical_float_clearance() const { return m_vertical_float_clearance; diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h index 1b21b669d7..72b4da4d4f 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h @@ -34,10 +34,6 @@ public: bool any_floats_intrude_at_y(CSSPixels y) const; bool can_fit_new_line_at_y(CSSPixels y) const; - virtual bool can_determine_size_of_child() const override; - virtual void determine_width_of_child(Box const&, AvailableSpace const&) override; - virtual void determine_height_of_child(Box const&, AvailableSpace const&) override; - CSSPixels vertical_float_clearance() const; void set_vertical_float_clearance(CSSPixels);