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 |