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