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