• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25 
26 #include "config.h"
27 #include "visible_units.h"
28 
29 #include "Document.h"
30 #include "Element.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 
40 namespace WebCore {
41 
42 using namespace HTMLNames;
43 
previousBoundary(const VisiblePosition & c,unsigned (* searchFunction)(const UChar *,unsigned))44 static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned))
45 {
46     Position pos = c.deepEquivalent();
47     Node *n = pos.node();
48     if (!n)
49         return VisiblePosition();
50     Document *d = n->document();
51     Node *de = d->documentElement();
52     if (!de)
53         return VisiblePosition();
54     Node *boundary = n->enclosingBlockFlowElement();
55     if (!boundary)
56         return VisiblePosition();
57     bool isContentEditable = boundary->isContentEditable();
58     while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
59         boundary = boundary->parentNode();
60 
61     Position start = rangeCompliantEquivalent(Position(boundary, 0));
62     Position end = rangeCompliantEquivalent(pos);
63     RefPtr<Range> searchRange = Range::create(d);
64 
65     int exception = 0;
66     searchRange->setStart(start.node(), start.offset(), exception);
67     searchRange->setEnd(end.node(), end.offset(), exception);
68 
69     ASSERT(!exception);
70     if (exception)
71         return VisiblePosition();
72 
73     SimplifiedBackwardsTextIterator it(searchRange.get());
74     Vector<UChar, 1024> string;
75     unsigned next = 0;
76     bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
77     while (!it.atEnd()) {
78         // iterate to get chunks until the searchFunction returns a non-zero value.
79         if (!inTextSecurityMode)
80             string.prepend(it.characters(), it.length());
81         else {
82             // Treat bullets used in the text security mode as regular characters when looking for boundaries
83             String iteratorString(it.characters(), it.length());
84             iteratorString = iteratorString.impl()->secure('x');
85             string.prepend(iteratorString.characters(), iteratorString.length());
86         }
87 
88         next = searchFunction(string.data(), string.size());
89         if (next != 0)
90             break;
91         it.advance();
92     }
93 
94     if (it.atEnd() && next == 0) {
95         pos = it.range()->startPosition();
96     } else if (next != 0) {
97         Node *node = it.range()->startContainer(exception);
98         if (node->isTextNode() || (node->renderer() && node->renderer()->isBR()))
99             // The next variable contains a usable index into a text node
100             pos = Position(node, next);
101         else {
102             // Use the end of the found range, the start is not guaranteed to
103             // be correct.
104             Position end = it.range()->endPosition();
105             VisiblePosition boundary(end);
106             unsigned i = it.length() - next;
107             while (i--)
108                 boundary = boundary.previous();
109             return boundary;
110         }
111     }
112 
113     return VisiblePosition(pos, DOWNSTREAM);
114 }
115 
nextBoundary(const VisiblePosition & c,unsigned (* searchFunction)(const UChar *,unsigned))116 static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned))
117 {
118     Position pos = c.deepEquivalent();
119     Node *n = pos.node();
120     if (!n)
121         return VisiblePosition();
122     Document *d = n->document();
123     Node *de = d->documentElement();
124     if (!de)
125         return VisiblePosition();
126     Node *boundary = n->enclosingBlockFlowElement();
127     if (!boundary)
128         return VisiblePosition();
129     bool isContentEditable = boundary->isContentEditable();
130     while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable())
131         boundary = boundary->parentNode();
132 
133     RefPtr<Range> searchRange(d->createRange());
134     Position start(rangeCompliantEquivalent(pos));
135     ExceptionCode ec = 0;
136     searchRange->selectNodeContents(boundary, ec);
137     searchRange->setStart(start.node(), start.offset(), ec);
138     TextIterator it(searchRange.get(), true);
139     Vector<UChar, 1024> string;
140     unsigned next = 0;
141     bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
142     while (!it.atEnd()) {
143         // Keep asking the iterator for chunks until the search function
144         // returns an end value not equal to the length of the string passed to it.
145         if (!inTextSecurityMode)
146             string.append(it.characters(), it.length());
147         else {
148             // Treat bullets used in the text security mode as regular characters when looking for boundaries
149             String iteratorString(it.characters(), it.length());
150             iteratorString = iteratorString.impl()->secure('x');
151             string.append(iteratorString.characters(), iteratorString.length());
152         }
153 
154         next = searchFunction(string.data(), string.size());
155         if (next != string.size())
156             break;
157         it.advance();
158     }
159 
160     if (it.atEnd() && next == string.size()) {
161         pos = it.range()->startPosition();
162     } else if (next != 0) {
163         // Use the character iterator to translate the next value into a DOM position.
164         CharacterIterator charIt(searchRange.get(), true);
165         charIt.advance(next - 1);
166         pos = charIt.range()->endPosition();
167 
168         // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
169         VisiblePosition visPos = VisiblePosition(pos);
170         if (visPos == VisiblePosition(charIt.range()->startPosition()))
171             pos = visPos.next(true).deepEquivalent();
172     }
173 
174     // generate VisiblePosition, use UPSTREAM affinity if possible
175     return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
176 }
177 
178 // ---------
179 
startWordBoundary(const UChar * characters,unsigned length)180 static unsigned startWordBoundary(const UChar* characters, unsigned length)
181 {
182     int start, end;
183     findWordBoundary(characters, length, length, &start, &end);
184     return start;
185 }
186 
startOfWord(const VisiblePosition & c,EWordSide side)187 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
188 {
189     // FIXME: This returns a null VP for c at the start of the document
190     // and side == LeftWordIfOnBoundary
191     VisiblePosition p = c;
192     if (side == RightWordIfOnBoundary) {
193         // at paragraph end, the startofWord is the current position
194         if (isEndOfParagraph(c))
195             return c;
196 
197         p = c.next();
198         if (p.isNull())
199             return c;
200     }
201     return previousBoundary(p, startWordBoundary);
202 }
203 
endWordBoundary(const UChar * characters,unsigned length)204 static unsigned endWordBoundary(const UChar* characters, unsigned length)
205 {
206     int start, end;
207     findWordBoundary(characters, length, 0, &start, &end);
208     return end;
209 }
210 
endOfWord(const VisiblePosition & c,EWordSide side)211 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
212 {
213     VisiblePosition p = c;
214     if (side == LeftWordIfOnBoundary) {
215         if (isStartOfParagraph(c))
216             return c;
217 
218         p = c.previous();
219         if (p.isNull())
220             return c;
221     } else if (isEndOfParagraph(c))
222         return c;
223 
224     return nextBoundary(p, endWordBoundary);
225 }
226 
previousWordPositionBoundary(const UChar * characters,unsigned length)227 static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length)
228 {
229     return findNextWordFromIndex(characters, length, length, false);
230 }
231 
previousWordPosition(const VisiblePosition & c)232 VisiblePosition previousWordPosition(const VisiblePosition &c)
233 {
234     VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
235     return c.honorEditableBoundaryAtOrAfter(prev);
236 }
237 
nextWordPositionBoundary(const UChar * characters,unsigned length)238 static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length)
239 {
240     return findNextWordFromIndex(characters, length, 0, true);
241 }
242 
nextWordPosition(const VisiblePosition & c)243 VisiblePosition nextWordPosition(const VisiblePosition &c)
244 {
245     VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
246     return c.honorEditableBoundaryAtOrBefore(next);
247 }
248 
249 // ---------
250 
rootBoxForLine(const VisiblePosition & c)251 static RootInlineBox *rootBoxForLine(const VisiblePosition &c)
252 {
253     Position p = c.deepEquivalent();
254     Node *node = p.node();
255     if (!node)
256         return 0;
257 
258     RenderObject *renderer = node->renderer();
259     if (!renderer)
260         return 0;
261 
262     InlineBox* box;
263     int offset;
264     c.getInlineBoxAndOffset(box, offset);
265 
266     return box ? box->root() : 0;
267 }
268 
positionAvoidingFirstPositionInTable(const VisiblePosition & c)269 static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c)
270 {
271     // return table offset 0 instead of the first VisiblePosition inside the table
272     VisiblePosition previous = c.previous();
273     if (isLastPositionBeforeTable(previous))
274         return previous;
275 
276     return c;
277 }
278 
startPositionForLine(const VisiblePosition & c)279 static VisiblePosition startPositionForLine(const VisiblePosition& c)
280 {
281     if (c.isNull())
282         return VisiblePosition();
283 
284     RootInlineBox *rootBox = rootBoxForLine(c);
285     if (!rootBox) {
286         // There are VisiblePositions at offset 0 in blocks without
287         // RootInlineBoxes, like empty editable blocks and bordered blocks.
288         Position p = c.deepEquivalent();
289         if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0)
290             return positionAvoidingFirstPositionInTable(c);
291 
292         return VisiblePosition();
293     }
294 
295     // Generated content (e.g. list markers and CSS :before and :after
296     // pseudoelements) have no corresponding DOM element, and so cannot be
297     // represented by a VisiblePosition.  Use whatever follows instead.
298     InlineBox *startBox = rootBox->firstLeafChild();
299     Node *startNode;
300     while (1) {
301         if (!startBox)
302             return VisiblePosition();
303 
304         RenderObject *startRenderer = startBox->object();
305         if (!startRenderer)
306             return VisiblePosition();
307 
308         startNode = startRenderer->element();
309         if (startNode)
310             break;
311 
312         startBox = startBox->nextLeafChild();
313     }
314 
315     int startOffset = 0;
316     if (startBox->isInlineTextBox()) {
317         InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox);
318         startOffset = startTextBox->start();
319     }
320 
321     VisiblePosition visPos = VisiblePosition(startNode, startOffset, DOWNSTREAM);
322     return positionAvoidingFirstPositionInTable(visPos);
323 }
324 
startOfLine(const VisiblePosition & c)325 VisiblePosition startOfLine(const VisiblePosition& c)
326 {
327     VisiblePosition visPos = startPositionForLine(c);
328 
329     if (visPos.isNotNull()) {
330         // Make sure the start of line is not greater than the given input position.  Else use the previous position to
331         // obtain start of line.  This condition happens when the input position is before the space character at the end
332         // of a soft-wrapped non-editable line. In this scenario, startPositionForLine would incorrectly hand back a position
333         // greater than the input position.  This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space
334         // style versus lines without that style, which would break before a space by default.
335         Position p = visPos.deepEquivalent();
336         if (p.offset() > c.deepEquivalent().offset() && p.node()->isSameNode(c.deepEquivalent().node())) {
337             visPos = c.previous();
338             if (visPos.isNull())
339                 return VisiblePosition();
340             visPos = startPositionForLine(visPos);
341         }
342     }
343 
344     return c.honorEditableBoundaryAtOrAfter(visPos);
345 }
346 
endPositionForLine(const VisiblePosition & c)347 static VisiblePosition endPositionForLine(const VisiblePosition& c)
348 {
349     if (c.isNull())
350         return VisiblePosition();
351 
352     RootInlineBox *rootBox = rootBoxForLine(c);
353     if (!rootBox) {
354         // There are VisiblePositions at offset 0 in blocks without
355         // RootInlineBoxes, like empty editable blocks and bordered blocks.
356         Position p = c.deepEquivalent();
357         if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0)
358             return c;
359         return VisiblePosition();
360     }
361 
362     // Generated content (e.g. list markers and CSS :before and :after
363     // pseudoelements) have no corresponding DOM element, and so cannot be
364     // represented by a VisiblePosition.  Use whatever precedes instead.
365     Node *endNode;
366     InlineBox *endBox = rootBox->lastLeafChild();
367     while (1) {
368         if (!endBox)
369             return VisiblePosition();
370 
371         RenderObject *endRenderer = endBox->object();
372         if (!endRenderer)
373             return VisiblePosition();
374 
375         endNode = endRenderer->element();
376         if (endNode)
377             break;
378 
379         endBox = endBox->prevLeafChild();
380     }
381 
382     int endOffset = 1;
383     if (endNode->hasTagName(brTag)) {
384         endOffset = 0;
385     } else if (endBox->isInlineTextBox()) {
386         InlineTextBox *endTextBox = static_cast<InlineTextBox *>(endBox);
387         endOffset = endTextBox->start();
388         if (!endTextBox->isLineBreak())
389             endOffset += endTextBox->len();
390     }
391 
392     return VisiblePosition(endNode, endOffset, VP_UPSTREAM_IF_POSSIBLE);
393 }
394 
endOfLine(const VisiblePosition & c)395 VisiblePosition endOfLine(const VisiblePosition& c)
396 {
397     VisiblePosition visPos = endPositionForLine(c);
398 
399     // Make sure the end of line is at the same line as the given input position.  Else use the previous position to
400     // obtain end of line.  This condition happens when the input position is before the space character at the end
401     // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
402     // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
403     // versus lines without that style, which would break before a space by default.
404     if (!inSameLine(c, visPos)) {
405         visPos = c.previous();
406         if (visPos.isNull())
407             return VisiblePosition();
408         visPos = endPositionForLine(visPos);
409     }
410 
411     return c.honorEditableBoundaryAtOrBefore(visPos);
412 }
413 
inSameLine(const VisiblePosition & a,const VisiblePosition & b)414 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
415 {
416     return a.isNotNull() && startOfLine(a) == startOfLine(b);
417 }
418 
isStartOfLine(const VisiblePosition & p)419 bool isStartOfLine(const VisiblePosition &p)
420 {
421     return p.isNotNull() && p == startOfLine(p);
422 }
423 
isEndOfLine(const VisiblePosition & p)424 bool isEndOfLine(const VisiblePosition &p)
425 {
426     return p.isNotNull() && p == endOfLine(p);
427 }
428 
429 // The first leaf before node that has the same editability as node.
previousLeafWithSameEditability(Node * node)430 static Node* previousLeafWithSameEditability(Node* node)
431 {
432     bool editable = node->isContentEditable();
433     Node* n = node->previousLeafNode();
434     while (n) {
435         if (editable == n->isContentEditable())
436             return n;
437         n = n->previousLeafNode();
438     }
439     return 0;
440 }
441 
previousLinePosition(const VisiblePosition & visiblePosition,int x)442 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int x)
443 {
444     Position p = visiblePosition.deepEquivalent();
445     Node *node = p.node();
446     Node* highestRoot = highestEditableRoot(p);
447     if (!node)
448         return VisiblePosition();
449 
450     node->document()->updateLayoutIgnorePendingStylesheets();
451 
452     RenderObject *renderer = node->renderer();
453     if (!renderer)
454         return VisiblePosition();
455 
456     RenderBlock *containingBlock = 0;
457     RootInlineBox *root = 0;
458     InlineBox* box;
459     int ignoredCaretOffset;
460     visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
461     if (box) {
462         root = box->root()->prevRootBox();
463         if (root)
464             containingBlock = renderer->containingBlock();
465     }
466 
467     if (!root) {
468         // This containing editable block does not have a previous line.
469         // Need to move back to previous containing editable block in this root editable
470         // block and find the last root line box in that block.
471         Node* startBlock = enclosingBlock(node);
472         Node* n = previousLeafWithSameEditability(node);
473         while (n && startBlock == enclosingBlock(n))
474             n = previousLeafWithSameEditability(n);
475         while (n) {
476             if (highestEditableRoot(Position(n, 0)) != highestRoot)
477                 break;
478             Position pos(n, caretMinOffset(n));
479             if (pos.isCandidate()) {
480                 ASSERT(n->renderer());
481                 Position maxPos(n, caretMaxOffset(n));
482                 maxPos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
483                 if (box) {
484                     // previous root line box found
485                     root = box->root();
486                     containingBlock = n->renderer()->containingBlock();
487                     break;
488                 }
489 
490                 return VisiblePosition(pos, DOWNSTREAM);
491             }
492             n = previousLeafWithSameEditability(n);
493         }
494     }
495 
496     if (root) {
497         // FIXME: Can be wrong for multi-column layout and with transforms.
498         FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
499         if (containingBlock->hasOverflowClip())
500             absPos -= containingBlock->layer()->scrolledContentOffset();
501         RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object();
502         Node* node = renderer->node();
503         if (editingIgnoresContent(node))
504             return Position(node->parent(), node->nodeIndex());
505         return renderer->positionForCoordinates(x - absPos.x(), root->topOverflow());
506     }
507 
508     // Could not find a previous line. This means we must already be on the first line.
509     // Move to the start of the content in this block, which effectively moves us
510     // to the start of the line we're on.
511     Node* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement();
512     return VisiblePosition(rootElement, 0, DOWNSTREAM);
513 }
514 
nextLeafWithSameEditability(Node * node,int offset)515 static Node* nextLeafWithSameEditability(Node* node, int offset)
516 {
517     bool editable = node->isContentEditable();
518     ASSERT(offset >= 0);
519     Node* child = node->childNode(offset);
520     Node* n = child ? child->nextLeafNode() : node->nextLeafNode();
521     while (n) {
522         if (editable == n->isContentEditable())
523             return n;
524         n = n->nextLeafNode();
525     }
526     return 0;
527 }
528 
nextLeafWithSameEditability(Node * node)529 static Node* nextLeafWithSameEditability(Node* node)
530 {
531     if (!node)
532         return 0;
533 
534     bool editable = node->isContentEditable();
535     Node* n = node->nextLeafNode();
536     while (n) {
537         if (editable == n->isContentEditable())
538             return n;
539         n = n->nextLeafNode();
540     }
541     return 0;
542 }
543 
nextLinePosition(const VisiblePosition & visiblePosition,int x)544 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
545 {
546     Position p = visiblePosition.deepEquivalent();
547     Node *node = p.node();
548     Node* highestRoot = highestEditableRoot(p);
549     if (!node)
550         return VisiblePosition();
551 
552     node->document()->updateLayoutIgnorePendingStylesheets();
553 
554     RenderObject *renderer = node->renderer();
555     if (!renderer)
556         return VisiblePosition();
557 
558     RenderBlock *containingBlock = 0;
559     RootInlineBox *root = 0;
560     InlineBox* box;
561     int ignoredCaretOffset;
562     visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
563     if (box) {
564         root = box->root()->nextRootBox();
565         if (root)
566             containingBlock = renderer->containingBlock();
567     }
568 
569     if (!root) {
570         // This containing editable block does not have a next line.
571         // Need to move forward to next containing editable block in this root editable
572         // block and find the first root line box in that block.
573         Node* startBlock = enclosingBlock(node);
574         Node* n = nextLeafWithSameEditability(node, p.offset());
575         while (n && startBlock == enclosingBlock(n))
576             n = nextLeafWithSameEditability(n);
577         while (n) {
578             if (highestEditableRoot(Position(n, 0)) != highestRoot)
579                 break;
580             Position pos(n, caretMinOffset(n));
581             if (pos.isCandidate()) {
582                 ASSERT(n->renderer());
583                 pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
584                 if (box) {
585                     // next root line box found
586                     root = box->root();
587                     containingBlock = n->renderer()->containingBlock();
588                     break;
589                 }
590 
591                 return VisiblePosition(pos, DOWNSTREAM);
592             }
593             n = nextLeafWithSameEditability(n);
594         }
595     }
596 
597     if (root) {
598         // FIXME: Can be wrong for multi-column layout and with transforms.
599         FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
600         if (containingBlock->hasOverflowClip())
601             absPos -= containingBlock->layer()->scrolledContentOffset();
602         RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object();
603         Node* node = renderer->node();
604         if (editingIgnoresContent(node))
605             return Position(node->parent(), node->nodeIndex());
606         return renderer->positionForCoordinates(x - absPos.x(), root->topOverflow());
607     }
608 
609     // Could not find a next line. This means we must already be on the last line.
610     // Move to the end of the content in this block, which effectively moves us
611     // to the end of the line we're on.
612     Element* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement();
613     return VisiblePosition(rootElement, rootElement ? rootElement->childNodeCount() : 0, DOWNSTREAM);
614 }
615 
616 // ---------
617 
startSentenceBoundary(const UChar * characters,unsigned length)618 static unsigned startSentenceBoundary(const UChar* characters, unsigned length)
619 {
620     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
621     // FIXME: The following function can return -1; we don't handle that.
622     return textBreakPreceding(iterator, length);
623 }
624 
startOfSentence(const VisiblePosition & c)625 VisiblePosition startOfSentence(const VisiblePosition &c)
626 {
627     return previousBoundary(c, startSentenceBoundary);
628 }
629 
endSentenceBoundary(const UChar * characters,unsigned length)630 static unsigned endSentenceBoundary(const UChar* characters, unsigned length)
631 {
632     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
633     return textBreakNext(iterator);
634 }
635 
636 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
endOfSentence(const VisiblePosition & c)637 VisiblePosition endOfSentence(const VisiblePosition &c)
638 {
639     return nextBoundary(c, endSentenceBoundary);
640 }
641 
previousSentencePositionBoundary(const UChar * characters,unsigned length)642 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length)
643 {
644     // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
645     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
646     // FIXME: The following function can return -1; we don't handle that.
647     return textBreakPreceding(iterator, length);
648 }
649 
previousSentencePosition(const VisiblePosition & c)650 VisiblePosition previousSentencePosition(const VisiblePosition &c)
651 {
652     VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
653     return c.honorEditableBoundaryAtOrAfter(prev);
654 }
655 
nextSentencePositionBoundary(const UChar * characters,unsigned length)656 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length)
657 {
658     // FIXME: This is identical to endSentenceBoundary.  This isn't right, it needs to
659     // move to the equivlant position in the following sentence.
660     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
661     return textBreakFollowing(iterator, 0);
662 }
663 
nextSentencePosition(const VisiblePosition & c)664 VisiblePosition nextSentencePosition(const VisiblePosition &c)
665 {
666     VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
667     return c.honorEditableBoundaryAtOrBefore(next);
668 }
669 
670 // FIXME: Broken for positions before/after images that aren't inline (5027702)
startOfParagraph(const VisiblePosition & c)671 VisiblePosition startOfParagraph(const VisiblePosition &c)
672 {
673     Position p = c.deepEquivalent();
674     Node *startNode = p.node();
675 
676     if (!startNode)
677         return VisiblePosition();
678 
679     if (startNode->renderer()
680         && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline())
681             || startNode->renderer()->isHR())
682         && p.offset() == maxDeepOffset(startNode))
683         return VisiblePosition(Position(startNode, 0));
684 
685     Node* startBlock = enclosingBlock(startNode);
686 
687     Node *node = startNode;
688     int offset = p.offset();
689 
690     Node *n = startNode;
691     while (n) {
692         if (n->isContentEditable() != startNode->isContentEditable())
693             break;
694         RenderObject *r = n->renderer();
695         if (!r) {
696             n = n->traversePreviousNodePostOrder(startBlock);
697             continue;
698         }
699         RenderStyle *style = r->style();
700         if (style->visibility() != VISIBLE) {
701             n = n->traversePreviousNodePostOrder(startBlock);
702             continue;
703         }
704 
705         if (r->isBR() || isBlock(n))
706             break;
707 
708         if (r->isText()) {
709             if (style->preserveNewline()) {
710                 const UChar* chars = toRenderText(r)->characters();
711                 int i = toRenderText(r)->textLength();
712                 int o = offset;
713                 if (n == startNode && o < i)
714                     i = max(0, o);
715                 while (--i >= 0)
716                     if (chars[i] == '\n')
717                         return VisiblePosition(n, i + 1, DOWNSTREAM);
718             }
719             node = n;
720             offset = 0;
721             n = n->traversePreviousNodePostOrder(startBlock);
722         } else if (editingIgnoresContent(n) || isTableElement(n)) {
723             node = n;
724             offset = 0;
725             n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock);
726         } else
727             n = n->traversePreviousNodePostOrder(startBlock);
728     }
729 
730     return VisiblePosition(node, offset, DOWNSTREAM);
731 }
732 
733 // FIXME: Broken for positions before/after images that aren't inline (5027702)
endOfParagraph(const VisiblePosition & c)734 VisiblePosition endOfParagraph(const VisiblePosition &c)
735 {
736     if (c.isNull())
737         return VisiblePosition();
738 
739     Position p = c.deepEquivalent();
740     Node* startNode = p.node();
741 
742     if (startNode->renderer()
743         && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline())
744             || startNode->renderer()->isHR())
745         && p.offset() == 0)
746         return VisiblePosition(Position(startNode, maxDeepOffset(startNode)));
747 
748     Node* startBlock = enclosingBlock(startNode);
749     Node *stayInsideBlock = startBlock;
750 
751     Node *node = startNode;
752     int offset = p.offset();
753 
754     Node *n = startNode;
755     while (n) {
756         if (n->isContentEditable() != startNode->isContentEditable())
757             break;
758         RenderObject *r = n->renderer();
759         if (!r) {
760             n = n->traverseNextNode(stayInsideBlock);
761             continue;
762         }
763         RenderStyle *style = r->style();
764         if (style->visibility() != VISIBLE) {
765             n = n->traverseNextNode(stayInsideBlock);
766             continue;
767         }
768 
769         if (r->isBR() || isBlock(n))
770             break;
771 
772         // FIXME: We avoid returning a position where the renderer can't accept the caret.
773         // We should probably do this in other cases such as startOfParagraph.
774         if (r->isText() && r->caretMaxRenderedOffset() > 0) {
775             int length = toRenderText(r)->textLength();
776             if (style->preserveNewline()) {
777                 const UChar* chars = toRenderText(r)->characters();
778                 int o = n == startNode ? offset : 0;
779                 for (int i = o; i < length; ++i)
780                     if (chars[i] == '\n')
781                         return VisiblePosition(n, i, DOWNSTREAM);
782             }
783             node = n;
784             offset = r->caretMaxOffset();
785             n = n->traverseNextNode(stayInsideBlock);
786         } else if (editingIgnoresContent(n) || isTableElement(n)) {
787             node = n;
788             offset = maxDeepOffset(n);
789             n = n->traverseNextSibling(stayInsideBlock);
790         } else
791             n = n->traverseNextNode(stayInsideBlock);
792     }
793 
794     return VisiblePosition(node, offset, DOWNSTREAM);
795 }
796 
startOfNextParagraph(const VisiblePosition & visiblePosition)797 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
798 {
799     VisiblePosition paragraphEnd(endOfParagraph(visiblePosition));
800     VisiblePosition afterParagraphEnd(paragraphEnd.next(true));
801     // The position after the last position in the last cell of a table
802     // is not the start of the next paragraph.
803     if (isFirstPositionAfterTable(afterParagraphEnd))
804         return afterParagraphEnd.next(true);
805     return afterParagraphEnd;
806 }
807 
inSameParagraph(const VisiblePosition & a,const VisiblePosition & b)808 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b)
809 {
810     return a.isNotNull() && startOfParagraph(a) == startOfParagraph(b);
811 }
812 
isStartOfParagraph(const VisiblePosition & pos)813 bool isStartOfParagraph(const VisiblePosition &pos)
814 {
815     return pos.isNotNull() && pos == startOfParagraph(pos);
816 }
817 
isEndOfParagraph(const VisiblePosition & pos)818 bool isEndOfParagraph(const VisiblePosition &pos)
819 {
820     return pos.isNotNull() && pos == endOfParagraph(pos);
821 }
822 
previousParagraphPosition(const VisiblePosition & p,int x)823 VisiblePosition previousParagraphPosition(const VisiblePosition &p, int x)
824 {
825     VisiblePosition pos = p;
826     do {
827         VisiblePosition n = previousLinePosition(pos, x);
828         if (n.isNull() || n == pos)
829             return p;
830         pos = n;
831     } while (inSameParagraph(p, pos));
832     return pos;
833 }
834 
nextParagraphPosition(const VisiblePosition & p,int x)835 VisiblePosition nextParagraphPosition(const VisiblePosition &p, int x)
836 {
837     VisiblePosition pos = p;
838     do {
839         VisiblePosition n = nextLinePosition(pos, x);
840         if (n.isNull() || n == pos)
841             return p;
842         pos = n;
843     } while (inSameParagraph(p, pos));
844     return pos;
845 }
846 
847 // ---------
848 
startOfBlock(const VisiblePosition & c)849 VisiblePosition startOfBlock(const VisiblePosition &c)
850 {
851     Position p = c.deepEquivalent();
852     Node *startNode = p.node();
853     if (!startNode)
854         return VisiblePosition();
855     return VisiblePosition(Position(startNode->enclosingBlockFlowElement(), 0), DOWNSTREAM);
856 }
857 
endOfBlock(const VisiblePosition & c)858 VisiblePosition endOfBlock(const VisiblePosition &c)
859 {
860     Position p = c.deepEquivalent();
861 
862     Node *startNode = p.node();
863     if (!startNode)
864         return VisiblePosition();
865 
866     Node *startBlock = startNode->enclosingBlockFlowElement();
867 
868     return VisiblePosition(startBlock, startBlock->childNodeCount(), VP_DEFAULT_AFFINITY);
869 }
870 
inSameBlock(const VisiblePosition & a,const VisiblePosition & b)871 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
872 {
873     return !a.isNull() && enclosingBlockFlowElement(a) == enclosingBlockFlowElement(b);
874 }
875 
isStartOfBlock(const VisiblePosition & pos)876 bool isStartOfBlock(const VisiblePosition &pos)
877 {
878     return pos.isNotNull() && pos == startOfBlock(pos);
879 }
880 
isEndOfBlock(const VisiblePosition & pos)881 bool isEndOfBlock(const VisiblePosition &pos)
882 {
883     return pos.isNotNull() && pos == endOfBlock(pos);
884 }
885 
886 // ---------
887 
startOfDocument(const Node * node)888 VisiblePosition startOfDocument(const Node* node)
889 {
890     if (!node)
891         return VisiblePosition();
892 
893     return VisiblePosition(node->document()->documentElement(), 0, DOWNSTREAM);
894 }
895 
startOfDocument(const VisiblePosition & c)896 VisiblePosition startOfDocument(const VisiblePosition &c)
897 {
898     return startOfDocument(c.deepEquivalent().node());
899 }
900 
endOfDocument(const Node * node)901 VisiblePosition endOfDocument(const Node* node)
902 {
903     if (!node || !node->document())
904         return VisiblePosition();
905 
906     Element* doc = node->document()->documentElement();
907     return VisiblePosition(doc, doc->childNodeCount(), DOWNSTREAM);
908 }
909 
endOfDocument(const VisiblePosition & c)910 VisiblePosition endOfDocument(const VisiblePosition &c)
911 {
912     return endOfDocument(c.deepEquivalent().node());
913 }
914 
inSameDocument(const VisiblePosition & a,const VisiblePosition & b)915 bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
916 {
917     Position ap = a.deepEquivalent();
918     Node *an = ap.node();
919     if (!an)
920         return false;
921     Position bp = b.deepEquivalent();
922     Node *bn = bp.node();
923     if (an == bn)
924         return true;
925 
926     return an->document() == bn->document();
927 }
928 
isStartOfDocument(const VisiblePosition & p)929 bool isStartOfDocument(const VisiblePosition &p)
930 {
931     return p.isNotNull() && p.previous().isNull();
932 }
933 
isEndOfDocument(const VisiblePosition & p)934 bool isEndOfDocument(const VisiblePosition &p)
935 {
936     return p.isNotNull() && p.next().isNull();
937 }
938 
939 // ---------
940 
startOfEditableContent(const VisiblePosition & visiblePosition)941 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
942 {
943     Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
944     if (!highestRoot)
945         return VisiblePosition();
946 
947     return VisiblePosition(highestRoot, 0, DOWNSTREAM);
948 }
949 
endOfEditableContent(const VisiblePosition & visiblePosition)950 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
951 {
952     Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
953     if (!highestRoot)
954         return VisiblePosition();
955 
956     return VisiblePosition(highestRoot, maxDeepOffset(highestRoot), DOWNSTREAM);
957 }
958 
959 }
960