Make ComputeNGCaretPosition() faster

This patch changes |ComputeNGCaretPosition()| to utilize |LayoutText|
parameter to start searching caret position at inline fragment
associated to |LayoutText| parameter instead of starting from first
inline fragment in containing block to avoid redundant looping for
making |ComputeNGCaretPosition()| faster.

This patch also fixes legacy layout and LayoutNG differences observed
in |ParameterizedLocalCaretRectTest|.

Bug: 707656
Change-Id: I57087ab4634e528ca5c66a9ac87b7c95a18e9674
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2509832
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823104}
diff --git a/third_party/blink/perf_tests/editing/move_backward_with_may_elements.html b/third_party/blink/perf_tests/editing/move_backward_with_may_elements.html
new file mode 100644
index 0000000..511e39d
--- /dev/null
+++ b/third_party/blink/perf_tests/editing/move_backward_with_may_elements.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<body>
+<script src="../resources/runner.js"></script>
+<script src="resources/line-layout-perf-test.js"></script>
+<style>
+#container {
+    width: 400px;
+    height: 200px;
+    overflow: scroll;
+}
+</style>
+<div id="container"></div>
+<script>
+const NUMBER_OF_WORDS = 300;
+const NUMBER_OF_MOVES = 500;
+const container = document.getElementById('container');
+const selection = window.getSelection();
+
+for (let wordCount = NUMBER_OF_WORDS; wordCount > 0; --wordCount) {
+    const word = document.createElement('span');
+    word.textContent = Math.random().toString(36).slice(2) + ' ';
+    container.appendChild(word);
+}
+
+PerfTestRunner.measureRunsPerSecond({
+  setup: () => {
+  },
+  run: () => {
+    container.scrollTo(0, container.scrollHeight);
+    selection.collapse(container, container.childNodes.length);
+    for (let i = 0; i < NUMBER_OF_MOVES; ++i)
+      selection.modify('extend', 'backward', 'character');
+  },
+});
+</script>
diff --git a/third_party/blink/renderer/core/editing/local_caret_rect_test.cc b/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
index 4f59c057..03a54014 100644
--- a/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
+++ b/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
@@ -445,13 +445,10 @@
           PositionWithAffinity(after_c, TextAffinity::kDownstream)));
 
   const Position before_d(text_d, 0);
-  // TODO(xiaochengh): Should return the same result for legacy and LayoutNG.
-  EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(text_c->GetLayoutObject(),
-                                               PhysicalRect(29, 0, 1, 10))
-                              : LocalCaretRect(text_d->GetLayoutObject(),
-                                               PhysicalRect(0, 10, 1, 10)),
-            LocalCaretRectOfPosition(
-                PositionWithAffinity(before_d, TextAffinity::kUpstream)));
+  EXPECT_EQ(
+      LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
+      LocalCaretRectOfPosition(
+          PositionWithAffinity(before_d, TextAffinity::kUpstream)));
   EXPECT_EQ(
       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
       LocalCaretRectOfPosition(
@@ -487,13 +484,10 @@
           PositionWithAffinity(after_c, TextAffinity::kDownstream)));
 
   const Position before_d(text_d, 0);
-  // TODO(xiaochengh): Should return the same result for legacy and LayoutNG.
-  EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(text_c->GetLayoutObject(),
-                                               PhysicalRect(0, 0, 1, 10))
-                              : LocalCaretRect(text_d->GetLayoutObject(),
-                                               PhysicalRect(29, 10, 1, 10)),
-            LocalCaretRectOfPosition(
-                PositionWithAffinity(before_d, TextAffinity::kUpstream)));
+  EXPECT_EQ(
+      LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(29, 10, 1, 10)),
+      LocalCaretRectOfPosition(
+          PositionWithAffinity(before_d, TextAffinity::kUpstream)));
   EXPECT_EQ(
       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(29, 10, 1, 10)),
       LocalCaretRectOfPosition(
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc
index dc599758..06bcec8 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.cc
@@ -271,6 +271,8 @@
   NGInlineCursor cursor(context);
 
   NGCaretPosition candidate;
+  if (layout_text && layout_text->HasInlineFragments())
+    cursor.MoveTo(*layout_text);
   for (; cursor; cursor.MoveToNext()) {
     const CaretPositionResolution resolution =
         TryResolveCaretPositionWithFragment(cursor, offset, affinity);
@@ -321,7 +323,8 @@
 
   const LayoutText* const layout_text =
       position.IsOffsetInAnchor() && IsA<Text>(position.AnchorNode())
-          ? To<Text>(position.AnchorNode())->GetLayoutObject()
+          ? ToLayoutText(AssociatedLayoutObjectOf(
+                *position.AnchorNode(), position.OffsetInContainerNode()))
           : nullptr;
 
   const unsigned offset = *maybe_offset;