1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 // Copyright 2017 The Chromium Authors. All rights reserved.
28 // Use of this source code is governed by a BSD-style license that can be
29 // found in the LICENSE file.
30
31 #include "third_party/blink/renderer/core/editing/visible_units.h"
32
33 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
34 #include "third_party/blink/renderer/core/editing/inline_box_position.h"
35 #include "third_party/blink/renderer/core/editing/ng_flat_tree_shorthands.h"
36 #include "third_party/blink/renderer/core/editing/visible_position.h"
37 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
38 #include "third_party/blink/renderer/core/layout/line/root_inline_box.h"
39 #include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
40 #include "third_party/blink/renderer/core/layout/ng/inline/ng_line_utils.h"
41 #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
42
43 namespace blink {
44
45 namespace {
46
47 struct VisualOrdering;
48
49 template <typename Strategy, typename Ordering>
StartPositionForLine(const PositionWithAffinityTemplate<Strategy> & c)50 PositionWithAffinityTemplate<Strategy> StartPositionForLine(
51 const PositionWithAffinityTemplate<Strategy>& c) {
52 if (c.IsNull())
53 return PositionWithAffinityTemplate<Strategy>();
54 const PositionWithAffinityTemplate<Strategy> adjusted =
55 ComputeInlineAdjustedPosition(c);
56
57 if (const LayoutBlockFlow* context =
58 NGInlineFormattingContextOf(adjusted.GetPosition())) {
59 DCHECK((std::is_same<Ordering, VisualOrdering>::value) ||
60 !RuntimeEnabledFeatures::BidiCaretAffinityEnabled())
61 << "Logical line boundary for BidiCaretAffinity is not implemented yet";
62
63 const NGCaretPosition caret_position = ComputeNGCaretPosition(adjusted);
64 if (caret_position.IsNull()) {
65 // TODO(crbug.com/947593): Support |ComputeNGCaretPosition()| on content
66 // hidden by 'text-overflow:ellipsis' so that we always have a non-null
67 // |caret_position| here.
68 return PositionWithAffinityTemplate<Strategy>();
69 }
70 NGInlineCursor line_box = caret_position.cursor;
71 line_box.MoveToContainingLine();
72 DCHECK(line_box.Current().IsLineBox()) << line_box;
73 const PhysicalOffset start_point = line_box.LineStartPoint();
74 return FromPositionInDOMTree<Strategy>(
75 line_box.PositionForPointInInlineBox(start_point));
76 }
77
78 const InlineBox* inline_box =
79 adjusted.IsNotNull()
80 ? ComputeInlineBoxPositionForInlineAdjustedPosition(adjusted)
81 .inline_box
82 : nullptr;
83 if (!inline_box) {
84 // There are VisiblePositions at offset 0 in blocks without
85 // RootInlineBoxes, like empty editable blocks and bordered blocks.
86 PositionTemplate<Strategy> p = c.GetPosition();
87 if (p.AnchorNode()->GetLayoutObject() &&
88 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() &&
89 !p.ComputeEditingOffset())
90 return c;
91
92 return PositionWithAffinityTemplate<Strategy>();
93 }
94
95 const RootInlineBox& root_box = inline_box->Root();
96 const InlineBox* const start_box = Ordering::StartNonPseudoBoxOf(root_box);
97 if (!start_box)
98 return PositionWithAffinityTemplate<Strategy>();
99
100 const Node* const start_node = start_box->GetLineLayoutItem().NonPseudoNode();
101 auto* text_start_node = DynamicTo<Text>(start_node);
102 return PositionWithAffinityTemplate<Strategy>(
103 text_start_node
104 ? PositionTemplate<Strategy>(text_start_node,
105 ToInlineTextBox(start_box)->Start())
106 : PositionTemplate<Strategy>::BeforeNode(*start_node));
107 }
108
109 // Provides start and end of line in logical order for implementing Home and End
110 // keys.
111 struct LogicalOrdering {
StartNonPseudoBoxOfblink::__anonae756a040111::LogicalOrdering112 static const InlineBox* StartNonPseudoBoxOf(const RootInlineBox& root_box) {
113 return root_box.GetLogicalStartNonPseudoBox();
114 }
115
EndNonPseudoBoxOfblink::__anonae756a040111::LogicalOrdering116 static const InlineBox* EndNonPseudoBoxOf(const RootInlineBox& root_box) {
117 return root_box.GetLogicalEndNonPseudoBox();
118 }
119 };
120
121 // Provides start end end of line in visual order for implementing expanding
122 // selection in line granularity.
123 struct VisualOrdering {
StartNonPseudoBoxOfblink::__anonae756a040111::VisualOrdering124 static const InlineBox* StartNonPseudoBoxOf(const RootInlineBox& root_box) {
125 // Generated content (e.g. list markers and CSS :before and :after
126 // pseudoelements) have no corresponding DOM element, and so cannot be
127 // represented by a VisiblePosition. Use whatever follows instead.
128 // TODO(editing-dev): We should consider text-direction of line to
129 // find non-pseudo node.
130 for (InlineBox* inline_box = root_box.FirstLeafChild(); inline_box;
131 inline_box = inline_box->NextLeafChild()) {
132 if (inline_box->GetLineLayoutItem().NonPseudoNode())
133 return inline_box;
134 }
135 return nullptr;
136 }
137
EndNonPseudoBoxOfblink::__anonae756a040111::VisualOrdering138 static const InlineBox* EndNonPseudoBoxOf(const RootInlineBox& root_box) {
139 // Generated content (e.g. list markers and CSS :before and :after
140 // pseudo elements) have no corresponding DOM element, and so cannot be
141 // represented by a VisiblePosition. Use whatever precedes instead.
142 // TODO(editing-dev): We should consider text-direction of line to
143 // find non-pseudo node.
144 for (InlineBox* inline_box = root_box.LastLeafChild(); inline_box;
145 inline_box = inline_box->PrevLeafChild()) {
146 if (inline_box->GetLineLayoutItem().NonPseudoNode())
147 return inline_box;
148 }
149 return nullptr;
150 }
151 };
152
153 template <typename Strategy>
StartOfLineAlgorithm(const PositionWithAffinityTemplate<Strategy> & c)154 PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm(
155 const PositionWithAffinityTemplate<Strategy>& c) {
156 // TODO: this is the current behavior that might need to be fixed.
157 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
158 PositionWithAffinityTemplate<Strategy> vis_pos =
159 StartPositionForLine<Strategy, VisualOrdering>(c);
160 return AdjustBackwardPositionToAvoidCrossingEditingBoundaries(
161 vis_pos, c.GetPosition());
162 }
163
StartOfLine(const PositionWithAffinity & current_position)164 PositionWithAffinity StartOfLine(const PositionWithAffinity& current_position) {
165 return StartOfLineAlgorithm<EditingStrategy>(current_position);
166 }
167
StartOfLine(const PositionInFlatTreeWithAffinity & current_position)168 PositionInFlatTreeWithAffinity StartOfLine(
169 const PositionInFlatTreeWithAffinity& current_position) {
170 return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position);
171 }
172
173 } // namespace
174
175 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
StartOfLine(const VisiblePosition & current_position)176 VisiblePosition StartOfLine(const VisiblePosition& current_position) {
177 DCHECK(current_position.IsValid()) << current_position;
178 return CreateVisiblePosition(
179 StartOfLine(current_position.ToPositionWithAffinity()));
180 }
181
StartOfLine(const VisiblePositionInFlatTree & current_position)182 VisiblePositionInFlatTree StartOfLine(
183 const VisiblePositionInFlatTree& current_position) {
184 DCHECK(current_position.IsValid()) << current_position;
185 return CreateVisiblePosition(
186 StartOfLine(current_position.ToPositionWithAffinity()));
187 }
188
189 template <typename Strategy>
LogicalStartOfLineAlgorithm(const PositionWithAffinityTemplate<Strategy> & c)190 static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm(
191 const PositionWithAffinityTemplate<Strategy>& c) {
192 // TODO: this is the current behavior that might need to be fixed.
193 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
194 PositionWithAffinityTemplate<Strategy> vis_pos =
195 StartPositionForLine<Strategy, LogicalOrdering>(c);
196
197 if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) {
198 if (!editable_root->contains(
199 vis_pos.GetPosition().ComputeContainerNode())) {
200 return PositionWithAffinityTemplate<Strategy>(
201 PositionTemplate<Strategy>::FirstPositionInNode(*editable_root));
202 }
203 }
204
205 return AdjustBackwardPositionToAvoidCrossingEditingBoundaries(
206 vis_pos, c.GetPosition());
207 }
208
LogicalStartOfLine(const PositionWithAffinity & position)209 static PositionWithAffinity LogicalStartOfLine(
210 const PositionWithAffinity& position) {
211 return LogicalStartOfLineAlgorithm<EditingStrategy>(position);
212 }
213
LogicalStartOfLine(const PositionInFlatTreeWithAffinity & position)214 static PositionInFlatTreeWithAffinity LogicalStartOfLine(
215 const PositionInFlatTreeWithAffinity& position) {
216 return LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>(position);
217 }
218
LogicalStartOfLine(const VisiblePosition & current_position)219 VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) {
220 DCHECK(current_position.IsValid()) << current_position;
221 return CreateVisiblePosition(
222 LogicalStartOfLine(current_position.ToPositionWithAffinity()));
223 }
224
LogicalStartOfLine(const VisiblePositionInFlatTree & current_position)225 VisiblePositionInFlatTree LogicalStartOfLine(
226 const VisiblePositionInFlatTree& current_position) {
227 DCHECK(current_position.IsValid()) << current_position;
228 return CreateVisiblePosition(
229 LogicalStartOfLine(current_position.ToPositionWithAffinity()));
230 }
231
232 template <typename Strategy, typename Ordering>
EndPositionForLine(const PositionWithAffinityTemplate<Strategy> & c)233 static PositionWithAffinityTemplate<Strategy> EndPositionForLine(
234 const PositionWithAffinityTemplate<Strategy>& c) {
235 if (c.IsNull())
236 return PositionWithAffinityTemplate<Strategy>();
237 const PositionWithAffinityTemplate<Strategy> adjusted =
238 ComputeInlineAdjustedPosition(c);
239
240 if (const LayoutBlockFlow* context =
241 NGInlineFormattingContextOf(adjusted.GetPosition())) {
242 DCHECK((std::is_same<Ordering, VisualOrdering>::value) ||
243 !RuntimeEnabledFeatures::BidiCaretAffinityEnabled())
244 << "Logical line boundary for BidiCaretAffinity is not implemented yet";
245
246 const NGCaretPosition caret_position = ComputeNGCaretPosition(adjusted);
247 if (caret_position.IsNull()) {
248 // TODO(crbug.com/947593): Support |ComputeNGCaretPosition()| on content
249 // hidden by 'text-overflow:ellipsis' so that we always have a non-null
250 // |caret_position| here.
251 return PositionWithAffinityTemplate<Strategy>();
252 }
253 NGInlineCursor line_box = caret_position.cursor;
254 line_box.MoveToContainingLine();
255 const PhysicalOffset end_point = line_box.LineEndPoint();
256 return FromPositionInDOMTree<Strategy>(
257 line_box.PositionForPointInInlineBox(end_point));
258 }
259
260 const InlineBox* inline_box =
261 adjusted.IsNotNull() ? ComputeInlineBoxPosition(c).inline_box : nullptr;
262 if (!inline_box) {
263 // There are VisiblePositions at offset 0 in blocks without
264 // RootInlineBoxes, like empty editable blocks and bordered blocks.
265 const PositionTemplate<Strategy> p = c.GetPosition();
266 if (p.AnchorNode()->GetLayoutObject() &&
267 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() &&
268 !p.ComputeEditingOffset())
269 return c;
270 return PositionWithAffinityTemplate<Strategy>();
271 }
272
273 const RootInlineBox& root_box = inline_box->Root();
274 const InlineBox* const end_box = Ordering::EndNonPseudoBoxOf(root_box);
275 if (!end_box)
276 return PositionWithAffinityTemplate<Strategy>();
277
278 const Node* const end_node = end_box->GetLineLayoutItem().NonPseudoNode();
279 DCHECK(end_node);
280 if (IsA<HTMLBRElement>(*end_node)) {
281 return PositionWithAffinityTemplate<Strategy>(
282 PositionTemplate<Strategy>::BeforeNode(*end_node),
283 TextAffinity::kUpstreamIfPossible);
284 }
285
286 auto* end_text_node = DynamicTo<Text>(end_node);
287 if (end_box->IsInlineTextBox() && end_text_node) {
288 const InlineTextBox* end_text_box = ToInlineTextBox(end_box);
289 int end_offset = end_text_box->Start();
290 if (!end_text_box->IsLineBreak())
291 end_offset += end_text_box->Len();
292 return PositionWithAffinityTemplate<Strategy>(
293 PositionTemplate<Strategy>(end_text_node, end_offset),
294 TextAffinity::kUpstreamIfPossible);
295 }
296 return PositionWithAffinityTemplate<Strategy>(
297 PositionTemplate<Strategy>::AfterNode(*end_node),
298 TextAffinity::kUpstreamIfPossible);
299 }
300
301 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels.
302 template <typename Strategy>
EndOfLineAlgorithm(const PositionWithAffinityTemplate<Strategy> & current_position)303 static PositionWithAffinityTemplate<Strategy> EndOfLineAlgorithm(
304 const PositionWithAffinityTemplate<Strategy>& current_position) {
305 // TODO(yosin) this is the current behavior that might need to be fixed.
306 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
307 const PositionWithAffinityTemplate<Strategy>& candidate_position =
308 EndPositionForLine<Strategy, VisualOrdering>(current_position);
309
310 // Make sure the end of line is at the same line as the given input
311 // position. Else use the previous position to obtain end of line. This
312 // condition happens when the input position is before the space character
313 // at the end of a soft-wrapped non-editable line. In this scenario,
314 // |endPositionForLine()| would incorrectly hand back a position in the next
315 // line instead. This fix is to account for the discrepancy between lines
316 // with "webkit-line-break:after-white-space" style versus lines without
317 // that style, which would break before a space by default.
318 if (InSameLine(current_position, candidate_position)) {
319 return AdjustForwardPositionToAvoidCrossingEditingBoundaries(
320 candidate_position, current_position.GetPosition());
321 }
322 const PositionWithAffinityTemplate<Strategy>& adjusted_position =
323 PreviousPositionOf(CreateVisiblePosition(current_position))
324 .ToPositionWithAffinity();
325 if (adjusted_position.IsNull())
326 return PositionWithAffinityTemplate<Strategy>();
327 return AdjustForwardPositionToAvoidCrossingEditingBoundaries(
328 EndPositionForLine<Strategy, VisualOrdering>(adjusted_position),
329 current_position.GetPosition());
330 }
331
EndOfLine(const PositionWithAffinity & position)332 static PositionWithAffinity EndOfLine(const PositionWithAffinity& position) {
333 return EndOfLineAlgorithm<EditingStrategy>(position);
334 }
335
EndOfLine(const PositionInFlatTreeWithAffinity & position)336 static PositionInFlatTreeWithAffinity EndOfLine(
337 const PositionInFlatTreeWithAffinity& position) {
338 return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(position);
339 }
340
341 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels.
EndOfLine(const VisiblePosition & current_position)342 VisiblePosition EndOfLine(const VisiblePosition& current_position) {
343 DCHECK(current_position.IsValid()) << current_position;
344 return CreateVisiblePosition(
345 EndOfLine(current_position.ToPositionWithAffinity()));
346 }
347
EndOfLine(const VisiblePositionInFlatTree & current_position)348 VisiblePositionInFlatTree EndOfLine(
349 const VisiblePositionInFlatTree& current_position) {
350 DCHECK(current_position.IsValid()) << current_position;
351 return CreateVisiblePosition(
352 EndOfLine(current_position.ToPositionWithAffinity()));
353 }
354
355 template <typename Strategy>
InSameLogicalLine(const PositionWithAffinityTemplate<Strategy> & position1,const PositionWithAffinityTemplate<Strategy> & position2)356 static bool InSameLogicalLine(
357 const PositionWithAffinityTemplate<Strategy>& position1,
358 const PositionWithAffinityTemplate<Strategy>& position2) {
359 return position1.IsNotNull() &&
360 LogicalStartOfLine(position1).GetPosition() ==
361 LogicalStartOfLine(position2).GetPosition();
362 }
363
364 template <typename Strategy>
LogicalEndOfLineAlgorithm(const PositionWithAffinityTemplate<Strategy> & current_position)365 static PositionWithAffinityTemplate<Strategy> LogicalEndOfLineAlgorithm(
366 const PositionWithAffinityTemplate<Strategy>& current_position) {
367 // TODO(yosin) this is the current behavior that might need to be fixed.
368 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
369 PositionWithAffinityTemplate<Strategy> vis_pos =
370 EndPositionForLine<Strategy, LogicalOrdering>(current_position);
371
372 // Make sure the end of line is at the same line as the given input
373 // position. For a wrapping line, the logical end position for the
374 // not-last-2-lines might incorrectly hand back the logical beginning of the
375 // next line. For example,
376 // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz
377 // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div>
378 // In this case, use the previous position of the computed logical end
379 // position.
380 if (!InSameLogicalLine(current_position, vis_pos)) {
381 vis_pos = PreviousPositionOf(CreateVisiblePosition(vis_pos))
382 .ToPositionWithAffinity();
383 }
384
385 if (ContainerNode* editable_root =
386 HighestEditableRoot(current_position.GetPosition())) {
387 if (!editable_root->contains(
388 vis_pos.GetPosition().ComputeContainerNode())) {
389 return PositionWithAffinityTemplate<Strategy>(
390 PositionTemplate<Strategy>::LastPositionInNode(*editable_root));
391 }
392 }
393
394 return AdjustForwardPositionToAvoidCrossingEditingBoundaries(
395 vis_pos, current_position.GetPosition());
396 }
397
LogicalEndOfLine(const PositionWithAffinity & position)398 static PositionWithAffinity LogicalEndOfLine(
399 const PositionWithAffinity& position) {
400 return LogicalEndOfLineAlgorithm<EditingStrategy>(position);
401 }
402
LogicalEndOfLine(const PositionInFlatTreeWithAffinity & position)403 static PositionInFlatTreeWithAffinity LogicalEndOfLine(
404 const PositionInFlatTreeWithAffinity& position) {
405 return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(position);
406 }
407
LogicalEndOfLine(const VisiblePosition & current_position)408 VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) {
409 DCHECK(current_position.IsValid()) << current_position;
410 return CreateVisiblePosition(
411 LogicalEndOfLine(current_position.ToPositionWithAffinity()));
412 }
413
LogicalEndOfLine(const VisiblePositionInFlatTree & current_position)414 VisiblePositionInFlatTree LogicalEndOfLine(
415 const VisiblePositionInFlatTree& current_position) {
416 DCHECK(current_position.IsValid()) << current_position;
417 return CreateVisiblePosition(
418 LogicalEndOfLine(current_position.ToPositionWithAffinity()));
419 }
420
421 template <typename Strategy>
InSameLineAlgorithm(const PositionWithAffinityTemplate<Strategy> & position1,const PositionWithAffinityTemplate<Strategy> & position2)422 static bool InSameLineAlgorithm(
423 const PositionWithAffinityTemplate<Strategy>& position1,
424 const PositionWithAffinityTemplate<Strategy>& position2) {
425 if (position1.IsNull() || position2.IsNull())
426 return false;
427 DCHECK_EQ(position1.GetDocument(), position2.GetDocument());
428 DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate());
429
430 if (RuntimeEnabledFeatures::LayoutNGEnabled()) {
431 const LayoutBlockFlow* block1 =
432 NGInlineFormattingContextOf(position1.GetPosition());
433 const LayoutBlockFlow* block2 =
434 NGInlineFormattingContextOf(position2.GetPosition());
435 if (block1 || block2) {
436 if (block1 != block2)
437 return false;
438 // TODO(editing-dev): We may incorrectly return false if a position is in
439 // an empty NG block with height, in which case there is no line box. We
440 // must handle this case when enabling Layout NG for contenteditable.
441 return InSameNGLineBox(position1, position2);
442 }
443
444 // Neither positions are in LayoutNG. Fall through to legacy handling.
445 }
446
447 PositionWithAffinityTemplate<Strategy> start_of_line1 =
448 StartOfLine(position1);
449 PositionWithAffinityTemplate<Strategy> start_of_line2 =
450 StartOfLine(position2);
451 if (start_of_line1 == start_of_line2)
452 return true;
453 PositionTemplate<Strategy> canonicalized1 =
454 CanonicalPositionOf(start_of_line1.GetPosition());
455 if (canonicalized1 == start_of_line2.GetPosition())
456 return true;
457 return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition());
458 }
459
InSameLine(const PositionWithAffinity & a,const PositionWithAffinity & b)460 bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) {
461 return InSameLineAlgorithm<EditingStrategy>(a, b);
462 }
463
InSameLine(const PositionInFlatTreeWithAffinity & position1,const PositionInFlatTreeWithAffinity & position2)464 bool InSameLine(const PositionInFlatTreeWithAffinity& position1,
465 const PositionInFlatTreeWithAffinity& position2) {
466 return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2);
467 }
468
InSameLine(const VisiblePosition & position1,const VisiblePosition & position2)469 bool InSameLine(const VisiblePosition& position1,
470 const VisiblePosition& position2) {
471 DCHECK(position1.IsValid()) << position1;
472 DCHECK(position2.IsValid()) << position2;
473 return InSameLine(position1.ToPositionWithAffinity(),
474 position2.ToPositionWithAffinity());
475 }
476
InSameLine(const VisiblePositionInFlatTree & position1,const VisiblePositionInFlatTree & position2)477 bool InSameLine(const VisiblePositionInFlatTree& position1,
478 const VisiblePositionInFlatTree& position2) {
479 DCHECK(position1.IsValid()) << position1;
480 DCHECK(position2.IsValid()) << position2;
481 return InSameLine(position1.ToPositionWithAffinity(),
482 position2.ToPositionWithAffinity());
483 }
484
485 template <typename Strategy>
IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy> & p)486 static bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) {
487 DCHECK(p.IsValid()) << p;
488 return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent();
489 }
490
IsStartOfLine(const VisiblePosition & p)491 bool IsStartOfLine(const VisiblePosition& p) {
492 return IsStartOfLineAlgorithm<EditingStrategy>(p);
493 }
494
IsStartOfLine(const VisiblePositionInFlatTree & p)495 bool IsStartOfLine(const VisiblePositionInFlatTree& p) {
496 return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
497 }
498
499 template <typename Strategy>
IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy> & p)500 static bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) {
501 DCHECK(p.IsValid()) << p;
502 return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent();
503 }
504
IsEndOfLine(const VisiblePosition & p)505 bool IsEndOfLine(const VisiblePosition& p) {
506 return IsEndOfLineAlgorithm<EditingStrategy>(p);
507 }
508
IsEndOfLine(const VisiblePositionInFlatTree & p)509 bool IsEndOfLine(const VisiblePositionInFlatTree& p) {
510 return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
511 }
512
513 template <typename Strategy>
IsLogicalEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy> & p)514 static bool IsLogicalEndOfLineAlgorithm(
515 const VisiblePositionTemplate<Strategy>& p) {
516 DCHECK(p.IsValid()) << p;
517 return p.IsNotNull() &&
518 p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent();
519 }
520
IsLogicalEndOfLine(const VisiblePosition & p)521 bool IsLogicalEndOfLine(const VisiblePosition& p) {
522 return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p);
523 }
524
IsLogicalEndOfLine(const VisiblePositionInFlatTree & p)525 bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) {
526 return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p);
527 }
528
529 } // namespace blink
530