OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights | |
Xiaocheng
2017/05/31 18:12:21
We should use the Chromium Authors ... copyright n
yosin_UTC9
2017/06/01 01:09:16
No, we should use original copyright header.
Diss
tkent
2017/06/01 01:15:58
yeah, I agree about this case.
| |
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 #include "core/editing/VisibleUnits.h" | |
28 | |
29 #include "core/editing/EditingUtilities.h" | |
30 #include "core/editing/RenderedPosition.h" | |
31 #include "core/layout/api/LineLayoutBlockFlow.h" | |
32 #include "core/layout/line/InlineTextBox.h" | |
33 #include "core/layout/line/RootInlineBox.h" | |
34 | |
35 namespace blink { | |
36 | |
37 namespace { | |
38 | |
39 bool HasEditableStyle(const Node& node, EditableType editable_type) { | |
40 if (editable_type == kHasEditableAXRole) { | |
41 if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) { | |
42 if (cache->RootAXEditableElement(&node)) | |
43 return true; | |
44 } | |
45 } | |
46 | |
47 return HasEditableStyle(node); | |
48 } | |
49 | |
50 Element* RootEditableElement(const Node& node, EditableType editable_type) { | |
51 if (editable_type == kHasEditableAXRole) { | |
52 if (AXObjectCache* cache = node.GetDocument().ExistingAXObjectCache()) | |
53 return const_cast<Element*>(cache->RootAXEditableElement(&node)); | |
54 } | |
55 | |
56 return RootEditableElement(node); | |
57 } | |
58 | |
59 Element* RootAXEditableElementOf(const Position& position) { | |
60 Node* node = position.ComputeContainerNode(); | |
61 if (!node) | |
62 return nullptr; | |
63 | |
64 if (IsDisplayInsideTable(node)) | |
65 node = node->parentNode(); | |
66 | |
67 return RootEditableElement(*node, kHasEditableAXRole); | |
68 } | |
69 | |
70 bool HasAXEditableStyle(const Node& node) { | |
71 return HasEditableStyle(node, kHasEditableAXRole); | |
72 } | |
73 | |
74 ContainerNode* HighestEditableRoot(const Position& position, | |
75 EditableType editable_type) { | |
76 if (editable_type == kHasEditableAXRole) { | |
77 return HighestEditableRoot(position, RootAXEditableElementOf, | |
78 HasAXEditableStyle); | |
79 } | |
80 | |
81 return HighestEditableRoot(position); | |
82 } | |
83 | |
84 Node* PreviousLeafWithSameEditability(Node* node, EditableType editable_type) { | |
85 bool editable = HasEditableStyle(*node, editable_type); | |
86 node = PreviousAtomicLeafNode(*node); | |
87 while (node) { | |
88 if (editable == HasEditableStyle(*node, editable_type)) | |
89 return node; | |
90 node = PreviousAtomicLeafNode(*node); | |
91 } | |
92 return nullptr; | |
93 } | |
94 | |
95 Node* NextLeafWithSameEditability( | |
96 Node* node, | |
97 EditableType editable_type = kContentIsEditable) { | |
98 if (!node) | |
99 return nullptr; | |
100 | |
101 bool editable = HasEditableStyle(*node, editable_type); | |
102 node = NextAtomicLeafNode(*node); | |
103 while (node) { | |
104 if (editable == HasEditableStyle(*node, editable_type)) | |
105 return node; | |
106 node = NextAtomicLeafNode(*node); | |
107 } | |
108 return nullptr; | |
109 } | |
110 | |
111 enum LineEndpointComputationMode { kUseLogicalOrdering, kUseInlineBoxOrdering }; | |
112 template <typename Strategy> | |
113 PositionWithAffinityTemplate<Strategy> StartPositionForLine( | |
114 const PositionWithAffinityTemplate<Strategy>& c, | |
115 LineEndpointComputationMode mode) { | |
116 if (c.IsNull()) | |
117 return PositionWithAffinityTemplate<Strategy>(); | |
118 | |
119 RootInlineBox* root_box = | |
120 RenderedPosition(c.GetPosition(), c.Affinity()).RootBox(); | |
121 if (!root_box) { | |
122 // There are VisiblePositions at offset 0 in blocks without | |
123 // RootInlineBoxes, like empty editable blocks and bordered blocks. | |
124 PositionTemplate<Strategy> p = c.GetPosition(); | |
125 if (p.AnchorNode()->GetLayoutObject() && | |
126 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && | |
127 !p.ComputeEditingOffset()) | |
128 return c; | |
129 | |
130 return PositionWithAffinityTemplate<Strategy>(); | |
131 } | |
132 | |
133 Node* start_node; | |
134 InlineBox* start_box; | |
135 if (mode == kUseLogicalOrdering) { | |
136 start_node = root_box->GetLogicalStartBoxWithNode(start_box); | |
137 if (!start_node) | |
138 return PositionWithAffinityTemplate<Strategy>(); | |
139 } else { | |
140 // Generated content (e.g. list markers and CSS :before and :after | |
141 // pseudoelements) have no corresponding DOM element, and so cannot be | |
142 // represented by a VisiblePosition. Use whatever follows instead. | |
143 start_box = root_box->FirstLeafChild(); | |
144 while (true) { | |
145 if (!start_box) | |
146 return PositionWithAffinityTemplate<Strategy>(); | |
147 | |
148 start_node = start_box->GetLineLayoutItem().NonPseudoNode(); | |
149 if (start_node) | |
150 break; | |
151 | |
152 start_box = start_box->NextLeafChild(); | |
153 } | |
154 } | |
155 | |
156 return PositionWithAffinityTemplate<Strategy>( | |
157 start_node->IsTextNode() | |
158 ? PositionTemplate<Strategy>(ToText(start_node), | |
159 ToInlineTextBox(start_box)->Start()) | |
160 : PositionTemplate<Strategy>::BeforeNode(start_node)); | |
161 } | |
162 | |
163 template <typename Strategy> | |
164 PositionWithAffinityTemplate<Strategy> StartOfLineAlgorithm( | |
165 const PositionWithAffinityTemplate<Strategy>& c) { | |
166 // TODO: this is the current behavior that might need to be fixed. | |
167 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
168 PositionWithAffinityTemplate<Strategy> vis_pos = | |
169 StartPositionForLine(c, kUseInlineBoxOrdering); | |
170 return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); | |
171 } | |
172 | |
173 PositionWithAffinity StartOfLine(const PositionWithAffinity& current_position) { | |
174 return StartOfLineAlgorithm<EditingStrategy>(current_position); | |
175 } | |
176 | |
177 PositionInFlatTreeWithAffinity StartOfLine( | |
178 const PositionInFlatTreeWithAffinity& current_position) { | |
179 return StartOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
180 } | |
181 | |
182 LayoutPoint AbsoluteLineDirectionPointToLocalPointInBlock( | |
183 RootInlineBox* root, | |
184 LayoutUnit line_direction_point) { | |
185 DCHECK(root); | |
186 LineLayoutBlockFlow containing_block = root->Block(); | |
187 FloatPoint absolute_block_point = | |
188 containing_block.LocalToAbsolute(FloatPoint()); | |
189 if (containing_block.HasOverflowClip()) | |
190 absolute_block_point -= FloatSize(containing_block.ScrolledContentOffset()); | |
191 | |
192 if (root->Block().IsHorizontalWritingMode()) { | |
193 return LayoutPoint( | |
194 LayoutUnit(line_direction_point - absolute_block_point.X()), | |
195 root->BlockDirectionPointInLine()); | |
196 } | |
197 | |
198 return LayoutPoint( | |
199 root->BlockDirectionPointInLine(), | |
200 LayoutUnit(line_direction_point - absolute_block_point.Y())); | |
201 } | |
202 | |
203 } // namespace | |
204 | |
205 // FIXME: consolidate with code in previousLinePosition. | |
206 Position PreviousRootInlineBoxCandidatePosition( | |
207 Node* node, | |
208 const VisiblePosition& visible_position, | |
209 EditableType editable_type) { | |
210 DCHECK(visible_position.IsValid()) << visible_position; | |
211 ContainerNode* highest_root = | |
212 HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); | |
213 Node* previous_node = PreviousLeafWithSameEditability(node, editable_type); | |
214 | |
215 while (previous_node && | |
216 (!previous_node->GetLayoutObject() || | |
217 InSameLine( | |
218 CreateVisiblePosition(FirstPositionInOrBeforeNode(previous_node)), | |
219 visible_position))) { | |
220 previous_node = | |
221 PreviousLeafWithSameEditability(previous_node, editable_type); | |
222 } | |
223 | |
224 while (previous_node && !previous_node->IsShadowRoot()) { | |
225 if (HighestEditableRoot(FirstPositionInOrBeforeNode(previous_node), | |
226 editable_type) != highest_root) | |
227 break; | |
228 | |
229 Position pos = isHTMLBRElement(*previous_node) | |
230 ? Position::BeforeNode(previous_node) | |
231 : Position::EditingPositionOf( | |
232 previous_node, CaretMaxOffset(previous_node)); | |
233 | |
234 if (IsVisuallyEquivalentCandidate(pos)) | |
235 return pos; | |
236 | |
237 previous_node = | |
238 PreviousLeafWithSameEditability(previous_node, editable_type); | |
239 } | |
240 return Position(); | |
241 } | |
242 | |
243 Position NextRootInlineBoxCandidatePosition( | |
244 Node* node, | |
245 const VisiblePosition& visible_position, | |
246 EditableType editable_type) { | |
247 DCHECK(visible_position.IsValid()) << visible_position; | |
248 ContainerNode* highest_root = | |
249 HighestEditableRoot(visible_position.DeepEquivalent(), editable_type); | |
250 Node* next_node = NextLeafWithSameEditability(node, editable_type); | |
251 while (next_node && (!next_node->GetLayoutObject() || | |
252 InSameLine(CreateVisiblePosition( | |
253 FirstPositionInOrBeforeNode(next_node)), | |
254 visible_position))) | |
255 next_node = NextLeafWithSameEditability(next_node, kContentIsEditable); | |
256 | |
257 while (next_node && !next_node->IsShadowRoot()) { | |
258 if (HighestEditableRoot(FirstPositionInOrBeforeNode(next_node), | |
259 editable_type) != highest_root) | |
260 break; | |
261 | |
262 Position pos; | |
263 pos = Position::EditingPositionOf(next_node, CaretMinOffset(next_node)); | |
264 | |
265 if (IsVisuallyEquivalentCandidate(pos)) | |
266 return pos; | |
267 | |
268 next_node = NextLeafWithSameEditability(next_node, editable_type); | |
269 } | |
270 return Position(); | |
271 } | |
272 | |
273 // FIXME: Rename this function to reflect the fact it ignores bidi levels. | |
274 VisiblePosition StartOfLine(const VisiblePosition& current_position) { | |
275 DCHECK(current_position.IsValid()) << current_position; | |
276 return CreateVisiblePosition( | |
277 StartOfLine(current_position.ToPositionWithAffinity())); | |
278 } | |
279 | |
280 VisiblePositionInFlatTree StartOfLine( | |
281 const VisiblePositionInFlatTree& current_position) { | |
282 DCHECK(current_position.IsValid()) << current_position; | |
283 return CreateVisiblePosition( | |
284 StartOfLine(current_position.ToPositionWithAffinity())); | |
285 } | |
286 | |
287 template <typename Strategy> | |
288 static PositionWithAffinityTemplate<Strategy> LogicalStartOfLineAlgorithm( | |
289 const PositionWithAffinityTemplate<Strategy>& c) { | |
290 // TODO: this is the current behavior that might need to be fixed. | |
291 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
292 PositionWithAffinityTemplate<Strategy> vis_pos = | |
293 StartPositionForLine(c, kUseLogicalOrdering); | |
294 | |
295 if (ContainerNode* editable_root = HighestEditableRoot(c.GetPosition())) { | |
296 if (!editable_root->contains( | |
297 vis_pos.GetPosition().ComputeContainerNode())) { | |
298 return PositionWithAffinityTemplate<Strategy>( | |
299 PositionTemplate<Strategy>::FirstPositionInNode(editable_root)); | |
300 } | |
301 } | |
302 | |
303 return HonorEditingBoundaryAtOrBefore(vis_pos, c.GetPosition()); | |
304 } | |
305 | |
306 VisiblePosition LogicalStartOfLine(const VisiblePosition& current_position) { | |
307 DCHECK(current_position.IsValid()) << current_position; | |
308 return CreateVisiblePosition(LogicalStartOfLineAlgorithm<EditingStrategy>( | |
309 current_position.ToPositionWithAffinity())); | |
310 } | |
311 | |
312 VisiblePositionInFlatTree LogicalStartOfLine( | |
313 const VisiblePositionInFlatTree& current_position) { | |
314 DCHECK(current_position.IsValid()) << current_position; | |
315 return CreateVisiblePosition( | |
316 LogicalStartOfLineAlgorithm<EditingInFlatTreeStrategy>( | |
317 current_position.ToPositionWithAffinity())); | |
318 } | |
319 | |
320 template <typename Strategy> | |
321 static VisiblePositionTemplate<Strategy> EndPositionForLine( | |
322 const VisiblePositionTemplate<Strategy>& c, | |
323 LineEndpointComputationMode mode) { | |
324 DCHECK(c.IsValid()) << c; | |
325 if (c.IsNull()) | |
326 return VisiblePositionTemplate<Strategy>(); | |
327 | |
328 RootInlineBox* root_box = RenderedPosition(c).RootBox(); | |
329 if (!root_box) { | |
330 // There are VisiblePositions at offset 0 in blocks without | |
331 // RootInlineBoxes, like empty editable blocks and bordered blocks. | |
332 const PositionTemplate<Strategy> p = c.DeepEquivalent(); | |
333 if (p.AnchorNode()->GetLayoutObject() && | |
334 p.AnchorNode()->GetLayoutObject()->IsLayoutBlock() && | |
335 !p.ComputeEditingOffset()) | |
336 return c; | |
337 return VisiblePositionTemplate<Strategy>(); | |
338 } | |
339 | |
340 Node* end_node; | |
341 InlineBox* end_box; | |
342 if (mode == kUseLogicalOrdering) { | |
343 end_node = root_box->GetLogicalEndBoxWithNode(end_box); | |
344 if (!end_node) | |
345 return VisiblePositionTemplate<Strategy>(); | |
346 } else { | |
347 // Generated content (e.g. list markers and CSS :before and :after | |
348 // pseudo elements) have no corresponding DOM element, and so cannot be | |
349 // represented by a VisiblePosition. Use whatever precedes instead. | |
350 end_box = root_box->LastLeafChild(); | |
351 while (true) { | |
352 if (!end_box) | |
353 return VisiblePositionTemplate<Strategy>(); | |
354 | |
355 end_node = end_box->GetLineLayoutItem().NonPseudoNode(); | |
356 if (end_node) | |
357 break; | |
358 | |
359 end_box = end_box->PrevLeafChild(); | |
360 } | |
361 } | |
362 | |
363 PositionTemplate<Strategy> pos; | |
364 if (isHTMLBRElement(*end_node)) { | |
365 pos = PositionTemplate<Strategy>::BeforeNode(end_node); | |
366 } else if (end_box->IsInlineTextBox() && end_node->IsTextNode()) { | |
367 InlineTextBox* end_text_box = ToInlineTextBox(end_box); | |
368 int end_offset = end_text_box->Start(); | |
369 if (!end_text_box->IsLineBreak()) | |
370 end_offset += end_text_box->Len(); | |
371 pos = PositionTemplate<Strategy>(ToText(end_node), end_offset); | |
372 } else { | |
373 pos = PositionTemplate<Strategy>::AfterNode(end_node); | |
374 } | |
375 | |
376 return CreateVisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); | |
377 } | |
378 | |
379 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. | |
380 template <typename Strategy> | |
381 static VisiblePositionTemplate<Strategy> EndOfLineAlgorithm( | |
382 const VisiblePositionTemplate<Strategy>& current_position) { | |
383 DCHECK(current_position.IsValid()) << current_position; | |
384 // TODO(yosin) this is the current behavior that might need to be fixed. | |
385 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
386 VisiblePositionTemplate<Strategy> vis_pos = | |
387 EndPositionForLine(current_position, kUseInlineBoxOrdering); | |
388 | |
389 // Make sure the end of line is at the same line as the given input | |
390 // position. Else use the previous position to obtain end of line. This | |
391 // condition happens when the input position is before the space character | |
392 // at the end of a soft-wrapped non-editable line. In this scenario, | |
393 // |endPositionForLine()| would incorrectly hand back a position in the next | |
394 // line instead. This fix is to account for the discrepancy between lines | |
395 // with "webkit-line-break:after-white-space" style versus lines without | |
396 // that style, which would break before a space by default. | |
397 if (!InSameLine(current_position, vis_pos)) { | |
398 vis_pos = PreviousPositionOf(current_position); | |
399 if (vis_pos.IsNull()) | |
400 return VisiblePositionTemplate<Strategy>(); | |
401 vis_pos = EndPositionForLine(vis_pos, kUseInlineBoxOrdering); | |
402 } | |
403 | |
404 return HonorEditingBoundaryAtOrAfter(vis_pos, | |
405 current_position.DeepEquivalent()); | |
406 } | |
407 | |
408 // TODO(yosin) Rename this function to reflect the fact it ignores bidi levels. | |
409 VisiblePosition EndOfLine(const VisiblePosition& current_position) { | |
410 return EndOfLineAlgorithm<EditingStrategy>(current_position); | |
411 } | |
412 | |
413 VisiblePositionInFlatTree EndOfLine( | |
414 const VisiblePositionInFlatTree& current_position) { | |
415 return EndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
416 } | |
417 | |
418 template <typename Strategy> | |
419 static bool InSameLogicalLine(const VisiblePositionTemplate<Strategy>& a, | |
420 const VisiblePositionTemplate<Strategy>& b) { | |
421 DCHECK(a.IsValid()) << a; | |
422 DCHECK(b.IsValid()) << b; | |
423 return a.IsNotNull() && LogicalStartOfLine(a).DeepEquivalent() == | |
424 LogicalStartOfLine(b).DeepEquivalent(); | |
425 } | |
426 | |
427 template <typename Strategy> | |
428 static VisiblePositionTemplate<Strategy> LogicalEndOfLineAlgorithm( | |
429 const VisiblePositionTemplate<Strategy>& current_position) { | |
430 DCHECK(current_position.IsValid()) << current_position; | |
431 // TODO(yosin) this is the current behavior that might need to be fixed. | |
432 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail. | |
433 VisiblePositionTemplate<Strategy> vis_pos = | |
434 EndPositionForLine(current_position, kUseLogicalOrdering); | |
435 | |
436 // Make sure the end of line is at the same line as the given input | |
437 // position. For a wrapping line, the logical end position for the | |
438 // not-last-2-lines might incorrectly hand back the logical beginning of the | |
439 // next line. For example, | |
440 // <div contenteditable dir="rtl" style="line-break:before-white-space">xyz | |
441 // a xyz xyz xyz xyz xyz xyz xyz xyz xyz xyz </div> | |
442 // In this case, use the previous position of the computed logical end | |
443 // position. | |
444 if (!InSameLogicalLine(current_position, vis_pos)) | |
445 vis_pos = PreviousPositionOf(vis_pos); | |
446 | |
447 if (ContainerNode* editable_root = | |
448 HighestEditableRoot(current_position.DeepEquivalent())) { | |
449 if (!editable_root->contains( | |
450 vis_pos.DeepEquivalent().ComputeContainerNode())) { | |
451 return CreateVisiblePosition( | |
452 PositionTemplate<Strategy>::LastPositionInNode(editable_root)); | |
453 } | |
454 } | |
455 | |
456 return HonorEditingBoundaryAtOrAfter(vis_pos, | |
457 current_position.DeepEquivalent()); | |
458 } | |
459 | |
460 VisiblePosition LogicalEndOfLine(const VisiblePosition& current_position) { | |
461 return LogicalEndOfLineAlgorithm<EditingStrategy>(current_position); | |
462 } | |
463 | |
464 VisiblePositionInFlatTree LogicalEndOfLine( | |
465 const VisiblePositionInFlatTree& current_position) { | |
466 return LogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(current_position); | |
467 } | |
468 | |
469 template <typename Strategy> | |
470 static bool InSameLineAlgorithm( | |
471 const PositionWithAffinityTemplate<Strategy>& position1, | |
472 const PositionWithAffinityTemplate<Strategy>& position2) { | |
473 if (position1.IsNull() || position2.IsNull()) | |
474 return false; | |
475 DCHECK_EQ(position1.GetDocument(), position2.GetDocument()); | |
476 DCHECK(!position1.GetDocument()->NeedsLayoutTreeUpdate()); | |
477 | |
478 PositionWithAffinityTemplate<Strategy> start_of_line1 = | |
479 StartOfLine(position1); | |
480 PositionWithAffinityTemplate<Strategy> start_of_line2 = | |
481 StartOfLine(position2); | |
482 if (start_of_line1 == start_of_line2) | |
483 return true; | |
484 PositionTemplate<Strategy> canonicalized1 = | |
485 CanonicalPositionOf(start_of_line1.GetPosition()); | |
486 if (canonicalized1 == start_of_line2.GetPosition()) | |
487 return true; | |
488 return canonicalized1 == CanonicalPositionOf(start_of_line2.GetPosition()); | |
489 } | |
490 | |
491 bool InSameLine(const PositionWithAffinity& a, const PositionWithAffinity& b) { | |
492 return InSameLineAlgorithm<EditingStrategy>(a, b); | |
493 } | |
494 | |
495 bool InSameLine(const PositionInFlatTreeWithAffinity& position1, | |
496 const PositionInFlatTreeWithAffinity& position2) { | |
497 return InSameLineAlgorithm<EditingInFlatTreeStrategy>(position1, position2); | |
498 } | |
499 | |
500 bool InSameLine(const VisiblePosition& position1, | |
501 const VisiblePosition& position2) { | |
502 DCHECK(position1.IsValid()) << position1; | |
503 DCHECK(position2.IsValid()) << position2; | |
504 return InSameLine(position1.ToPositionWithAffinity(), | |
505 position2.ToPositionWithAffinity()); | |
506 } | |
507 | |
508 bool InSameLine(const VisiblePositionInFlatTree& position1, | |
509 const VisiblePositionInFlatTree& position2) { | |
510 DCHECK(position1.IsValid()) << position1; | |
511 DCHECK(position2.IsValid()) << position2; | |
512 return InSameLine(position1.ToPositionWithAffinity(), | |
513 position2.ToPositionWithAffinity()); | |
514 } | |
515 | |
516 template <typename Strategy> | |
517 static bool IsStartOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { | |
518 DCHECK(p.IsValid()) << p; | |
519 return p.IsNotNull() && p.DeepEquivalent() == StartOfLine(p).DeepEquivalent(); | |
520 } | |
521 | |
522 bool IsStartOfLine(const VisiblePosition& p) { | |
523 return IsStartOfLineAlgorithm<EditingStrategy>(p); | |
524 } | |
525 | |
526 bool IsStartOfLine(const VisiblePositionInFlatTree& p) { | |
527 return IsStartOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
528 } | |
529 | |
530 template <typename Strategy> | |
531 static bool IsEndOfLineAlgorithm(const VisiblePositionTemplate<Strategy>& p) { | |
532 DCHECK(p.IsValid()) << p; | |
533 return p.IsNotNull() && p.DeepEquivalent() == EndOfLine(p).DeepEquivalent(); | |
534 } | |
535 | |
536 bool IsEndOfLine(const VisiblePosition& p) { | |
537 return IsEndOfLineAlgorithm<EditingStrategy>(p); | |
538 } | |
539 | |
540 bool IsEndOfLine(const VisiblePositionInFlatTree& p) { | |
541 return IsEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
542 } | |
543 | |
544 template <typename Strategy> | |
545 static bool IsLogicalEndOfLineAlgorithm( | |
546 const VisiblePositionTemplate<Strategy>& p) { | |
547 DCHECK(p.IsValid()) << p; | |
548 return p.IsNotNull() && | |
549 p.DeepEquivalent() == LogicalEndOfLine(p).DeepEquivalent(); | |
550 } | |
551 | |
552 bool IsLogicalEndOfLine(const VisiblePosition& p) { | |
553 return IsLogicalEndOfLineAlgorithm<EditingStrategy>(p); | |
554 } | |
555 | |
556 bool IsLogicalEndOfLine(const VisiblePositionInFlatTree& p) { | |
557 return IsLogicalEndOfLineAlgorithm<EditingInFlatTreeStrategy>(p); | |
558 } | |
559 | |
560 VisiblePosition PreviousLinePosition(const VisiblePosition& visible_position, | |
561 LayoutUnit line_direction_point, | |
562 EditableType editable_type) { | |
563 DCHECK(visible_position.IsValid()) << visible_position; | |
564 | |
565 Position p = visible_position.DeepEquivalent(); | |
566 Node* node = p.AnchorNode(); | |
567 | |
568 if (!node) | |
569 return VisiblePosition(); | |
570 | |
571 LayoutObject* layout_object = node->GetLayoutObject(); | |
572 if (!layout_object) | |
573 return VisiblePosition(); | |
574 | |
575 RootInlineBox* root = nullptr; | |
576 InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; | |
577 if (box) { | |
578 root = box->Root().PrevRootBox(); | |
579 // We want to skip zero height boxes. | |
580 // This could happen in case it is a TrailingFloatsRootInlineBox. | |
581 if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) | |
582 root = nullptr; | |
583 } | |
584 | |
585 if (!root) { | |
586 Position position = PreviousRootInlineBoxCandidatePosition( | |
587 node, visible_position, editable_type); | |
588 if (position.IsNotNull()) { | |
589 RenderedPosition rendered_position((CreateVisiblePosition(position))); | |
590 root = rendered_position.RootBox(); | |
591 if (!root) | |
592 return CreateVisiblePosition(position); | |
593 } | |
594 } | |
595 | |
596 if (root) { | |
597 // FIXME: Can be wrong for multi-column layout and with transforms. | |
598 LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( | |
599 root, line_direction_point); | |
600 LineLayoutItem line_layout_item = | |
601 root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) | |
602 ->GetLineLayoutItem(); | |
603 Node* node = line_layout_item.GetNode(); | |
604 if (node && EditingIgnoresContent(*node)) | |
605 return VisiblePosition::InParentBeforeNode(*node); | |
606 return CreateVisiblePosition( | |
607 line_layout_item.PositionForPoint(point_in_line)); | |
608 } | |
609 | |
610 // Could not find a previous line. This means we must already be on the first | |
611 // line. Move to the start of the content in this block, which effectively | |
612 // moves us to the start of the line we're on. | |
613 Element* root_element = HasEditableStyle(*node, editable_type) | |
614 ? RootEditableElement(*node, editable_type) | |
615 : node->GetDocument().documentElement(); | |
616 if (!root_element) | |
617 return VisiblePosition(); | |
618 return VisiblePosition::FirstPositionInNode(root_element); | |
619 } | |
620 | |
621 VisiblePosition NextLinePosition(const VisiblePosition& visible_position, | |
622 LayoutUnit line_direction_point, | |
623 EditableType editable_type) { | |
624 DCHECK(visible_position.IsValid()) << visible_position; | |
625 | |
626 Position p = visible_position.DeepEquivalent(); | |
627 Node* node = p.AnchorNode(); | |
628 | |
629 if (!node) | |
630 return VisiblePosition(); | |
631 | |
632 LayoutObject* layout_object = node->GetLayoutObject(); | |
633 if (!layout_object) | |
634 return VisiblePosition(); | |
635 | |
636 RootInlineBox* root = nullptr; | |
637 InlineBox* box = ComputeInlineBoxPosition(visible_position).inline_box; | |
638 if (box) { | |
639 root = box->Root().NextRootBox(); | |
640 // We want to skip zero height boxes. | |
641 // This could happen in case it is a TrailingFloatsRootInlineBox. | |
642 if (!root || !root->LogicalHeight() || !root->FirstLeafChild()) | |
643 root = nullptr; | |
644 } | |
645 | |
646 if (!root) { | |
647 // FIXME: We need do the same in previousLinePosition. | |
648 Node* child = NodeTraversal::ChildAt(*node, p.ComputeEditingOffset()); | |
649 node = child ? child : &NodeTraversal::LastWithinOrSelf(*node); | |
650 Position position = NextRootInlineBoxCandidatePosition( | |
651 node, visible_position, editable_type); | |
652 if (position.IsNotNull()) { | |
653 RenderedPosition rendered_position((CreateVisiblePosition(position))); | |
654 root = rendered_position.RootBox(); | |
655 if (!root) | |
656 return CreateVisiblePosition(position); | |
657 } | |
658 } | |
659 | |
660 if (root) { | |
661 // FIXME: Can be wrong for multi-column layout and with transforms. | |
662 LayoutPoint point_in_line = AbsoluteLineDirectionPointToLocalPointInBlock( | |
663 root, line_direction_point); | |
664 LineLayoutItem line_layout_item = | |
665 root->ClosestLeafChildForPoint(point_in_line, IsEditablePosition(p)) | |
666 ->GetLineLayoutItem(); | |
667 Node* node = line_layout_item.GetNode(); | |
668 if (node && EditingIgnoresContent(*node)) | |
669 return VisiblePosition::InParentBeforeNode(*node); | |
670 return CreateVisiblePosition( | |
671 line_layout_item.PositionForPoint(point_in_line)); | |
672 } | |
673 | |
674 // Could not find a next line. This means we must already be on the last line. | |
675 // Move to the end of the content in this block, which effectively moves us | |
676 // to the end of the line we're on. | |
677 Element* root_element = HasEditableStyle(*node, editable_type) | |
678 ? RootEditableElement(*node, editable_type) | |
679 : node->GetDocument().documentElement(); | |
680 if (!root_element) | |
681 return VisiblePosition(); | |
682 return VisiblePosition::LastPositionInNode(root_element); | |
683 } | |
684 | |
685 } // namespace blink | |
OLD | NEW |