From 09f5ce42f687105926e17abf91617e535a38112a Mon Sep 17 00:00:00 2001 From: Ruben <96021831+rubenqwertyuiop@users.noreply.github.com> Date: Sun, 18 May 2025 19:03:57 +0200 Subject: [PATCH] LibWeb: Ensure floats are correctly placed under the preceding float Previously floats would be placed next to the highest float that fitted the new float on its line. However, this violates the rule that floats should be placed under the preceding float if it does not fit next to it. --- .../LibWeb/Layout/BlockFormattingContext.cpp | 1 + .../LibWeb/Layout/BlockFormattingContext.h | 37 ++++++++++--------- Libraries/LibWeb/Layout/LineBuilder.cpp | 8 ++++ .../float-under-if-would-fit.txt | 33 +++++++++++++++++ .../float-under-if-would-fit.html | 32 ++++++++++++++++ 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 Tests/LibWeb/Layout/expected/block-and-inline/float-under-if-would-fit.txt create mode 100644 Tests/LibWeb/Layout/input/block-and-inline/float-under-if-would-fit.html diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 3f340c5a810..c2dbe4f8f82 100644 --- a/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -1147,6 +1147,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer .margin_box_rect_in_root_coordinate_space = margin_box_rect_in_ancestor_coordinate_space(box_state, root()), })); side_data.current_boxes.append(*side_data.all_boxes.last()); + m_last_inserted_float = *side_data.all_boxes.last(); if (side == FloatSide::Left) { side_data.current_width = offset_from_edge + box_state.content_width() + box_state.margin_box_right(); diff --git a/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Libraries/LibWeb/Layout/BlockFormattingContext.h index 7ba4077c805..d535bb2bf6f 100644 --- a/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -74,6 +74,25 @@ public: void reset_margin_state() { m_margin_state.reset(); } + struct FloatingBox { + GC::Ref box; + + LayoutState::UsedValues& used_values; + + // Offset from left/right edge to the left content edge of `box`. + CSSPixels offset_from_edge { 0 }; + + // Top margin edge of `box`. + CSSPixels top_margin_edge { 0 }; + + // Bottom margin edge of `box`. + CSSPixels bottom_margin_edge { 0 }; + + CSSPixelRect margin_box_rect_in_root_coordinate_space; + }; + + Optional last_inserted_float() { return m_last_inserted_float; } + private: CSSPixels compute_auto_height_for_block_level_element(Box const&, AvailableSpace const&); @@ -99,23 +118,6 @@ private: Right, }; - struct FloatingBox { - GC::Ref box; - - LayoutState::UsedValues& used_values; - - // Offset from left/right edge to the left content edge of `box`. - CSSPixels offset_from_edge { 0 }; - - // Top margin edge of `box`. - CSSPixels top_margin_edge { 0 }; - - // Bottom margin edge of `box`. - CSSPixels bottom_margin_edge { 0 }; - - CSSPixelRect margin_box_rect_in_root_coordinate_space; - }; - struct FloatSideData { // Floating boxes currently accumulating on this side. Vector current_boxes; @@ -189,6 +191,7 @@ private: FloatSideData m_left_floats; FloatSideData m_right_floats; + Optional m_last_inserted_float; bool m_was_notified_after_parent_dimensioned_my_root_box { false }; }; diff --git a/Libraries/LibWeb/Layout/LineBuilder.cpp b/Libraries/LibWeb/Layout/LineBuilder.cpp index a02f516e07a..59d5ed72a02 100644 --- a/Libraries/LibWeb/Layout/LineBuilder.cpp +++ b/Libraries/LibWeb/Layout/LineBuilder.cpp @@ -129,6 +129,14 @@ CSSPixels LineBuilder::y_for_float_to_be_inserted_here(Box const& box) // Then, look for the next Y position where we can fit the new float. auto box_in_root_rect = m_context.parent().content_box_rect_in_ancestor_coordinate_space(box_state, m_context.parent().root()); + // New floats will always be placed vertically at or below the lowest float. + // This applies to all floats, so the last inserted float will always be the lowest. + auto last_float = m_context.parent().last_inserted_float(); + if (last_float.has_value()) { + auto float_box_top = last_float->margin_box_rect_in_root_coordinate_space.top() - box_in_root_rect.y(); + candidate_block_offset = max(candidate_block_offset, float_box_top); + } + HashMap available_space_cache; for (;;) { Optional highest_intersection_bottom; diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/float-under-if-would-fit.txt b/Tests/LibWeb/Layout/expected/block-and-inline/float-under-if-would-fit.txt new file mode 100644 index 00000000000..cc391628c45 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/block-and-inline/float-under-if-would-fit.txt @@ -0,0 +1,33 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (0,0) content-size 800x168 [BFC] children: not-inline + BlockContainer <(anonymous)> at (0,0) content-size 800x0 children: inline + TextNode <#text> + BlockContainer at (8,8) content-size 784x152 children: not-inline + BlockContainer <(anonymous)> at (8,8) content-size 784x0 children: inline + TextNode <#text> + BlockContainer at (9,9) content-size 100x150 children: inline + frag 0 from TextNode start: 1, length: 11, rect: [9,9 99.109375x18] baseline: 13.796875 + "abc abc abc" + frag 1 from TextNode start: 13, length: 11, rect: [9,27 99.109375x18] baseline: 13.796875 + "abc abc abc" + TextNode <#text> + BlockContainer at (9,45) content-size 80x50 floating [BFC] children: not-inline + TextNode <#text> + BlockContainer at (9,95) content-size 40x50 floating [BFC] children: not-inline + TextNode <#text> + BlockContainer at (49,95) content-size 20x50 floating [BFC] children: not-inline + TextNode <#text> + BlockContainer <(anonymous)> at (8,160) content-size 784x0 children: inline + TextNode <#text> + +ViewportPaintable (Viewport<#document>) [0,0 800x600] + PaintableWithLines (BlockContainer) [0,0 800x168] + PaintableWithLines (BlockContainer(anonymous)) [0,0 800x0] + PaintableWithLines (BlockContainer) [8,8 784x152] + PaintableWithLines (BlockContainer(anonymous)) [8,8 784x0] + PaintableWithLines (BlockContainer
.container) [8,8 102x152] + TextPaintable (TextNode<#text>) + PaintableWithLines (BlockContainer
.left.w80.red) [9,45 80x50] + PaintableWithLines (BlockContainer
.left.w40.yellow) [9,95 40x50] + PaintableWithLines (BlockContainer
.left.w20.blue) [49,95 20x50] + PaintableWithLines (BlockContainer(anonymous)) [8,160 784x0] diff --git a/Tests/LibWeb/Layout/input/block-and-inline/float-under-if-would-fit.html b/Tests/LibWeb/Layout/input/block-and-inline/float-under-if-would-fit.html new file mode 100644 index 00000000000..ea36ab652fc --- /dev/null +++ b/Tests/LibWeb/Layout/input/block-and-inline/float-under-if-would-fit.html @@ -0,0 +1,32 @@ + + + + + +
+ abc abc abc + abc abc abc +
+
+
+
+