From 389f47f6fe63e2defe4a7a91ae6fdf5a5ac5c5e7 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 22 Sep 2022 14:07:28 +0200 Subject: [PATCH] LibWeb: Check both top and bottom of float position when looking for fit We have to check that there's enough space at both the top and bottom of the float's margin box, otherwise we risk overlapping existing content. --- Base/res/html/misc/float-stress-2.html | 42 +++++++++++++++++++ .../Libraries/LibWeb/Layout/LineBuilder.cpp | 37 +++++++++------- 2 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 Base/res/html/misc/float-stress-2.html diff --git a/Base/res/html/misc/float-stress-2.html b/Base/res/html/misc/float-stress-2.html new file mode 100644 index 00000000000..4f4c5bea6d6 --- /dev/null +++ b/Base/res/html/misc/float-stress-2.html @@ -0,0 +1,42 @@ + + + + +
first
second
diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp index 9f11e914df5..7e0efc8e1b6 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp @@ -95,27 +95,32 @@ void LineBuilder::append_text_chunk(TextNode const& text_node, size_t offset_in_ float LineBuilder::y_for_float_to_be_inserted_here(Box const& box) { auto const& box_state = m_layout_state.get(box); - auto width = box_state.margin_box_width(); + auto const width = box_state.margin_box_width(); + auto const height = box_state.margin_box_height(); - auto current_line_width = ensure_last_line_box().width(); - if (roundf(current_line_width + width) > m_available_width_for_current_line) { - float candidate_y = m_current_y + m_context.containing_block().line_height(); - // FIXME: This is super dumb, we move 1px downwards per iteration and stop - // when we find an Y value where we don't collide with other floats. - while (true) { - if (width > m_context.available_space_for_line(candidate_y)) { - if (!m_context.any_floats_intrude_at_y(candidate_y)) { - return candidate_y; - } - } else { + float candidate_y = m_current_y; + + float current_line_width = ensure_last_line_box().width(); + // If there's already inline content on the current line, check if the new float can fit + // alongside the content. If not, place it on the next line. + if (current_line_width > 0 && roundf(current_line_width + width) > m_available_width_for_current_line) + candidate_y += m_context.containing_block().line_height(); + + // Then, look for the next Y position where we can fit the new float. + // FIXME: This is super dumb, we move 1px downwards per iteration and stop + // when we find an Y value where we don't collide with other floats. + while (true) { + auto space_at_y_top = m_context.available_space_for_line(candidate_y); + auto space_at_y_bottom = m_context.available_space_for_line(candidate_y + height); + if (width >= space_at_y_top || width >= space_at_y_bottom) { + if (!m_context.any_floats_intrude_at_y(candidate_y) && !m_context.any_floats_intrude_at_y(candidate_y + height)) { return candidate_y; } - candidate_y += 1; + } else { + return candidate_y; } - - return m_current_y + m_context.containing_block().line_height(); + candidate_y += 1; } - return m_current_y; } bool LineBuilder::should_break(float next_item_width)