| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 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/VisibleSelection.h" | 26 #include "core/editing/VisibleSelection.h" |
| 27 | 27 |
| 28 #include "bindings/core/v8/ExceptionState.h" | 28 #include "bindings/core/v8/ExceptionState.h" |
| 29 #include "core/dom/Document.h" | 29 #include "core/dom/Document.h" |
| 30 #include "core/dom/Element.h" | 30 #include "core/dom/Element.h" |
| 31 #include "core/dom/Range.h" | 31 #include "core/dom/Range.h" |
| 32 #include "core/editing/EditingUtilities.h" | 32 #include "core/editing/EditingUtilities.h" |
| 33 #include "core/editing/SelectionAdjuster.h" |
| 33 #include "core/editing/iterators/CharacterIterator.h" | 34 #include "core/editing/iterators/CharacterIterator.h" |
| 34 #include "core/layout/LayoutObject.h" | 35 #include "core/layout/LayoutObject.h" |
| 35 #include "platform/geometry/LayoutPoint.h" | 36 #include "platform/geometry/LayoutPoint.h" |
| 36 #include "wtf/Assertions.h" | 37 #include "wtf/Assertions.h" |
| 37 #include "wtf/text/CString.h" | 38 #include "wtf/text/CString.h" |
| 38 #include "wtf/text/CharacterNames.h" | 39 #include "wtf/text/CharacterNames.h" |
| 39 #include "wtf/text/StringBuilder.h" | 40 #include "wtf/text/StringBuilder.h" |
| 40 | 41 |
| 41 namespace blink { | 42 namespace blink { |
| 42 | 43 |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 template <typename Strategy> | 507 template <typename Strategy> |
| 507 void VisibleSelectionTemplate<Strategy>::updateSelectionType() | 508 void VisibleSelectionTemplate<Strategy>::updateSelectionType() |
| 508 { | 509 { |
| 509 m_selectionType = computeSelectionType(m_start, m_end); | 510 m_selectionType = computeSelectionType(m_start, m_end); |
| 510 | 511 |
| 511 // Affinity only makes sense for a caret | 512 // Affinity only makes sense for a caret |
| 512 if (m_selectionType != CaretSelection) | 513 if (m_selectionType != CaretSelection) |
| 513 m_affinity = TextAffinity::Downstream; | 514 m_affinity = TextAffinity::Downstream; |
| 514 } | 515 } |
| 515 | 516 |
| 516 static Node* enclosingShadowHost(Node* node) | |
| 517 { | |
| 518 for (Node* runner = node; runner; runner = ComposedTreeTraversal::parent(*ru
nner)) { | |
| 519 if (isShadowHost(runner)) | |
| 520 return runner; | |
| 521 } | |
| 522 return nullptr; | |
| 523 } | |
| 524 | |
| 525 static bool isEnclosedBy(const PositionInComposedTree& position, const Node& nod
e) | |
| 526 { | |
| 527 ASSERT(position.isNotNull()); | |
| 528 Node* anchorNode = position.anchorNode(); | |
| 529 if (anchorNode == node) | |
| 530 return !position.isAfterAnchor() && !position.isBeforeAnchor(); | |
| 531 | |
| 532 return ComposedTreeTraversal::isDescendantOf(*anchorNode, node); | |
| 533 } | |
| 534 | |
| 535 static bool isSelectionBoundary(const Node& node) | |
| 536 { | |
| 537 return isHTMLTextAreaElement(node) || isHTMLInputElement(node) || isHTMLSele
ctElement(node); | |
| 538 } | |
| 539 | |
| 540 static Node* enclosingShadowHostForStart(const PositionInComposedTree& position) | |
| 541 { | |
| 542 Node* node = position.nodeAsRangeFirstNode(); | |
| 543 if (!node) | |
| 544 return nullptr; | |
| 545 Node* shadowHost = enclosingShadowHost(node); | |
| 546 if (!shadowHost) | |
| 547 return nullptr; | |
| 548 if (!isEnclosedBy(position, *shadowHost)) | |
| 549 return nullptr; | |
| 550 return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; | |
| 551 } | |
| 552 | |
| 553 static Node* enclosingShadowHostForEnd(const PositionInComposedTree& position) | |
| 554 { | |
| 555 Node* node = position.nodeAsRangeLastNode(); | |
| 556 if (!node) | |
| 557 return nullptr; | |
| 558 Node* shadowHost = enclosingShadowHost(node); | |
| 559 if (!shadowHost) | |
| 560 return nullptr; | |
| 561 if (!isEnclosedBy(position, *shadowHost)) | |
| 562 return nullptr; | |
| 563 return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; | |
| 564 } | |
| 565 | |
| 566 template <typename Strategy> | 517 template <typename Strategy> |
| 567 void VisibleSelectionTemplate<Strategy>::validate(TextGranularity granularity) | 518 void VisibleSelectionTemplate<Strategy>::validate(TextGranularity granularity) |
| 568 { | 519 { |
| 569 setBaseAndExtentToDeepEquivalents(); | 520 setBaseAndExtentToDeepEquivalents(); |
| 570 if (m_base.isNull() || m_extent.isNull()) { | 521 if (m_base.isNull() || m_extent.isNull()) { |
| 571 m_base = m_extent = m_start = m_end = PositionTemplate<Strategy>(); | 522 m_base = m_extent = m_start = m_end = PositionTemplate<Strategy>(); |
| 572 updateSelectionType(); | 523 updateSelectionType(); |
| 573 return; | 524 return; |
| 574 } | 525 } |
| 575 | 526 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 638 m_selectionType = base == extent ? CaretSelection : RangeSelection; | 589 m_selectionType = base == extent ? CaretSelection : RangeSelection; |
| 639 if (m_selectionType != CaretSelection) { | 590 if (m_selectionType != CaretSelection) { |
| 640 // Since |m_affinity| for non-|CaretSelection| is always |Downstream|, | 591 // Since |m_affinity| for non-|CaretSelection| is always |Downstream|, |
| 641 // we should keep this invariant. Note: This function can be called with | 592 // we should keep this invariant. Note: This function can be called with |
| 642 // |m_affinity| is |TextAffinity::Upstream|. | 593 // |m_affinity| is |TextAffinity::Upstream|. |
| 643 m_affinity = TextAffinity::Downstream; | 594 m_affinity = TextAffinity::Downstream; |
| 644 } | 595 } |
| 645 didChange(); | 596 didChange(); |
| 646 } | 597 } |
| 647 | 598 |
| 648 static PositionInComposedTree adjustPositionInComposedTreeForStart(const Positio
nInComposedTree& position, Node* shadowHost) | |
| 649 { | |
| 650 if (isEnclosedBy(position, *shadowHost)) { | |
| 651 if (position.isBeforeChildren()) | |
| 652 return PositionInComposedTree::beforeNode(shadowHost); | |
| 653 return PositionInComposedTree::afterNode(shadowHost); | |
| 654 } | |
| 655 | |
| 656 // We use |firstChild|'s after instead of beforeAllChildren for backward | |
| 657 // compatibility. The positions are same but the anchors would be different, | |
| 658 // and selection painting uses anchor nodes. | |
| 659 if (Node* firstChild = ComposedTreeTraversal::firstChild(*shadowHost)) | |
| 660 return PositionInComposedTree::beforeNode(firstChild); | |
| 661 return PositionInComposedTree(); | |
| 662 } | |
| 663 | |
| 664 static Position adjustPositionForEnd(const Position& currentPosition, Node* star
tContainerNode) | |
| 665 { | |
| 666 TreeScope& treeScope = startContainerNode->treeScope(); | |
| 667 | |
| 668 ASSERT(currentPosition.computeContainerNode()->treeScope() != treeScope); | |
| 669 | |
| 670 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.computeCo
ntainerNode())) { | |
| 671 if (ancestor->contains(startContainerNode)) | |
| 672 return positionAfterNode(ancestor); | |
| 673 return positionBeforeNode(ancestor); | |
| 674 } | |
| 675 | |
| 676 if (Node* lastChild = treeScope.rootNode().lastChild()) | |
| 677 return positionAfterNode(lastChild); | |
| 678 | |
| 679 return Position(); | |
| 680 } | |
| 681 | |
| 682 static PositionInComposedTree adjustPositionInComposedTreeForEnd(const PositionI
nComposedTree& position, Node* shadowHost) | |
| 683 { | |
| 684 if (isEnclosedBy(position, *shadowHost)) { | |
| 685 if (position.isAfterChildren()) | |
| 686 return PositionInComposedTree::afterNode(shadowHost); | |
| 687 return PositionInComposedTree::beforeNode(shadowHost); | |
| 688 } | |
| 689 | |
| 690 // We use |lastChild|'s after instead of afterAllChildren for backward | |
| 691 // compatibility. The positions are same but the anchors would be different, | |
| 692 // and selection painting uses anchor nodes. | |
| 693 if (Node* lastChild = ComposedTreeTraversal::lastChild(*shadowHost)) | |
| 694 return PositionInComposedTree::afterNode(lastChild); | |
| 695 return PositionInComposedTree(); | |
| 696 } | |
| 697 | |
| 698 static Position adjustPositionForStart(const Position& currentPosition, Node* en
dContainerNode) | |
| 699 { | |
| 700 TreeScope& treeScope = endContainerNode->treeScope(); | |
| 701 | |
| 702 ASSERT(currentPosition.computeContainerNode()->treeScope() != treeScope); | |
| 703 | |
| 704 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.computeCo
ntainerNode())) { | |
| 705 if (ancestor->contains(endContainerNode)) | |
| 706 return positionBeforeNode(ancestor); | |
| 707 return positionAfterNode(ancestor); | |
| 708 } | |
| 709 | |
| 710 if (Node* firstChild = treeScope.rootNode().firstChild()) | |
| 711 return positionBeforeNode(firstChild); | |
| 712 | |
| 713 return Position(); | |
| 714 } | |
| 715 | |
| 716 // TODO(yosin): We should move | |
| 717 // |SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries()| to | |
| 718 // "SelectionAdjuster.cpp" | |
| 719 void SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(VisibleSe
lection* selection) | |
| 720 { | |
| 721 // Note: |m_selectionType| isn't computed yet. | |
| 722 ASSERT(selection->base().isNotNull()); | |
| 723 ASSERT(selection->extent().isNotNull()); | |
| 724 ASSERT(selection->start().isNotNull()); | |
| 725 ASSERT(selection->end().isNotNull()); | |
| 726 | |
| 727 // TODO(hajimehoshi): Checking treeScope is wrong when a node is | |
| 728 // distributed, but we leave it as it is for backward compatibility. | |
| 729 if (selection->start().anchorNode()->treeScope() == selection->end().anchorN
ode()->treeScope()) | |
| 730 return; | |
| 731 | |
| 732 if (selection->isBaseFirst()) { | |
| 733 const Position& newEnd = adjustPositionForEnd(selection->end(), selectio
n->start().computeContainerNode()); | |
| 734 selection->m_extent = newEnd; | |
| 735 selection->m_end = newEnd; | |
| 736 return; | |
| 737 } | |
| 738 | |
| 739 const Position& newStart = adjustPositionForStart(selection->start(), select
ion->end().computeContainerNode()); | |
| 740 selection->m_extent = newStart; | |
| 741 selection->m_start = newStart; | |
| 742 } | |
| 743 | |
| 744 // TODO(yosin): We should move | |
| 745 // |SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries()| to | |
| 746 // "SelectionAdjuster.cpp" | |
| 747 // This function is called twice. The first is called when |m_start| and |m_end| | |
| 748 // or |m_extent| are same, and the second when |m_start| and |m_end| are changed | |
| 749 // after downstream/upstream. | |
| 750 void SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(VisibleSe
lectionInComposedTree* selection) | |
| 751 { | |
| 752 Node* const shadowHostStart = enclosingShadowHostForStart(selection->start()
); | |
| 753 Node* const shadowHostEnd = enclosingShadowHostForEnd(selection->end()); | |
| 754 if (shadowHostStart == shadowHostEnd) | |
| 755 return; | |
| 756 | |
| 757 if (selection->isBaseFirst()) { | |
| 758 Node* const shadowHost = shadowHostStart ? shadowHostStart : shadowHostE
nd; | |
| 759 const PositionInComposedTree& newEnd = adjustPositionInComposedTreeForEn
d(selection->end(), shadowHost); | |
| 760 selection->m_extent = newEnd; | |
| 761 selection->m_end = newEnd; | |
| 762 return; | |
| 763 } | |
| 764 Node* const shadowHost = shadowHostEnd ? shadowHostEnd : shadowHostStart; | |
| 765 const PositionInComposedTree& newStart = adjustPositionInComposedTreeForStar
t(selection->start(), shadowHost); | |
| 766 selection->m_extent = newStart; | |
| 767 selection->m_start = newStart; | |
| 768 } | |
| 769 | |
| 770 template <typename Strategy> | 599 template <typename Strategy> |
| 771 void VisibleSelectionTemplate<Strategy>::adjustSelectionToAvoidCrossingShadowBou
ndaries() | 600 void VisibleSelectionTemplate<Strategy>::adjustSelectionToAvoidCrossingShadowBou
ndaries() |
| 772 { | 601 { |
| 773 if (m_base.isNull() || m_start.isNull() || m_base.isNull()) | 602 if (m_base.isNull() || m_start.isNull() || m_base.isNull()) |
| 774 return; | 603 return; |
| 775 SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(this); | 604 SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(this); |
| 776 } | 605 } |
| 777 | 606 |
| 778 static Element* lowestEditableAncestor(Node* node) | 607 static Element* lowestEditableAncestor(Node* node) |
| 779 { | 608 { |
| (...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1129 { | 958 { |
| 1130 sel.showTreeForThis(); | 959 sel.showTreeForThis(); |
| 1131 } | 960 } |
| 1132 | 961 |
| 1133 void showTree(const blink::VisibleSelectionInComposedTree* sel) | 962 void showTree(const blink::VisibleSelectionInComposedTree* sel) |
| 1134 { | 963 { |
| 1135 if (sel) | 964 if (sel) |
| 1136 sel->showTreeForThis(); | 965 sel->showTreeForThis(); |
| 1137 } | 966 } |
| 1138 #endif | 967 #endif |
| OLD | NEW |