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

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

Issue 2755013004: Improve how DocumentMarkerController updates markers in response to text edits (Closed)
Patch Set: Put shifting logic in relocateMarkerIfNeeded() method Created 3 years, 9 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
« no previous file with comments | « third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 TextIterator markedText(range.startPosition(), range.endPosition()); 170 TextIterator markedText(range.startPosition(), range.endPosition());
171 DocumentMarkerController::removeMarkers( 171 DocumentMarkerController::removeMarkers(
172 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); 172 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker);
173 } 173 }
174 174
175 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, 175 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv,
176 const DocumentMarker* rhv) { 176 const DocumentMarker* rhv) {
177 return lhv->startOffset() < rhv->startOffset(); 177 return lhv->startOffset() < rhv->startOffset();
178 } 178 }
179 179
180 static bool startsAfter(const Member<RenderedDocumentMarker>& marker,
181 size_t startOffset) {
182 return marker->startOffset() < startOffset;
183 }
184
185 static bool endsBefore(size_t startOffset, 180 static bool endsBefore(size_t startOffset,
186 const Member<RenderedDocumentMarker>& rhv) { 181 const Member<RenderedDocumentMarker>& rhv) {
187 return startOffset < rhv->endOffset(); 182 return startOffset < rhv->endOffset();
188 } 183 }
189 184
190 static bool compareByStart(const Member<DocumentMarker>& lhv, 185 static bool compareByStart(const Member<DocumentMarker>& lhv,
191 const Member<DocumentMarker>& rhv) { 186 const Member<DocumentMarker>& rhv) {
192 return lhv->startOffset() < rhv->startOffset(); 187 return lhv->startOffset() < rhv->startOffset();
193 } 188 }
194 189
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 297
303 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 298 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
304 return; 299 return;
305 DCHECK(!m_markers.isEmpty()); 300 DCHECK(!m_markers.isEmpty());
306 301
307 MarkerLists* markers = m_markers.at(srcNode); 302 MarkerLists* markers = m_markers.at(srcNode);
308 if (!markers) 303 if (!markers)
309 return; 304 return;
310 305
311 bool docDirty = false; 306 bool docDirty = false;
312 for (size_t markerListIndex = 0; 307 for (Member<MarkerList> list : *markers) {
313 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
314 ++markerListIndex) {
315 Member<MarkerList>& list = (*markers)[markerListIndex];
316 if (!list) 308 if (!list)
317 continue; 309 continue;
318 310
319 unsigned endOffset = startOffset + length - 1; 311 unsigned endOffset = startOffset + length - 1;
320 MarkerList::iterator startPos = std::lower_bound( 312 MarkerList::iterator startPos = std::lower_bound(
321 list->begin(), list->end(), startOffset, doesNotInclude); 313 list->begin(), list->end(), startOffset, doesNotInclude);
322 for (MarkerList::iterator i = startPos; i != list->end(); ++i) { 314 for (MarkerList::iterator i = startPos; i != list->end(); ++i) {
323 DocumentMarker* marker = i->get(); 315 DocumentMarker* marker = i->get();
324 316
325 // stop if we are now past the specified range 317 // stop if we are now past the specified range
(...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after
647 unsigned size = nodesWithMarkers.size(); 639 unsigned size = nodesWithMarkers.size();
648 for (unsigned i = 0; i < size; ++i) { 640 for (unsigned i = 0; i < size; ++i) {
649 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); 641 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]);
650 if (iterator != m_markers.end()) 642 if (iterator != m_markers.end())
651 removeMarkersFromList(iterator, markerTypes); 643 removeMarkersFromList(iterator, markerTypes);
652 } 644 }
653 645
654 m_possiblyExistingMarkerTypes.remove(markerTypes); 646 m_possiblyExistingMarkerTypes.remove(markerTypes);
655 } 647 }
656 648
649 bool DocumentMarkerController::relocateMarkerIfNeeded(
650 RenderedDocumentMarker* marker,
651 unsigned offset,
652 unsigned oldLength,
653 unsigned newLength) {
654 bool didShiftMarker = false;
655
656 // algorithm inspired by https://dom.spec.whatwg.org/#concept-cd-replace
657 // but with some changes
658 if (marker->startOffset() > offset) {
rlanday 2017/03/23 22:52:00 I think there's one more tweak we want to make: as
659 // Deviation from the concept-cd-replace algorithm: < instead of <= in
660 // the next line
661 if (marker->startOffset() < offset + oldLength) {
rlanday 2017/03/23 22:52:00 Since we're moving the marker start to the end of
662 // Marker start was in the replaced text. Move to end of new text
663 // (Deviation from the concept-cd-replace algorithm: that algorithm
664 // would move to the beginning of the new text here)
665 marker->setStartOffset(offset + newLength);
666 } else {
667 // Marker start was after the replaced text. Shift by length
668 // difference
669 unsigned oldStartOffset = marker->startOffset();
670 marker->setStartOffset(oldStartOffset + newLength - oldLength);
671 }
672 didShiftMarker = true;
673 }
674
675 if (marker->endOffset() > offset) {
676 // Deviation from the concept-cd-replace algorithm: < instead of <= in
677 // the next line
678 if (marker->endOffset() < offset + oldLength) {
679 // Marker end was in the replaced text. Move to beginning of new text
680 marker->setEndOffset(offset);
681 } else {
682 // Marker end was after the replaced text. Shift by length difference
683 unsigned oldEndOffset = marker->endOffset();
684 marker->setEndOffset(oldEndOffset + newLength - oldLength);
685 }
686 didShiftMarker = true;
687 }
688
689 return didShiftMarker;
690 }
691
657 void DocumentMarkerController::removeMarkersFromList( 692 void DocumentMarkerController::removeMarkersFromList(
658 MarkerMap::iterator iterator, 693 MarkerMap::iterator iterator,
659 DocumentMarker::MarkerTypes markerTypes) { 694 DocumentMarker::MarkerTypes markerTypes) {
660 bool needsRepainting = false; 695 bool needsRepainting = false;
661 bool nodeCanBeRemoved; 696 bool nodeCanBeRemoved;
662 697
663 size_t emptyListsCount = 0; 698 size_t emptyListsCount = 0;
664 if (markerTypes == DocumentMarker::AllMarkers()) { 699 if (markerTypes == DocumentMarker::AllMarkers()) {
665 needsRepainting = true; 700 needsRepainting = true;
666 nodeCanBeRemoved = true; 701 nodeCanBeRemoved = true;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
729 // cause the node to be redrawn 764 // cause the node to be redrawn
730 if (LayoutObject* layoutObject = node->layoutObject()) { 765 if (LayoutObject* layoutObject = node->layoutObject()) {
731 layoutObject->setShouldDoFullPaintInvalidation( 766 layoutObject->setShouldDoFullPaintInvalidation(
732 PaintInvalidationDocumentMarkerChange); 767 PaintInvalidationDocumentMarkerChange);
733 break; 768 break;
734 } 769 }
735 } 770 }
736 } 771 }
737 } 772 }
738 773
739 void DocumentMarkerController::shiftMarkers(Node* node,
740 unsigned startOffset,
741 int delta) {
742 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
743 return;
744 DCHECK(!m_markers.isEmpty());
745
746 MarkerLists* markers = m_markers.at(node);
747 if (!markers)
748 return;
749
750 bool didShiftMarker = false;
751 for (size_t markerListIndex = 0;
752 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
753 ++markerListIndex) {
754 Member<MarkerList>& list = (*markers)[markerListIndex];
755 if (!list)
756 continue;
757 MarkerList::iterator startPos =
758 std::lower_bound(list->begin(), list->end(), startOffset, startsAfter);
759 for (MarkerList::iterator marker = startPos; marker != list->end();
760 ++marker) {
761 #if DCHECK_IS_ON()
762 int startOffset = (*marker)->startOffset();
763 DCHECK_GE(startOffset + delta, 0);
764 #endif
765 (*marker)->shiftOffsets(delta);
766 didShiftMarker = true;
767 }
768 }
769
770 if (didShiftMarker) {
771 invalidateRectsForMarkersInNode(*node);
772 // repaint the affected node
773 if (node->layoutObject()) {
774 node->layoutObject()->setShouldDoFullPaintInvalidation(
775 PaintInvalidationDocumentMarkerChange);
776 }
777 }
778 }
779
780 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, 774 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range,
781 bool active) { 775 bool active) {
782 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 776 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
783 return false; 777 return false;
784 778
785 DCHECK(!m_markers.isEmpty()); 779 DCHECK(!m_markers.isEmpty());
786 780
787 Node* const startContainer = range.startPosition().computeContainerNode(); 781 Node* const startContainer = range.startPosition().computeContainerNode();
788 DCHECK(startContainer); 782 DCHECK(startContainer);
789 Node* const endContainer = range.endPosition().computeContainerNode(); 783 Node* const endContainer = range.endPosition().computeContainerNode();
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
869 LOG(INFO) << m_markers.size() << " nodes have markers:\n" 863 LOG(INFO) << m_markers.size() << " nodes have markers:\n"
870 << builder.toString().utf8().data(); 864 << builder.toString().utf8().data();
871 } 865 }
872 #endif 866 #endif
873 867
874 // SynchronousMutationObserver 868 // SynchronousMutationObserver
875 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, 869 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node,
876 unsigned offset, 870 unsigned offset,
877 unsigned oldLength, 871 unsigned oldLength,
878 unsigned newLength) { 872 unsigned newLength) {
879 // Shift markers as if we first remove the old text, then insert the new text 873 // If we're doing a pure remove operation, remove the markers in the range
880 removeMarkers(node, offset, oldLength); 874 // being removed (markers containing, but larger than, the range, will be
881 shiftMarkers(node, offset + oldLength, 0 - oldLength); 875 // split)
882 shiftMarkers(node, offset, newLength); 876 if (newLength == 0)
877 removeMarkers(node, offset, oldLength);
878
879 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
880 return;
881 DCHECK(!m_markers.isEmpty());
882
883 MarkerLists* markers = m_markers.at(node);
884 if (!markers)
885 return;
886
887 bool didShiftMarker = false;
888 for (MarkerList* list : *markers) {
889 if (!list)
890 continue;
891
892 for (MarkerList::iterator it = list->begin(); it != list->end(); ++it) {
893 RenderedDocumentMarker& marker = **it;
894 if (relocateMarkerIfNeeded(&marker, offset, oldLength, newLength)) {
895 didShiftMarker = true;
896
897 if (marker.startOffset() < marker.endOffset())
898 continue;
899 list->remove(it - list->begin());
900 --it;
901 }
902 }
903 }
904
905 if (!didShiftMarker)
906 return;
907 if (!node->layoutObject())
908 return;
909 invalidateRectsForMarkersInNode(*node);
910 // repaint the affected node
911 node->layoutObject()->setShouldDoFullPaintInvalidation();
883 } 912 }
884 913
885 } // namespace blink 914 } // namespace blink
886 915
887 #ifndef NDEBUG 916 #ifndef NDEBUG
888 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { 917 void showDocumentMarkers(const blink::DocumentMarkerController* controller) {
889 if (controller) 918 if (controller)
890 controller->showMarkers(); 919 controller->showMarkers();
891 } 920 }
892 #endif 921 #endif
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698