Chromium Code Reviews| Index: third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp | 
| diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp | 
| index 41afe11c85185d22f509ddc551141887d1ae33c5..9e6f06b28f1e731421150e71cfb61d9da13861bd 100644 | 
| --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp | 
| +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp | 
| @@ -84,8 +84,10 @@ inline bool DocumentMarkerController::possiblyHasMarkers( | 
| return m_possiblyExistingMarkerTypes.intersects(types); | 
| } | 
| -DocumentMarkerController::DocumentMarkerController(const Document& document) | 
| - : m_possiblyExistingMarkerTypes(0), m_document(&document) {} | 
| +DocumentMarkerController::DocumentMarkerController(Document& document) | 
| + : m_possiblyExistingMarkerTypes(0), m_document(&document) { | 
| + setContext(&document); | 
| +} | 
| void DocumentMarkerController::clear() { | 
| m_markers.clear(); | 
| @@ -178,11 +180,6 @@ static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, | 
| return lhv->startOffset() < rhv->startOffset(); | 
| } | 
| -static bool startsAfter(const Member<RenderedDocumentMarker>& marker, | 
| - size_t startOffset) { | 
| - return marker->startOffset() < startOffset; | 
| -} | 
| - | 
| static bool endsBefore(size_t startOffset, | 
| const Member<RenderedDocumentMarker>& rhv) { | 
| return startOffset < rhv->endOffset(); | 
| @@ -588,6 +585,7 @@ void DocumentMarkerController::invalidateRectsForAllMarkers() { | 
| DEFINE_TRACE(DocumentMarkerController) { | 
| visitor->trace(m_markers); | 
| visitor->trace(m_document); | 
| + SynchronousMutationObserver::trace(visitor); | 
| } | 
| void DocumentMarkerController::removeMarkers( | 
| @@ -697,6 +695,32 @@ void DocumentMarkerController::removeMarkersFromList( | 
| } | 
| } | 
| +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
 
 | 
| + for (auto& nodeMarkers : m_markers) { | 
| + const Node& node = *nodeMarkers.key; | 
| + MarkerLists& markers = *nodeMarkers.value; | 
| + for (size_t markerListIndex = 0; | 
| + markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 
| + ++markerListIndex) { | 
| + Member<MarkerList> list = markers[markerListIndex]; | 
| + if (!list) | 
| + continue; | 
| + bool removedMarkers = false; | 
| + for (size_t j = list->size(); j > 0; --j) { | 
| + if (list->at(j - 1)->startOffset() == list->at(j - 1)->endOffset()) { | 
| + list->remove(j - 1); | 
| + removedMarkers = true; | 
| + } | 
| + } | 
| + if (!removedMarkers) | 
| + continue; | 
| + if (markerListIndex != DocumentMarker::TextMatchMarkerIndex) | 
| + continue; | 
| + invalidatePaintForTickmarks(node); | 
| + } | 
| + } | 
| +} | 
| + | 
| void DocumentMarkerController::repaintMarkers( | 
| DocumentMarker::MarkerTypes markerTypes) { | 
| if (!possiblyHasMarkers(markerTypes)) | 
| @@ -727,45 +751,6 @@ void DocumentMarkerController::repaintMarkers( | 
| } | 
| } | 
| -void DocumentMarkerController::shiftMarkers(Node* node, | 
| - unsigned startOffset, | 
| - int delta) { | 
| - if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | 
| - return; | 
| - DCHECK(!m_markers.isEmpty()); | 
| - | 
| - MarkerLists* markers = m_markers.get(node); | 
| - if (!markers) | 
| - return; | 
| - | 
| - bool didShiftMarker = false; | 
| - for (size_t markerListIndex = 0; | 
| - markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 
| - ++markerListIndex) { | 
| - Member<MarkerList>& list = (*markers)[markerListIndex]; | 
| - if (!list) | 
| - continue; | 
| - MarkerList::iterator startPos = | 
| - std::lower_bound(list->begin(), list->end(), startOffset, startsAfter); | 
| - for (MarkerList::iterator marker = startPos; marker != list->end(); | 
| - ++marker) { | 
| -#if DCHECK_IS_ON() | 
| - int startOffset = (*marker)->startOffset(); | 
| - DCHECK_GE(startOffset + delta, 0); | 
| -#endif | 
| - (*marker)->shiftOffsets(delta); | 
| - didShiftMarker = true; | 
| - } | 
| - } | 
| - | 
| - if (didShiftMarker) { | 
| - invalidateRectsForMarkersInNode(*node); | 
| - // repaint the affected node | 
| - if (node->layoutObject()) | 
| - node->layoutObject()->setShouldDoFullPaintInvalidation(); | 
| - } | 
| -} | 
| - | 
| bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, | 
| bool active) { | 
| if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | 
| @@ -858,6 +843,89 @@ void DocumentMarkerController::showMarkers() const { | 
| } | 
| #endif | 
| +// SynchronousMutationObserver | 
| +void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, | 
| + unsigned offset, | 
| + unsigned oldLength, | 
| + unsigned newLength) { | 
| + // If we're doing a pure remove operation, remove the markers in the range | 
| + // being removed (markers containing, but larger than, the range, will be | 
| + // split) | 
| + if (newLength == 0) | 
| + removeMarkers(node, offset, oldLength); | 
| + | 
| + if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | 
| + return; | 
| + DCHECK(!m_markers.isEmpty()); | 
| + | 
| + MarkerLists* markers = m_markers.get(node); | 
| + if (!markers) | 
| + return; | 
| + | 
| + bool didShiftMarker = false; | 
| + 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
 
 | 
| + markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 
| + ++markerListIndex) { | 
| + Member<MarkerList> list = (*markers)[markerListIndex]; | 
| + if (!list) | 
| + continue; | 
| + | 
| + for (MarkerList::iterator marker = list->begin(); marker != list->end(); | 
| + ++marker) { | 
| + // if marker is contained by but not equal to the replaced range, remove | 
| + // the marker | 
| + if ((offset <= (*marker)->startOffset() && | 
| + (*marker)->endOffset() < offset + oldLength) || | 
| + (offset < (*marker)->startOffset() && | 
| + (*marker)->endOffset() <= offset + oldLength)) { | 
| + list->remove(marker - list->begin()); | 
| + marker--; | 
| 
 
Xiaocheng
2017/02/22 01:49:23
s/marker--/--marker/
Chromium prefers prefix incr
 
 | 
| + continue; | 
| + } | 
| + | 
| + // algorithm inspired by https://dom.spec.whatwg.org/#concept-cd-replace | 
| + // but with some changes | 
| + if ((*marker)->startOffset() > offset) { | 
| + // Deviation from the concept-cd-replace algorithm: < instead of <= in | 
| + // the next line | 
| + if ((*marker)->startOffset() < offset + oldLength) { | 
| + // Marker start was in the replaced text. Move to beginning of new | 
| + // text | 
| + (*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
 
 | 
| + } else { | 
| + // Marker start was after the replaced text. Shift by length | 
| + // difference | 
| + unsigned oldStartOffset = (*marker)->startOffset(); | 
| + (*marker)->setStartOffset(oldStartOffset + newLength - oldLength); | 
| + } | 
| + } | 
| + | 
| + if ((*marker)->endOffset() > offset) { | 
| + // Deviation from the concept-cd-replace algorithm: < instead of <= in | 
| + // the next line | 
| + if ((*marker)->endOffset() < offset + oldLength) { | 
| + // Marker end was in the replaced text. Move to beginning of new text | 
| + (*marker)->setEndOffset(offset); | 
| + } else { | 
| + // Marker end was after the replaced text. Shift by length difference | 
| + unsigned oldEndOffset = (*marker)->endOffset(); | 
| + (*marker)->setEndOffset(oldEndOffset + newLength - oldLength); | 
| + } | 
| + } | 
| + | 
| + didShiftMarker = true; | 
| + } | 
| + } | 
| + | 
| + if (didShiftMarker) { | 
| + removeZeroLengthMarkers(); | 
| + invalidateRectsForMarkersInNode(*node); | 
| + // repaint the affected node | 
| + if (node->layoutObject()) | 
| + 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
 
 | 
| + } | 
| +} | 
| + | 
| } // namespace blink | 
| #ifndef NDEBUG |