| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| 11 * documentation and/or other materials provided with the distribution. | 11 * documentation and/or other materials provided with the distribution. |
| 12 * | 12 * |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |
| 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR |
| 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 */ | 24 */ |
| 25 | 25 |
| 26 #include "core/editing/Position.h" | 26 #include "core/editing/Position.h" |
| 27 | 27 |
| 28 #include "core/dom/Text.h" |
| 28 #include "core/dom/shadow/ElementShadow.h" | 29 #include "core/dom/shadow/ElementShadow.h" |
| 29 #include "core/editing/EditingUtilities.h" | 30 #include "core/editing/EditingUtilities.h" |
| 30 #include "core/editing/TextAffinity.h" | 31 #include "core/editing/TextAffinity.h" |
| 31 #include "wtf/text/CString.h" | 32 #include "wtf/text/CString.h" |
| 32 #include <stdio.h> | |
| 33 #include <ostream> // NOLINT | 33 #include <ostream> // NOLINT |
| 34 | 34 |
| 35 namespace blink { | 35 namespace blink { |
| 36 | 36 |
| 37 #if DCHECK_IS_ON() | 37 #if DCHECK_IS_ON() |
| 38 static bool canBeAnchorNode(Node* node) | 38 static bool canBeAnchorNode(Node* node) |
| 39 { | 39 { |
| 40 return !node || !node->isPseudoElement(); | 40 return !node || !node->isPseudoElement(); |
| 41 } | 41 } |
| 42 #endif | 42 #endif |
| (...skipping 24 matching lines...) Expand all Loading... |
| 67 DCHECK_GE(offset, 1); | 67 DCHECK_GE(offset, 1); |
| 68 return PositionTemplate<Strategy>(anchorNode, PositionAnchorType::AfterAncho
r); | 68 return PositionTemplate<Strategy>(anchorNode, PositionAnchorType::AfterAncho
r); |
| 69 } | 69 } |
| 70 | 70 |
| 71 template <typename Strategy> | 71 template <typename Strategy> |
| 72 PositionTemplate<Strategy>::PositionTemplate(Node* anchorNode, PositionAnchorTyp
e anchorType) | 72 PositionTemplate<Strategy>::PositionTemplate(Node* anchorNode, PositionAnchorTyp
e anchorType) |
| 73 : m_anchorNode(anchorNode) | 73 : m_anchorNode(anchorNode) |
| 74 , m_offset(0) | 74 , m_offset(0) |
| 75 , m_anchorType(anchorType) | 75 , m_anchorType(anchorType) |
| 76 { | 76 { |
| 77 if (!m_anchorNode) { | 77 DCHECK(m_anchorNode); |
| 78 m_anchorType = PositionAnchorType::OffsetInAnchor; | 78 DCHECK_NE(m_anchorType, PositionAnchorType::OffsetInAnchor); |
| 79 return; | |
| 80 } | |
| 81 if (m_anchorNode->isTextNode()) { | 79 if (m_anchorNode->isTextNode()) { |
| 82 DCHECK(m_anchorType == PositionAnchorType::BeforeAnchor || m_anchorType
== PositionAnchorType::AfterAnchor); | 80 DCHECK(isBeforeAnchor() || isAfterAnchor()) << m_anchorType; |
| 83 return; | 81 return; |
| 84 } | 82 } |
| 85 if (m_anchorNode->isDocumentNode()) { | 83 if (m_anchorNode->isDocumentNode()) { |
| 86 // Since |RangeBoundaryPoint| can't represent before/after Document, we | 84 // Since |RangeBoundaryPoint| can't represent before/after Document, we |
| 87 // should not use them. | 85 // should not use them. |
| 88 DCHECK(isBeforeChildren() || isAfterChildren()) << m_anchorType; | 86 DCHECK(isBeforeChildren() || isAfterChildren()) << m_anchorType; |
| 89 return; | 87 return; |
| 90 } | 88 } |
| 89 |
| 91 #if DCHECK_IS_ON() | 90 #if DCHECK_IS_ON() |
| 92 DCHECK(canBeAnchorNode(m_anchorNode.get())); | 91 DCHECK(canBeAnchorNode(m_anchorNode.get())); |
| 93 #endif | 92 #endif |
| 94 DCHECK_NE(m_anchorType, PositionAnchorType::OffsetInAnchor); | 93 DCHECK_NE(m_anchorType, PositionAnchorType::OffsetInAnchor); |
| 95 } | 94 } |
| 96 | 95 |
| 97 template <typename Strategy> | 96 template <typename Strategy> |
| 98 PositionTemplate<Strategy>::PositionTemplate(Node* anchorNode, int offset) | 97 PositionTemplate<Strategy>::PositionTemplate(Node* anchorNode, int offset) |
| 99 : m_anchorNode(anchorNode) | 98 : m_anchorNode(anchorNode) |
| 100 , m_offset(offset) | 99 , m_offset(offset) |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 if (isNull()) | 385 if (isNull()) |
| 387 return true; | 386 return true; |
| 388 // TODO(yosin) We should use |Strategy::lastOffsetForEditing()| instead of | 387 // TODO(yosin) We should use |Strategy::lastOffsetForEditing()| instead of |
| 389 // DOM tree version. | 388 // DOM tree version. |
| 390 return !Strategy::parent(*anchorNode()) && m_offset >= EditingStrategy::last
OffsetForEditing(anchorNode()); | 389 return !Strategy::parent(*anchorNode()) && m_offset >= EditingStrategy::last
OffsetForEditing(anchorNode()); |
| 391 } | 390 } |
| 392 | 391 |
| 393 template <typename Strategy> | 392 template <typename Strategy> |
| 394 void PositionTemplate<Strategy>::debugPosition(const char* msg) const | 393 void PositionTemplate<Strategy>::debugPosition(const char* msg) const |
| 395 { | 394 { |
| 396 static const char* const anchorTypes[] = { | 395 VLOG(0) << msg << ' ' << *this; |
| 397 "OffsetInAnchor", | |
| 398 "BeforeAnchor", | |
| 399 "AfterAnchor", | |
| 400 "BeforeChildren", | |
| 401 "AfterChildren", | |
| 402 "Invalid", | |
| 403 }; | |
| 404 | |
| 405 if (isNull()) { | |
| 406 fprintf(stderr, "Position [%s]: null\n", msg); | |
| 407 return; | |
| 408 } | |
| 409 | |
| 410 const char* anchorType = anchorTypes[std::min(static_cast<size_t>(m_anchorTy
pe), WTF_ARRAY_LENGTH(anchorTypes) - 1)]; | |
| 411 if (m_anchorNode->isTextNode()) { | |
| 412 fprintf(stderr, "Position [%s]: %s [%p] %s, (%s) at %d\n", msg, anchorNo
de()->nodeName().utf8().data(), anchorNode(), anchorType, m_anchorNode->nodeValu
e().utf8().data(), m_offset); | |
| 413 return; | |
| 414 } | |
| 415 | |
| 416 fprintf(stderr, "Position [%s]: %s [%p] %s at %d\n", msg, anchorNode()->node
Name().utf8().data(), anchorNode(), anchorType, m_offset); | |
| 417 } | 396 } |
| 418 | 397 |
| 419 PositionInFlatTree toPositionInFlatTree(const Position& pos) | 398 PositionInFlatTree toPositionInFlatTree(const Position& pos) |
| 420 { | 399 { |
| 421 if (pos.isNull()) | 400 if (pos.isNull()) |
| 422 return PositionInFlatTree(); | 401 return PositionInFlatTree(); |
| 423 | 402 |
| 424 if (pos.isOffsetInAnchor()) { | 403 if (pos.isOffsetInAnchor()) { |
| 425 Node* anchor = pos.anchorNode(); | 404 Node* anchor = pos.anchorNode(); |
| 426 if (anchor->offsetInCharacters()) | 405 if (anchor->offsetInCharacters()) |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 487 // |child| is null when the position is at the end of the children. | 466 // |child| is null when the position is at the end of the children. |
| 488 // <div>foo|</div> | 467 // <div>foo|</div> |
| 489 return Position(anchorNode, PositionAnchorType::AfterChildren); | 468 return Position(anchorNode, PositionAnchorType::AfterChildren); |
| 490 } | 469 } |
| 491 default: | 470 default: |
| 492 ASSERT_NOT_REACHED(); | 471 ASSERT_NOT_REACHED(); |
| 493 return Position(); | 472 return Position(); |
| 494 } | 473 } |
| 495 } | 474 } |
| 496 | 475 |
| 476 // |normalize()| returns an equivalent position with |BeforeNode| position, or |
| 477 // |OffsetInAnchor| for |Text| node if possible, otherwise |normalize()| returns |
| 478 // - |AfterAnchor| if anchor node has no next siblings. |
| 479 // - |BeforeChildren| if anchor node has no children. |
| 480 // |normalized()| never return |AfterChildren| position. |
| 481 template <typename Strategy> |
| 482 PositionTemplate<Strategy> PositionTemplate<Strategy>::normalize(const PositionT
emplate<Strategy>& position) |
| 483 { |
| 484 if (position.isNull()) |
| 485 return PositionTemplate<Strategy>(); |
| 486 Node* const anchorNode = position.anchorNode(); |
| 487 switch (position.anchorType()) { |
| 488 case PositionAnchorType::AfterAnchor: |
| 489 if (Node* nextSibling = Strategy::nextSibling(*anchorNode)) |
| 490 return normalize(beforeNode(nextSibling)); |
| 491 if (anchorNode->isTextNode()) |
| 492 return PositionTemplate<Strategy>(anchorNode, toText(anchorNode)->le
ngth()); |
| 493 return position; |
| 494 case PositionAnchorType::AfterChildren: |
| 495 ASSERT(!anchorNode->isTextNode()); |
| 496 if (Node* lastChild = Strategy::lastChild(*anchorNode)) |
| 497 return normalize(afterNode(lastChild)); |
| 498 return firstPositionInNode(anchorNode); |
| 499 case PositionAnchorType::BeforeAnchor: |
| 500 if (anchorNode->isTextNode()) |
| 501 return PositionTemplate<Strategy>(anchorNode, 0); |
| 502 return position; |
| 503 case PositionAnchorType::BeforeChildren: |
| 504 ASSERT(!anchorNode->isTextNode()); |
| 505 if (Node* firstChild = Strategy::firstChild(*anchorNode)) |
| 506 return normalize(beforeNode(firstChild)); |
| 507 return position; |
| 508 case PositionAnchorType::OffsetInAnchor: { |
| 509 const int offsetInContainer = position.offsetInContainerNode(); |
| 510 if (anchorNode->isTextNode()) { |
| 511 const Text& textNode = *toText(anchorNode); |
| 512 if (offsetInContainer < static_cast<int>(textNode.length())) |
| 513 return position; |
| 514 return normalize(afterNode(anchorNode)); |
| 515 } |
| 516 if (Node* child = Strategy::childAt(*anchorNode, offsetInContainer)) |
| 517 return normalize(beforeNode(child)); |
| 518 if (Node* lastChild = Strategy::lastChild(*anchorNode)) |
| 519 return normalize(afterNode(lastChild)); |
| 520 return firstPositionInNode(anchorNode); |
| 521 } |
| 522 } |
| 523 ASSERT_NOT_REACHED(); |
| 524 return PositionTemplate<Strategy>(); |
| 525 } |
| 526 |
| 497 #ifndef NDEBUG | 527 #ifndef NDEBUG |
| 498 | 528 |
| 499 template <typename Strategy> | 529 template <typename Strategy> |
| 500 void PositionTemplate<Strategy>::formatForDebugger(char* buffer, unsigned length
) const | 530 void PositionTemplate<Strategy>::formatForDebugger(char* buffer, unsigned length
) const |
| 501 { | 531 { |
| 502 StringBuilder result; | 532 StringBuilder result; |
| 503 | 533 |
| 504 if (isNull()) { | 534 if (isNull()) { |
| 505 result.appendLiteral("<null>"); | 535 result.appendLiteral("<null>"); |
| 506 } else { | 536 } else { |
| 507 char s[1024]; | 537 char s[1024]; |
| 508 result.appendLiteral("offset "); | 538 result.appendLiteral("offset "); |
| 509 result.appendNumber(m_offset); | 539 result.appendNumber(m_offset); |
| 510 result.appendLiteral(" of "); | 540 result.appendLiteral(" of "); |
| 511 anchorNode()->formatForDebugger(s, sizeof(s)); | 541 anchorNode()->formatForDebugger(s, sizeof(s)); |
| 512 result.append(s); | 542 result.append(s); |
| 513 } | 543 } |
| 514 | 544 |
| 515 strncpy(buffer, result.toString().utf8().data(), length - 1); | 545 strncpy(buffer, result.toString().utf8().data(), length - 1); |
| 516 } | 546 } |
| 517 | 547 |
| 518 template <typename Strategy> | 548 template <typename Strategy> |
| 519 void PositionTemplate<Strategy>::showAnchorTypeAndOffset() const | |
| 520 { | |
| 521 switch (anchorType()) { | |
| 522 case PositionAnchorType::OffsetInAnchor: | |
| 523 fputs("offset", stderr); | |
| 524 break; | |
| 525 case PositionAnchorType::BeforeChildren: | |
| 526 fputs("beforeChildren", stderr); | |
| 527 break; | |
| 528 case PositionAnchorType::AfterChildren: | |
| 529 fputs("afterChildren", stderr); | |
| 530 break; | |
| 531 case PositionAnchorType::BeforeAnchor: | |
| 532 fputs("before", stderr); | |
| 533 break; | |
| 534 case PositionAnchorType::AfterAnchor: | |
| 535 fputs("after", stderr); | |
| 536 break; | |
| 537 } | |
| 538 fprintf(stderr, ", offset:%d\n", m_offset); | |
| 539 } | |
| 540 | |
| 541 template <typename Strategy> | |
| 542 void PositionTemplate<Strategy>::showTreeForThis() const | 549 void PositionTemplate<Strategy>::showTreeForThis() const |
| 543 { | 550 { |
| 544 if (!anchorNode()) | 551 if (!anchorNode()) |
| 545 return; | 552 return; |
| 546 anchorNode()->showTreeForThis(); | 553 anchorNode()->showTreeForThis(); |
| 547 showAnchorTypeAndOffset(); | 554 VLOG(0) << *this; |
| 548 } | 555 } |
| 549 | 556 |
| 550 template <typename Strategy> | 557 template <typename Strategy> |
| 551 void PositionTemplate<Strategy>::showTreeForThisInFlatTree() const | 558 void PositionTemplate<Strategy>::showTreeForThisInFlatTree() const |
| 552 { | 559 { |
| 553 if (!anchorNode()) | 560 if (!anchorNode()) |
| 554 return; | 561 return; |
| 555 anchorNode()->showTreeForThisInFlatTree(); | 562 anchorNode()->showTreeForThisInFlatTree(); |
| 556 showAnchorTypeAndOffset(); | 563 VLOG(0) << *this; |
| 557 } | 564 } |
| 558 | 565 |
| 559 #endif | 566 #endif |
| 560 | 567 |
| 561 template <typename PositionType> | 568 template <typename PositionType> |
| 562 static std::ostream& printPosition(std::ostream& ostream, const PositionType& po
sition) | 569 static std::ostream& printPosition(std::ostream& ostream, const PositionType& po
sition) |
| 563 { | 570 { |
| 564 if (position.isNull()) | 571 if (position.isNull()) |
| 565 return ostream << "null"; | 572 return ostream << "null"; |
| 566 ostream << position.anchorNode() << "@"; | 573 ostream << position.anchorNode() << "@"; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 | 618 |
| 612 void showTree(const blink::Position* pos) | 619 void showTree(const blink::Position* pos) |
| 613 { | 620 { |
| 614 if (pos) | 621 if (pos) |
| 615 pos->showTreeForThis(); | 622 pos->showTreeForThis(); |
| 616 else | 623 else |
| 617 fprintf(stderr, "Cannot showTree for (nil)\n"); | 624 fprintf(stderr, "Cannot showTree for (nil)\n"); |
| 618 } | 625 } |
| 619 | 626 |
| 620 #endif | 627 #endif |
| OLD | NEW |