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" | |
34 #include "core/editing/iterators/CharacterIterator.h" | 33 #include "core/editing/iterators/CharacterIterator.h" |
35 #include "core/layout/LayoutObject.h" | 34 #include "core/layout/LayoutObject.h" |
36 #include "platform/geometry/LayoutPoint.h" | 35 #include "platform/geometry/LayoutPoint.h" |
37 #include "wtf/Assertions.h" | 36 #include "wtf/Assertions.h" |
38 #include "wtf/text/CString.h" | 37 #include "wtf/text/CString.h" |
39 #include "wtf/text/CharacterNames.h" | 38 #include "wtf/text/CharacterNames.h" |
40 #include "wtf/text/StringBuilder.h" | 39 #include "wtf/text/StringBuilder.h" |
41 | 40 |
42 namespace blink { | 41 namespace blink { |
43 | 42 |
(...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 template <typename Strategy> | 506 template <typename Strategy> |
508 void VisibleSelectionTemplate<Strategy>::updateSelectionType() | 507 void VisibleSelectionTemplate<Strategy>::updateSelectionType() |
509 { | 508 { |
510 m_selectionType = computeSelectionType(m_start, m_end); | 509 m_selectionType = computeSelectionType(m_start, m_end); |
511 | 510 |
512 // Affinity only makes sense for a caret | 511 // Affinity only makes sense for a caret |
513 if (m_selectionType != CaretSelection) | 512 if (m_selectionType != CaretSelection) |
514 m_affinity = TextAffinity::Downstream; | 513 m_affinity = TextAffinity::Downstream; |
515 } | 514 } |
516 | 515 |
| 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 |
517 template <typename Strategy> | 566 template <typename Strategy> |
518 void VisibleSelectionTemplate<Strategy>::validate(TextGranularity granularity) | 567 void VisibleSelectionTemplate<Strategy>::validate(TextGranularity granularity) |
519 { | 568 { |
520 setBaseAndExtentToDeepEquivalents(); | 569 setBaseAndExtentToDeepEquivalents(); |
521 if (m_base.isNull() || m_extent.isNull()) { | 570 if (m_base.isNull() || m_extent.isNull()) { |
522 m_base = m_extent = m_start = m_end = PositionTemplate<Strategy>(); | 571 m_base = m_extent = m_start = m_end = PositionTemplate<Strategy>(); |
523 updateSelectionType(); | 572 updateSelectionType(); |
524 return; | 573 return; |
525 } | 574 } |
526 | 575 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 m_selectionType = base == extent ? CaretSelection : RangeSelection; | 638 m_selectionType = base == extent ? CaretSelection : RangeSelection; |
590 if (m_selectionType != CaretSelection) { | 639 if (m_selectionType != CaretSelection) { |
591 // Since |m_affinity| for non-|CaretSelection| is always |Downstream|, | 640 // Since |m_affinity| for non-|CaretSelection| is always |Downstream|, |
592 // we should keep this invariant. Note: This function can be called with | 641 // we should keep this invariant. Note: This function can be called with |
593 // |m_affinity| is |TextAffinity::Upstream|. | 642 // |m_affinity| is |TextAffinity::Upstream|. |
594 m_affinity = TextAffinity::Downstream; | 643 m_affinity = TextAffinity::Downstream; |
595 } | 644 } |
596 didChange(); | 645 didChange(); |
597 } | 646 } |
598 | 647 |
| 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 |
599 template <typename Strategy> | 770 template <typename Strategy> |
600 void VisibleSelectionTemplate<Strategy>::adjustSelectionToAvoidCrossingShadowBou
ndaries() | 771 void VisibleSelectionTemplate<Strategy>::adjustSelectionToAvoidCrossingShadowBou
ndaries() |
601 { | 772 { |
602 if (m_base.isNull() || m_start.isNull() || m_base.isNull()) | 773 if (m_base.isNull() || m_start.isNull() || m_base.isNull()) |
603 return; | 774 return; |
604 SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(this); | 775 SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(this); |
605 } | 776 } |
606 | 777 |
607 static Element* lowestEditableAncestor(Node* node) | 778 static Element* lowestEditableAncestor(Node* node) |
608 { | 779 { |
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
958 { | 1129 { |
959 sel.showTreeForThis(); | 1130 sel.showTreeForThis(); |
960 } | 1131 } |
961 | 1132 |
962 void showTree(const blink::VisibleSelectionInComposedTree* sel) | 1133 void showTree(const blink::VisibleSelectionInComposedTree* sel) |
963 { | 1134 { |
964 if (sel) | 1135 if (sel) |
965 sel->showTreeForThis(); | 1136 sel->showTreeForThis(); |
966 } | 1137 } |
967 #endif | 1138 #endif |
OLD | NEW |