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 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 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. 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 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 134 size_t size() const { return m_leafBoxes.size(); } 135 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 145 CachedLogicallyOrderedLeafBoxes::CachedLogicallyOrderedLeafBoxes() : m_rootInlineBox(0) { }; 146 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 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 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 198 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 687 VisiblePosition previousWordPosition(const VisiblePosition &c) 688 { 689 VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary); 690 return c.honorEditingBoundaryAtOrBefore(prev); 691 } 692 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 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 }; 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 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. 770 VisiblePosition startOfLine(const VisiblePosition& currentPosition) 771 { 772 return startOfLine(currentPosition, UseInlineBoxOrdering); 773 } 774 775 VisiblePosition logicalStartOfLine(const VisiblePosition& currentPosition) 776 { 777 return startOfLine(currentPosition, UseLogicalOrdering); 778 } 779 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 832 static bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b) 833 { 834 return a.isNotNull() && logicalStartOfLine(a) == logicalStartOfLine(b); 835 } 836 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. 876 VisiblePosition endOfLine(const VisiblePosition& currentPosition) 877 { 878 return endOfLine(currentPosition, UseInlineBoxOrdering); 879 } 880 881 VisiblePosition logicalEndOfLine(const VisiblePosition& currentPosition) 882 { 883 return endOfLine(currentPosition, UseLogicalOrdering); 884 } 885 886 bool inSameLine(const VisiblePosition &a, const VisiblePosition &b) 887 { 888 return a.isNotNull() && startOfLine(a) == startOfLine(b); 889 } 890 891 bool isStartOfLine(const VisiblePosition &p) 892 { 893 return p.isNotNull() && p == startOfLine(p); 894 } 895 896 bool isEndOfLine(const VisiblePosition &p) 897 { 898 return p.isNotNull() && p == endOfLine(p); 899 } 900 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 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 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 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 1037 VisiblePosition startOfSentence(const VisiblePosition &c) 1038 { 1039 return previousBoundary(c, startSentenceBoundary); 1040 } 1041 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. 1049 VisiblePosition endOfSentence(const VisiblePosition &c) 1050 { 1051 return nextBoundary(c, endSentenceBoundary); 1052 } 1053 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 1062 VisiblePosition previousSentencePosition(const VisiblePosition &c) 1063 { 1064 VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary); 1065 return c.honorEditingBoundaryAtOrBefore(prev); 1066 } 1067 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 1076 VisiblePosition nextSentencePosition(const VisiblePosition &c) 1077 { 1078 VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary); 1079 return c.honorEditingBoundaryAtOrAfter(next); 1080 } 1081 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 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 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 1247 bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b, EditingBoundaryCrossingRule boundaryCrossingRule) 1248 { 1249 return a.isNotNull() && startOfParagraph(a, boundaryCrossingRule) == startOfParagraph(b, boundaryCrossingRule); 1250 } 1251 1252 bool isStartOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule) 1253 { 1254 return pos.isNotNull() && pos == startOfParagraph(pos, boundaryCrossingRule); 1255 } 1256 1257 bool isEndOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule) 1258 { 1259 return pos.isNotNull() && pos == endOfParagraph(pos, boundaryCrossingRule); 1260 } 1261 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 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 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 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 1306 bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b) 1307 { 1308 return !a.isNull() && enclosingBlock(a.deepEquivalent().containerNode()) == enclosingBlock(b.deepEquivalent().containerNode()); 1309 } 1310 1311 bool isStartOfBlock(const VisiblePosition &pos) 1312 { 1313 return pos.isNotNull() && pos == startOfBlock(pos, CanCrossEditingBoundary); 1314 } 1315 1316 bool isEndOfBlock(const VisiblePosition &pos) 1317 { 1318 return pos.isNotNull() && pos == endOfBlock(pos, CanCrossEditingBoundary); 1319 } 1320 1321 // --------- 1322 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 1331 VisiblePosition startOfDocument(const VisiblePosition &c) 1332 { 1333 return startOfDocument(c.deepEquivalent().deprecatedNode()); 1334 } 1335 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 1345 VisiblePosition endOfDocument(const VisiblePosition &c) 1346 { 1347 return endOfDocument(c.deepEquivalent().deprecatedNode()); 1348 } 1349 1350 bool isStartOfDocument(const VisiblePosition &p) 1351 { 1352 return p.isNotNull() && p.previous(CanCrossEditingBoundary).isNull(); 1353 } 1354 1355 bool isEndOfDocument(const VisiblePosition &p) 1356 { 1357 return p.isNotNull() && p.next(CanCrossEditingBoundary).isNull(); 1358 } 1359 1360 // --------- 1361 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 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 1380 bool isEndOfEditableOrNonEditableContent(const VisiblePosition &p) 1381 { 1382 return p.isNotNull() && p.next().isNull(); 1383 } 1384 1385 VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction) 1386 { 1387 return direction == LTR ? logicalStartOfLine(c) : logicalEndOfLine(c); 1388 } 1389 1390 VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction) 1391 { 1392 return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c); 1393 } 1394 1395 } 1396