From 30c39e0e41c1ce602d39e768036c769644278141 Mon Sep 17 00:00:00 2001 From: Felix Rauch Date: Tue, 26 Oct 2021 16:15:53 +0200 Subject: [PATCH] LibWeb: Fix inline blocks swallowing trailing whitespace In #10434 an issue with leading whitespace in new lines after a
element was fixed by checking whether the last fragment of LineBox is empty. However, this introduced a regression by which whitespace following inline elements was swallowed, so `Test 123` would appear like `Test123`. By asking specifically if we are handling a forced linebreak instead of implicity asking for a property that may be shared by other Node types, we can maintain the correct behavior in regards to leading whitespace on new lines, as well as trailing whitespace of inline elements. --- Userland/Libraries/LibWeb/Layout/BreakNode.h | 4 ++++ Userland/Libraries/LibWeb/Layout/LineBox.cpp | 10 ++++++++-- Userland/Libraries/LibWeb/Layout/LineBox.h | 1 + Userland/Libraries/LibWeb/Layout/Node.h | 1 + Userland/Libraries/LibWeb/Layout/TextNode.cpp | 2 +- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibWeb/Layout/BreakNode.h b/Userland/Libraries/LibWeb/Layout/BreakNode.h index 80ec3ed773..4276b4eaee 100644 --- a/Userland/Libraries/LibWeb/Layout/BreakNode.h +++ b/Userland/Libraries/LibWeb/Layout/BreakNode.h @@ -19,9 +19,13 @@ public: const HTML::HTMLBRElement& dom_node() const { return verify_cast(*Node::dom_node()); } private: + virtual bool is_break_node() const final { return true; } virtual void paint(PaintContext&, PaintPhase) override; virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override; }; +template<> +inline bool Node::fast_is() const { return is_break_node(); } + } diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.cpp b/Userland/Libraries/LibWeb/Layout/LineBox.cpp index de4d900ad4..50a65af787 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBox.cpp @@ -5,8 +5,10 @@ */ #include +#include #include #include +#include #include #include #include @@ -61,9 +63,13 @@ bool LineBox::is_empty_or_ends_in_whitespace() const { if (m_fragments.is_empty()) return true; - if (m_fragments.last().length() == 0) - return true; + return m_fragments.last().ends_in_whitespace(); } +bool LineBox::ends_with_forced_line_break() const +{ + return is(m_fragments.last().layout_node()); +} + } diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.h b/Userland/Libraries/LibWeb/Layout/LineBox.h index 77fab5346c..6e6a3b3cd9 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBox.h +++ b/Userland/Libraries/LibWeb/Layout/LineBox.h @@ -26,6 +26,7 @@ public: void trim_trailing_whitespace(); bool is_empty_or_ends_in_whitespace() const; + bool ends_with_forced_line_break() const; private: friend class BlockContainer; diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h index f9d384f190..f466652439 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.h +++ b/Userland/Libraries/LibWeb/Layout/Node.h @@ -99,6 +99,7 @@ public: // These are used to optimize hot is variants for some classes where dynamic_cast is too slow. virtual bool is_box() const { return false; } virtual bool is_block_container() const { return false; } + virtual bool is_break_node() const { return false; } virtual bool is_text_node() const { return false; } virtual bool is_initial_containing_block_box() const { return false; } virtual bool is_svg_box() const { return false; } diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.cpp b/Userland/Libraries/LibWeb/Layout/TextNode.cpp index 76dc291508..e2aec8e005 100644 --- a/Userland/Libraries/LibWeb/Layout/TextNode.cpp +++ b/Userland/Libraries/LibWeb/Layout/TextNode.cpp @@ -217,7 +217,7 @@ void TextNode::split_into_lines_by_rules(InlineFormattingContext& context, Layou float chunk_width; if (do_wrap_lines) { - if (do_collapse && is_ascii_space(*chunk.view.begin()) && line_boxes.last().is_empty_or_ends_in_whitespace()) { + if (do_collapse && is_ascii_space(*chunk.view.begin()) && (line_boxes.last().is_empty_or_ends_in_whitespace() || line_boxes.last().ends_with_forced_line_break())) { // This is a non-empty chunk that starts with collapsible whitespace. // We are at either at the start of a new line, or after something that ended in whitespace, // so we don't need to contribute our own whitespace to the line. Skip over it instead!