1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/editing/local_caret_rect.h"
6 
7 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
8 #include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
9 #include "third_party/blink/renderer/core/editing/text_affinity.h"
10 #include "third_party/blink/renderer/core/editing/visible_position.h"
11 #include "third_party/blink/renderer/core/html/forms/text_control_element.h"
12 #include "third_party/blink/renderer/core/layout/layout_object.h"
13 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
14 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
15 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
16 
17 namespace blink {
18 
operator ==(const LocalCaretRect & rect1,const LocalCaretRect & rect2)19 bool operator==(const LocalCaretRect& rect1, const LocalCaretRect& rect2) {
20   return rect1.layout_object == rect2.layout_object && rect1.rect == rect2.rect;
21 }
22 
operator <<(std::ostream & out,const LocalCaretRect & caret_rect)23 std::ostream& operator<<(std::ostream& out, const LocalCaretRect& caret_rect) {
24   return out << "layout_object = " << caret_rect.layout_object
25              << ", rect = " << caret_rect.rect;
26 }
27 
28 class LocalCaretRectTest : public EditingTestBase {};
29 
30 // Helper class to run the same test code with and without LayoutNG
31 class ParameterizedLocalCaretRectTest
32     : public testing::WithParamInterface<bool>,
33       private ScopedLayoutNGForTest,
34       public LocalCaretRectTest {
35  public:
ParameterizedLocalCaretRectTest()36   ParameterizedLocalCaretRectTest() : ScopedLayoutNGForTest(GetParam()) {}
37 
38  protected:
LayoutNGEnabled() const39   bool LayoutNGEnabled() const {
40     return RuntimeEnabledFeatures::LayoutNGEnabled();
41   }
42 };
43 
44 INSTANTIATE_TEST_SUITE_P(All, ParameterizedLocalCaretRectTest, testing::Bool());
45 
TEST_P(ParameterizedLocalCaretRectTest,DOMAndFlatTrees)46 TEST_P(ParameterizedLocalCaretRectTest, DOMAndFlatTrees) {
47   const char* body_content =
48       "<p id='host'><b id='one'>1</b></p><b id='two'>22</b>";
49   const char* shadow_content =
50       "<b id='two'>22</b><content select=#one></content><b id='three'>333</b>";
51   SetBodyContent(body_content);
52   SetShadowContent(shadow_content, "host");
53 
54   Element* one = GetDocument().getElementById("one");
55 
56   const LocalCaretRect& caret_rect_from_dom_tree = LocalCaretRectOfPosition(
57       PositionWithAffinity(Position(one->firstChild(), 0)));
58 
59   const LocalCaretRect& caret_rect_from_flat_tree = LocalCaretRectOfPosition(
60       PositionInFlatTreeWithAffinity(PositionInFlatTree(one->firstChild(), 0)));
61 
62   EXPECT_FALSE(caret_rect_from_dom_tree.IsEmpty());
63   EXPECT_EQ(caret_rect_from_dom_tree, caret_rect_from_flat_tree);
64 }
65 
TEST_P(ParameterizedLocalCaretRectTest,SimpleText)66 TEST_P(ParameterizedLocalCaretRectTest, SimpleText) {
67   // This test only records the current behavior. Future changes are allowed.
68 
69   LoadAhem();
70   SetBodyContent(
71       "<div id=div style='font: 10px/10px Ahem; width: 30px'>XXX</div>");
72   const Node* foo = GetElementById("div")->firstChild();
73 
74   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
75             LocalCaretRectOfPosition(PositionWithAffinity(
76                 Position(foo, 0), TextAffinity::kDownstream)));
77   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 0, 1, 10)),
78             LocalCaretRectOfPosition(PositionWithAffinity(
79                 Position(foo, 1), TextAffinity::kDownstream)));
80   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 0, 1, 10)),
81             LocalCaretRectOfPosition(PositionWithAffinity(
82                 Position(foo, 2), TextAffinity::kDownstream)));
83   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
84             LocalCaretRectOfPosition(PositionWithAffinity(
85                 Position(foo, 3), TextAffinity::kDownstream)));
86 }
87 
TEST_P(ParameterizedLocalCaretRectTest,MixedHeightText)88 TEST_P(ParameterizedLocalCaretRectTest, MixedHeightText) {
89   // This test only records the current behavior. Future changes are allowed.
90 
91   LoadAhem();
92   SetBodyContent(
93       "<div id=div style='font: 10px/10px Ahem; width: 30px'>Xpp</div>");
94   const Node* foo = GetElementById("div")->firstChild();
95 
96   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
97             LocalCaretRectOfPosition(PositionWithAffinity(
98                 Position(foo, 0), TextAffinity::kDownstream)));
99   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 0, 1, 10)),
100             LocalCaretRectOfPosition(PositionWithAffinity(
101                 Position(foo, 1), TextAffinity::kDownstream)));
102   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 0, 1, 10)),
103             LocalCaretRectOfPosition(PositionWithAffinity(
104                 Position(foo, 2), TextAffinity::kDownstream)));
105   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
106             LocalCaretRectOfPosition(PositionWithAffinity(
107                 Position(foo, 3), TextAffinity::kDownstream)));
108 }
109 
TEST_P(ParameterizedLocalCaretRectTest,RtlText)110 TEST_P(ParameterizedLocalCaretRectTest, RtlText) {
111   // This test only records the current behavior. Future changes are allowed.
112 
113   LoadAhem();
114   SetBodyContent(
115       "<bdo dir=rtl id=bdo style='display: block; "
116       "font: 10px/10px Ahem; width: 30px'>XXX</bdo>");
117   const Node* foo = GetElementById("bdo")->firstChild();
118 
119   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
120             LocalCaretRectOfPosition(PositionWithAffinity(
121                 Position(foo, 0), TextAffinity::kDownstream)));
122   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 0, 1, 10)),
123             LocalCaretRectOfPosition(PositionWithAffinity(
124                 Position(foo, 1), TextAffinity::kDownstream)));
125   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 0, 1, 10)),
126             LocalCaretRectOfPosition(PositionWithAffinity(
127                 Position(foo, 2), TextAffinity::kDownstream)));
128   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
129             LocalCaretRectOfPosition(PositionWithAffinity(
130                 Position(foo, 3), TextAffinity::kDownstream)));
131 }
132 
TEST_P(ParameterizedLocalCaretRectTest,OverflowTextLtr)133 TEST_P(ParameterizedLocalCaretRectTest, OverflowTextLtr) {
134   // This test only records the current behavior. Future changes are allowed.
135 
136   LoadAhem();
137   SetBodyContent(
138       "<div id=root style='font: 10px/10px Ahem; width: 30px'>"
139       "XXXX"
140       "</div>");
141   const Node* text = GetElementById("root")->firstChild();
142   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
143             LocalCaretRectOfPosition(PositionWithAffinity(
144                 Position(text, 0), TextAffinity::kDownstream)));
145   // LocalCaretRect may be outside the containing block.
146   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(39, 0, 1, 10)),
147             LocalCaretRectOfPosition(PositionWithAffinity(
148                 Position(text, 4), TextAffinity::kDownstream)));
149 }
150 
TEST_P(ParameterizedLocalCaretRectTest,UnderflowTextLtr)151 TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextLtr) {
152   // This test only records the current behavior. Future changes are allowed.
153 
154   LoadAhem();
155   SetBodyContent(
156       "<div id=root style='font: 10px/10px Ahem; width: 30px'>"
157       "XX"
158       "</div>");
159   const Node* text = GetElementById("root")->firstChild();
160   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
161             LocalCaretRectOfPosition(PositionWithAffinity(
162                 Position(text, 0), TextAffinity::kDownstream)));
163   // LocalCaretRect may be outside the containing block.
164   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(20, 0, 1, 10)),
165             LocalCaretRectOfPosition(PositionWithAffinity(
166                 Position(text, 2), TextAffinity::kDownstream)));
167 }
168 
TEST_P(ParameterizedLocalCaretRectTest,OverflowTextRtl)169 TEST_P(ParameterizedLocalCaretRectTest, OverflowTextRtl) {
170   // This test only records the current behavior. Future changes are allowed.
171 
172   LoadAhem();
173   SetBodyContent(
174       "<bdo id=root style='display:block; font: 10px/10px Ahem; width: 30px' "
175       "dir=rtl>"
176       "XXXX"
177       "</bdo>");
178   const Node* text = GetElementById("root")->firstChild();
179   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
180             LocalCaretRectOfPosition(PositionWithAffinity(
181                 Position(text, 0), TextAffinity::kDownstream)));
182   // LocalCaretRect may be outside the containing block.
183   EXPECT_EQ(
184       LocalCaretRect(text->GetLayoutObject(), PhysicalRect(-10, 0, 1, 10)),
185       LocalCaretRectOfPosition(
186           PositionWithAffinity(Position(text, 4), TextAffinity::kDownstream)));
187 }
188 
TEST_P(ParameterizedLocalCaretRectTest,UnderflowTextRtl)189 TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextRtl) {
190   // This test only records the current behavior. Future changes are allowed.
191 
192   LoadAhem();
193   SetBodyContent(
194       "<bdo id=root style='display:block; font: 10px/10px Ahem; width: 30px' "
195       "dir=rtl>"
196       "XX"
197       "</bdo>");
198   const Node* text = GetElementById("root")->firstChild();
199   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
200             LocalCaretRectOfPosition(PositionWithAffinity(
201                 Position(text, 0), TextAffinity::kDownstream)));
202   // LocalCaretRect may be outside the containing block.
203   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(10, 0, 1, 10)),
204             LocalCaretRectOfPosition(PositionWithAffinity(
205                 Position(text, 2), TextAffinity::kDownstream)));
206 }
207 
TEST_P(ParameterizedLocalCaretRectTest,VerticalRLText)208 TEST_P(ParameterizedLocalCaretRectTest, VerticalRLText) {
209   // This test only records the current behavior. Future changes are allowed.
210 
211   LoadAhem();
212   SetBodyContent(
213       "<div id=div style='writing-mode: vertical-rl; word-break: break-all; "
214       "font: 10px/10px Ahem; width: 30px; height: 30px'>XXXYYYZZZ</div>");
215   const Node* foo = GetElementById("div")->firstChild();
216 
217   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 0, 10, 1)),
218             LocalCaretRectOfPosition(PositionWithAffinity(
219                 Position(foo, 0), TextAffinity::kDownstream)));
220   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 10, 10, 1)),
221             LocalCaretRectOfPosition(PositionWithAffinity(
222                 Position(foo, 1), TextAffinity::kDownstream)));
223   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 20, 10, 1)),
224             LocalCaretRectOfPosition(PositionWithAffinity(
225                 Position(foo, 2), TextAffinity::kDownstream)));
226   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 29, 10, 1)),
227             LocalCaretRectOfPosition(PositionWithAffinity(
228                 Position(foo, 3), TextAffinity::kUpstream)));
229 
230   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 0, 10, 1)),
231             LocalCaretRectOfPosition(PositionWithAffinity(
232                 Position(foo, 3), TextAffinity::kDownstream)));
233   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 10, 10, 1)),
234             LocalCaretRectOfPosition(PositionWithAffinity(
235                 Position(foo, 4), TextAffinity::kDownstream)));
236   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 20, 10, 1)),
237             LocalCaretRectOfPosition(PositionWithAffinity(
238                 Position(foo, 5), TextAffinity::kDownstream)));
239   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 29, 10, 1)),
240             LocalCaretRectOfPosition(PositionWithAffinity(
241                 Position(foo, 6), TextAffinity::kUpstream)));
242 
243   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 0, 10, 1)),
244             LocalCaretRectOfPosition(PositionWithAffinity(
245                 Position(foo, 6), TextAffinity::kDownstream)));
246   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 10, 10, 1)),
247             LocalCaretRectOfPosition(PositionWithAffinity(
248                 Position(foo, 7), TextAffinity::kDownstream)));
249   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 20, 10, 1)),
250             LocalCaretRectOfPosition(PositionWithAffinity(
251                 Position(foo, 8), TextAffinity::kDownstream)));
252   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 29, 10, 1)),
253             LocalCaretRectOfPosition(PositionWithAffinity(
254                 Position(foo, 9), TextAffinity::kDownstream)));
255 }
256 
TEST_P(ParameterizedLocalCaretRectTest,VerticalLRText)257 TEST_P(ParameterizedLocalCaretRectTest, VerticalLRText) {
258   // This test only records the current behavior. Future changes are allowed.
259 
260   LoadAhem();
261   SetBodyContent(
262       "<div id=div style='writing-mode: vertical-lr; word-break: break-all; "
263       "font: 10px/10px Ahem; width: 30px; height: 30px'>XXXYYYZZZ</div>");
264   const Node* foo = GetElementById("div")->firstChild();
265 
266   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 0, 10, 1)),
267             LocalCaretRectOfPosition(PositionWithAffinity(
268                 Position(foo, 0), TextAffinity::kDownstream)));
269   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 10, 10, 1)),
270             LocalCaretRectOfPosition(PositionWithAffinity(
271                 Position(foo, 1), TextAffinity::kDownstream)));
272   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 20, 10, 1)),
273             LocalCaretRectOfPosition(PositionWithAffinity(
274                 Position(foo, 2), TextAffinity::kDownstream)));
275   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 29, 10, 1)),
276             LocalCaretRectOfPosition(PositionWithAffinity(
277                 Position(foo, 3), TextAffinity::kUpstream)));
278 
279   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 0, 10, 1)),
280             LocalCaretRectOfPosition(PositionWithAffinity(
281                 Position(foo, 3), TextAffinity::kDownstream)));
282   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 10, 10, 1)),
283             LocalCaretRectOfPosition(PositionWithAffinity(
284                 Position(foo, 4), TextAffinity::kDownstream)));
285   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 20, 10, 1)),
286             LocalCaretRectOfPosition(PositionWithAffinity(
287                 Position(foo, 5), TextAffinity::kDownstream)));
288   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 29, 10, 1)),
289             LocalCaretRectOfPosition(PositionWithAffinity(
290                 Position(foo, 6), TextAffinity::kUpstream)));
291 
292   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 0, 10, 1)),
293             LocalCaretRectOfPosition(PositionWithAffinity(
294                 Position(foo, 6), TextAffinity::kDownstream)));
295   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 10, 10, 1)),
296             LocalCaretRectOfPosition(PositionWithAffinity(
297                 Position(foo, 7), TextAffinity::kDownstream)));
298   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 20, 10, 1)),
299             LocalCaretRectOfPosition(PositionWithAffinity(
300                 Position(foo, 8), TextAffinity::kDownstream)));
301   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 29, 10, 1)),
302             LocalCaretRectOfPosition(PositionWithAffinity(
303                 Position(foo, 9), TextAffinity::kDownstream)));
304 }
305 
TEST_P(ParameterizedLocalCaretRectTest,OverflowTextVerticalLtr)306 TEST_P(ParameterizedLocalCaretRectTest, OverflowTextVerticalLtr) {
307   // This test only records the current behavior. Future changes are allowed.
308 
309   LoadAhem();
310   SetBodyContent(
311       "<div id=root style='font: 10px/10px Ahem; height: 30px; writing-mode: "
312       "vertical-lr'>"
313       "XXXX"
314       "</div>");
315   const Node* text = GetElementById("root")->firstChild();
316   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 0, 10, 1)),
317             LocalCaretRectOfPosition(PositionWithAffinity(
318                 Position(text, 0), TextAffinity::kDownstream)));
319   // LocalCaretRect may be outside the containing block.
320   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 39, 10, 1)),
321             LocalCaretRectOfPosition(PositionWithAffinity(
322                 Position(text, 4), TextAffinity::kDownstream)));
323 }
324 
TEST_P(ParameterizedLocalCaretRectTest,UnderflowTextVerticalLtr)325 TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextVerticalLtr) {
326   // This test only records the current behavior. Future changes are allowed.
327 
328   LoadAhem();
329   SetBodyContent(
330       "<div id=root style='font: 10px/10px Ahem; height: 30px; writing-mode: "
331       "vertical-lr'>"
332       "XX"
333       "</div>");
334   const Node* text = GetElementById("root")->firstChild();
335   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 0, 10, 1)),
336             LocalCaretRectOfPosition(PositionWithAffinity(
337                 Position(text, 0), TextAffinity::kDownstream)));
338   // LocalCaretRect may be outside the containing block.
339   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 20, 10, 1)),
340             LocalCaretRectOfPosition(PositionWithAffinity(
341                 Position(text, 2), TextAffinity::kDownstream)));
342 }
343 
TEST_P(ParameterizedLocalCaretRectTest,OverflowTextVerticalRtl)344 TEST_P(ParameterizedLocalCaretRectTest, OverflowTextVerticalRtl) {
345   // This test only records the current behavior. Future changes are allowed.
346 
347   LoadAhem();
348   SetBodyContent(
349       "<bdo id=root style='display:block; font: 10px/10px Ahem; height: 30px; "
350       "writing-mode: vertical-lr' dir=rtl>"
351       "XXXX"
352       "</bdo>");
353   const Node* text = GetElementById("root")->firstChild();
354   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 29, 10, 1)),
355             LocalCaretRectOfPosition(PositionWithAffinity(
356                 Position(text, 0), TextAffinity::kDownstream)));
357   // LocalCaretRect may be outside the containing block.
358   EXPECT_EQ(
359       LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, -10, 10, 1)),
360       LocalCaretRectOfPosition(
361           PositionWithAffinity(Position(text, 4), TextAffinity::kDownstream)));
362 }
363 
TEST_P(ParameterizedLocalCaretRectTest,UnderflowTextVerticalRtl)364 TEST_P(ParameterizedLocalCaretRectTest, UnderflowTextVerticalRtl) {
365   // This test only records the current behavior. Future changes are allowed.
366 
367   LoadAhem();
368   SetBodyContent(
369       "<bdo id=root style='display:block; font: 10px/10px Ahem; height: 30px; "
370       "writing-mode: vertical-lr' dir=rtl>"
371       "XX"
372       "</bdo>");
373   const Node* text = GetElementById("root")->firstChild();
374   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 29, 10, 1)),
375             LocalCaretRectOfPosition(PositionWithAffinity(
376                 Position(text, 0), TextAffinity::kDownstream)));
377   // LocalCaretRect may be outside the containing block.
378   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(0, 10, 10, 1)),
379             LocalCaretRectOfPosition(PositionWithAffinity(
380                 Position(text, 2), TextAffinity::kDownstream)));
381 }
382 
TEST_P(ParameterizedLocalCaretRectTest,TwoLinesOfTextWithSoftWrap)383 TEST_P(ParameterizedLocalCaretRectTest, TwoLinesOfTextWithSoftWrap) {
384   // This test only records the current behavior. Future changes are allowed.
385 
386   LoadAhem();
387   SetBodyContent(
388       "<div id=div style='font: 10px/10px Ahem; width: 30px; "
389       "word-break: break-all'>XXXXXX</div>");
390   const Node* foo = GetElementById("div")->firstChild();
391 
392   // First line
393   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
394             LocalCaretRectOfPosition(PositionWithAffinity(
395                 Position(foo, 0), TextAffinity::kDownstream)));
396   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 0, 1, 10)),
397             LocalCaretRectOfPosition(PositionWithAffinity(
398                 Position(foo, 1), TextAffinity::kDownstream)));
399   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 0, 1, 10)),
400             LocalCaretRectOfPosition(PositionWithAffinity(
401                 Position(foo, 2), TextAffinity::kDownstream)));
402   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
403             LocalCaretRectOfPosition(PositionWithAffinity(
404                 Position(foo, 3), TextAffinity::kUpstream)));
405 
406   // Second line
407   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
408             LocalCaretRectOfPosition(PositionWithAffinity(
409                 Position(foo, 3), TextAffinity::kDownstream)));
410   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(10, 10, 1, 10)),
411             LocalCaretRectOfPosition(PositionWithAffinity(
412                 Position(foo, 4), TextAffinity::kDownstream)));
413   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(20, 10, 1, 10)),
414             LocalCaretRectOfPosition(PositionWithAffinity(
415                 Position(foo, 5), TextAffinity::kDownstream)));
416   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(29, 10, 1, 10)),
417             LocalCaretRectOfPosition(PositionWithAffinity(
418                 Position(foo, 6), TextAffinity::kDownstream)));
419 }
420 
TEST_P(ParameterizedLocalCaretRectTest,SoftLineWrapBetweenMultipleTextNodes)421 TEST_P(ParameterizedLocalCaretRectTest, SoftLineWrapBetweenMultipleTextNodes) {
422   // This test only records the current behavior. Future changes are allowed.
423 
424   LoadAhem();
425   SetBodyContent(
426       "<div style='font: 10px/10px Ahem; width: 30px; word-break: break-all'>"
427       "<span>A</span>"
428       "<span>B</span>"
429       "<span id=span-c>C</span>"
430       "<span id=span-d>D</span>"
431       "<span>E</span>"
432       "<span>F</span>"
433       "</div>");
434   const Node* text_c = GetElementById("span-c")->firstChild();
435   const Node* text_d = GetElementById("span-d")->firstChild();
436 
437   const Position after_c(text_c, 1);
438   EXPECT_EQ(
439       LocalCaretRect(text_c->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
440       LocalCaretRectOfPosition(
441           PositionWithAffinity(after_c, TextAffinity::kUpstream)));
442   EXPECT_EQ(
443       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
444       LocalCaretRectOfPosition(
445           PositionWithAffinity(after_c, TextAffinity::kDownstream)));
446 
447   const Position before_d(text_d, 0);
448   EXPECT_EQ(
449       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
450       LocalCaretRectOfPosition(
451           PositionWithAffinity(before_d, TextAffinity::kUpstream)));
452   EXPECT_EQ(
453       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
454       LocalCaretRectOfPosition(
455           PositionWithAffinity(before_d, TextAffinity::kDownstream)));
456 }
457 
TEST_P(ParameterizedLocalCaretRectTest,SoftLineWrapBetweenMultipleTextNodesRtl)458 TEST_P(ParameterizedLocalCaretRectTest,
459        SoftLineWrapBetweenMultipleTextNodesRtl) {
460   // This test only records the current behavior. Future changes are allowed.
461 
462   LoadAhem();
463   SetBodyContent(
464       "<bdo dir=rtl style='font: 10px/10px Ahem; width: 30px; "
465       "word-break: break-all; display: block'>"
466       "<span>A</span>"
467       "<span>B</span>"
468       "<span id=span-c>C</span>"
469       "<span id=span-d>D</span>"
470       "<span>E</span>"
471       "<span>F</span>"
472       "</bdo>");
473   const Node* text_c = GetElementById("span-c")->firstChild();
474   const Node* text_d = GetElementById("span-d")->firstChild();
475 
476   const Position after_c(text_c, 1);
477   EXPECT_EQ(
478       LocalCaretRect(text_c->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
479       LocalCaretRectOfPosition(
480           PositionWithAffinity(after_c, TextAffinity::kUpstream)));
481   EXPECT_EQ(
482       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(29, 10, 1, 10)),
483       LocalCaretRectOfPosition(
484           PositionWithAffinity(after_c, TextAffinity::kDownstream)));
485 
486   const Position before_d(text_d, 0);
487   EXPECT_EQ(
488       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(29, 10, 1, 10)),
489       LocalCaretRectOfPosition(
490           PositionWithAffinity(before_d, TextAffinity::kUpstream)));
491   EXPECT_EQ(
492       LocalCaretRect(text_d->GetLayoutObject(), PhysicalRect(29, 10, 1, 10)),
493       LocalCaretRectOfPosition(
494           PositionWithAffinity(before_d, TextAffinity::kDownstream)));
495 }
496 
TEST_P(ParameterizedLocalCaretRectTest,CaretRectAtBR)497 TEST_P(ParameterizedLocalCaretRectTest, CaretRectAtBR) {
498   // This test only records the current behavior. Future changes are allowed.
499 
500   LoadAhem();
501   SetBodyContent(
502       "<div style='font: 10px/10px Ahem; width: 30px'><br>foo</div>");
503   const Element& br = *GetDocument().QuerySelector("br");
504 
505   EXPECT_EQ(LocalCaretRect(br.GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
506             LocalCaretRectOfPosition(PositionWithAffinity(
507                 Position::BeforeNode(br), TextAffinity::kDownstream)));
508 }
509 
TEST_P(ParameterizedLocalCaretRectTest,CaretRectAtRtlBR)510 TEST_P(ParameterizedLocalCaretRectTest, CaretRectAtRtlBR) {
511   // This test only records the current behavior. Future changes are allowed.
512 
513   LoadAhem();
514   SetBodyContent(
515       "<bdo dir=rtl style='display: block; font: 10px/10px Ahem; width: 30px'>"
516       "<br>foo</bdo>");
517   const Element& br = *GetDocument().QuerySelector("br");
518 
519   EXPECT_EQ(LocalCaretRect(br.GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
520             LocalCaretRectOfPosition(PositionWithAffinity(
521                 Position::BeforeNode(br), TextAffinity::kDownstream)));
522 }
523 
TEST_P(ParameterizedLocalCaretRectTest,Images)524 TEST_P(ParameterizedLocalCaretRectTest, Images) {
525   // This test only records the current behavior. Future changes are allowed.
526 
527   LoadAhem();
528   SetBodyContent(
529       "<div id=div style='font: 10px/10px Ahem; width: 30px'>"
530       "<img id=img1 width=10px height=10px>"
531       "<img id=img2 width=10px height=10px>"
532       "</div>");
533 
534   const Element& img1 = *GetElementById("img1");
535 
536   EXPECT_EQ(LocalCaretRect(img1.GetLayoutObject(), PhysicalRect(0, 0, 1, 12)),
537             LocalCaretRectOfPosition(PositionWithAffinity(
538                 Position::BeforeNode(img1), TextAffinity::kDownstream)));
539   EXPECT_EQ(LocalCaretRect(img1.GetLayoutObject(), PhysicalRect(9, 0, 1, 12)),
540             LocalCaretRectOfPosition(PositionWithAffinity(
541                 Position::AfterNode(img1), TextAffinity::kDownstream)));
542 
543   const Element& img2 = *GetElementById("img2");
544 
545   // Box-anchored LocalCaretRect is local to the box itself, instead of its
546   // containing block.
547   // TODO(xiaochengh): Should return the same result for legacy and LayoutNG.
548   EXPECT_EQ(
549       LayoutNGEnabled()
550           ? LocalCaretRect(img1.GetLayoutObject(), PhysicalRect(9, 0, 1, 12))
551           : LocalCaretRect(img2.GetLayoutObject(), PhysicalRect(0, 0, 1, 12)),
552       LocalCaretRectOfPosition(PositionWithAffinity(
553           Position::BeforeNode(img2), TextAffinity::kDownstream)));
554   EXPECT_EQ(LocalCaretRect(img2.GetLayoutObject(), PhysicalRect(9, 0, 1, 12)),
555             LocalCaretRectOfPosition(PositionWithAffinity(
556                 Position::AfterNode(img2), TextAffinity::kDownstream)));
557 }
558 
TEST_P(ParameterizedLocalCaretRectTest,RtlImages)559 TEST_P(ParameterizedLocalCaretRectTest, RtlImages) {
560   // This test only records the current behavior. Future changes are allowed.
561 
562   LoadAhem();
563   SetBodyContent(
564       "<bdo dir=rtl style='font: 10px/10px Ahem; width: 30px; display: block'>"
565       "<img id=img1 width=10px height=10px>"
566       "<img id=img2 width=10px height=10px>"
567       "</bdo>");
568 
569   const Element& img1 = *GetElementById("img1");
570   const Element& img2 = *GetElementById("img2");
571 
572   // Box-anchored LocalCaretRect is local to the box itself, instead of its
573   // containing block.
574   EXPECT_EQ(LocalCaretRect(img1.GetLayoutObject(), PhysicalRect(9, 0, 1, 12)),
575             LocalCaretRectOfPosition(PositionWithAffinity(
576                 Position::BeforeNode(img1), TextAffinity::kDownstream)));
577   EXPECT_EQ(
578       LayoutNGEnabled()
579           ? LocalCaretRect(img2.GetLayoutObject(), PhysicalRect(9, 0, 1, 12))
580           : LocalCaretRect(img1.GetLayoutObject(), PhysicalRect(0, 0, 1, 12)),
581       LocalCaretRectOfPosition(PositionWithAffinity(
582           Position::AfterNode(img1), TextAffinity::kDownstream)));
583 
584   EXPECT_EQ(LocalCaretRect(img2.GetLayoutObject(), PhysicalRect(9, 0, 1, 12)),
585             LocalCaretRectOfPosition(PositionWithAffinity(
586                 Position::BeforeNode(img2), TextAffinity::kDownstream)));
587   EXPECT_EQ(LocalCaretRect(img2.GetLayoutObject(), PhysicalRect(0, 0, 1, 12)),
588             LocalCaretRectOfPosition(PositionWithAffinity(
589                 Position::AfterNode(img2), TextAffinity::kDownstream)));
590 }
591 
TEST_P(ParameterizedLocalCaretRectTest,VerticalImage)592 TEST_P(ParameterizedLocalCaretRectTest, VerticalImage) {
593   // This test only records the current behavior. Future changes are allowed.
594 
595   SetBodyContent(
596       "<div style='writing-mode: vertical-rl'>"
597       "<img id=img width=10px height=20px>"
598       "</div>");
599 
600   const Element& img = *GetElementById("img");
601 
602   // Box-anchored LocalCaretRect is local to the box itself, instead of its
603   // containing block.
604   EXPECT_EQ(LocalCaretRect(img.GetLayoutObject(), PhysicalRect(0, 0, 10, 1)),
605             LocalCaretRectOfPosition(PositionWithAffinity(
606                 Position::BeforeNode(img), TextAffinity::kDownstream)));
607 
608   EXPECT_EQ(
609       LayoutNGEnabled()
610           ? LocalCaretRect(img.GetLayoutObject(), PhysicalRect(0, 19, 10, 1))
611           // TODO(crbug.com/805064): The legacy behavior is wrong. Fix it.
612           : LocalCaretRect(img.GetLayoutObject(), PhysicalRect(0, 9, 10, 1)),
613       LocalCaretRectOfPosition(PositionWithAffinity(
614           Position::AfterNode(img), TextAffinity::kDownstream)));
615 }
616 
TEST_P(ParameterizedLocalCaretRectTest,TextAndImageMixedHeight)617 TEST_P(ParameterizedLocalCaretRectTest, TextAndImageMixedHeight) {
618   // This test only records the current behavior. Future changes are allowed.
619 
620   LoadAhem();
621   SetBodyContent(
622       "<div id=div style='font: 10px/10px Ahem; width: 30px'>"
623       "X"
624       "<img id=img width=10px height=5px style='vertical-align: text-bottom'>"
625       "p</div>");
626 
627   const Element& img = *GetElementById("img");
628   const Node* text1 = img.previousSibling();
629   const Node* text2 = img.nextSibling();
630 
631   EXPECT_EQ(LocalCaretRect(text1->GetLayoutObject(), PhysicalRect(0, 0, 1, 10)),
632             LocalCaretRectOfPosition(PositionWithAffinity(
633                 Position(text1, 0), TextAffinity::kDownstream)));
634   EXPECT_EQ(
635       LocalCaretRect(text1->GetLayoutObject(), PhysicalRect(10, 0, 1, 10)),
636       LocalCaretRectOfPosition(
637           PositionWithAffinity(Position(text1, 1), TextAffinity::kDownstream)));
638 
639   // TODO(xiaochengh): Should return the same result for legacy and LayoutNG.
640   EXPECT_EQ(
641       LayoutNGEnabled()
642           ? LocalCaretRect(text1->GetLayoutObject(), PhysicalRect(10, 0, 1, 10))
643           : LocalCaretRect(img.GetLayoutObject(), PhysicalRect(0, -5, 1, 10)),
644       LocalCaretRectOfPosition(PositionWithAffinity(
645           Position::BeforeNode(img), TextAffinity::kDownstream)));
646   EXPECT_EQ(LocalCaretRect(img.GetLayoutObject(), PhysicalRect(9, -5, 1, 10)),
647             LocalCaretRectOfPosition(PositionWithAffinity(
648                 Position::AfterNode(img), TextAffinity::kDownstream)));
649 
650   // TODO(xiaochengh): Should return the same result for legacy and LayoutNG.
651   EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(text2->GetLayoutObject(),
652                                                PhysicalRect(20, 0, 1, 10))
653                               : LocalCaretRect(text2->GetLayoutObject(),
654                                                PhysicalRect(20, 5, 1, 10)),
655             LocalCaretRectOfPosition(PositionWithAffinity(
656                 Position(text2, 0), TextAffinity::kDownstream)));
657   EXPECT_EQ(
658       LocalCaretRect(text2->GetLayoutObject(), PhysicalRect(29, 0, 1, 10)),
659       LocalCaretRectOfPosition(
660           PositionWithAffinity(Position(text2, 1), TextAffinity::kDownstream)));
661 }
662 
TEST_P(ParameterizedLocalCaretRectTest,FloatFirstLetter)663 TEST_P(ParameterizedLocalCaretRectTest, FloatFirstLetter) {
664   LoadAhem();
665   InsertStyleElement("#container::first-letter{float:right}");
666   SetBodyContent(
667       "<div id=container style='font: 10px/10px Ahem; width: 40px'>foo</div>");
668   const Node* foo = GetElementById("container")->firstChild();
669   const LayoutObject* first_letter = AssociatedLayoutObjectOf(*foo, 0);
670   const LayoutObject* remaining_text = AssociatedLayoutObjectOf(*foo, 1);
671 
672   // TODO(editing-dev): Legacy LocalCaretRectOfPosition() is not aware of the
673   // first-letter LayoutObject. Fix it.
674 
675   EXPECT_EQ(LocalCaretRect(LayoutNGEnabled() ? first_letter : remaining_text,
676                            PhysicalRect(0, 0, 1, 10)),
677             LocalCaretRectOfPosition(PositionWithAffinity(
678                 Position(foo, 0), TextAffinity::kDownstream)));
679   EXPECT_EQ(LocalCaretRect(remaining_text,
680                            PhysicalRect(LayoutNGEnabled() ? 0 : 10, 0, 1, 10)),
681             LocalCaretRectOfPosition(PositionWithAffinity(
682                 Position(foo, 1), TextAffinity::kDownstream)));
683   EXPECT_EQ(LocalCaretRect(remaining_text,
684                            PhysicalRect(LayoutNGEnabled() ? 10 : 20, 0, 1, 10)),
685             LocalCaretRectOfPosition(PositionWithAffinity(
686                 Position(foo, 2), TextAffinity::kDownstream)));
687   EXPECT_EQ(LocalCaretRect(remaining_text, PhysicalRect(20, 0, 1, 10)),
688             LocalCaretRectOfPosition(PositionWithAffinity(
689                 Position(foo, 3), TextAffinity::kDownstream)));
690 }
691 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreak)692 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreak) {
693   LoadAhem();
694   SetBodyContent("<div style='font: 10px/10px Ahem;'>foo<br><br></div>");
695   const Node* div = GetDocument().body()->firstChild();
696   const Node* foo = div->firstChild();
697   const Node* first_br = foo->nextSibling();
698   const Node* second_br = first_br->nextSibling();
699   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
700             LocalCaretRectOfPosition(PositionWithAffinity(
701                 Position::AfterNode(*foo), TextAffinity::kDownstream)));
702   EXPECT_EQ(
703       LocalCaretRect(second_br->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
704       LocalCaretRectOfPosition(PositionWithAffinity(
705           Position::AfterNode(*first_br), TextAffinity::kDownstream)));
706   EXPECT_EQ(
707       LocalCaretRect(second_br->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
708       LocalCaretRectOfPosition(PositionWithAffinity(
709           Position::AfterNode(*second_br), TextAffinity::kDownstream)));
710 }
711 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakInPre)712 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPre) {
713   LoadAhem();
714   SetBodyContent("<pre style='font: 10px/10px Ahem;'>foo\n\n</pre>");
715   const Node* pre = GetDocument().body()->firstChild();
716   const Node* foo = pre->firstChild();
717   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
718             LocalCaretRectOfPosition(PositionWithAffinity(
719                 Position(foo, 3), TextAffinity::kDownstream)));
720   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
721             LocalCaretRectOfPosition(PositionWithAffinity(
722                 Position(foo, 4), TextAffinity::kDownstream)));
723   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
724             LocalCaretRectOfPosition(PositionWithAffinity(
725                 Position(foo, 5), TextAffinity::kDownstream)));
726 }
727 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakInPre2)728 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPre2) {
729   LoadAhem();
730   // This test case simulates the rendering of the inner editor of
731   // <textarea>foo\n</textarea> without using text control element.
732   SetBodyContent("<pre style='font: 10px/10px Ahem;'>foo\n<br></pre>");
733   const Node* pre = GetDocument().body()->firstChild();
734   const Node* foo = pre->firstChild();
735   const Node* br = foo->nextSibling();
736   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
737             LocalCaretRectOfPosition(PositionWithAffinity(
738                 Position(foo, 3), TextAffinity::kDownstream)));
739   EXPECT_EQ(LocalCaretRect(br->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
740             LocalCaretRectOfPosition(PositionWithAffinity(
741                 Position(foo, 4), TextAffinity::kDownstream)));
742   EXPECT_EQ(LocalCaretRect(br->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
743             LocalCaretRectOfPosition(PositionWithAffinity(
744                 Position::AfterNode(*br), TextAffinity::kDownstream)));
745 }
746 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakTextArea)747 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakTextArea) {
748   LoadAhem();
749   SetBodyContent("<textarea style='font: 10px/10px Ahem; '>foo\n\n</textarea>");
750   const auto* textarea = ToTextControl(GetDocument().body()->firstChild());
751   const Node* inner_text = textarea->InnerEditorElement()->firstChild();
752   EXPECT_EQ(
753       LocalCaretRect(inner_text->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
754       LocalCaretRectOfPosition(PositionWithAffinity(
755           Position(inner_text, 3), TextAffinity::kDownstream)));
756   EXPECT_EQ(
757       LocalCaretRect(inner_text->GetLayoutObject(), PhysicalRect(0, 10, 1, 10)),
758       LocalCaretRectOfPosition(PositionWithAffinity(
759           Position(inner_text, 4), TextAffinity::kDownstream)));
760   const Node* hidden_br = inner_text->nextSibling();
761   EXPECT_EQ(
762       LocalCaretRect(hidden_br->GetLayoutObject(), PhysicalRect(0, 20, 1, 10)),
763       LocalCaretRectOfPosition(PositionWithAffinity(
764           Position(inner_text, 5), TextAffinity::kDownstream)));
765 }
766 
TEST_P(ParameterizedLocalCaretRectTest,CollapsedSpace)767 TEST_P(ParameterizedLocalCaretRectTest, CollapsedSpace) {
768   LoadAhem();
769   SetBodyContent(
770       "<div style='font: 10px/10px Ahem;'>"
771       "<span>foo</span><span>  </span></div>");
772   const Node* first_span = GetDocument().body()->firstChild()->firstChild();
773   const Node* foo = first_span->firstChild();
774   const Node* second_span = first_span->nextSibling();
775   const Node* white_spaces = second_span->firstChild();
776   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
777             LocalCaretRectOfPosition(PositionWithAffinity(
778                 Position(foo, 3), TextAffinity::kDownstream)));
779   EXPECT_EQ(LocalCaretRect(foo->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
780             LocalCaretRectOfPosition(PositionWithAffinity(
781                 Position::AfterNode(*foo), TextAffinity::kDownstream)));
782   // TODO(yoichio): Following should return valid rect: crbug.com/812535.
783   EXPECT_EQ(
784       LocalCaretRect(first_span->GetLayoutObject(), PhysicalRect(0, 0, 0, 0)),
785       LocalCaretRectOfPosition(PositionWithAffinity(
786           Position(first_span, PositionAnchorType::kAfterChildren),
787           TextAffinity::kDownstream)));
788   EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(foo->GetLayoutObject(),
789                                                PhysicalRect(30, 0, 1, 10))
790                               : LocalCaretRect(white_spaces->GetLayoutObject(),
791                                                PhysicalRect(0, 0, 0, 0)),
792             LocalCaretRectOfPosition(PositionWithAffinity(
793                 Position(white_spaces, 0), TextAffinity::kDownstream)));
794   EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(foo->GetLayoutObject(),
795                                                PhysicalRect(30, 0, 1, 10))
796                               : LocalCaretRect(white_spaces->GetLayoutObject(),
797                                                PhysicalRect(0, 0, 0, 0)),
798             LocalCaretRectOfPosition(PositionWithAffinity(
799                 Position(white_spaces, 1), TextAffinity::kDownstream)));
800   EXPECT_EQ(LayoutNGEnabled() ? LocalCaretRect(foo->GetLayoutObject(),
801                                                PhysicalRect(30, 0, 1, 10))
802                               : LocalCaretRect(white_spaces->GetLayoutObject(),
803                                                PhysicalRect(0, 0, 0, 0)),
804             LocalCaretRectOfPosition(PositionWithAffinity(
805                 Position(white_spaces, 2), TextAffinity::kDownstream)));
806 }
807 
TEST_P(ParameterizedLocalCaretRectTest,AbsoluteCaretBoundsOfWithShadowDOM)808 TEST_P(ParameterizedLocalCaretRectTest, AbsoluteCaretBoundsOfWithShadowDOM) {
809   const char* body_content =
810       "<p id='host'><b id='one'>11</b><b id='two'>22</b></p>";
811   const char* shadow_content =
812       "<div><content select=#two></content><content "
813       "select=#one></content></div>";
814   SetBodyContent(body_content);
815   SetShadowContent(shadow_content, "host");
816 
817   Element* body = GetDocument().body();
818   Element* one = body->QuerySelector("#one");
819 
820   IntRect bounds_in_dom_tree = AbsoluteCaretBoundsOf(
821       CreateVisiblePosition(Position(one, 0)).ToPositionWithAffinity());
822   IntRect bounds_in_flat_tree =
823       AbsoluteCaretBoundsOf(CreateVisiblePosition(PositionInFlatTree(one, 0))
824                                 .ToPositionWithAffinity());
825 
826   EXPECT_FALSE(bounds_in_dom_tree.IsEmpty());
827   EXPECT_EQ(bounds_in_dom_tree, bounds_in_flat_tree);
828 }
829 
830 // Repro case of crbug.com/680428
TEST_P(ParameterizedLocalCaretRectTest,AbsoluteSelectionBoundsOfWithImage)831 TEST_P(ParameterizedLocalCaretRectTest, AbsoluteSelectionBoundsOfWithImage) {
832   SetBodyContent("<div>foo<img></div>");
833 
834   Node* node = GetDocument().QuerySelector("img");
835   IntRect rect =
836       AbsoluteSelectionBoundsOf(VisiblePosition::Create(PositionWithAffinity(
837           Position(node, PositionAnchorType::kAfterChildren))));
838   EXPECT_FALSE(rect.IsEmpty());
839 }
840 
GetPhysicalRects(const Position & caret)841 static std::pair<PhysicalRect, PhysicalRect> GetPhysicalRects(
842     const Position& caret) {
843   const PositionWithAffinity position(caret);
844   const PhysicalRect& position_rect = LocalCaretRectOfPosition(position).rect;
845   const PositionWithAffinity visible_position(
846       CreateVisiblePosition(position).DeepEquivalent());
847   const PhysicalRect& visible_position_rect =
848       LocalCaretRectOfPosition(visible_position).rect;
849   return {position_rect, visible_position_rect};
850 }
851 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakInPreBlockLTRLineLTR)852 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPreBlockLTRLineLTR) {
853   LoadAhem();
854   InsertStyleElement("pre{ font: 10px/10px Ahem; width: 300px }");
855   const Position& caret =
856       SetCaretTextToBody("<pre dir='ltr'>foo\n|<bdo dir='ltr'>abc</bdo></pre>");
857   PhysicalRect position_rect, visible_position_rect;
858   std::tie(position_rect, visible_position_rect) = GetPhysicalRects(caret);
859   EXPECT_EQ(PhysicalRect(0, 10, 1, 10), position_rect);
860   EXPECT_EQ(PhysicalRect(0, 10, 1, 10), visible_position_rect);
861 }
862 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakInPreBlockLTRLineRTL)863 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPreBlockLTRLineRTL) {
864   LoadAhem();
865   InsertStyleElement("pre{ font: 10px/10px Ahem; width: 300px }");
866   const Position& caret =
867       SetCaretTextToBody("<pre dir='ltr'>foo\n|<bdo dir='rtl'>abc</bdo></pre>");
868   PhysicalRect position_rect, visible_position_rect;
869   std::tie(position_rect, visible_position_rect) = GetPhysicalRects(caret);
870   EXPECT_EQ(PhysicalRect(0, 10, 1, 10), position_rect);
871   EXPECT_EQ(PhysicalRect(0, 10, 1, 10), visible_position_rect);
872 }
873 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakInPreBlockRTLLineLTR)874 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPreBlockRTLLineLTR) {
875   LoadAhem();
876   InsertStyleElement("pre{ font: 10px/10px Ahem; width: 300px }");
877   const Position& caret =
878       SetCaretTextToBody("<pre dir='rtl'>foo\n|<bdo dir='ltr'>abc</bdo></pre>");
879   PhysicalRect position_rect, visible_position_rect;
880   std::tie(position_rect, visible_position_rect) = GetPhysicalRects(caret);
881   EXPECT_EQ(PhysicalRect(299, 10, 1, 10), position_rect);
882   EXPECT_EQ(PhysicalRect(299, 10, 1, 10), visible_position_rect);
883 }
884 
TEST_P(ParameterizedLocalCaretRectTest,AfterLineBreakInPreBlockRTLLineRTL)885 TEST_P(ParameterizedLocalCaretRectTest, AfterLineBreakInPreBlockRTLLineRTL) {
886   LoadAhem();
887   InsertStyleElement("pre{ font: 10px/10px Ahem; width: 300px }");
888   const Position& caret =
889       SetCaretTextToBody("<pre dir='rtl'>foo\n|<bdo dir='rtl'>abc</bdo></pre>");
890   PhysicalRect position_rect, visible_position_rect;
891   std::tie(position_rect, visible_position_rect) = GetPhysicalRects(caret);
892   EXPECT_EQ(PhysicalRect(299, 10, 1, 10), position_rect);
893   EXPECT_EQ(PhysicalRect(299, 10, 1, 10), visible_position_rect);
894 }
895 
896 // crbug.com/834686
TEST_P(ParameterizedLocalCaretRectTest,AfterTrimedLineBreak)897 TEST_P(ParameterizedLocalCaretRectTest, AfterTrimedLineBreak) {
898   LoadAhem();
899   InsertStyleElement("body { font: 10px/10px Ahem; width: 300px }");
900   const Position& caret = SetCaretTextToBody("<div>foo\n|</div>");
901   PhysicalRect position_rect, visible_position_rect;
902   std::tie(position_rect, visible_position_rect) = GetPhysicalRects(caret);
903   EXPECT_EQ(PhysicalRect(30, 0, 1, 10), position_rect);
904   EXPECT_EQ(PhysicalRect(30, 0, 1, 10), visible_position_rect);
905 }
906 
TEST_P(ParameterizedLocalCaretRectTest,UnicodeBidiPlaintextWithDifferentBlockDirection)907 TEST_P(ParameterizedLocalCaretRectTest,
908        UnicodeBidiPlaintextWithDifferentBlockDirection) {
909   LoadAhem();
910   InsertStyleElement("div { font: 10px/10px Ahem; unicode-bidi: plaintext }");
911   const Position position = SetCaretTextToBody("<div dir='rtl'>|abc</div>");
912   const PhysicalRect caret_rect =
913       LocalCaretRectOfPosition(PositionWithAffinity(position)).rect;
914   EXPECT_EQ(PhysicalRect(0, 0, 1, 10), caret_rect);
915 }
916 
917 // http://crbug.com/835779
TEST_P(ParameterizedLocalCaretRectTest,NextLineWithoutLeafChild)918 TEST_P(ParameterizedLocalCaretRectTest, NextLineWithoutLeafChild) {
919   LoadAhem();
920   InsertStyleElement("div { font: 10px/10px Ahem; width: 30px }");
921   SetBodyContent(
922       "<div>"
923       "<br>"
924       "<span style=\"border-left: 50px solid\"></span>"
925       "foo"
926       "</div>");
927 
928   const Element& br = *GetDocument().QuerySelector("br");
929   EXPECT_EQ(
930       // TODO(xiaochengh): Should return the same result for legacy and
931       // LayoutNG.
932       LayoutNGEnabled() ? PhysicalRect(50, 10, 1, 10)
933                         : PhysicalRect(0, 20, 1, 10),
934       LocalCaretRectOfPosition(PositionWithAffinity(Position::AfterNode(br)))
935           .rect);
936 }
937 
TEST_P(ParameterizedLocalCaretRectTest,BidiTextWithImage)938 TEST_P(ParameterizedLocalCaretRectTest, BidiTextWithImage) {
939   LoadAhem();
940   InsertStyleElement(
941       "div { font: 10px/10px Ahem; width: 30px }"
942       "img { width: 10px; height: 10px; vertical-align: bottom }");
943   SetBodyContent("<div dir=rtl>X<img id=image>Y</div>");
944   const Element& image = *GetElementById("image");
945   const LayoutObject* image_layout = image.GetLayoutObject();
946   const LayoutObject* text_before = image.previousSibling()->GetLayoutObject();
947   // TODO(xiaochengh): Should return the same result for legacy and NG
948   EXPECT_EQ(LayoutNGEnabled()
949                 ? LocalCaretRect(text_before, PhysicalRect(10, 0, 1, 10))
950                 : LocalCaretRect(image_layout, PhysicalRect(0, 0, 1, 10)),
951             LocalCaretRectOfPosition(
952                 PositionWithAffinity(Position::BeforeNode(image))));
953   EXPECT_EQ(LocalCaretRect(image_layout, PhysicalRect(9, 0, 1, 10)),
954             LocalCaretRectOfPosition(
955                 PositionWithAffinity(Position::AfterNode(image))));
956 }
957 
958 // https://crbug.com/876044
TEST_P(ParameterizedLocalCaretRectTest,RtlMeterNoCrash)959 TEST_P(ParameterizedLocalCaretRectTest, RtlMeterNoCrash) {
960   SetBodyContent("foo<meter dir=rtl></meter>");
961   const Position position = Position::LastPositionInNode(*GetDocument().body());
962   // Shouldn't crash inside
963   const LocalCaretRect local_caret_rect =
964       LocalCaretRectOfPosition(PositionWithAffinity(position));
965   EXPECT_EQ(GetDocument().QuerySelector("meter")->GetLayoutObject(),
966             local_caret_rect.layout_object);
967 }
968 
969 // https://crbug.com/883044
TEST_P(ParameterizedLocalCaretRectTest,AfterCollapsedWhiteSpaceInRTLText)970 TEST_P(ParameterizedLocalCaretRectTest, AfterCollapsedWhiteSpaceInRTLText) {
971   LoadAhem();
972   InsertStyleElement(
973       "bdo { display: block; font: 10px/10px Ahem; width: 100px }");
974   const Position position =
975       SetCaretTextToBody("<bdo dir=rtl>AAA  |BBB<span>CCC</span></bdo>");
976   const Node* text = position.AnchorNode();
977   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(60, 0, 1, 10)),
978             LocalCaretRectOfPosition(
979                 PositionWithAffinity(position, TextAffinity::kDownstream)));
980 }
981 
982 // https://crbug.com/936988
TEST_P(ParameterizedLocalCaretRectTest,AfterIneditableInline)983 TEST_P(ParameterizedLocalCaretRectTest, AfterIneditableInline) {
984   // For LayoutNG, we also enable EditingNG to test NG caret rendering.
985   ScopedEditingNGForTest editing_ng(LayoutNGEnabled());
986 
987   LoadAhem();
988   InsertStyleElement("div { font: 10px/10px Ahem }");
989   SetBodyContent(
990       "<div contenteditable><span contenteditable=\"false\">foo</span></div>");
991   const Element* div = GetDocument().QuerySelector("div");
992   const Node* text = div->firstChild()->firstChild();
993 
994   const Position position = Position::LastPositionInNode(*div);
995   EXPECT_EQ(LocalCaretRect(text->GetLayoutObject(), PhysicalRect(30, 0, 1, 10)),
996             LocalCaretRectOfPosition(PositionWithAffinity(position)));
997 }
998 
999 }  // namespace blink
1000