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

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

Issue 2723663002: Refactor DocumentMarkerController (Closed)
Patch Set: Use correct base commit 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
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/GrammarMarkerList.h"
40 #include "core/editing/markers/RenderedTextMatchMarker.h"
41 #include "core/editing/markers/SpellingMarkerList.h"
42 #include "core/editing/markers/TextMatchMarkerList.h"
37 #include "core/frame/FrameView.h" 43 #include "core/frame/FrameView.h"
38 #include "core/layout/LayoutObject.h" 44 #include "core/layout/LayoutObject.h"
39 #include <algorithm>
40 45
41 #ifndef NDEBUG 46 #ifndef NDEBUG
42 #include <stdio.h> 47 #include <stdio.h>
43 #endif 48 #endif
44 49
45 namespace blink { 50 namespace blink {
46 51
47 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) 52 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words)
48 : m_words(words) {} 53 : m_words(words) {}
49 54
50 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, 55 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker,
51 const Text& textNode) const { 56 const Text& textNode) const {
52 unsigned start = documentMarker.startOffset(); 57 unsigned start = documentMarker.startOffset();
53 unsigned length = documentMarker.endOffset() - documentMarker.startOffset(); 58 unsigned length = documentMarker.endOffset() - documentMarker.startOffset();
54 59
55 String markerText = textNode.data().substring(start, length); 60 String markerText = textNode.data().substring(start, length);
56 return m_words.contains(markerText); 61 return m_words.contains(markerText);
57 } 62 }
58 63
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) 64 DocumentMarkerController::DocumentMarkerController(Document& document)
86 : m_possiblyExistingMarkerTypes(0), m_document(&document) { 65 : m_document(&document) {
87 setContext(&document); 66 setContext(&document);
88 } 67 }
89 68
90 void DocumentMarkerController::clear() { 69 void DocumentMarkerController::clear() {
91 m_markers.clear(); 70 m_spelling.clear();
92 m_possiblyExistingMarkerTypes = 0; 71 m_grammar.clear();
72 m_textMatches.clear();
73 m_compositions.clear();
93 } 74 }
94 75
95 void DocumentMarkerController::addMarker(const Position& start, 76 void DocumentMarkerController::addMarker(const Position& start,
96 const Position& end, 77 const Position& end,
97 DocumentMarker::MarkerType type, 78 DocumentMarker::MarkerType type,
98 const String& description) { 79 const String& description) {
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 DocumentMarker(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 switch (newMarker->type()) {
231 152 case DocumentMarker::Spelling:
232 Member<MarkerLists>& markers = 153 addIntoMarkerMap(&m_spelling, node, newMarker);
233 m_markers.insert(node, nullptr).storedValue->value; 154 break;
234 if (!markers) { 155 case DocumentMarker::Grammar:
235 markers = new MarkerLists; 156 addIntoMarkerMap(&m_grammar, node, newMarker);
236 markers->grow(DocumentMarker::MarkerTypeIndexesCount); 157 break;
237 } 158 case DocumentMarker::TextMatch: {
238 159 auto it = m_textMatches.find(node);
239 DocumentMarker::MarkerTypeIndex markerListIndex = 160 if (it == m_textMatches.end()) {
240 MarkerTypeToMarkerIndex(newMarker.type()); 161 m_textMatches.insert(node, new TextMatchMarkerList(this));
241 if (!markers->at(markerListIndex)) { 162 it = m_textMatches.find(node);
242 markers->at(markerListIndex) = new MarkerList; 163 }
243 } 164 it->value->push_back(newMarker);
244 165 break;
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 } 166 }
167 case DocumentMarker::Composition:
168 addIntoMarkerMap(&m_compositions, node, newMarker);
169 break;
259 } 170 }
260 171
261 // repaint the affected node 172 // repaint the affected node
262 if (node->layoutObject()) { 173 if (node->layoutObject()) {
263 node->layoutObject()->setShouldDoFullPaintInvalidation( 174 node->layoutObject()->setShouldDoFullPaintInvalidation(
264 PaintInvalidationDocumentMarkerChange); 175 PaintInvalidationDocumentMarkerChange);
265 } 176 }
266 } 177 }
267 178
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->remove(i - list->begin());
284 }
285 }
286
287 // copies markers from srcNode to dstNode, applying the specified shift delta to 179 // 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 180 // the copies. The shift is useful if, e.g., the caller has created the dstNode
289 // from a non-prefix substring of the srcNode. 181 // from a non-prefix substring of the srcNode.
290 void DocumentMarkerController::copyMarkers(Node* srcNode, 182 void DocumentMarkerController::copyMarkers(Node* srcNode,
291 unsigned startOffset, 183 unsigned startOffset,
292 int length, 184 int length,
293 Node* dstNode, 185 Node* dstNode,
294 int delta) { 186 int delta) {
295 if (length <= 0) 187 if (length <= 0)
296 return; 188 return;
297 189
298 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 190 bool docDirty = false;
299 return;
300 DCHECK(!m_markers.isEmpty());
301 191
302 MarkerLists* markers = m_markers.at(srcNode); 192 for (Member<DocumentMarkerList> list : getMarkerListsForNode(srcNode)) {
303 if (!markers) 193 docDirty =
304 return; 194 list->copyMarkers(startOffset, length, dstNode, delta) || docDirty;
305
306 bool docDirty = false;
307 for (Member<MarkerList> list : *markers) {
308 if (!list)
309 continue;
310
311 unsigned endOffset = startOffset + length - 1;
312 MarkerList::iterator startPos = std::lower_bound(
313 list->begin(), list->end(), startOffset, doesNotInclude);
314 for (MarkerList::iterator i = startPos; i != list->end(); ++i) {
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 } 195 }
332 196
333 // repaint the affected node 197 // repaint the affected node
334 if (docDirty && dstNode->layoutObject()) { 198 if (docDirty && dstNode->layoutObject()) {
335 dstNode->layoutObject()->setShouldDoFullPaintInvalidation( 199 dstNode->layoutObject()->setShouldDoFullPaintInvalidation(
336 PaintInvalidationDocumentMarkerChange); 200 PaintInvalidationDocumentMarkerChange);
337 } 201 }
338 } 202 }
339 203
340 void DocumentMarkerController::removeMarkers( 204 void DocumentMarkerController::removeMarkers(
341 Node* node, 205 Node* node,
342 unsigned startOffset, 206 unsigned startOffset,
343 int length, 207 int length,
344 DocumentMarker::MarkerTypes markerTypes, 208 DocumentMarker::MarkerTypes markerTypes,
345 RemovePartiallyOverlappingMarkerOrNot 209 RemovePartiallyOverlappingMarkerOrNot
346 shouldRemovePartiallyOverlappingMarker) { 210 shouldRemovePartiallyOverlappingMarker) {
347 if (length <= 0) 211 if (length <= 0)
348 return; 212 return;
349 213
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; 214 bool docDirty = false;
359 size_t emptyListsCount = 0; 215 if (markerTypes.contains(DocumentMarker::Spelling)) {
360 for (size_t markerListIndex = 0; 216 removeMarkersInRangeFromNode(&m_spelling, node, startOffset, length,
361 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 217 shouldRemovePartiallyOverlappingMarker,
362 ++markerListIndex) { 218 &docDirty);
363 Member<MarkerList>& list = (*markers)[markerListIndex];
364 if (!list || list->isEmpty()) {
365 if (list.get() && list->isEmpty())
366 list.clear();
367 ++emptyListsCount;
368 continue;
369 }
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 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->remove(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;
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;
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 } 219 }
418 220
419 if (emptyListsCount == DocumentMarker::MarkerTypeIndexesCount) { 221 if (markerTypes.contains(DocumentMarker::Grammar)) {
420 m_markers.erase(node); 222 removeMarkersInRangeFromNode(&m_grammar, node, startOffset, length,
421 if (m_markers.isEmpty()) 223 shouldRemovePartiallyOverlappingMarker,
422 m_possiblyExistingMarkerTypes = 0; 224 &docDirty);
225 }
226
227 if (markerTypes.contains(DocumentMarker::TextMatch)) {
228 removeMarkersInRangeFromNode(&m_textMatches, node, startOffset, length,
229 shouldRemovePartiallyOverlappingMarker,
230 &docDirty);
231 }
232
233 if (markerTypes.contains(DocumentMarker::Composition)) {
234 removeMarkersInRangeFromNode(&m_compositions, node, startOffset, length,
235 shouldRemovePartiallyOverlappingMarker,
236 &docDirty);
423 } 237 }
424 238
425 // repaint the affected node 239 // repaint the affected node
426 if (docDirty && node->layoutObject()) { 240 if (docDirty && node->layoutObject()) {
427 node->layoutObject()->setShouldDoFullPaintInvalidation( 241 node->layoutObject()->setShouldDoFullPaintInvalidation(
428 PaintInvalidationDocumentMarkerChange); 242 PaintInvalidationDocumentMarkerChange);
429 } 243 }
430 } 244 }
431 245
432 DocumentMarkerVector DocumentMarkerController::markersFor( 246 DocumentMarkerVector DocumentMarkerController::markersFor(
433 Node* node, 247 Node* node,
434 DocumentMarker::MarkerTypes markerTypes) { 248 DocumentMarker::MarkerTypes markerTypes) {
435 DocumentMarkerVector result; 249 DocumentMarkerVector result;
436 250
437 MarkerLists* markers = m_markers.at(node); 251 for (Member<DocumentMarkerList> list :
438 if (!markers) 252 getMarkerListsForNode(node, markerTypes)) {
439 return result; 253 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 } 254 }
452 255
453 std::sort(result.begin(), result.end(), compareByStart); 256 std::sort(result.begin(), result.end(), compareByStart);
454 return result; 257 return result;
455 } 258 }
456 259
457 DocumentMarkerVector DocumentMarkerController::markers() { 260 DocumentMarkerVector DocumentMarkerController::markers() {
458 DocumentMarkerVector result; 261 DocumentMarkerVector result;
459 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) { 262 for (auto it = m_spelling.begin(); it != m_spelling.end(); ++it)
460 MarkerLists* markers = i->value.get(); 263 it->value->appendMarkersToInputList(&result);
461 for (size_t markerListIndex = 0; 264
462 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 265 for (auto it = m_grammar.begin(); it != m_grammar.end(); ++it)
463 ++markerListIndex) { 266 it->value->appendMarkersToInputList(&result);
464 Member<MarkerList>& list = (*markers)[markerListIndex]; 267
465 for (size_t j = 0; list.get() && j < list->size(); ++j) 268 for (auto it = m_textMatches.begin(); it != m_textMatches.end(); ++it)
466 result.push_back(list->at(j).get()); 269 it->value->appendMarkersToInputList(&result);
467 } 270
468 } 271 for (auto it = m_compositions.begin(); it != m_compositions.end(); ++it)
272 it->value->appendMarkersToInputList(&result);
273
469 std::sort(result.begin(), result.end(), compareByStart); 274 std::sort(result.begin(), result.end(), compareByStart);
470 return result; 275 return result;
471 } 276 }
472 277
473 DocumentMarkerVector DocumentMarkerController::markersInRange( 278 DocumentMarkerVector DocumentMarkerController::markersInRange(
474 const EphemeralRange& range, 279 const EphemeralRange& range,
475 DocumentMarker::MarkerTypes markerTypes) { 280 DocumentMarker::MarkerTypes markerTypes) {
476 if (!possiblyHasMarkers(markerTypes))
477 return DocumentMarkerVector();
478 281
479 DocumentMarkerVector foundMarkers; 282 DocumentMarkerVector foundMarkers;
480 283
481 Node* startContainer = range.startPosition().computeContainerNode(); 284 Node* startContainer = range.startPosition().computeContainerNode();
482 DCHECK(startContainer); 285 DCHECK(startContainer);
483 unsigned startOffset = static_cast<unsigned>( 286 unsigned startOffset = static_cast<unsigned>(
484 range.startPosition().computeOffsetInContainerNode()); 287 range.startPosition().computeOffsetInContainerNode());
485 Node* endContainer = range.endPosition().computeContainerNode(); 288 Node* endContainer = range.endPosition().computeContainerNode();
486 DCHECK(endContainer); 289 DCHECK(endContainer);
487 unsigned endOffset = 290 unsigned endOffset =
488 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); 291 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode());
489 292
490 for (Node& node : range.nodes()) { 293 for (Node& node : range.nodes()) {
491 for (DocumentMarker* marker : markersFor(&node)) { 294 for (DocumentMarker* marker : markersFor(&node)) {
492 if (!markerTypes.contains(marker->type())) 295 if (!markerTypes.contains(marker->type()))
493 continue; 296 continue;
494 if (node == startContainer && marker->endOffset() <= startOffset) 297 if (node == startContainer && marker->endOffset() <= startOffset)
495 continue; 298 continue;
496 if (node == endContainer && marker->startOffset() >= endOffset) 299 if (node == endContainer && marker->startOffset() >= endOffset)
497 continue; 300 continue;
498 foundMarkers.push_back(marker); 301 foundMarkers.push_back(marker);
499 } 302 }
500 } 303 }
501 return foundMarkers; 304 return foundMarkers;
502 } 305 }
503 306
504 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( 307 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(
505 DocumentMarker::MarkerType markerType) { 308 DocumentMarker::MarkerType) {
Xiaocheng 2017/03/21 19:12:11 I think it's better to: 1. Add a comment that thi
506 Vector<IntRect> result; 309 Vector<IntRect> result;
507 310
508 if (!possiblyHasMarkers(markerType))
509 return result;
510 DCHECK(!(m_markers.isEmpty()));
511
512 // outer loop: process each node 311 // outer loop: process each node
513 MarkerMap::iterator end = m_markers.end(); 312 for (auto nodeIterator = m_textMatches.begin();
514 for (MarkerMap::iterator nodeIterator = m_markers.begin(); 313 nodeIterator != m_textMatches.end(); ++nodeIterator) {
515 nodeIterator != end; ++nodeIterator) {
516 // inner loop; process each marker in this node 314 // inner loop; process each marker in this node
517 const Node& node = *nodeIterator->key; 315 const Node& node = *nodeIterator->key;
518 MarkerLists* markers = nodeIterator->value.get(); 316 TextMatchMarkerList* list = nodeIterator->value.get();
519 for (size_t markerListIndex = 0; 317 #if DCHECK_IS_ON()
520 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 318 if (!list->empty()) {
521 ++markerListIndex) { 319 DCHECK(!m_document->view() || !m_document->view()->needsLayout());
522 Member<MarkerList>& list = (*markers)[markerListIndex]; 320 DCHECK(!m_document->needsLayoutTreeUpdate());
523 if (!list || list->isEmpty() || (*list->begin())->type() != markerType) 321 }
322 #endif
323 for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) {
324 RenderedTextMatchMarker* marker = list->at(markerIndex);
325 marker->updateRenderedRectIfNeeded(node);
326 if (!marker->isRendered())
524 continue; 327 continue;
525 for (unsigned markerIndex = 0; markerIndex < list->size(); 328 result.push_back(marker->renderedRect());
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 } 329 }
534 } 330 }
535 331
536 return result; 332 return result;
537 } 333 }
538 334
539 static void invalidatePaintForTickmarks(const Node& node) { 335 static void invalidatePaintForTickmarks(const Node& node) {
540 if (FrameView* frameView = node.document().view()) 336 if (FrameView* frameView = node.document().view())
541 frameView->invalidatePaintForTickmarks(); 337 frameView->invalidatePaintForTickmarks();
542 } 338 }
543 339
544 void DocumentMarkerController::updateMarkerRenderedRectIfNeeded( 340 void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node) {
Xiaocheng 2017/03/21 19:12:11 Implementation of this function should also be mov
545 const Node& node, 341 if (!m_textMatches.contains(&node))
546 RenderedDocumentMarker& marker) { 342 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 343
553 void DocumentMarkerController::invalidateRectsForMarkersInNode( 344 TextMatchMarkerList& markerList = *(m_textMatches.find(&node)->value);
554 const Node& node) {
555 MarkerLists* markers = m_markers.at(&node);
556 345
557 for (auto& markerList : *markers) { 346 for (Member<DocumentMarker> marker : markerList) {
558 if (!markerList || markerList->isEmpty()) 347 auto renderedTextMatchMarker = toRenderedTextMatchMarkerOrDie(marker.get());
559 continue; 348 renderedTextMatchMarker->invalidate();
349 }
560 350
561 for (auto& marker : *markerList) 351 invalidatePaintForTickmarks(node);
562 marker->invalidate();
563
564 if (markerList->front()->type() == DocumentMarker::TextMatch)
565 invalidatePaintForTickmarks(node);
566 }
567 } 352 }
568 353
569 void DocumentMarkerController::invalidateRectsForAllMarkers() { 354 void DocumentMarkerController::invalidateRectsForAllMarkers() {
Xiaocheng 2017/03/21 19:12:11 Ditto.
570 for (auto& nodeMarkers : m_markers) { 355 for (auto& nodeMarkers : m_textMatches)
571 const Node& node = *nodeMarkers.key; 356 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 } 357 }
584 358
585 DEFINE_TRACE(DocumentMarkerController) { 359 DEFINE_TRACE(DocumentMarkerController) {
586 visitor->trace(m_markers); 360 visitor->trace(m_spelling);
361 visitor->trace(m_grammar);
362 visitor->trace(m_textMatches);
363 visitor->trace(m_compositions);
587 visitor->trace(m_document); 364 visitor->trace(m_document);
588 SynchronousMutationObserver::trace(visitor); 365 SynchronousMutationObserver::trace(visitor);
589 } 366 }
590 367
591 void DocumentMarkerController::removeMarkers( 368 void DocumentMarkerController::removeMarkers(
592 Node* node, 369 Node* node,
593 DocumentMarker::MarkerTypes markerTypes) { 370 DocumentMarker::MarkerTypes markerTypes) {
594 if (!possiblyHasMarkers(markerTypes)) 371 if (markerTypes.contains(DocumentMarker::Spelling))
595 return; 372 m_spelling.remove(node);
596 DCHECK(!m_markers.isEmpty());
597 373
598 MarkerMap::iterator iterator = m_markers.find(node); 374 if (markerTypes.contains(DocumentMarker::Grammar))
599 if (iterator != m_markers.end()) 375 m_grammar.remove(node);
600 removeMarkersFromList(iterator, markerTypes); 376
377 if (markerTypes.contains(DocumentMarker::TextMatch))
378 m_textMatches.remove(node);
379
380 if (markerTypes.contains(DocumentMarker::Composition))
381 m_compositions.remove(node);
382 }
383
384 void DocumentMarkerController::removeMarkers(
385 DocumentMarker::MarkerTypes markerTypes) {
386 if (markerTypes.contains(DocumentMarker::Spelling))
387 m_spelling.clear();
388
389 if (markerTypes.contains(DocumentMarker::Grammar))
390 m_grammar.clear();
391
392 if (markerTypes.contains(DocumentMarker::TextMatch))
393 m_textMatches.clear();
394
395 if (markerTypes.contains(DocumentMarker::Composition))
396 m_compositions.clear();
601 } 397 }
602 398
603 void DocumentMarkerController::removeMarkers( 399 void DocumentMarkerController::removeMarkers(
604 const MarkerRemoverPredicate& shouldRemoveMarker) { 400 const MarkerRemoverPredicate& shouldRemoveMarker) {
605 for (auto& nodeMarkers : m_markers) { 401 for (auto& nodeMarkers : m_spelling) {
606 const Node& node = *nodeMarkers.key; 402 const Node& node = *nodeMarkers.key;
607 if (!node.isTextNode()) // MarkerRemoverPredicate requires a Text node. 403 if (!node.isTextNode())
608 continue; 404 continue;
609 MarkerLists& markers = *nodeMarkers.value; 405 nodeMarkers.value->removeMarkersForWords(static_cast<const Text&>(node),
610 for (size_t markerListIndex = 0; 406 shouldRemoveMarker.m_words);
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->remove(j - 1);
621 removedMarkers = true;
622 }
623 }
624 if (removedMarkers &&
625 markerListIndex == DocumentMarker::TextMatchMarkerIndex)
626 invalidatePaintForTickmarks(node);
627 }
628 }
629 }
630
631 void DocumentMarkerController::removeMarkers(
632 DocumentMarker::MarkerTypes markerTypes) {
633 if (!possiblyHasMarkers(markerTypes))
634 return;
635 DCHECK(!m_markers.isEmpty());
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 }
645
646 m_possiblyExistingMarkerTypes.remove(markerTypes);
647 }
648
649 void DocumentMarkerController::removeMarkersFromList(
650 MarkerMap::iterator iterator,
651 DocumentMarker::MarkerTypes markerTypes) {
652 bool needsRepainting = false;
653 bool nodeCanBeRemoved;
654
655 size_t emptyListsCount = 0;
656 if (markerTypes == DocumentMarker::AllMarkers()) {
657 needsRepainting = true;
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.remove(iterator);
695 if (m_markers.isEmpty())
696 m_possiblyExistingMarkerTypes = 0;
697 } 407 }
698 } 408 }
699 409
700 void DocumentMarkerController::repaintMarkers( 410 void DocumentMarkerController::repaintMarkers(
701 DocumentMarker::MarkerTypes markerTypes) { 411 DocumentMarker::MarkerTypes markerTypes) {
702 if (!possiblyHasMarkers(markerTypes)) 412 HeapHashSet<Member<Node>> nodesToRepaint;
703 return; 413 for (auto& nodeMarkers : m_spelling) {
704 DCHECK(!m_markers.isEmpty()); 414 if (!nodeMarkers.value->empty())
415 nodesToRepaint.insert(nodeMarkers.key.get());
416 }
705 417
706 // outer loop: process each markered node in the document 418 for (auto& nodeMarkers : m_grammar) {
707 MarkerMap::iterator end = m_markers.end(); 419 if (!nodeMarkers.value->empty())
708 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { 420 nodesToRepaint.insert(nodeMarkers.key.get());
709 const Node* node = i->key; 421 }
710 422
711 // inner loop: process each marker in the current node 423 for (auto& nodeMarkers : m_textMatches) {
712 MarkerLists* markers = i->value.get(); 424 if (!nodeMarkers.value->empty())
713 for (size_t markerListIndex = 0; 425 nodesToRepaint.insert(nodeMarkers.key.get());
714 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 426 }
715 ++markerListIndex) {
716 Member<MarkerList>& list = (*markers)[markerListIndex];
717 if (!list || list->isEmpty() ||
718 !markerTypes.contains((*list->begin())->type()))
719 continue;
720 427
721 // cause the node to be redrawn 428 for (auto& nodeMarkers : m_compositions) {
722 if (LayoutObject* layoutObject = node->layoutObject()) { 429 if (!nodeMarkers.value->empty())
723 layoutObject->setShouldDoFullPaintInvalidation( 430 nodesToRepaint.insert(nodeMarkers.key.get());
724 PaintInvalidationDocumentMarkerChange); 431 }
725 break; 432
726 } 433 for (Member<Node> node : nodesToRepaint) {
434 // cause the node to be redrawn
435 if (LayoutObject* layoutObject = node->layoutObject()) {
436 layoutObject->setShouldDoFullPaintInvalidation(
437 PaintInvalidationDocumentMarkerChange);
438 break;
727 } 439 }
728 } 440 }
729 } 441 }
730 442
731 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, 443 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range,
732 bool active) { 444 bool active) {
733 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
734 return false;
735
736 DCHECK(!m_markers.isEmpty());
737
738 Node* const startContainer = range.startPosition().computeContainerNode(); 445 Node* const startContainer = range.startPosition().computeContainerNode();
739 DCHECK(startContainer); 446 DCHECK(startContainer);
740 Node* const endContainer = range.endPosition().computeContainerNode(); 447 Node* const endContainer = range.endPosition().computeContainerNode();
741 DCHECK(endContainer); 448 DCHECK(endContainer);
742 449
743 const unsigned containerStartOffset = 450 const unsigned containerStartOffset =
744 range.startPosition().computeOffsetInContainerNode(); 451 range.startPosition().computeOffsetInContainerNode();
745 const unsigned containerEndOffset = 452 const unsigned containerEndOffset =
746 range.endPosition().computeOffsetInContainerNode(); 453 range.endPosition().computeOffsetInContainerNode();
747 454
748 bool markerFound = false; 455 bool markerFound = false;
749 for (Node& node : range.nodes()) { 456 for (Node& node : range.nodes()) {
750 int startOffset = node == startContainer ? containerStartOffset : 0; 457 int startOffset = node == startContainer ? containerStartOffset : 0;
751 int endOffset = node == endContainer ? containerEndOffset : INT_MAX; 458 int endOffset = node == endContainer ? containerEndOffset : INT_MAX;
752 markerFound |= setMarkersActive(&node, startOffset, endOffset, active); 459 markerFound |= setMarkersActive(&node, startOffset, endOffset, active);
753 } 460 }
754 return markerFound; 461 return markerFound;
755 } 462 }
756 463
464 bool DocumentMarkerController::hasMarkers(Node* node) const {
465 return m_spelling.contains(node) || m_grammar.contains(node) ||
466 m_textMatches.contains(node) || m_compositions.contains(node);
467 }
468
757 bool DocumentMarkerController::setMarkersActive(Node* node, 469 bool DocumentMarkerController::setMarkersActive(Node* node,
758 unsigned startOffset, 470 unsigned startOffset,
759 unsigned endOffset, 471 unsigned endOffset,
760 bool active) { 472 bool active) {
761 MarkerLists* markers = m_markers.at(node);
762 if (!markers)
763 return false;
764
765 bool docDirty = false; 473 bool docDirty = false;
766 Member<MarkerList>& list = 474 if (m_textMatches.contains(node)) {
767 (*markers)[MarkerTypeToMarkerIndex(DocumentMarker::TextMatch)]; 475 docDirty = m_textMatches.at(node)->setTextMatchMarkersActive(
768 if (!list) 476 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 } 477 }
782 478
783 // repaint the affected node 479 if (docDirty && node->layoutObject())
784 if (docDirty && node->layoutObject()) {
785 node->layoutObject()->setShouldDoFullPaintInvalidation( 480 node->layoutObject()->setShouldDoFullPaintInvalidation(
786 PaintInvalidationDocumentMarkerChange); 481 PaintInvalidationDocumentMarkerChange);
787 } 482
788 return docDirty; 483 return docDirty;
789 } 484 }
790 485
791 #ifndef NDEBUG 486 #ifndef NDEBUG
487 static String showMarkerHelper(const DocumentMarkerVector& markers) {
488 StringBuilder builder;
489
490 for (const Member<DocumentMarker> marker : markers) {
491 builder.append(" ");
492 builder.appendNumber(marker->type());
493 builder.append(":[");
494 builder.appendNumber(marker->startOffset());
495 builder.append(":");
496 builder.appendNumber(marker->endOffset());
497 builder.append("](");
498 builder.appendNumber(marker->activeMatch());
499 builder.append(")");
500 }
501
502 return builder.toString();
503 }
504
792 void DocumentMarkerController::showMarkers() const { 505 void DocumentMarkerController::showMarkers() const {
793 StringBuilder builder; 506 StringBuilder builder;
794 MarkerMap::const_iterator end = m_markers.end(); 507
795 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); 508 HeapHashSet<Member<Node>> nodes;
796 nodeIterator != end; ++nodeIterator) { 509 for (const auto& nodeMarkers : m_spelling)
797 const Node* node = nodeIterator->key; 510 nodes.insert(nodeMarkers.key);
511 for (const auto& nodeMarkers : m_grammar)
512 nodes.insert(nodeMarkers.key);
513 for (const auto& nodeMarkers : m_textMatches)
514 nodes.insert(nodeMarkers.key);
515 for (const auto& nodeMarkers : m_compositions)
516 nodes.insert(nodeMarkers.key);
517
518 HeapHashSet<Member<const Node>> nodesWithMarkers;
519 for (Node* node : nodes) {
798 builder.append(String::format("%p", node)); 520 builder.append(String::format("%p", node));
799 MarkerLists* markers = m_markers.at(node); 521
800 for (size_t markerListIndex = 0; 522 if (m_spelling.contains(node)) {
801 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 523 DocumentMarkerVector markers;
802 ++markerListIndex) { 524 m_spelling.at(node)->appendMarkersToInputList(&markers);
803 Member<MarkerList>& list = (*markers)[markerListIndex]; 525 if (!markers.isEmpty()) {
804 for (unsigned markerIndex = 0; list.get() && markerIndex < list->size(); 526 builder.append(showMarkerHelper(markers));
805 ++markerIndex) { 527 nodesWithMarkers.insert(node);
806 DocumentMarker* marker = list->at(markerIndex).get();
807 builder.append(" ");
808 builder.appendNumber(marker->type());
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 } 528 }
817 } 529 }
530
531 if (m_grammar.contains(node)) {
532 DocumentMarkerVector markers;
533 m_grammar.at(node)->appendMarkersToInputList(&markers);
534 if (!markers.isEmpty()) {
535 builder.append(showMarkerHelper(markers));
536 nodesWithMarkers.insert(node);
537 }
538 }
539
540 if (m_textMatches.contains(node)) {
541 DocumentMarkerVector markers;
542 m_textMatches.at(node)->appendMarkersToInputList(&markers);
543 if (!markers.isEmpty()) {
544 builder.append(showMarkerHelper(markers));
545 nodesWithMarkers.insert(node);
546 }
547 }
548
549 if (m_compositions.contains(node)) {
550 DocumentMarkerVector markers;
551 m_compositions.at(node)->appendMarkersToInputList(&markers);
552 if (!markers.isEmpty()) {
553 builder.append(showMarkerHelper(markers));
554 nodesWithMarkers.insert(node);
555 }
556 }
557
818 builder.append("\n"); 558 builder.append("\n");
819 } 559 }
820 LOG(INFO) << m_markers.size() << " nodes have markers:\n" 560 LOG(INFO) << nodesWithMarkers.size() << " nodes have markers:\n"
821 << builder.toString().utf8().data(); 561 << builder.toString().utf8().data();
822 } 562 }
823 #endif 563 #endif
824 564
565 HeapVector<Member<DocumentMarkerList>>
566 DocumentMarkerController::getMarkerListsForNode(
567 Node* node,
568 DocumentMarker::MarkerTypes markerTypes) {
569 HeapVector<Member<DocumentMarkerList>> markerLists;
570
571 if (markerTypes.contains(DocumentMarker::Spelling)) {
572 auto spellingIt = m_spelling.find(node);
573 if (spellingIt != m_spelling.end())
574 markerLists.push_back(spellingIt->value);
575 }
576
577 if (markerTypes.contains(DocumentMarker::Grammar)) {
578 auto grammarIt = m_grammar.find(node);
579 if (grammarIt != m_grammar.end())
580 markerLists.push_back(grammarIt->value);
581 }
582
583 if (markerTypes.contains(DocumentMarker::TextMatch)) {
584 auto textMatchesIt = m_textMatches.find(node);
585 if (textMatchesIt != m_textMatches.end())
586 markerLists.push_back(textMatchesIt->value);
587 }
588
589 if (markerTypes.contains(DocumentMarker::Composition)) {
590 auto compositionsIt = m_compositions.find(node);
591 if (compositionsIt != m_compositions.end())
592 markerLists.push_back(compositionsIt->value);
593 }
594
595 return markerLists;
596 }
597
598 void DocumentMarkerController::removeMarkers(
599 TextIterator& markedText,
600 DocumentMarker::MarkerTypes markerTypes,
601 RemovePartiallyOverlappingMarkerOrNot
602 shouldRemovePartiallyOverlappingMarker) {
603 for (; !markedText.atEnd(); markedText.advance()) {
604 int startOffset = markedText.startOffsetInCurrentContainer();
605 int endOffset = markedText.endOffsetInCurrentContainer();
606 removeMarkers(markedText.currentContainer(), startOffset,
607 endOffset - startOffset, markerTypes,
608 shouldRemovePartiallyOverlappingMarker);
609 }
610 }
611
825 // SynchronousMutationObserver 612 // SynchronousMutationObserver
826 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, 613 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node,
827 unsigned offset, 614 unsigned offset,
828 unsigned oldLength, 615 unsigned oldLength,
829 unsigned newLength) { 616 unsigned newLength) {
830 // If we're doing a pure remove operation, remove the markers in the range 617 bool didShiftMarker = false;
831 // being removed (markers containing, but larger than, the range, will be 618 shiftMarkersInNode(&m_spelling, node, offset, oldLength, newLength,
832 // split) 619 &didShiftMarker);
833 if (newLength == 0) 620 shiftMarkersInNode(&m_grammar, node, offset, oldLength, newLength,
834 removeMarkers(node, offset, oldLength); 621 &didShiftMarker);
622 shiftMarkersInNode(&m_textMatches, node, offset, oldLength, newLength,
623 &didShiftMarker);
624 shiftMarkersInNode(&m_compositions, node, offset, oldLength, newLength,
625 &didShiftMarker);
835 626
836 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 627 if (didShiftMarker) {
837 return; 628 invalidateRectsForMarkersInNode(*node);
838 DCHECK(!m_markers.isEmpty()); 629 // repaint the affected node
839 630 if (node->layoutObject()) {
840 MarkerLists* markers = m_markers.at(node); 631 node->layoutObject()->setShouldDoFullPaintInvalidation(
841 if (!markers) 632 PaintInvalidationDocumentMarkerChange);
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 // algorithm inspired by https://dom.spec.whatwg.org/#concept-cd-replace
852 // but with some changes
853 if (marker.startOffset() > offset) {
854 // Deviation from the concept-cd-replace algorithm: < instead of <= in
855 // the next line
856 if (marker.startOffset() < offset + oldLength) {
857 // Marker start was in the replaced text. Move to end of new text
858 // (Deviation from the concept-cd-replace algorithm: that algorithm
859 // would move to the beginning of the new text here)
860 marker.setStartOffset(offset + newLength);
861 } else {
862 // Marker start was after the replaced text. Shift by length
863 // difference
864 unsigned oldStartOffset = marker.startOffset();
865 marker.setStartOffset(oldStartOffset + newLength - oldLength);
866 }
867 didShiftMarker = true;
868 }
869
870 if (marker.endOffset() > offset) {
871 // Deviation from the concept-cd-replace algorithm: < instead of <= in
872 // the next line
873 if (marker.endOffset() < offset + oldLength) {
874 // Marker end was in the replaced text. Move to beginning of new text
875 marker.setEndOffset(offset);
876 } else {
877 // Marker end was after the replaced text. Shift by length difference
878 unsigned oldEndOffset = marker.endOffset();
879 marker.setEndOffset(oldEndOffset + newLength - oldLength);
880 }
881 didShiftMarker = true;
882 }
883
884 if (marker.startOffset() < marker.endOffset())
885 continue;
886 list->remove(it - list->begin());
887 --it;
888 } 633 }
889 } 634 }
890
891 if (!didShiftMarker)
892 return;
893 if (!node->layoutObject())
894 return;
895 invalidateRectsForMarkersInNode(*node);
896 // repaint the affected node
897 node->layoutObject()->setShouldDoFullPaintInvalidation();
898 } 635 }
899 636
900 } // namespace blink 637 } // namespace blink
901 638
902 #ifndef NDEBUG 639 #ifndef NDEBUG
903 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { 640 void showDocumentMarkers(const blink::DocumentMarkerController* controller) {
904 if (controller) 641 if (controller)
905 controller->showMarkers(); 642 controller->showMarkers();
906 } 643 }
907 #endif 644 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698