diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/float-left-and-right-with-text-in-between.txt b/Tests/LibWeb/Layout/expected/block-and-inline/float-left-and-right-with-text-in-between.txt new file mode 100644 index 00000000000..de998e86b14 --- /dev/null +++ b/Tests/LibWeb/Layout/expected/block-and-inline/float-left-and-right-with-text-in-between.txt @@ -0,0 +1,62 @@ +Viewport <#document> at (0,0) content-size 800x600 children: not-inline + BlockContainer at (1,1) content-size 798x600 [BFC] children: not-inline + BlockContainer at (252,10) content-size 538x398.257812 children: inline + line 0 width: 228.339843, height: 21.835937, bottom: 21.835937, baseline: 16.914062 + frag 0 from TextNode start: 1, length: 21, rect: [554,10 228.339843x21.835937] + "Lorem ipsum dolor sit" + line 1 width: 183.769531, height: 22.671875, bottom: 44.507812, baseline: 16.914062 + frag 0 from TextNode start: 23, length: 17, rect: [554,31 183.769531x21.835937] + "amet, consectetur" + line 2 width: 140.546875, height: 22.507812, bottom: 66.179687, baseline: 16.914062 + frag 0 from TextNode start: 41, length: 16, rect: [554,53 140.546875x21.835937] + "adipiscing elit." + line 3 width: 145, height: 22.34375, bottom: 87.851562, baseline: 16.914062 + frag 0 from TextNode start: 58, length: 13, rect: [554,75 145x21.835937] + "Suspendisse a" + line 4 width: 196.699218, height: 22.179687, bottom: 109.523437, baseline: 16.914062 + frag 0 from TextNode start: 72, length: 19, rect: [554,97 196.699218x21.835937] + "placerat mauris, ut" + line 5 width: 234.6875, height: 22.015625, bottom: 131.195312, baseline: 16.914062 + frag 0 from TextNode start: 92, length: 22, rect: [554,119 234.6875x21.835937] + "elementum mi. Morbi ut" + line 6 width: 201.523437, height: 21.851562, bottom: 152.867187, baseline: 16.914062 + frag 0 from TextNode start: 115, length: 20, rect: [554,141 201.523437x21.835937] + "vehicula ipsum, eget" + line 7 width: 232.539062, height: 22.6875, bottom: 175.539062, baseline: 16.914062 + frag 0 from TextNode start: 136, length: 23, rect: [554,162 232.539062x21.835937] + "placerat augue. Integer" + line 8 width: 202.96875, height: 22.523437, bottom: 197.210937, baseline: 16.914062 + frag 0 from TextNode start: 160, length: 20, rect: [554,184 202.96875x21.835937] + "rutrum nisi eget dui" + line 9 width: 0, height: 0, bottom: 0, baseline: 0 + line 10 width: 208.828125, height: 22.359375, bottom: 223.882812, baseline: 16.914062 + frag 0 from TextNode start: 181, length: 19, rect: [252,211 208.828125x21.835937] + "dictum, eu accumsan" + line 11 width: 180.195312, height: 22.195312, bottom: 245.554687, baseline: 16.914062 + frag 0 from TextNode start: 201, length: 18, rect: [252,233 180.195312x21.835937] + "enim tristique. Ut" + line 12 width: 195.273437, height: 22.03125, bottom: 267.226562, baseline: 16.914062 + frag 0 from TextNode start: 220, length: 19, rect: [252,255 195.273437x21.835937] + "lobortis lorem eget" + line 13 width: 222.910156, height: 21.867187, bottom: 288.898437, baseline: 16.914062 + frag 0 from TextNode start: 240, length: 22, rect: [252,277 222.910156x21.835937] + "est vulputate egestas." + line 14 width: 223.125, height: 22.703125, bottom: 311.570312, baseline: 16.914062 + frag 0 from TextNode start: 263, length: 23, rect: [252,298 223.125x21.835937] + "Integer laoreet lacinia" + line 15 width: 222.617187, height: 22.539062, bottom: 333.242187, baseline: 16.914062 + frag 0 from TextNode start: 287, length: 22, rect: [252,320 222.617187x21.835937] + "ante sodales lobortis." + line 16 width: 178.300781, height: 22.375, bottom: 354.914062, baseline: 16.914062 + frag 0 from TextNode start: 310, length: 17, rect: [252,342 178.300781x21.835937] + "Donec a tincidunt" + line 17 width: 231.074218, height: 22.210937, bottom: 376.585937, baseline: 16.914062 + frag 0 from TextNode start: 328, length: 22, rect: [252,364 231.074218x21.835937] + "ante. Phasellus a arcu" + line 18 width: 70.546875, height: 22.046875, bottom: 398.257812, baseline: 16.914062 + frag 0 from TextNode start: 351, length: 7, rect: [252,386 70.546875x21.835937] + "tortor." + BlockContainer at (253,11) content-size 300x200 floating [BFC] children: not-inline + TextNode <#text> + BlockContainer at (489,213) content-size 300x200 floating [BFC] children: not-inline + TextNode <#text> diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-1.txt b/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-1.txt index 951a049857b..c1ed8ecd0cb 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-1.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-1.txt @@ -5,18 +5,18 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline TextNode <#text> BlockContainer at (9,9) content-size 300x250 children: inline line 0 width: 239.15625, height: 16, bottom: 16, baseline: 12.796875 - frag 0 from TextNode start: 1, length: 24, rect: [61,9 212x16] + frag 0 from TextNode start: 1, length: 24, rect: [60,9 212x16] "foo bar baz foo bar baz " - frag 1 from TextNode start: 1, length: 3, rect: [273,9 27.15625x16] + frag 1 from TextNode start: 1, length: 3, rect: [272,9 27.15625x16] "foo" line 1 width: 27.640625, height: 16, bottom: 32, baseline: 12.796875 - frag 0 from TextNode start: 5, length: 3, rect: [263,25 27.640625x16] + frag 0 from TextNode start: 5, length: 3, rect: [262,25 27.640625x16] "bar" line 2 width: 27.203125, height: 16, bottom: 48, baseline: 12.796875 - frag 0 from TextNode start: 9, length: 3, rect: [263,41 27.203125x16] + frag 0 from TextNode start: 9, length: 3, rect: [262,41 27.203125x16] "baz" line 3 width: 27.15625, height: 16, bottom: 64, baseline: 12.796875 - frag 0 from TextNode start: 13, length: 3, rect: [263,57 27.15625x16] + frag 0 from TextNode start: 13, length: 3, rect: [262,57 27.15625x16] "foo" line 4 width: 0, height: 0, bottom: 0, baseline: 0 line 5 width: 98, height: 16, bottom: 84, baseline: 12.796875 @@ -40,20 +40,20 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline frag 0 from TextNode start: 21, length: 31, rect: [9,157 274.359375x16] "baz foo bar baz foo bar baz foo" line 11 width: 239.640625, height: 16, bottom: 180, baseline: 12.796875 - frag 0 from TextNode start: 53, length: 20, rect: [61,173 176.84375x16] + frag 0 from TextNode start: 53, length: 20, rect: [60,173 176.84375x16] "bar baz foo bar baz " - frag 1 from TextNode start: 1, length: 7, rect: [238,173 62.796875x16] + frag 1 from TextNode start: 1, length: 7, rect: [237,173 62.796875x16] "foo bar" line 12 width: 204, height: 16, bottom: 196, baseline: 12.796875 - frag 0 from TextNode start: 9, length: 16, rect: [61,189 141.203125x16] + frag 0 from TextNode start: 9, length: 16, rect: [60,189 141.203125x16] "baz foo bar baz " - frag 1 from TextNode start: 1, length: 7, rect: [202,189 62.796875x16] + frag 1 from TextNode start: 1, length: 7, rect: [201,189 62.796875x16] "foo bar" line 13 width: 204, height: 16, bottom: 212, baseline: 12.796875 - frag 0 from TextNode start: 9, length: 23, rect: [61,205 204x16] + frag 0 from TextNode start: 9, length: 23, rect: [60,205 204x16] "baz foo bar baz foo bar" line 14 width: 239.203125, height: 16, bottom: 228, baseline: 12.796875 - frag 0 from TextNode start: 33, length: 27, rect: [61,221 239.203125x16] + frag 0 from TextNode start: 33, length: 27, rect: [60,221 239.203125x16] "baz foo bar baz foo bar baz" line 15 width: 274.796875, height: 16, bottom: 244, baseline: 12.796875 frag 0 from TextNode start: 61, length: 31, rect: [9,237 274.796875x16] diff --git a/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-3.txt b/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-3.txt index db2dc0cbe36..86c2821ed36 100644 --- a/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-3.txt +++ b/Tests/LibWeb/Layout/expected/block-and-inline/float-stress-3.txt @@ -5,7 +5,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline TextNode <#text> BlockContainer at (9,9) content-size 300x250 children: inline line 0 width: 204, height: 16, bottom: 16, baseline: 12.796875 - frag 0 from TextNode start: 1, length: 23, rect: [61,9 204x16] + frag 0 from TextNode start: 1, length: 23, rect: [60,9 204x16] "foo bar baz foo bar baz" TextNode <#text> BlockContainer at (10,10) content-size 50x50 floating [BFC] children: not-inline diff --git a/Tests/LibWeb/Layout/input/block-and-inline/float-left-and-right-with-text-in-between.html b/Tests/LibWeb/Layout/input/block-and-inline/float-left-and-right-with-text-in-between.html new file mode 100644 index 00000000000..64d6bc99f5f --- /dev/null +++ b/Tests/LibWeb/Layout/input/block-and-inline/float-left-and-right-with-text-in-between.html @@ -0,0 +1,24 @@ + +
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse a placerat mauris, ut elementum mi. Morbi ut vehicula ipsum, eget placerat augue. Integer rutrum nisi eget dui dictum, eu accumsan enim tristique. Ut lobortis lorem eget est vulputate egestas. Integer laoreet lacinia ante sodales lobortis. Donec a tincidunt ante. Phasellus a arcu tortor. diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp index 2a92319614e..07159f8964f 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp @@ -125,9 +125,8 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const& // sufficient space. They may even make the border box of said element narrower than defined by section 10.3.3. // CSS2 does not define when a UA may put said element next to the float or by how much said element may // become narrower. - auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(box, root(), m_state); - auto space = space_used_by_floats(box_in_root_rect.y()); - auto remaining_width = available_space.width.to_px() - space.left - space.right; + auto intrusion = intrusion_by_floats_into_box(box, 0); + auto remaining_width = available_space.width.to_px() - intrusion.left - intrusion.right; remaining_available_space.width = AvailableSize::make_definite(remaining_width); } @@ -994,7 +993,13 @@ BlockFormattingContext::SpaceUsedByFloats BlockFormattingContext::space_used_by_ // NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid. auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state); if (rect.contains_vertically(y.value())) { - space_used_by_floats.left = floating_box.offset_from_edge + CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0; + for (auto const* containing_block = floating_box.box->containing_block(); containing_block && containing_block != &root(); containing_block = containing_block->containing_block()) { + auto const& containing_block_state = m_state.get(*containing_block); + offset_from_containing_block_chain_margins_between_here_and_root = max(offset_from_containing_block_chain_margins_between_here_and_root, containing_block_state.margin_box_left()); + } + space_used_by_floats.left = offset_from_containing_block_chain_margins_between_here_and_root + + floating_box.offset_from_edge + floating_box_state.content_width() + floating_box_state.margin_box_right(); break; @@ -1007,7 +1012,13 @@ BlockFormattingContext::SpaceUsedByFloats BlockFormattingContext::space_used_by_ // NOTE: The floating box is *not* in the final horizontal position yet, but the size and vertical position is valid. auto rect = margin_box_rect_in_ancestor_coordinate_space(floating_box.box, root(), m_state); if (rect.contains_vertically(y.value())) { - space_used_by_floats.right = floating_box.offset_from_edge + CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0; + for (auto const* containing_block = floating_box.box->containing_block(); containing_block && containing_block != &root(); containing_block = containing_block->containing_block()) { + auto const& containing_block_state = m_state.get(*containing_block); + offset_from_containing_block_chain_margins_between_here_and_root = max(offset_from_containing_block_chain_margins_between_here_and_root, containing_block_state.margin_box_right()); + } + space_used_by_floats.right = offset_from_containing_block_chain_margins_between_here_and_root + + floating_box.offset_from_edge + floating_box_state.margin_box_left(); break; } @@ -1016,6 +1027,25 @@ BlockFormattingContext::SpaceUsedByFloats BlockFormattingContext::space_used_by_ return space_used_by_floats; } +FormattingContext::SpaceUsedByFloats BlockFormattingContext::intrusion_by_floats_into_box(Box const& box, CSSPixels y_in_box) const +{ + // NOTE: Floats are relative to the BFC root box, not necessarily the containing block of this IFC. + auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(box, root(), m_state); + CSSPixels y_in_root = box_in_root_rect.y() + y_in_box; + auto space_used_by_floats_in_root = space_used_by_floats(y_in_root); + + auto left_intrusion = max(CSSPixels(0), space_used_by_floats_in_root.left - box_in_root_rect.x()); + + CSSPixels offset_from_containing_block_chain_margins_between_here_and_root = 0; + for (auto const* containing_block = static_cast(&box); containing_block && containing_block != &root(); containing_block = containing_block->containing_block()) { + auto const& containing_block_state = m_state.get(*containing_block); + offset_from_containing_block_chain_margins_between_here_and_root = max(offset_from_containing_block_chain_margins_between_here_and_root, containing_block_state.margin_box_right()); + } + auto right_intrusion = max(CSSPixels(0), space_used_by_floats_in_root.right - offset_from_containing_block_chain_margins_between_here_and_root); + + return { left_intrusion, right_intrusion }; +} + CSSPixels BlockFormattingContext::greatest_child_width(Box const& box) const { // Similar to FormattingContext::greatest_child_width() diff --git a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h index 4b16599d761..8b8c04426ae 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h @@ -40,6 +40,7 @@ public: void add_absolutely_positioned_box(Box const& box) { m_absolutely_positioned_boxes.append(box); } SpaceUsedByFloats space_used_by_floats(CSSPixels y) const; + SpaceUsedByFloats intrusion_by_floats_into_box(Box const&, CSSPixels y_in_box) const; virtual CSSPixels greatest_child_width(Box const&) const override; diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index df19494cf01..d87e561b217 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -44,21 +44,20 @@ CSSPixels InlineFormattingContext::leftmost_x_offset_at(CSSPixels y) const auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(containing_block(), parent().root(), m_state); CSSPixels y_in_root = box_in_root_rect.y() + y; auto space = parent().space_used_by_floats(y_in_root); - return space.left; + if (box_in_root_rect.x() >= space.left) { + // The left edge of the containing block is to the right of the rightmost left-side float. + // We start placing inline content at the left edge of the containing block. + return 0; + } + // The left edge of the containing block is to the left of the rightmost left-side float. + // We adjust the inline content insertion point by the overlap between the containing block and the float. + return space.left - box_in_root_rect.x(); } CSSPixels InlineFormattingContext::available_space_for_line(CSSPixels y) const { - // NOTE: Floats are relative to the BFC root box, not necessarily the containing block of this IFC. - auto& root_block = parent().root(); - auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(containing_block(), root_block, m_state); - CSSPixels y_in_root = box_in_root_rect.y() + y; - auto space = parent().space_used_by_floats(y_in_root); - - space.left = space.left; - space.right = min(m_available_space->width.to_px() - space.right, m_available_space->width.to_px()); - - return space.right - space.left; + auto intrusions = parent().intrusion_by_floats_into_box(containing_block(), y); + return m_available_space->width.to_px() - (intrusions.left + intrusions.right); } CSSPixels InlineFormattingContext::automatic_content_width() const @@ -330,15 +329,22 @@ bool InlineFormattingContext::any_floats_intrude_at_y(CSSPixels y) const bool InlineFormattingContext::can_fit_new_line_at_y(CSSPixels y) const { - auto box_in_root_rect = content_box_rect_in_ancestor_coordinate_space(containing_block(), parent().root(), m_state); - CSSPixels y_in_root = box_in_root_rect.y() + y; - auto space_top = parent().space_used_by_floats(y_in_root); - auto space_bottom = parent().space_used_by_floats(y_in_root + containing_block().line_height() - 1); - [[maybe_unused]] auto top_left_edge = space_top.left; - [[maybe_unused]] auto top_right_edge = m_available_space->width.to_px() - space_top.right; - [[maybe_unused]] auto bottom_left_edge = space_bottom.left; - [[maybe_unused]] auto bottom_right_edge = m_available_space->width.to_px() - space_bottom.right; + auto top_intrusions = parent().intrusion_by_floats_into_box(containing_block(), y); + auto bottom_intrusions = parent().intrusion_by_floats_into_box(containing_block(), y + containing_block().line_height() - 1); + + auto left_edge = [](auto& space) -> CSSPixels { + return space.left; + }; + + auto right_edge = [this](auto& space) -> CSSPixels { + return m_available_space->width.to_px() - space.right; + }; + + auto top_left_edge = left_edge(top_intrusions); + auto top_right_edge = right_edge(top_intrusions); + auto bottom_left_edge = left_edge(bottom_intrusions); + auto bottom_right_edge = right_edge(bottom_intrusions); if (top_left_edge > bottom_right_edge) return false; diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp index 09672032252..85a1ab501d9 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp @@ -167,10 +167,6 @@ void LineBuilder::update_last_line() CSSPixels x_offset_bottom = m_context.leftmost_x_offset_at(m_current_y + current_line_height - 1); CSSPixels x_offset = max(x_offset_top, x_offset_bottom); - // If the IFC's containing block has left-side margin, it has already been shifted to the right by that amount. - // We subtract the margin-left here to ensure that the left-side "space used by floats" doesn't get applied twice. - x_offset = max(CSSPixels(0), x_offset - m_containing_block_state.margin_left); - CSSPixels excess_horizontal_space = m_available_width_for_current_line - line_box.width(); switch (text_align) {