From 027c9f53be4b87cf55d799cb41abffa55173f11d Mon Sep 17 00:00:00 2001 From: Callum Law Date: Mon, 11 Aug 2025 03:08:18 +1200 Subject: [PATCH] LibWeb: Handle `text-overflow: ellipsis` with trailing whitespace We should calculate whether we need to truncate text with an ellipsis exclusive of any trailing collapsible whitespace. This was causing issues where an inline element was automatically sized (e.g. min-content) as we would calculate available width exclusive of trailing collapsible whitespace and then our text chunk would be wider, always inserting an ellipsis. Fixes the visual element of #4048 but we still are incorrect in how we collapse whitespace more generally --- .../LibWeb/Layout/InlineFormattingContext.cpp | 24 +++++++++++++++++-- ...ellipsis-with-trailing-whitespace-ref.html | 2 ++ ...low-ellipsis-with-trailing-whitespace.html | 12 ++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Tests/LibWeb/Ref/expected/text-overflow-ellipsis-with-trailing-whitespace-ref.html create mode 100644 Tests/LibWeb/Ref/input/text-overflow-ellipsis-with-trailing-whitespace.html diff --git a/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index 00eca44b53f..d074a7b8d76 100644 --- a/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -350,8 +350,28 @@ void InlineFormattingContext::generate_line_boxes() && text_node.computed_values().overflow_x() != CSS::Overflow::Visible) { // We may need to do an ellipsis if the text is too long for the container constexpr u32 ellipsis_codepoint = 0x2026; - if (m_available_space.has_value() - && item.width.to_double() > m_available_space.value().width.to_px_or_zero().to_double()) { + + // NOTE: We compute whether we need to truncate the text with an ellipsis based on the width of the + // text node EXCLUSIVE of trailing collapsible whitespace, this is because that trailing + // collapsible whitespace is removed later when we trim the line-boxes anyway. + // Note that this relies on the assumption that this text node is the last in this linebox (as + // there is only one text-node per line box when we have text-wrap: nowrap) + double width_without_trailing_collapsible_whitespace = item.width.to_double(); + + if (first_is_one_of(text_node.computed_values().white_space_collapse(), CSS::WhiteSpaceCollapse::Collapse, CSS::WhiteSpaceCollapse::PreserveBreaks)) { + auto last_text = text_node.text_for_rendering(); + + size_t last_fragment_length = last_text.length_in_code_units(); + while (last_fragment_length) { + auto last_character = last_text.code_unit_at(--last_fragment_length); + if (!is_ascii_space(last_character)) + break; + + width_without_trailing_collapsible_whitespace -= item.glyph_run->font().glyph_width(last_character); + } + } + + if (m_available_space.has_value() && width_without_trailing_collapsible_whitespace > m_available_space.value().width.to_px_or_zero().to_double()) { // Do the ellipsis auto& glyph_run = item.glyph_run; diff --git a/Tests/LibWeb/Ref/expected/text-overflow-ellipsis-with-trailing-whitespace-ref.html b/Tests/LibWeb/Ref/expected/text-overflow-ellipsis-with-trailing-whitespace-ref.html new file mode 100644 index 00000000000..2b8da41b4ee --- /dev/null +++ b/Tests/LibWeb/Ref/expected/text-overflow-ellipsis-with-trailing-whitespace-ref.html @@ -0,0 +1,2 @@ + +abc diff --git a/Tests/LibWeb/Ref/input/text-overflow-ellipsis-with-trailing-whitespace.html b/Tests/LibWeb/Ref/input/text-overflow-ellipsis-with-trailing-whitespace.html new file mode 100644 index 00000000000..3a42a22f1bd --- /dev/null +++ b/Tests/LibWeb/Ref/input/text-overflow-ellipsis-with-trailing-whitespace.html @@ -0,0 +1,12 @@ + + + +abc