| 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 530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 541 m_baseIsFirst = true; | 541 m_baseIsFirst = true; |
| 542 } else if (m_extent.isNull()) { | 542 } else if (m_extent.isNull()) { |
| 543 m_extent = m_base; | 543 m_extent = m_base; |
| 544 m_baseIsFirst = true; | 544 m_baseIsFirst = true; |
| 545 } else { | 545 } else { |
| 546 m_baseIsFirst = m_base.compareTo(m_extent) <= 0; | 546 m_baseIsFirst = m_base.compareTo(m_extent) <= 0; |
| 547 } | 547 } |
| 548 } | 548 } |
| 549 | 549 |
| 550 template <typename Strategy> | 550 template <typename Strategy> |
| 551 void VisibleSelectionTemplate<Strategy>::setStartRespectingGranularity( | 551 static PositionTemplate<Strategy> computeStartRespectingGranularity( |
| 552 const PositionWithAffinityTemplate<Strategy>& passedStart, |
| 552 TextGranularity granularity) { | 553 TextGranularity granularity) { |
| 553 DCHECK(m_base.isNotNull()); | 554 DCHECK(passedStart.isNotNull()); |
| 554 DCHECK(m_extent.isNotNull()); | |
| 555 | |
| 556 m_start = m_baseIsFirst ? m_base : m_extent; | |
| 557 | 555 |
| 558 switch (granularity) { | 556 switch (granularity) { |
| 559 case CharacterGranularity: | 557 case CharacterGranularity: |
| 560 // Don't do any expansion. | 558 // Don't do any expansion. |
| 561 break; | 559 return passedStart.position(); |
| 562 case WordGranularity: { | 560 case WordGranularity: { |
| 563 // General case: Select the word the caret is positioned inside of. | 561 // General case: Select the word the caret is positioned inside of. |
| 564 // If the caret is on the word boundary, select the word according to | 562 // If the caret is on the word boundary, select the word according to |
| 565 // |wordSide|. | 563 // |wordSide|. |
| 566 // Edge case: If the caret is after the last word in a soft-wrapped line | 564 // Edge case: If the caret is after the last word in a soft-wrapped line |
| 567 // or the last word in the document, select that last word | 565 // or the last word in the document, select that last word |
| 568 // (LeftWordIfOnBoundary). | 566 // (LeftWordIfOnBoundary). |
| 569 // Edge case: If the caret is after the last word in a paragraph, select | 567 // Edge case: If the caret is after the last word in a paragraph, select |
| 570 // from the the end of the last word to the line break (also | 568 // from the the end of the last word to the line break (also |
| 571 // RightWordIfOnBoundary); | 569 // RightWordIfOnBoundary); |
| 572 const VisiblePositionTemplate<Strategy> visibleStart = | 570 const VisiblePositionTemplate<Strategy> visibleStart = |
| 573 createVisiblePosition(m_start, m_affinity); | 571 createVisiblePosition(passedStart); |
| 574 EWordSide side = RightWordIfOnBoundary; | |
| 575 if (isEndOfEditableOrNonEditableContent(visibleStart) || | 572 if (isEndOfEditableOrNonEditableContent(visibleStart) || |
| 576 (isEndOfLine(visibleStart) && !isStartOfLine(visibleStart) && | 573 (isEndOfLine(visibleStart) && !isStartOfLine(visibleStart) && |
| 577 !isEndOfParagraph(visibleStart))) | 574 !isEndOfParagraph(visibleStart))) { |
| 578 side = LeftWordIfOnBoundary; | 575 return startOfWord(visibleStart, LeftWordIfOnBoundary).deepEquivalent(); |
| 579 m_start = startOfWord(visibleStart, side).deepEquivalent(); | 576 } |
| 580 break; | 577 return startOfWord(visibleStart, RightWordIfOnBoundary).deepEquivalent(); |
| 581 } | 578 } |
| 582 case SentenceGranularity: { | 579 case SentenceGranularity: |
| 583 m_start = startOfSentence(createVisiblePosition(m_start, m_affinity)) | 580 return startOfSentence(createVisiblePosition(passedStart)) |
| 584 .deepEquivalent(); | 581 .deepEquivalent(); |
| 585 break; | 582 case LineGranularity: |
| 586 } | 583 return startOfLine(createVisiblePosition(passedStart)).deepEquivalent(); |
| 587 case LineGranularity: { | |
| 588 m_start = startOfLine(createVisiblePosition(m_start, m_affinity)) | |
| 589 .deepEquivalent(); | |
| 590 break; | |
| 591 } | |
| 592 case LineBoundary: | 584 case LineBoundary: |
| 593 m_start = startOfLine(createVisiblePosition(m_start, m_affinity)) | 585 return startOfLine(createVisiblePosition(passedStart)).deepEquivalent(); |
| 594 .deepEquivalent(); | |
| 595 break; | |
| 596 case ParagraphGranularity: { | 586 case ParagraphGranularity: { |
| 597 VisiblePositionTemplate<Strategy> pos = | 587 const VisiblePositionTemplate<Strategy> pos = |
| 598 createVisiblePosition(m_start, m_affinity); | 588 createVisiblePosition(passedStart); |
| 599 if (isStartOfLine(pos) && isEndOfEditableOrNonEditableContent(pos)) | 589 if (isStartOfLine(pos) && isEndOfEditableOrNonEditableContent(pos)) |
| 600 pos = previousPositionOf(pos); | 590 return startOfParagraph(previousPositionOf(pos)).deepEquivalent(); |
| 601 m_start = startOfParagraph(pos).deepEquivalent(); | 591 return startOfParagraph(pos).deepEquivalent(); |
| 602 break; | |
| 603 } | 592 } |
| 604 case DocumentBoundary: | 593 case DocumentBoundary: |
| 605 m_start = startOfDocument(createVisiblePosition(m_start, m_affinity)) | 594 return startOfDocument(createVisiblePosition(passedStart)) |
| 606 .deepEquivalent(); | 595 .deepEquivalent(); |
| 607 break; | |
| 608 case ParagraphBoundary: | 596 case ParagraphBoundary: |
| 609 m_start = startOfParagraph(createVisiblePosition(m_start, m_affinity)) | 597 return startOfParagraph(createVisiblePosition(passedStart)) |
| 610 .deepEquivalent(); | 598 .deepEquivalent(); |
| 611 break; | |
| 612 case SentenceBoundary: | 599 case SentenceBoundary: |
| 613 m_start = startOfSentence(createVisiblePosition(m_start, m_affinity)) | 600 return startOfSentence(createVisiblePosition(passedStart)) |
| 614 .deepEquivalent(); | 601 .deepEquivalent(); |
| 615 break; | |
| 616 } | 602 } |
| 617 | 603 |
| 618 // Make sure we do not have a Null position. | 604 NOTREACHED(); |
| 619 if (m_start.isNull()) | 605 return passedStart.position(); |
| 620 m_start = m_baseIsFirst ? m_base : m_extent; | |
| 621 } | 606 } |
| 622 | 607 |
| 623 template <typename Strategy> | 608 template <typename Strategy> |
| 624 void VisibleSelectionTemplate<Strategy>::setEndRespectingGranularity( | 609 static PositionTemplate<Strategy> computeEndRespectingGranularity( |
| 610 const PositionTemplate<Strategy>& start, |
| 611 const PositionWithAffinityTemplate<Strategy>& passedEnd, |
| 625 TextGranularity granularity) { | 612 TextGranularity granularity) { |
| 626 DCHECK(m_base.isNotNull()); | 613 DCHECK(passedEnd.isNotNull()); |
| 627 DCHECK(m_extent.isNotNull()); | |
| 628 | |
| 629 m_end = m_baseIsFirst ? m_extent : m_base; | |
| 630 | 614 |
| 631 switch (granularity) { | 615 switch (granularity) { |
| 632 case CharacterGranularity: | 616 case CharacterGranularity: |
| 633 // Don't do any expansion. | 617 // Don't do any expansion. |
| 634 break; | 618 return passedEnd.position(); |
| 635 case WordGranularity: { | 619 case WordGranularity: { |
| 636 // General case: Select the word the caret is positioned inside of. | 620 // General case: Select the word the caret is positioned inside of. |
| 637 // If the caret is on the word boundary, select the word according to | 621 // If the caret is on the word boundary, select the word according to |
| 638 // |wordSide|. | 622 // |wordSide|. |
| 639 // Edge case: If the caret is after the last word in a soft-wrapped line | 623 // Edge case: If the caret is after the last word in a soft-wrapped line |
| 640 // or the last word in the document, select that last word | 624 // or the last word in the document, select that last word |
| 641 // (|LeftWordIfOnBoundary|). | 625 // (|LeftWordIfOnBoundary|). |
| 642 // Edge case: If the caret is after the last word in a paragraph, select | 626 // Edge case: If the caret is after the last word in a paragraph, select |
| 643 // from the the end of the last word to the line break (also | 627 // from the the end of the last word to the line break (also |
| 644 // |RightWordIfOnBoundary|); | 628 // |RightWordIfOnBoundary|); |
| 645 const VisiblePositionTemplate<Strategy> originalEnd = | 629 const VisiblePositionTemplate<Strategy> originalEnd = |
| 646 createVisiblePosition(m_end, m_affinity); | 630 createVisiblePosition(passedEnd); |
| 647 EWordSide side = RightWordIfOnBoundary; | 631 EWordSide side = RightWordIfOnBoundary; |
| 648 if (isEndOfEditableOrNonEditableContent(originalEnd) || | 632 if (isEndOfEditableOrNonEditableContent(originalEnd) || |
| 649 (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && | 633 (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && |
| 650 !isEndOfParagraph(originalEnd))) | 634 !isEndOfParagraph(originalEnd))) |
| 651 side = LeftWordIfOnBoundary; | 635 side = LeftWordIfOnBoundary; |
| 652 | 636 |
| 653 const VisiblePositionTemplate<Strategy> wordEnd = | 637 const VisiblePositionTemplate<Strategy> wordEnd = |
| 654 endOfWord(originalEnd, side); | 638 endOfWord(originalEnd, side); |
| 655 VisiblePositionTemplate<Strategy> end = wordEnd; | 639 if (!isEndOfParagraph(originalEnd)) |
| 640 return wordEnd.deepEquivalent(); |
| 641 if (isEmptyTableCell(start.anchorNode())) |
| 642 return wordEnd.deepEquivalent(); |
| 656 | 643 |
| 657 if (isEndOfParagraph(originalEnd) && | 644 // Select the paragraph break (the space from the end of a paragraph |
| 658 !isEmptyTableCell(m_start.anchorNode())) { | 645 // to the start of the next one) to match TextEdit. |
| 659 // Select the paragraph break (the space from the end of a paragraph | 646 const VisiblePositionTemplate<Strategy> end = nextPositionOf(wordEnd); |
| 660 // to the start of the next one) to match TextEdit. | 647 Element* const table = tableElementJustBefore(end); |
| 661 end = nextPositionOf(wordEnd); | 648 if (!table) { |
| 662 | |
| 663 if (Element* table = tableElementJustBefore(end)) { | |
| 664 // The paragraph break after the last paragraph in the last cell | |
| 665 // of a block table ends at the start of the paragraph after the | |
| 666 // table. | |
| 667 if (isEnclosingBlock(table)) | |
| 668 end = nextPositionOf(end, CannotCrossEditingBoundary); | |
| 669 else | |
| 670 end = wordEnd; | |
| 671 } | |
| 672 | |
| 673 if (end.isNull()) | 649 if (end.isNull()) |
| 674 end = wordEnd; | 650 return wordEnd.deepEquivalent(); |
| 651 return end.deepEquivalent(); |
| 675 } | 652 } |
| 676 | 653 |
| 677 m_end = end.deepEquivalent(); | 654 if (!isEnclosingBlock(table)) |
| 678 break; | 655 return wordEnd.deepEquivalent(); |
| 656 |
| 657 // The paragraph break after the last paragraph in the last cell |
| 658 // of a block table ends at the start of the paragraph after the |
| 659 // table. |
| 660 const VisiblePositionTemplate<Strategy> next = |
| 661 nextPositionOf(end, CannotCrossEditingBoundary); |
| 662 if (next.isNull()) |
| 663 return wordEnd.deepEquivalent(); |
| 664 return next.deepEquivalent(); |
| 679 } | 665 } |
| 680 case SentenceGranularity: { | 666 case SentenceGranularity: |
| 681 m_end = endOfSentence(createVisiblePosition(m_end, m_affinity)) | 667 return endOfSentence(createVisiblePosition(passedEnd)).deepEquivalent(); |
| 682 .deepEquivalent(); | |
| 683 break; | |
| 684 } | |
| 685 case LineGranularity: { | 668 case LineGranularity: { |
| 686 VisiblePositionTemplate<Strategy> end = | 669 const VisiblePositionTemplate<Strategy> end = |
| 687 endOfLine(createVisiblePosition(m_end, m_affinity)); | 670 endOfLine(createVisiblePosition(passedEnd)); |
| 671 if (!isEndOfParagraph(end)) |
| 672 return end.deepEquivalent(); |
| 688 // If the end of this line is at the end of a paragraph, include the | 673 // If the end of this line is at the end of a paragraph, include the |
| 689 // space after the end of the line in the selection. | 674 // space after the end of the line in the selection. |
| 690 if (isEndOfParagraph(end)) { | 675 const VisiblePositionTemplate<Strategy> next = nextPositionOf(end); |
| 691 VisiblePositionTemplate<Strategy> next = nextPositionOf(end); | 676 if (next.isNull()) |
| 692 if (next.isNotNull()) | 677 return end.deepEquivalent(); |
| 693 end = next; | 678 return next.deepEquivalent(); |
| 694 } | |
| 695 m_end = end.deepEquivalent(); | |
| 696 break; | |
| 697 } | 679 } |
| 698 case LineBoundary: | 680 case LineBoundary: |
| 699 m_end = | 681 return endOfLine(createVisiblePosition(passedEnd)).deepEquivalent(); |
| 700 endOfLine(createVisiblePosition(m_end, m_affinity)).deepEquivalent(); | |
| 701 break; | |
| 702 case ParagraphGranularity: { | 682 case ParagraphGranularity: { |
| 703 const VisiblePositionTemplate<Strategy> visibleParagraphEnd = | 683 const VisiblePositionTemplate<Strategy> visibleParagraphEnd = |
| 704 endOfParagraph(createVisiblePosition(m_end, m_affinity)); | 684 endOfParagraph(createVisiblePosition(passedEnd)); |
| 705 | 685 |
| 706 // Include the "paragraph break" (the space from the end of this | 686 // Include the "paragraph break" (the space from the end of this |
| 707 // paragraph to the start of the next one) in the selection. | 687 // paragraph to the start of the next one) in the selection. |
| 708 VisiblePositionTemplate<Strategy> end = | 688 const VisiblePositionTemplate<Strategy> end = |
| 709 nextPositionOf(visibleParagraphEnd); | 689 nextPositionOf(visibleParagraphEnd); |
| 710 | 690 |
| 711 if (Element* table = tableElementJustBefore(end)) { | 691 Element* const table = tableElementJustBefore(end); |
| 712 // The paragraph break after the last paragraph in the last cell of | 692 if (!table) { |
| 713 // a block table ends at the start of the paragraph after the table, | 693 if (end.isNull()) |
| 714 // not at the position just after the table. | 694 return visibleParagraphEnd.deepEquivalent(); |
| 715 if (isEnclosingBlock(table)) { | 695 return end.deepEquivalent(); |
| 716 end = nextPositionOf(end, CannotCrossEditingBoundary); | |
| 717 } else { | |
| 718 // There is no parargraph break after the last paragraph in the | |
| 719 // last cell of an inline table. | |
| 720 end = visibleParagraphEnd; | |
| 721 } | |
| 722 } | 696 } |
| 723 | 697 |
| 724 if (end.isNull()) | 698 if (!isEnclosingBlock(table)) { |
| 725 end = visibleParagraphEnd; | 699 // There is no paragraph break after the last paragraph in the |
| 700 // last cell of an inline table. |
| 701 return visibleParagraphEnd.deepEquivalent(); |
| 702 } |
| 726 | 703 |
| 727 m_end = end.deepEquivalent(); | 704 // The paragraph break after the last paragraph in the last cell of |
| 728 break; | 705 // a block table ends at the start of the paragraph after the table, |
| 706 // not at the position just after the table. |
| 707 const VisiblePositionTemplate<Strategy> next = |
| 708 nextPositionOf(end, CannotCrossEditingBoundary); |
| 709 if (next.isNull()) |
| 710 return visibleParagraphEnd.deepEquivalent(); |
| 711 return next.deepEquivalent(); |
| 729 } | 712 } |
| 730 case DocumentBoundary: | 713 case DocumentBoundary: |
| 731 m_end = endOfDocument(createVisiblePosition(m_end, m_affinity)) | 714 return endOfDocument(createVisiblePosition(passedEnd)).deepEquivalent(); |
| 732 .deepEquivalent(); | |
| 733 break; | |
| 734 case ParagraphBoundary: | 715 case ParagraphBoundary: |
| 735 m_end = endOfParagraph(createVisiblePosition(m_end, m_affinity)) | 716 return endOfParagraph(createVisiblePosition(passedEnd)).deepEquivalent(); |
| 736 .deepEquivalent(); | |
| 737 break; | |
| 738 case SentenceBoundary: | 717 case SentenceBoundary: |
| 739 m_end = endOfSentence(createVisiblePosition(m_end, m_affinity)) | 718 return endOfSentence(createVisiblePosition(passedEnd)).deepEquivalent(); |
| 740 .deepEquivalent(); | |
| 741 break; | |
| 742 } | 719 } |
| 743 | 720 NOTREACHED(); |
| 744 // Make sure we do not have a Null position. | 721 return passedEnd.position(); |
| 745 if (m_end.isNull()) | |
| 746 m_end = m_baseIsFirst ? m_extent : m_base; | |
| 747 } | 722 } |
| 748 | 723 |
| 749 template <typename Strategy> | 724 template <typename Strategy> |
| 750 void VisibleSelectionTemplate<Strategy>::updateSelectionType() { | 725 void VisibleSelectionTemplate<Strategy>::updateSelectionType() { |
| 751 m_selectionType = computeSelectionType(m_start, m_end); | 726 m_selectionType = computeSelectionType(m_start, m_end); |
| 752 | 727 |
| 753 // Affinity only makes sense for a caret | 728 // Affinity only makes sense for a caret |
| 754 if (m_selectionType != CaretSelection) | 729 if (m_selectionType != CaretSelection) |
| 755 m_affinity = TextAffinity::Downstream; | 730 m_affinity = TextAffinity::Downstream; |
| 756 } | 731 } |
| 757 | 732 |
| 758 template <typename Strategy> | 733 template <typename Strategy> |
| 759 void VisibleSelectionTemplate<Strategy>::validate(TextGranularity granularity) { | 734 void VisibleSelectionTemplate<Strategy>::validate(TextGranularity granularity) { |
| 760 DCHECK(!needsLayoutTreeUpdate(m_base)); | 735 DCHECK(!needsLayoutTreeUpdate(m_base)); |
| 761 DCHECK(!needsLayoutTreeUpdate(m_extent)); | 736 DCHECK(!needsLayoutTreeUpdate(m_extent)); |
| 762 // TODO(xiaochengh): Add a DocumentLifecycle::DisallowTransitionScope here. | 737 // TODO(xiaochengh): Add a DocumentLifecycle::DisallowTransitionScope here. |
| 763 | 738 |
| 764 m_granularity = granularity; | 739 m_granularity = granularity; |
| 765 m_hasTrailingWhitespace = false; | 740 m_hasTrailingWhitespace = false; |
| 766 setBaseAndExtentToDeepEquivalents(); | 741 setBaseAndExtentToDeepEquivalents(); |
| 767 if (m_base.isNull() || m_extent.isNull()) { | 742 if (m_base.isNull() || m_extent.isNull()) { |
| 768 m_base = m_extent = m_start = m_end = PositionTemplate<Strategy>(); | 743 m_base = m_extent = m_start = m_end = PositionTemplate<Strategy>(); |
| 769 updateSelectionType(); | 744 updateSelectionType(); |
| 770 return; | 745 return; |
| 771 } | 746 } |
| 772 | 747 |
| 773 m_start = m_baseIsFirst ? m_base : m_extent; | 748 const PositionTemplate<Strategy> start = m_baseIsFirst ? m_base : m_extent; |
| 774 m_end = m_baseIsFirst ? m_extent : m_base; | 749 const PositionTemplate<Strategy> newStart = computeStartRespectingGranularity( |
| 775 setStartRespectingGranularity(granularity); | 750 PositionWithAffinityTemplate<Strategy>(start, m_affinity), granularity); |
| 776 DCHECK(m_start.isNotNull()); | 751 m_start = newStart.isNotNull() ? newStart : start; |
| 777 setEndRespectingGranularity(granularity); | 752 |
| 778 DCHECK(m_end.isNotNull()); | 753 const PositionTemplate<Strategy> end = m_baseIsFirst ? m_extent : m_base; |
| 754 const PositionTemplate<Strategy> newEnd = computeEndRespectingGranularity( |
| 755 m_start, PositionWithAffinityTemplate<Strategy>(end, m_affinity), |
| 756 granularity); |
| 757 m_end = newEnd.isNotNull() ? newEnd : end; |
| 758 |
| 779 adjustSelectionToAvoidCrossingShadowBoundaries(); | 759 adjustSelectionToAvoidCrossingShadowBoundaries(); |
| 780 adjustSelectionToAvoidCrossingEditingBoundaries(); | 760 adjustSelectionToAvoidCrossingEditingBoundaries(); |
| 781 updateSelectionType(); | 761 updateSelectionType(); |
| 782 | 762 |
| 783 if (getSelectionType() == RangeSelection) { | 763 if (getSelectionType() == RangeSelection) { |
| 784 // "Constrain" the selection to be the smallest equivalent range of | 764 // "Constrain" the selection to be the smallest equivalent range of |
| 785 // nodes. This is a somewhat arbitrary choice, but experience shows that | 765 // nodes. This is a somewhat arbitrary choice, but experience shows that |
| 786 // it is useful to make to make the selection "canonical" (if only for | 766 // it is useful to make to make the selection "canonical" (if only for |
| 787 // purposes of comparing selections). This is an ideal point of the code | 767 // purposes of comparing selections). This is an ideal point of the code |
| 788 // to do this operation, since all selection changes that result in a | 768 // to do this operation, since all selection changes that result in a |
| (...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1131 | 1111 |
| 1132 void showTree(const blink::VisibleSelectionInFlatTree& sel) { | 1112 void showTree(const blink::VisibleSelectionInFlatTree& sel) { |
| 1133 sel.showTreeForThis(); | 1113 sel.showTreeForThis(); |
| 1134 } | 1114 } |
| 1135 | 1115 |
| 1136 void showTree(const blink::VisibleSelectionInFlatTree* sel) { | 1116 void showTree(const blink::VisibleSelectionInFlatTree* sel) { |
| 1137 if (sel) | 1117 if (sel) |
| 1138 sel->showTreeForThis(); | 1118 sel->showTreeForThis(); |
| 1139 } | 1119 } |
| 1140 #endif | 1120 #endif |
| OLD | NEW |