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