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