• 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 "core/editing/VisibleUnits.h"
28 
29 #include "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/HTMLNames.h"
32 #include "core/dom/Document.h"
33 #include "core/dom/Element.h"
34 #include "core/dom/NodeTraversal.h"
35 #include "core/dom/Position.h"
36 #include "core/dom/Text.h"
37 #include "core/editing/RenderedPosition.h"
38 #include "core/editing/TextIterator.h"
39 #include "core/editing/VisiblePosition.h"
40 #include "core/editing/htmlediting.h"
41 #include "core/html/HTMLBRElement.h"
42 #include "core/rendering/InlineTextBox.h"
43 #include "core/rendering/RenderBlockFlow.h"
44 #include "core/rendering/RenderObject.h"
45 #include "platform/RuntimeEnabledFeatures.h"
46 #include "platform/heap/Handle.h"
47 #include "platform/text/TextBoundaries.h"
48 
49 namespace WebCore {
50 
51 using namespace HTMLNames;
52 using namespace WTF::Unicode;
53 
previousLeafWithSameEditability(Node * node,EditableType editableType)54 static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
55 {
56     bool editable = node->rendererIsEditable(editableType);
57     node = node->previousLeafNode();
58     while (node) {
59         if (editable == node->rendererIsEditable(editableType))
60             return node;
61         node = node->previousLeafNode();
62     }
63     return 0;
64 }
65 
nextLeafWithSameEditability(Node * node,EditableType editableType=ContentIsEditable)66 static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable)
67 {
68     if (!node)
69         return 0;
70 
71     bool editable = node->rendererIsEditable(editableType);
72     node = node->nextLeafNode();
73     while (node) {
74         if (editable == node->rendererIsEditable(editableType))
75             return node;
76         node = node->nextLeafNode();
77     }
78     return 0;
79 }
80 
81 // FIXME: consolidate with code in previousLinePosition.
previousRootInlineBoxCandidatePosition(Node * node,const VisiblePosition & visiblePosition,EditableType editableType)82 static Position previousRootInlineBoxCandidatePosition(Node* node, const VisiblePosition& visiblePosition, EditableType editableType)
83 {
84     Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), editableType);
85     Node* previousNode = previousLeafWithSameEditability(node, editableType);
86 
87     while (previousNode && (!previousNode->renderer() || inSameLine(VisiblePosition(firstPositionInOrBeforeNode(previousNode)), visiblePosition)))
88         previousNode = previousLeafWithSameEditability(previousNode, editableType);
89 
90     while (previousNode && !previousNode->isShadowRoot()) {
91         if (highestEditableRoot(firstPositionInOrBeforeNode(previousNode), editableType) != highestRoot)
92             break;
93 
94         Position pos = isHTMLBRElement(*previousNode) ? positionBeforeNode(previousNode) :
95             createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
96 
97         if (pos.isCandidate())
98             return pos;
99 
100         previousNode = previousLeafWithSameEditability(previousNode, editableType);
101     }
102     return Position();
103 }
104 
nextRootInlineBoxCandidatePosition(Node * node,const VisiblePosition & visiblePosition,EditableType editableType)105 static Position nextRootInlineBoxCandidatePosition(Node* node, const VisiblePosition& visiblePosition, EditableType editableType)
106 {
107     Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), editableType);
108     Node* nextNode = nextLeafWithSameEditability(node, editableType);
109     while (nextNode && (!nextNode->renderer() || inSameLine(VisiblePosition(firstPositionInOrBeforeNode(nextNode)), visiblePosition)))
110         nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable);
111 
112     while (nextNode && !nextNode->isShadowRoot()) {
113         if (highestEditableRoot(firstPositionInOrBeforeNode(nextNode), editableType) != highestRoot)
114             break;
115 
116         Position pos;
117         pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
118 
119         if (pos.isCandidate())
120             return pos;
121 
122         nextNode = nextLeafWithSameEditability(nextNode, editableType);
123     }
124     return Position();
125 }
126 
127 class CachedLogicallyOrderedLeafBoxes {
128 public:
129     CachedLogicallyOrderedLeafBoxes();
130 
131     const InlineTextBox* previousTextBox(const RootInlineBox*, const InlineTextBox*);
132     const InlineTextBox* nextTextBox(const RootInlineBox*, const InlineTextBox*);
133 
size() const134     size_t size() const { return m_leafBoxes.size(); }
firstBox() const135     const InlineBox* firstBox() const { return m_leafBoxes[0]; }
136 
137 private:
138     const Vector<InlineBox*>& collectBoxes(const RootInlineBox*);
139     int boxIndexInLeaves(const InlineTextBox*) const;
140 
141     const RootInlineBox* m_rootInlineBox;
142     Vector<InlineBox*> m_leafBoxes;
143 };
144 
CachedLogicallyOrderedLeafBoxes()145 CachedLogicallyOrderedLeafBoxes::CachedLogicallyOrderedLeafBoxes() : m_rootInlineBox(0) { };
146 
previousTextBox(const RootInlineBox * root,const InlineTextBox * box)147 const InlineTextBox* CachedLogicallyOrderedLeafBoxes::previousTextBox(const RootInlineBox* root, const InlineTextBox* box)
148 {
149     if (!root)
150         return 0;
151 
152     collectBoxes(root);
153 
154     // If box is null, root is box's previous RootInlineBox, and previousBox is the last logical box in root.
155     int boxIndex = m_leafBoxes.size() - 1;
156     if (box)
157         boxIndex = boxIndexInLeaves(box) - 1;
158 
159     for (int i = boxIndex; i >= 0; --i) {
160         if (m_leafBoxes[i]->isInlineTextBox())
161             return toInlineTextBox(m_leafBoxes[i]);
162     }
163 
164     return 0;
165 }
166 
nextTextBox(const RootInlineBox * root,const InlineTextBox * box)167 const InlineTextBox* CachedLogicallyOrderedLeafBoxes::nextTextBox(const RootInlineBox* root, const InlineTextBox* box)
168 {
169     if (!root)
170         return 0;
171 
172     collectBoxes(root);
173 
174     // If box is null, root is box's next RootInlineBox, and nextBox is the first logical box in root.
175     // Otherwise, root is box's RootInlineBox, and nextBox is the next logical box in the same line.
176     size_t nextBoxIndex = 0;
177     if (box)
178         nextBoxIndex = boxIndexInLeaves(box) + 1;
179 
180     for (size_t i = nextBoxIndex; i < m_leafBoxes.size(); ++i) {
181         if (m_leafBoxes[i]->isInlineTextBox())
182             return toInlineTextBox(m_leafBoxes[i]);
183     }
184 
185     return 0;
186 }
187 
collectBoxes(const RootInlineBox * root)188 const Vector<InlineBox*>& CachedLogicallyOrderedLeafBoxes::collectBoxes(const RootInlineBox* root)
189 {
190     if (m_rootInlineBox != root) {
191         m_rootInlineBox = root;
192         m_leafBoxes.clear();
193         root->collectLeafBoxesInLogicalOrder(m_leafBoxes);
194     }
195     return m_leafBoxes;
196 }
197 
boxIndexInLeaves(const InlineTextBox * box) const198 int CachedLogicallyOrderedLeafBoxes::boxIndexInLeaves(const InlineTextBox* box) const
199 {
200     for (size_t i = 0; i < m_leafBoxes.size(); ++i) {
201         if (box == m_leafBoxes[i])
202             return i;
203     }
204     return 0;
205 }
206 
logicallyPreviousBox(const VisiblePosition & visiblePosition,const InlineTextBox * textBox,bool & previousBoxInDifferentBlock,CachedLogicallyOrderedLeafBoxes & leafBoxes)207 static const InlineTextBox* logicallyPreviousBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
208     bool& previousBoxInDifferentBlock, CachedLogicallyOrderedLeafBoxes& leafBoxes)
209 {
210     const InlineBox* startBox = textBox;
211 
212     const InlineTextBox* previousBox = leafBoxes.previousTextBox(&startBox->root(), textBox);
213     if (previousBox)
214         return previousBox;
215 
216     previousBox = leafBoxes.previousTextBox(startBox->root().prevRootBox(), 0);
217     if (previousBox)
218         return previousBox;
219 
220     while (1) {
221         Node* startNode = startBox->renderer().nonPseudoNode();
222         if (!startNode)
223             break;
224 
225         Position position = previousRootInlineBoxCandidatePosition(startNode, visiblePosition, ContentIsEditable);
226         if (position.isNull())
227             break;
228 
229         RenderedPosition renderedPosition(position, DOWNSTREAM);
230         RootInlineBox* previousRoot = renderedPosition.rootBox();
231         if (!previousRoot)
232             break;
233 
234         previousBox = leafBoxes.previousTextBox(previousRoot, 0);
235         if (previousBox) {
236             previousBoxInDifferentBlock = true;
237             return previousBox;
238         }
239 
240         if (!leafBoxes.size())
241             break;
242         startBox = leafBoxes.firstBox();
243     }
244     return 0;
245 }
246 
247 
logicallyNextBox(const VisiblePosition & visiblePosition,const InlineTextBox * textBox,bool & nextBoxInDifferentBlock,CachedLogicallyOrderedLeafBoxes & leafBoxes)248 static const InlineTextBox* logicallyNextBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
249     bool& nextBoxInDifferentBlock, CachedLogicallyOrderedLeafBoxes& leafBoxes)
250 {
251     const InlineBox* startBox = textBox;
252 
253     const InlineTextBox* nextBox = leafBoxes.nextTextBox(&startBox->root(), textBox);
254     if (nextBox)
255         return nextBox;
256 
257     nextBox = leafBoxes.nextTextBox(startBox->root().nextRootBox(), 0);
258     if (nextBox)
259         return nextBox;
260 
261     while (1) {
262         Node* startNode =startBox->renderer().nonPseudoNode();
263         if (!startNode)
264             break;
265 
266         Position position = nextRootInlineBoxCandidatePosition(startNode, visiblePosition, ContentIsEditable);
267         if (position.isNull())
268             break;
269 
270         RenderedPosition renderedPosition(position, DOWNSTREAM);
271         RootInlineBox* nextRoot = renderedPosition.rootBox();
272         if (!nextRoot)
273             break;
274 
275         nextBox = leafBoxes.nextTextBox(nextRoot, 0);
276         if (nextBox) {
277             nextBoxInDifferentBlock = true;
278             return nextBox;
279         }
280 
281         if (!leafBoxes.size())
282             break;
283         startBox = leafBoxes.firstBox();
284     }
285     return 0;
286 }
287 
wordBreakIteratorForMinOffsetBoundary(const VisiblePosition & visiblePosition,const InlineTextBox * textBox,int & previousBoxLength,bool & previousBoxInDifferentBlock,Vector<UChar,1024> & string,CachedLogicallyOrderedLeafBoxes & leafBoxes)288 static TextBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
289     int& previousBoxLength, bool& previousBoxInDifferentBlock, Vector<UChar, 1024>& string, CachedLogicallyOrderedLeafBoxes& leafBoxes)
290 {
291     previousBoxInDifferentBlock = false;
292 
293     // FIXME: Handle the case when we don't have an inline text box.
294     const InlineTextBox* previousBox = logicallyPreviousBox(visiblePosition, textBox, previousBoxInDifferentBlock, leafBoxes);
295 
296     int len = 0;
297     string.clear();
298     if (previousBox) {
299         previousBoxLength = previousBox->len();
300         previousBox->textRenderer().text().appendTo(string, previousBox->start(), previousBoxLength);
301         len += previousBoxLength;
302     }
303     textBox->textRenderer().text().appendTo(string, textBox->start(), textBox->len());
304     len += textBox->len();
305 
306     return wordBreakIterator(string.data(), len);
307 }
308 
wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition & visiblePosition,const InlineTextBox * textBox,bool & nextBoxInDifferentBlock,Vector<UChar,1024> & string,CachedLogicallyOrderedLeafBoxes & leafBoxes)309 static TextBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
310     bool& nextBoxInDifferentBlock, Vector<UChar, 1024>& string, CachedLogicallyOrderedLeafBoxes& leafBoxes)
311 {
312     nextBoxInDifferentBlock = false;
313 
314     // FIXME: Handle the case when we don't have an inline text box.
315     const InlineTextBox* nextBox = logicallyNextBox(visiblePosition, textBox, nextBoxInDifferentBlock, leafBoxes);
316 
317     int len = 0;
318     string.clear();
319     textBox->textRenderer().text().appendTo(string, textBox->start(), textBox->len());
320     len += textBox->len();
321     if (nextBox) {
322         nextBox->textRenderer().text().appendTo(string, nextBox->start(), nextBox->len());
323         len += nextBox->len();
324     }
325 
326     return wordBreakIterator(string.data(), len);
327 }
328 
isLogicalStartOfWord(TextBreakIterator * iter,int position,bool hardLineBreak)329 static bool isLogicalStartOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
330 {
331     bool boundary = hardLineBreak ? true : iter->isBoundary(position);
332     if (!boundary)
333         return false;
334 
335     iter->following(position);
336     // isWordTextBreak returns true after moving across a word and false after moving across a punctuation/space.
337     return isWordTextBreak(iter);
338 }
339 
islogicalEndOfWord(TextBreakIterator * iter,int position,bool hardLineBreak)340 static bool islogicalEndOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
341 {
342     bool boundary = iter->isBoundary(position);
343     return (hardLineBreak || boundary) && isWordTextBreak(iter);
344 }
345 
346 enum CursorMovementDirection { MoveLeft, MoveRight };
347 
visualWordPosition(const VisiblePosition & visiblePosition,CursorMovementDirection direction,bool skipsSpaceWhenMovingRight)348 static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition, CursorMovementDirection direction,
349     bool skipsSpaceWhenMovingRight)
350 {
351     if (visiblePosition.isNull())
352         return VisiblePosition();
353 
354     TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
355     InlineBox* previouslyVisitedBox = 0;
356     VisiblePosition current = visiblePosition;
357     TextBreakIterator* iter = 0;
358 
359     CachedLogicallyOrderedLeafBoxes leafBoxes;
360     Vector<UChar, 1024> string;
361 
362     while (1) {
363         VisiblePosition adjacentCharacterPosition = direction == MoveRight ? current.right(true) : current.left(true);
364         if (adjacentCharacterPosition == current || adjacentCharacterPosition.isNull())
365             return VisiblePosition();
366 
367         InlineBox* box;
368         int offsetInBox;
369         adjacentCharacterPosition.deepEquivalent().getInlineBoxAndOffset(UPSTREAM, box, offsetInBox);
370 
371         if (!box)
372             break;
373         if (!box->isInlineTextBox()) {
374             current = adjacentCharacterPosition;
375             continue;
376         }
377 
378         InlineTextBox* textBox = toInlineTextBox(box);
379         int previousBoxLength = 0;
380         bool previousBoxInDifferentBlock = false;
381         bool nextBoxInDifferentBlock = false;
382         bool movingIntoNewBox = previouslyVisitedBox != box;
383 
384         if (offsetInBox == box->caretMinOffset())
385             iter = wordBreakIteratorForMinOffsetBoundary(visiblePosition, textBox, previousBoxLength, previousBoxInDifferentBlock, string, leafBoxes);
386         else if (offsetInBox == box->caretMaxOffset())
387             iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, textBox, nextBoxInDifferentBlock, string, leafBoxes);
388         else if (movingIntoNewBox) {
389             iter = wordBreakIterator(textBox->textRenderer().text(), textBox->start(), textBox->len());
390             previouslyVisitedBox = box;
391         }
392 
393         if (!iter)
394             break;
395 
396         iter->first();
397         int offsetInIterator = offsetInBox - textBox->start() + previousBoxLength;
398 
399         bool isWordBreak;
400         bool boxHasSameDirectionalityAsBlock = box->direction() == blockDirection;
401         bool movingBackward = (direction == MoveLeft && box->direction() == LTR) || (direction == MoveRight && box->direction() == RTL);
402         if ((skipsSpaceWhenMovingRight && boxHasSameDirectionalityAsBlock)
403             || (!skipsSpaceWhenMovingRight && movingBackward)) {
404             bool logicalStartInRenderer = offsetInBox == static_cast<int>(textBox->start()) && previousBoxInDifferentBlock;
405             isWordBreak = isLogicalStartOfWord(iter, offsetInIterator, logicalStartInRenderer);
406         } else {
407             bool logicalEndInRenderer = offsetInBox == static_cast<int>(textBox->start() + textBox->len()) && nextBoxInDifferentBlock;
408             isWordBreak = islogicalEndOfWord(iter, offsetInIterator, logicalEndInRenderer);
409         }
410 
411         if (isWordBreak)
412             return adjacentCharacterPosition;
413 
414         current = adjacentCharacterPosition;
415     }
416     return VisiblePosition();
417 }
418 
leftWordPosition(const VisiblePosition & visiblePosition,bool skipsSpaceWhenMovingRight)419 VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition, bool skipsSpaceWhenMovingRight)
420 {
421     VisiblePosition leftWordBreak = visualWordPosition(visiblePosition, MoveLeft, skipsSpaceWhenMovingRight);
422     leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
423 
424     // FIXME: How should we handle a non-editable position?
425     if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
426         TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
427         leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition);
428     }
429     return leftWordBreak;
430 }
431 
rightWordPosition(const VisiblePosition & visiblePosition,bool skipsSpaceWhenMovingRight)432 VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition, bool skipsSpaceWhenMovingRight)
433 {
434     VisiblePosition rightWordBreak = visualWordPosition(visiblePosition, MoveRight, skipsSpaceWhenMovingRight);
435     rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);
436 
437     // FIXME: How should we handle a non-editable position?
438     if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
439         TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
440         rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition);
441     }
442     return rightWordBreak;
443 }
444 
445 
446 enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
447 
448 typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
449 
previousBoundary(const VisiblePosition & c,BoundarySearchFunction searchFunction)450 static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
451 {
452     Position pos = c.deepEquivalent();
453     Node* boundary = pos.parentEditingBoundary();
454     if (!boundary)
455         return VisiblePosition();
456 
457     Document& d = boundary->document();
458     Position start = createLegacyEditingPosition(boundary, 0).parentAnchoredEquivalent();
459     Position end = pos.parentAnchoredEquivalent();
460     RefPtrWillBeRawPtr<Range> searchRange = Range::create(d);
461 
462     Vector<UChar, 1024> string;
463     unsigned suffixLength = 0;
464 
465     TrackExceptionState exceptionState;
466     if (requiresContextForWordBoundary(c.characterBefore())) {
467         RefPtrWillBeRawPtr<Range> forwardsScanRange(d.createRange());
468         forwardsScanRange->setEndAfter(boundary, exceptionState);
469         forwardsScanRange->setStart(end.deprecatedNode(), end.deprecatedEditingOffset(), exceptionState);
470         TextIterator forwardsIterator(forwardsScanRange.get());
471         while (!forwardsIterator.atEnd()) {
472             Vector<UChar, 1024> characters;
473             forwardsIterator.appendTextTo(characters);
474             int i = endOfFirstWordBoundaryContext(characters.data(), characters.size());
475             string.append(characters.data(), i);
476             suffixLength += i;
477             if (static_cast<unsigned>(i) < characters.size())
478                 break;
479             forwardsIterator.advance();
480         }
481     }
482 
483     searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), exceptionState);
484     searchRange->setEnd(end.deprecatedNode(), end.deprecatedEditingOffset(), exceptionState);
485 
486     ASSERT(!exceptionState.hadException());
487     if (exceptionState.hadException())
488         return VisiblePosition();
489 
490     SimplifiedBackwardsTextIterator it(searchRange.get());
491     unsigned next = 0;
492     bool needMoreContext = false;
493     while (!it.atEnd()) {
494         bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style()->textSecurity() != TSNONE;
495         // iterate to get chunks until the searchFunction returns a non-zero value.
496         if (!inTextSecurityMode)
497             it.prependTextTo(string);
498         else {
499             // Treat bullets used in the text security mode as regular characters when looking for boundaries
500             Vector<UChar, 1024> iteratorString;
501             iteratorString.fill('x', it.length());
502             string.prepend(iteratorString.data(), iteratorString.size());
503         }
504         next = searchFunction(string.data(), string.size(), string.size() - suffixLength, MayHaveMoreContext, needMoreContext);
505         if (next)
506             break;
507         it.advance();
508     }
509     if (needMoreContext) {
510         // The last search returned the beginning of the buffer and asked for more context,
511         // but there is no earlier text. Force a search with what's available.
512         next = searchFunction(string.data(), string.size(), string.size() - suffixLength, DontHaveMoreContext, needMoreContext);
513         ASSERT(!needMoreContext);
514     }
515 
516     if (!next)
517         return VisiblePosition(it.atEnd() ? it.range()->startPosition() : pos, DOWNSTREAM);
518 
519     Node* node = it.range()->startContainer();
520     if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next))
521         // The next variable contains a usable index into a text node
522         return VisiblePosition(createLegacyEditingPosition(node, next), DOWNSTREAM);
523 
524     // Use the character iterator to translate the next value into a DOM position.
525     BackwardsCharacterIterator charIt(searchRange.get());
526     charIt.advance(string.size() - suffixLength - next);
527     // FIXME: charIt can get out of shadow host.
528     return VisiblePosition(charIt.range()->endPosition(), DOWNSTREAM);
529 }
530 
nextBoundary(const VisiblePosition & c,BoundarySearchFunction searchFunction)531 static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
532 {
533     Position pos = c.deepEquivalent();
534     Node* boundary = pos.parentEditingBoundary();
535     if (!boundary)
536         return VisiblePosition();
537 
538     Document& d = boundary->document();
539     RefPtrWillBeRawPtr<Range> searchRange(d.createRange());
540     Position start(pos.parentAnchoredEquivalent());
541 
542     Vector<UChar, 1024> string;
543     unsigned prefixLength = 0;
544 
545     if (requiresContextForWordBoundary(c.characterAfter())) {
546         RefPtrWillBeRawPtr<Range> backwardsScanRange(d.createRange());
547         backwardsScanRange->setEnd(start.deprecatedNode(), start.deprecatedEditingOffset(), IGNORE_EXCEPTION);
548         SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
549         while (!backwardsIterator.atEnd()) {
550             Vector<UChar, 1024> characters;
551             backwardsIterator.prependTextTo(characters);
552             int length = characters.size();
553             int i = startOfLastWordBoundaryContext(characters.data(), length);
554             string.prepend(characters.data() + i, length - i);
555             prefixLength += length - i;
556             if (i > 0)
557                 break;
558             backwardsIterator.advance();
559         }
560     }
561 
562     searchRange->selectNodeContents(boundary, IGNORE_EXCEPTION);
563     searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), IGNORE_EXCEPTION);
564     TextIterator it(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
565     const unsigned invalidOffset = static_cast<unsigned>(-1);
566     unsigned next = invalidOffset;
567     bool needMoreContext = false;
568     while (!it.atEnd()) {
569         // Keep asking the iterator for chunks until the search function
570         // returns an end value not equal to the length of the string passed to it.
571         bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style()->textSecurity() != TSNONE;
572         if (!inTextSecurityMode)
573             it.appendTextTo(string);
574         else {
575             // Treat bullets used in the text security mode as regular characters when looking for boundaries
576             Vector<UChar, 1024> iteratorString;
577             iteratorString.fill('x', it.length());
578             string.append(iteratorString.data(), iteratorString.size());
579         }
580         next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
581         if (next != string.size())
582             break;
583         it.advance();
584     }
585     if (needMoreContext) {
586         // The last search returned the end of the buffer and asked for more context,
587         // but there is no further text. Force a search with what's available.
588         next = searchFunction(string.data(), string.size(), prefixLength, DontHaveMoreContext, needMoreContext);
589         ASSERT(!needMoreContext);
590     }
591 
592     if (it.atEnd() && next == string.size()) {
593         pos = it.range()->startPosition();
594     } else if (next != invalidOffset && next != prefixLength) {
595         // Use the character iterator to translate the next value into a DOM position.
596         CharacterIterator charIt(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
597         charIt.advance(next - prefixLength - 1);
598         RefPtrWillBeRawPtr<Range> characterRange = charIt.range();
599         pos = characterRange->endPosition();
600 
601         if (charIt.characterAt(0) == '\n') {
602             // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
603             VisiblePosition visPos = VisiblePosition(pos);
604             if (visPos == VisiblePosition(characterRange->startPosition())) {
605                 charIt.advance(1);
606                 pos = charIt.range()->startPosition();
607             }
608         }
609     }
610 
611     // generate VisiblePosition, use UPSTREAM affinity if possible
612     return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
613 }
614 
615 // ---------
616 
startWordBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)617 static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
618 {
619     ASSERT(offset);
620     if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
621         needMoreContext = true;
622         return 0;
623     }
624     needMoreContext = false;
625     int start, end;
626     U16_BACK_1(characters, 0, offset);
627     findWordBoundary(characters, length, offset, &start, &end);
628     return start;
629 }
630 
startOfWord(const VisiblePosition & c,EWordSide side)631 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
632 {
633     // FIXME: This returns a null VP for c at the start of the document
634     // and side == LeftWordIfOnBoundary
635     VisiblePosition p = c;
636     if (side == RightWordIfOnBoundary) {
637         // at paragraph end, the startofWord is the current position
638         if (isEndOfParagraph(c))
639             return c;
640 
641         p = c.next();
642         if (p.isNull())
643             return c;
644     }
645     return previousBoundary(p, startWordBoundary);
646 }
647 
endWordBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)648 static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
649 {
650     ASSERT(offset <= length);
651     if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
652         needMoreContext = true;
653         return length;
654     }
655     needMoreContext = false;
656     int start, end;
657     findWordBoundary(characters, length, offset, &start, &end);
658     return end;
659 }
660 
endOfWord(const VisiblePosition & c,EWordSide side)661 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
662 {
663     VisiblePosition p = c;
664     if (side == LeftWordIfOnBoundary) {
665         if (isStartOfParagraph(c))
666             return c;
667 
668         p = c.previous();
669         if (p.isNull())
670             return c;
671     } else if (isEndOfParagraph(c))
672         return c;
673 
674     return nextBoundary(p, endWordBoundary);
675 }
676 
previousWordPositionBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)677 static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
678 {
679     if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
680         needMoreContext = true;
681         return 0;
682     }
683     needMoreContext = false;
684     return findNextWordFromIndex(characters, length, offset, false);
685 }
686 
previousWordPosition(const VisiblePosition & c)687 VisiblePosition previousWordPosition(const VisiblePosition &c)
688 {
689     VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
690     return c.honorEditingBoundaryAtOrBefore(prev);
691 }
692 
nextWordPositionBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)693 static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
694 {
695     if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
696         needMoreContext = true;
697         return length;
698     }
699     needMoreContext = false;
700     return findNextWordFromIndex(characters, length, offset, true);
701 }
702 
nextWordPosition(const VisiblePosition & c)703 VisiblePosition nextWordPosition(const VisiblePosition &c)
704 {
705     VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
706     return c.honorEditingBoundaryAtOrAfter(next);
707 }
708 
709 // ---------
710 
711 enum LineEndpointComputationMode { UseLogicalOrdering, UseInlineBoxOrdering };
startPositionForLine(const VisiblePosition & c,LineEndpointComputationMode mode)712 static VisiblePosition startPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
713 {
714     if (c.isNull())
715         return VisiblePosition();
716 
717     RootInlineBox* rootBox = RenderedPosition(c).rootBox();
718     if (!rootBox) {
719         // There are VisiblePositions at offset 0 in blocks without
720         // RootInlineBoxes, like empty editable blocks and bordered blocks.
721         Position p = c.deepEquivalent();
722         if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
723             return c;
724 
725         return VisiblePosition();
726     }
727 
728     Node* startNode;
729     InlineBox* startBox;
730     if (mode == UseLogicalOrdering) {
731         startNode = rootBox->getLogicalStartBoxWithNode(startBox);
732         if (!startNode)
733             return VisiblePosition();
734     } else {
735         // Generated content (e.g. list markers and CSS :before and :after pseudoelements) have no corresponding DOM element,
736         // and so cannot be represented by a VisiblePosition. Use whatever follows instead.
737         startBox = rootBox->firstLeafChild();
738         while (true) {
739             if (!startBox)
740                 return VisiblePosition();
741 
742             startNode = startBox->renderer().nonPseudoNode();
743             if (startNode)
744                 break;
745 
746             startBox = startBox->nextLeafChild();
747         }
748     }
749 
750     return VisiblePosition(startNode->isTextNode() ? Position(toText(startNode), toInlineTextBox(startBox)->start()) : positionBeforeNode(startNode));
751 }
752 
startOfLine(const VisiblePosition & c,LineEndpointComputationMode mode)753 static VisiblePosition startOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
754 {
755     // TODO: this is the current behavior that might need to be fixed.
756     // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
757     VisiblePosition visPos = startPositionForLine(c, mode);
758 
759     if (mode == UseLogicalOrdering) {
760         if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
761             if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
762                 return VisiblePosition(firstPositionInNode(editableRoot));
763         }
764     }
765 
766     return c.honorEditingBoundaryAtOrBefore(visPos);
767 }
768 
769 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
startOfLine(const VisiblePosition & currentPosition)770 VisiblePosition startOfLine(const VisiblePosition& currentPosition)
771 {
772     return startOfLine(currentPosition, UseInlineBoxOrdering);
773 }
774 
logicalStartOfLine(const VisiblePosition & currentPosition)775 VisiblePosition logicalStartOfLine(const VisiblePosition& currentPosition)
776 {
777     return startOfLine(currentPosition, UseLogicalOrdering);
778 }
779 
endPositionForLine(const VisiblePosition & c,LineEndpointComputationMode mode)780 static VisiblePosition endPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
781 {
782     if (c.isNull())
783         return VisiblePosition();
784 
785     RootInlineBox* rootBox = RenderedPosition(c).rootBox();
786     if (!rootBox) {
787         // There are VisiblePositions at offset 0 in blocks without
788         // RootInlineBoxes, like empty editable blocks and bordered blocks.
789         Position p = c.deepEquivalent();
790         if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
791             return c;
792         return VisiblePosition();
793     }
794 
795     Node* endNode;
796     InlineBox* endBox;
797     if (mode == UseLogicalOrdering) {
798         endNode = rootBox->getLogicalEndBoxWithNode(endBox);
799         if (!endNode)
800             return VisiblePosition();
801     } else {
802         // Generated content (e.g. list markers and CSS :before and :after pseudoelements) have no corresponding DOM element,
803         // and so cannot be represented by a VisiblePosition. Use whatever precedes instead.
804         endBox = rootBox->lastLeafChild();
805         while (true) {
806             if (!endBox)
807                 return VisiblePosition();
808 
809             endNode = endBox->renderer().nonPseudoNode();
810             if (endNode)
811                 break;
812 
813             endBox = endBox->prevLeafChild();
814         }
815     }
816 
817     Position pos;
818     if (isHTMLBRElement(*endNode))
819         pos = positionBeforeNode(endNode);
820     else if (endBox->isInlineTextBox() && endNode->isTextNode()) {
821         InlineTextBox* endTextBox = toInlineTextBox(endBox);
822         int endOffset = endTextBox->start();
823         if (!endTextBox->isLineBreak())
824             endOffset += endTextBox->len();
825         pos = Position(toText(endNode), endOffset);
826     } else
827         pos = positionAfterNode(endNode);
828 
829     return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
830 }
831 
inSameLogicalLine(const VisiblePosition & a,const VisiblePosition & b)832 static bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b)
833 {
834     return a.isNotNull() && logicalStartOfLine(a) == logicalStartOfLine(b);
835 }
836 
endOfLine(const VisiblePosition & c,LineEndpointComputationMode mode)837 static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
838 {
839     // TODO: this is the current behavior that might need to be fixed.
840     // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
841     VisiblePosition visPos = endPositionForLine(c, mode);
842 
843     if (mode == UseLogicalOrdering) {
844         // Make sure the end of line is at the same line as the given input position. For a wrapping line, the logical end
845         // position for the not-last-2-lines might incorrectly hand back the logical beginning of the next line.
846         // For example, <div contenteditable dir="rtl" style="line-break:before-white-space">abcdefg abcdefg abcdefg
847         // a abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg </div>
848         // In this case, use the previous position of the computed logical end position.
849         if (!inSameLogicalLine(c, visPos))
850             visPos = visPos.previous();
851 
852         if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
853             if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
854                 return VisiblePosition(lastPositionInNode(editableRoot));
855         }
856 
857         return c.honorEditingBoundaryAtOrAfter(visPos);
858     }
859 
860     // Make sure the end of line is at the same line as the given input position. Else use the previous position to
861     // obtain end of line. This condition happens when the input position is before the space character at the end
862     // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
863     // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
864     // versus lines without that style, which would break before a space by default.
865     if (!inSameLine(c, visPos)) {
866         visPos = c.previous();
867         if (visPos.isNull())
868             return VisiblePosition();
869         visPos = endPositionForLine(visPos, UseInlineBoxOrdering);
870     }
871 
872     return c.honorEditingBoundaryAtOrAfter(visPos);
873 }
874 
875 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
endOfLine(const VisiblePosition & currentPosition)876 VisiblePosition endOfLine(const VisiblePosition& currentPosition)
877 {
878     return endOfLine(currentPosition, UseInlineBoxOrdering);
879 }
880 
logicalEndOfLine(const VisiblePosition & currentPosition)881 VisiblePosition logicalEndOfLine(const VisiblePosition& currentPosition)
882 {
883     return endOfLine(currentPosition, UseLogicalOrdering);
884 }
885 
inSameLine(const VisiblePosition & a,const VisiblePosition & b)886 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
887 {
888     return a.isNotNull() && startOfLine(a) == startOfLine(b);
889 }
890 
isStartOfLine(const VisiblePosition & p)891 bool isStartOfLine(const VisiblePosition &p)
892 {
893     return p.isNotNull() && p == startOfLine(p);
894 }
895 
isEndOfLine(const VisiblePosition & p)896 bool isEndOfLine(const VisiblePosition &p)
897 {
898     return p.isNotNull() && p == endOfLine(p);
899 }
900 
absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox * root,int lineDirectionPoint)901 static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint)
902 {
903     ASSERT(root);
904     RenderBlockFlow& containingBlock = root->block();
905     FloatPoint absoluteBlockPoint = containingBlock.localToAbsolute(FloatPoint());
906     if (containingBlock.hasOverflowClip())
907         absoluteBlockPoint -= containingBlock.scrolledContentOffset();
908 
909     if (root->block().isHorizontalWritingMode())
910         return IntPoint(lineDirectionPoint - absoluteBlockPoint.x(), root->blockDirectionPointInLine());
911 
912     return IntPoint(root->blockDirectionPointInLine(), lineDirectionPoint - absoluteBlockPoint.y());
913 }
914 
previousLinePosition(const VisiblePosition & visiblePosition,int lineDirectionPoint,EditableType editableType)915 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
916 {
917     Position p = visiblePosition.deepEquivalent();
918     Node* node = p.deprecatedNode();
919 
920     if (!node)
921         return VisiblePosition();
922 
923     node->document().updateLayoutIgnorePendingStylesheets();
924 
925     RenderObject* renderer = node->renderer();
926     if (!renderer)
927         return VisiblePosition();
928 
929     RootInlineBox* root = 0;
930     InlineBox* box;
931     int ignoredCaretOffset;
932     visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
933     if (box) {
934         root = box->root().prevRootBox();
935         // We want to skip zero height boxes.
936         // This could happen in case it is a TrailingFloatsRootInlineBox.
937         if (!root || !root->logicalHeight() || !root->firstLeafChild())
938             root = 0;
939     }
940 
941     if (!root) {
942         Position position = previousRootInlineBoxCandidatePosition(node, visiblePosition, editableType);
943         if (position.isNotNull()) {
944             RenderedPosition renderedPosition((VisiblePosition(position)));
945             root = renderedPosition.rootBox();
946             if (!root)
947                 return VisiblePosition(position);
948         }
949     }
950 
951     if (root) {
952         // FIXME: Can be wrong for multi-column layout and with transforms.
953         IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
954         RenderObject& renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
955         Node* node = renderer.node();
956         if (node && editingIgnoresContent(node))
957             return VisiblePosition(positionInParentBeforeNode(*node));
958         return VisiblePosition(renderer.positionForPoint(pointInLine));
959     }
960 
961     // Could not find a previous line. This means we must already be on the first line.
962     // Move to the start of the content in this block, which effectively moves us
963     // to the start of the line we're on.
964     Element* rootElement = node->rendererIsEditable(editableType) ? node->rootEditableElement(editableType) : node->document().documentElement();
965     if (!rootElement)
966         return VisiblePosition();
967     return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM);
968 }
969 
nextLinePosition(const VisiblePosition & visiblePosition,int lineDirectionPoint,EditableType editableType)970 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
971 {
972     Position p = visiblePosition.deepEquivalent();
973     Node* node = p.deprecatedNode();
974 
975     if (!node)
976         return VisiblePosition();
977 
978     node->document().updateLayoutIgnorePendingStylesheets();
979 
980     RenderObject* renderer = node->renderer();
981     if (!renderer)
982         return VisiblePosition();
983 
984     RootInlineBox* root = 0;
985     InlineBox* box;
986     int ignoredCaretOffset;
987     visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
988     if (box) {
989         root = box->root().nextRootBox();
990         // We want to skip zero height boxes.
991         // This could happen in case it is a TrailingFloatsRootInlineBox.
992         if (!root || !root->logicalHeight() || !root->firstLeafChild())
993             root = 0;
994     }
995 
996     if (!root) {
997         // FIXME: We need do the same in previousLinePosition.
998         Node* child = node->traverseToChildAt(p.deprecatedEditingOffset());
999         node = child ? child : &node->lastDescendantOrSelf();
1000         Position position = nextRootInlineBoxCandidatePosition(node, visiblePosition, editableType);
1001         if (position.isNotNull()) {
1002             RenderedPosition renderedPosition((VisiblePosition(position)));
1003             root = renderedPosition.rootBox();
1004             if (!root)
1005                 return VisiblePosition(position);
1006         }
1007     }
1008 
1009     if (root) {
1010         // FIXME: Can be wrong for multi-column layout and with transforms.
1011         IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
1012         RenderObject& renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
1013         Node* node = renderer.node();
1014         if (node && editingIgnoresContent(node))
1015             return VisiblePosition(positionInParentBeforeNode(*node));
1016         return VisiblePosition(renderer.positionForPoint(pointInLine));
1017     }
1018 
1019     // Could not find a next line. This means we must already be on the last line.
1020     // Move to the end of the content in this block, which effectively moves us
1021     // to the end of the line we're on.
1022     Element* rootElement = node->rendererIsEditable(editableType) ? node->rootEditableElement(editableType) : node->document().documentElement();
1023     if (!rootElement)
1024         return VisiblePosition();
1025     return VisiblePosition(lastPositionInNode(rootElement), DOWNSTREAM);
1026 }
1027 
1028 // ---------
1029 
startSentenceBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1030 static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1031 {
1032     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1033     // FIXME: The following function can return -1; we don't handle that.
1034     return iterator->preceding(length);
1035 }
1036 
startOfSentence(const VisiblePosition & c)1037 VisiblePosition startOfSentence(const VisiblePosition &c)
1038 {
1039     return previousBoundary(c, startSentenceBoundary);
1040 }
1041 
endSentenceBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1042 static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1043 {
1044     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1045     return iterator->next();
1046 }
1047 
1048 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
endOfSentence(const VisiblePosition & c)1049 VisiblePosition endOfSentence(const VisiblePosition &c)
1050 {
1051     return nextBoundary(c, endSentenceBoundary);
1052 }
1053 
previousSentencePositionBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1054 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1055 {
1056     // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
1057     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1058     // FIXME: The following function can return -1; we don't handle that.
1059     return iterator->preceding(length);
1060 }
1061 
previousSentencePosition(const VisiblePosition & c)1062 VisiblePosition previousSentencePosition(const VisiblePosition &c)
1063 {
1064     VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
1065     return c.honorEditingBoundaryAtOrBefore(prev);
1066 }
1067 
nextSentencePositionBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1068 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1069 {
1070     // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
1071     // move to the equivlant position in the following sentence.
1072     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1073     return iterator->following(0);
1074 }
1075 
nextSentencePosition(const VisiblePosition & c)1076 VisiblePosition nextSentencePosition(const VisiblePosition &c)
1077 {
1078     VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
1079     return c.honorEditingBoundaryAtOrAfter(next);
1080 }
1081 
startOfParagraph(const VisiblePosition & c,EditingBoundaryCrossingRule boundaryCrossingRule)1082 VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
1083 {
1084     Position p = c.deepEquivalent();
1085     Node* startNode = p.deprecatedNode();
1086 
1087     if (!startNode)
1088         return VisiblePosition();
1089 
1090     if (isRenderedAsNonInlineTableImageOrHR(startNode))
1091         return VisiblePosition(positionBeforeNode(startNode));
1092 
1093     Node* startBlock = enclosingBlock(startNode);
1094 
1095     Node* node = startNode;
1096     Node* highestRoot = highestEditableRoot(p);
1097     int offset = p.deprecatedEditingOffset();
1098     Position::AnchorType type = p.anchorType();
1099 
1100     Node* n = startNode;
1101     bool startNodeIsEditable = startNode->rendererIsEditable();
1102     while (n) {
1103         if (boundaryCrossingRule == CannotCrossEditingBoundary && !Position::nodeIsUserSelectAll(n) && n->rendererIsEditable() != startNodeIsEditable)
1104             break;
1105         if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
1106             while (n && n->rendererIsEditable() != startNodeIsEditable)
1107                 n = NodeTraversal::previousPostOrder(*n, startBlock);
1108             if (!n || !n->isDescendantOf(highestRoot))
1109                 break;
1110         }
1111         RenderObject* r = n->renderer();
1112         if (!r) {
1113             n = NodeTraversal::previousPostOrder(*n, startBlock);
1114             continue;
1115         }
1116         RenderStyle* style = r->style();
1117         if (style->visibility() != VISIBLE) {
1118             n = NodeTraversal::previousPostOrder(*n, startBlock);
1119             continue;
1120         }
1121 
1122         if (r->isBR() || isBlock(n))
1123             break;
1124 
1125         if (r->isText() && toRenderText(r)->renderedTextLength()) {
1126             ASSERT_WITH_SECURITY_IMPLICATION(n->isTextNode());
1127             type = Position::PositionIsOffsetInAnchor;
1128             if (style->preserveNewline()) {
1129                 RenderText* text = toRenderText(r);
1130                 int i = text->textLength();
1131                 int o = offset;
1132                 if (n == startNode && o < i)
1133                     i = max(0, o);
1134                 while (--i >= 0) {
1135                     if ((*text)[i] == '\n')
1136                         return VisiblePosition(Position(toText(n), i + 1), DOWNSTREAM);
1137                 }
1138             }
1139             node = n;
1140             offset = 0;
1141             n = NodeTraversal::previousPostOrder(*n, startBlock);
1142         } else if (editingIgnoresContent(n) || isRenderedTable(n)) {
1143             node = n;
1144             type = Position::PositionIsBeforeAnchor;
1145             n = n->previousSibling() ? n->previousSibling() : NodeTraversal::previousPostOrder(*n, startBlock);
1146         } else {
1147             n = NodeTraversal::previousPostOrder(*n, startBlock);
1148         }
1149     }
1150 
1151     if (type == Position::PositionIsOffsetInAnchor) {
1152         ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
1153         return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
1154     }
1155 
1156     return VisiblePosition(Position(node, type), DOWNSTREAM);
1157 }
1158 
endOfParagraph(const VisiblePosition & c,EditingBoundaryCrossingRule boundaryCrossingRule)1159 VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossingRule boundaryCrossingRule)
1160 {
1161     if (c.isNull())
1162         return VisiblePosition();
1163 
1164     Position p = c.deepEquivalent();
1165     Node* startNode = p.deprecatedNode();
1166 
1167     if (isRenderedAsNonInlineTableImageOrHR(startNode))
1168         return VisiblePosition(positionAfterNode(startNode));
1169 
1170     Node* startBlock = enclosingBlock(startNode);
1171     Node* stayInsideBlock = startBlock;
1172 
1173     Node* node = startNode;
1174     Node* highestRoot = highestEditableRoot(p);
1175     int offset = p.deprecatedEditingOffset();
1176     Position::AnchorType type = p.anchorType();
1177 
1178     Node* n = startNode;
1179     bool startNodeIsEditable = startNode->rendererIsEditable();
1180     while (n) {
1181         if (boundaryCrossingRule == CannotCrossEditingBoundary && !Position::nodeIsUserSelectAll(n) && n->rendererIsEditable() != startNodeIsEditable)
1182             break;
1183         if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
1184             while (n && n->rendererIsEditable() != startNodeIsEditable)
1185                 n = NodeTraversal::next(*n, stayInsideBlock);
1186             if (!n || !n->isDescendantOf(highestRoot))
1187                 break;
1188         }
1189 
1190         RenderObject* r = n->renderer();
1191         if (!r) {
1192             n = NodeTraversal::next(*n, stayInsideBlock);
1193             continue;
1194         }
1195         RenderStyle* style = r->style();
1196         if (style->visibility() != VISIBLE) {
1197             n = NodeTraversal::next(*n, stayInsideBlock);
1198             continue;
1199         }
1200 
1201         if (r->isBR() || isBlock(n))
1202             break;
1203 
1204         // FIXME: We avoid returning a position where the renderer can't accept the caret.
1205         if (r->isText() && toRenderText(r)->renderedTextLength()) {
1206             ASSERT_WITH_SECURITY_IMPLICATION(n->isTextNode());
1207             int length = toRenderText(r)->textLength();
1208             type = Position::PositionIsOffsetInAnchor;
1209             if (style->preserveNewline()) {
1210                 RenderText* text = toRenderText(r);
1211                 int o = n == startNode ? offset : 0;
1212                 for (int i = o; i < length; ++i) {
1213                     if ((*text)[i] == '\n')
1214                         return VisiblePosition(Position(toText(n), i), DOWNSTREAM);
1215                 }
1216             }
1217             node = n;
1218             offset = r->caretMaxOffset();
1219             n = NodeTraversal::next(*n, stayInsideBlock);
1220         } else if (editingIgnoresContent(n) || isRenderedTable(n)) {
1221             node = n;
1222             type = Position::PositionIsAfterAnchor;
1223             n = NodeTraversal::nextSkippingChildren(*n, stayInsideBlock);
1224         } else {
1225             n = NodeTraversal::next(*n, stayInsideBlock);
1226         }
1227     }
1228 
1229     if (type == Position::PositionIsOffsetInAnchor)
1230         return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
1231 
1232     return VisiblePosition(Position(node, type), DOWNSTREAM);
1233 }
1234 
1235 // FIXME: isStartOfParagraph(startOfNextParagraph(pos)) is not always true
startOfNextParagraph(const VisiblePosition & visiblePosition)1236 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
1237 {
1238     VisiblePosition paragraphEnd(endOfParagraph(visiblePosition, CanSkipOverEditingBoundary));
1239     VisiblePosition afterParagraphEnd(paragraphEnd.next(CannotCrossEditingBoundary));
1240     // The position after the last position in the last cell of a table
1241     // is not the start of the next paragraph.
1242     if (isFirstPositionAfterTable(afterParagraphEnd))
1243         return afterParagraphEnd.next(CannotCrossEditingBoundary);
1244     return afterParagraphEnd;
1245 }
1246 
inSameParagraph(const VisiblePosition & a,const VisiblePosition & b,EditingBoundaryCrossingRule boundaryCrossingRule)1247 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b, EditingBoundaryCrossingRule boundaryCrossingRule)
1248 {
1249     return a.isNotNull() && startOfParagraph(a, boundaryCrossingRule) == startOfParagraph(b, boundaryCrossingRule);
1250 }
1251 
isStartOfParagraph(const VisiblePosition & pos,EditingBoundaryCrossingRule boundaryCrossingRule)1252 bool isStartOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
1253 {
1254     return pos.isNotNull() && pos == startOfParagraph(pos, boundaryCrossingRule);
1255 }
1256 
isEndOfParagraph(const VisiblePosition & pos,EditingBoundaryCrossingRule boundaryCrossingRule)1257 bool isEndOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
1258 {
1259     return pos.isNotNull() && pos == endOfParagraph(pos, boundaryCrossingRule);
1260 }
1261 
previousParagraphPosition(const VisiblePosition & p,int x)1262 VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x)
1263 {
1264     VisiblePosition pos = p;
1265     do {
1266         VisiblePosition n = previousLinePosition(pos, x);
1267         if (n.isNull() || n == pos)
1268             break;
1269         pos = n;
1270     } while (inSameParagraph(p, pos));
1271     return pos;
1272 }
1273 
nextParagraphPosition(const VisiblePosition & p,int x)1274 VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x)
1275 {
1276     VisiblePosition pos = p;
1277     do {
1278         VisiblePosition n = nextLinePosition(pos, x);
1279         if (n.isNull() || n == pos)
1280             break;
1281         pos = n;
1282     } while (inSameParagraph(p, pos));
1283     return pos;
1284 }
1285 
1286 // ---------
1287 
startOfBlock(const VisiblePosition & visiblePosition,EditingBoundaryCrossingRule rule)1288 VisiblePosition startOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
1289 {
1290     Position position = visiblePosition.deepEquivalent();
1291     Node* startBlock;
1292     if (!position.containerNode() || !(startBlock = enclosingBlock(position.containerNode(), rule)))
1293         return VisiblePosition();
1294     return VisiblePosition(firstPositionInNode(startBlock));
1295 }
1296 
endOfBlock(const VisiblePosition & visiblePosition,EditingBoundaryCrossingRule rule)1297 VisiblePosition endOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
1298 {
1299     Position position = visiblePosition.deepEquivalent();
1300     Node* endBlock;
1301     if (!position.containerNode() || !(endBlock = enclosingBlock(position.containerNode(), rule)))
1302         return VisiblePosition();
1303     return VisiblePosition(lastPositionInNode(endBlock));
1304 }
1305 
inSameBlock(const VisiblePosition & a,const VisiblePosition & b)1306 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
1307 {
1308     return !a.isNull() && enclosingBlock(a.deepEquivalent().containerNode()) == enclosingBlock(b.deepEquivalent().containerNode());
1309 }
1310 
isStartOfBlock(const VisiblePosition & pos)1311 bool isStartOfBlock(const VisiblePosition &pos)
1312 {
1313     return pos.isNotNull() && pos == startOfBlock(pos, CanCrossEditingBoundary);
1314 }
1315 
isEndOfBlock(const VisiblePosition & pos)1316 bool isEndOfBlock(const VisiblePosition &pos)
1317 {
1318     return pos.isNotNull() && pos == endOfBlock(pos, CanCrossEditingBoundary);
1319 }
1320 
1321 // ---------
1322 
startOfDocument(const Node * node)1323 VisiblePosition startOfDocument(const Node* node)
1324 {
1325     if (!node || !node->document().documentElement())
1326         return VisiblePosition();
1327 
1328     return VisiblePosition(firstPositionInNode(node->document().documentElement()), DOWNSTREAM);
1329 }
1330 
startOfDocument(const VisiblePosition & c)1331 VisiblePosition startOfDocument(const VisiblePosition &c)
1332 {
1333     return startOfDocument(c.deepEquivalent().deprecatedNode());
1334 }
1335 
endOfDocument(const Node * node)1336 VisiblePosition endOfDocument(const Node* node)
1337 {
1338     if (!node || !node->document().documentElement())
1339         return VisiblePosition();
1340 
1341     Element* doc = node->document().documentElement();
1342     return VisiblePosition(lastPositionInNode(doc), DOWNSTREAM);
1343 }
1344 
endOfDocument(const VisiblePosition & c)1345 VisiblePosition endOfDocument(const VisiblePosition &c)
1346 {
1347     return endOfDocument(c.deepEquivalent().deprecatedNode());
1348 }
1349 
isStartOfDocument(const VisiblePosition & p)1350 bool isStartOfDocument(const VisiblePosition &p)
1351 {
1352     return p.isNotNull() && p.previous(CanCrossEditingBoundary).isNull();
1353 }
1354 
isEndOfDocument(const VisiblePosition & p)1355 bool isEndOfDocument(const VisiblePosition &p)
1356 {
1357     return p.isNotNull() && p.next(CanCrossEditingBoundary).isNull();
1358 }
1359 
1360 // ---------
1361 
startOfEditableContent(const VisiblePosition & visiblePosition)1362 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
1363 {
1364     Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1365     if (!highestRoot)
1366         return VisiblePosition();
1367 
1368     return VisiblePosition(firstPositionInNode(highestRoot));
1369 }
1370 
endOfEditableContent(const VisiblePosition & visiblePosition)1371 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
1372 {
1373     Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1374     if (!highestRoot)
1375         return VisiblePosition();
1376 
1377     return VisiblePosition(lastPositionInNode(highestRoot));
1378 }
1379 
isEndOfEditableOrNonEditableContent(const VisiblePosition & p)1380 bool isEndOfEditableOrNonEditableContent(const VisiblePosition &p)
1381 {
1382     return p.isNotNull() && p.next().isNull();
1383 }
1384 
leftBoundaryOfLine(const VisiblePosition & c,TextDirection direction)1385 VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1386 {
1387     return direction == LTR ? logicalStartOfLine(c) : logicalEndOfLine(c);
1388 }
1389 
rightBoundaryOfLine(const VisiblePosition & c,TextDirection direction)1390 VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1391 {
1392     return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
1393 }
1394 
1395 }
1396