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

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

Issue 2650113004: [WIP] Add support for Android SuggestionSpans when editing text (Closed)
Patch Set: Remove logging statements, fix copyright years in new files 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 17 matching lines...) Expand all
28 28
29 #include "core/editing/markers/DocumentMarkerController.h" 29 #include "core/editing/markers/DocumentMarkerController.h"
30 30
31 #include "core/dom/Node.h" 31 #include "core/dom/Node.h"
32 #include "core/dom/NodeTraversal.h" 32 #include "core/dom/NodeTraversal.h"
33 #include "core/dom/Range.h" 33 #include "core/dom/Range.h"
34 #include "core/dom/Text.h" 34 #include "core/dom/Text.h"
35 #include "core/editing/iterators/TextIterator.h" 35 #include "core/editing/iterators/TextIterator.h"
36 #include "core/editing/markers/RenderedDocumentMarker.h" 36 #include "core/editing/markers/RenderedDocumentMarker.h"
37 #include "core/frame/FrameView.h" 37 #include "core/frame/FrameView.h"
38 #include "core/frame/LocalFrame.h"
38 #include "core/layout/LayoutObject.h" 39 #include "core/layout/LayoutObject.h"
39 #include <algorithm> 40 #include <algorithm>
40 41
41 #ifndef NDEBUG 42 #ifndef NDEBUG
42 #include <stdio.h> 43 #include <stdio.h>
43 #endif 44 #endif
44 45
45 namespace blink { 46 namespace blink {
46 47
47 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) 48 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words)
(...skipping 16 matching lines...) Expand all
64 case DocumentMarker::Spelling: 65 case DocumentMarker::Spelling:
65 return DocumentMarker::SpellingMarkerIndex; 66 return DocumentMarker::SpellingMarkerIndex;
66 case DocumentMarker::Grammar: 67 case DocumentMarker::Grammar:
67 return DocumentMarker::GrammarMarkerIndex; 68 return DocumentMarker::GrammarMarkerIndex;
68 case DocumentMarker::TextMatch: 69 case DocumentMarker::TextMatch:
69 return DocumentMarker::TextMatchMarkerIndex; 70 return DocumentMarker::TextMatchMarkerIndex;
70 case DocumentMarker::InvisibleSpellcheck: 71 case DocumentMarker::InvisibleSpellcheck:
71 return DocumentMarker::InvisibleSpellcheckMarkerIndex; 72 return DocumentMarker::InvisibleSpellcheckMarkerIndex;
72 case DocumentMarker::Composition: 73 case DocumentMarker::Composition:
73 return DocumentMarker::CompositionMarkerIndex; 74 return DocumentMarker::CompositionMarkerIndex;
75 case DocumentMarker::Suggestion:
76 return DocumentMarker::SuggestionMarkerIndex;
77 case DocumentMarker::SuggestionBackgroundHighlight:
78 return DocumentMarker::SuggestionBackgroundHighlightMarkerIndex;
74 } 79 }
75 80
76 NOTREACHED(); 81 NOTREACHED();
77 return DocumentMarker::SpellingMarkerIndex; 82 return DocumentMarker::SpellingMarkerIndex;
78 } 83 }
79 84
80 } // namespace 85 } // namespace
81 86
82 inline bool DocumentMarkerController::possiblyHasMarkers( 87 inline bool DocumentMarkerController::possiblyHasMarkers(
83 DocumentMarker::MarkerTypes types) { 88 DocumentMarker::MarkerTypes types) {
84 return m_possiblyExistingMarkerTypes.intersects(types); 89 return m_possiblyExistingMarkerTypes.intersects(types);
85 } 90 }
86 91
87 DocumentMarkerController::DocumentMarkerController(const Document& document) 92 DocumentMarkerController::DocumentMarkerController(const Document& document)
88 : m_possiblyExistingMarkerTypes(0), m_document(&document) {} 93 : m_possiblyExistingMarkerTypes(0),
94 m_document(&document),
95 m_nextSuggestionMarkerID(0) {}
89 96
90 void DocumentMarkerController::clear() { 97 void DocumentMarkerController::clear() {
91 m_markers.clear(); 98 m_markers.clear();
92 m_possiblyExistingMarkerTypes = 0; 99 m_possiblyExistingMarkerTypes = 0;
100 m_nextSuggestionMarkerID = 0;
93 } 101 }
94 102
95 void DocumentMarkerController::addMarker(const Position& start, 103 void DocumentMarkerController::addMarker(const Position& start,
96 const Position& end, 104 const Position& end,
97 DocumentMarker::MarkerType type, 105 DocumentMarker::MarkerType type,
98 const String& description, 106 const String& description,
99 uint32_t hash) { 107 uint32_t hash) {
100 // Use a TextIterator to visit the potentially multiple nodes the range 108 // Use a TextIterator to visit the potentially multiple nodes the range
101 // covers. 109 // covers.
102 for (TextIterator markedText(start, end); !markedText.atEnd(); 110 for (TextIterator markedText(start, end); !markedText.atEnd();
(...skipping 14 matching lines...) Expand all
117 for (TextIterator markedText(range.startPosition(), range.endPosition()); 125 for (TextIterator markedText(range.startPosition(), range.endPosition());
118 !markedText.atEnd(); markedText.advance()) 126 !markedText.atEnd(); markedText.advance())
119 addMarker( 127 addMarker(
120 markedText.currentContainer(), 128 markedText.currentContainer(),
121 DocumentMarker(markedText.startOffsetInCurrentContainer(), 129 DocumentMarker(markedText.startOffsetInCurrentContainer(),
122 markedText.endOffsetInCurrentContainer(), activeMatch)); 130 markedText.endOffsetInCurrentContainer(), activeMatch));
123 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a 131 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a
124 // throttling algorithm. crbug.com/6819. 132 // throttling algorithm. crbug.com/6819.
125 } 133 }
126 134
127 void DocumentMarkerController::addCompositionMarker(const Position& start, 135 void DocumentMarkerController::addCompositionMarker(
128 const Position& end, 136 const Position& start,
129 Color underlineColor, 137 const Position& end,
130 bool thick, 138 Color underlineColor,
131 Color backgroundColor) { 139 bool thick,
140 Color backgroundColor,
141 const std::vector<std::string>& suggestions) {
esprehn 2017/01/31 22:41:35 ditto
rlanday 2017/01/31 23:30:09 Ok
142 DCHECK(!m_document->needsLayoutTreeUpdate());
143 for (TextIterator markedText(start, end); !markedText.atEnd();
144 markedText.advance()) {
145 addMarker(markedText.currentContainer(),
146 DocumentMarker(markedText.startOffsetInCurrentContainer(),
147 markedText.endOffsetInCurrentContainer(),
148 underlineColor, thick, backgroundColor,
149 suggestions, m_nextSuggestionMarkerID++));
150 }
151 }
152
153 void DocumentMarkerController::addSuggestionBackgroundHighlightMarker(
154 const Position& start,
155 const Position& end,
156 Color underlineColor,
157 bool thick,
158 Color backgroundColor) {
132 DCHECK(!m_document->needsLayoutTreeUpdate()); 159 DCHECK(!m_document->needsLayoutTreeUpdate());
133 160
134 for (TextIterator markedText(start, end); !markedText.atEnd(); 161 for (TextIterator markedText(start, end); !markedText.atEnd();
135 markedText.advance()) 162 markedText.advance()) {
136 addMarker(markedText.currentContainer(), 163 addMarker(markedText.currentContainer(),
137 DocumentMarker(markedText.startOffsetInCurrentContainer(), 164 DocumentMarker(DocumentMarker::SuggestionBackgroundHighlight,
165 markedText.startOffsetInCurrentContainer(),
138 markedText.endOffsetInCurrentContainer(), 166 markedText.endOffsetInCurrentContainer(),
139 underlineColor, thick, backgroundColor)); 167 underlineColor, thick, backgroundColor));
168 }
140 } 169 }
141 170
142 void DocumentMarkerController::prepareForDestruction() { 171 void DocumentMarkerController::prepareForDestruction() {
143 clear(); 172 clear();
144 } 173 }
145 174
146 void DocumentMarkerController::removeMarkers( 175 void DocumentMarkerController::removeMarkers(
147 TextIterator& markedText, 176 TextIterator& markedText,
148 DocumentMarker::MarkerTypes markerTypes, 177 DocumentMarker::MarkerTypes markerTypes,
149 RemovePartiallyOverlappingMarkerOrNot 178 RemovePartiallyOverlappingMarkerOrNot
150 shouldRemovePartiallyOverlappingMarker) { 179 shouldRemovePartiallyOverlappingMarker) {
151 for (; !markedText.atEnd(); markedText.advance()) { 180 for (; !markedText.atEnd(); markedText.advance()) {
152 if (!possiblyHasMarkers(markerTypes)) 181 if (!possiblyHasMarkers(markerTypes))
153 return; 182 return;
154 DCHECK(!m_markers.isEmpty()); 183 DCHECK(!m_markers.isEmpty());
155 184
156 int startOffset = markedText.startOffsetInCurrentContainer(); 185 int startOffset = markedText.startOffsetInCurrentContainer();
157 int endOffset = markedText.endOffsetInCurrentContainer(); 186 int endOffset = markedText.endOffsetInCurrentContainer();
187
158 removeMarkers(markedText.currentContainer(), startOffset, 188 removeMarkers(markedText.currentContainer(), startOffset,
159 endOffset - startOffset, markerTypes, 189 endOffset - startOffset, markerTypes,
160 shouldRemovePartiallyOverlappingMarker); 190 shouldRemovePartiallyOverlappingMarker);
161 } 191 }
162 } 192 }
163 193
164 void DocumentMarkerController::removeMarkers( 194 void DocumentMarkerController::removeMarkers(
165 const EphemeralRange& range, 195 const EphemeralRange& range,
166 DocumentMarker::MarkerTypes markerTypes, 196 DocumentMarker::MarkerTypes markerTypes,
167 RemovePartiallyOverlappingMarkerOrNot 197 RemovePartiallyOverlappingMarkerOrNot
168 shouldRemovePartiallyOverlappingMarker) { 198 shouldRemovePartiallyOverlappingMarker) {
169 DCHECK(!m_document->needsLayoutTreeUpdate()); 199 DCHECK(!m_document->needsLayoutTreeUpdate());
170 200
171 TextIterator markedText(range.startPosition(), range.endPosition()); 201 TextIterator markedText(range.startPosition(), range.endPosition());
172 DocumentMarkerController::removeMarkers( 202 DocumentMarkerController::removeMarkers(
173 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); 203 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker);
174 } 204 }
175 205
176 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, 206 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv,
177 const DocumentMarker* rhv) { 207 const DocumentMarker* rhv) {
178 return lhv->startOffset() < rhv->startOffset(); 208 return lhv->startOffset() < rhv->startOffset();
179 } 209 }
180 210
181 static bool startsAfter(const Member<RenderedDocumentMarker>& marker,
182 size_t startOffset) {
183 return marker->startOffset() < startOffset;
184 }
185
186 static bool compareByStart(const Member<DocumentMarker>& lhv, 211 static bool compareByStart(const Member<DocumentMarker>& lhv,
187 const Member<DocumentMarker>& rhv) { 212 const Member<DocumentMarker>& rhv) {
188 return lhv->startOffset() < rhv->startOffset(); 213 return lhv->startOffset() < rhv->startOffset();
189 } 214 }
190 215
191 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv, 216 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv,
192 const DocumentMarker* rhv) { 217 const DocumentMarker* rhv) {
193 return lhv->endOffset() < rhv->startOffset(); 218 return lhv->endOffset() < rhv->startOffset();
194 } 219 }
195 220
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 markers->at(markerListIndex) = new MarkerList; 267 markers->at(markerListIndex) = new MarkerList;
243 } 268 }
244 269
245 Member<MarkerList>& list = markers->at(markerListIndex); 270 Member<MarkerList>& list = markers->at(markerListIndex);
246 RenderedDocumentMarker* newRenderedMarker = 271 RenderedDocumentMarker* newRenderedMarker =
247 RenderedDocumentMarker::create(newMarker); 272 RenderedDocumentMarker::create(newMarker);
248 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) { 273 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) {
249 list->push_back(newRenderedMarker); 274 list->push_back(newRenderedMarker);
250 } else { 275 } else {
251 if (newMarker.type() != DocumentMarker::TextMatch && 276 if (newMarker.type() != DocumentMarker::TextMatch &&
252 newMarker.type() != DocumentMarker::Composition) { 277 newMarker.type() != DocumentMarker::Composition &&
278 newMarker.type() != DocumentMarker::Suggestion) {
253 mergeOverlapping(list.get(), newRenderedMarker); 279 mergeOverlapping(list.get(), newRenderedMarker);
254 } else { 280 } else {
255 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), 281 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(),
256 &newMarker, startsFurther); 282 &newMarker, startsFurther);
257 list->insert(pos - list->begin(), newRenderedMarker); 283 list->insert(pos - list->begin(), newRenderedMarker);
258 } 284 }
259 } 285 }
260 286
261 // repaint the affected node 287 // repaint the affected node
262 if (node->layoutObject()) 288 if (node->layoutObject())
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 unsigned startOffset, 367 unsigned startOffset,
342 int length, 368 int length,
343 DocumentMarker::MarkerTypes markerTypes, 369 DocumentMarker::MarkerTypes markerTypes,
344 RemovePartiallyOverlappingMarkerOrNot 370 RemovePartiallyOverlappingMarkerOrNot
345 shouldRemovePartiallyOverlappingMarker) { 371 shouldRemovePartiallyOverlappingMarker) {
346 if (length <= 0) 372 if (length <= 0)
347 return; 373 return;
348 374
349 if (!possiblyHasMarkers(markerTypes)) 375 if (!possiblyHasMarkers(markerTypes))
350 return; 376 return;
377
351 DCHECK(!(m_markers.isEmpty())); 378 DCHECK(!(m_markers.isEmpty()));
352 379
353 MarkerLists* markers = m_markers.get(node); 380 MarkerLists* markers = m_markers.get(node);
354 if (!markers) 381 if (!markers)
355 return; 382 return;
356 383
357 bool docDirty = false; 384 bool docDirty = false;
358 size_t emptyListsCount = 0; 385 size_t emptyListsCount = 0;
359 for (size_t markerListIndex = 0; 386 for (size_t markerListIndex = 0;
360 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 387 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 result.push_back(list->at(j).get()); 489 result.push_back(list->at(j).get());
463 } 490 }
464 } 491 }
465 std::sort(result.begin(), result.end(), compareByStart); 492 std::sort(result.begin(), result.end(), compareByStart);
466 return result; 493 return result;
467 } 494 }
468 495
469 DocumentMarkerVector DocumentMarkerController::markersInRange( 496 DocumentMarkerVector DocumentMarkerController::markersInRange(
470 const EphemeralRange& range, 497 const EphemeralRange& range,
471 DocumentMarker::MarkerTypes markerTypes) { 498 DocumentMarker::MarkerTypes markerTypes) {
499 return markersInRangeInternal(range, markerTypes, false);
500 }
501
502 DocumentMarkerVector DocumentMarkerController::markersInRangeInclusive(
503 const EphemeralRange& range,
504 DocumentMarker::MarkerTypes markerTypes) {
505 return markersInRangeInternal(range, markerTypes, true);
506 }
507
508 DocumentMarkerVector DocumentMarkerController::markersInRangeInternal(
509 const EphemeralRange& range,
510 DocumentMarker::MarkerTypes markerTypes,
511 bool includeSpansTouchingEndpoints) {
472 if (!possiblyHasMarkers(markerTypes)) 512 if (!possiblyHasMarkers(markerTypes))
473 return DocumentMarkerVector(); 513 return DocumentMarkerVector();
474 514
475 DocumentMarkerVector foundMarkers; 515 DocumentMarkerVector foundMarkers;
476 516
477 Node* startContainer = range.startPosition().computeContainerNode(); 517 Node* startContainer = range.startPosition().computeContainerNode();
478 DCHECK(startContainer); 518 if (!startContainer)
519 return DocumentMarkerVector();
520
479 unsigned startOffset = static_cast<unsigned>( 521 unsigned startOffset = static_cast<unsigned>(
480 range.startPosition().computeOffsetInContainerNode()); 522 range.startPosition().computeOffsetInContainerNode());
523
481 Node* endContainer = range.endPosition().computeContainerNode(); 524 Node* endContainer = range.endPosition().computeContainerNode();
482 DCHECK(endContainer); 525 if (!endContainer)
526 return DocumentMarkerVector();
527
483 unsigned endOffset = 528 unsigned endOffset =
484 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); 529 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode());
485 530
486 for (Node& node : range.nodes()) { 531 for (Node& node : range.nodes()) {
487 for (DocumentMarker* marker : markersFor(&node)) { 532 for (DocumentMarker* marker : markersFor(&node)) {
488 if (!markerTypes.contains(marker->type())) 533 if (!markerTypes.contains(marker->type()))
489 continue; 534 continue;
490 if (node == startContainer && marker->endOffset() <= startOffset) 535 if (node == startContainer) {
491 continue; 536 if (marker->endOffset() < startOffset ||
492 if (node == endContainer && marker->startOffset() >= endOffset) 537 (marker->endOffset() == startOffset &&
493 continue; 538 !includeSpansTouchingEndpoints))
539 continue;
540 }
541 if (node == endContainer) {
542 if (marker->startOffset() > endOffset ||
543 (marker->startOffset() == endOffset &&
544 !includeSpansTouchingEndpoints))
545 continue;
546 }
494 foundMarkers.push_back(marker); 547 foundMarkers.push_back(marker);
495 } 548 }
496 } 549 }
497 return foundMarkers; 550 return foundMarkers;
498 } 551 }
499 552
500 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( 553 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(
501 DocumentMarker::MarkerType markerType) { 554 DocumentMarker::MarkerType markerType) {
502 Vector<IntRect> result; 555 Vector<IntRect> result;
503 556
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
634 unsigned size = nodesWithMarkers.size(); 687 unsigned size = nodesWithMarkers.size();
635 for (unsigned i = 0; i < size; ++i) { 688 for (unsigned i = 0; i < size; ++i) {
636 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); 689 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]);
637 if (iterator != m_markers.end()) 690 if (iterator != m_markers.end())
638 removeMarkersFromList(iterator, markerTypes); 691 removeMarkersFromList(iterator, markerTypes);
639 } 692 }
640 693
641 m_possiblyExistingMarkerTypes.remove(markerTypes); 694 m_possiblyExistingMarkerTypes.remove(markerTypes);
642 } 695 }
643 696
697 void DocumentMarkerController::removeSuggestionMarkersByID(
698 const Vector<int>& idsToRemove) {
699 for (auto& nodeMarkers : m_markers) {
700 MarkerLists& markers = *nodeMarkers.value;
701 Member<MarkerList>& suggestionMarkerList =
702 markers[DocumentMarker::SuggestionMarkerIndex];
703 if (!suggestionMarkerList)
704 continue;
705 bool removedMarkers = false;
706 for (size_t j = suggestionMarkerList->size(); j > 0; --j) {
707 if (idsToRemove.contains(
708 suggestionMarkerList->at(j - 1)->suggestionMarkerID())) {
709 suggestionMarkerList->remove(j - 1);
710 removedMarkers = true;
711 }
712 }
713 }
714 }
715
716 void DocumentMarkerController::removeMarkersForWordsAffectedByEditing(
717 DocumentMarker::MarkerTypes markerTypes,
718 bool doNotRemoveIfSelectionAtWordBoundary) {
719 DCHECK(m_document->frame()->selection().isAvailable());
720 TRACE_EVENT0(
721 "blink",
722 "DocumentMarkerController::removeMarkersForWordsAffectedByEditing");
723
724 Document* document = m_document->frame()->document();
725 DCHECK(document);
726
727 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
728 // needs to be audited. See http://crbug.com/590369 for more details.
729 document->updateStyleAndLayoutIgnorePendingStylesheets();
730
731 // We want to remove the markers from a word if an editing command will change
732 // the word. This can happen in one of several scenarios:
733 // 1. Insert in the middle of a word.
734 // 2. Appending non whitespace at the beginning of word.
735 // 3. Appending non whitespace at the end of word.
736 // Note that, appending only whitespaces at the beginning or end of word won't
737 // change the word, so we don't need to remove the markers on that word. Of
738 // course, if current selection is a range, we potentially will edit two words
739 // that fall on the boundaries of selection, and remove words between the
740 // selection boundaries.
741 VisiblePosition startOfSelection =
742 m_document->frame()->selection().selection().visibleStart();
743 VisiblePosition endOfSelection =
744 m_document->frame()->selection().selection().visibleEnd();
745 if (startOfSelection.isNull())
746 return;
747 // First word is the word that ends after or on the start of selection.
748 VisiblePosition startOfFirstWord =
749 startOfWord(startOfSelection, LeftWordIfOnBoundary);
750 VisiblePosition endOfFirstWord =
751 endOfWord(startOfSelection, LeftWordIfOnBoundary);
752 // Last word is the word that begins before or on the end of selection
753 VisiblePosition startOfLastWord =
754 startOfWord(endOfSelection, RightWordIfOnBoundary);
755 VisiblePosition endOfLastWord =
756 endOfWord(endOfSelection, RightWordIfOnBoundary);
757
758 if (startOfFirstWord.isNull()) {
759 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary);
760 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary);
761 }
762
763 if (endOfLastWord.isNull()) {
764 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary);
765 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary);
766 }
767
768 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the
769 // start of selection, we choose next word as the first word.
770 if (doNotRemoveIfSelectionAtWordBoundary &&
771 endOfFirstWord.deepEquivalent() == startOfSelection.deepEquivalent()) {
772 startOfFirstWord = nextWordPosition(startOfFirstWord);
773 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary);
774 if (startOfFirstWord.deepEquivalent() == endOfSelection.deepEquivalent())
775 return;
776 }
777
778 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at
779 // the end of selection, we choose previous word as the last word.
780 if (doNotRemoveIfSelectionAtWordBoundary &&
781 startOfLastWord.deepEquivalent() == endOfSelection.deepEquivalent()) {
782 startOfLastWord = previousWordPosition(startOfLastWord);
783 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary);
784 if (endOfLastWord.deepEquivalent() == startOfSelection.deepEquivalent())
785 return;
786 }
787
788 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() ||
789 startOfLastWord.isNull() || endOfLastWord.isNull())
790 return;
791
792 const Position& removeMarkerStart = startOfFirstWord.deepEquivalent();
793 const Position& removeMarkerEnd = endOfLastWord.deepEquivalent();
794 if (removeMarkerStart > removeMarkerEnd) {
795 // editing/inserting/insert-br-008.html and more reach here.
796 // TODO(yosin): To avoid |DCHECK(removeMarkerStart <= removeMarkerEnd)|
797 // in |EphemeralRange| constructor, we have this if-statement. Once we
798 // fix |startOfWord()| and |endOfWord()|, we should remove this
799 // if-statement.
800 return;
801 }
802
803 // Now we remove markers on everything between startOfFirstWord and
804 // endOfLastWord. However, if an autocorrection change a single word to
805 // multiple words, we want to remove correction mark from all the resulted
806 // words even we only edit one of them. For example, assuming autocorrection
807 // changes "avantgarde" to "avant garde", we will have CorrectionIndicator
808 // marker on both words and on the whitespace between them. If we then edit
809 // garde, we would like to remove the marker from word "avant" and whitespace
810 // as well. So we need to get the continous range of of marker that contains
811 // the word in question, and remove marker on that whole range.
812 const EphemeralRange wordRange(removeMarkerStart, removeMarkerEnd);
813 document->markers().removeMarkers(
814 wordRange, markerTypes,
815 DocumentMarkerController::RemovePartiallyOverlappingMarker);
816 }
817
644 void DocumentMarkerController::removeMarkersFromList( 818 void DocumentMarkerController::removeMarkersFromList(
645 MarkerMap::iterator iterator, 819 MarkerMap::iterator iterator,
646 DocumentMarker::MarkerTypes markerTypes) { 820 DocumentMarker::MarkerTypes markerTypes) {
647 bool needsRepainting = false; 821 bool needsRepainting = false;
648 bool nodeCanBeRemoved; 822 bool nodeCanBeRemoved;
649 823
650 size_t emptyListsCount = 0; 824 size_t emptyListsCount = 0;
651 if (markerTypes == DocumentMarker::AllMarkers()) { 825 if (markerTypes == DocumentMarker::AllMarkers()) {
652 needsRepainting = true; 826 needsRepainting = true;
653 nodeCanBeRemoved = true; 827 nodeCanBeRemoved = true;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 // cause the node to be redrawn 888 // cause the node to be redrawn
715 if (LayoutObject* layoutObject = node->layoutObject()) { 889 if (LayoutObject* layoutObject = node->layoutObject()) {
716 layoutObject->setShouldDoFullPaintInvalidation(); 890 layoutObject->setShouldDoFullPaintInvalidation();
717 break; 891 break;
718 } 892 }
719 } 893 }
720 } 894 }
721 } 895 }
722 896
723 void DocumentMarkerController::shiftMarkers(Node* node, 897 void DocumentMarkerController::shiftMarkers(Node* node,
724 unsigned startOffset, 898 int startOffset,
725 int delta) { 899 int prevLength,
900 int newLength) {
901 DCHECK_GE(startOffset, 0);
902 DCHECK_GE(prevLength, 0);
903 DCHECK_GE(newLength, 0);
904
726 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 905 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
727 return; 906 return;
728 DCHECK(!m_markers.isEmpty()); 907 DCHECK(!m_markers.isEmpty());
729 908
730 MarkerLists* markers = m_markers.get(node); 909 MarkerLists* markers = m_markers.get(node);
731 if (!markers) 910 if (!markers)
732 return; 911 return;
733 912
734 bool didShiftMarker = false; 913 bool didShiftMarker = false;
735 for (size_t markerListIndex = 0; 914 for (size_t markerListIndex = 0;
736 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 915 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
737 ++markerListIndex) { 916 ++markerListIndex) {
738 Member<MarkerList>& list = (*markers)[markerListIndex]; 917 Member<MarkerList>& list = (*markers)[markerListIndex];
739 if (!list) 918 if (!list)
740 continue; 919 continue;
741 MarkerList::iterator startPos = 920
742 std::lower_bound(list->begin(), list->end(), startOffset, startsAfter); 921 // algorithm from https://dom.spec.whatwg.org/#concept-cd-replace
rlanday 2017/01/31 19:50:22 This is fancy logic that lets you replace part of
743 for (MarkerList::iterator marker = startPos; marker != list->end(); 922 for (MarkerList::iterator marker = list->begin(); marker != list->end();
744 ++marker) { 923 ++marker) {
745 #if DCHECK_IS_ON() 924 if ((*marker)->startOffset() > (unsigned)startOffset) {
746 int startOffset = (*marker)->startOffset(); 925 if ((*marker)->startOffset() <= (unsigned)(startOffset + prevLength)) {
747 DCHECK_GE(startOffset + delta, 0); 926 // Marker start was in the replaced text. Move to beginning of new
748 #endif 927 // text
749 (*marker)->shiftOffsets(delta); 928 (*marker)->setStartOffset(startOffset);
929 } else {
930 // Marker start was after the replaced text. Shift by length
931 // difference
932 unsigned oldStartOffset = (*marker)->startOffset();
933 (*marker)->setStartOffset(oldStartOffset + newLength - prevLength);
934 }
935 }
936
937 if ((*marker)->endOffset() > (unsigned)startOffset) {
938 if ((*marker)->endOffset() <= (unsigned)(startOffset + prevLength)) {
939 // Marker end was in the replaced text. Move to beginning of new text
940 (*marker)->setEndOffset(startOffset);
941 } else {
942 // Marker end was after the replaced text. Shift by length difference
943 unsigned oldEndOffset = (*marker)->endOffset();
944 (*marker)->setEndOffset(oldEndOffset + newLength - prevLength);
945 }
946 }
947
750 didShiftMarker = true; 948 didShiftMarker = true;
751 } 949 }
752 } 950 }
753 951
754 if (didShiftMarker) { 952 if (didShiftMarker) {
755 invalidateRectsForMarkersInNode(*node); 953 invalidateRectsForMarkersInNode(*node);
756 // repaint the affected node 954 // repaint the affected node
757 if (node->layoutObject()) 955 if (node->layoutObject())
758 node->layoutObject()->setShouldDoFullPaintInvalidation(); 956 node->layoutObject()->setShouldDoFullPaintInvalidation();
759 } 957 }
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 #endif 1047 #endif
850 1048
851 } // namespace blink 1049 } // namespace blink
852 1050
853 #ifndef NDEBUG 1051 #ifndef NDEBUG
854 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { 1052 void showDocumentMarkers(const blink::DocumentMarkerController* controller) {
855 if (controller) 1053 if (controller)
856 controller->showMarkers(); 1054 controller->showMarkers();
857 } 1055 }
858 #endif 1056 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698