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 static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c)
323 // return table offset 0 instead of the first VisiblePosition inside the table
324 VisiblePosition previous = c.previous();
325 if (isLastPositionBeforeTable(previous) && isEditablePosition(previous.deepEquivalent()))
331 static VisiblePosition startPositionForLine(const VisiblePosition& c)
334 return VisiblePosition();
336 RootInlineBox* rootBox = RenderedPosition(c).rootBox();
338 // There are VisiblePositions at offset 0 in blocks without
339 // RootInlineBoxes, like empty editable blocks and bordered blocks.
340 Position p = c.deepEquivalent();
341 if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
342 return positionAvoidingFirstPositionInTable(c);
344 return VisiblePosition();
347 // Generated content (e.g. list markers and CSS :before and :after
348 // pseudoelements) have no corresponding DOM element, and so cannot be
349 // represented by a VisiblePosition. Use whatever follows instead.
350 InlineBox *startBox = rootBox->firstLeafChild();
354 return VisiblePosition();
356 RenderObject *startRenderer = startBox->renderer();
358 return VisiblePosition();
360 startNode = startRenderer->node();
364 startBox = startBox->nextLeafChild();
367 VisiblePosition visPos = startNode->isTextNode() ? VisiblePosition(Position(static_cast<Text*>(startNode), static_cast<InlineTextBox*>(startBox)->start()), DOWNSTREAM)
368 : VisiblePosition(positionBeforeNode(startNode), DOWNSTREAM);
369 return positionAvoidingFirstPositionInTable(visPos);
372 VisiblePosition startOfLine(const VisiblePosition& c)
374 VisiblePosition visPos = startPositionForLine(c);
376 return c.honorEditingBoundaryAtOrBefore(visPos);
379 static VisiblePosition endPositionForLine(const VisiblePosition& c)
382 return VisiblePosition();
384 RootInlineBox* rootBox = RenderedPosition(c).rootBox();
386 // There are VisiblePositions at offset 0 in blocks without
387 // RootInlineBoxes, like empty editable blocks and bordered blocks.
388 Position p = c.deepEquivalent();
389 if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
391 return VisiblePosition();
394 // Generated content (e.g. list markers and CSS :before and :after
395 // pseudoelements) have no corresponding DOM element, and so cannot be
396 // represented by a VisiblePosition. Use whatever precedes instead.
398 InlineBox *endBox = rootBox->lastLeafChild();
401 return VisiblePosition();
403 RenderObject *endRenderer = endBox->renderer();
405 return VisiblePosition();
407 endNode = endRenderer->node();
411 endBox = endBox->prevLeafChild();
415 if (endNode->hasTagName(brTag)) {
416 pos = positionBeforeNode(endNode);
417 } else if (endBox->isInlineTextBox() && endNode->isTextNode()) {
418 InlineTextBox* endTextBox = static_cast<InlineTextBox *>(endBox);
419 int endOffset = endTextBox->start();
420 if (!endTextBox->isLineBreak())
421 endOffset += endTextBox->len();
422 pos = Position(static_cast<Text*>(endNode), endOffset);
424 pos = positionAfterNode(endNode);
426 return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
429 VisiblePosition endOfLine(const VisiblePosition& c)
431 VisiblePosition visPos = endPositionForLine(c);
433 // Make sure the end of line is at the same line as the given input position. Else use the previous position to
434 // obtain end of line. This condition happens when the input position is before the space character at the end
435 // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
436 // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
437 // versus lines without that style, which would break before a space by default.
438 if (!inSameLine(c, visPos)) {
439 visPos = c.previous();
441 return VisiblePosition();
442 visPos = endPositionForLine(visPos);
445 return c.honorEditingBoundaryAtOrAfter(visPos);
448 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
450 return a.isNotNull() && startOfLine(a) == startOfLine(b);
453 bool isStartOfLine(const VisiblePosition &p)
455 return p.isNotNull() && p == startOfLine(p);
458 bool isEndOfLine(const VisiblePosition &p)
460 return p.isNotNull() && p == endOfLine(p);
463 // The first leaf before node that has the same editability as node.
464 static Node* previousLeafWithSameEditability(Node* node)
466 bool editable = node->rendererIsEditable();
467 Node* n = node->previousLeafNode();
469 if (editable == n->rendererIsEditable())
471 n = n->previousLeafNode();
476 static Node* enclosingNodeWithNonInlineRenderer(Node* n)
478 for (Node* p = n; p; p = p->parentNode()) {
479 if (p->renderer() && !p->renderer()->isInline())
485 static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint)
488 RenderBlock* containingBlock = root->block();
489 FloatPoint absoluteBlockPoint = containingBlock->localToAbsolute(FloatPoint());
490 if (containingBlock->hasOverflowClip())
491 absoluteBlockPoint -= containingBlock->layer()->scrolledContentOffset();
493 if (root->block()->isHorizontalWritingMode())
494 return IntPoint(lineDirectionPoint - absoluteBlockPoint.x(), root->blockDirectionPointInLine());
496 return IntPoint(root->selectionTop(), lineDirectionPoint - absoluteBlockPoint.y());
499 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint)
501 Position p = visiblePosition.deepEquivalent();
502 Node* node = p.deprecatedNode();
503 Node* highestRoot = highestEditableRoot(p);
505 return VisiblePosition();
507 node->document()->updateLayoutIgnorePendingStylesheets();
509 RenderObject *renderer = node->renderer();
511 return VisiblePosition();
513 RootInlineBox *root = 0;
515 int ignoredCaretOffset;
516 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
518 root = box->root()->prevRootBox();
519 // We want to skip zero height boxes.
520 // This could happen in case it is a TrailingFloatsRootInlineBox.
521 if (!root || !root->logicalHeight())
526 // This containing editable block does not have a previous line.
527 // Need to move back to previous containing editable block in this root editable
528 // block and find the last root line box in that block.
529 Node* startBlock = enclosingNodeWithNonInlineRenderer(node);
530 Node* n = previousLeafWithSameEditability(node);
531 while (n && startBlock == enclosingNodeWithNonInlineRenderer(n))
532 n = previousLeafWithSameEditability(n);
534 if (highestEditableRoot(firstPositionInOrBeforeNode(n)) != highestRoot)
536 Position pos = n->hasTagName(brTag) ? positionBeforeNode(n) : createLegacyEditingPosition(n, caretMaxOffset(n));
537 if (pos.isCandidate()) {
538 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
540 // previous root line box found
545 return VisiblePosition(pos, DOWNSTREAM);
547 n = previousLeafWithSameEditability(n);
552 // FIXME: Can be wrong for multi-column layout and with transforms.
553 IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
554 RenderObject* renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
555 Node* node = renderer->node();
556 if (node && editingIgnoresContent(node))
557 return positionInParentBeforeNode(node);
558 return renderer->positionForPoint(pointInLine);
561 // Could not find a previous line. This means we must already be on the first line.
562 // Move to the start of the content in this block, which effectively moves us
563 // to the start of the line we're on.
564 Element* rootElement = node->rendererIsEditable() ? node->rootEditableElement() : node->document()->documentElement();
566 return VisiblePosition();
567 return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM);
570 static Node* nextLeafWithSameEditability(Node* node, int offset)
572 bool editable = node->rendererIsEditable();
574 Node* child = node->childNode(offset);
575 Node* n = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode();
577 if (editable == n->rendererIsEditable())
579 n = n->nextLeafNode();
584 static Node* nextLeafWithSameEditability(Node* node)
589 bool editable = node->rendererIsEditable();
590 Node* n = node->nextLeafNode();
592 if (editable == n->rendererIsEditable())
594 n = n->nextLeafNode();
599 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint)
601 Position p = visiblePosition.deepEquivalent();
602 Node* node = p.deprecatedNode();
603 Node* highestRoot = highestEditableRoot(p);
605 return VisiblePosition();
607 node->document()->updateLayoutIgnorePendingStylesheets();
609 RenderObject *renderer = node->renderer();
611 return VisiblePosition();
613 RootInlineBox *root = 0;
615 int ignoredCaretOffset;
616 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
618 root = box->root()->nextRootBox();
619 // We want to skip zero height boxes.
620 // This could happen in case it is a TrailingFloatsRootInlineBox.
621 if (!root || !root->logicalHeight())
626 // This containing editable block does not have a next line.
627 // Need to move forward to next containing editable block in this root editable
628 // block and find the first root line box in that block.
629 Node* startBlock = enclosingNodeWithNonInlineRenderer(node);
630 Node* n = nextLeafWithSameEditability(node, p.deprecatedEditingOffset());
631 while (n && startBlock == enclosingNodeWithNonInlineRenderer(n))
632 n = nextLeafWithSameEditability(n);
634 if (highestEditableRoot(firstPositionInOrBeforeNode(n)) != highestRoot)
636 Position pos = createLegacyEditingPosition(n, caretMinOffset(n));
637 if (pos.isCandidate()) {
638 ASSERT(n->renderer());
639 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
641 // next root line box found
646 return VisiblePosition(pos, DOWNSTREAM);
648 n = nextLeafWithSameEditability(n);
653 // FIXME: Can be wrong for multi-column layout and with transforms.
654 IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
655 RenderObject* renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
656 Node* node = renderer->node();
657 if (node && editingIgnoresContent(node))
658 return positionInParentBeforeNode(node);
659 return renderer->positionForPoint(pointInLine);
662 // Could not find a next line. This means we must already be on the last line.
663 // Move to the end of the content in this block, which effectively moves us
664 // to the end of the line we're on.
665 Element* rootElement = node->rendererIsEditable() ? node->rootEditableElement() : node->document()->documentElement();
667 return VisiblePosition();
668 return VisiblePosition(lastPositionInNode(rootElement), DOWNSTREAM);
673 static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
675 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
676 // FIXME: The following function can return -1; we don't handle that.
677 return textBreakPreceding(iterator, length);
680 VisiblePosition startOfSentence(const VisiblePosition &c)
682 return previousBoundary(c, startSentenceBoundary);
685 static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
687 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
688 return textBreakNext(iterator);
691 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
692 VisiblePosition endOfSentence(const VisiblePosition &c)
694 return nextBoundary(c, endSentenceBoundary);
697 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
699 // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
700 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
701 // FIXME: The following function can return -1; we don't handle that.
702 return textBreakPreceding(iterator, length);
705 VisiblePosition previousSentencePosition(const VisiblePosition &c)
707 VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
708 return c.honorEditingBoundaryAtOrBefore(prev);
711 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
713 // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
714 // move to the equivlant position in the following sentence.
715 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
716 return textBreakFollowing(iterator, 0);
719 VisiblePosition nextSentencePosition(const VisiblePosition &c)
721 VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
722 return c.honorEditingBoundaryAtOrAfter(next);
725 VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
727 Position p = c.deepEquivalent();
728 Node* startNode = p.deprecatedNode();
731 return VisiblePosition();
733 if (isRenderedAsNonInlineTableImageOrHR(startNode))
734 return positionBeforeNode(startNode);
736 Node* startBlock = enclosingBlock(startNode);
738 Node* node = startNode;
739 Node* highestRoot = highestEditableRoot(p);
740 int offset = p.deprecatedEditingOffset();
741 Position::AnchorType type = p.anchorType();
745 if (boundaryCrossingRule == CannotCrossEditingBoundary && n->rendererIsEditable() != startNode->rendererIsEditable())
747 if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
748 while (n && n->rendererIsEditable() != startNode->rendererIsEditable())
749 n = n->traversePreviousNodePostOrder(startBlock);
750 if (!n || !n->isDescendantOf(highestRoot))
753 RenderObject *r = n->renderer();
755 n = n->traversePreviousNodePostOrder(startBlock);
758 RenderStyle *style = r->style();
759 if (style->visibility() != VISIBLE) {
760 n = n->traversePreviousNodePostOrder(startBlock);
764 if (r->isBR() || isBlock(n))
767 if (r->isText() && r->caretMaxRenderedOffset() > 0) {
768 ASSERT(n->isTextNode());
769 type = Position::PositionIsOffsetInAnchor;
770 if (style->preserveNewline()) {
771 const UChar* chars = toRenderText(r)->characters();
772 int i = toRenderText(r)->textLength();
774 if (n == startNode && o < i)
777 if (chars[i] == '\n')
778 return VisiblePosition(Position(static_cast<Text*>(n), i + 1), DOWNSTREAM);
783 n = n->traversePreviousNodePostOrder(startBlock);
784 } else if (editingIgnoresContent(n) || isTableElement(n)) {
786 type = Position::PositionIsBeforeAnchor;
787 n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock);
789 n = n->traversePreviousNodePostOrder(startBlock);
792 if (type == Position::PositionIsOffsetInAnchor) {
793 ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
794 return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
797 return VisiblePosition(Position(node, type), DOWNSTREAM);
800 VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossingRule boundaryCrossingRule)
803 return VisiblePosition();
805 Position p = c.deepEquivalent();
806 Node* startNode = p.deprecatedNode();
808 if (isRenderedAsNonInlineTableImageOrHR(startNode))
809 return positionAfterNode(startNode);
811 Node* startBlock = enclosingBlock(startNode);
812 Node* stayInsideBlock = startBlock;
814 Node* node = startNode;
815 Node* highestRoot = highestEditableRoot(p);
816 int offset = p.deprecatedEditingOffset();
817 Position::AnchorType type = p.anchorType();
821 if (boundaryCrossingRule == CannotCrossEditingBoundary && n->rendererIsEditable() != startNode->rendererIsEditable())
823 if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
824 while (n && n->rendererIsEditable() != startNode->rendererIsEditable())
825 n = n->traverseNextNode(stayInsideBlock);
826 if (!n || !n->isDescendantOf(highestRoot))
830 RenderObject *r = n->renderer();
832 n = n->traverseNextNode(stayInsideBlock);
835 RenderStyle *style = r->style();
836 if (style->visibility() != VISIBLE) {
837 n = n->traverseNextNode(stayInsideBlock);
841 if (r->isBR() || isBlock(n))
844 // FIXME: We avoid returning a position where the renderer can't accept the caret.
845 if (r->isText() && r->caretMaxRenderedOffset() > 0) {
846 ASSERT(n->isTextNode());
847 int length = toRenderText(r)->textLength();
848 type = Position::PositionIsOffsetInAnchor;
849 if (style->preserveNewline()) {
850 const UChar* chars = toRenderText(r)->characters();
851 int o = n == startNode ? offset : 0;
852 for (int i = o; i < length; ++i) {
853 if (chars[i] == '\n')
854 return VisiblePosition(Position(static_cast<Text*>(n), i), DOWNSTREAM);
858 offset = r->caretMaxOffset();
859 n = n->traverseNextNode(stayInsideBlock);
860 } else if (editingIgnoresContent(n) || isTableElement(n)) {
862 type = Position::PositionIsAfterAnchor;
863 n = n->traverseNextSibling(stayInsideBlock);
865 n = n->traverseNextNode(stayInsideBlock);
868 if (type == Position::PositionIsOffsetInAnchor)
869 return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
871 return VisiblePosition(Position(node, type), DOWNSTREAM);
874 // FIXME: isStartOfParagraph(startOfNextParagraph(pos)) is not always true
875 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
877 VisiblePosition paragraphEnd(endOfParagraph(visiblePosition, CanSkipOverEditingBoundary));
878 VisiblePosition afterParagraphEnd(paragraphEnd.next(CannotCrossEditingBoundary));
879 // The position after the last position in the last cell of a table
880 // is not the start of the next paragraph.
881 if (isFirstPositionAfterTable(afterParagraphEnd))
882 return afterParagraphEnd.next(CannotCrossEditingBoundary);
883 return afterParagraphEnd;
886 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b, EditingBoundaryCrossingRule boundaryCrossingRule)
888 return a.isNotNull() && startOfParagraph(a, boundaryCrossingRule) == startOfParagraph(b, boundaryCrossingRule);
891 bool isStartOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
893 return pos.isNotNull() && pos == startOfParagraph(pos, boundaryCrossingRule);
896 bool isEndOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
898 return pos.isNotNull() && pos == endOfParagraph(pos, boundaryCrossingRule);
901 VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x)
903 VisiblePosition pos = p;
905 VisiblePosition n = previousLinePosition(pos, x);
906 if (n.isNull() || n == pos)
909 } while (inSameParagraph(p, pos));
913 VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x)
915 VisiblePosition pos = p;
917 VisiblePosition n = nextLinePosition(pos, x);
918 if (n.isNull() || n == pos)
921 } while (inSameParagraph(p, pos));
927 VisiblePosition startOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
929 Position position = visiblePosition.deepEquivalent();
931 if (!position.containerNode() || !(startBlock = enclosingBlock(position.containerNode(), rule)))
932 return VisiblePosition();
933 return firstPositionInNode(startBlock);
936 VisiblePosition endOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
938 Position position = visiblePosition.deepEquivalent();
940 if (!position.containerNode() || !(endBlock = enclosingBlock(position.containerNode(), rule)))
941 return VisiblePosition();
942 return lastPositionInNode(endBlock);
945 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
947 return !a.isNull() && enclosingBlock(a.deepEquivalent().containerNode()) == enclosingBlock(b.deepEquivalent().containerNode());
950 bool isStartOfBlock(const VisiblePosition &pos)
952 return pos.isNotNull() && pos == startOfBlock(pos, CanCrossEditingBoundary);
955 bool isEndOfBlock(const VisiblePosition &pos)
957 return pos.isNotNull() && pos == endOfBlock(pos, CanCrossEditingBoundary);
962 VisiblePosition startOfDocument(const Node* node)
964 if (!node || !node->document() || !node->document()->documentElement())
965 return VisiblePosition();
967 return VisiblePosition(firstPositionInNode(node->document()->documentElement()), DOWNSTREAM);
970 VisiblePosition startOfDocument(const VisiblePosition &c)
972 return startOfDocument(c.deepEquivalent().deprecatedNode());
975 VisiblePosition endOfDocument(const Node* node)
977 if (!node || !node->document() || !node->document()->documentElement())
978 return VisiblePosition();
980 Element* doc = node->document()->documentElement();
981 return VisiblePosition(lastPositionInNode(doc), DOWNSTREAM);
984 VisiblePosition endOfDocument(const VisiblePosition &c)
986 return endOfDocument(c.deepEquivalent().deprecatedNode());
989 bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
991 Position ap = a.deepEquivalent();
992 Node* an = ap.deprecatedNode();
995 Position bp = b.deepEquivalent();
996 Node* bn = bp.deprecatedNode();
1000 return an->document() == bn->document();
1003 bool isStartOfDocument(const VisiblePosition &p)
1005 return p.isNotNull() && p.previous().isNull();
1008 bool isEndOfDocument(const VisiblePosition &p)
1010 return p.isNotNull() && p.next().isNull();
1015 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
1017 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1019 return VisiblePosition();
1021 return firstPositionInNode(highestRoot);
1024 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
1026 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1028 return VisiblePosition();
1030 return lastPositionInNode(highestRoot);
1033 static VisiblePosition logicalStartPositionForLine(const VisiblePosition& c)
1036 return VisiblePosition();
1038 RootInlineBox* rootBox = RenderedPosition(c).rootBox();
1040 // There are VisiblePositions at offset 0 in blocks without
1041 // RootInlineBoxes, like empty editable blocks and bordered blocks.
1042 Position p = c.deepEquivalent();
1043 if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
1044 return positionAvoidingFirstPositionInTable(c);
1046 return VisiblePosition();
1049 InlineBox* logicalStartBox;
1050 Node* logicalStartNode = rootBox->getLogicalStartBoxWithNode(logicalStartBox);
1052 if (!logicalStartNode)
1053 return VisiblePosition();
1055 VisiblePosition visPos = logicalStartNode->isTextNode() ? VisiblePosition(Position(logicalStartNode, logicalStartBox->caretMinOffset(), Position::PositionIsOffsetInAnchor), DOWNSTREAM)
1056 : VisiblePosition(positionBeforeNode(logicalStartNode), DOWNSTREAM);
1057 return positionAvoidingFirstPositionInTable(visPos);
1060 VisiblePosition logicalStartOfLine(const VisiblePosition& c)
1062 // TODO: this is the current behavior that might need to be fixed.
1063 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
1064 VisiblePosition visPos = logicalStartPositionForLine(c);
1066 if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
1067 if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
1068 return firstPositionInNode(editableRoot);
1070 return c.honorEditingBoundaryAtOrBefore(visPos);
1073 static VisiblePosition logicalEndPositionForLine(const VisiblePosition& c)
1076 return VisiblePosition();
1078 RootInlineBox* rootBox = RenderedPosition(c).rootBox();
1080 // There are VisiblePositions at offset 0 in blocks without
1081 // RootInlineBoxes, like empty editable blocks and bordered blocks.
1082 Position p = c.deepEquivalent();
1083 if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
1085 return VisiblePosition();
1088 InlineBox* logicalEndBox;
1089 Node* logicalEndNode = rootBox->getLogicalEndBoxWithNode(logicalEndBox);
1091 if (!logicalEndNode)
1092 return VisiblePosition();
1095 if (logicalEndNode->hasTagName(brTag))
1096 pos = positionBeforeNode(logicalEndNode);
1097 else if (logicalEndBox->isInlineTextBox()) {
1098 InlineTextBox* endTextBox = static_cast<InlineTextBox*>(logicalEndBox);
1099 int endOffset = endTextBox->start();
1100 if (!endTextBox->isLineBreak())
1101 endOffset += endTextBox->len();
1102 pos = Position(logicalEndNode, endOffset, Position::PositionIsOffsetInAnchor);
1104 pos = positionAfterNode(logicalEndNode);
1106 return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
1109 bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b)
1111 return a.isNotNull() && logicalStartOfLine(a) == logicalStartOfLine(b);
1114 VisiblePosition logicalEndOfLine(const VisiblePosition& c)
1116 // TODO: this is the current behavior that might need to be fixed.
1117 // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
1119 VisiblePosition visPos = logicalEndPositionForLine(c);
1121 // Make sure the end of line is at the same line as the given input position. For a wrapping line, the logical end
1122 // position for the not-last-2-lines might incorrectly hand back the logical beginning of the next line.
1123 // For example, <div contenteditable dir="rtl" style="line-break:before-white-space">abcdefg abcdefg abcdefg
1124 // a abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg </div>
1125 // In this case, use the previous position of the computed logical end position.
1126 if (!inSameLogicalLine(c, visPos))
1127 visPos = visPos.previous();
1129 if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
1130 if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
1131 return lastPositionInNode(editableRoot);
1133 return c.honorEditingBoundaryAtOrAfter(visPos);
1136 VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1138 return direction == LTR ? logicalStartOfLine(c) : logicalEndOfLine(c);
1141 VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1143 return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
1146 static const int invalidOffset = -1;
1147 static const int offsetNotFound = -1;
1149 static bool isBoxVisuallyLastInLine(const InlineBox* box, TextDirection blockDirection)
1151 return blockDirection == LTR ? !box->nextLeafChild() || box->nextLeafChild()->renderer()->isBR()
1152 : !box->prevLeafChild() || box->prevLeafChild()->renderer()->isBR();
1155 static bool positionIsInBox(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
1157 if (wordBreak.isNull())
1160 InlineBox* boxOfWordBreak;
1161 wordBreak.getInlineBoxAndOffset(boxOfWordBreak, offsetOfWordBreak);
1162 return box == boxOfWordBreak;
1165 static VisiblePosition previousWordBreakInBoxInsideBlockWithSameDirectionality(const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak)
1167 // In a LTR block, the word break should be on the left boundary of a word.
1168 // In a RTL block, the word break should be on the right boundary of a word.
1169 // Because nextWordPosition() returns the word break on the right boundary of the word for LTR text,
1170 // we need to use previousWordPosition() to traverse words within the inline boxes from right to left
1171 // to find the previous word break (i.e. the first word break on the left). The same applies to RTL text.
1173 bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
1175 VisiblePosition wordBreak;
1177 if (hasSeenWordBreakInThisBox)
1178 wordBreak = previousWordBreak;
1180 wordBreak = createLegacyEditingPosition(box->renderer()->node(), box->caretMaxOffset());
1182 // Return the rightmost word boundary of LTR box or leftmost word boundary of RTL box if
1183 // it is not in the previously visited boxes. For example, given a logical text
1184 // "abc def hij opq", there are 2 boxes: the "abc def " (starts at 0 and length is 8)
1185 // and the "hij opq" (starts at 12 and length is 7). The word breaks are
1186 // "abc |def | hij |opq". We normally catch the word break between "def" and "hij" when
1187 // we visit the box that contains "hij opq", but this word break doesn't exist in the box
1188 // that contains "hij opq" when there are multiple spaces. So we detect it when we're
1189 // traversing the box that contains "abc def " instead.
1191 if ((box->isLeftToRightDirection() && box->nextLeafChild())
1192 || (!box->isLeftToRightDirection() && box->prevLeafChild())) {
1194 VisiblePosition positionAfterWord = nextBoundary(wordBreak, nextWordPositionBoundary);
1195 if (positionAfterWord.isNotNull()) {
1196 VisiblePosition positionBeforeWord = previousBoundary(positionAfterWord, previousWordPositionBoundary);
1198 if (positionIsInBox(positionBeforeWord, box, offsetOfWordBreak))
1199 return positionBeforeWord;
1204 wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
1205 if (previousWordBreak == wordBreak)
1206 return VisiblePosition();
1208 return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
1211 static VisiblePosition leftmostPositionInRTLBoxInLTRBlock(const InlineBox* box)
1213 // FIXME: Probably need to take care of bidi level too.
1214 Node* node = box->renderer()->node();
1215 InlineBox* previousLeaf = box->prevLeafChild();
1216 InlineBox* nextLeaf = box->nextLeafChild();
1218 if (previousLeaf && !previousLeaf->isLeftToRightDirection())
1219 return createLegacyEditingPosition(node, box->caretMaxOffset());
1221 if (nextLeaf && !nextLeaf->isLeftToRightDirection()) {
1223 return createLegacyEditingPosition(previousLeaf->renderer()->node(), previousLeaf->caretMaxOffset());
1225 InlineBox* lastRTLLeaf;
1227 lastRTLLeaf = nextLeaf;
1228 nextLeaf = nextLeaf->nextLeafChild();
1229 } while (nextLeaf && !nextLeaf->isLeftToRightDirection());
1230 return createLegacyEditingPosition(lastRTLLeaf->renderer()->node(), lastRTLLeaf->caretMinOffset());
1233 return createLegacyEditingPosition(node, box->caretMinOffset());
1236 static VisiblePosition rightmostPositionInLTRBoxInRTLBlock(const InlineBox* box)
1238 // FIXME: Probably need to take care of bidi level too.
1239 Node* node = box->renderer()->node();
1240 InlineBox* previousLeaf = box->prevLeafChild();
1241 InlineBox* nextLeaf = box->nextLeafChild();
1243 if (nextLeaf && nextLeaf->isLeftToRightDirection())
1244 return createLegacyEditingPosition(node, box->caretMaxOffset());
1246 if (previousLeaf && previousLeaf->isLeftToRightDirection()) {
1248 return createLegacyEditingPosition(nextLeaf->renderer()->node(), nextLeaf->caretMaxOffset());
1250 InlineBox* firstLTRLeaf;
1252 firstLTRLeaf = previousLeaf;
1253 previousLeaf = previousLeaf->prevLeafChild();
1254 } while (previousLeaf && previousLeaf->isLeftToRightDirection());
1255 return createLegacyEditingPosition(firstLTRLeaf->renderer()->node(), firstLTRLeaf->caretMinOffset());
1258 return createLegacyEditingPosition(node, box->caretMinOffset());
1261 static VisiblePosition lastWordBreakInBox(const InlineBox* box, int& offsetOfWordBreak)
1263 // Add the leftmost word break for RTL box or rightmost word break for LTR box.
1264 InlineBox* previousLeaf = box->prevLeafChild();
1265 InlineBox* nextLeaf = box->nextLeafChild();
1266 VisiblePosition boundaryPosition;
1267 if (box->direction() == RTL && (!previousLeaf || previousLeaf->isLeftToRightDirection()))
1268 boundaryPosition = leftmostPositionInRTLBoxInLTRBlock(box);
1269 else if (box->direction() == LTR && (!nextLeaf || !nextLeaf->isLeftToRightDirection()))
1270 boundaryPosition = rightmostPositionInLTRBoxInRTLBlock(box);
1272 if (boundaryPosition.isNull())
1273 return VisiblePosition();
1275 VisiblePosition wordBreak = nextBoundary(boundaryPosition, nextWordPositionBoundary);
1276 if (wordBreak.isNull())
1277 wordBreak = boundaryPosition;
1278 else if (wordBreak != boundaryPosition)
1279 wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
1281 return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
1284 static bool positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
1286 int previousOffset = offsetOfWordBreak;
1287 return positionIsInBox(wordBreak, box, offsetOfWordBreak)
1288 && (previousOffset == invalidOffset || previousOffset < offsetOfWordBreak);
1291 static VisiblePosition nextWordBreakInBoxInsideBlockWithDifferentDirectionality(
1292 const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak, bool& isLastWordBreakInBox)
1294 // FIXME: Probably need to take care of bidi level too.
1296 // In a LTR block, the word break should be on the left boundary of a word.
1297 // In a RTL block, the word break should be on the right boundary of a word.
1298 // Because previousWordPosition() returns the word break on the right boundary of the word for RTL text,
1299 // we need to use nextWordPosition() to traverse words within the inline boxes from right to left to find the next word break.
1300 // The same applies to LTR text, in which words are traversed within the inline boxes from left to right.
1302 bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
1303 VisiblePosition wordBreak = hasSeenWordBreakInThisBox ? previousWordBreak :
1304 createLegacyEditingPosition(box->renderer()->node(), box->caretMinOffset());
1306 wordBreak = nextBoundary(wordBreak, nextWordPositionBoundary);
1308 // Given RTL box "ABC DEF" either follows a LTR box or is the first visual box in an LTR block as an example,
1309 // 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)",
1310 // where the number in parenthesis represents offset in visiblePosition.
1311 // 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.
1312 // But nextWordPosition() of offset 7 is offset 11, which should be ignored,
1313 // and the position at offset 0 should be manually added as the last word break within the box.
1314 if (wordBreak != previousWordBreak && positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(wordBreak, box, offsetOfWordBreak)) {
1315 isLastWordBreakInBox = false;
1319 isLastWordBreakInBox = true;
1320 return lastWordBreakInBox(box, offsetOfWordBreak);
1323 struct WordBoundaryEntry {
1325 : offsetInInlineBox(invalidOffset)
1329 WordBoundaryEntry(const VisiblePosition& position, int offset)
1330 : visiblePosition(position)
1331 , offsetInInlineBox(offset)
1335 VisiblePosition visiblePosition;
1336 int offsetInInlineBox;
1339 typedef Vector<WordBoundaryEntry, 50> WordBoundaryVector;
1341 static void appendPositionAtLogicalEndOfLine(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
1343 VisiblePosition endOfBlock = logicalEndOfLine(createLegacyEditingPosition(box->renderer()->node(), box->caretMaxOffset()));
1345 int offsetOfEndOfBlock;
1346 if (positionIsInBox(endOfBlock, box, offsetOfEndOfBlock))
1347 orderedWordBoundaries.append(WordBoundaryEntry(endOfBlock, offsetOfEndOfBlock));
1350 static void collectWordBreaksInBoxInsideBlockWithSameDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
1352 orderedWordBoundaries.clear();
1354 if (!box->renderer()->isBR() && isBoxVisuallyLastInLine(box, box->direction()))
1355 appendPositionAtLogicalEndOfLine(box, orderedWordBoundaries);
1357 VisiblePosition wordBreak;
1358 int offsetOfWordBreak = invalidOffset;
1360 wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
1361 if (wordBreak.isNull())
1363 WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
1364 orderedWordBoundaries.append(wordBoundaryEntry);
1368 static void collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
1370 orderedWordBoundaries.clear();
1372 if (!box->renderer()->isBR() && isBoxVisuallyLastInLine(box, box->direction() == LTR ? RTL : LTR))
1373 appendPositionAtLogicalEndOfLine(box, orderedWordBoundaries);
1375 VisiblePosition wordBreak;
1376 int offsetOfWordBreak = invalidOffset;
1377 bool isLastWordBreakInBox = false;
1379 wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
1380 if (wordBreak.isNotNull()) {
1381 WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
1382 orderedWordBoundaries.append(wordBoundaryEntry);
1384 if (isLastWordBreakInBox)
1389 static void collectWordBreaksInBox(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries, TextDirection blockDirection)
1391 if (box->direction() == blockDirection)
1392 collectWordBreaksInBoxInsideBlockWithSameDirectionality(box, orderedWordBoundaries);
1394 collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(box, orderedWordBoundaries);
1397 static VisiblePosition previousWordBoundaryInBox(const InlineBox* box, int offset)
1399 int offsetOfWordBreak = 0;
1400 VisiblePosition wordBreak;
1402 wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
1403 if (wordBreak.isNull())
1405 if (offset == invalidOffset || offsetOfWordBreak != offset)
1408 return VisiblePosition();
1411 static VisiblePosition nextWordBoundaryInBox(const InlineBox* box, int offset)
1413 int offsetOfWordBreak = 0;
1414 VisiblePosition wordBreak;
1415 bool isLastWordBreakInBox = false;
1417 wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
1418 if (wordBreak.isNotNull() && (offset == invalidOffset || offsetOfWordBreak != offset))
1420 } while (!isLastWordBreakInBox);
1421 return VisiblePosition();
1424 static VisiblePosition visuallyLastWordBoundaryInBox(const InlineBox* box, int offset, TextDirection blockDirection)
1426 WordBoundaryVector orderedWordBoundaries;
1427 collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
1428 if (!orderedWordBoundaries.size())
1429 return VisiblePosition();
1430 if (offset == invalidOffset || orderedWordBoundaries[orderedWordBoundaries.size() - 1].offsetInInlineBox != offset)
1431 return orderedWordBoundaries[orderedWordBoundaries.size() - 1].visiblePosition;
1432 if (orderedWordBoundaries.size() > 1)
1433 return orderedWordBoundaries[orderedWordBoundaries.size() - 2].visiblePosition;
1434 return VisiblePosition();
1437 static int greatestOffsetUnder(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
1439 if (!orderedWordBoundaries.size())
1440 return offsetNotFound;
1441 // FIXME: binary search.
1442 if (boxAndBlockAreInSameDirection) {
1443 for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
1444 if (orderedWordBoundaries[i].offsetInInlineBox < offset)
1447 return offsetNotFound;
1449 for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
1450 if (orderedWordBoundaries[i].offsetInInlineBox < offset)
1453 return offsetNotFound;
1456 static int smallestOffsetAbove(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
1458 if (!orderedWordBoundaries.size())
1459 return offsetNotFound;
1460 // FIXME: binary search.
1461 if (boxAndBlockAreInSameDirection) {
1462 for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
1463 if (orderedWordBoundaries[i].offsetInInlineBox > offset)
1466 return offsetNotFound;
1468 for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
1469 if (orderedWordBoundaries[i].offsetInInlineBox > offset)
1472 return offsetNotFound;
1475 static const InlineBox* leftInlineBox(const InlineBox* box, TextDirection blockDirection)
1477 if (box->prevLeafChild())
1478 return box->prevLeafChild();
1480 const RootInlineBox* rootBox = box->root();
1481 const bool isBlockLTR = blockDirection == LTR;
1482 const InlineFlowBox* leftLineBox = isBlockLTR ? rootBox->prevLineBox() : rootBox->nextLineBox();
1484 return leftLineBox->lastLeafChild();
1489 static const InlineBox* rightInlineBox(const InlineBox* box, TextDirection blockDirection)
1491 if (box->nextLeafChild())
1492 return box->nextLeafChild();
1494 const RootInlineBox* rootBox = box->root();
1495 const bool isBlockLTR = blockDirection == LTR;
1496 const InlineFlowBox* rightLineBox = isBlockLTR ? rootBox->nextLineBox() : rootBox->prevLineBox();
1498 return rightLineBox->firstLeafChild();
1503 static VisiblePosition leftWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
1505 VisiblePosition wordBreak;
1506 for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = leftInlineBox(adjacentBox, blockDirection)) {
1507 if (blockDirection == LTR) {
1508 if (adjacentBox->isLeftToRightDirection())
1509 wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1511 wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1513 wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
1514 if (wordBreak.isNotNull())
1517 return VisiblePosition();
1520 static VisiblePosition rightWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
1523 VisiblePosition wordBreak;
1524 for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = rightInlineBox(adjacentBox, blockDirection)) {
1525 if (blockDirection == RTL) {
1526 if (adjacentBox->isLeftToRightDirection())
1527 wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1529 wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
1531 wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
1532 if (!wordBreak.isNull())
1535 return VisiblePosition();
1538 static bool positionIsInBoxButNotOnBoundary(const VisiblePosition& wordBreak, const InlineBox* box)
1540 int offsetOfWordBreak;
1541 return positionIsInBox(wordBreak, box, offsetOfWordBreak)
1542 && offsetOfWordBreak != box->caretMaxOffset() && offsetOfWordBreak != box->caretMinOffset();
1545 static VisiblePosition leftWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
1549 visiblePosition.getInlineBoxAndOffset(box, offset);
1552 return VisiblePosition();
1554 TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
1556 // FIXME: If the box's directionality is the same as that of the enclosing block, when the offset is at the box boundary
1557 // 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,
1558 // when offset is at box's caretMinOffset and the direction is DirectionRight, should it be taken care as a general case?
1559 if (offset == box->caretLeftmostOffset())
1560 return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
1561 if (offset == box->caretRightmostOffset())
1562 return leftWordBoundary(box, offset, blockDirection);
1565 VisiblePosition wordBreak;
1566 if (blockDirection == LTR) {
1567 if (box->direction() == blockDirection)
1568 wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
1570 wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
1572 if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
1575 WordBoundaryVector orderedWordBoundaries;
1576 collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
1578 int index = box->isLeftToRightDirection() ? greatestOffsetUnder(offset, blockDirection == LTR, orderedWordBoundaries)
1579 : smallestOffsetAbove(offset, blockDirection == RTL, orderedWordBoundaries);
1581 return orderedWordBoundaries[index].visiblePosition;
1583 return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
1586 static VisiblePosition rightWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
1590 visiblePosition.getInlineBoxAndOffset(box, offset);
1593 return VisiblePosition();
1595 TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
1597 if (offset == box->caretLeftmostOffset())
1598 return rightWordBoundary(box, offset, blockDirection);
1599 if (offset == box->caretRightmostOffset())
1600 return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
1602 VisiblePosition wordBreak;
1603 if (blockDirection == RTL) {
1604 if (box->direction() == blockDirection)
1605 wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
1607 wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
1609 if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
1612 WordBoundaryVector orderedWordBoundaries;
1613 collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
1615 int index = box->isLeftToRightDirection() ? smallestOffsetAbove(offset, blockDirection == LTR, orderedWordBoundaries)
1616 : greatestOffsetUnder(offset, blockDirection == RTL, orderedWordBoundaries);
1618 return orderedWordBoundaries[index].visiblePosition;
1620 return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
1623 VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition)
1625 if (visiblePosition.isNull())
1626 return VisiblePosition();
1628 VisiblePosition leftWordBreak = leftWordPositionIgnoringEditingBoundary(visiblePosition);
1629 return visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
1632 VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition)
1634 if (visiblePosition.isNull())
1635 return VisiblePosition();
1637 VisiblePosition rightWordBreak = rightWordPositionIgnoringEditingBoundary(visiblePosition);
1638 return visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);