diff --git a/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index fc7e4b90537..a0a6de22fe9 100644 --- a/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/FlexFormattingContext.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024, Andreas Kling + * Copyright (c) 2021-2025, Andreas Kling * Copyright (c) 2021, Tobias Christiansen * * SPDX-License-Identifier: BSD-2-Clause @@ -94,7 +94,38 @@ void FlexFormattingContext::run(AvailableSpace const& available_space) // FIXME: Get rid of prepare_for_replaced_layout() and make replaced elements figure out their intrinsic size lazily. static_cast(*item.box).prepare_for_replaced_layout(); } - determine_flex_base_size_and_hypothetical_main_size(item); + determine_flex_base_size(item); + } + + // OPTIMIZATION: We try to avoid calculating the "automatic minimum size" if possible, + // since it may require expensive intrinsic sizing layout. + // If this is a single-line flex container and the sum of flex bases sizes + // won't exceed the available space in the main axis, we know the layout + // algorithm won't have to shrink anything, thus not needing the minimum size. + bool const should_skip_automatic_minimum_size_clamp = [&] { + if (m_layout_mode == LayoutMode::IntrinsicSizing) + return false; + if (!is_single_line()) + return false; + if (is_row_layout() && !m_flex_container_state.has_definite_width()) + return false; + if (!is_row_layout() && !m_flex_container_state.has_definite_height()) + return false; + CSSPixels sum_of_item_flex_base_sizes = 0; + for (auto& item : m_flex_items) { + if (!has_definite_main_size(item)) + return false; + sum_of_item_flex_base_sizes += item.flex_base_size; + } + if (sum_of_item_flex_base_sizes > m_available_space_for_items->main.to_px_or_zero()) + return false; + return true; + }(); + for (auto& item : m_flex_items) { + // The hypothetical main size is the item’s flex base size clamped according to its used min and max main sizes (and flooring the content box size at zero). + auto clamp_max = has_main_max_size(item.box) ? specified_main_max_size(item.box) : CSSPixels::max(); + auto clamp_min = has_main_min_size(item.box) ? specified_main_min_size(item.box) : (should_skip_automatic_minimum_size_clamp ? 0 : automatic_minimum_size(item)); + item.hypothetical_main_size = max(CSSPixels(0), css_clamp(item.flex_base_size, clamp_min, clamp_max)); } if (available_space.width.is_intrinsic_sizing_constraint() || available_space.height.is_intrinsic_sizing_constraint()) { @@ -556,8 +587,8 @@ CSSPixels FlexFormattingContext::adjust_cross_size_through_aspect_ratio_for_main return cross_size; } -// https://www.w3.org/TR/css-flexbox-1/#algo-main-item -void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(FlexItem& item) +// https://drafts.csswg.org/css-flexbox-1/#algo-main-item +void FlexFormattingContext::determine_flex_base_size(FlexItem& item) { auto& child_box = item.box; @@ -694,10 +725,7 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size( item.flex_base_size = adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(child_box, item.flex_base_size, computed_cross_min_size(child_box), computed_cross_max_size(child_box)); } - // The hypothetical main size is the item’s flex base size clamped according to its used min and max main sizes (and flooring the content box size at zero). - auto clamp_min = has_main_min_size(child_box) ? specified_main_min_size(child_box) : automatic_minimum_size(item); - auto clamp_max = has_main_max_size(child_box) ? specified_main_max_size(child_box) : CSSPixels::max(); - item.hypothetical_main_size = max(CSSPixels(0), css_clamp(item.flex_base_size, clamp_min, clamp_max)); + // NOTE: We don't clamp the main size until we have them for all items. } // https://drafts.csswg.org/css-flexbox-1/#min-size-auto diff --git a/Libraries/LibWeb/Layout/FlexFormattingContext.h b/Libraries/LibWeb/Layout/FlexFormattingContext.h index bc10319fa84..77362b092a2 100644 --- a/Libraries/LibWeb/Layout/FlexFormattingContext.h +++ b/Libraries/LibWeb/Layout/FlexFormattingContext.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024, Andreas Kling + * Copyright (c) 2021-2025, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -173,7 +173,7 @@ private: void determine_available_space_for_items(AvailableSpace const&); - void determine_flex_base_size_and_hypothetical_main_size(FlexItem&); + void determine_flex_base_size(FlexItem&); void collect_flex_items_into_flex_lines(); diff --git a/Tests/LibWeb/Layout/expected/flex/flex-optimization-cases.txt b/Tests/LibWeb/Layout/expected/flex/flex-optimization-cases.txt new file mode 100644 index 00000000000..6f3075771bb --- /dev/null +++ b/Tests/LibWeb/Layout/expected/flex/flex-optimization-cases.txt @@ -0,0 +1,245 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x2016 [BFC] children: not-inline + BlockContainer at (8,8) content-size 500x2000 children: not-inline + Box
at (8,8) content-size 500x17 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,8) content-size 78.59375x17 flex-item [BFC] children: inline + frag 0 from TextNode start: 0, length: 10, rect: [8,8 78.59375x17] baseline: 13.296875 + "abcdefghij" + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,25) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,25) content-size 500x17 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,25) content-size 0x17 flex-item [BFC] children: inline + frag 0 from TextNode start: 0, length: 10, rect: [8,25 78.59375x17] baseline: 13.296875 + "abcdefghij" + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,42) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,42) content-size 500x17 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,42) content-size 100x17 flex-item [BFC] children: inline + frag 0 from BlockContainer start: 0, length: 0, rect: [8,42 100x17] baseline: 13.296875 + TextNode <#text> + BlockContainer at (8,42) content-size 100x17 inline-block [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [8,42 9.703125x17] baseline: 13.296875 + "x" + TextNode <#text> + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,59) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,59) content-size 500x17 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,59) content-size 210.09375x17 flex-item [BFC] children: inline + frag 0 from TextNode start: 0, length: 24, rect: [8,59 210.09375x17] baseline: 13.296875 + "longwordlongwordlongword" + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,76) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,76) content-size 500x200 flex-container(column) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,76) content-size 500x200 flex-item [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,76) content-size 500x0 children: inline + TextNode <#text> + BlockContainer
at (8,76) content-size 500x200 children: not-inline + BlockContainer <(anonymous)> at (8,276) content-size 500x0 children: inline + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,276) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,276) content-size 500x17 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,276) content-size 500x17 flex-item [BFC] children: inline + frag 0 from TextNode start: 0, length: 10, rect: [8,276 78.59375x17] baseline: 13.296875 + "abcdefghij" + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,293) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,293) content-size 500x8 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + ImageBox at (8,293) content-size 8x8 flex-item children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,301) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,301) content-size 100x17 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,301) content-size 210.09375x17 flex-item [BFC] children: inline + frag 0 from TextNode start: 0, length: 24, rect: [8,301 210.09375x17] baseline: 13.296875 + "longwordlongwordlongword" + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,318) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,318) content-size 500x18.421875 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,318) content-size 36.84375x18.421875 flex-item [BFC] children: inline + frag 0 from TextNode start: 0, length: 5, rect: [8,318 36.84375x17] baseline: 13.296875 + "hello" + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,336.421875) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,336.421875) content-size 500x100 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,336.421875) content-size 200x100 flex-item [BFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,436.421875) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,436.421875) content-size 500x0 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,436.421875) content-size 0x0 flex-item [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,436.421875) content-size 0x0 children: inline + TextNode <#text> + BlockContainer
at (8,436.421875) content-size 0x100 children: not-inline + BlockContainer <(anonymous)> at (8,536.421875) content-size 0x0 children: inline + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,436.421875) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,436.421875) content-size 500x120 flex-container(column) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,436.421875) content-size 500x120 flex-item [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,436.421875) content-size 500x0 children: inline + TextNode <#text> + BlockContainer
at (8,436.421875) content-size 500x120 children: not-inline + BlockContainer <(anonymous)> at (8,556.421875) content-size 500x0 children: inline + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,556.421875) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,556.421875) content-size 500x300 flex-container(column) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,556.421875) content-size 500x300 flex-item [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,556.421875) content-size 500x0 children: inline + TextNode <#text> + BlockContainer
at (8,556.421875) content-size 500x300 children: not-inline + BlockContainer <(anonymous)> at (8,856.421875) content-size 500x0 children: inline + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,856.421875) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,856.421875) content-size 500x150 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer
at (8,856.421875) content-size 300x150 flex-item [BFC] children: not-inline + BlockContainer <(anonymous)> at (8,856.421875) content-size 300x0 children: inline + TextNode <#text> + BlockContainer
at (8,856.421875) content-size 300x150 children: not-inline + BlockContainer <(anonymous)> at (8,1006.421875) content-size 300x0 children: inline + TextNode <#text> + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,1006.421875) content-size 500x0 children: inline + TextNode <#text> + Box
at (8,1006.421875) content-size 500x8 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + ImageBox at (8,1006.421875) content-size 8x8 flex-item children: not-inline + BlockContainer <(anonymous)> (not painted) [BFC] children: inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,1014.421875) content-size 500x0 children: inline + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] overflow: [0,0 800x2016] + PaintableWithLines (BlockContainer) [0,0 800x2016] + PaintableWithLines (BlockContainer) [8,8 500x2000] + PaintableBox (Box
) [8,8 500x17] + PaintableWithLines (BlockContainer
) [8,8 78.59375x17] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,25 500x0] + PaintableBox (Box
) [8,25 500x17] + PaintableWithLines (BlockContainer
) [8,25 0x17] overflow: [8,25 78.59375x17] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,42 500x0] + PaintableBox (Box
) [8,42 500x17] + PaintableWithLines (BlockContainer
) [8,42 100x17] + PaintableWithLines (BlockContainer) [8,42 100x17] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,59 500x0] + PaintableBox (Box
) [8,59 500x17] + PaintableWithLines (BlockContainer
) [8,59 210.09375x17] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,76 500x0] + PaintableBox (Box
) [8,76 500x200] + PaintableWithLines (BlockContainer
) [8,76 500x200] + PaintableWithLines (BlockContainer(anonymous)) [8,76 500x0] + PaintableWithLines (BlockContainer
) [8,76 500x200] + PaintableWithLines (BlockContainer(anonymous)) [8,276 500x0] + PaintableWithLines (BlockContainer(anonymous)) [8,276 500x0] + PaintableBox (Box
) [8,276 500x17] + PaintableWithLines (BlockContainer
) [8,276 500x17] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,293 500x0] + PaintableBox (Box
) [8,293 500x8] + ImagePaintable (ImageBox) [8,293 8x8] + PaintableWithLines (BlockContainer(anonymous)) [8,301 500x0] + PaintableBox (Box
) [8,301 100x17] overflow: [8,301 210.09375x17] + PaintableWithLines (BlockContainer
) [8,301 210.09375x17] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,318 500x0] + PaintableBox (Box
) [8,318 500x18.421875] + PaintableWithLines (BlockContainer
) [8,318 36.84375x18.421875] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer(anonymous)) [8,336.421875 500x0] + PaintableBox (Box
) [8,336.421875 500x100] + PaintableWithLines (BlockContainer
) [8,336.421875 200x100] + PaintableWithLines (BlockContainer(anonymous)) [8,436.421875 500x0] + PaintableBox (Box
) [8,436.421875 500x0] + PaintableWithLines (BlockContainer
) [8,436.421875 0x0] + PaintableWithLines (BlockContainer(anonymous)) [8,436.421875 0x0] + PaintableWithLines (BlockContainer
) [8,436.421875 0x100] + PaintableWithLines (BlockContainer(anonymous)) [8,536.421875 0x0] + PaintableWithLines (BlockContainer(anonymous)) [8,436.421875 500x0] + PaintableBox (Box
) [8,436.421875 500x120] + PaintableWithLines (BlockContainer
) [8,436.421875 500x120] + PaintableWithLines (BlockContainer(anonymous)) [8,436.421875 500x0] + PaintableWithLines (BlockContainer
) [8,436.421875 500x120] + PaintableWithLines (BlockContainer(anonymous)) [8,556.421875 500x0] + PaintableWithLines (BlockContainer(anonymous)) [8,556.421875 500x0] + PaintableBox (Box
) [8,556.421875 500x300] + PaintableWithLines (BlockContainer
) [8,556.421875 500x300] + PaintableWithLines (BlockContainer(anonymous)) [8,556.421875 500x0] + PaintableWithLines (BlockContainer
) [8,556.421875 500x300] + PaintableWithLines (BlockContainer(anonymous)) [8,856.421875 500x0] + PaintableWithLines (BlockContainer(anonymous)) [8,856.421875 500x0] + PaintableBox (Box
) [8,856.421875 500x150] + PaintableWithLines (BlockContainer
) [8,856.421875 300x150] + PaintableWithLines (BlockContainer(anonymous)) [8,856.421875 300x0] + PaintableWithLines (BlockContainer
) [8,856.421875 300x150] + PaintableWithLines (BlockContainer(anonymous)) [8,1006.421875 300x0] + PaintableWithLines (BlockContainer(anonymous)) [8,1006.421875 500x0] + PaintableBox (Box
) [8,1006.421875 500x8] + ImagePaintable (ImageBox) [8,1006.421875 8x8] + PaintableWithLines (BlockContainer(anonymous)) [8,1014.421875 500x0] diff --git a/Tests/LibWeb/Layout/input/flex/flex-optimization-cases.html b/Tests/LibWeb/Layout/input/flex/flex-optimization-cases.html new file mode 100644 index 00000000000..591d854baf7 --- /dev/null +++ b/Tests/LibWeb/Layout/input/flex/flex-optimization-cases.html @@ -0,0 +1,83 @@ + + +
+
abcdefghij
+
+ +
+
abcdefghij
+
+ +
+
+ x +
+
+ +
+
longwordlongwordlongword
+
+ +
+
+
+
+
+ +
+
abcdefghij
+
+ +
+ +
+ +
+
longwordlongwordlongword
+
+ +
+
hello
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+ +