• 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/core/v8/ExceptionState.h"
30 #include "bindings/core/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 blink {
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->hasEditableStyle(editableType);
57     node = node->previousLeafNode();
58     while (node) {
59         if (editable == node->hasEditableStyle(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->hasEditableStyle(editableType);
72     node = node->nextLeafNode();
73     while (node) {
74         if (editable == node->hasEditableStyle(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     ContainerNode* 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     ContainerNode* 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->renderer().text().appendTo(string, previousBox->start(), previousBoxLength);
301         len += previousBoxLength;
302     }
303     textBox->renderer().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->renderer().text().appendTo(string, textBox->start(), textBox->len());
320     len += textBox->len();
321     if (nextBox) {
322         nextBox->renderer().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->renderer().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 
461     Vector<UChar, 1024> string;
462     unsigned suffixLength = 0;
463 
464     TrackExceptionState exceptionState;
465     if (requiresContextForWordBoundary(c.characterBefore())) {
466         RefPtrWillBeRawPtr<Range> forwardsScanRange(d.createRange());
467         forwardsScanRange->setEndAfter(boundary, exceptionState);
468         forwardsScanRange->setStart(end.deprecatedNode(), end.deprecatedEditingOffset(), exceptionState);
469         TextIterator forwardsIterator(forwardsScanRange.get());
470         while (!forwardsIterator.atEnd()) {
471             Vector<UChar, 1024> characters;
472             forwardsIterator.appendTextTo(characters);
473             int i = endOfFirstWordBoundaryContext(characters.data(), characters.size());
474             string.append(characters.data(), i);
475             suffixLength += i;
476             if (static_cast<unsigned>(i) < characters.size())
477                 break;
478             forwardsIterator.advance();
479         }
480     }
481 
482     ASSERT(!exceptionState.hadException());
483     if (exceptionState.hadException())
484         return VisiblePosition();
485 
486     SimplifiedBackwardsTextIterator it(start, end);
487     unsigned next = 0;
488     bool needMoreContext = false;
489     while (!it.atEnd()) {
490         bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style()->textSecurity() != TSNONE;
491         // iterate to get chunks until the searchFunction returns a non-zero value.
492         if (!inTextSecurityMode)
493             it.prependTextTo(string);
494         else {
495             // Treat bullets used in the text security mode as regular characters when looking for boundaries
496             Vector<UChar, 1024> iteratorString;
497             iteratorString.fill('x', it.length());
498             string.prepend(iteratorString.data(), iteratorString.size());
499         }
500         next = searchFunction(string.data(), string.size(), string.size() - suffixLength, MayHaveMoreContext, needMoreContext);
501         if (next)
502             break;
503         it.advance();
504     }
505     if (needMoreContext) {
506         // The last search returned the beginning of the buffer and asked for more context,
507         // but there is no earlier text. Force a search with what's available.
508         next = searchFunction(string.data(), string.size(), string.size() - suffixLength, DontHaveMoreContext, needMoreContext);
509         ASSERT(!needMoreContext);
510     }
511 
512     if (!next)
513         return VisiblePosition(it.atEnd() ? it.startPosition() : pos, DOWNSTREAM);
514 
515     Node* node = it.startContainer();
516     if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next))
517         // The next variable contains a usable index into a text node
518         return VisiblePosition(createLegacyEditingPosition(node, next), DOWNSTREAM);
519 
520     // Use the character iterator to translate the next value into a DOM position.
521     BackwardsCharacterIterator charIt(start, end);
522     charIt.advance(string.size() - suffixLength - next);
523     // FIXME: charIt can get out of shadow host.
524     return VisiblePosition(charIt.endPosition(), DOWNSTREAM);
525 }
526 
nextBoundary(const VisiblePosition & c,BoundarySearchFunction searchFunction)527 static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
528 {
529     Position pos = c.deepEquivalent();
530     Node* boundary = pos.parentEditingBoundary();
531     if (!boundary)
532         return VisiblePosition();
533 
534     Document& d = boundary->document();
535     Position start(pos.parentAnchoredEquivalent());
536 
537     Vector<UChar, 1024> string;
538     unsigned prefixLength = 0;
539 
540     if (requiresContextForWordBoundary(c.characterAfter())) {
541         RefPtrWillBeRawPtr<Range> backwardsScanRange(d.createRange());
542         backwardsScanRange->setEnd(start.deprecatedNode(), start.deprecatedEditingOffset(), IGNORE_EXCEPTION);
543         SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
544         while (!backwardsIterator.atEnd()) {
545             Vector<UChar, 1024> characters;
546             backwardsIterator.prependTextTo(characters);
547             int length = characters.size();
548             int i = startOfLastWordBoundaryContext(characters.data(), length);
549             string.prepend(characters.data() + i, length - i);
550             prefixLength += length - i;
551             if (i > 0)
552                 break;
553             backwardsIterator.advance();
554         }
555     }
556 
557     Position searchStart = createLegacyEditingPosition(start.deprecatedNode(), start.deprecatedEditingOffset());
558     RangeBoundaryPoint searchEndPoint(boundary);
559     searchEndPoint.setToEndOfNode(*boundary);
560     Position searchEnd = searchEndPoint.toPosition();
561     TextIterator it(searchStart, searchEnd, TextIteratorEmitsCharactersBetweenAllVisiblePositions);
562     const unsigned invalidOffset = static_cast<unsigned>(-1);
563     unsigned next = invalidOffset;
564     bool needMoreContext = false;
565     while (!it.atEnd()) {
566         // Keep asking the iterator for chunks until the search function
567         // returns an end value not equal to the length of the string passed to it.
568         bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style()->textSecurity() != TSNONE;
569         if (!inTextSecurityMode)
570             it.appendTextTo(string);
571         else {
572             // Treat bullets used in the text security mode as regular characters when looking for boundaries
573             Vector<UChar, 1024> iteratorString;
574             iteratorString.fill('x', it.length());
575             string.append(iteratorString.data(), iteratorString.size());
576         }
577         next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
578         if (next != string.size())
579             break;
580         it.advance();
581     }
582     if (needMoreContext) {
583         // The last search returned the end of the buffer and asked for more context,
584         // but there is no further text. Force a search with what's available.
585         next = searchFunction(string.data(), string.size(), prefixLength, DontHaveMoreContext, needMoreContext);
586         ASSERT(!needMoreContext);
587     }
588 
589     if (it.atEnd() && next == string.size()) {
590         pos = it.startPosition();
591     } else if (next != invalidOffset && next != prefixLength) {
592         // Use the character iterator to translate the next value into a DOM position.
593         CharacterIterator charIt(searchStart, searchEnd, TextIteratorEmitsCharactersBetweenAllVisiblePositions);
594         charIt.advance(next - prefixLength - 1);
595         pos = charIt.endPosition();
596 
597         if (charIt.characterAt(0) == '\n') {
598             // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
599             VisiblePosition visPos = VisiblePosition(pos);
600             if (visPos == VisiblePosition(charIt.startPosition())) {
601                 charIt.advance(1);
602                 pos = charIt.startPosition();
603             }
604         }
605     }
606 
607     // generate VisiblePosition, use UPSTREAM affinity if possible
608     return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
609 }
610 
611 // ---------
612 
startWordBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)613 static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
614 {
615     ASSERT(offset);
616     if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
617         needMoreContext = true;
618         return 0;
619     }
620     needMoreContext = false;
621     int start, end;
622     U16_BACK_1(characters, 0, offset);
623     findWordBoundary(characters, length, offset, &start, &end);
624     return start;
625 }
626 
startOfWord(const VisiblePosition & c,EWordSide side)627 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
628 {
629     // FIXME: This returns a null VP for c at the start of the document
630     // and side == LeftWordIfOnBoundary
631     VisiblePosition p = c;
632     if (side == RightWordIfOnBoundary) {
633         // at paragraph end, the startofWord is the current position
634         if (isEndOfParagraph(c))
635             return c;
636 
637         p = c.next();
638         if (p.isNull())
639             return c;
640     }
641     return previousBoundary(p, startWordBoundary);
642 }
643 
endWordBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)644 static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
645 {
646     ASSERT(offset <= length);
647     if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
648         needMoreContext = true;
649         return length;
650     }
651     needMoreContext = false;
652     return findWordEndBoundary(characters, length, offset);
653 }
654 
endOfWord(const VisiblePosition & c,EWordSide side)655 VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
656 {
657     VisiblePosition p = c;
658     if (side == LeftWordIfOnBoundary) {
659         if (isStartOfParagraph(c))
660             return c;
661 
662         p = c.previous();
663         if (p.isNull())
664             return c;
665     } else if (isEndOfParagraph(c))
666         return c;
667 
668     return nextBoundary(p, endWordBoundary);
669 }
670 
previousWordPositionBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)671 static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
672 {
673     if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
674         needMoreContext = true;
675         return 0;
676     }
677     needMoreContext = false;
678     return findNextWordFromIndex(characters, length, offset, false);
679 }
680 
previousWordPosition(const VisiblePosition & c)681 VisiblePosition previousWordPosition(const VisiblePosition &c)
682 {
683     VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
684     return c.honorEditingBoundaryAtOrBefore(prev);
685 }
686 
nextWordPositionBoundary(const UChar * characters,unsigned length,unsigned offset,BoundarySearchContextAvailability mayHaveMoreContext,bool & needMoreContext)687 static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
688 {
689     if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
690         needMoreContext = true;
691         return length;
692     }
693     needMoreContext = false;
694     return findNextWordFromIndex(characters, length, offset, true);
695 }
696 
nextWordPosition(const VisiblePosition & c)697 VisiblePosition nextWordPosition(const VisiblePosition &c)
698 {
699     VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
700     return c.honorEditingBoundaryAtOrAfter(next);
701 }
702 
703 // ---------
704 
705 enum LineEndpointComputationMode { UseLogicalOrdering, UseInlineBoxOrdering };
startPositionForLine(const VisiblePosition & c,LineEndpointComputationMode mode)706 static VisiblePosition startPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
707 {
708     if (c.isNull())
709         return VisiblePosition();
710 
711     RootInlineBox* rootBox = RenderedPosition(c).rootBox();
712     if (!rootBox) {
713         // There are VisiblePositions at offset 0 in blocks without
714         // RootInlineBoxes, like empty editable blocks and bordered blocks.
715         Position p = c.deepEquivalent();
716         if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
717             return c;
718 
719         return VisiblePosition();
720     }
721 
722     Node* startNode;
723     InlineBox* startBox;
724     if (mode == UseLogicalOrdering) {
725         startNode = rootBox->getLogicalStartBoxWithNode(startBox);
726         if (!startNode)
727             return VisiblePosition();
728     } else {
729         // Generated content (e.g. list markers and CSS :before and :after pseudoelements) have no corresponding DOM element,
730         // and so cannot be represented by a VisiblePosition. Use whatever follows instead.
731         startBox = rootBox->firstLeafChild();
732         while (true) {
733             if (!startBox)
734                 return VisiblePosition();
735 
736             startNode = startBox->renderer().nonPseudoNode();
737             if (startNode)
738                 break;
739 
740             startBox = startBox->nextLeafChild();
741         }
742     }
743 
744     return VisiblePosition(startNode->isTextNode() ? Position(toText(startNode), toInlineTextBox(startBox)->start()) : positionBeforeNode(startNode));
745 }
746 
startOfLine(const VisiblePosition & c,LineEndpointComputationMode mode)747 static VisiblePosition startOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
748 {
749     // TODO: this is the current behavior that might need to be fixed.
750     // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
751     VisiblePosition visPos = startPositionForLine(c, mode);
752 
753     if (mode == UseLogicalOrdering) {
754         if (ContainerNode* editableRoot = highestEditableRoot(c.deepEquivalent())) {
755             if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
756                 return VisiblePosition(firstPositionInNode(editableRoot));
757         }
758     }
759 
760     return c.honorEditingBoundaryAtOrBefore(visPos);
761 }
762 
763 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
startOfLine(const VisiblePosition & currentPosition)764 VisiblePosition startOfLine(const VisiblePosition& currentPosition)
765 {
766     return startOfLine(currentPosition, UseInlineBoxOrdering);
767 }
768 
logicalStartOfLine(const VisiblePosition & currentPosition)769 VisiblePosition logicalStartOfLine(const VisiblePosition& currentPosition)
770 {
771     return startOfLine(currentPosition, UseLogicalOrdering);
772 }
773 
endPositionForLine(const VisiblePosition & c,LineEndpointComputationMode mode)774 static VisiblePosition endPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
775 {
776     if (c.isNull())
777         return VisiblePosition();
778 
779     RootInlineBox* rootBox = RenderedPosition(c).rootBox();
780     if (!rootBox) {
781         // There are VisiblePositions at offset 0 in blocks without
782         // RootInlineBoxes, like empty editable blocks and bordered blocks.
783         Position p = c.deepEquivalent();
784         if (p.deprecatedNode()->renderer() && p.deprecatedNode()->renderer()->isRenderBlock() && !p.deprecatedEditingOffset())
785             return c;
786         return VisiblePosition();
787     }
788 
789     Node* endNode;
790     InlineBox* endBox;
791     if (mode == UseLogicalOrdering) {
792         endNode = rootBox->getLogicalEndBoxWithNode(endBox);
793         if (!endNode)
794             return VisiblePosition();
795     } else {
796         // Generated content (e.g. list markers and CSS :before and :after pseudoelements) have no corresponding DOM element,
797         // and so cannot be represented by a VisiblePosition. Use whatever precedes instead.
798         endBox = rootBox->lastLeafChild();
799         while (true) {
800             if (!endBox)
801                 return VisiblePosition();
802 
803             endNode = endBox->renderer().nonPseudoNode();
804             if (endNode)
805                 break;
806 
807             endBox = endBox->prevLeafChild();
808         }
809     }
810 
811     Position pos;
812     if (isHTMLBRElement(*endNode))
813         pos = positionBeforeNode(endNode);
814     else if (endBox->isInlineTextBox() && endNode->isTextNode()) {
815         InlineTextBox* endTextBox = toInlineTextBox(endBox);
816         int endOffset = endTextBox->start();
817         if (!endTextBox->isLineBreak())
818             endOffset += endTextBox->len();
819         pos = Position(toText(endNode), endOffset);
820     } else
821         pos = positionAfterNode(endNode);
822 
823     return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE);
824 }
825 
inSameLogicalLine(const VisiblePosition & a,const VisiblePosition & b)826 static bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b)
827 {
828     return a.isNotNull() && logicalStartOfLine(a) == logicalStartOfLine(b);
829 }
830 
endOfLine(const VisiblePosition & c,LineEndpointComputationMode mode)831 static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
832 {
833     // TODO: this is the current behavior that might need to be fixed.
834     // Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
835     VisiblePosition visPos = endPositionForLine(c, mode);
836 
837     if (mode == UseLogicalOrdering) {
838         // Make sure the end of line is at the same line as the given input position. For a wrapping line, the logical end
839         // position for the not-last-2-lines might incorrectly hand back the logical beginning of the next line.
840         // For example, <div contenteditable dir="rtl" style="line-break:before-white-space">abcdefg abcdefg abcdefg
841         // a abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg abcdefg </div>
842         // In this case, use the previous position of the computed logical end position.
843         if (!inSameLogicalLine(c, visPos))
844             visPos = visPos.previous();
845 
846         if (ContainerNode* editableRoot = highestEditableRoot(c.deepEquivalent())) {
847             if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
848                 return VisiblePosition(lastPositionInNode(editableRoot));
849         }
850 
851         return c.honorEditingBoundaryAtOrAfter(visPos);
852     }
853 
854     // Make sure the end of line is at the same line as the given input position. Else use the previous position to
855     // obtain end of line. This condition happens when the input position is before the space character at the end
856     // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position
857     // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style
858     // versus lines without that style, which would break before a space by default.
859     if (!inSameLine(c, visPos)) {
860         visPos = c.previous();
861         if (visPos.isNull())
862             return VisiblePosition();
863         visPos = endPositionForLine(visPos, UseInlineBoxOrdering);
864     }
865 
866     return c.honorEditingBoundaryAtOrAfter(visPos);
867 }
868 
869 // FIXME: Rename this function to reflect the fact it ignores bidi levels.
endOfLine(const VisiblePosition & currentPosition)870 VisiblePosition endOfLine(const VisiblePosition& currentPosition)
871 {
872     return endOfLine(currentPosition, UseInlineBoxOrdering);
873 }
874 
logicalEndOfLine(const VisiblePosition & currentPosition)875 VisiblePosition logicalEndOfLine(const VisiblePosition& currentPosition)
876 {
877     return endOfLine(currentPosition, UseLogicalOrdering);
878 }
879 
inSameLine(const VisiblePosition & a,const VisiblePosition & b)880 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
881 {
882     return a.isNotNull() && startOfLine(a) == startOfLine(b);
883 }
884 
isStartOfLine(const VisiblePosition & p)885 bool isStartOfLine(const VisiblePosition &p)
886 {
887     return p.isNotNull() && p == startOfLine(p);
888 }
889 
isEndOfLine(const VisiblePosition & p)890 bool isEndOfLine(const VisiblePosition &p)
891 {
892     return p.isNotNull() && p == endOfLine(p);
893 }
894 
isLogicalEndOfLine(const VisiblePosition & p)895 bool isLogicalEndOfLine(const VisiblePosition &p)
896 {
897     return p.isNotNull() && p == logicalEndOfLine(p);
898 }
899 
absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox * root,int lineDirectionPoint)900 static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint)
901 {
902     ASSERT(root);
903     RenderBlockFlow& containingBlock = root->block();
904     FloatPoint absoluteBlockPoint = containingBlock.localToAbsolute(FloatPoint());
905     if (containingBlock.hasOverflowClip())
906         absoluteBlockPoint -= containingBlock.scrolledContentOffset();
907 
908     if (root->block().isHorizontalWritingMode())
909         return IntPoint(lineDirectionPoint - absoluteBlockPoint.x(), root->blockDirectionPointInLine());
910 
911     return IntPoint(root->blockDirectionPointInLine(), lineDirectionPoint - absoluteBlockPoint.y());
912 }
913 
previousLinePosition(const VisiblePosition & visiblePosition,int lineDirectionPoint,EditableType editableType)914 VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
915 {
916     Position p = visiblePosition.deepEquivalent();
917     Node* node = p.deprecatedNode();
918 
919     if (!node)
920         return VisiblePosition();
921 
922     node->document().updateLayoutIgnorePendingStylesheets();
923 
924     RenderObject* renderer = node->renderer();
925     if (!renderer)
926         return VisiblePosition();
927 
928     RootInlineBox* root = 0;
929     InlineBox* box;
930     int ignoredCaretOffset;
931     visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
932     if (box) {
933         root = box->root().prevRootBox();
934         // We want to skip zero height boxes.
935         // This could happen in case it is a TrailingFloatsRootInlineBox.
936         if (!root || !root->logicalHeight() || !root->firstLeafChild())
937             root = 0;
938     }
939 
940     if (!root) {
941         Position position = previousRootInlineBoxCandidatePosition(node, visiblePosition, editableType);
942         if (position.isNotNull()) {
943             RenderedPosition renderedPosition((VisiblePosition(position)));
944             root = renderedPosition.rootBox();
945             if (!root)
946                 return VisiblePosition(position);
947         }
948     }
949 
950     if (root) {
951         // FIXME: Can be wrong for multi-column layout and with transforms.
952         IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
953         RenderObject& renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
954         Node* node = renderer.node();
955         if (node && editingIgnoresContent(node))
956             return VisiblePosition(positionInParentBeforeNode(*node));
957         return VisiblePosition(renderer.positionForPoint(pointInLine));
958     }
959 
960     // Could not find a previous line. This means we must already be on the first line.
961     // Move to the start of the content in this block, which effectively moves us
962     // to the start of the line we're on.
963     Element* rootElement = node->hasEditableStyle(editableType) ? node->rootEditableElement(editableType) : node->document().documentElement();
964     if (!rootElement)
965         return VisiblePosition();
966     return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM);
967 }
968 
nextLinePosition(const VisiblePosition & visiblePosition,int lineDirectionPoint,EditableType editableType)969 VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
970 {
971     Position p = visiblePosition.deepEquivalent();
972     Node* node = p.deprecatedNode();
973 
974     if (!node)
975         return VisiblePosition();
976 
977     node->document().updateLayoutIgnorePendingStylesheets();
978 
979     RenderObject* renderer = node->renderer();
980     if (!renderer)
981         return VisiblePosition();
982 
983     RootInlineBox* root = 0;
984     InlineBox* box;
985     int ignoredCaretOffset;
986     visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
987     if (box) {
988         root = box->root().nextRootBox();
989         // We want to skip zero height boxes.
990         // This could happen in case it is a TrailingFloatsRootInlineBox.
991         if (!root || !root->logicalHeight() || !root->firstLeafChild())
992             root = 0;
993     }
994 
995     if (!root) {
996         // FIXME: We need do the same in previousLinePosition.
997         Node* child = NodeTraversal::childAt(*node, p.deprecatedEditingOffset());
998         node = child ? child : &NodeTraversal::lastWithinOrSelf(*node);
999         Position position = nextRootInlineBoxCandidatePosition(node, visiblePosition, editableType);
1000         if (position.isNotNull()) {
1001             RenderedPosition renderedPosition((VisiblePosition(position)));
1002             root = renderedPosition.rootBox();
1003             if (!root)
1004                 return VisiblePosition(position);
1005         }
1006     }
1007 
1008     if (root) {
1009         // FIXME: Can be wrong for multi-column layout and with transforms.
1010         IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
1011         RenderObject& renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
1012         Node* node = renderer.node();
1013         if (node && editingIgnoresContent(node))
1014             return VisiblePosition(positionInParentBeforeNode(*node));
1015         return VisiblePosition(renderer.positionForPoint(pointInLine));
1016     }
1017 
1018     // Could not find a next line. This means we must already be on the last line.
1019     // Move to the end of the content in this block, which effectively moves us
1020     // to the end of the line we're on.
1021     Element* rootElement = node->hasEditableStyle(editableType) ? node->rootEditableElement(editableType) : node->document().documentElement();
1022     if (!rootElement)
1023         return VisiblePosition();
1024     return VisiblePosition(lastPositionInNode(rootElement), DOWNSTREAM);
1025 }
1026 
1027 // ---------
1028 
startSentenceBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1029 static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1030 {
1031     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1032     // FIXME: The following function can return -1; we don't handle that.
1033     return iterator->preceding(length);
1034 }
1035 
startOfSentence(const VisiblePosition & c)1036 VisiblePosition startOfSentence(const VisiblePosition &c)
1037 {
1038     return previousBoundary(c, startSentenceBoundary);
1039 }
1040 
endSentenceBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1041 static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1042 {
1043     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1044     return iterator->next();
1045 }
1046 
1047 // FIXME: This includes the space after the punctuation that marks the end of the sentence.
endOfSentence(const VisiblePosition & c)1048 VisiblePosition endOfSentence(const VisiblePosition &c)
1049 {
1050     return nextBoundary(c, endSentenceBoundary);
1051 }
1052 
previousSentencePositionBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1053 static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1054 {
1055     // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
1056     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1057     // FIXME: The following function can return -1; we don't handle that.
1058     return iterator->preceding(length);
1059 }
1060 
previousSentencePosition(const VisiblePosition & c)1061 VisiblePosition previousSentencePosition(const VisiblePosition &c)
1062 {
1063     VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
1064     return c.honorEditingBoundaryAtOrBefore(prev);
1065 }
1066 
nextSentencePositionBoundary(const UChar * characters,unsigned length,unsigned,BoundarySearchContextAvailability,bool &)1067 static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
1068 {
1069     // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
1070     // move to the equivlant position in the following sentence.
1071     TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
1072     return iterator->following(0);
1073 }
1074 
nextSentencePosition(const VisiblePosition & c)1075 VisiblePosition nextSentencePosition(const VisiblePosition &c)
1076 {
1077     VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
1078     return c.honorEditingBoundaryAtOrAfter(next);
1079 }
1080 
startOfParagraph(const VisiblePosition & c,EditingBoundaryCrossingRule boundaryCrossingRule)1081 VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
1082 {
1083     Position p = c.deepEquivalent();
1084     Node* startNode = p.deprecatedNode();
1085 
1086     if (!startNode)
1087         return VisiblePosition();
1088 
1089     if (isRenderedAsNonInlineTableImageOrHR(startNode))
1090         return VisiblePosition(positionBeforeNode(startNode));
1091 
1092     Element* startBlock = enclosingBlock(startNode);
1093 
1094     Node* node = startNode;
1095     ContainerNode* highestRoot = highestEditableRoot(p);
1096     int offset = p.deprecatedEditingOffset();
1097     Position::AnchorType type = p.anchorType();
1098 
1099     Node* n = startNode;
1100     bool startNodeIsEditable = startNode->hasEditableStyle();
1101     while (n) {
1102         if (boundaryCrossingRule == CannotCrossEditingBoundary && !Position::nodeIsUserSelectAll(n) && n->hasEditableStyle() != startNodeIsEditable)
1103             break;
1104         if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
1105             while (n && n->hasEditableStyle() != startNodeIsEditable)
1106                 n = NodeTraversal::previousPostOrder(*n, startBlock);
1107             if (!n || !n->isDescendantOf(highestRoot))
1108                 break;
1109         }
1110         RenderObject* r = n->renderer();
1111         if (!r) {
1112             n = NodeTraversal::previousPostOrder(*n, startBlock);
1113             continue;
1114         }
1115         RenderStyle* style = r->style();
1116         if (style->visibility() != VISIBLE) {
1117             n = NodeTraversal::previousPostOrder(*n, startBlock);
1118             continue;
1119         }
1120 
1121         if (r->isBR() || isBlock(n))
1122             break;
1123 
1124         if (r->isText() && toRenderText(r)->renderedTextLength()) {
1125             ASSERT_WITH_SECURITY_IMPLICATION(n->isTextNode());
1126             type = Position::PositionIsOffsetInAnchor;
1127             if (style->preserveNewline()) {
1128                 RenderText* text = toRenderText(r);
1129                 int i = text->textLength();
1130                 int o = offset;
1131                 if (n == startNode && o < i)
1132                     i = max(0, o);
1133                 while (--i >= 0) {
1134                     if ((*text)[i] == '\n')
1135                         return VisiblePosition(Position(toText(n), i + 1), DOWNSTREAM);
1136                 }
1137             }
1138             node = n;
1139             offset = 0;
1140             n = NodeTraversal::previousPostOrder(*n, startBlock);
1141         } else if (editingIgnoresContent(n) || isRenderedTableElement(n)) {
1142             node = n;
1143             type = Position::PositionIsBeforeAnchor;
1144             n = n->previousSibling() ? n->previousSibling() : NodeTraversal::previousPostOrder(*n, startBlock);
1145         } else {
1146             n = NodeTraversal::previousPostOrder(*n, startBlock);
1147         }
1148     }
1149 
1150     if (type == Position::PositionIsOffsetInAnchor) {
1151         ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
1152         return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
1153     }
1154 
1155     return VisiblePosition(Position(node, type), DOWNSTREAM);
1156 }
1157 
endOfParagraph(const VisiblePosition & c,EditingBoundaryCrossingRule boundaryCrossingRule)1158 VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossingRule boundaryCrossingRule)
1159 {
1160     if (c.isNull())
1161         return VisiblePosition();
1162 
1163     Position p = c.deepEquivalent();
1164     Node* startNode = p.deprecatedNode();
1165 
1166     if (isRenderedAsNonInlineTableImageOrHR(startNode))
1167         return VisiblePosition(positionAfterNode(startNode));
1168 
1169     Element* startBlock = enclosingBlock(startNode);
1170     Element* stayInsideBlock = startBlock;
1171 
1172     Node* node = startNode;
1173     ContainerNode* highestRoot = highestEditableRoot(p);
1174     int offset = p.deprecatedEditingOffset();
1175     Position::AnchorType type = p.anchorType();
1176 
1177     Node* n = startNode;
1178     bool startNodeIsEditable = startNode->hasEditableStyle();
1179     while (n) {
1180         if (boundaryCrossingRule == CannotCrossEditingBoundary && !Position::nodeIsUserSelectAll(n) && n->hasEditableStyle() != startNodeIsEditable)
1181             break;
1182         if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
1183             while (n && n->hasEditableStyle() != startNodeIsEditable)
1184                 n = NodeTraversal::next(*n, stayInsideBlock);
1185             if (!n || !n->isDescendantOf(highestRoot))
1186                 break;
1187         }
1188 
1189         RenderObject* r = n->renderer();
1190         if (!r) {
1191             n = NodeTraversal::next(*n, stayInsideBlock);
1192             continue;
1193         }
1194         RenderStyle* style = r->style();
1195         if (style->visibility() != VISIBLE) {
1196             n = NodeTraversal::next(*n, stayInsideBlock);
1197             continue;
1198         }
1199 
1200         if (r->isBR() || isBlock(n))
1201             break;
1202 
1203         // FIXME: We avoid returning a position where the renderer can't accept the caret.
1204         if (r->isText() && toRenderText(r)->renderedTextLength()) {
1205             ASSERT_WITH_SECURITY_IMPLICATION(n->isTextNode());
1206             int length = toRenderText(r)->textLength();
1207             type = Position::PositionIsOffsetInAnchor;
1208             if (style->preserveNewline()) {
1209                 RenderText* text = toRenderText(r);
1210                 int o = n == startNode ? offset : 0;
1211                 for (int i = o; i < length; ++i) {
1212                     if ((*text)[i] == '\n')
1213                         return VisiblePosition(Position(toText(n), i), DOWNSTREAM);
1214                 }
1215             }
1216             node = n;
1217             offset = r->caretMaxOffset();
1218             n = NodeTraversal::next(*n, stayInsideBlock);
1219         } else if (editingIgnoresContent(n) || isRenderedTableElement(n)) {
1220             node = n;
1221             type = Position::PositionIsAfterAnchor;
1222             n = NodeTraversal::nextSkippingChildren(*n, stayInsideBlock);
1223         } else {
1224             n = NodeTraversal::next(*n, stayInsideBlock);
1225         }
1226     }
1227 
1228     if (type == Position::PositionIsOffsetInAnchor)
1229         return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
1230 
1231     return VisiblePosition(Position(node, type), DOWNSTREAM);
1232 }
1233 
1234 // FIXME: isStartOfParagraph(startOfNextParagraph(pos)) is not always true
startOfNextParagraph(const VisiblePosition & visiblePosition)1235 VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
1236 {
1237     VisiblePosition paragraphEnd(endOfParagraph(visiblePosition, CanSkipOverEditingBoundary));
1238     VisiblePosition afterParagraphEnd(paragraphEnd.next(CannotCrossEditingBoundary));
1239     // The position after the last position in the last cell of a table
1240     // is not the start of the next paragraph.
1241     if (isFirstPositionAfterTable(afterParagraphEnd))
1242         return afterParagraphEnd.next(CannotCrossEditingBoundary);
1243     return afterParagraphEnd;
1244 }
1245 
inSameParagraph(const VisiblePosition & a,const VisiblePosition & b,EditingBoundaryCrossingRule boundaryCrossingRule)1246 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b, EditingBoundaryCrossingRule boundaryCrossingRule)
1247 {
1248     return a.isNotNull() && startOfParagraph(a, boundaryCrossingRule) == startOfParagraph(b, boundaryCrossingRule);
1249 }
1250 
isStartOfParagraph(const VisiblePosition & pos,EditingBoundaryCrossingRule boundaryCrossingRule)1251 bool isStartOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
1252 {
1253     return pos.isNotNull() && pos == startOfParagraph(pos, boundaryCrossingRule);
1254 }
1255 
isEndOfParagraph(const VisiblePosition & pos,EditingBoundaryCrossingRule boundaryCrossingRule)1256 bool isEndOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
1257 {
1258     return pos.isNotNull() && pos == endOfParagraph(pos, boundaryCrossingRule);
1259 }
1260 
previousParagraphPosition(const VisiblePosition & p,int x)1261 VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x)
1262 {
1263     VisiblePosition pos = p;
1264     do {
1265         VisiblePosition n = previousLinePosition(pos, x);
1266         if (n.isNull() || n == pos)
1267             break;
1268         pos = n;
1269     } while (inSameParagraph(p, pos));
1270     return pos;
1271 }
1272 
nextParagraphPosition(const VisiblePosition & p,int x)1273 VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x)
1274 {
1275     VisiblePosition pos = p;
1276     do {
1277         VisiblePosition n = nextLinePosition(pos, x);
1278         if (n.isNull() || n == pos)
1279             break;
1280         pos = n;
1281     } while (inSameParagraph(p, pos));
1282     return pos;
1283 }
1284 
1285 // ---------
1286 
startOfBlock(const VisiblePosition & visiblePosition,EditingBoundaryCrossingRule rule)1287 VisiblePosition startOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
1288 {
1289     Position position = visiblePosition.deepEquivalent();
1290     Element* startBlock = position.containerNode() ? enclosingBlock(position.containerNode(), rule) : 0;
1291     return startBlock ? VisiblePosition(firstPositionInNode(startBlock)) : VisiblePosition();
1292 }
1293 
endOfBlock(const VisiblePosition & visiblePosition,EditingBoundaryCrossingRule rule)1294 VisiblePosition endOfBlock(const VisiblePosition& visiblePosition, EditingBoundaryCrossingRule rule)
1295 {
1296     Position position = visiblePosition.deepEquivalent();
1297     Element* endBlock = position.containerNode() ? enclosingBlock(position.containerNode(), rule) : 0;
1298     return endBlock ? VisiblePosition(lastPositionInNode(endBlock)) : VisiblePosition();
1299 }
1300 
inSameBlock(const VisiblePosition & a,const VisiblePosition & b)1301 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
1302 {
1303     return !a.isNull() && enclosingBlock(a.deepEquivalent().containerNode()) == enclosingBlock(b.deepEquivalent().containerNode());
1304 }
1305 
isStartOfBlock(const VisiblePosition & pos)1306 bool isStartOfBlock(const VisiblePosition &pos)
1307 {
1308     return pos.isNotNull() && pos == startOfBlock(pos, CanCrossEditingBoundary);
1309 }
1310 
isEndOfBlock(const VisiblePosition & pos)1311 bool isEndOfBlock(const VisiblePosition &pos)
1312 {
1313     return pos.isNotNull() && pos == endOfBlock(pos, CanCrossEditingBoundary);
1314 }
1315 
1316 // ---------
1317 
startOfDocument(const Node * node)1318 VisiblePosition startOfDocument(const Node* node)
1319 {
1320     if (!node || !node->document().documentElement())
1321         return VisiblePosition();
1322 
1323     return VisiblePosition(firstPositionInNode(node->document().documentElement()), DOWNSTREAM);
1324 }
1325 
startOfDocument(const VisiblePosition & c)1326 VisiblePosition startOfDocument(const VisiblePosition &c)
1327 {
1328     return startOfDocument(c.deepEquivalent().deprecatedNode());
1329 }
1330 
endOfDocument(const Node * node)1331 VisiblePosition endOfDocument(const Node* node)
1332 {
1333     if (!node || !node->document().documentElement())
1334         return VisiblePosition();
1335 
1336     Element* doc = node->document().documentElement();
1337     return VisiblePosition(lastPositionInNode(doc), DOWNSTREAM);
1338 }
1339 
endOfDocument(const VisiblePosition & c)1340 VisiblePosition endOfDocument(const VisiblePosition &c)
1341 {
1342     return endOfDocument(c.deepEquivalent().deprecatedNode());
1343 }
1344 
isStartOfDocument(const VisiblePosition & p)1345 bool isStartOfDocument(const VisiblePosition &p)
1346 {
1347     return p.isNotNull() && p.previous(CanCrossEditingBoundary).isNull();
1348 }
1349 
isEndOfDocument(const VisiblePosition & p)1350 bool isEndOfDocument(const VisiblePosition &p)
1351 {
1352     return p.isNotNull() && p.next(CanCrossEditingBoundary).isNull();
1353 }
1354 
1355 // ---------
1356 
startOfEditableContent(const VisiblePosition & visiblePosition)1357 VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
1358 {
1359     ContainerNode* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1360     if (!highestRoot)
1361         return VisiblePosition();
1362 
1363     return VisiblePosition(firstPositionInNode(highestRoot));
1364 }
1365 
endOfEditableContent(const VisiblePosition & visiblePosition)1366 VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
1367 {
1368     ContainerNode* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
1369     if (!highestRoot)
1370         return VisiblePosition();
1371 
1372     return VisiblePosition(lastPositionInNode(highestRoot));
1373 }
1374 
isEndOfEditableOrNonEditableContent(const VisiblePosition & p)1375 bool isEndOfEditableOrNonEditableContent(const VisiblePosition &p)
1376 {
1377     return p.isNotNull() && p.next().isNull();
1378 }
1379 
leftBoundaryOfLine(const VisiblePosition & c,TextDirection direction)1380 VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1381 {
1382     return direction == LTR ? logicalStartOfLine(c) : logicalEndOfLine(c);
1383 }
1384 
rightBoundaryOfLine(const VisiblePosition & c,TextDirection direction)1385 VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
1386 {
1387     return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
1388 }
1389 
localCaretRectOfPosition(const PositionWithAffinity & position,RenderObject * & renderer)1390 LayoutRect localCaretRectOfPosition(const PositionWithAffinity& position, RenderObject*& renderer)
1391 {
1392     if (position.position().isNull()) {
1393         renderer = nullptr;
1394         return IntRect();
1395     }
1396     Node* node = position.position().anchorNode();
1397 
1398     renderer = node->renderer();
1399     if (!renderer)
1400         return LayoutRect();
1401 
1402     InlineBox* inlineBox;
1403     int caretOffset;
1404     position.position().getInlineBoxAndOffset(position.affinity(), inlineBox, caretOffset);
1405 
1406     if (inlineBox)
1407         renderer = &inlineBox->renderer();
1408 
1409     return renderer->localCaretRect(inlineBox, caretOffset);
1410 }
1411 
1412 }
1413