Make TextIterator abort when ::first-letter contains multiple leading spaces

When TextIterator is passed with iteration range fulled contained in
the leading collapsed whitespaces of ::first-letter, it currently doesn't
stop in the first letter part, but advances to remaining text with
invalid parameters.

This patch fixes it by fixing an existing stop condition:
- Old condition: stop if the current text box starts at iteration range end
- New condition: stop if the current text box start at or after the
  iteration range end. The condition is also written in a more intuitive
  way.

Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: I2768d41ec5e792353023ca77749575383020ae18
Reviewed-on: https://chromium-review.googlesource.com/1137697
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#575644}
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
index def66a7..2221e11 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
@@ -526,6 +526,18 @@
   EXPECT_EQ(11, TestRangeLength("<p>^ (1) abc def|</p>"));
 }
 
+TEST_P(ParameterizedTextIteratorTest,
+       RangeLengthWithFirstLetterMultipleLeadingSpaces) {
+  InsertStyleElement("p::first-letter {font-size:200%;}");
+  EXPECT_EQ(0, TestRangeLength("<p>^|   foo</p>"));
+  EXPECT_EQ(0, TestRangeLength("<p>^ |  foo</p>"));
+  EXPECT_EQ(0, TestRangeLength("<p>^  | foo</p>"));
+  EXPECT_EQ(0, TestRangeLength("<p>^   |foo</p>"));
+  EXPECT_EQ(1, TestRangeLength("<p>^   f|oo</p>"));
+  EXPECT_EQ(2, TestRangeLength("<p>^   fo|o</p>"));
+  EXPECT_EQ(3, TestRangeLength("<p>^   foo|</p>"));
+}
+
 TEST_F(TextIteratorTest, WhitespaceCollapseForReplacedElements) {
   static const char* body_content =
       "<span>Some text </span> <input type='button' value='Button "
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
index ef3e3a2..1513e70 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
@@ -376,9 +376,7 @@
     // Start and end offsets in |str|, i.e., str[start..end - 1] should be
     // emitted (after handling whitespace collapsing).
     DCHECK_GE(offset_, text_start_offset);
-    // TODO(editing-dev): Add the DCHECK below after fixing
-    // accessibility/inline-text-word-boundary-causes-crash.html
-    // DCHECK_GE(end_offset_, text_start_offset);
+    DCHECK_GE(end_offset_, text_start_offset);
     const unsigned start = offset_ - text_start_offset;
     const unsigned end = end_offset_ - text_start_offset;
     while (text_box_) {
@@ -487,13 +485,12 @@
           ++sorted_text_boxes_position_;
         return;
       }
-      // Advance and continue
-      if (run_start == run_end && run_end == end) {
-        // "<p>^ |(1) foo</p>" with ::first-letter reaches here.
-        // Where "^" is start of range and "|" is end of range.
+      // All remaining text boxes are after range end. Nothing left to emit.
+      if (text_box_start >= end) {
         offset_ = end_offset_;
         return;
       }
+      // Advance and continue
       text_box_ = next_text_box;
       if (layout_object->ContainsReversedText())
         ++sorted_text_boxes_position_;