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 "RenderBlock.h"
33 #include "RenderLayer.h"
34 #include "TextBoundaries.h"
35 #include "TextBreakIterator.h"
36 #include "TextIterator.h"
37 #include "VisiblePosition.h"
38 #include "htmlediting.h"
39 #include <wtf/unicode/Unicode.h>
43 using namespace HTMLNames;
44 using namespace WTF::Unicode;
46 static int endOfFirstWordBoundaryContext(const UChar* characters, int length)
48 for (int i = 0; i < length; ) {
51 U16_NEXT(characters, i, length, ch);
52 if (!requiresContextForWordBoundary(ch))
58 static int startOfLastWordBoundaryContext(const UChar* characters, int length)
60 for (int i = length; i > 0; ) {
63 U16_PREV(characters, 0, i, ch);
64 if (!requiresContextForWordBoundary(ch))
70 enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
72 typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
74 static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
76 Position pos = c.deepEquivalent();
79 return VisiblePosition();
80 Document *d = n->document();
81 Node *de = d->documentElement();
83 return VisiblePosition();
84 Node *boundary = n->enclosingBlockFlowElement();
86 return VisiblePosition();
87 bool isContentEditable = boundary->isContentEditable();
88 while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
89 boundary = boundary->parentNode();
91 Position start = rangeCompliantEquivalent(Position(boundary, 0));
92 Position end = rangeCompliantEquivalent(pos);
93 RefPtr<Range> searchRange = Range::create(d);
95 Vector<UChar, 1024> string;
96 unsigned suffixLength = 0;
99 if (requiresContextForWordBoundary(c.characterBefore())) {
100 RefPtr<Range> forwardsScanRange(d->createRange());
101 forwardsScanRange->setEndAfter(boundary, ec);
102 forwardsScanRange->setStart(end.node(), end.m_offset, ec);
103 TextIterator forwardsIterator(forwardsScanRange.get());
104 while (!forwardsIterator.atEnd()) {
105 const UChar* characters = forwardsIterator.characters();
106 int length = forwardsIterator.length();
107 int i = endOfFirstWordBoundaryContext(characters, length);
108 string.append(characters, i);
112 forwardsIterator.advance();
116 searchRange->setStart(start.node(), start.m_offset, ec);
117 searchRange->setEnd(end.node(), end.m_offset, ec);
121 return VisiblePosition();
123 SimplifiedBackwardsTextIterator it(searchRange.get());
125 bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
126 bool needMoreContext = false;
127 while (!it.atEnd()) {
128 // iterate to get chunks until the searchFunction returns a non-zero value.
129 if (!inTextSecurityMode)
130 string.prepend(it.characters(), it.length());
132 // Treat bullets used in the text security mode as regular characters when looking for boundaries
133 String iteratorString(it.characters(), it.length());
134 iteratorString = iteratorString.impl()->secure('x');
135 string.prepend(iteratorString.characters(), iteratorString.length());
137 next = searchFunction(string.data(), string.size(), string.size() - suffixLength, MayHaveMoreContext, needMoreContext);
142 if (needMoreContext) {
143 // The last search returned the beginning of the buffer and asked for more context,
144 // but there is no earlier text. Force a search with what's available.
145 next = searchFunction(string.data(), string.size(), string.size() - suffixLength, DontHaveMoreContext, needMoreContext);
146 ASSERT(!needMoreContext);
149 if (it.atEnd() && next == 0) {
150 pos = it.range()->startPosition();
151 } else if (next != 0) {
152 Node *node = it.range()->startContainer(ec);
153 if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next))
154 // The next variable contains a usable index into a text node
155 pos = Position(node, next);
157 // Use the character iterator to translate the next value into a DOM position.
158 BackwardsCharacterIterator charIt(searchRange.get());
159 charIt.advance(string.size() - suffixLength - next);
160 pos = charIt.range()->endPosition();
164 return VisiblePosition(pos, DOWNSTREAM);
167 static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
169 Position pos = c.deepEquivalent();
170 Node *n = pos.node();
172 return VisiblePosition();
173 Document *d = n->document();
174 Node *de = d->documentElement();
176 return VisiblePosition();
177 Node *boundary = n->enclosingBlockFlowElement();
179 return VisiblePosition();
180 bool isContentEditable = boundary->isContentEditable();
181 while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
182 boundary = boundary->parentNode();
184 RefPtr<Range> searchRange(d->createRange());
185 Position start(rangeCompliantEquivalent(pos));
187 Vector<UChar, 1024> string;
188 unsigned prefixLength = 0;
190 ExceptionCode ec = 0;
191 if (requiresContextForWordBoundary(c.characterAfter())) {
192 RefPtr<Range> backwardsScanRange(d->createRange());
193 backwardsScanRange->setEnd(start.node(), start.m_offset, ec);
194 SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
195 while (!backwardsIterator.atEnd()) {
196 const UChar* characters = backwardsIterator.characters();
197 int length = backwardsIterator.length();
198 int i = startOfLastWordBoundaryContext(characters, length);
199 string.prepend(characters + i, length - i);
200 prefixLength += length - i;
203 backwardsIterator.advance();
207 searchRange->selectNodeContents(boundary, ec);
208 searchRange->setStart(start.node(), start.m_offset, ec);
209 TextIterator it(searchRange.get(), true);
211 bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
212 bool needMoreContext = false;
213 while (!it.atEnd()) {
214 // Keep asking the iterator for chunks until the search function
215 // returns an end value not equal to the length of the string passed to it.
216 if (!inTextSecurityMode)
217 string.append(it.characters(), it.length());
219 // Treat bullets used in the text security mode as regular characters when looking for boundaries
220 String iteratorString(it.characters(), it.length());
221 iteratorString = iteratorString.impl()->secure('x');
222 string.append(iteratorString.characters(), iteratorString.length());
224 next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
225 if (next != string.size())
229 if (needMoreContext) {
230 // The last search returned the end of the buffer and asked for more context,
231 // but there is no further text. Force a search with what's available.
232 next = searchFunction(string.data(), string.size(), prefixLength, DontHaveMoreContext, needMoreContext);
233 ASSERT(!needMoreContext);
236 if (it.atEnd() && next == string.size()) {
237 pos = it.range()->startPosition();
238 } else if (next != prefixLength) {
239 // Use the character iterator to translate the next value into a DOM position.
240 CharacterIterator charIt(searchRange.get(), true);
241 charIt.advance(next - prefixLength - 1);
242 pos = charIt.range()->endPosition();
244 if (*charIt.characters() == '\n') {
245 // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
246 VisiblePosition visPos = VisiblePosition(pos);
247 if (visPos == VisiblePosition(charIt.range()->startPosition()))
248 pos = visPos.next(true).deepEquivalent();
252 // generate VisiblePosition, use UPSTREAM affinity if possible
253 return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
258 static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
261 if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
262 needMoreContext = true;
265 needMoreContext = false;
267 findWordBoundary(characters, length, offset - 1, &start, &end);
271 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
273 // FIXME: This returns a null VP for c at the start of the document
274 // and side == LeftWordIfOnBoundary
275 VisiblePosition p = c;
276 if (side == RightWordIfOnBoundary) {
277 // at paragraph end, the startofWord is the current position
278 if (isEndOfParagraph(c))
285 return previousBoundary(p, startWordBoundary);
288 static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
290 ASSERT(offset <= length);
291 if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
292 needMoreContext = true;
295 needMoreContext = false;
297 findWordBoundary(characters, length, offset, &start, &end);
301 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
303 VisiblePosition p = c;
304 if (side == LeftWordIfOnBoundary) {
305 if (isStartOfParagraph(c))
311 } else if (isEndOfParagraph(c))
314 return nextBoundary(p, endWordBoundary);
317 static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
319 if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
320 needMoreContext = true;
323 needMoreContext = false;
324 return findNextWordFromIndex(characters, length, offset, false);
327 VisiblePosition previousWordPosition(const VisiblePosition &c)
329 VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
330 return c.honorEditableBoundaryAtOrAfter(prev);
333 static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
335 if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
336 needMoreContext = true;
339 needMoreContext = false;
340 return findNextWordFromIndex(characters, length, offset, true);
343 VisiblePosition nextWordPosition(const VisiblePosition &c)
345 VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
346 return c.honorEditableBoundaryAtOrBefore(next);
351 static RootInlineBox *rootBoxForLine(const VisiblePosition &c)
353 Position p = c.deepEquivalent();
354 Node *node = p.node();
358 RenderObject *renderer = node->renderer();
364 c.getInlineBoxAndOffset(box, offset);
366 return box ? box->root() : 0;
369 static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c)
371 // return table offset 0 instead of the first VisiblePosition inside the table
372 VisiblePosition previous = c.previous();
373 if (isLastPositionBeforeTable(previous))
379 static VisiblePosition startPositionForLine(const VisiblePosition& c)
382 return VisiblePosition();
384 RootInlineBox *rootBox = rootBoxForLine(c);
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.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.m_offset == 0)
390 return positionAvoidingFirstPositionInTable(c);
392 return VisiblePosition();
395 // Generated content (e.g. list markers and CSS :before and :after
396 // pseudoelements) have no corresponding DOM element, and so cannot be
397 // represented by a VisiblePosition. Use whatever follows instead.
398 InlineBox *startBox = rootBox->firstLeafChild();
402 return VisiblePosition();
404 RenderObject *startRenderer = startBox->renderer();
406 return VisiblePosition();
408 startNode = startRenderer->node();
412 startBox = startBox->nextLeafChild();
416 if (startBox->isInlineTextBox()) {
417 InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox);
418 startOffset = startTextBox->start();
421 VisiblePosition visPos = VisiblePosition(startNode, startOffset, DOWNSTREAM);
422 return positionAvoidingFirstPositionInTable(visPos);
425 VisiblePosition startOfLine(const VisiblePosition& c)
427 VisiblePosition visPos = startPositionForLine(c);
429 if (visPos.isNotNull()) {
430 // Make sure the start of line is not greater than the given input position. Else use the previous position to
431 // obtain start of line. This condition happens when the input position is before the space character at the end
432 // of a soft-wrapped non-editable line. In this scenario, startPositionForLine would incorrectly hand back a position
433 // greater than the input position. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space
434 // style versus lines without that style, which would break before a space by default.
435 Position p = visPos.deepEquivalent();
436 if (p.m_offset > c.deepEquivalent().m_offset && p.node()->isSameNode(c.deepEquivalent().node())) {
437 visPos = c.previous();
439 return VisiblePosition();
440 visPos = startPositionForLine(visPos);
444 return c.honorEditableBoundaryAtOrAfter(visPos);
447 static VisiblePosition endPositionForLine(const VisiblePosition& c)
450 return VisiblePosition();
452 RootInlineBox *rootBox = rootBoxForLine(c);
454 // There are VisiblePositions at offset 0 in blocks without
455 // RootInlineBoxes, like empty editable blocks and bordered blocks.
456 Position p = c.deepEquivalent();
457 if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.m_offset == 0)
459 return VisiblePosition();
462 // Generated content (e.g. list markers and CSS :before and :after
463 // pseudoelements) have no corresponding DOM element, and so cannot be
464 // represented by a VisiblePosition. Use whatever precedes instead.
466 InlineBox *endBox = rootBox->lastLeafChild();
469 return VisiblePosition();
471 RenderObject *endRenderer = endBox->renderer();
473 return VisiblePosition();
475 endNode = endRenderer->node();
479 endBox = endBox->prevLeafChild();
483 if (endNode->hasTagName(brTag)) {
485 } else if (endBox->isInlineTextBox()) {
486 InlineTextBox *endTextBox = static_cast<InlineTextBox *>(endBox);
487 endOffset = endTextBox->start();
488 if (!endTextBox->isLineBreak())
489 endOffset += endTextBox->len();
492 return VisiblePosition(endNode, endOffset, VP_UPSTREAM_IF_POSSIBLE);
495 VisiblePosition endOfLine(const VisiblePosition& c)
497 VisiblePosition visPos = endPositionForLine(c);
499 // Make sure the end of line is at the same line as the given input position. Else use the previous position to
500 // obtain end of line. This condition happens when the input position is before the space character at the end
501 // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
502 // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
503 // versus lines without that style, which would break before a space by default.
504 if (!inSameLine(c, visPos)) {
505 visPos = c.previous();
507 return VisiblePosition();
508 visPos = endPositionForLine(visPos);
511 return c.honorEditableBoundaryAtOrBefore(visPos);
514 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
516 return a.isNotNull() && startOfLine(a) == startOfLine(b);
519 bool isStartOfLine(const VisiblePosition &p)
521 return p.isNotNull() && p == startOfLine(p);
524 bool isEndOfLine(const VisiblePosition &p)
526 return p.isNotNull() && p == endOfLine(p);
529 // The first leaf before node that has the same editability as node.
530 static Node* previousLeafWithSameEditability(Node* node)
532 bool editable = node->isContentEditable();
533 Node* n = node->previousLeafNode();
535 if (editable == n->isContentEditable())
537 n = n->previousLeafNode();
542 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int x)
544 Position p = visiblePosition.deepEquivalent();
545 Node *node = p.node();
546 Node* highestRoot = highestEditableRoot(p);
548 return VisiblePosition();
550 node->document()->updateLayoutIgnorePendingStylesheets();
552 RenderObject *renderer = node->renderer();
554 return VisiblePosition();
556 RenderBlock *containingBlock = 0;
557 RootInlineBox *root = 0;
559 int ignoredCaretOffset;
560 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
562 root = box->root()->prevRootBox();
564 containingBlock = renderer->containingBlock();
568 // This containing editable block does not have a previous line.
569 // Need to move back to previous containing editable block in this root editable
570 // block and find the last root line box in that block.
571 Node* startBlock = enclosingBlock(node);
572 Node* n = previousLeafWithSameEditability(node);
573 while (n && startBlock == enclosingBlock(n))
574 n = previousLeafWithSameEditability(n);
576 if (highestEditableRoot(Position(n, 0)) != highestRoot)
578 Position pos(n, caretMinOffset(n));
579 if (pos.isCandidate()) {
580 ASSERT(n->renderer());
581 Position maxPos(n, caretMaxOffset(n));
582 maxPos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
584 // previous root line box found
586 containingBlock = n->renderer()->containingBlock();
590 return VisiblePosition(pos, DOWNSTREAM);
592 n = previousLeafWithSameEditability(n);
597 // FIXME: Can be wrong for multi-column layout and with transforms.
598 FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
599 if (containingBlock->hasOverflowClip())
600 absPos -= containingBlock->layer()->scrolledContentOffset();
601 RenderObject* renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->renderer();
602 Node* node = renderer->node();
603 if (node && editingIgnoresContent(node))
604 return Position(node->parent(), node->nodeIndex());
605 return renderer->positionForPoint(IntPoint(x - absPos.x(), root->topOverflow()));
608 // Could not find a previous line. This means we must already be on the first line.
609 // Move to the start of the content in this block, which effectively moves us
610 // to the start of the line we're on.
611 Element* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement();
612 return VisiblePosition(rootElement, 0, DOWNSTREAM);
615 static Node* nextLeafWithSameEditability(Node* node, int offset)
617 bool editable = node->isContentEditable();
619 Node* child = node->childNode(offset);
620 Node* n = child ? child->nextLeafNode() : node->nextLeafNode();
622 if (editable == n->isContentEditable())
624 n = n->nextLeafNode();
629 static Node* nextLeafWithSameEditability(Node* node)
634 bool editable = node->isContentEditable();
635 Node* n = node->nextLeafNode();
637 if (editable == n->isContentEditable())
639 n = n->nextLeafNode();
644 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
646 Position p = visiblePosition.deepEquivalent();
647 Node *node = p.node();
648 Node* highestRoot = highestEditableRoot(p);
650 return VisiblePosition();
652 node->document()->updateLayoutIgnorePendingStylesheets();
654 RenderObject *renderer = node->renderer();
656 return VisiblePosition();
658 RenderBlock *containingBlock = 0;
659 RootInlineBox *root = 0;
661 int ignoredCaretOffset;
662 visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
664 root = box->root()->nextRootBox();
666 containingBlock = renderer->containingBlock();
670 // This containing editable block does not have a next line.
671 // Need to move forward to next containing editable block in this root editable
672 // block and find the first root line box in that block.
673 Node* startBlock = enclosingBlock(node);
674 Node* n = nextLeafWithSameEditability(node, p.m_offset);
675 while (n && startBlock == enclosingBlock(n))
676 n = nextLeafWithSameEditability(n);
678 if (highestEditableRoot(Position(n, 0)) != highestRoot)
680 Position pos(n, caretMinOffset(n));
681 if (pos.isCandidate()) {
682 ASSERT(n->renderer());
683 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
685 // next root line box found
687 containingBlock = n->renderer()->containingBlock();
691 return VisiblePosition(pos, DOWNSTREAM);
693 n = nextLeafWithSameEditability(n);
698 // FIXME: Can be wrong for multi-column layout and with transforms.
699 FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
700 if (containingBlock->hasOverflowClip())
701 absPos -= containingBlock->layer()->scrolledContentOffset();
702 RenderObject* renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->renderer();
703 Node* node = renderer->node();
704 if (node && editingIgnoresContent(node))
705 return Position(node->parent(), node->nodeIndex());
706 return renderer->positionForPoint(IntPoint(x - absPos.x(), root->topOverflow()));
709 // Could not find a next line. This means we must already be on the last line.
710 // Move to the end of the content in this block, which effectively moves us
711 // to the end of the line we're on.
712 Element* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement();
713 return VisiblePosition(rootElement, rootElement ? rootElement->childNodeCount() : 0, DOWNSTREAM);
718 static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
720 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
721 // FIXME: The following function can return -1; we don't handle that.
722 return textBreakPreceding(iterator, length);
725 VisiblePosition startOfSentence(const VisiblePosition &c)
727 return previousBoundary(c, startSentenceBoundary);
730 static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
732 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
733 return textBreakNext(iterator);
736 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
737 VisiblePosition endOfSentence(const VisiblePosition &c)
739 return nextBoundary(c, endSentenceBoundary);
742 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
744 // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
745 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
746 // FIXME: The following function can return -1; we don't handle that.
747 return textBreakPreceding(iterator, length);
750 VisiblePosition previousSentencePosition(const VisiblePosition &c)
752 VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
753 return c.honorEditableBoundaryAtOrAfter(prev);
756 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
758 // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
759 // move to the equivlant position in the following sentence.
760 TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
761 return textBreakFollowing(iterator, 0);
764 VisiblePosition nextSentencePosition(const VisiblePosition &c)
766 VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
767 return c.honorEditableBoundaryAtOrBefore(next);
770 static bool renderedAsNonInlineTableOrHR(RenderObject* renderer)
772 return renderer && ((renderer->isTable() && !renderer->isInline()) || renderer->isHR());
775 // FIXME: Broken for positions before/after images that aren't inline (5027702)
776 VisiblePosition startOfParagraph(const VisiblePosition& c)
778 Position p = c.deepEquivalent();
779 Node *startNode = p.node();
782 return VisiblePosition();
784 if (renderedAsNonInlineTableOrHR(startNode->renderer()) && p.atLastEditingPositionForNode())
785 return firstDeepEditingPositionForNode(startNode);
787 Node* startBlock = enclosingBlock(startNode);
789 Node *node = startNode;
790 int offset = p.m_offset;
794 if (n->isContentEditable() != startNode->isContentEditable())
796 RenderObject *r = n->renderer();
798 n = n->traversePreviousNodePostOrder(startBlock);
801 RenderStyle *style = r->style();
802 if (style->visibility() != VISIBLE) {
803 n = n->traversePreviousNodePostOrder(startBlock);
807 if (r->isBR() || isBlock(n))
811 if (style->preserveNewline()) {
812 const UChar* chars = toRenderText(r)->characters();
813 int i = toRenderText(r)->textLength();
815 if (n == startNode && o < i)
818 if (chars[i] == '\n')
819 return VisiblePosition(n, i + 1, DOWNSTREAM);
823 n = n->traversePreviousNodePostOrder(startBlock);
824 } else if (editingIgnoresContent(n) || isTableElement(n)) {
827 n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock);
829 n = n->traversePreviousNodePostOrder(startBlock);
832 return VisiblePosition(node, offset, DOWNSTREAM);
835 // FIXME: Broken for positions before/after images that aren't inline (5027702)
836 VisiblePosition endOfParagraph(const VisiblePosition &c)
839 return VisiblePosition();
841 Position p = c.deepEquivalent();
842 Node* startNode = p.node();
844 if (renderedAsNonInlineTableOrHR(startNode->renderer()) && p.atFirstEditingPositionForNode())
845 return lastDeepEditingPositionForNode(startNode);
847 Node* startBlock = enclosingBlock(startNode);
848 Node *stayInsideBlock = startBlock;
850 Node *node = startNode;
851 int offset = p.m_offset;
855 if (n->isContentEditable() != startNode->isContentEditable())
857 RenderObject *r = n->renderer();
859 n = n->traverseNextNode(stayInsideBlock);
862 RenderStyle *style = r->style();
863 if (style->visibility() != VISIBLE) {
864 n = n->traverseNextNode(stayInsideBlock);
868 if (r->isBR() || isBlock(n))
871 // FIXME: We avoid returning a position where the renderer can't accept the caret.
872 // We should probably do this in other cases such as startOfParagraph.
873 if (r->isText() && r->caretMaxRenderedOffset() > 0) {
874 int length = toRenderText(r)->textLength();
875 if (style->preserveNewline()) {
876 const UChar* chars = toRenderText(r)->characters();
877 int o = n == startNode ? offset : 0;
878 for (int i = o; i < length; ++i)
879 if (chars[i] == '\n')
880 return VisiblePosition(n, i, DOWNSTREAM);
883 offset = r->caretMaxOffset();
884 n = n->traverseNextNode(stayInsideBlock);
885 } else if (editingIgnoresContent(n) || isTableElement(n)) {
887 offset = lastOffsetForEditing(n);
888 n = n->traverseNextSibling(stayInsideBlock);
890 n = n->traverseNextNode(stayInsideBlock);
893 return VisiblePosition(node, offset, DOWNSTREAM);
896 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
898 VisiblePosition paragraphEnd(endOfParagraph(visiblePosition));
899 VisiblePosition afterParagraphEnd(paragraphEnd.next(true));
900 // The position after the last position in the last cell of a table
901 // is not the start of the next paragraph.
902 if (isFirstPositionAfterTable(afterParagraphEnd))
903 return afterParagraphEnd.next(true);
904 return afterParagraphEnd;
907 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b)
909 return a.isNotNull() && startOfParagraph(a) == startOfParagraph(b);
912 bool isStartOfParagraph(const VisiblePosition &pos)
914 return pos.isNotNull() && pos == startOfParagraph(pos);
917 bool isEndOfParagraph(const VisiblePosition &pos)
919 return pos.isNotNull() && pos == endOfParagraph(pos);
922 VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x)
924 VisiblePosition pos = p;
926 VisiblePosition n = previousLinePosition(pos, x);
927 if (n.isNull() || n == pos)
930 } while (inSameParagraph(p, pos));
934 VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x)
936 VisiblePosition pos = p;
938 VisiblePosition n = nextLinePosition(pos, x);
939 if (n.isNull() || n == pos)
942 } while (inSameParagraph(p, pos));
948 VisiblePosition startOfBlock(const VisiblePosition &c)
950 Position p = c.deepEquivalent();
951 Node *startNode = p.node();
953 return VisiblePosition();
954 return VisiblePosition(Position(startNode->enclosingBlockFlowElement(), 0), DOWNSTREAM);
957 VisiblePosition endOfBlock(const VisiblePosition &c)
959 Position p = c.deepEquivalent();
961 Node *startNode = p.node();
963 return VisiblePosition();
965 Node *startBlock = startNode->enclosingBlockFlowElement();
967 return VisiblePosition(startBlock, startBlock->childNodeCount(), VP_DEFAULT_AFFINITY);
970 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
972 return !a.isNull() && enclosingBlockFlowElement(a) == enclosingBlockFlowElement(b);
975 bool isStartOfBlock(const VisiblePosition &pos)
977 return pos.isNotNull() && pos == startOfBlock(pos);
980 bool isEndOfBlock(const VisiblePosition &pos)
982 return pos.isNotNull() && pos == endOfBlock(pos);
987 VisiblePosition startOfDocument(const Node* node)
990 return VisiblePosition();
992 return VisiblePosition(node->document()->documentElement(), 0, DOWNSTREAM);
995 VisiblePosition startOfDocument(const VisiblePosition &c)
997 return startOfDocument(c.deepEquivalent().node());
1000 VisiblePosition endOfDocument(const Node* node)
1002 if (!node || !node->document())
1003 return VisiblePosition();
1005 Element* doc = node->document()->documentElement();
1006 return VisiblePosition(doc, doc->childNodeCount(), DOWNSTREAM);
1009 VisiblePosition endOfDocument(const VisiblePosition &c)
1011 return endOfDocument(c.deepEquivalent().node());
1014 bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
1016 Position ap = a.deepEquivalent();
1017 Node *an = ap.node();
1020 Position bp = b.deepEquivalent();
1021 Node *bn = bp.node();
1025 return an->document() == bn->document();
1028 bool isStartOfDocument(const VisiblePosition &p)
1030 return p.isNotNull() && p.previous().isNull();
1033 bool isEndOfDocument(const VisiblePosition &p)
1035 return p.isNotNull() && p.next().isNull();
1040 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
1042 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1044 return VisiblePosition();
1046 return firstDeepEditingPositionForNode(highestRoot);
1049 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
1051 Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1053 return VisiblePosition();
1055 return lastDeepEditingPositionForNode(highestRoot);