2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "visible_units.h"
31 #include "HTMLNames.h"
32 #include "InlineTextBox.h"
34 #include "RenderBlock.h"
35 #include "RenderLayer.h"
36 #include "RenderObject.h"
37 #include "RenderedPosition.h"
39 #include "TextBoundaries.h"
40 #include "TextBreakIterator.h"
41 #include "TextIterator.h"
42 #include "VisiblePosition.h"
43 #include "VisibleSelection.h"
44 #include "htmlediting.h"
45 #include <wtf/unicode/Unicode.h>
49 using namespace HTMLNames;
50 using namespace WTF::Unicode;
52 enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
54 typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
56 static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
58 Position pos = c.deepEquivalent();
59 Node* boundary = pos.parentEditingBoundary();
61 return VisiblePosition();
63 Document* d = boundary->document();
64 Position start = createLegacyEditingPosition(boundary, 0).parentAnchoredEquivalent();
65 Position end = pos.parentAnchoredEquivalent();
66 RefPtr<Range> searchRange = Range::create(d);
68 Vector<UChar, 1024> string;
69 unsigned suffixLength = 0;
72 if (requiresContextForWordBoundary(c.characterBefore())) {
73 RefPtr<Range> forwardsScanRange(d->createRange());
74 forwardsScanRange->setEndAfter(boundary, ec);
75 forwardsScanRange->setStart(end.deprecatedNode(), end.deprecatedEditingOffset(), ec);
76 TextIterator forwardsIterator(forwardsScanRange.get());
77 while (!forwardsIterator.atEnd()) {
78 const UChar* characters = forwardsIterator.characters();
79 int length = forwardsIterator.length();
80 int i = endOfFirstWordBoundaryContext(characters, length);
81 string.append(characters, i);
85 forwardsIterator.advance();
89 searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), ec);
90 searchRange->setEnd(end.deprecatedNode(), end.deprecatedEditingOffset(), ec);
94 return VisiblePosition();
96 SimplifiedBackwardsTextIterator it(searchRange.get());
98 bool inTextSecurityMode = start.deprecatedNode() && start.deprecatedNode()->renderer() && start.deprecatedNode()->renderer()->style()->textSecurity() != TSNONE;
99 bool needMoreContext = false;
100 while (!it.atEnd()) {
101 // iterate to get chunks until the searchFunction returns a non-zero value.
102 if (!inTextSecurityMode)
103 string.prepend(it.characters(), it.length());
105 // Treat bullets used in the text security mode as regular characters when looking for boundaries
106 String iteratorString(it.characters(), it.length());
107 iteratorString.fill('x');
108 string.prepend(iteratorString.characters(), iteratorString.length());
110 next = searchFunction(string.data(), string.size(), string.size() - suffixLength, MayHaveMoreContext, needMoreContext);
115 if (needMoreContext) {
116 // The last search returned the beginning of the buffer and asked for more context,
117 // but there is no earlier text. Force a search with what's available.
118 next = searchFunction(string.data(), string.size(), string.size() - suffixLength, DontHaveMoreContext, needMoreContext);
119 ASSERT(!needMoreContext);
123 return VisiblePosition(it.atEnd() ? it.range()->startPosition() : pos, DOWNSTREAM);
125 Node* node = it.range()->startContainer(ec);
126 if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next))
127 // The next variable contains a usable index into a text node
128 return VisiblePosition(createLegacyEditingPosition(node, next), DOWNSTREAM);
130 // Use the character iterator to translate the next value into a DOM position.
131 BackwardsCharacterIterator charIt(searchRange.get());
132 charIt.advance(string.size() - suffixLength - next);
133 // FIXME: charIt can get out of shadow host.
134 return VisiblePosition(charIt.range()->endPosition(), DOWNSTREAM);
137 static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
139 Position pos = c.deepEquivalent();
140 Node* boundary = pos.parentEditingBoundary();
142 return VisiblePosition();
144 Document* d = boundary->document();
145 RefPtr<Range> searchRange(d->createRange());
146 Position start(pos.parentAnchoredEquivalent());
148 Vector<UChar, 1024> string;
149 unsigned prefixLength = 0;
151 ExceptionCode ec = 0;
152 if (requiresContextForWordBoundary(c.characterAfter())) {
153 RefPtr<Range> backwardsScanRange(d->createRange());
154 backwardsScanRange->setEnd(start.deprecatedNode(), start.deprecatedEditingOffset(), ec);
155 SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
156 while (!backwardsIterator.atEnd()) {
157 const UChar* characters = backwardsIterator.characters();
158 int length = backwardsIterator.length();
159 int i = startOfLastWordBoundaryContext(characters, length);
160 string.prepend(characters + i, length - i);
161 prefixLength += length - i;
164 backwardsIterator.advance();
168 searchRange->selectNodeContents(boundary, ec);
169 searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), ec);
170 TextIterator it(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
172 bool inTextSecurityMode = start.deprecatedNode() && start.deprecatedNode()->renderer() && start.deprecatedNode()->renderer()->style()->textSecurity() != TSNONE;
173 bool needMoreContext = false;
174 while (!it.atEnd()) {
175 // Keep asking the iterator for chunks until the search function
176 // returns an end value not equal to the length of the string passed to it.
177 if (!inTextSecurityMode)
178 string.append(it.characters(), it.length());
180 // Treat bullets used in the text security mode as regular characters when looking for boundaries
181 String iteratorString(it.characters(), it.length());
182 iteratorString.fill('x');
183 string.append(iteratorString.characters(), iteratorString.length());
185 next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
186 if (next != string.size())
190 if (needMoreContext) {
191 // The last search returned the end of the buffer and asked for more context,
192 // but there is no further text. Force a search with what's available.
193 next = searchFunction(string.data(), string.size(), prefixLength, DontHaveMoreContext, needMoreContext);
194 ASSERT(!needMoreContext);
197 if (it.atEnd() && next == string.size()) {
198 pos = it.range()->startPosition();
199 } else if (next != prefixLength) {
200 // Use the character iterator to translate the next value into a DOM position.
201 CharacterIterator charIt(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
202 charIt.advance(next - prefixLength - 1);
203 RefPtr<Range> characterRange = charIt.range();
204 pos = characterRange->endPosition();
206 if (*charIt.characters() == '\n') {
207 // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
208 VisiblePosition visPos = VisiblePosition(pos);
209 if (visPos == VisiblePosition(characterRange->startPosition())) {
211 pos = charIt.range()->startPosition();
216 // generate VisiblePosition, use UPSTREAM affinity if possible
217 return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
222 static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
225 if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
226 needMoreContext = true;
229 needMoreContext = false;
231 U16_BACK_1(characters, 0, offset);
232 findWordBoundary(characters, length, offset, &start, &end);
236 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
238 // FIXME: This returns a null VP for c at the start of the document
239 // and side == LeftWordIfOnBoundary
240 VisiblePosition p = c;
241 if (side == RightWordIfOnBoundary) {
242 // at paragraph end, the startofWord is the current position
243 if (isEndOfParagraph(c))
250 return previousBoundary(p, startWordBoundary);
253 static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
255 ASSERT(offset <= length);
256 if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
257 needMoreContext = true;
260 needMoreContext = false;
262 findWordBoundary(characters, length, offset, &start, &end);
266 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
268 VisiblePosition p = c;
269 if (side == LeftWordIfOnBoundary) {
270 if (isStartOfParagraph(c))
276 } else if (isEndOfParagraph(c))
279 return nextBoundary(p, endWordBoundary);
282 static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
284 if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
285 needMoreContext = true;
288 needMoreContext = false;
289 return findNextWordFromIndex(characters, length, offset, false);
292 VisiblePosition previousWordPosition(const VisiblePosition &c)
294 VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
295 return c.honorEditingBoundaryAtOrBefore(prev);
298 static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
300 if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
301 needMoreContext = true;
304 needMoreContext = false;
305 return findNextWordFromIndex(characters, length, offset, true);
308 VisiblePosition nextWordPosition(const VisiblePosition &c)
310 VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
311 return c.honorEditingBoundaryAtOrAfter(next);
314 bool isStartOfWord(const VisiblePosition& p)
316 return p.isNotNull() && p == startOfWord(p, RightWordIfOnBoundary);
321 enum LineEndpointComputationMode { UseLogicalOrdering, UseInlineBoxOrdering };
322 static VisiblePosition startPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
325 return VisiblePosition();
327 RootInlineBox* rootBox = RenderedPosition(c).rootBox();
329 // There are VisiblePositions at offset 0 in blocks without
330 // RootInlineBoxes, like empty editable blocks and bordered blocks.
331 Position p = c.deepEquivalent();
332 if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
335 return VisiblePosition();
340 if (mode == UseLogicalOrdering) {
341 startNode = rootBox->getLogicalStartBoxWithNode(startBox);
343 return VisiblePosition();
345 // Generated content (e.g. list markers and CSS :before and :after pseudoelements) have no corresponding DOM element,
346 // and so cannot be represented by a VisiblePosition. Use whatever follows instead.
347 startBox = rootBox->firstLeafChild();
350 return VisiblePosition();
352 RenderObject* startRenderer = startBox->renderer();
354 return VisiblePosition();
356 startNode = startRenderer->node();
360 startBox = startBox->nextLeafChild();
364 return startNode->isTextNode() ? Position(static_cast<Text*>(startNode), toInlineTextBox(startBox)->start())
365 : positionBeforeNode(startNode);
368 static VisiblePosition startOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
370 // TODO: this is the current behavior that might need to be fixed.
371 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
372 VisiblePosition visPos = startPositionForLine(c, mode);
374 if (mode == UseLogicalOrdering) {
375 if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
376 if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
377 return firstPositionInNode(editableRoot);
381 return c.honorEditingBoundaryAtOrBefore(visPos);
384 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
385 VisiblePosition startOfLine(const VisiblePosition& currentPosition)
387 return startOfLine(currentPosition, UseInlineBoxOrdering);
390 VisiblePosition logicalStartOfLine(const VisiblePosition& currentPosition)
392 return startOfLine(currentPosition, UseLogicalOrdering);
395 static VisiblePosition endPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
398 return VisiblePosition();
400 RootInlineBox* rootBox = RenderedPosition(c).rootBox();
402 // There are VisiblePositions at offset 0 in blocks without
403 // RootInlineBoxes, like empty editable blocks and bordered blocks.
404 Position p = c.deepEquivalent();
405 if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
407 return VisiblePosition();
412 if (mode == UseLogicalOrdering) {
413 endNode = rootBox->getLogicalEndBoxWithNode(endBox);
415 return VisiblePosition();
417 // Generated content (e.g. list markers and CSS :before and :after pseudoelements) have no corresponding DOM element,
418 // and so cannot be represented by a VisiblePosition. Use whatever precedes instead.
419 endBox = rootBox->lastLeafChild();
422 return VisiblePosition();
424 RenderObject* endRenderer = endBox->renderer();
426 return VisiblePosition();
428 endNode = endRenderer->node();
432 endBox = endBox->prevLeafChild();
437 if (endNode->hasTagName(brTag))
438 pos = positionBeforeNode(endNode);
439 else if (endBox->isInlineTextBox() && endNode->isTextNode()) {
440 InlineTextBox* endTextBox = toInlineTextBox(endBox);
441 int endOffset = endTextBox->start();
442 if (!endTextBox->isLineBreak())
443 endOffset += endTextBox->len();
444 pos = Position(static_cast<Text*>(endNode), endOffset);
446 pos = positionAfterNode(endNode);
448 return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
451 static bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b)
453 return a.isNotNull() && logicalStartOfLine(a) == logicalStartOfLine(b);
456 static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
458 // TODO: this is the current behavior that might need to be fixed.
459 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
460 VisiblePosition visPos = endPositionForLine(c, mode);
462 if (mode == UseLogicalOrdering) {
463 // Make sure the end of line is at the same line as the given input position. For a wrapping line, the logical end
464 // position for the not-last-2-lines might incorrectly hand back the logical beginning of the next line.
465 // For example, <div contenteditable dir="rtl" style="line-break:before-white-space">abcdefg abcdefg abcdefg
466 // a abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg </div>
467 // In this case, use the previous position of the computed logical end position.
468 if (!inSameLogicalLine(c, visPos))
469 visPos = visPos.previous();
471 if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
472 if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
473 return lastPositionInNode(editableRoot);
476 return c.honorEditingBoundaryAtOrAfter(visPos);
479 // Make sure the end of line is at the same line as the given input position. Else use the previous position to
480 // obtain end of line. This condition happens when the input position is before the space character at the end
481 // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
482 // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
483 // versus lines without that style, which would break before a space by default.
484 if (!inSameLine(c, visPos)) {
485 visPos = c.previous();
487 return VisiblePosition();
488 visPos = endPositionForLine(visPos, UseInlineBoxOrdering);
491 return c.honorEditingBoundaryAtOrAfter(visPos);
494 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
495 VisiblePosition endOfLine(const VisiblePosition& currentPosition)
497 return endOfLine(currentPosition, UseInlineBoxOrdering);
500 VisiblePosition logicalEndOfLine(const VisiblePosition& currentPosition)
502 return endOfLine(currentPosition, UseLogicalOrdering);
505 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
507 return a.isNotNull() && startOfLine(a) == startOfLine(b);
510 bool isStartOfLine(const VisiblePosition &p)
512 return p.isNotNull() && p == startOfLine(p);
515 bool isEndOfLine(const VisiblePosition &p)
517 return p.isNotNull() && p == endOfLine(p);
520 // The first leaf before node that has the same editability as node.
521 static Node* previousLeafWithSameEditability(Node* node)
523 bool editable = node->rendererIsEditable();
524 Node* n = node->previousLeafNode();
526 if (editable == n->rendererIsEditable())
528 n = n->previousLeafNode();
533 static Node* enclosingNodeWithNonInlineRenderer(Node* n)
535 for (Node* p = n; p; p = p->parentNode()) {
536 if (p->renderer() && !p->renderer()->isInline())
542 static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint)
545 RenderBlock* containingBlock = root->block();
546 FloatPoint absoluteBlockPoint = containingBlock->localToAbsolute(FloatPoint());
547 if (containingBlock->hasOverflowClip())
548 absoluteBlockPoint -= containingBlock->layer()->scrolledContentOffset();
550 if (root->block()->isHorizontalWritingMode())
551 return IntPoint(lineDirectionPoint - absoluteBlockPoint.x(), root->blockDirectionPointInLine());
553 return IntPoint(root->selectionTop(), lineDirectionPoint - absoluteBlockPoint.y());
556 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint)
558 Position p = visiblePosition.deepEquivalent();
559 Node* node = p.deprecatedNode();
560 Node* highestRoot = highestEditableRoot(p);
562 return VisiblePosition();
564 node->document()->updateLayoutIgnorePendingStylesheets();
566 RenderObject *renderer = node->renderer();
568 return VisiblePosition();
570 RootInlineBox *root = 0;
572 int ignoredCaretOffset;
573 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
575 root = box->root()->prevRootBox();
576 // We want to skip zero height boxes.
577 // This could happen in case it is a TrailingFloatsRootInlineBox.
578 if (!root || !root->logicalHeight())
583 // This containing editable block does not have a previous line.
584 // Need to move back to previous containing editable block in this root editable
585 // block and find the last root line box in that block.
586 Node* startBlock = enclosingNodeWithNonInlineRenderer(node);
587 Node* n = previousLeafWithSameEditability(node);
588 while (n && startBlock == enclosingNodeWithNonInlineRenderer(n))
589 n = previousLeafWithSameEditability(n);
591 if (highestEditableRoot(firstPositionInOrBeforeNode(n)) != highestRoot)
593 Position pos = n->hasTagName(brTag) ? positionBeforeNode(n) : createLegacyEditingPosition(n, caretMaxOffset(n));
594 if (pos.isCandidate()) {
595 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
597 // previous root line box found
602 return VisiblePosition(pos, DOWNSTREAM);
604 n = previousLeafWithSameEditability(n);
609 // FIXME: Can be wrong for multi-column layout and with transforms.
610 IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
611 RenderObject* renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
612 Node* node = renderer->node();
613 if (node && editingIgnoresContent(node))
614 return positionInParentBeforeNode(node);
615 return renderer->positionForPoint(pointInLine);
618 // Could not find a previous line. This means we must already be on the first line.
619 // Move to the start of the content in this block, which effectively moves us
620 // to the start of the line we're on.
621 Element* rootElement = node->rendererIsEditable() ? node->rootEditableElement() : node->document()->documentElement();
623 return VisiblePosition();
624 return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM);
627 static Node* nextLeafWithSameEditability(Node* node, int offset)
629 bool editable = node->rendererIsEditable();
631 Node* child = node->childNode(offset);
632 Node* n = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode();
634 if (editable == n->rendererIsEditable())
636 n = n->nextLeafNode();
641 static Node* nextLeafWithSameEditability(Node* node)
646 bool editable = node->rendererIsEditable();
647 Node* n = node->nextLeafNode();
649 if (editable == n->rendererIsEditable())
651 n = n->nextLeafNode();
656 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint)
658 Position p = visiblePosition.deepEquivalent();
659 Node* node = p.deprecatedNode();
660 Node* highestRoot = highestEditableRoot(p);
662 return VisiblePosition();
664 node->document()->updateLayoutIgnorePendingStylesheets();
666 RenderObject *renderer = node->renderer();
668 return VisiblePosition();
670 RootInlineBox *root = 0;
672 int ignoredCaretOffset;
673 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
675 root = box->root()->nextRootBox();
676 // We want to skip zero height boxes.
677 // This could happen in case it is a TrailingFloatsRootInlineBox.
678 if (!root || !root->logicalHeight())
683 // This containing editable block does not have a next line.
684 // Need to move forward to next containing editable block in this root editable
685 // block and find the first root line box in that block.
686 Node* startBlock = enclosingNodeWithNonInlineRenderer(node);
687 Node* n = nextLeafWithSameEditability(node, p.deprecatedEditingOffset());
688 while (n && startBlock == enclosingNodeWithNonInlineRenderer(n))
689 n = nextLeafWithSameEditability(n);
691 if (highestEditableRoot(firstPositionInOrBeforeNode(n)) != highestRoot)
693 Position pos = createLegacyEditingPosition(n, caretMinOffset(n));
694 if (pos.isCandidate()) {
695 ASSERT(n->renderer());
696 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
698 // next root line box found
703 return VisiblePosition(pos, DOWNSTREAM);
705 n = nextLeafWithSameEditability(n);
710 // FIXME: Can be wrong for multi-column layout and with transforms.
711 IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
712 RenderObject* renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
713 Node* node = renderer->node();
714 if (node && editingIgnoresContent(node))
715 return positionInParentBeforeNode(node);
716 return renderer->positionForPoint(pointInLine);
719 // Could not find a next line. This means we must already be on the last line.
720 // Move to the end of the content in this block, which effectively moves us
721 // to the end of the line we're on.
722 Element* rootElement = node->rendererIsEditable() ? node->rootEditableElement() : node->document()->documentElement();
724 return VisiblePosition();
725 return VisiblePosition(lastPositionInNode(rootElement), DOWNSTREAM);
730 static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
732 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
733 // FIXME: The following function can return -1; we don't handle that.
734 return textBreakPreceding(iterator, length);
737 VisiblePosition startOfSentence(const VisiblePosition &c)
739 return previousBoundary(c, startSentenceBoundary);
742 static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
744 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
745 return textBreakNext(iterator);
748 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
749 VisiblePosition endOfSentence(const VisiblePosition &c)
751 return nextBoundary(c, endSentenceBoundary);
754 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
756 // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
757 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
758 // FIXME: The following function can return -1; we don't handle that.
759 return textBreakPreceding(iterator, length);
762 VisiblePosition previousSentencePosition(const VisiblePosition &c)
764 VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
765 return c.honorEditingBoundaryAtOrBefore(prev);
768 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
770 // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
771 // move to the equivlant position in the following sentence.
772 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
773 return textBreakFollowing(iterator, 0);
776 VisiblePosition nextSentencePosition(const VisiblePosition &c)
778 VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
779 return c.honorEditingBoundaryAtOrAfter(next);
782 VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
784 Position p = c.deepEquivalent();
785 Node* startNode = p.deprecatedNode();
788 return VisiblePosition();
790 if (isRenderedAsNonInlineTableImageOrHR(startNode))
791 return positionBeforeNode(startNode);
793 Node* startBlock = enclosingBlock(startNode);
795 Node* node = startNode;
796 Node* highestRoot = highestEditableRoot(p);
797 int offset = p.deprecatedEditingOffset();
798 Position::AnchorType type = p.anchorType();
802 if (boundaryCrossingRule == CannotCrossEditingBoundary && n->rendererIsEditable() != startNode->rendererIsEditable())
804 if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
805 while (n && n->rendererIsEditable() != startNode->rendererIsEditable())
806 n = n->traversePreviousNodePostOrder(startBlock);
807 if (!n || !n->isDescendantOf(highestRoot))
810 RenderObject *r = n->renderer();
812 n = n->traversePreviousNodePostOrder(startBlock);
815 RenderStyle *style = r->style();
816 if (style->visibility() != VISIBLE) {
817 n = n->traversePreviousNodePostOrder(startBlock);
821 if (r->isBR() || isBlock(n))
824 if (r->isText() && toRenderText(r)->renderedTextLength()) {
825 ASSERT(n->isTextNode());
826 type = Position::PositionIsOffsetInAnchor;
827 if (style->preserveNewline()) {
828 const UChar* chars = toRenderText(r)->characters();
829 int i = toRenderText(r)->textLength();
831 if (n == startNode && o < i)
834 if (chars[i] == '\n')
835 return VisiblePosition(Position(static_cast<Text*>(n), i + 1), DOWNSTREAM);
840 n = n->traversePreviousNodePostOrder(startBlock);
841 } else if (editingIgnoresContent(n) || isTableElement(n)) {
843 type = Position::PositionIsBeforeAnchor;
844 n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock);
846 n = n->traversePreviousNodePostOrder(startBlock);
849 if (type == Position::PositionIsOffsetInAnchor) {
850 ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
851 return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
854 return VisiblePosition(Position(node, type), DOWNSTREAM);
857 VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossingRule boundaryCrossingRule)
860 return VisiblePosition();
862 Position p = c.deepEquivalent();
863 Node* startNode = p.deprecatedNode();
865 if (isRenderedAsNonInlineTableImageOrHR(startNode))
866 return positionAfterNode(startNode);
868 Node* startBlock = enclosingBlock(startNode);
869 Node* stayInsideBlock = startBlock;
871 Node* node = startNode;
872 Node* highestRoot = highestEditableRoot(p);
873 int offset = p.deprecatedEditingOffset();
874 Position::AnchorType type = p.anchorType();
878 if (boundaryCrossingRule == CannotCrossEditingBoundary && n->rendererIsEditable() != startNode->rendererIsEditable())
880 if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
881 while (n && n->rendererIsEditable() != startNode->rendererIsEditable())
882 n = n->traverseNextNode(stayInsideBlock);
883 if (!n || !n->isDescendantOf(highestRoot))
887 RenderObject *r = n->renderer();
889 n = n->traverseNextNode(stayInsideBlock);
892 RenderStyle *style = r->style();
893 if (style->visibility() != VISIBLE) {
894 n = n->traverseNextNode(stayInsideBlock);
898 if (r->isBR() || isBlock(n))
901 // FIXME: We avoid returning a position where the renderer can't accept the caret.
902 if (r->isText() && toRenderText(r)->renderedTextLength()) {
903 ASSERT(n->isTextNode());
904 int length = toRenderText(r)->textLength();
905 type = Position::PositionIsOffsetInAnchor;
906 if (style->preserveNewline()) {
907 const UChar* chars = toRenderText(r)->characters();
908 int o = n == startNode ? offset : 0;
909 for (int i = o; i < length; ++i) {
910 if (chars[i] == '\n')
911 return VisiblePosition(Position(static_cast<Text*>(n), i), DOWNSTREAM);
915 offset = r->caretMaxOffset();
916 n = n->traverseNextNode(stayInsideBlock);
917 } else if (editingIgnoresContent(n) || isTableElement(n)) {
919 type = Position::PositionIsAfterAnchor;
920 n = n->traverseNextSibling(stayInsideBlock);
922 n = n->traverseNextNode(stayInsideBlock);
925 if (type == Position::PositionIsOffsetInAnchor)
926 return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
928 return VisiblePosition(Position(node, type), DOWNSTREAM);
931 // FIXME: isStartOfParagraph(startOfNextParagraph(pos)) is not always true
932 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
934 VisiblePosition paragraphEnd(endOfParagraph(visiblePosition, CanSkipOverEditingBoundary));
935 VisiblePosition afterParagraphEnd(paragraphEnd.next(CannotCrossEditingBoundary));
936 // The position after the last position in the last cell of a table
937 // is not the start of the next paragraph.
938 if (isFirstPositionAfterTable(afterParagraphEnd))
939 return afterParagraphEnd.next(CannotCrossEditingBoundary);
940 return afterParagraphEnd;
943 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b, EditingBoundaryCrossingRule boundaryCrossingRule)
945 return a.isNotNull() && startOfParagraph(a, boundaryCrossingRule) == startOfParagraph(b, boundaryCrossingRule);
948 bool isStartOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
950 return pos.isNotNull() && pos == startOfParagraph(pos, boundaryCrossingRule);
953 bool isEndOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
955 return pos.isNotNull() && pos == endOfParagraph(pos, boundaryCrossingRule);
958 VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x)
960 VisiblePosition pos = p;
962 VisiblePosition n = previousLinePosition(pos, x);
963 if (n.isNull() || n == pos)
966 } while (inSameParagraph(p, pos));
970 VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x)
972 VisiblePosition pos = p;
974 VisiblePosition n = nextLinePosition(pos, x);
975 if (n.isNull() || n == pos)
978 } while (inSameParagraph(p, pos));
984 VisiblePosition startOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
986 Position position = visiblePosition.deepEquivalent();
988 if (!position.containerNode() || !(startBlock = enclosingBlock(position.containerNode(), rule)))
989 return VisiblePosition();
990 return firstPositionInNode(startBlock);
993 VisiblePosition endOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
995 Position position = visiblePosition.deepEquivalent();
997 if (!position.containerNode() || !(endBlock = enclosingBlock(position.containerNode(), rule)))
998 return VisiblePosition();
999 return lastPositionInNode(endBlock);
1002 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
1004 return !a.isNull() && enclosingBlock(a.deepEquivalent().containerNode()) == enclosingBlock(b.deepEquivalent().containerNode());
1007 bool isStartOfBlock(const VisiblePosition &pos)
1009 return pos.isNotNull() && pos == startOfBlock(pos, CanCrossEditingBoundary);
1012 bool isEndOfBlock(const VisiblePosition &pos)
1014 return pos.isNotNull() && pos == endOfBlock(pos, CanCrossEditingBoundary);
1019 VisiblePosition startOfDocument(const Node* node)
1021 if (!node || !node->document() || !node->document()->documentElement())
1022 return VisiblePosition();
1024 return VisiblePosition(firstPositionInNode(node->document()->documentElement()), DOWNSTREAM);
1027 VisiblePosition startOfDocument(const VisiblePosition &c)
1029 return startOfDocument(c.deepEquivalent().deprecatedNode());
1032 VisiblePosition endOfDocument(const Node* node)
1034 if (!node || !node->document() || !node->document()->documentElement())
1035 return VisiblePosition();
1037 Element* doc = node->document()->documentElement();
1038 return VisiblePosition(lastPositionInNode(doc), DOWNSTREAM);
1041 VisiblePosition endOfDocument(const VisiblePosition &c)
1043 return endOfDocument(c.deepEquivalent().deprecatedNode());
1046 bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
1048 Position ap = a.deepEquivalent();
1049 Node* an = ap.deprecatedNode();
1052 Position bp = b.deepEquivalent();
1053 Node* bn = bp.deprecatedNode();
1057 return an->document() == bn->document();
1060 bool isStartOfDocument(const VisiblePosition &p)
1062 return p.isNotNull() && p.previous().isNull();
1065 bool isEndOfDocument(const VisiblePosition &p)
1067 return p.isNotNull() && p.next().isNull();
1072 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
1074 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1076 return VisiblePosition();
1078 return firstPositionInNode(highestRoot);
1081 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
1083 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1085 return VisiblePosition();
1087 return lastPositionInNode(highestRoot);
1090 VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1092 return direction == LTR ? logicalStartOfLine(c) : logicalEndOfLine(c);
1095 VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1097 return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
1100 static const int invalidOffset = -1;
1101 static const int offsetNotFound = -1;
1103 static bool positionIsInBox(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
1105 if (wordBreak.isNull())
1108 InlineBox* boxOfWordBreak;
1109 wordBreak.getInlineBoxAndOffset(boxOfWordBreak, offsetOfWordBreak);
1110 return box == boxOfWordBreak;
1113 static VisiblePosition previousWordBreakInBoxInsideBlockWithSameDirectionality(const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak)
1115 // In a LTR block, the word break should be on the left boundary of a word.
1116 // In a RTL block, the word break should be on the right boundary of a word.
1117 // Because nextWordPosition() returns the word break on the right boundary of the word for LTR text,
1118 // we need to use previousWordPosition() to traverse words within the inline boxes from right to left
1119 // to find the previous word break (i.e. the first word break on the left). The same applies to RTL text.
1121 bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
1123 VisiblePosition wordBreak;
1125 if (hasSeenWordBreakInThisBox)
1126 wordBreak = previousWordBreak;
1128 wordBreak = createLegacyEditingPosition(box->renderer()->node(), box->caretMaxOffset());
1130 // Return the rightmost word boundary of LTR box or leftmost word boundary of RTL box if
1131 // it is not in the previously visited boxes. For example, given a logical text
1132 // "abc def hij opq", there are 2 boxes: the "abc def " (starts at 0 and length is 8)
1133 // and the "hij opq" (starts at 12 and length is 7). The word breaks are
1134 // "abc |def | hij |opq". We normally catch the word break between "def" and "hij" when
1135 // we visit the box that contains "hij opq", but this word break doesn't exist in the box
1136 // that contains "hij opq" when there are multiple spaces. So we detect it when we're
1137 // traversing the box that contains "abc def " instead.
1139 if ((box->isLeftToRightDirection() && box->nextLeafChild())
1140 || (!box->isLeftToRightDirection() && box->prevLeafChild())) {
1142 VisiblePosition positionAfterWord = nextBoundary(wordBreak, nextWordPositionBoundary);
1143 if (positionAfterWord.isNotNull()) {
1144 VisiblePosition positionBeforeWord = previousBoundary(positionAfterWord, previousWordPositionBoundary);
1146 if (positionIsInBox(positionBeforeWord, box, offsetOfWordBreak))
1147 return positionBeforeWord;
1152 wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
1153 if (previousWordBreak == wordBreak)
1154 return VisiblePosition();
1156 return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
1159 static VisiblePosition leftmostPositionInRTLBoxInLTRBlock(const InlineBox* box)
1161 // FIXME: Probably need to take care of bidi level too.
1162 Node* node = box->renderer()->node();
1163 InlineBox* previousLeaf = box->prevLeafChild();
1164 InlineBox* nextLeaf = box->nextLeafChild();
1166 if (previousLeaf && !previousLeaf->isLeftToRightDirection())
1167 return createLegacyEditingPosition(node, box->caretMaxOffset());
1169 if (nextLeaf && !nextLeaf->isLeftToRightDirection()) {
1171 return createLegacyEditingPosition(previousLeaf->renderer()->node(), previousLeaf->caretMaxOffset());
1173 InlineBox* lastRTLLeaf;
1175 lastRTLLeaf = nextLeaf;
1176 nextLeaf = nextLeaf->nextLeafChild();
1177 } while (nextLeaf && !nextLeaf->isLeftToRightDirection());
1178 return createLegacyEditingPosition(lastRTLLeaf->renderer()->node(), lastRTLLeaf->caretMinOffset());
1181 return createLegacyEditingPosition(node, box->caretMinOffset());
1184 static VisiblePosition rightmostPositionInLTRBoxInRTLBlock(const InlineBox* box)
1186 // FIXME: Probably need to take care of bidi level too.
1187 Node* node = box->renderer()->node();
1188 InlineBox* previousLeaf = box->prevLeafChild();
1189 InlineBox* nextLeaf = box->nextLeafChild();
1191 if (nextLeaf && nextLeaf->isLeftToRightDirection())
1192 return createLegacyEditingPosition(node, box->caretMaxOffset());
1194 if (previousLeaf && previousLeaf->isLeftToRightDirection()) {
1196 return createLegacyEditingPosition(nextLeaf->renderer()->node(), nextLeaf->caretMaxOffset());
1198 InlineBox* firstLTRLeaf;
1200 firstLTRLeaf = previousLeaf;
1201 previousLeaf = previousLeaf->prevLeafChild();
1202 } while (previousLeaf && previousLeaf->isLeftToRightDirection());
1203 return createLegacyEditingPosition(firstLTRLeaf->renderer()->node(), firstLTRLeaf->caretMinOffset());
1206 return createLegacyEditingPosition(node, box->caretMinOffset());
1209 static VisiblePosition lastWordBreakInBox(const InlineBox* box, int& offsetOfWordBreak)
1211 // Add the leftmost word break for RTL box or rightmost word break for LTR box.
1212 InlineBox* previousLeaf = box->prevLeafChild();
1213 InlineBox* nextLeaf = box->nextLeafChild();
1214 VisiblePosition boundaryPosition;
1215 if (box->direction() == RTL && (!previousLeaf || previousLeaf->isLeftToRightDirection()))
1216 boundaryPosition = leftmostPositionInRTLBoxInLTRBlock(box);
1217 else if (box->direction() == LTR && (!nextLeaf || !nextLeaf->isLeftToRightDirection()))
1218 boundaryPosition = rightmostPositionInLTRBoxInRTLBlock(box);
1220 if (boundaryPosition.isNull())
1221 return VisiblePosition();
1223 VisiblePosition wordBreak = nextBoundary(boundaryPosition, nextWordPositionBoundary);
1224 if (wordBreak.isNull())
1225 wordBreak = boundaryPosition;
1226 else if (wordBreak != boundaryPosition)
1227 wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
1229 return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
1232 static bool positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
1234 int previousOffset = offsetOfWordBreak;
1235 return positionIsInBox(wordBreak, box, offsetOfWordBreak)
1236 && (previousOffset == invalidOffset || previousOffset < offsetOfWordBreak);
1239 static VisiblePosition nextWordBreakInBoxInsideBlockWithDifferentDirectionality(
1240 const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak, bool& isLastWordBreakInBox)
1242 // FIXME: Probably need to take care of bidi level too.
1244 // In a LTR block, the word break should be on the left boundary of a word.
1245 // In a RTL block, the word break should be on the right boundary of a word.
1246 // Because previousWordPosition() returns the word break on the right boundary of the word for RTL text,
1247 // we need to use nextWordPosition() to traverse words within the inline boxes from right to left to find the next word break.
1248 // The same applies to LTR text, in which words are traversed within the inline boxes from left to right.
1250 bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
1251 VisiblePosition wordBreak = hasSeenWordBreakInThisBox ? previousWordBreak :
1252 createLegacyEditingPosition(box->renderer()->node(), box->caretMinOffset());
1254 wordBreak = nextBoundary(wordBreak, nextWordPositionBoundary);
1256 // Given RTL box "ABC DEF" either follows a LTR box or is the first visual box in an LTR block as an example,
1257 // the visual display of the RTL box is: "(0)J(10)I(9)H(8) (7)F(6)E(5)D(4) (3)C(2)B(1)A(11)",
1258 // where the number in parenthesis represents offset in visiblePosition.
1259 // Start at offset 0, the first word break is at offset 3, the 2nd word break is at offset 7, and the 3rd word break should be at offset 0.
1260 // But nextWordPosition() of offset 7 is offset 11, which should be ignored,
1261 // and the position at offset 0 should be manually added as the last word break within the box.
1262 if (wordBreak != previousWordBreak && positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(wordBreak, box, offsetOfWordBreak)) {
1263 isLastWordBreakInBox = false;
1267 isLastWordBreakInBox = true;
1268 return lastWordBreakInBox(box, offsetOfWordBreak);
1271 struct WordBoundaryEntry {
1273 : offsetInInlineBox(invalidOffset)
1277 WordBoundaryEntry(const VisiblePosition& position, int offset)
1278 : visiblePosition(position)
1279 , offsetInInlineBox(offset)
1283 VisiblePosition visiblePosition;
1284 int offsetInInlineBox;
1287 typedef Vector<WordBoundaryEntry, 50> WordBoundaryVector;
1289 static void collectWordBreaksInBoxInsideBlockWithSameDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
1291 orderedWordBoundaries.clear();
1293 VisiblePosition wordBreak;
1294 int offsetOfWordBreak = invalidOffset;
1296 wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
1297 if (wordBreak.isNull())
1299 WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
1300 orderedWordBoundaries.append(wordBoundaryEntry);
1304 static void collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
1306 orderedWordBoundaries.clear();
1308 VisiblePosition wordBreak;
1309 int offsetOfWordBreak = invalidOffset;
1310 bool isLastWordBreakInBox = false;
1312 wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
1313 if (wordBreak.isNotNull()) {
1314 WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
1315 orderedWordBoundaries.append(wordBoundaryEntry);
1317 if (isLastWordBreakInBox)
1322 static void collectWordBreaksInBox(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries, TextDirection blockDirection)
1324 if (box->direction() == blockDirection)
1325 collectWordBreaksInBoxInsideBlockWithSameDirectionality(box, orderedWordBoundaries);
1327 collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(box, orderedWordBoundaries);
1330 static VisiblePosition previousWordBoundaryInBox(const InlineBox* box, int offset)
1332 int offsetOfWordBreak = 0;
1333 VisiblePosition wordBreak;
1335 wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
1336 if (wordBreak.isNull())
1338 if (offset == invalidOffset || offsetOfWordBreak != offset)
1341 return VisiblePosition();
1344 static VisiblePosition nextWordBoundaryInBox(const InlineBox* box, int offset)
1346 int offsetOfWordBreak = 0;
1347 VisiblePosition wordBreak;
1348 bool isLastWordBreakInBox = false;
1350 wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
1351 if (wordBreak.isNotNull() && (offset == invalidOffset || offsetOfWordBreak != offset))
1353 } while (!isLastWordBreakInBox);
1354 return VisiblePosition();
1357 static VisiblePosition visuallyLastWordBoundaryInBox(const InlineBox* box, int offset, TextDirection blockDirection)
1359 WordBoundaryVector orderedWordBoundaries;
1360 collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
1361 if (!orderedWordBoundaries.size())
1362 return VisiblePosition();
1363 if (offset == invalidOffset || orderedWordBoundaries[orderedWordBoundaries.size() - 1].offsetInInlineBox != offset)
1364 return orderedWordBoundaries[orderedWordBoundaries.size() - 1].visiblePosition;
1365 if (orderedWordBoundaries.size() > 1)
1366 return orderedWordBoundaries[orderedWordBoundaries.size() - 2].visiblePosition;
1367 return VisiblePosition();
1370 static int greatestOffsetUnder(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
1372 if (!orderedWordBoundaries.size())
1373 return offsetNotFound;
1374 // FIXME: binary search.
1375 if (boxAndBlockAreInSameDirection) {
1376 for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
1377 if (orderedWordBoundaries[i].offsetInInlineBox < offset)
1380 return offsetNotFound;
1382 for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
1383 if (orderedWordBoundaries[i].offsetInInlineBox < offset)
1386 return offsetNotFound;
1389 static int smallestOffsetAbove(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
1391 if (!orderedWordBoundaries.size())
1392 return offsetNotFound;
1393 // FIXME: binary search.
1394 if (boxAndBlockAreInSameDirection) {
1395 for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
1396 if (orderedWordBoundaries[i].offsetInInlineBox > offset)
1399 return offsetNotFound;
1401 for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
1402 if (orderedWordBoundaries[i].offsetInInlineBox > offset)
1405 return offsetNotFound;
1408 static const RootInlineBox* previousRootInlineBox(const InlineBox* box)
1410 Node* node = box->renderer()->node();
1411 Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
1412 Node* previousNode = node->previousLeafNode();
1413 while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode))
1414 previousNode = previousNode->previousLeafNode();
1416 while (previousNode && !previousNode->isShadowRoot()) {
1417 Position pos = createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
1419 if (pos.isCandidate()) {
1420 RenderedPosition renderedPos(pos, DOWNSTREAM);
1421 RootInlineBox* root = renderedPos.rootBox();
1426 previousNode = previousNode->previousLeafNode();
1431 static const RootInlineBox* nextRootInlineBox(const InlineBox* box)
1433 Node* node = box->renderer()->node();
1434 Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
1435 Node* nextNode = node->nextLeafNode();
1436 while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode))
1437 nextNode = nextNode->nextLeafNode();
1439 while (nextNode && !nextNode->isShadowRoot()) {
1441 pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
1443 if (pos.isCandidate()) {
1444 RenderedPosition renderedPos(pos, DOWNSTREAM);
1445 RootInlineBox* root = renderedPos.rootBox();
1450 nextNode = nextNode->nextLeafNode();
1455 static const InlineBox* leftInlineBox(const InlineBox* box, TextDirection blockDirection)
1457 if (box->prevLeafChild())
1458 return box->prevLeafChild();
1460 const RootInlineBox* rootBox = box->root();
1461 const bool isBlockLTR = blockDirection == LTR;
1462 const InlineFlowBox* leftLineBox = isBlockLTR ? rootBox->prevLineBox() : rootBox->nextLineBox();
1464 return leftLineBox->lastLeafChild();
1466 const RootInlineBox* leftRootInlineBox = isBlockLTR ? previousRootInlineBox(box) :
1467 nextRootInlineBox(box);
1468 return leftRootInlineBox ? leftRootInlineBox->lastLeafChild() : 0;
1471 static const InlineBox* rightInlineBox(const InlineBox* box, TextDirection blockDirection)
1473 if (box->nextLeafChild())
1474 return box->nextLeafChild();
1476 const RootInlineBox* rootBox = box->root();
1477 const bool isBlockLTR = blockDirection == LTR;
1478 const InlineFlowBox* rightLineBox = isBlockLTR ? rootBox->nextLineBox() : rootBox->prevLineBox();
1480 return rightLineBox->firstLeafChild();
1482 const RootInlineBox* rightRootInlineBox = isBlockLTR ? nextRootInlineBox(box) :
1483 previousRootInlineBox(box);
1484 return rightRootInlineBox ? rightRootInlineBox->firstLeafChild() : 0;
1487 static VisiblePosition leftWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
1489 VisiblePosition wordBreak;
1490 for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = leftInlineBox(adjacentBox, blockDirection)) {
1491 if (blockDirection == LTR) {
1492 if (adjacentBox->isLeftToRightDirection())
1493 wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1495 wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1497 wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
1498 if (wordBreak.isNotNull())
1501 return VisiblePosition();
1504 static VisiblePosition rightWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
1507 VisiblePosition wordBreak;
1508 for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = rightInlineBox(adjacentBox, blockDirection)) {
1509 if (blockDirection == RTL) {
1510 if (adjacentBox->isLeftToRightDirection())
1511 wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1513 wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1515 wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
1516 if (!wordBreak.isNull())
1519 return VisiblePosition();
1522 static bool positionIsInBoxButNotOnBoundary(const VisiblePosition& wordBreak, const InlineBox* box)
1524 int offsetOfWordBreak;
1525 return positionIsInBox(wordBreak, box, offsetOfWordBreak)
1526 && offsetOfWordBreak != box->caretMaxOffset() && offsetOfWordBreak != box->caretMinOffset();
1529 static VisiblePosition leftWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
1533 visiblePosition.getInlineBoxAndOffset(box, offset);
1536 return VisiblePosition();
1538 TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
1540 // FIXME: If the box's directionality is the same as that of the enclosing block, when the offset is at the box boundary
1541 // and the direction is towards inside the box, do I still need to make it a special case? For example, a LTR box inside a LTR block,
1542 // when offset is at box's caretMinOffset and the direction is DirectionRight, should it be taken care as a general case?
1543 if (offset == box->caretLeftmostOffset())
1544 return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
1545 if (offset == box->caretRightmostOffset())
1546 return leftWordBoundary(box, offset, blockDirection);
1549 VisiblePosition wordBreak;
1550 if (blockDirection == LTR) {
1551 if (box->direction() == blockDirection)
1552 wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
1554 wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
1556 if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
1559 WordBoundaryVector orderedWordBoundaries;
1560 collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
1562 int index = box->isLeftToRightDirection() ? greatestOffsetUnder(offset, blockDirection == LTR, orderedWordBoundaries)
1563 : smallestOffsetAbove(offset, blockDirection == RTL, orderedWordBoundaries);
1565 return orderedWordBoundaries[index].visiblePosition;
1567 return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
1570 static VisiblePosition rightWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
1574 visiblePosition.getInlineBoxAndOffset(box, offset);
1577 return VisiblePosition();
1579 TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
1581 if (offset == box->caretLeftmostOffset())
1582 return rightWordBoundary(box, offset, blockDirection);
1583 if (offset == box->caretRightmostOffset())
1584 return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
1586 VisiblePosition wordBreak;
1587 if (blockDirection == RTL) {
1588 if (box->direction() == blockDirection)
1589 wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
1591 wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
1593 if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
1596 WordBoundaryVector orderedWordBoundaries;
1597 collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
1599 int index = box->isLeftToRightDirection() ? smallestOffsetAbove(offset, blockDirection == LTR, orderedWordBoundaries)
1600 : greatestOffsetUnder(offset, blockDirection == RTL, orderedWordBoundaries);
1602 return orderedWordBoundaries[index].visiblePosition;
1604 return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
1607 VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition)
1609 if (visiblePosition.isNull())
1610 return VisiblePosition();
1612 VisiblePosition leftWordBreak = leftWordPositionIgnoringEditingBoundary(visiblePosition);
1613 leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
1615 // FIXME: How should we handle a non-editable position?
1616 if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
1617 TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
1618 leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition);
1620 return leftWordBreak;
1623 VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition)
1625 if (visiblePosition.isNull())
1626 return VisiblePosition();
1628 VisiblePosition rightWordBreak = rightWordPositionIgnoringEditingBoundary(visiblePosition);
1629 rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);
1631 // FIXME: How should we handle a non-editable position?
1632 if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
1633 TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
1634 rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition);
1636 return rightWordBreak;