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