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 "VisiblePosition.h" 28 29 #include "CString.h" 30 #include "Document.h" 31 #include "FloatQuad.h" 32 #include "HTMLElement.h" 33 #include "HTMLNames.h" 34 #include "InlineTextBox.h" 35 #include "Logging.h" 36 #include "Range.h" 37 #include "Text.h" 38 #include "htmlediting.h" 39 #include "visible_units.h" 40 #include <stdio.h> 41 42 namespace WebCore { 43 44 using namespace HTMLNames; 45 46 VisiblePosition::VisiblePosition(const Position &pos, EAffinity affinity) 47 { 48 init(pos, affinity); 49 } 50 51 VisiblePosition::VisiblePosition(Node *node, int offset, EAffinity affinity) 52 { 53 ASSERT(offset >= 0); 54 init(Position(node, offset), affinity); 55 } 56 57 void VisiblePosition::init(const Position& position, EAffinity affinity) 58 { 59 m_affinity = affinity; 60 61 m_deepPosition = canonicalPosition(position); 62 63 // When not at a line wrap, make sure to end up with DOWNSTREAM affinity. 64 if (m_affinity == UPSTREAM && (isNull() || inSameLine(VisiblePosition(position, DOWNSTREAM), *this))) 65 m_affinity = DOWNSTREAM; 66 } 67 68 VisiblePosition VisiblePosition::next(bool stayInEditableContent) const 69 { 70 VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity); 71 72 if (!stayInEditableContent) 73 return next; 74 75 return honorEditableBoundaryAtOrAfter(next); 76 } 77 78 VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const 79 { 80 // find first previous DOM position that is visible 81 Position pos = previousVisuallyDistinctCandidate(m_deepPosition); 82 83 // return null visible position if there is no previous visible position 84 if (pos.atStartOfTree()) 85 return VisiblePosition(); 86 87 VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM); 88 ASSERT(prev != *this); 89 90 #ifndef NDEBUG 91 // we should always be able to make the affinity DOWNSTREAM, because going previous from an 92 // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!). 93 if (prev.isNotNull() && m_affinity == UPSTREAM) { 94 VisiblePosition temp = prev; 95 temp.setAffinity(UPSTREAM); 96 ASSERT(inSameLine(temp, prev)); 97 } 98 #endif 99 100 if (!stayInEditableContent) 101 return prev; 102 103 return honorEditableBoundaryAtOrBefore(prev); 104 } 105 106 Position VisiblePosition::leftVisuallyDistinctCandidate() const 107 { 108 Position p = m_deepPosition; 109 if (!p.node()) 110 return Position(); 111 112 Position downstreamStart = p.downstream(); 113 TextDirection primaryDirection = LTR; 114 for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) { 115 if (r->isBlockFlow()) { 116 primaryDirection = r->style()->direction(); 117 break; 118 } 119 } 120 121 while (true) { 122 InlineBox* box; 123 int offset; 124 p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); 125 if (!box) 126 return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); 127 128 RenderObject* renderer = box->renderer(); 129 130 while (true) { 131 if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretRightmostOffset()) 132 return box->direction() == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); 133 134 offset = box->direction() == LTR ? renderer->previousOffset(offset) : renderer->nextOffset(offset); 135 136 int caretMinOffset = box->caretMinOffset(); 137 int caretMaxOffset = box->caretMaxOffset(); 138 139 if (offset > caretMinOffset && offset < caretMaxOffset) 140 break; 141 142 if (box->direction() == LTR ? offset < caretMinOffset : offset > caretMaxOffset) { 143 // Overshot to the left. 144 InlineBox* prevBox = box->prevLeafChild(); 145 if (!prevBox) 146 return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); 147 148 // Reposition at the other logical position corresponding to our edge's visual position and go for another round. 149 box = prevBox; 150 renderer = box->renderer(); 151 offset = prevBox->caretRightmostOffset(); 152 continue; 153 } 154 155 ASSERT(offset == box->caretLeftmostOffset()); 156 157 unsigned char level = box->bidiLevel(); 158 InlineBox* prevBox = box->prevLeafChild(); 159 160 if (box->direction() == primaryDirection) { 161 if (!prevBox || prevBox->bidiLevel() >= level) 162 break; 163 164 level = prevBox->bidiLevel(); 165 166 InlineBox* nextBox = box; 167 do { 168 nextBox = nextBox->nextLeafChild(); 169 } while (nextBox && nextBox->bidiLevel() > level); 170 171 if (nextBox && nextBox->bidiLevel() == level) 172 break; 173 174 while (InlineBox* prevBox = box->prevLeafChild()) { 175 if (prevBox->bidiLevel() < level) 176 break; 177 box = prevBox; 178 } 179 renderer = box->renderer(); 180 offset = box->caretRightmostOffset(); 181 if (box->direction() == primaryDirection) 182 break; 183 continue; 184 } 185 186 if (prevBox) { 187 box = prevBox; 188 renderer = box->renderer(); 189 offset = box->caretRightmostOffset(); 190 if (box->bidiLevel() > level) { 191 do { 192 prevBox = box->prevLeafChild(); 193 } while (prevBox && prevBox->bidiLevel() > level); 194 195 if (!prevBox || prevBox->bidiLevel() < level) 196 continue; 197 } 198 } else { 199 // Trailing edge of a secondary run. Set to the leading edge of the entire run. 200 while (true) { 201 while (InlineBox* nextBox = box->nextLeafChild()) { 202 if (nextBox->bidiLevel() < level) 203 break; 204 box = nextBox; 205 } 206 if (box->bidiLevel() == level) 207 break; 208 level = box->bidiLevel(); 209 while (InlineBox* prevBox = box->prevLeafChild()) { 210 if (prevBox->bidiLevel() < level) 211 break; 212 box = prevBox; 213 } 214 if (box->bidiLevel() == level) 215 break; 216 level = box->bidiLevel(); 217 } 218 renderer = box->renderer(); 219 offset = primaryDirection == LTR ? box->caretMinOffset() : box->caretMaxOffset(); 220 } 221 break; 222 } 223 224 p = Position(renderer->node(), offset); 225 226 if ((p.isCandidate() && p.downstream() != downstreamStart) || p.atStartOfTree() || p.atEndOfTree()) 227 return p; 228 } 229 } 230 231 VisiblePosition VisiblePosition::left(bool stayInEditableContent) const 232 { 233 Position pos = leftVisuallyDistinctCandidate(); 234 // FIXME: Why can't we move left from the last position in a tree? 235 if (pos.atStartOfTree() || pos.atEndOfTree()) 236 return VisiblePosition(); 237 238 VisiblePosition left = VisiblePosition(pos, DOWNSTREAM); 239 ASSERT(left != *this); 240 241 if (!stayInEditableContent) 242 return left; 243 244 // FIXME: This may need to do something different from "before". 245 return honorEditableBoundaryAtOrBefore(left); 246 } 247 248 Position VisiblePosition::rightVisuallyDistinctCandidate() const 249 { 250 Position p = m_deepPosition; 251 if (!p.node()) 252 return Position(); 253 254 Position downstreamStart = p.downstream(); 255 TextDirection primaryDirection = LTR; 256 for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) { 257 if (r->isBlockFlow()) { 258 primaryDirection = r->style()->direction(); 259 break; 260 } 261 } 262 263 while (true) { 264 InlineBox* box; 265 int offset; 266 p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); 267 if (!box) 268 return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); 269 270 RenderObject* renderer = box->renderer(); 271 272 while (true) { 273 if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretLeftmostOffset()) 274 return box->direction() == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); 275 276 offset = box->direction() == LTR ? renderer->nextOffset(offset) : renderer->previousOffset(offset); 277 278 int caretMinOffset = box->caretMinOffset(); 279 int caretMaxOffset = box->caretMaxOffset(); 280 281 if (offset > caretMinOffset && offset < caretMaxOffset) 282 break; 283 284 if (box->direction() == LTR ? offset > caretMaxOffset : offset < caretMinOffset) { 285 // Overshot to the right. 286 InlineBox* nextBox = box->nextLeafChild(); 287 if (!nextBox) 288 return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); 289 290 // Reposition at the other logical position corresponding to our edge's visual position and go for another round. 291 box = nextBox; 292 renderer = box->renderer(); 293 offset = nextBox->caretLeftmostOffset(); 294 continue; 295 } 296 297 ASSERT(offset == box->caretRightmostOffset()); 298 299 unsigned char level = box->bidiLevel(); 300 InlineBox* nextBox = box->nextLeafChild(); 301 302 if (box->direction() == primaryDirection) { 303 if (!nextBox || nextBox->bidiLevel() >= level) 304 break; 305 306 level = nextBox->bidiLevel(); 307 308 InlineBox* prevBox = box; 309 do { 310 prevBox = prevBox->prevLeafChild(); 311 } while (prevBox && prevBox->bidiLevel() > level); 312 313 if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA 314 break; 315 316 // For example, abc 123 ^ CBA 317 while (InlineBox* nextBox = box->nextLeafChild()) { 318 if (nextBox->bidiLevel() < level) 319 break; 320 box = nextBox; 321 } 322 renderer = box->renderer(); 323 offset = box->caretLeftmostOffset(); 324 if (box->direction() == primaryDirection) 325 break; 326 continue; 327 } 328 329 if (nextBox) { 330 box = nextBox; 331 renderer = box->renderer(); 332 offset = box->caretLeftmostOffset(); 333 if (box->bidiLevel() > level) { 334 do { 335 nextBox = box->nextLeafChild(); 336 } while (nextBox && nextBox->bidiLevel() > level); 337 338 if (!nextBox || nextBox->bidiLevel() < level) 339 continue; 340 } 341 } else { 342 // Trailing edge of a secondary run. Set to the leading edge of the entire run. 343 while (true) { 344 while (InlineBox* prevBox = box->prevLeafChild()) { 345 if (prevBox->bidiLevel() < level) 346 break; 347 box = prevBox; 348 } 349 if (box->bidiLevel() == level) 350 break; 351 level = box->bidiLevel(); 352 while (InlineBox* nextBox = box->nextLeafChild()) { 353 if (nextBox->bidiLevel() < level) 354 break; 355 box = nextBox; 356 } 357 if (box->bidiLevel() == level) 358 break; 359 level = box->bidiLevel(); 360 } 361 renderer = box->renderer(); 362 offset = primaryDirection == LTR ? box->caretMaxOffset() : box->caretMinOffset(); 363 } 364 break; 365 } 366 367 p = Position(renderer->node(), offset); 368 369 if ((p.isCandidate() && p.downstream() != downstreamStart) || p.atStartOfTree() || p.atEndOfTree()) 370 return p; 371 } 372 } 373 374 VisiblePosition VisiblePosition::right(bool stayInEditableContent) const 375 { 376 Position pos = rightVisuallyDistinctCandidate(); 377 // FIXME: Why can't we move left from the last position in a tree? 378 if (pos.atStartOfTree() || pos.atEndOfTree()) 379 return VisiblePosition(); 380 381 VisiblePosition right = VisiblePosition(pos, DOWNSTREAM); 382 ASSERT(right != *this); 383 384 if (!stayInEditableContent) 385 return right; 386 387 // FIXME: This may need to do something different from "after". 388 return honorEditableBoundaryAtOrAfter(right); 389 } 390 391 VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePosition &pos) const 392 { 393 if (pos.isNull()) 394 return pos; 395 396 Node* highestRoot = highestEditableRoot(deepEquivalent()); 397 398 // Return empty position if pos is not somewhere inside the editable region containing this position 399 if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot)) 400 return VisiblePosition(); 401 402 // Return pos itself if the two are from the very same editable region, or both are non-editable 403 // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement 404 // to it is allowed. VisibleSelection::adjustForEditableContent has this problem too. 405 if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) 406 return pos; 407 408 // Return empty position if this position is non-editable, but pos is editable 409 // FIXME: Move to the previous non-editable region. 410 if (!highestRoot) 411 return VisiblePosition(); 412 413 // Return the last position before pos that is in the same editable region as this position 414 return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot); 415 } 416 417 VisiblePosition VisiblePosition::honorEditableBoundaryAtOrAfter(const VisiblePosition &pos) const 418 { 419 if (pos.isNull()) 420 return pos; 421 422 Node* highestRoot = highestEditableRoot(deepEquivalent()); 423 424 // Return empty position if pos is not somewhere inside the editable region containing this position 425 if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot)) 426 return VisiblePosition(); 427 428 // Return pos itself if the two are from the very same editable region, or both are non-editable 429 // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement 430 // to it is allowed. VisibleSelection::adjustForEditableContent has this problem too. 431 if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) 432 return pos; 433 434 // Return empty position if this position is non-editable, but pos is editable 435 // FIXME: Move to the next non-editable region. 436 if (!highestRoot) 437 return VisiblePosition(); 438 439 // Return the next position after pos that is in the same editable region as this position 440 return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot); 441 } 442 443 static Position canonicalizeCandidate(const Position& candidate) 444 { 445 if (candidate.isNull()) 446 return Position(); 447 ASSERT(candidate.isCandidate()); 448 Position upstream = candidate.upstream(); 449 if (upstream.isCandidate()) 450 return upstream; 451 return candidate; 452 } 453 454 Position VisiblePosition::canonicalPosition(const Position& position) 455 { 456 // FIXME (9535): Canonicalizing to the leftmost candidate means that if we're at a line wrap, we will 457 // ask renderers to paint downstream carets for other renderers. 458 // To fix this, we need to either a) add code to all paintCarets to pass the responsibility off to 459 // the appropriate renderer for VisiblePosition's like these, or b) canonicalize to the rightmost candidate 460 // unless the affinity is upstream. 461 Node* node = position.node(); 462 if (!node) 463 return Position(); 464 465 node->document()->updateLayoutIgnorePendingStylesheets(); 466 467 Position candidate = position.upstream(); 468 if (candidate.isCandidate()) 469 return candidate; 470 candidate = position.downstream(); 471 if (candidate.isCandidate()) 472 return candidate; 473 474 // When neither upstream or downstream gets us to a candidate (upstream/downstream won't leave 475 // blocks or enter new ones), we search forward and backward until we find one. 476 Position next = canonicalizeCandidate(nextCandidate(position)); 477 Position prev = canonicalizeCandidate(previousCandidate(position)); 478 Node* nextNode = next.node(); 479 Node* prevNode = prev.node(); 480 481 // The new position must be in the same editable element. Enforce that first. 482 // Unless the descent is from a non-editable html element to an editable body. 483 if (node->hasTagName(htmlTag) && !node->isContentEditable() && node->document()->body() && node->document()->body()->isContentEditable()) 484 return next.isNotNull() ? next : prev; 485 486 Node* editingRoot = editableRootForPosition(position); 487 488 // If the html element is editable, descending into its body will look like a descent 489 // from non-editable to editable content since rootEditableElement() always stops at the body. 490 if ((editingRoot && editingRoot->hasTagName(htmlTag)) || position.node()->isDocumentNode()) 491 return next.isNotNull() ? next : prev; 492 493 bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot; 494 bool nextIsInSameEditableElement = nextNode && editableRootForPosition(next) == editingRoot; 495 if (prevIsInSameEditableElement && !nextIsInSameEditableElement) 496 return prev; 497 498 if (nextIsInSameEditableElement && !prevIsInSameEditableElement) 499 return next; 500 501 if (!nextIsInSameEditableElement && !prevIsInSameEditableElement) 502 return Position(); 503 504 // The new position should be in the same block flow element. Favor that. 505 Node *originalBlock = node->enclosingBlockFlowElement(); 506 bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock; 507 bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock; 508 if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock) 509 return prev; 510 511 return next; 512 } 513 514 UChar32 VisiblePosition::characterAfter() const 515 { 516 // We canonicalize to the first of two equivalent candidates, but the second of the two candidates 517 // is the one that will be inside the text node containing the character after this visible position. 518 Position pos = m_deepPosition.downstream(); 519 Node* node = pos.node(); 520 if (!node || !node->isTextNode()) 521 return 0; 522 Text* textNode = static_cast<Text*>(pos.node()); 523 unsigned offset = pos.deprecatedEditingOffset(); 524 unsigned length = textNode->length(); 525 if (offset >= length) 526 return 0; 527 528 UChar32 ch; 529 const UChar* characters = textNode->data().characters(); 530 U16_NEXT(characters, offset, length, ch); 531 return ch; 532 } 533 534 IntRect VisiblePosition::localCaretRect(RenderObject*& renderer) const 535 { 536 Node* node = m_deepPosition.node(); 537 if (!node) { 538 renderer = 0; 539 return IntRect(); 540 } 541 542 renderer = node->renderer(); 543 if (!renderer) 544 return IntRect(); 545 546 InlineBox* inlineBox; 547 int caretOffset; 548 getInlineBoxAndOffset(inlineBox, caretOffset); 549 550 if (inlineBox) 551 renderer = inlineBox->renderer(); 552 553 return renderer->localCaretRect(inlineBox, caretOffset); 554 } 555 556 IntRect VisiblePosition::absoluteCaretBounds() const 557 { 558 RenderObject* renderer; 559 IntRect localRect = localCaretRect(renderer); 560 if (localRect.isEmpty() || !renderer) 561 return IntRect(); 562 563 return renderer->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox(); 564 } 565 566 int VisiblePosition::xOffsetForVerticalNavigation() const 567 { 568 RenderObject* renderer; 569 IntRect localRect = localCaretRect(renderer); 570 if (localRect.isEmpty() || !renderer) 571 return 0; 572 573 // This ignores transforms on purpose, for now. Vertical navigation is done 574 // without consulting transforms, so that 'up' in transformed text is 'up' 575 // relative to the text, not absolute 'up'. 576 return renderer->localToAbsolute(localRect.location()).x(); 577 } 578 579 void VisiblePosition::debugPosition(const char* msg) const 580 { 581 if (isNull()) 582 fprintf(stderr, "Position [%s]: null\n", msg); 583 else 584 fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, m_deepPosition.node()->nodeName().utf8().data(), m_deepPosition.node(), m_deepPosition.deprecatedEditingOffset()); 585 } 586 587 #ifndef NDEBUG 588 589 void VisiblePosition::formatForDebugger(char* buffer, unsigned length) const 590 { 591 m_deepPosition.formatForDebugger(buffer, length); 592 } 593 594 void VisiblePosition::showTreeForThis() const 595 { 596 m_deepPosition.showTreeForThis(); 597 } 598 599 #endif 600 601 PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition &end) 602 { 603 if (start.isNull() || end.isNull()) 604 return 0; 605 606 Position s = rangeCompliantEquivalent(start); 607 Position e = rangeCompliantEquivalent(end); 608 return Range::create(s.node()->document(), s.node(), s.deprecatedEditingOffset(), e.node(), e.deprecatedEditingOffset()); 609 } 610 611 VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity) 612 { 613 int exception = 0; 614 return VisiblePosition(r->startContainer(exception), r->startOffset(exception), affinity); 615 } 616 617 VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity) 618 { 619 int exception = 0; 620 return VisiblePosition(r->endContainer(exception), r->endOffset(exception), affinity); 621 } 622 623 bool setStart(Range *r, const VisiblePosition &visiblePosition) 624 { 625 if (!r) 626 return false; 627 Position p = rangeCompliantEquivalent(visiblePosition); 628 int code = 0; 629 r->setStart(p.node(), p.deprecatedEditingOffset(), code); 630 return code == 0; 631 } 632 633 bool setEnd(Range *r, const VisiblePosition &visiblePosition) 634 { 635 if (!r) 636 return false; 637 Position p = rangeCompliantEquivalent(visiblePosition); 638 int code = 0; 639 r->setEnd(p.node(), p.deprecatedEditingOffset(), code); 640 return code == 0; 641 } 642 643 Node *enclosingBlockFlowElement(const VisiblePosition &visiblePosition) 644 { 645 if (visiblePosition.isNull()) 646 return NULL; 647 648 return visiblePosition.deepEquivalent().node()->enclosingBlockFlowElement(); 649 } 650 651 bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) 652 { 653 if (visiblePosition.isNull()) 654 return false; 655 656 if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node)) 657 return false; 658 659 VisiblePosition previous = visiblePosition.previous(); 660 return previous.isNull() || !previous.deepEquivalent().node()->isDescendantOf(node); 661 } 662 663 bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) 664 { 665 if (visiblePosition.isNull()) 666 return false; 667 668 if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node)) 669 return false; 670 671 VisiblePosition next = visiblePosition.next(); 672 return next.isNull() || !next.deepEquivalent().node()->isDescendantOf(node); 673 } 674 675 } // namespace WebCore 676 677 #ifndef NDEBUG 678 679 void showTree(const WebCore::VisiblePosition* vpos) 680 { 681 if (vpos) 682 vpos->showTreeForThis(); 683 } 684 685 void showTree(const WebCore::VisiblePosition& vpos) 686 { 687 vpos.showTreeForThis(); 688 } 689 690 #endif 691