Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * (C) 2001 Dirk Mueller (mueller@kde.org) | 4 * (C) 2001 Dirk Mueller (mueller@kde.org) |
| 5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org) | 5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
| 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights | 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights |
| 7 * reserved. | 7 * reserved. |
| 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. | 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| 9 * (http://www.torchmobile.com/) | 9 * (http://www.torchmobile.com/) |
| 10 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 10 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 return DocumentMarker::SpellingMarkerIndex; | 77 return DocumentMarker::SpellingMarkerIndex; |
| 78 } | 78 } |
| 79 | 79 |
| 80 } // namespace | 80 } // namespace |
| 81 | 81 |
| 82 inline bool DocumentMarkerController::possiblyHasMarkers( | 82 inline bool DocumentMarkerController::possiblyHasMarkers( |
| 83 DocumentMarker::MarkerTypes types) { | 83 DocumentMarker::MarkerTypes types) { |
| 84 return m_possiblyExistingMarkerTypes.intersects(types); | 84 return m_possiblyExistingMarkerTypes.intersects(types); |
| 85 } | 85 } |
| 86 | 86 |
| 87 DocumentMarkerController::DocumentMarkerController(const Document& document) | 87 DocumentMarkerController::DocumentMarkerController(Document& document) |
| 88 : m_possiblyExistingMarkerTypes(0), m_document(&document) {} | 88 : m_possiblyExistingMarkerTypes(0), m_document(&document) { |
| 89 setContext(&document); | |
| 90 } | |
| 89 | 91 |
| 90 void DocumentMarkerController::clear() { | 92 void DocumentMarkerController::clear() { |
| 91 m_markers.clear(); | 93 m_markers.clear(); |
| 92 m_possiblyExistingMarkerTypes = 0; | 94 m_possiblyExistingMarkerTypes = 0; |
| 93 } | 95 } |
| 94 | 96 |
| 95 void DocumentMarkerController::addMarker(const Position& start, | 97 void DocumentMarkerController::addMarker(const Position& start, |
| 96 const Position& end, | 98 const Position& end, |
| 97 DocumentMarker::MarkerType type, | 99 DocumentMarker::MarkerType type, |
| 98 const String& description, | 100 const String& description, |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 171 TextIterator markedText(range.startPosition(), range.endPosition()); | 173 TextIterator markedText(range.startPosition(), range.endPosition()); |
| 172 DocumentMarkerController::removeMarkers( | 174 DocumentMarkerController::removeMarkers( |
| 173 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); | 175 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); |
| 174 } | 176 } |
| 175 | 177 |
| 176 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, | 178 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, |
| 177 const DocumentMarker* rhv) { | 179 const DocumentMarker* rhv) { |
| 178 return lhv->startOffset() < rhv->startOffset(); | 180 return lhv->startOffset() < rhv->startOffset(); |
| 179 } | 181 } |
| 180 | 182 |
| 181 static bool startsAfter(const Member<RenderedDocumentMarker>& marker, | |
| 182 size_t startOffset) { | |
| 183 return marker->startOffset() < startOffset; | |
| 184 } | |
| 185 | |
| 186 static bool endsBefore(size_t startOffset, | 183 static bool endsBefore(size_t startOffset, |
| 187 const Member<RenderedDocumentMarker>& rhv) { | 184 const Member<RenderedDocumentMarker>& rhv) { |
| 188 return startOffset < rhv->endOffset(); | 185 return startOffset < rhv->endOffset(); |
| 189 } | 186 } |
| 190 | 187 |
| 191 static bool compareByStart(const Member<DocumentMarker>& lhv, | 188 static bool compareByStart(const Member<DocumentMarker>& lhv, |
| 192 const Member<DocumentMarker>& rhv) { | 189 const Member<DocumentMarker>& rhv) { |
| 193 return lhv->startOffset() < rhv->startOffset(); | 190 return lhv->startOffset() < rhv->startOffset(); |
| 194 } | 191 } |
| 195 | 192 |
| (...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 581 | 578 |
| 582 if (markerList->front()->type() == DocumentMarker::TextMatch) | 579 if (markerList->front()->type() == DocumentMarker::TextMatch) |
| 583 invalidatePaintForTickmarks(node); | 580 invalidatePaintForTickmarks(node); |
| 584 } | 581 } |
| 585 } | 582 } |
| 586 } | 583 } |
| 587 | 584 |
| 588 DEFINE_TRACE(DocumentMarkerController) { | 585 DEFINE_TRACE(DocumentMarkerController) { |
| 589 visitor->trace(m_markers); | 586 visitor->trace(m_markers); |
| 590 visitor->trace(m_document); | 587 visitor->trace(m_document); |
| 588 SynchronousMutationObserver::trace(visitor); | |
| 591 } | 589 } |
| 592 | 590 |
| 593 void DocumentMarkerController::removeMarkers( | 591 void DocumentMarkerController::removeMarkers( |
| 594 Node* node, | 592 Node* node, |
| 595 DocumentMarker::MarkerTypes markerTypes) { | 593 DocumentMarker::MarkerTypes markerTypes) { |
| 596 if (!possiblyHasMarkers(markerTypes)) | 594 if (!possiblyHasMarkers(markerTypes)) |
| 597 return; | 595 return; |
| 598 DCHECK(!m_markers.isEmpty()); | 596 DCHECK(!m_markers.isEmpty()); |
| 599 | 597 |
| 600 MarkerMap::iterator iterator = m_markers.find(node); | 598 MarkerMap::iterator iterator = m_markers.find(node); |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 690 invalidatePaintForTickmarks(node); | 688 invalidatePaintForTickmarks(node); |
| 691 } | 689 } |
| 692 | 690 |
| 693 if (nodeCanBeRemoved) { | 691 if (nodeCanBeRemoved) { |
| 694 m_markers.remove(iterator); | 692 m_markers.remove(iterator); |
| 695 if (m_markers.isEmpty()) | 693 if (m_markers.isEmpty()) |
| 696 m_possiblyExistingMarkerTypes = 0; | 694 m_possiblyExistingMarkerTypes = 0; |
| 697 } | 695 } |
| 698 } | 696 } |
| 699 | 697 |
| 698 void DocumentMarkerController::removeZeroLengthMarkers() { | |
|
Xiaocheng
2017/02/22 01:49:23
This function may cause performance issue. It iter
rlanday
2017/02/22 02:01:10
Ah, good catch
rlanday
2017/02/22 22:53:01
Wait a second, isn't it potentially quadratic anyw
Xiaocheng
2017/02/22 23:39:28
The marker removal itself is indeed quadratic, to
| |
| 699 for (auto& nodeMarkers : m_markers) { | |
| 700 const Node& node = *nodeMarkers.key; | |
| 701 MarkerLists& markers = *nodeMarkers.value; | |
| 702 for (size_t markerListIndex = 0; | |
| 703 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 704 ++markerListIndex) { | |
| 705 Member<MarkerList> list = markers[markerListIndex]; | |
| 706 if (!list) | |
| 707 continue; | |
| 708 bool removedMarkers = false; | |
| 709 for (size_t j = list->size(); j > 0; --j) { | |
| 710 if (list->at(j - 1)->startOffset() == list->at(j - 1)->endOffset()) { | |
| 711 list->remove(j - 1); | |
| 712 removedMarkers = true; | |
| 713 } | |
| 714 } | |
| 715 if (!removedMarkers) | |
| 716 continue; | |
| 717 if (markerListIndex != DocumentMarker::TextMatchMarkerIndex) | |
| 718 continue; | |
| 719 invalidatePaintForTickmarks(node); | |
| 720 } | |
| 721 } | |
| 722 } | |
| 723 | |
| 700 void DocumentMarkerController::repaintMarkers( | 724 void DocumentMarkerController::repaintMarkers( |
| 701 DocumentMarker::MarkerTypes markerTypes) { | 725 DocumentMarker::MarkerTypes markerTypes) { |
| 702 if (!possiblyHasMarkers(markerTypes)) | 726 if (!possiblyHasMarkers(markerTypes)) |
| 703 return; | 727 return; |
| 704 DCHECK(!m_markers.isEmpty()); | 728 DCHECK(!m_markers.isEmpty()); |
| 705 | 729 |
| 706 // outer loop: process each markered node in the document | 730 // outer loop: process each markered node in the document |
| 707 MarkerMap::iterator end = m_markers.end(); | 731 MarkerMap::iterator end = m_markers.end(); |
| 708 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { | 732 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { |
| 709 const Node* node = i->key; | 733 const Node* node = i->key; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 720 | 744 |
| 721 // cause the node to be redrawn | 745 // cause the node to be redrawn |
| 722 if (LayoutObject* layoutObject = node->layoutObject()) { | 746 if (LayoutObject* layoutObject = node->layoutObject()) { |
| 723 layoutObject->setShouldDoFullPaintInvalidation(); | 747 layoutObject->setShouldDoFullPaintInvalidation(); |
| 724 break; | 748 break; |
| 725 } | 749 } |
| 726 } | 750 } |
| 727 } | 751 } |
| 728 } | 752 } |
| 729 | 753 |
| 730 void DocumentMarkerController::shiftMarkers(Node* node, | |
| 731 unsigned startOffset, | |
| 732 int delta) { | |
| 733 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 734 return; | |
| 735 DCHECK(!m_markers.isEmpty()); | |
| 736 | |
| 737 MarkerLists* markers = m_markers.get(node); | |
| 738 if (!markers) | |
| 739 return; | |
| 740 | |
| 741 bool didShiftMarker = false; | |
| 742 for (size_t markerListIndex = 0; | |
| 743 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 744 ++markerListIndex) { | |
| 745 Member<MarkerList>& list = (*markers)[markerListIndex]; | |
| 746 if (!list) | |
| 747 continue; | |
| 748 MarkerList::iterator startPos = | |
| 749 std::lower_bound(list->begin(), list->end(), startOffset, startsAfter); | |
| 750 for (MarkerList::iterator marker = startPos; marker != list->end(); | |
| 751 ++marker) { | |
| 752 #if DCHECK_IS_ON() | |
| 753 int startOffset = (*marker)->startOffset(); | |
| 754 DCHECK_GE(startOffset + delta, 0); | |
| 755 #endif | |
| 756 (*marker)->shiftOffsets(delta); | |
| 757 didShiftMarker = true; | |
| 758 } | |
| 759 } | |
| 760 | |
| 761 if (didShiftMarker) { | |
| 762 invalidateRectsForMarkersInNode(*node); | |
| 763 // repaint the affected node | |
| 764 if (node->layoutObject()) | |
| 765 node->layoutObject()->setShouldDoFullPaintInvalidation(); | |
| 766 } | |
| 767 } | |
| 768 | |
| 769 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, | 754 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, |
| 770 bool active) { | 755 bool active) { |
| 771 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | 756 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) |
| 772 return false; | 757 return false; |
| 773 | 758 |
| 774 DCHECK(!m_markers.isEmpty()); | 759 DCHECK(!m_markers.isEmpty()); |
| 775 | 760 |
| 776 Node* const startContainer = range.startPosition().computeContainerNode(); | 761 Node* const startContainer = range.startPosition().computeContainerNode(); |
| 777 DCHECK(startContainer); | 762 DCHECK(startContainer); |
| 778 Node* const endContainer = range.endPosition().computeContainerNode(); | 763 Node* const endContainer = range.endPosition().computeContainerNode(); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 851 builder.append(")"); | 836 builder.append(")"); |
| 852 } | 837 } |
| 853 } | 838 } |
| 854 builder.append("\n"); | 839 builder.append("\n"); |
| 855 } | 840 } |
| 856 LOG(INFO) << m_markers.size() << " nodes have markers:\n" | 841 LOG(INFO) << m_markers.size() << " nodes have markers:\n" |
| 857 << builder.toString().utf8().data(); | 842 << builder.toString().utf8().data(); |
| 858 } | 843 } |
| 859 #endif | 844 #endif |
| 860 | 845 |
| 846 // SynchronousMutationObserver | |
| 847 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, | |
| 848 unsigned offset, | |
| 849 unsigned oldLength, | |
| 850 unsigned newLength) { | |
| 851 // If we're doing a pure remove operation, remove the markers in the range | |
| 852 // being removed (markers containing, but larger than, the range, will be | |
| 853 // split) | |
| 854 if (newLength == 0) | |
| 855 removeMarkers(node, offset, oldLength); | |
| 856 | |
| 857 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 858 return; | |
| 859 DCHECK(!m_markers.isEmpty()); | |
| 860 | |
| 861 MarkerLists* markers = m_markers.get(node); | |
| 862 if (!markers) | |
| 863 return; | |
| 864 | |
| 865 bool didShiftMarker = false; | |
| 866 for (size_t markerListIndex = 0; | |
|
Xiaocheng
2017/02/22 01:49:23
Range-based for loop is preferred.
rlanday
2017/02/22 02:14:57
I think that would be difficult given that we're r
| |
| 867 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 868 ++markerListIndex) { | |
| 869 Member<MarkerList> list = (*markers)[markerListIndex]; | |
| 870 if (!list) | |
| 871 continue; | |
| 872 | |
| 873 for (MarkerList::iterator marker = list->begin(); marker != list->end(); | |
| 874 ++marker) { | |
| 875 // if marker is contained by but not equal to the replaced range, remove | |
| 876 // the marker | |
| 877 if ((offset <= (*marker)->startOffset() && | |
| 878 (*marker)->endOffset() < offset + oldLength) || | |
| 879 (offset < (*marker)->startOffset() && | |
| 880 (*marker)->endOffset() <= offset + oldLength)) { | |
| 881 list->remove(marker - list->begin()); | |
| 882 marker--; | |
|
Xiaocheng
2017/02/22 01:49:23
s/marker--/--marker/
Chromium prefers prefix incr
| |
| 883 continue; | |
| 884 } | |
| 885 | |
| 886 // algorithm inspired by https://dom.spec.whatwg.org/#concept-cd-replace | |
| 887 // but with some changes | |
| 888 if ((*marker)->startOffset() > offset) { | |
| 889 // Deviation from the concept-cd-replace algorithm: < instead of <= in | |
| 890 // the next line | |
| 891 if ((*marker)->startOffset() < offset + oldLength) { | |
| 892 // Marker start was in the replaced text. Move to beginning of new | |
| 893 // text | |
| 894 (*marker)->setStartOffset(offset); | |
|
Xiaocheng
2017/02/22 01:49:23
It seems more natural to move to end of new text.
rlanday
2017/02/22 02:01:11
I think this is how it works for DOM ranges, but I
| |
| 895 } else { | |
| 896 // Marker start was after the replaced text. Shift by length | |
| 897 // difference | |
| 898 unsigned oldStartOffset = (*marker)->startOffset(); | |
| 899 (*marker)->setStartOffset(oldStartOffset + newLength - oldLength); | |
| 900 } | |
| 901 } | |
| 902 | |
| 903 if ((*marker)->endOffset() > offset) { | |
| 904 // Deviation from the concept-cd-replace algorithm: < instead of <= in | |
| 905 // the next line | |
| 906 if ((*marker)->endOffset() < offset + oldLength) { | |
| 907 // Marker end was in the replaced text. Move to beginning of new text | |
| 908 (*marker)->setEndOffset(offset); | |
| 909 } else { | |
| 910 // Marker end was after the replaced text. Shift by length difference | |
| 911 unsigned oldEndOffset = (*marker)->endOffset(); | |
| 912 (*marker)->setEndOffset(oldEndOffset + newLength - oldLength); | |
| 913 } | |
| 914 } | |
| 915 | |
| 916 didShiftMarker = true; | |
| 917 } | |
| 918 } | |
| 919 | |
| 920 if (didShiftMarker) { | |
| 921 removeZeroLengthMarkers(); | |
| 922 invalidateRectsForMarkersInNode(*node); | |
| 923 // repaint the affected node | |
| 924 if (node->layoutObject()) | |
| 925 node->layoutObject()->setShouldDoFullPaintInvalidation(); | |
|
Xiaocheng
2017/02/22 01:49:23
Do we really need this call?
Shouldn't paint (alo
rlanday
2017/02/22 02:01:11
I'm not really sure, I think I added this because
yosin_UTC9
2017/02/22 02:25:05
Please ask wangxianzhu@, very near to you, he is T
Xianzhu
2017/02/22 21:32:56
Though layout may also trigger the repaint, I thin
| |
| 926 } | |
| 927 } | |
| 928 | |
| 861 } // namespace blink | 929 } // namespace blink |
| 862 | 930 |
| 863 #ifndef NDEBUG | 931 #ifndef NDEBUG |
| 864 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { | 932 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { |
| 865 if (controller) | 933 if (controller) |
| 866 controller->showMarkers(); | 934 controller->showMarkers(); |
| 867 } | 935 } |
| 868 #endif | 936 #endif |
| OLD | NEW |