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

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

Issue 2723663002: Refactor DocumentMarkerController (Closed)
Patch Set: Rebase Created 3 years, 8 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 10 matching lines...) Expand all
21 * 21 *
22 * You should have received a copy of the GNU Library General Public License 22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to 23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA. 25 * Boston, MA 02110-1301, USA.
26 * 26 *
27 */ 27 */
28 28
29 #include "core/editing/markers/DocumentMarkerController.h" 29 #include "core/editing/markers/DocumentMarkerController.h"
30 30
31 #include <algorithm>
31 #include "core/dom/Node.h" 32 #include "core/dom/Node.h"
32 #include "core/dom/NodeTraversal.h" 33 #include "core/dom/NodeTraversal.h"
33 #include "core/dom/Range.h" 34 #include "core/dom/Range.h"
34 #include "core/dom/Text.h" 35 #include "core/dom/Text.h"
35 #include "core/editing/iterators/TextIterator.h" 36 #include "core/editing/iterators/TextIterator.h"
36 #include "core/editing/markers/RenderedDocumentMarker.h" 37 #include "core/editing/markers/CompositionMarkerList.h"
38 #include "core/editing/markers/DocumentMarkerList.h"
39 #include "core/editing/markers/EditingMarkerList.h"
40 #include "core/editing/markers/SpellCheckMarkerList.h"
41 #include "core/editing/markers/TextMatchMarkerList.h"
37 #include "core/frame/FrameView.h" 42 #include "core/frame/FrameView.h"
38 #include "core/layout/LayoutObject.h" 43 #include "core/layout/LayoutObject.h"
39 #include <algorithm>
40 44
41 #ifndef NDEBUG 45 #ifndef NDEBUG
42 #include <stdio.h> 46 #include <stdio.h>
43 #endif 47 #endif
44 48
45 namespace blink { 49 namespace blink {
46 50
47 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) 51 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words)
48 : m_words(words) {} 52 : m_words(words) {}
49 53
50 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, 54 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker,
51 const Text& textNode) const { 55 const Text& textNode) const {
52 unsigned start = documentMarker.startOffset(); 56 unsigned start = documentMarker.startOffset();
53 unsigned length = documentMarker.endOffset() - documentMarker.startOffset(); 57 unsigned length = documentMarker.endOffset() - documentMarker.startOffset();
54 58
55 String markerText = textNode.data().substring(start, length); 59 String markerText = textNode.data().substring(start, length);
56 return m_words.contains(markerText); 60 return m_words.contains(markerText);
57 } 61 }
58 62
59 namespace {
60
61 DocumentMarker::MarkerTypeIndex MarkerTypeToMarkerIndex(
62 DocumentMarker::MarkerType type) {
63 switch (type) {
64 case DocumentMarker::Spelling:
65 return DocumentMarker::SpellingMarkerIndex;
66 case DocumentMarker::Grammar:
67 return DocumentMarker::GrammarMarkerIndex;
68 case DocumentMarker::TextMatch:
69 return DocumentMarker::TextMatchMarkerIndex;
70 case DocumentMarker::Composition:
71 return DocumentMarker::CompositionMarkerIndex;
72 }
73
74 NOTREACHED();
75 return DocumentMarker::SpellingMarkerIndex;
76 }
77
78 } // namespace
79
80 inline bool DocumentMarkerController::possiblyHasMarkers(
81 DocumentMarker::MarkerTypes types) {
82 return m_possiblyExistingMarkerTypes.intersects(types);
83 }
84
85 DocumentMarkerController::DocumentMarkerController(Document& document) 63 DocumentMarkerController::DocumentMarkerController(Document& document)
86 : m_possiblyExistingMarkerTypes(0), m_document(&document) { 64 : m_document(&document) {
87 setContext(&document); 65 setContext(&document);
88 } 66 }
89 67
90 void DocumentMarkerController::clear() { 68 void DocumentMarkerController::clear() {
91 m_markers.clear(); 69 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
92 m_possiblyExistingMarkerTypes = 0; 70 MarkerMap& markerMap = markerMapForType(type);
71 markerMap.clear();
72 }
93 } 73 }
94 74
95 void DocumentMarkerController::addMarker(const Position& start, 75 void DocumentMarkerController::addMarker(const Position& start,
96 const Position& end, 76 const Position& end,
97 DocumentMarker::MarkerType type, 77 DocumentMarker::MarkerType type,
98 const String& description) { 78 const String& description) {
79 DCHECK(type != DocumentMarker::TextMatch);
99 // Use a TextIterator to visit the potentially multiple nodes the range 80 // Use a TextIterator to visit the potentially multiple nodes the range
100 // covers. 81 // covers.
101 for (TextIterator markedText(start, end); !markedText.atEnd(); 82 for (TextIterator markedText(start, end); !markedText.atEnd();
102 markedText.advance()) { 83 markedText.advance()) {
103 addMarker( 84 addMarker(markedText.currentContainer(),
104 markedText.currentContainer(), 85 new DocumentMarker(
105 DocumentMarker(type, markedText.startOffsetInCurrentContainer(), 86 type, markedText.startOffsetInCurrentContainer(),
106 markedText.endOffsetInCurrentContainer(), description)); 87 markedText.endOffsetInCurrentContainer(), description));
107 } 88 }
108 } 89 }
109 90
110 void DocumentMarkerController::addTextMatchMarker(const EphemeralRange& range, 91 void DocumentMarkerController::addTextMatchMarker(const EphemeralRange& range,
111 bool activeMatch) { 92 bool activeMatch) {
112 DCHECK(!m_document->needsLayoutTreeUpdate()); 93 DCHECK(!m_document->needsLayoutTreeUpdate());
113 94
114 // Use a TextIterator to visit the potentially multiple nodes the range 95 // Use a TextIterator to visit the potentially multiple nodes the range
115 // covers. 96 // covers.
116 for (TextIterator markedText(range.startPosition(), range.endPosition()); 97 for (TextIterator markedText(range.startPosition(), range.endPosition());
117 !markedText.atEnd(); markedText.advance()) 98 !markedText.atEnd(); markedText.advance()) {
118 addMarker( 99 addMarker(markedText.currentContainer(),
119 markedText.currentContainer(), 100 new TextMatchMarker(markedText.startOffsetInCurrentContainer(),
120 DocumentMarker(markedText.startOffsetInCurrentContainer(), 101 markedText.endOffsetInCurrentContainer(),
121 markedText.endOffsetInCurrentContainer(), activeMatch)); 102 activeMatch));
103 }
122 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a 104 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a
123 // throttling algorithm. crbug.com/6819. 105 // throttling algorithm. crbug.com/6819.
124 } 106 }
125 107
126 void DocumentMarkerController::addCompositionMarker(const Position& start, 108 void DocumentMarkerController::addCompositionMarker(const Position& start,
127 const Position& end, 109 const Position& end,
128 Color underlineColor, 110 Color underlineColor,
129 bool thick, 111 bool thick,
130 Color backgroundColor) { 112 Color backgroundColor) {
131 DCHECK(!m_document->needsLayoutTreeUpdate()); 113 DCHECK(!m_document->needsLayoutTreeUpdate());
132 114
133 for (TextIterator markedText(start, end); !markedText.atEnd(); 115 for (TextIterator markedText(start, end); !markedText.atEnd();
134 markedText.advance()) 116 markedText.advance()) {
135 addMarker(markedText.currentContainer(), 117 addMarker(markedText.currentContainer(),
136 DocumentMarker(markedText.startOffsetInCurrentContainer(), 118 new DocumentMarker(markedText.startOffsetInCurrentContainer(),
137 markedText.endOffsetInCurrentContainer(), 119 markedText.endOffsetInCurrentContainer(),
138 underlineColor, thick, backgroundColor)); 120 underlineColor, thick, backgroundColor));
121 }
139 } 122 }
140 123
141 void DocumentMarkerController::prepareForDestruction() { 124 void DocumentMarkerController::prepareForDestruction() {
142 clear(); 125 clear();
143 } 126 }
144 127
145 void DocumentMarkerController::removeMarkers( 128 void DocumentMarkerController::removeMarkers(
146 TextIterator& markedText,
147 DocumentMarker::MarkerTypes markerTypes,
148 RemovePartiallyOverlappingMarkerOrNot
149 shouldRemovePartiallyOverlappingMarker) {
150 for (; !markedText.atEnd(); markedText.advance()) {
151 if (!possiblyHasMarkers(markerTypes))
152 return;
153 DCHECK(!m_markers.isEmpty());
154
155 int startOffset = markedText.startOffsetInCurrentContainer();
156 int endOffset = markedText.endOffsetInCurrentContainer();
157 removeMarkers(markedText.currentContainer(), startOffset,
158 endOffset - startOffset, markerTypes,
159 shouldRemovePartiallyOverlappingMarker);
160 }
161 }
162
163 void DocumentMarkerController::removeMarkers(
164 const EphemeralRange& range, 129 const EphemeralRange& range,
165 DocumentMarker::MarkerTypes markerTypes, 130 DocumentMarker::MarkerTypes markerTypes,
166 RemovePartiallyOverlappingMarkerOrNot 131 RemovePartiallyOverlappingMarkerOrNot
167 shouldRemovePartiallyOverlappingMarker) { 132 shouldRemovePartiallyOverlappingMarker) {
168 DCHECK(!m_document->needsLayoutTreeUpdate()); 133 DCHECK(!m_document->needsLayoutTreeUpdate());
169 134
170 TextIterator markedText(range.startPosition(), range.endPosition()); 135 TextIterator markedText(range.startPosition(), range.endPosition());
171 DocumentMarkerController::removeMarkers( 136 DocumentMarkerController::removeMarkers(
172 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); 137 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker);
173 } 138 }
174 139
175 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv,
176 const DocumentMarker* rhv) {
177 return lhv->startOffset() < rhv->startOffset();
178 }
179
180 static bool endsBefore(size_t startOffset,
181 const Member<RenderedDocumentMarker>& rhv) {
182 return startOffset < rhv->endOffset();
183 }
184
185 static bool compareByStart(const Member<DocumentMarker>& lhv, 140 static bool compareByStart(const Member<DocumentMarker>& lhv,
186 const Member<DocumentMarker>& rhv) { 141 const Member<DocumentMarker>& rhv) {
187 return lhv->startOffset() < rhv->startOffset(); 142 return lhv->startOffset() < rhv->startOffset();
188 } 143 }
189 144
190 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv,
191 const DocumentMarker* rhv) {
192 return lhv->endOffset() < rhv->startOffset();
193 }
194
195 static bool doesNotInclude(const Member<RenderedDocumentMarker>& marker,
196 size_t startOffset) {
197 return marker->endOffset() < startOffset;
198 }
199
200 static void updateMarkerRenderedRect(const Node& node,
201 RenderedDocumentMarker& marker) {
202 Range* range = Range::create(node.document());
203 // The offsets of the marker may be out-dated, so check for exceptions.
204 DummyExceptionStateForTesting exceptionState;
205 range->setStart(&const_cast<Node&>(node), marker.startOffset(),
206 exceptionState);
207 if (!exceptionState.hadException()) {
208 range->setEnd(&const_cast<Node&>(node), marker.endOffset(),
209 IGNORE_EXCEPTION_FOR_TESTING);
210 }
211 if (!exceptionState.hadException()) {
212 // TODO(yosin): Once we have a |EphemeralRange| version of |boundingBox()|,
213 // we should use it instead of |Range| version.
214 marker.setRenderedRect(LayoutRect(range->boundingBox()));
215 } else {
216 marker.nullifyRenderedRect();
217 }
218 range->dispose();
219 }
220
221 // Markers are stored in order sorted by their start offset.
222 // Markers of the same type do not overlap each other.
223
224 void DocumentMarkerController::addMarker(Node* node, 145 void DocumentMarkerController::addMarker(Node* node,
225 const DocumentMarker& newMarker) { 146 DocumentMarker* newMarker) {
226 DCHECK_GE(newMarker.endOffset(), newMarker.startOffset()); 147 DCHECK_GE(newMarker->endOffset(), newMarker->startOffset());
227 if (newMarker.endOffset() == newMarker.startOffset()) 148 if (newMarker->endOffset() == newMarker->startOffset())
228 return; 149 return;
229 150
230 m_possiblyExistingMarkerTypes.add(newMarker.type()); 151 MarkerMap& markerMap = markerMapForType(newMarker->type());
231 152 auto it = markerMap.find(node);
232 Member<MarkerLists>& markers = 153 if (it == markerMap.end()) {
233 m_markers.insert(node, nullptr).storedValue->value; 154 markerMap.insert(node, createMarkerListOfType(newMarker->type()));
234 if (!markers) { 155 it = markerMap.find(node);
235 markers = new MarkerLists;
236 markers->grow(DocumentMarker::MarkerTypeIndexesCount);
237 } 156 }
238 157
239 DocumentMarker::MarkerTypeIndex markerListIndex = 158 it->value.get()->add(newMarker);
240 MarkerTypeToMarkerIndex(newMarker.type());
241 if (!markers->at(markerListIndex)) {
242 markers->at(markerListIndex) = new MarkerList;
243 }
244
245 Member<MarkerList>& list = markers->at(markerListIndex);
246 RenderedDocumentMarker* newRenderedMarker =
247 RenderedDocumentMarker::create(newMarker);
248 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) {
249 list->push_back(newRenderedMarker);
250 } else {
251 if (newMarker.type() != DocumentMarker::TextMatch &&
252 newMarker.type() != DocumentMarker::Composition) {
253 mergeOverlapping(list.get(), newRenderedMarker);
254 } else {
255 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(),
256 &newMarker, startsFurther);
257 list->insert(pos - list->begin(), newRenderedMarker);
258 }
259 }
260 159
261 // repaint the affected node 160 // repaint the affected node
262 if (node->layoutObject()) { 161 if (node->layoutObject()) {
263 node->layoutObject()->setShouldDoFullPaintInvalidation( 162 node->layoutObject()->setShouldDoFullPaintInvalidation(
264 PaintInvalidationDocumentMarkerChange); 163 PaintInvalidationDocumentMarkerChange);
265 } 164 }
266 } 165 }
267 166
268 void DocumentMarkerController::mergeOverlapping(
269 MarkerList* list,
270 RenderedDocumentMarker* toInsert) {
271 MarkerList::iterator firstOverlapping =
272 std::lower_bound(list->begin(), list->end(), toInsert, doesNotOverlap);
273 size_t index = firstOverlapping - list->begin();
274 list->insert(index, toInsert);
275 MarkerList::iterator inserted = list->begin() + index;
276 firstOverlapping = inserted + 1;
277 for (MarkerList::iterator i = firstOverlapping;
278 i != list->end() && (*i)->startOffset() <= (*inserted)->endOffset();) {
279 (*inserted)->setStartOffset(
280 std::min((*inserted)->startOffset(), (*i)->startOffset()));
281 (*inserted)->setEndOffset(
282 std::max((*inserted)->endOffset(), (*i)->endOffset()));
283 list->erase(i - list->begin());
284 }
285 }
286
287 // copies markers from srcNode to dstNode, applying the specified shift delta to 167 // copies markers from srcNode to dstNode, applying the specified shift delta to
288 // the copies. The shift is useful if, e.g., the caller has created the dstNode 168 // the copies. The shift is useful if, e.g., the caller has created the dstNode
289 // from a non-prefix substring of the srcNode. 169 // from a non-prefix substring of the srcNode.
290 void DocumentMarkerController::copyMarkers(Node* srcNode, 170 void DocumentMarkerController::copyMarkers(Node* srcNode,
291 unsigned startOffset, 171 unsigned startOffset,
292 int length, 172 int length,
293 Node* dstNode, 173 Node* dstNode,
294 int delta) { 174 int delta) {
295 if (length <= 0) 175 if (length <= 0)
296 return; 176 return;
297 177
298 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 178 bool docDirty = false;
299 return;
300 DCHECK(!m_markers.isEmpty());
301 179
302 MarkerLists* markers = m_markers.at(srcNode); 180 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
303 if (!markers) 181 MarkerMap& markerMap = markerMapForType(type);
304 return; 182 auto dst_it = markerMap.find(dstNode);
183 if (dst_it == markerMap.end()) {
184 markerMap.insert(dstNode, createMarkerListOfType(type));
185 dst_it = markerMap.find(dstNode);
186 }
305 187
306 bool docDirty = false; 188 // note: this must be looked up after dst_it because inserting a new
307 for (Member<MarkerList> list : *markers) { 189 // destination list can cause the HashMap to re-hash, invalidating iterators
308 if (!list) 190 auto src_it = markerMap.find(srcNode);
191 if (src_it == markerMap.end())
309 continue; 192 continue;
310 193
311 unsigned endOffset = startOffset + length - 1; 194 docDirty = (src_it->value->copyMarkers(startOffset, length, dst_it->value,
312 MarkerList::iterator startPos = std::lower_bound( 195 delta) ==
313 list->begin(), list->end(), startOffset, doesNotInclude); 196 DocumentMarkerList::DidCopyMarkerOrNot::DidCopyMarker) ||
314 for (MarkerList::iterator i = startPos; i != list->end(); ++i) { 197 docDirty;
315 DocumentMarker* marker = i->get();
316
317 // stop if we are now past the specified range
318 if (marker->startOffset() > endOffset)
319 break;
320
321 // pin the marker to the specified range and apply the shift delta
322 docDirty = true;
323 if (marker->startOffset() < startOffset)
324 marker->setStartOffset(startOffset);
325 if (marker->endOffset() > endOffset)
326 marker->setEndOffset(endOffset);
327 marker->shiftOffsets(delta);
328
329 addMarker(dstNode, *marker);
330 }
331 } 198 }
332 199
333 // repaint the affected node 200 // repaint the affected node
334 if (docDirty && dstNode->layoutObject()) { 201 if (docDirty && dstNode->layoutObject()) {
335 dstNode->layoutObject()->setShouldDoFullPaintInvalidation( 202 dstNode->layoutObject()->setShouldDoFullPaintInvalidation(
336 PaintInvalidationDocumentMarkerChange); 203 PaintInvalidationDocumentMarkerChange);
337 } 204 }
338 } 205 }
339 206
340 void DocumentMarkerController::removeMarkers( 207 void DocumentMarkerController::removeMarkers(
341 Node* node, 208 Node* node,
342 unsigned startOffset, 209 unsigned startOffset,
343 int length, 210 int length,
344 DocumentMarker::MarkerTypes markerTypes, 211 DocumentMarker::MarkerTypes markerTypes,
345 RemovePartiallyOverlappingMarkerOrNot 212 RemovePartiallyOverlappingMarkerOrNot
346 shouldRemovePartiallyOverlappingMarker) { 213 shouldRemovePartiallyOverlappingMarker) {
347 if (length <= 0) 214 if (length <= 0)
348 return; 215 return;
349 216
350 if (!possiblyHasMarkers(markerTypes))
351 return;
352 DCHECK(!(m_markers.isEmpty()));
353
354 MarkerLists* markers = m_markers.at(node);
355 if (!markers)
356 return;
357
358 bool docDirty = false; 217 bool docDirty = false;
359 size_t emptyListsCount = 0; 218 for (DocumentMarker::MarkerType type : markerTypes) {
360 for (size_t markerListIndex = 0; 219 MarkerMap& markerMap = markerMapForType(type);
361 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 220 auto it = markerMap.find(node);
362 ++markerListIndex) { 221 if (it != markerMap.end()) {
363 Member<MarkerList>& list = (*markers)[markerListIndex]; 222 docDirty =
364 if (!list || list->isEmpty()) { 223 (it->value->removeMarkers(startOffset, length,
365 if (list.get() && list->isEmpty()) 224 shouldRemovePartiallyOverlappingMarker) ==
366 list.clear(); 225 DocumentMarkerList::DidRemoveMarkerOrNot::DidRemoveMarker) ||
367 ++emptyListsCount; 226 docDirty;
368 continue; 227 if (it->value->empty())
228 markerMap.erase(node);
369 } 229 }
370 if (!markerTypes.contains((*list->begin())->type()))
371 continue;
372 unsigned endOffset = startOffset + length;
373 MarkerList::iterator startPos =
374 std::upper_bound(list->begin(), list->end(), startOffset, endsBefore);
375 for (MarkerList::iterator i = startPos; i != list->end();) {
376 const DocumentMarker& marker(*i->get());
377
378 // markers are returned in order, so stop if we are now past the specified
379 // range
380 if (marker.startOffset() >= endOffset)
381 break;
382
383 // at this point we know that marker and target intersect in some way
384 docDirty = true;
385
386 // pitch the old marker
387 list->erase(i - list->begin());
388
389 if (shouldRemovePartiallyOverlappingMarker) {
390 // Stop here. Don't add resulting slices back.
391 continue;
392 }
393
394 // add either of the resulting slices that are left after removing target
395 if (startOffset > marker.startOffset()) {
396 DocumentMarker* newLeft = marker.clone();
397 newLeft->setEndOffset(startOffset);
398 size_t insertIndex = i - list->begin();
399 list->insert(insertIndex, RenderedDocumentMarker::create(*newLeft));
400 // Move to the marker after the inserted one.
401 i = list->begin() + insertIndex + 1;
402 }
403 if (marker.endOffset() > endOffset) {
404 DocumentMarker* newRight = marker.clone();
405 newRight->setStartOffset(endOffset);
406 size_t insertIndex = i - list->begin();
407 list->insert(insertIndex, RenderedDocumentMarker::create(*newRight));
408 // Move to the marker after the inserted one.
409 i = list->begin() + insertIndex + 1;
410 }
411 }
412
413 if (list->isEmpty()) {
414 list.clear();
415 ++emptyListsCount;
416 }
417 }
418
419 if (emptyListsCount == DocumentMarker::MarkerTypeIndexesCount) {
420 m_markers.erase(node);
421 if (m_markers.isEmpty())
422 m_possiblyExistingMarkerTypes = 0;
423 } 230 }
424 231
425 // repaint the affected node 232 // repaint the affected node
426 if (docDirty && node->layoutObject()) { 233 if (docDirty && node->layoutObject()) {
427 node->layoutObject()->setShouldDoFullPaintInvalidation( 234 node->layoutObject()->setShouldDoFullPaintInvalidation(
428 PaintInvalidationDocumentMarkerChange); 235 PaintInvalidationDocumentMarkerChange);
429 } 236 }
430 } 237 }
431 238
432 DocumentMarkerVector DocumentMarkerController::markersFor( 239 DocumentMarkerVector DocumentMarkerController::markersFor(
433 Node* node, 240 Node* node,
434 DocumentMarker::MarkerTypes markerTypes) { 241 DocumentMarker::MarkerTypes markerTypes) {
435 DocumentMarkerVector result; 242 DocumentMarkerVector result;
436 243
437 MarkerLists* markers = m_markers.at(node); 244 for (Member<DocumentMarkerList> list :
438 if (!markers) 245 getMarkerListsForNode(node, markerTypes)) {
439 return result; 246 list->appendMarkersToInputList(&result);
440
441 for (size_t markerListIndex = 0;
442 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
443 ++markerListIndex) {
444 Member<MarkerList>& list = (*markers)[markerListIndex];
445 if (!list || list->isEmpty() ||
446 !markerTypes.contains((*list->begin())->type()))
447 continue;
448
449 for (size_t i = 0; i < list->size(); ++i)
450 result.push_back(list->at(i).get());
451 } 247 }
452 248
453 std::sort(result.begin(), result.end(), compareByStart); 249 std::sort(result.begin(), result.end(), compareByStart);
454 return result; 250 return result;
455 } 251 }
456 252
457 DocumentMarkerVector DocumentMarkerController::markers() { 253 DocumentMarkerVector DocumentMarkerController::markers() {
458 DocumentMarkerVector result; 254 DocumentMarkerVector result;
459 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) { 255 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
460 MarkerLists* markers = i->value.get(); 256 MarkerMap& markerMap = markerMapForType(type);
461 for (size_t markerListIndex = 0; 257 for (auto it = markerMap.begin(); it != markerMap.end(); ++it)
462 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 258 it->value->appendMarkersToInputList(&result);
463 ++markerListIndex) {
464 Member<MarkerList>& list = (*markers)[markerListIndex];
465 for (size_t j = 0; list.get() && j < list->size(); ++j)
466 result.push_back(list->at(j).get());
467 }
468 } 259 }
260
469 std::sort(result.begin(), result.end(), compareByStart); 261 std::sort(result.begin(), result.end(), compareByStart);
470 return result; 262 return result;
471 } 263 }
472 264
473 DocumentMarkerVector DocumentMarkerController::markersInRange( 265 DocumentMarkerVector DocumentMarkerController::markersInRange(
474 const EphemeralRange& range, 266 const EphemeralRange& range,
475 DocumentMarker::MarkerTypes markerTypes) { 267 DocumentMarker::MarkerTypes markerTypes) {
476 if (!possiblyHasMarkers(markerTypes))
477 return DocumentMarkerVector();
478 268
479 DocumentMarkerVector foundMarkers; 269 DocumentMarkerVector foundMarkers;
480 270
481 Node* startContainer = range.startPosition().computeContainerNode(); 271 Node* startContainer = range.startPosition().computeContainerNode();
482 DCHECK(startContainer); 272 DCHECK(startContainer);
483 unsigned startOffset = static_cast<unsigned>( 273 unsigned startOffset = static_cast<unsigned>(
484 range.startPosition().computeOffsetInContainerNode()); 274 range.startPosition().computeOffsetInContainerNode());
485 Node* endContainer = range.endPosition().computeContainerNode(); 275 Node* endContainer = range.endPosition().computeContainerNode();
486 DCHECK(endContainer); 276 DCHECK(endContainer);
487 unsigned endOffset = 277 unsigned endOffset =
488 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); 278 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode());
489 279
490 for (Node& node : range.nodes()) { 280 for (Node& node : range.nodes()) {
491 for (DocumentMarker* marker : markersFor(&node)) { 281 for (DocumentMarker* marker : markersFor(&node)) {
492 if (!markerTypes.contains(marker->type())) 282 if (!markerTypes.contains(marker->type()))
493 continue; 283 continue;
494 if (node == startContainer && marker->endOffset() <= startOffset) 284 if (node == startContainer && marker->endOffset() <= startOffset)
495 continue; 285 continue;
496 if (node == endContainer && marker->startOffset() >= endOffset) 286 if (node == endContainer && marker->startOffset() >= endOffset)
497 continue; 287 continue;
498 foundMarkers.push_back(marker); 288 foundMarkers.push_back(marker);
499 } 289 }
500 } 290 }
501 return foundMarkers; 291 return foundMarkers;
502 } 292 }
503 293
294 // This method is only ever called with the type DocumentMarker::TextMatch
295 // TODO(rlanday): remove the param
504 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( 296 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(
505 DocumentMarker::MarkerType markerType) { 297 DocumentMarker::MarkerType) {
506 Vector<IntRect> result; 298 Vector<IntRect> result;
507 299 for (auto nodeIterator = m_textMatches.begin();
508 if (!possiblyHasMarkers(markerType)) 300 nodeIterator != m_textMatches.end(); ++nodeIterator) {
509 return result;
510 DCHECK(!(m_markers.isEmpty()));
511
512 // outer loop: process each node
513 MarkerMap::iterator end = m_markers.end();
514 for (MarkerMap::iterator nodeIterator = m_markers.begin();
515 nodeIterator != end; ++nodeIterator) {
516 // inner loop; process each marker in this node
517 const Node& node = *nodeIterator->key; 301 const Node& node = *nodeIterator->key;
518 MarkerLists* markers = nodeIterator->value.get(); 302 TextMatchMarkerList* list =
519 for (size_t markerListIndex = 0; 303 toTextMatchMarkerList(nodeIterator->value.get());
520 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 304 #if DCHECK_IS_ON()
521 ++markerListIndex) { 305 if (!list->empty()) {
522 Member<MarkerList>& list = (*markers)[markerListIndex]; 306 DCHECK(!m_document->view() || !m_document->view()->needsLayout());
523 if (!list || list->isEmpty() || (*list->begin())->type() != markerType) 307 DCHECK(!m_document->needsLayoutTreeUpdate());
524 continue;
525 for (unsigned markerIndex = 0; markerIndex < list->size();
526 ++markerIndex) {
527 RenderedDocumentMarker* marker = list->at(markerIndex).get();
528 updateMarkerRenderedRectIfNeeded(node, *marker);
529 if (!marker->isRendered())
530 continue;
531 result.push_back(marker->renderedRect());
532 }
533 } 308 }
309 #endif
310 list->appendRenderedRectsToInputList(node, &result);
534 } 311 }
535 312
536 return result; 313 return result;
537 } 314 }
538 315
539 static void invalidatePaintForTickmarks(const Node& node) { 316 static void invalidatePaintForTickmarks(const Node& node) {
540 if (FrameView* frameView = node.document().view()) 317 if (FrameView* frameView = node.document().view())
541 frameView->invalidatePaintForTickmarks(); 318 frameView->invalidatePaintForTickmarks();
542 } 319 }
543 320
544 void DocumentMarkerController::updateMarkerRenderedRectIfNeeded( 321 void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node) {
545 const Node& node, 322 if (!m_textMatches.contains(&node))
546 RenderedDocumentMarker& marker) { 323 return;
547 DCHECK(!m_document->view() || !m_document->view()->needsLayout());
548 DCHECK(!m_document->needsLayoutTreeUpdate());
549 if (!marker.isValid())
550 updateMarkerRenderedRect(node, marker);
551 }
552 324
553 void DocumentMarkerController::invalidateRectsForMarkersInNode( 325 toTextMatchMarkerList(m_textMatches.find(&node)->value.get())
554 const Node& node) { 326 ->invalidateRects();
555 MarkerLists* markers = m_markers.at(&node); 327 invalidatePaintForTickmarks(node);
556
557 for (auto& markerList : *markers) {
558 if (!markerList || markerList->isEmpty())
559 continue;
560
561 for (auto& marker : *markerList)
562 marker->invalidate();
563
564 if (markerList->front()->type() == DocumentMarker::TextMatch)
565 invalidatePaintForTickmarks(node);
566 }
567 } 328 }
568 329
569 void DocumentMarkerController::invalidateRectsForAllMarkers() { 330 void DocumentMarkerController::invalidateRectsForAllMarkers() {
570 for (auto& nodeMarkers : m_markers) { 331 for (auto& nodeMarkers : m_textMatches)
571 const Node& node = *nodeMarkers.key; 332 invalidateRectsForMarkersInNode(*nodeMarkers.key);
572 for (auto& markerList : *nodeMarkers.value) {
573 if (!markerList || markerList->isEmpty())
574 continue;
575
576 for (auto& marker : *markerList)
577 marker->invalidate();
578
579 if (markerList->front()->type() == DocumentMarker::TextMatch)
580 invalidatePaintForTickmarks(node);
581 }
582 }
583 } 333 }
584 334
585 DEFINE_TRACE(DocumentMarkerController) { 335 DEFINE_TRACE(DocumentMarkerController) {
586 visitor->trace(m_markers); 336 visitor->trace(m_spelling);
337 visitor->trace(m_grammar);
338 visitor->trace(m_textMatches);
339 visitor->trace(m_compositions);
587 visitor->trace(m_document); 340 visitor->trace(m_document);
588 SynchronousMutationObserver::trace(visitor); 341 SynchronousMutationObserver::trace(visitor);
589 } 342 }
590 343
591 void DocumentMarkerController::removeMarkers( 344 void DocumentMarkerController::removeMarkers(
592 Node* node, 345 Node* node,
593 DocumentMarker::MarkerTypes markerTypes) { 346 DocumentMarker::MarkerTypes markerTypes) {
594 if (!possiblyHasMarkers(markerTypes)) 347 for (DocumentMarker::MarkerType type : markerTypes) {
595 return; 348 MarkerMap& markerMap = markerMapForType(type);
596 DCHECK(!m_markers.isEmpty()); 349 markerMap.erase(node);
597
598 MarkerMap::iterator iterator = m_markers.find(node);
599 if (iterator != m_markers.end())
600 removeMarkersFromList(iterator, markerTypes);
601 }
602
603 void DocumentMarkerController::removeMarkers(
604 const MarkerRemoverPredicate& shouldRemoveMarker) {
605 for (auto& nodeMarkers : m_markers) {
606 const Node& node = *nodeMarkers.key;
607 if (!node.isTextNode()) // MarkerRemoverPredicate requires a Text node.
608 continue;
609 MarkerLists& markers = *nodeMarkers.value;
610 for (size_t markerListIndex = 0;
611 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
612 ++markerListIndex) {
613 Member<MarkerList>& list = markers[markerListIndex];
614 if (!list)
615 continue;
616 bool removedMarkers = false;
617 for (size_t j = list->size(); j > 0; --j) {
618 if (shouldRemoveMarker(*list->at(j - 1),
619 static_cast<const Text&>(node))) {
620 list->erase(j - 1);
621 removedMarkers = true;
622 }
623 }
624 if (removedMarkers &&
625 markerListIndex == DocumentMarker::TextMatchMarkerIndex)
626 invalidatePaintForTickmarks(node);
627 }
628 } 350 }
629 } 351 }
630 352
631 void DocumentMarkerController::removeMarkers( 353 void DocumentMarkerController::removeMarkers(
632 DocumentMarker::MarkerTypes markerTypes) { 354 DocumentMarker::MarkerTypes markerTypes) {
633 if (!possiblyHasMarkers(markerTypes)) 355 for (DocumentMarker::MarkerType type : markerTypes) {
634 return; 356 MarkerMap& markerMap = markerMapForType(type);
635 DCHECK(!m_markers.isEmpty()); 357 markerMap.clear();
636
637 HeapVector<Member<const Node>> nodesWithMarkers;
638 copyKeysToVector(m_markers, nodesWithMarkers);
639 unsigned size = nodesWithMarkers.size();
640 for (unsigned i = 0; i < size; ++i) {
641 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]);
642 if (iterator != m_markers.end())
643 removeMarkersFromList(iterator, markerTypes);
644 } 358 }
645
646 m_possiblyExistingMarkerTypes.remove(markerTypes);
647 } 359 }
648 360
649 void DocumentMarkerController::removeMarkersFromList( 361 void DocumentMarkerController::removeMarkers(
650 MarkerMap::iterator iterator, 362 const MarkerRemoverPredicate& shouldRemoveMarker) {
651 DocumentMarker::MarkerTypes markerTypes) { 363 for (auto& nodeMarkers : m_spelling) {
652 bool needsRepainting = false; 364 const Node& node = *nodeMarkers.key;
653 bool nodeCanBeRemoved; 365 if (!node.isTextNode())
654 366 continue;
655 size_t emptyListsCount = 0; 367 toSpellCheckMarkerList(nodeMarkers.value.get())
656 if (markerTypes == DocumentMarker::AllMarkers()) { 368 ->removeMarkersForWords(static_cast<const Text&>(node).data(),
657 needsRepainting = true; 369 shouldRemoveMarker.m_words);
658 nodeCanBeRemoved = true;
659 } else {
660 MarkerLists* markers = iterator->value.get();
661
662 for (size_t markerListIndex = 0;
663 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
664 ++markerListIndex) {
665 Member<MarkerList>& list = (*markers)[markerListIndex];
666 if (!list || list->isEmpty()) {
667 if (list.get() && list->isEmpty())
668 list.clear();
669 ++emptyListsCount;
670 continue;
671 }
672 if (markerTypes.contains((*list->begin())->type())) {
673 list->clear();
674 list.clear();
675 ++emptyListsCount;
676 needsRepainting = true;
677 }
678 }
679
680 nodeCanBeRemoved =
681 emptyListsCount == DocumentMarker::MarkerTypeIndexesCount;
682 }
683
684 if (needsRepainting) {
685 const Node& node = *iterator->key;
686 if (LayoutObject* layoutObject = node.layoutObject()) {
687 layoutObject->setShouldDoFullPaintInvalidation(
688 PaintInvalidationDocumentMarkerChange);
689 }
690 invalidatePaintForTickmarks(node);
691 }
692
693 if (nodeCanBeRemoved) {
694 m_markers.erase(iterator);
695 if (m_markers.isEmpty())
696 m_possiblyExistingMarkerTypes = 0;
697 } 370 }
698 } 371 }
699 372
700 void DocumentMarkerController::repaintMarkers( 373 void DocumentMarkerController::repaintMarkers(
701 DocumentMarker::MarkerTypes markerTypes) { 374 DocumentMarker::MarkerTypes markerTypes) {
702 if (!possiblyHasMarkers(markerTypes)) 375 HeapHashSet<Member<Node>> nodesToRepaint;
703 return; 376 for (DocumentMarker::MarkerType type : markerTypes) {
704 DCHECK(!m_markers.isEmpty()); 377 MarkerMap& markerMap = markerMapForType(type);
378 for (auto& nodeMarkers : markerMap) {
379 if (!nodeMarkers.value->empty())
380 nodesToRepaint.insert(nodeMarkers.key.get());
381 }
382 }
705 383
706 // outer loop: process each markered node in the document 384 for (Member<Node> node : nodesToRepaint) {
707 MarkerMap::iterator end = m_markers.end(); 385 // cause the node to be redrawn
708 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { 386 if (LayoutObject* layoutObject = node->layoutObject()) {
709 const Node* node = i->key; 387 layoutObject->setShouldDoFullPaintInvalidation(
710 388 PaintInvalidationDocumentMarkerChange);
711 // inner loop: process each marker in the current node 389 break;
712 MarkerLists* markers = i->value.get();
713 for (size_t markerListIndex = 0;
714 markerListIndex < DocumentMarker::MarkerTypeIndexesCount;
715 ++markerListIndex) {
716 Member<MarkerList>& list = (*markers)[markerListIndex];
717 if (!list || list->isEmpty() ||
718 !markerTypes.contains((*list->begin())->type()))
719 continue;
720
721 // cause the node to be redrawn
722 if (LayoutObject* layoutObject = node->layoutObject()) {
723 layoutObject->setShouldDoFullPaintInvalidation(
724 PaintInvalidationDocumentMarkerChange);
725 break;
726 }
727 } 390 }
728 } 391 }
729 } 392 }
730 393
731 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, 394 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range,
732 bool active) { 395 bool active) {
733 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
734 return false;
735
736 DCHECK(!m_markers.isEmpty());
737
738 Node* const startContainer = range.startPosition().computeContainerNode(); 396 Node* const startContainer = range.startPosition().computeContainerNode();
739 DCHECK(startContainer); 397 DCHECK(startContainer);
740 Node* const endContainer = range.endPosition().computeContainerNode(); 398 Node* const endContainer = range.endPosition().computeContainerNode();
741 DCHECK(endContainer); 399 DCHECK(endContainer);
742 400
743 const unsigned containerStartOffset = 401 const unsigned containerStartOffset =
744 range.startPosition().computeOffsetInContainerNode(); 402 range.startPosition().computeOffsetInContainerNode();
745 const unsigned containerEndOffset = 403 const unsigned containerEndOffset =
746 range.endPosition().computeOffsetInContainerNode(); 404 range.endPosition().computeOffsetInContainerNode();
747 405
748 bool markerFound = false; 406 bool markerFound = false;
749 for (Node& node : range.nodes()) { 407 for (Node& node : range.nodes()) {
750 int startOffset = node == startContainer ? containerStartOffset : 0; 408 int startOffset = node == startContainer ? containerStartOffset : 0;
751 int endOffset = node == endContainer ? containerEndOffset : INT_MAX; 409 int endOffset = node == endContainer ? containerEndOffset : INT_MAX;
752 markerFound |= setMarkersActive(&node, startOffset, endOffset, active); 410 markerFound |= setMarkersActive(&node, startOffset, endOffset, active);
753 } 411 }
754 return markerFound; 412 return markerFound;
755 } 413 }
756 414
415 bool DocumentMarkerController::hasMarkers(Node* node) const {
416 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
417 const MarkerMap& markerMap = markerMapForType(type);
418 if (markerMap.contains(node))
419 return true;
420 }
421
422 return false;
423 }
424
757 bool DocumentMarkerController::setMarkersActive(Node* node, 425 bool DocumentMarkerController::setMarkersActive(Node* node,
758 unsigned startOffset, 426 unsigned startOffset,
759 unsigned endOffset, 427 unsigned endOffset,
760 bool active) { 428 bool active) {
761 MarkerLists* markers = m_markers.at(node);
762 if (!markers)
763 return false;
764
765 bool docDirty = false; 429 bool docDirty = false;
766 Member<MarkerList>& list = 430 if (m_textMatches.contains(node)) {
767 (*markers)[MarkerTypeToMarkerIndex(DocumentMarker::TextMatch)]; 431 docDirty = toTextMatchMarkerList(m_textMatches.at(node))
768 if (!list) 432 ->setTextMatchMarkersActive(startOffset, endOffset, active);
769 return false;
770 MarkerList::iterator startPos =
771 std::upper_bound(list->begin(), list->end(), startOffset, endsBefore);
772 for (MarkerList::iterator marker = startPos; marker != list->end();
773 ++marker) {
774 // Markers are returned in order, so stop if we are now past the specified
775 // range.
776 if ((*marker)->startOffset() >= endOffset)
777 break;
778
779 (*marker)->setActiveMatch(active);
780 docDirty = true;
781 } 433 }
782 434
783 // repaint the affected node 435 if (docDirty && node->layoutObject())
784 if (docDirty && node->layoutObject()) {
785 node->layoutObject()->setShouldDoFullPaintInvalidation( 436 node->layoutObject()->setShouldDoFullPaintInvalidation(
786 PaintInvalidationDocumentMarkerChange); 437 PaintInvalidationDocumentMarkerChange);
787 } 438
788 return docDirty; 439 return docDirty;
789 } 440 }
790 441
791 #ifndef NDEBUG 442 #ifndef NDEBUG
443 static String showMarkerHelper(const DocumentMarkerVector& markers) {
444 StringBuilder builder;
445
446 for (const Member<DocumentMarker> marker : markers) {
447 builder.append(" ");
448 builder.appendNumber(marker->type());
449 builder.append(":[");
450 builder.appendNumber(marker->startOffset());
451 builder.append(":");
452 builder.appendNumber(marker->endOffset());
453 builder.append("](");
454 builder.appendNumber(marker->activeMatch());
455 builder.append(")");
456 }
457
458 return builder.toString();
459 }
460
792 void DocumentMarkerController::showMarkers() const { 461 void DocumentMarkerController::showMarkers() const {
793 StringBuilder builder; 462 StringBuilder builder;
794 MarkerMap::const_iterator end = m_markers.end(); 463
795 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); 464 HeapHashSet<Member<Node>> nodes;
796 nodeIterator != end; ++nodeIterator) { 465 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
797 const Node* node = nodeIterator->key; 466 const MarkerMap& markerMap = markerMapForType(type);
467 for (const auto& nodeMarkers : markerMap)
468 nodes.insert(nodeMarkers.key);
469 }
470
471 HeapHashSet<Member<const Node>> nodesWithMarkers;
472 for (Node* node : nodes) {
798 builder.append(String::format("%p", node)); 473 builder.append(String::format("%p", node));
799 MarkerLists* markers = m_markers.at(node); 474
800 for (size_t markerListIndex = 0; 475 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
801 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 476 const MarkerMap& markerMap = markerMapForType(type);
802 ++markerListIndex) { 477 if (markerMap.contains(node)) {
803 Member<MarkerList>& list = (*markers)[markerListIndex]; 478 DocumentMarkerVector markers;
804 for (unsigned markerIndex = 0; list.get() && markerIndex < list->size(); 479 markerMap.at(node)->appendMarkersToInputList(&markers);
805 ++markerIndex) { 480 if (!markers.isEmpty()) {
806 DocumentMarker* marker = list->at(markerIndex).get(); 481 builder.append(showMarkerHelper(markers));
807 builder.append(" "); 482 nodesWithMarkers.insert(node);
808 builder.appendNumber(marker->type()); 483 }
809 builder.append(":[");
810 builder.appendNumber(marker->startOffset());
811 builder.append(":");
812 builder.appendNumber(marker->endOffset());
813 builder.append("](");
814 builder.appendNumber(marker->activeMatch());
815 builder.append(")");
816 } 484 }
817 } 485 }
486
818 builder.append("\n"); 487 builder.append("\n");
819 } 488 }
820 LOG(INFO) << m_markers.size() << " nodes have markers:\n" 489 LOG(INFO) << nodesWithMarkers.size() << " nodes have markers:\n"
821 << builder.toString().utf8().data(); 490 << builder.toString().utf8().data();
822 } 491 }
823 #endif 492 #endif
824 493
494 DocumentMarkerList* DocumentMarkerController::createMarkerListOfType(
495 DocumentMarker::MarkerType type) {
496 switch (type) {
497 case DocumentMarker::Spelling:
498 return new SpellCheckMarkerList(DocumentMarker::Spelling);
499 case DocumentMarker::Grammar:
500 return new SpellCheckMarkerList(DocumentMarker::Grammar);
501 case DocumentMarker::TextMatch:
502 return new TextMatchMarkerList();
503 case DocumentMarker::Composition:
504 return new CompositionMarkerList();
505 default:
506 // MSVC thinks this method doesn't handle all enum values even though it
507 // does. So we have to return something for the default case to avoid a
508 // C4715 warning.
509 UNREACHABLE();
510 return nullptr;
511 }
512 }
513
514 HeapVector<Member<DocumentMarkerList>>
515 DocumentMarkerController::getMarkerListsForNode(
516 Node* node,
517 DocumentMarker::MarkerTypes markerTypes) {
518 HeapVector<Member<DocumentMarkerList>> markerLists;
519
520 for (DocumentMarker::MarkerType type : markerTypes) {
521 MarkerMap& markerMap = markerMapForType(type);
522 auto it = markerMap.find(node);
523 if (it != markerMap.end())
524 markerLists.push_back(it->value);
525 }
526
527 return markerLists;
528 }
529
530 DocumentMarkerController::MarkerMap& DocumentMarkerController::markerMapForType(
531 DocumentMarker::MarkerType type) {
532 return const_cast<MarkerMap&>(
533 const_cast<const DocumentMarkerController&>(*this).markerMapForType(
534 type));
535 }
536
537 const DocumentMarkerController::MarkerMap&
538 DocumentMarkerController::markerMapForType(
539 DocumentMarker::MarkerType type) const {
540 switch (type) {
541 case DocumentMarker::Spelling:
542 return m_spelling;
543 case DocumentMarker::Grammar:
544 return m_grammar;
545 case DocumentMarker::TextMatch:
546 return m_textMatches;
547 case DocumentMarker::Composition:
548 return m_compositions;
549 default:
550 // MSVC thinks this method doesn't handle all enum values even though it
551 // does. So we have to return something for the default case to avoid a
552 // C4715 warning.
553 UNREACHABLE();
554 return m_spelling;
555 }
556 }
557
558 void DocumentMarkerController::removeMarkers(
559 TextIterator& markedText,
560 DocumentMarker::MarkerTypes markerTypes,
561 RemovePartiallyOverlappingMarkerOrNot
562 shouldRemovePartiallyOverlappingMarker) {
563 for (; !markedText.atEnd(); markedText.advance()) {
564 int startOffset = markedText.startOffsetInCurrentContainer();
565 int endOffset = markedText.endOffsetInCurrentContainer();
566 removeMarkers(markedText.currentContainer(), startOffset,
567 endOffset - startOffset, markerTypes,
568 shouldRemovePartiallyOverlappingMarker);
569 }
570 }
571
825 // SynchronousMutationObserver 572 // SynchronousMutationObserver
826 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, 573 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node,
827 unsigned offset, 574 unsigned offset,
828 unsigned oldLength, 575 unsigned oldLength,
829 unsigned newLength) { 576 unsigned newLength) {
830 // If we're doing a pure remove operation, remove the markers in the range 577 bool didShiftMarker = false;
831 // being removed (markers containing, but larger than, the range, will be 578 for (DocumentMarker::MarkerType type : DocumentMarker::AllMarkers()) {
832 // split) 579 MarkerMap& markerMap = markerMapForType(type);
833 if (newLength == 0)
834 removeMarkers(node, offset, oldLength);
835 580
836 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 581 auto it = markerMap.find(node);
837 return; 582 if (it != markerMap.end()) {
838 DCHECK(!m_markers.isEmpty()); 583 didShiftMarker =
839 584 (it->value->shiftMarkers(offset, oldLength, newLength) ==
840 MarkerLists* markers = m_markers.at(node); 585 DocumentMarkerList::DidShiftMarkerOrNot::DidShiftMarker) ||
841 if (!markers) 586 didShiftMarker;
842 return;
843
844 bool didShiftMarker = false;
845 for (MarkerList* list : *markers) {
846 if (!list)
847 continue;
848
849 for (MarkerList::iterator it = list->begin(); it != list->end(); ++it) {
850 RenderedDocumentMarker& marker = **it;
851 Optional<DocumentMarker::MarkerOffsets> result =
852 marker.computeOffsetsAfterShift(offset, oldLength, newLength);
853 if (result == WTF::nullopt) {
854 list->erase(it - list->begin());
855 --it;
856 didShiftMarker = true;
857 continue;
858 }
859
860 if (marker.startOffset() != result.value().startOffset ||
861 marker.endOffset() != result.value().endOffset) {
862 didShiftMarker = true;
863 marker.setStartOffset(result.value().startOffset);
864 marker.setEndOffset(result.value().endOffset);
865 }
866 } 587 }
867 } 588 }
868 589
869 if (!didShiftMarker) 590 if (didShiftMarker) {
870 return; 591 invalidateRectsForMarkersInNode(*node);
871 if (!node->layoutObject()) 592 // repaint the affected node
872 return; 593 if (node->layoutObject()) {
873 invalidateRectsForMarkersInNode(*node); 594 node->layoutObject()->setShouldDoFullPaintInvalidation(
874 // repaint the affected node 595 PaintInvalidationDocumentMarkerChange);
875 node->layoutObject()->setShouldDoFullPaintInvalidation(); 596 }
597 }
876 } 598 }
877 599
878 } // namespace blink 600 } // namespace blink
879 601
880 #ifndef NDEBUG 602 #ifndef NDEBUG
881 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { 603 void showDocumentMarkers(const blink::DocumentMarkerController* controller) {
882 if (controller) 604 if (controller)
883 controller->showMarkers(); 605 controller->showMarkers();
884 } 606 }
885 #endif 607 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698