1 | /* |
2 | * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
3 | * Portions Copyright (c) 2011 Motorola Mobility, Inc. All rights 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 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 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 "config.h" |
28 | #include "VisiblePosition.h" |
29 | |
30 | #include "Document.h" |
31 | #include "Editing.h" |
32 | #include "FloatQuad.h" |
33 | #include "HTMLElement.h" |
34 | #include "HTMLHtmlElement.h" |
35 | #include "HTMLNames.h" |
36 | #include "InlineTextBox.h" |
37 | #include "Logging.h" |
38 | #include "Range.h" |
39 | #include "RenderBlock.h" |
40 | #include "RootInlineBox.h" |
41 | #include "Text.h" |
42 | #include "VisibleUnits.h" |
43 | #include <stdio.h> |
44 | #include <wtf/text/CString.h> |
45 | #include <wtf/text/TextStream.h> |
46 | |
47 | namespace WebCore { |
48 | |
49 | using namespace HTMLNames; |
50 | |
51 | VisiblePosition::VisiblePosition(const Position &pos, EAffinity affinity) |
52 | { |
53 | init(pos, affinity); |
54 | } |
55 | |
56 | void VisiblePosition::init(const Position& position, EAffinity affinity) |
57 | { |
58 | m_affinity = affinity; |
59 | |
60 | m_deepPosition = canonicalPosition(position); |
61 | |
62 | // When not at a line wrap, make sure to end up with DOWNSTREAM affinity. |
63 | if (m_affinity == UPSTREAM && (isNull() || inSameLine(VisiblePosition(position, DOWNSTREAM), *this))) |
64 | m_affinity = DOWNSTREAM; |
65 | } |
66 | |
67 | VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule, bool* reachedBoundary) const |
68 | { |
69 | if (reachedBoundary) |
70 | *reachedBoundary = false; |
71 | // FIXME: Support CanSkipEditingBoundary |
72 | ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary); |
73 | VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity); |
74 | |
75 | if (rule == CanCrossEditingBoundary) |
76 | return next; |
77 | |
78 | return honorEditingBoundaryAtOrAfter(next, reachedBoundary); |
79 | } |
80 | |
81 | VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule, bool* reachedBoundary) const |
82 | { |
83 | if (reachedBoundary) |
84 | *reachedBoundary = false; |
85 | // FIXME: Support CanSkipEditingBoundary |
86 | ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary); |
87 | // find first previous DOM position that is visible |
88 | Position pos = previousVisuallyDistinctCandidate(m_deepPosition); |
89 | |
90 | // return null visible position if there is no previous visible position |
91 | if (pos.atStartOfTree()) { |
92 | if (reachedBoundary) |
93 | *reachedBoundary = true; |
94 | return VisiblePosition(); |
95 | } |
96 | |
97 | VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM); |
98 | ASSERT(prev != *this); |
99 | |
100 | #ifndef NDEBUG |
101 | // we should always be able to make the affinity DOWNSTREAM, because going previous from an |
102 | // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!). |
103 | if (prev.isNotNull() && m_affinity == UPSTREAM) { |
104 | VisiblePosition temp = prev; |
105 | temp.setAffinity(UPSTREAM); |
106 | ASSERT(inSameLine(temp, prev)); |
107 | } |
108 | #endif |
109 | |
110 | if (rule == CanCrossEditingBoundary) |
111 | return prev; |
112 | |
113 | return honorEditingBoundaryAtOrBefore(prev, reachedBoundary); |
114 | } |
115 | |
116 | Position VisiblePosition::leftVisuallyDistinctCandidate() const |
117 | { |
118 | Position p = m_deepPosition; |
119 | if (p.isNull()) |
120 | return Position(); |
121 | |
122 | Position downstreamStart = p.downstream(); |
123 | TextDirection primaryDirection = p.primaryDirection(); |
124 | |
125 | while (true) { |
126 | InlineBox* box; |
127 | int offset; |
128 | p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); |
129 | if (!box) |
130 | return primaryDirection == TextDirection::LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); |
131 | |
132 | RenderObject* renderer = &box->renderer(); |
133 | |
134 | while (true) { |
135 | if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretRightmostOffset()) |
136 | return box->isLeftToRightDirection() ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); |
137 | |
138 | if (!renderer->node()) { |
139 | box = box->prevLeafChild(); |
140 | if (!box) |
141 | return primaryDirection == TextDirection::LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); |
142 | renderer = &box->renderer(); |
143 | offset = box->caretRightmostOffset(); |
144 | continue; |
145 | } |
146 | |
147 | offset = box->isLeftToRightDirection() ? renderer->previousOffset(offset) : renderer->nextOffset(offset); |
148 | |
149 | int caretMinOffset = box->caretMinOffset(); |
150 | int caretMaxOffset = box->caretMaxOffset(); |
151 | |
152 | if (offset > caretMinOffset && offset < caretMaxOffset) |
153 | break; |
154 | |
155 | if (box->isLeftToRightDirection() ? offset < caretMinOffset : offset > caretMaxOffset) { |
156 | // Overshot to the left. |
157 | InlineBox* prevBox = box->prevLeafChildIgnoringLineBreak(); |
158 | if (!prevBox) { |
159 | Position positionOnLeft = primaryDirection == TextDirection::LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); |
160 | if (positionOnLeft.isNull()) |
161 | return Position(); |
162 | |
163 | InlineBox* boxOnLeft; |
164 | int offsetOnLeft; |
165 | positionOnLeft.getInlineBoxAndOffset(m_affinity, primaryDirection, boxOnLeft, offsetOnLeft); |
166 | if (boxOnLeft && &boxOnLeft->root() == &box->root()) |
167 | return Position(); |
168 | return positionOnLeft; |
169 | } |
170 | |
171 | // Reposition at the other logical position corresponding to our edge's visual position and go for another round. |
172 | box = prevBox; |
173 | renderer = &box->renderer(); |
174 | offset = prevBox->caretRightmostOffset(); |
175 | continue; |
176 | } |
177 | |
178 | ASSERT(offset == box->caretLeftmostOffset()); |
179 | |
180 | unsigned char level = box->bidiLevel(); |
181 | InlineBox* prevBox = box->prevLeafChild(); |
182 | |
183 | if (box->direction() == primaryDirection) { |
184 | if (!prevBox) { |
185 | InlineBox* logicalStart = nullptr; |
186 | if (primaryDirection == TextDirection::LTR ? box->root().getLogicalStartBoxWithNode(logicalStart) : box->root().getLogicalEndBoxWithNode(logicalStart)) { |
187 | box = logicalStart; |
188 | renderer = &box->renderer(); |
189 | offset = primaryDirection == TextDirection::LTR ? box->caretMinOffset() : box->caretMaxOffset(); |
190 | } |
191 | break; |
192 | } |
193 | if (prevBox->bidiLevel() >= level) |
194 | break; |
195 | |
196 | level = prevBox->bidiLevel(); |
197 | |
198 | InlineBox* nextBox = box; |
199 | do { |
200 | nextBox = nextBox->nextLeafChild(); |
201 | } while (nextBox && nextBox->bidiLevel() > level); |
202 | |
203 | if (nextBox && nextBox->bidiLevel() == level) |
204 | break; |
205 | |
206 | box = prevBox; |
207 | renderer = &box->renderer(); |
208 | offset = box->caretRightmostOffset(); |
209 | if (box->direction() == primaryDirection) |
210 | break; |
211 | continue; |
212 | } |
213 | |
214 | while (prevBox && !prevBox->renderer().node()) |
215 | prevBox = prevBox->prevLeafChild(); |
216 | |
217 | if (prevBox) { |
218 | box = prevBox; |
219 | renderer = &box->renderer(); |
220 | offset = box->caretRightmostOffset(); |
221 | if (box->bidiLevel() > level) { |
222 | do { |
223 | prevBox = prevBox->prevLeafChild(); |
224 | } while (prevBox && prevBox->bidiLevel() > level); |
225 | |
226 | if (!prevBox || prevBox->bidiLevel() < level) |
227 | continue; |
228 | } |
229 | } else { |
230 | // Trailing edge of a secondary run. Set to the leading edge of the entire run. |
231 | while (true) { |
232 | while (InlineBox* nextBox = box->nextLeafChild()) { |
233 | if (nextBox->bidiLevel() < level) |
234 | break; |
235 | box = nextBox; |
236 | } |
237 | if (box->bidiLevel() == level) |
238 | break; |
239 | level = box->bidiLevel(); |
240 | while (InlineBox* prevBox = box->prevLeafChild()) { |
241 | if (prevBox->bidiLevel() < level) |
242 | break; |
243 | box = prevBox; |
244 | } |
245 | if (box->bidiLevel() == level) |
246 | break; |
247 | level = box->bidiLevel(); |
248 | } |
249 | renderer = &box->renderer(); |
250 | offset = primaryDirection == TextDirection::LTR ? box->caretMinOffset() : box->caretMaxOffset(); |
251 | } |
252 | break; |
253 | } |
254 | |
255 | p = createLegacyEditingPosition(renderer->node(), offset); |
256 | |
257 | if ((p.isCandidate() && p.downstream() != downstreamStart) || p.atStartOfTree() || p.atEndOfTree()) |
258 | return p; |
259 | |
260 | ASSERT(p != m_deepPosition); |
261 | } |
262 | } |
263 | |
264 | VisiblePosition VisiblePosition::left(bool stayInEditableContent, bool* reachedBoundary) const |
265 | { |
266 | if (reachedBoundary) |
267 | *reachedBoundary = false; |
268 | Position pos = leftVisuallyDistinctCandidate(); |
269 | // FIXME: Why can't we move left from the last position in a tree? |
270 | if (pos.atStartOfTree() || pos.atEndOfTree()) { |
271 | if (reachedBoundary) |
272 | *reachedBoundary = true; |
273 | return VisiblePosition(); |
274 | } |
275 | |
276 | VisiblePosition left = VisiblePosition(pos, DOWNSTREAM); |
277 | ASSERT(left != *this); |
278 | |
279 | if (!stayInEditableContent) |
280 | return left; |
281 | |
282 | // FIXME: This may need to do something different from "before". |
283 | return honorEditingBoundaryAtOrBefore(left, reachedBoundary); |
284 | } |
285 | |
286 | Position VisiblePosition::rightVisuallyDistinctCandidate() const |
287 | { |
288 | Position p = m_deepPosition; |
289 | if (p.isNull()) |
290 | return Position(); |
291 | |
292 | Position downstreamStart = p.downstream(); |
293 | TextDirection primaryDirection = p.primaryDirection(); |
294 | |
295 | while (true) { |
296 | InlineBox* box; |
297 | int offset; |
298 | p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); |
299 | if (!box) |
300 | return primaryDirection == TextDirection::LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); |
301 | |
302 | RenderObject* renderer = &box->renderer(); |
303 | |
304 | while (true) { |
305 | if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretLeftmostOffset()) |
306 | return box->isLeftToRightDirection() ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); |
307 | |
308 | if (!renderer->node()) { |
309 | box = box->nextLeafChild(); |
310 | if (!box) |
311 | return primaryDirection == TextDirection::LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); |
312 | renderer = &box->renderer(); |
313 | offset = box->caretLeftmostOffset(); |
314 | continue; |
315 | } |
316 | |
317 | offset = box->isLeftToRightDirection() ? renderer->nextOffset(offset) : renderer->previousOffset(offset); |
318 | |
319 | int caretMinOffset = box->caretMinOffset(); |
320 | int caretMaxOffset = box->caretMaxOffset(); |
321 | |
322 | if (offset > caretMinOffset && offset < caretMaxOffset) |
323 | break; |
324 | |
325 | if (box->isLeftToRightDirection() ? offset > caretMaxOffset : offset < caretMinOffset) { |
326 | // Overshot to the right. |
327 | InlineBox* nextBox = box->nextLeafChildIgnoringLineBreak(); |
328 | if (!nextBox) { |
329 | Position positionOnRight = primaryDirection == TextDirection::LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); |
330 | if (positionOnRight.isNull()) |
331 | return Position(); |
332 | |
333 | InlineBox* boxOnRight; |
334 | int offsetOnRight; |
335 | positionOnRight.getInlineBoxAndOffset(m_affinity, primaryDirection, boxOnRight, offsetOnRight); |
336 | if (boxOnRight && &boxOnRight->root() == &box->root()) |
337 | return Position(); |
338 | return positionOnRight; |
339 | } |
340 | |
341 | // Reposition at the other logical position corresponding to our edge's visual position and go for another round. |
342 | box = nextBox; |
343 | renderer = &box->renderer(); |
344 | offset = nextBox->caretLeftmostOffset(); |
345 | continue; |
346 | } |
347 | |
348 | ASSERT(offset == box->caretRightmostOffset()); |
349 | |
350 | unsigned char level = box->bidiLevel(); |
351 | InlineBox* nextBox = box->nextLeafChild(); |
352 | |
353 | if (box->direction() == primaryDirection) { |
354 | if (!nextBox) { |
355 | InlineBox* logicalEnd = nullptr; |
356 | if (primaryDirection == TextDirection::LTR ? box->root().getLogicalEndBoxWithNode(logicalEnd) : box->root().getLogicalStartBoxWithNode(logicalEnd)) { |
357 | box = logicalEnd; |
358 | renderer = &box->renderer(); |
359 | offset = primaryDirection == TextDirection::LTR ? box->caretMaxOffset() : box->caretMinOffset(); |
360 | } |
361 | break; |
362 | } |
363 | |
364 | if (nextBox->bidiLevel() >= level) |
365 | break; |
366 | |
367 | level = nextBox->bidiLevel(); |
368 | |
369 | InlineBox* prevBox = box; |
370 | do { |
371 | prevBox = prevBox->prevLeafChild(); |
372 | } while (prevBox && prevBox->bidiLevel() > level); |
373 | |
374 | if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA |
375 | break; |
376 | |
377 | // For example, abc 123 ^ CBA or 123 ^ CBA abc |
378 | box = nextBox; |
379 | renderer = &box->renderer(); |
380 | offset = box->caretLeftmostOffset(); |
381 | if (box->direction() == primaryDirection) |
382 | break; |
383 | continue; |
384 | } |
385 | |
386 | while (nextBox && !nextBox->renderer().node()) |
387 | nextBox = nextBox->nextLeafChild(); |
388 | |
389 | if (nextBox) { |
390 | box = nextBox; |
391 | renderer = &box->renderer(); |
392 | offset = box->caretLeftmostOffset(); |
393 | |
394 | if (box->bidiLevel() > level) { |
395 | do { |
396 | nextBox = nextBox->nextLeafChild(); |
397 | } while (nextBox && nextBox->bidiLevel() > level); |
398 | |
399 | if (!nextBox || nextBox->bidiLevel() < level) |
400 | continue; |
401 | } |
402 | } else { |
403 | // Trailing edge of a secondary run. Set to the leading edge of the entire run. |
404 | while (true) { |
405 | while (InlineBox* prevBox = box->prevLeafChild()) { |
406 | if (prevBox->bidiLevel() < level) |
407 | break; |
408 | box = prevBox; |
409 | } |
410 | if (box->bidiLevel() == level) |
411 | break; |
412 | level = box->bidiLevel(); |
413 | while (InlineBox* nextBox = box->nextLeafChild()) { |
414 | if (nextBox->bidiLevel() < level) |
415 | break; |
416 | box = nextBox; |
417 | } |
418 | if (box->bidiLevel() == level) |
419 | break; |
420 | level = box->bidiLevel(); |
421 | } |
422 | renderer = &box->renderer(); |
423 | offset = primaryDirection == TextDirection::LTR ? box->caretMaxOffset() : box->caretMinOffset(); |
424 | } |
425 | break; |
426 | } |
427 | |
428 | p = createLegacyEditingPosition(renderer->node(), offset); |
429 | |
430 | if ((p.isCandidate() && p.downstream() != downstreamStart) || p.atStartOfTree() || p.atEndOfTree()) |
431 | return p; |
432 | |
433 | ASSERT(p != m_deepPosition); |
434 | } |
435 | } |
436 | |
437 | VisiblePosition VisiblePosition::right(bool stayInEditableContent, bool* reachedBoundary) const |
438 | { |
439 | if (reachedBoundary) |
440 | *reachedBoundary = false; |
441 | Position pos = rightVisuallyDistinctCandidate(); |
442 | // FIXME: Why can't we move left from the last position in a tree? |
443 | if (pos.atStartOfTree() || pos.atEndOfTree()) { |
444 | if (reachedBoundary) |
445 | *reachedBoundary = true; |
446 | return VisiblePosition(); |
447 | } |
448 | |
449 | VisiblePosition right = VisiblePosition(pos, DOWNSTREAM); |
450 | ASSERT(right != *this); |
451 | |
452 | if (!stayInEditableContent) |
453 | return right; |
454 | |
455 | // FIXME: This may need to do something different from "after". |
456 | return honorEditingBoundaryAtOrAfter(right, reachedBoundary); |
457 | } |
458 | |
459 | VisiblePosition VisiblePosition::honorEditingBoundaryAtOrBefore(const VisiblePosition& position, bool* reachedBoundary) const |
460 | { |
461 | if (reachedBoundary) |
462 | *reachedBoundary = false; |
463 | if (position.isNull()) |
464 | return position; |
465 | |
466 | auto* highestRoot = highestEditableRoot(deepEquivalent()); |
467 | |
468 | // Return empty position if pos is not somewhere inside the editable region containing this position |
469 | if (highestRoot && !position.deepEquivalent().deprecatedNode()->isDescendantOf(*highestRoot)) { |
470 | if (reachedBoundary) |
471 | *reachedBoundary = true; |
472 | return VisiblePosition(); |
473 | } |
474 | |
475 | // Return position itself if the two are from the very same editable region, or both are non-editable |
476 | // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement |
477 | // to it is allowed. VisibleSelection::adjustForEditableContent has this problem too. |
478 | if (highestEditableRoot(position.deepEquivalent()) == highestRoot) { |
479 | if (reachedBoundary) |
480 | *reachedBoundary = *this == position; |
481 | return position; |
482 | } |
483 | |
484 | // Return empty position if this position is non-editable, but pos is editable |
485 | // FIXME: Move to the previous non-editable region. |
486 | if (!highestRoot) { |
487 | if (reachedBoundary) |
488 | *reachedBoundary = true; |
489 | return VisiblePosition(); |
490 | } |
491 | |
492 | // Return the last position before pos that is in the same editable region as this position |
493 | return lastEditablePositionBeforePositionInRoot(position.deepEquivalent(), highestRoot); |
494 | } |
495 | |
496 | VisiblePosition VisiblePosition::honorEditingBoundaryAtOrAfter(const VisiblePosition &pos, bool* reachedBoundary) const |
497 | { |
498 | if (reachedBoundary) |
499 | *reachedBoundary = false; |
500 | if (pos.isNull()) |
501 | return pos; |
502 | |
503 | auto* highestRoot = highestEditableRoot(deepEquivalent()); |
504 | |
505 | // Return empty position if pos is not somewhere inside the editable region containing this position |
506 | if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(*highestRoot)) { |
507 | if (reachedBoundary) |
508 | *reachedBoundary = true; |
509 | return VisiblePosition(); |
510 | } |
511 | |
512 | // Return pos itself if the two are from the very same editable region, or both are non-editable |
513 | // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement |
514 | // to it is allowed. VisibleSelection::adjustForEditableContent has this problem too. |
515 | if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) { |
516 | if (reachedBoundary) |
517 | *reachedBoundary = *this == pos; |
518 | return pos; |
519 | } |
520 | |
521 | // Return empty position if this position is non-editable, but pos is editable |
522 | // FIXME: Move to the next non-editable region. |
523 | if (!highestRoot) { |
524 | if (reachedBoundary) |
525 | *reachedBoundary = true; |
526 | return VisiblePosition(); |
527 | } |
528 | |
529 | // Return the next position after pos that is in the same editable region as this position |
530 | return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot); |
531 | } |
532 | |
533 | static Position canonicalizeCandidate(const Position& candidate) |
534 | { |
535 | if (candidate.isNull()) |
536 | return Position(); |
537 | ASSERT(candidate.isCandidate()); |
538 | Position upstream = candidate.upstream(); |
539 | if (upstream.isCandidate()) |
540 | return upstream; |
541 | return candidate; |
542 | } |
543 | |
544 | Position VisiblePosition::canonicalPosition(const Position& passedPosition) |
545 | { |
546 | // The updateLayout call below can do so much that even the position passed |
547 | // in to us might get changed as a side effect. Specifically, there are code |
548 | // paths that pass selection endpoints, and updateLayout can change the selection. |
549 | Position position = passedPosition; |
550 | |
551 | // FIXME (9535): Canonicalizing to the leftmost candidate means that if we're at a line wrap, we will |
552 | // ask renderers to paint downstream carets for other renderers. |
553 | // To fix this, we need to either a) add code to all paintCarets to pass the responsibility off to |
554 | // the appropriate renderer for VisiblePosition's like these, or b) canonicalize to the rightmost candidate |
555 | // unless the affinity is upstream. |
556 | if (position.isNull()) |
557 | return Position(); |
558 | |
559 | ASSERT(position.document()); |
560 | position.document()->updateLayoutIgnorePendingStylesheets(); |
561 | |
562 | Node* node = position.containerNode(); |
563 | |
564 | Position candidate = position.upstream(); |
565 | if (candidate.isCandidate()) |
566 | return candidate; |
567 | candidate = position.downstream(); |
568 | if (candidate.isCandidate()) |
569 | return candidate; |
570 | |
571 | // When neither upstream or downstream gets us to a candidate (upstream/downstream won't leave |
572 | // blocks or enter new ones), we search forward and backward until we find one. |
573 | Position next = canonicalizeCandidate(nextCandidate(position)); |
574 | Position prev = canonicalizeCandidate(previousCandidate(position)); |
575 | Node* nextNode = next.deprecatedNode(); |
576 | Node* prevNode = prev.deprecatedNode(); |
577 | |
578 | // The new position must be in the same editable element. Enforce that first. |
579 | // Unless the descent is from a non-editable html element to an editable body. |
580 | if (is<HTMLHtmlElement>(node) && !node->hasEditableStyle()) { |
581 | auto* body = node->document().bodyOrFrameset(); |
582 | if (body && body->hasEditableStyle()) |
583 | return next.isNotNull() ? next : prev; |
584 | } |
585 | |
586 | Node* editingRoot = editableRootForPosition(position); |
587 | |
588 | // If the html element is editable, descending into its body will look like a descent |
589 | // from non-editable to editable content since rootEditableElement() always stops at the body. |
590 | if ((editingRoot && editingRoot->hasTagName(htmlTag)) || (node && (node->isDocumentNode() || node->isShadowRoot()))) |
591 | return next.isNotNull() ? next : prev; |
592 | |
593 | bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot; |
594 | bool nextIsInSameEditableElement = nextNode && editableRootForPosition(next) == editingRoot; |
595 | if (prevIsInSameEditableElement && !nextIsInSameEditableElement) |
596 | return prev; |
597 | |
598 | if (nextIsInSameEditableElement && !prevIsInSameEditableElement) |
599 | return next; |
600 | |
601 | if (!nextIsInSameEditableElement && !prevIsInSameEditableElement) |
602 | return Position(); |
603 | |
604 | // The new position should be in the same block flow element. Favor that. |
605 | Element* originalBlock = deprecatedEnclosingBlockFlowElement(node); |
606 | bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock; |
607 | bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock; |
608 | if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock) |
609 | return prev; |
610 | |
611 | return next; |
612 | } |
613 | |
614 | UChar32 VisiblePosition::characterAfter() const |
615 | { |
616 | // We canonicalize to the first of two equivalent candidates, but the second of the two candidates |
617 | // is the one that will be inside the text node containing the character after this visible position. |
618 | Position pos = m_deepPosition.downstream(); |
619 | if (!pos.containerNode() || !pos.containerNode()->isTextNode()) |
620 | return 0; |
621 | switch (pos.anchorType()) { |
622 | case Position::PositionIsAfterChildren: |
623 | case Position::PositionIsAfterAnchor: |
624 | case Position::PositionIsBeforeAnchor: |
625 | case Position::PositionIsBeforeChildren: |
626 | return 0; |
627 | case Position::PositionIsOffsetInAnchor: |
628 | break; |
629 | } |
630 | unsigned offset = static_cast<unsigned>(pos.offsetInContainerNode()); |
631 | Text* textNode = pos.containerText(); |
632 | unsigned length = textNode->length(); |
633 | if (offset >= length) |
634 | return 0; |
635 | |
636 | UChar32 ch; |
637 | U16_NEXT(textNode->data(), offset, length, ch); |
638 | return ch; |
639 | } |
640 | |
641 | LayoutRect VisiblePosition::localCaretRect(RenderObject*& renderer) const |
642 | { |
643 | if (m_deepPosition.isNull()) { |
644 | renderer = nullptr; |
645 | return IntRect(); |
646 | } |
647 | Node* node = m_deepPosition.anchorNode(); |
648 | |
649 | renderer = node->renderer(); |
650 | if (!renderer) |
651 | return LayoutRect(); |
652 | |
653 | InlineBox* inlineBox; |
654 | int caretOffset; |
655 | getInlineBoxAndOffset(inlineBox, caretOffset); |
656 | |
657 | if (inlineBox) |
658 | renderer = &inlineBox->renderer(); |
659 | |
660 | return renderer->localCaretRect(inlineBox, caretOffset); |
661 | } |
662 | |
663 | IntRect VisiblePosition::absoluteCaretBounds(bool* insideFixed) const |
664 | { |
665 | RenderBlock* renderer = nullptr; |
666 | LayoutRect localRect = localCaretRectInRendererForCaretPainting(*this, renderer); |
667 | return absoluteBoundsForLocalCaretRect(renderer, localRect, insideFixed); |
668 | } |
669 | |
670 | int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const |
671 | { |
672 | RenderObject* renderer; |
673 | LayoutRect localRect = localCaretRect(renderer); |
674 | if (localRect.isEmpty() || !renderer) |
675 | return 0; |
676 | |
677 | // This ignores transforms on purpose, for now. Vertical navigation is done |
678 | // without consulting transforms, so that 'up' in transformed text is 'up' |
679 | // relative to the text, not absolute 'up'. |
680 | FloatPoint caretPoint = renderer->localToAbsolute(localRect.location()); |
681 | RenderObject* containingBlock = renderer->containingBlock(); |
682 | if (!containingBlock) |
683 | containingBlock = renderer; // Just use ourselves to determine the writing mode if we have no containing block. |
684 | return containingBlock->isHorizontalWritingMode() ? caretPoint.x() : caretPoint.y(); |
685 | } |
686 | |
687 | #if ENABLE(TREE_DEBUGGING) |
688 | |
689 | void VisiblePosition::debugPosition(const char* msg) const |
690 | { |
691 | if (isNull()) |
692 | fprintf(stderr, "Position [%s]: null\n" , msg); |
693 | else { |
694 | fprintf(stderr, "Position [%s]: %s, " , msg, m_deepPosition.deprecatedNode()->nodeName().utf8().data()); |
695 | m_deepPosition.showAnchorTypeAndOffset(); |
696 | } |
697 | } |
698 | |
699 | void VisiblePosition::formatForDebugger(char* buffer, unsigned length) const |
700 | { |
701 | m_deepPosition.formatForDebugger(buffer, length); |
702 | } |
703 | |
704 | void VisiblePosition::showTreeForThis() const |
705 | { |
706 | m_deepPosition.showTreeForThis(); |
707 | } |
708 | |
709 | #endif |
710 | |
711 | RefPtr<Range> makeRange(const VisiblePosition& start, const VisiblePosition& end) |
712 | { |
713 | if (start.isNull() || end.isNull()) |
714 | return nullptr; |
715 | |
716 | Position s = start.deepEquivalent().parentAnchoredEquivalent(); |
717 | Position e = end.deepEquivalent().parentAnchoredEquivalent(); |
718 | if (s.isNull() || e.isNull()) |
719 | return nullptr; |
720 | |
721 | return Range::create(s.containerNode()->document(), s.containerNode(), s.offsetInContainerNode(), e.containerNode(), e.offsetInContainerNode()); |
722 | } |
723 | |
724 | VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity) |
725 | { |
726 | return VisiblePosition(r->startPosition(), affinity); |
727 | } |
728 | |
729 | VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity) |
730 | { |
731 | return VisiblePosition(r->endPosition(), affinity); |
732 | } |
733 | |
734 | bool setStart(Range* range, const VisiblePosition& visiblePosition) |
735 | { |
736 | if (!range) |
737 | return false; |
738 | |
739 | Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent(); |
740 | if (!p.containerNode()) |
741 | return false; |
742 | |
743 | return !range->setStart(*p.containerNode(), p.offsetInContainerNode()).hasException(); |
744 | } |
745 | |
746 | bool setEnd(Range* range, const VisiblePosition& visiblePosition) |
747 | { |
748 | if (!range) |
749 | return false; |
750 | |
751 | Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent(); |
752 | if (!p.containerNode()) |
753 | return false; |
754 | |
755 | return !range->setEnd(*p.containerNode(), p.offsetInContainerNode()).hasException(); |
756 | } |
757 | |
758 | // FIXME: Maybe this should be deprecated too, like the underlying function? |
759 | Element* enclosingBlockFlowElement(const VisiblePosition& visiblePosition) |
760 | { |
761 | if (visiblePosition.isNull()) |
762 | return nullptr; |
763 | |
764 | return deprecatedEnclosingBlockFlowElement(visiblePosition.deepEquivalent().deprecatedNode()); |
765 | } |
766 | |
767 | bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) |
768 | { |
769 | if (visiblePosition.isNull()) |
770 | return false; |
771 | |
772 | if (!visiblePosition.deepEquivalent().containerNode()->isDescendantOf(node)) |
773 | return false; |
774 | |
775 | VisiblePosition previous = visiblePosition.previous(); |
776 | return previous.isNull() || !previous.deepEquivalent().deprecatedNode()->isDescendantOf(node); |
777 | } |
778 | |
779 | bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) |
780 | { |
781 | if (visiblePosition.isNull()) |
782 | return false; |
783 | |
784 | if (!visiblePosition.deepEquivalent().containerNode()->isDescendantOf(node)) |
785 | return false; |
786 | |
787 | VisiblePosition next = visiblePosition.next(); |
788 | return next.isNull() || !next.deepEquivalent().deprecatedNode()->isDescendantOf(node); |
789 | } |
790 | |
791 | bool VisiblePosition::equals(const VisiblePosition& other) const |
792 | { |
793 | return m_affinity == other.m_affinity && m_deepPosition.equals(other.m_deepPosition); |
794 | } |
795 | |
796 | TextStream& operator<<(TextStream& stream, EAffinity affinity) |
797 | { |
798 | switch (affinity) { |
799 | case UPSTREAM: |
800 | stream << "upstream" ; |
801 | break; |
802 | case DOWNSTREAM: |
803 | stream << "downstream" ; |
804 | break; |
805 | } |
806 | return stream; |
807 | } |
808 | |
809 | TextStream& operator<<(TextStream& stream, const VisiblePosition& visiblePosition) |
810 | { |
811 | TextStream::GroupScope scope(stream); |
812 | stream << "VisiblePosition " << &visiblePosition; |
813 | |
814 | stream.dumpProperty("position" , visiblePosition.deepEquivalent()); |
815 | stream.dumpProperty("affinity" , visiblePosition.affinity()); |
816 | |
817 | return stream; |
818 | } |
819 | |
820 | } // namespace WebCore |
821 | |
822 | #if ENABLE(TREE_DEBUGGING) |
823 | |
824 | void showTree(const WebCore::VisiblePosition* vpos) |
825 | { |
826 | if (vpos) |
827 | vpos->showTreeForThis(); |
828 | } |
829 | |
830 | void showTree(const WebCore::VisiblePosition& vpos) |
831 | { |
832 | vpos.showTreeForThis(); |
833 | } |
834 | |
835 | #endif |
836 | |