Text manipulation should ignore white spaces between nodes
https://bugs.webkit.org/show_bug.cgi?id=213907

Patch by Sihui Liu <sihui_liu@appe.com> on 2020-07-08
Source/WebCore:

Reviewed by Wenson Hsieh.

Text returned by TextIterator contains white spaces (including tabs and line breaks) that do not belong to
content of nodes. Those spaces are emitted based on style of nodes. For example, line breaks can be emitted
before and after block-level element. These spaces should not be extracted as part of the content, because
they could change based on the style of nodes or range of TextIterator, and manipulation fails if content is
changed. We want to make sure TextManipulationController monitors the real content of nodes.

r262778 tried solving this issue by excluding text with empty node from TextIterator's result. That worked with
line break, but not space and tab. See radar and new test. To solve this, now we exclude text with zero-length
range.

This patch does not change the behavior of line breaks, which is covered by:
TextManipulation.StartTextManipulationExtractsVisibleLineBreaksInTextAsExcludedTokens
TextManipulation.CompleteTextManipulationCanMergeContentAndPreserveLineBreaks
TextManipulation.CompleteTextManipulationReplaceTwoSimpleParagraphs

New test: TextManipulation.CompleteTextManipulationIgnoreWhiteSpacesBetweenParagraphs
Modified existing tests: TextManipulation.StartTextManipulationExtractsValuesByNode

* editing/TextManipulationController.cpp:
(WebCore::ParagraphContentIterator::ParagraphContentIterator):
(WebCore::ParagraphContentIterator::shouldAdvanceIteratorPastCurrentNode const):
(WebCore::ParagraphContentIterator::advanceIteratorNodeAndUpdateText):

Tools:

Reviewed by Wenson Hsieh.

* TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm:
(TestWebKitAPI::TEST):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@264120 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/editing/TextManipulationController.cpp b/Source/WebCore/editing/TextManipulationController.cpp
index 3b81c1b..d284455 100644
--- a/Source/WebCore/editing/TextManipulationController.cpp
+++ b/Source/WebCore/editing/TextManipulationController.cpp
@@ -147,7 +147,6 @@
 public:
     ParagraphContentIterator(const Position& start, const Position& end)
         : m_iterator({ *makeBoundaryPoint(start), *makeBoundaryPoint(end) }, TextIteratorIgnoresStyleVisibility)
-        , m_iteratorNode(m_iterator.atEnd() ? nullptr : createLiveRange(m_iterator.range())->firstNode())
         , m_node(start.firstNode())
         , m_pastEndNode(end.firstNode())
     {
@@ -188,7 +187,14 @@
     bool atEnd() const { return !m_text && m_iterator.atEnd() && m_node == m_pastEndNode; }
 
 private:
-    bool shouldAdvanceIteratorPastCurrentNode() const { return !m_iterator.atEnd() && m_iteratorNode == m_node; }
+    bool shouldAdvanceIteratorPastCurrentNode() const
+    {
+        if (m_iterator.atEnd())
+            return false;
+
+        auto* iteratorNode = m_iterator.node();
+        return !iteratorNode || iteratorNode == m_node;
+    }
 
     void advanceNode()
     {
@@ -215,28 +221,22 @@
         StringBuilder stringBuilder;
         Vector<String> text;
         while (shouldAdvanceIteratorPastCurrentNode()) {
-            if (!m_iterator.node()) {
-                auto iteratorText = m_iterator.text();
-                bool containsDelimiter = false;
-                for (unsigned index = 0; index < iteratorText.length() && !containsDelimiter; ++index)
-                    containsDelimiter = isTokenDelimiter(iteratorText[index]);
-
-                if (containsDelimiter) {
+            auto iteratorText = m_iterator.text();
+            if (m_iterator.range().collapsed()) {
+                if (iteratorText == "\n") {
                     appendToText(text, stringBuilder);
                     text.append({ });
                 }
             } else
-                stringBuilder.append(m_iterator.text());
+                stringBuilder.append(iteratorText);
 
             m_iterator.advance();
-            m_iteratorNode = m_iterator.atEnd() ? nullptr : createLiveRange(m_iterator.range())->firstNode();
         }
         appendToText(text, stringBuilder);
         m_text = text;
     }
 
     TextIterator m_iterator;
-    RefPtr<Node> m_iteratorNode;
     RefPtr<Node> m_node;
     RefPtr<Node> m_pastEndNode;
     Optional<Vector<String>> m_text;