Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Side by Side Diff: third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp

Issue 2692093003: Rewrite DocumentMarkerController to use SynchronousMutationObserver (Closed)
Patch Set: Rebase on separate CL to add SetCharacterDataCommand Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698