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

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

Issue 2723663002: Refactor DocumentMarkerController (Closed)
Patch Set: Fix bugs in didUpdateCharacterData 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,
51 const Text& textNode) const {
52 unsigned start = documentMarker.startOffset();
53 unsigned length = documentMarker.endOffset() - documentMarker.startOffset();
54
55 String markerText = textNode.data().substring(start, length);
56 return m_words.contains(markerText);
57 }
58
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) 55 DocumentMarkerController::DocumentMarkerController(Document& document)
86 : m_possiblyExistingMarkerTypes(0), m_document(&document) { 56 : m_document(&document) {
87 setContext(&document); 57 setContext(&document);
88 } 58 }
89 59
90 void DocumentMarkerController::clear() { 60 void DocumentMarkerController::clear() {
91 m_markers.clear(); 61 m_spelling.clear();
92 m_possiblyExistingMarkerTypes = 0; 62 m_grammar.clear();
63 m_textMatches.clear();
64 m_compositions.clear();
93 } 65 }
94 66
95 void DocumentMarkerController::addMarker(const Position& start, 67 void DocumentMarkerController::addMarker(const Position& start,
96 const Position& end, 68 const Position& end,
97 DocumentMarker::MarkerType type, 69 DocumentMarker::MarkerType type,
98 const String& description) { 70 const String& description) {
99 // Use a TextIterator to visit the potentially multiple nodes the range 71 // Use a TextIterator to visit the potentially multiple nodes the range
100 // covers. 72 // covers.
101 for (TextIterator markedText(start, end); !markedText.atEnd(); 73 for (TextIterator markedText(start, end); !markedText.atEnd();
102 markedText.advance()) { 74 markedText.advance()) {
103 addMarker( 75 addMarker(markedText.currentContainer(),
104 markedText.currentContainer(), 76 new DocumentMarker(
105 DocumentMarker(type, markedText.startOffsetInCurrentContainer(), 77 type, markedText.startOffsetInCurrentContainer(),
106 markedText.endOffsetInCurrentContainer(), description)); 78 markedText.endOffsetInCurrentContainer(), description));
107 } 79 }
108 } 80 }
109 81
110 void DocumentMarkerController::addTextMatchMarker(const EphemeralRange& range, 82 void DocumentMarkerController::addTextMatchMarker(const EphemeralRange& range,
111 bool activeMatch) { 83 bool activeMatch) {
112 DCHECK(!m_document->needsLayoutTreeUpdate()); 84 DCHECK(!m_document->needsLayoutTreeUpdate());
113 85
114 // Use a TextIterator to visit the potentially multiple nodes the range 86 // Use a TextIterator to visit the potentially multiple nodes the range
115 // covers. 87 // covers.
116 for (TextIterator markedText(range.startPosition(), range.endPosition()); 88 for (TextIterator markedText(range.startPosition(), range.endPosition());
117 !markedText.atEnd(); markedText.advance()) 89 !markedText.atEnd(); markedText.advance()) {
118 addMarker( 90 addMarker(markedText.currentContainer(),
119 markedText.currentContainer(), 91 new DocumentMarker(markedText.startOffsetInCurrentContainer(),
Xiaocheng 2017/03/18 06:43:14 These DocumentMarker ctors really look awkward...
rlanday 2017/03/20 18:13:29 Yeah, let's fix this in the follow-up CL where we'
120 DocumentMarker(markedText.startOffsetInCurrentContainer(), 92 markedText.endOffsetInCurrentContainer(),
121 markedText.endOffsetInCurrentContainer(), activeMatch)); 93 activeMatch));
94 }
122 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a 95 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a
123 // throttling algorithm. crbug.com/6819. 96 // throttling algorithm. crbug.com/6819.
124 } 97 }
125 98
126 void DocumentMarkerController::addCompositionMarker(const Position& start, 99 void DocumentMarkerController::addCompositionMarker(const Position& start,
127 const Position& end, 100 const Position& end,
128 Color underlineColor, 101 Color underlineColor,
129 bool thick, 102 bool thick,
130 Color backgroundColor) { 103 Color backgroundColor) {
131 DCHECK(!m_document->needsLayoutTreeUpdate()); 104 DCHECK(!m_document->needsLayoutTreeUpdate());
132 105
133 for (TextIterator markedText(start, end); !markedText.atEnd(); 106 for (TextIterator markedText(start, end); !markedText.atEnd();
134 markedText.advance()) 107 markedText.advance()) {
135 addMarker(markedText.currentContainer(), 108 addMarker(markedText.currentContainer(),
136 DocumentMarker(markedText.startOffsetInCurrentContainer(), 109 new DocumentMarker(markedText.startOffsetInCurrentContainer(),
137 markedText.endOffsetInCurrentContainer(), 110 markedText.endOffsetInCurrentContainer(),
138 underlineColor, thick, backgroundColor)); 111 underlineColor, thick, backgroundColor));
112 }
139 } 113 }
140 114
141 void DocumentMarkerController::prepareForDestruction() { 115 void DocumentMarkerController::prepareForDestruction() {
142 clear(); 116 clear();
143 } 117 }
144 118
145 void DocumentMarkerController::removeMarkers( 119 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, 120 const EphemeralRange& range,
165 DocumentMarker::MarkerTypes markerTypes, 121 DocumentMarker::MarkerTypes markerTypes,
166 RemovePartiallyOverlappingMarkerOrNot 122 RemovePartiallyOverlappingMarkerOrNot
167 shouldRemovePartiallyOverlappingMarker) { 123 shouldRemovePartiallyOverlappingMarker) {
168 DCHECK(!m_document->needsLayoutTreeUpdate()); 124 DCHECK(!m_document->needsLayoutTreeUpdate());
169 125
170 TextIterator markedText(range.startPosition(), range.endPosition()); 126 TextIterator markedText(range.startPosition(), range.endPosition());
171 DocumentMarkerController::removeMarkers( 127 DocumentMarkerController::removeMarkers(
172 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); 128 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker);
173 } 129 }
174 130
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, 131 static bool compareByStart(const Member<DocumentMarker>& lhv,
186 const Member<DocumentMarker>& rhv) { 132 const Member<DocumentMarker>& rhv) {
187 return lhv->startOffset() < rhv->startOffset(); 133 return lhv->startOffset() < rhv->startOffset();
188 } 134 }
189 135
190 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv, 136 static void updateTextMatchMarkerRenderedRect(const Node& node,
Xiaocheng 2017/03/18 06:43:14 Can we move this function to RenderedTextMatchMark
rlanday 2017/03/20 18:13:30 Probably
191 const DocumentMarker* rhv) { 137 RenderedTextMatchMarker& marker) {
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()); 138 Range* range = Range::create(node.document());
203 // The offsets of the marker may be out-dated, so check for exceptions. 139 // The offsets of the marker may be out-dated, so check for exceptions.
204 DummyExceptionStateForTesting exceptionState; 140 DummyExceptionStateForTesting exceptionState;
205 range->setStart(&const_cast<Node&>(node), marker.startOffset(), 141 range->setStart(&const_cast<Node&>(node), marker.startOffset(),
206 exceptionState); 142 exceptionState);
207 if (!exceptionState.hadException()) { 143 if (!exceptionState.hadException()) {
208 range->setEnd(&const_cast<Node&>(node), marker.endOffset(), 144 range->setEnd(&const_cast<Node&>(node), marker.endOffset(),
209 IGNORE_EXCEPTION_FOR_TESTING); 145 IGNORE_EXCEPTION_FOR_TESTING);
210 } 146 }
211 if (!exceptionState.hadException()) { 147 if (!exceptionState.hadException()) {
212 // TODO(yosin): Once we have a |EphemeralRange| version of |boundingBox()|, 148 // TODO(yosin): Once we have a |EphemeralRange| version of |boundingBox()|,
213 // we should use it instead of |Range| version. 149 // we should use it instead of |Range| version.
214 marker.setRenderedRect(LayoutRect(range->boundingBox())); 150 marker.setRenderedRect(LayoutRect(range->boundingBox()));
215 } else { 151 } else {
216 marker.nullifyRenderedRect(); 152 marker.nullifyRenderedRect();
217 } 153 }
218 range->dispose(); 154 range->dispose();
219 } 155 }
220 156
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, 157 void DocumentMarkerController::addMarker(Node* node,
225 const DocumentMarker& newMarker) { 158 DocumentMarker* newMarker) {
226 DCHECK_GE(newMarker.endOffset(), newMarker.startOffset()); 159 DCHECK_GE(newMarker->endOffset(), newMarker->startOffset());
227 if (newMarker.endOffset() == newMarker.startOffset()) 160 if (newMarker->endOffset() == newMarker->startOffset())
228 return; 161 return;
229 162
230 m_possiblyExistingMarkerTypes.add(newMarker.type()); 163 switch (newMarker->type()) {
Xiaocheng 2017/03/18 06:43:14 Could you try to reduce the code duplication? The
rlanday 2017/03/20 18:13:30 I'll see what I can come up with, I was running in
231 164 case DocumentMarker::Spelling: {
232 Member<MarkerLists>& markers = 165 auto it = m_spelling.find(node);
233 m_markers.insert(node, nullptr).storedValue->value; 166 if (it == m_spelling.end()) {
234 if (!markers) { 167 m_spelling.insert(node, new SpellingMarkerList(this));
235 markers = new MarkerLists; 168 it = m_spelling.find(node);
236 markers->grow(DocumentMarker::MarkerTypeIndexesCount); 169 }
237 } 170 it->value->insert(newMarker);
238 171 break;
239 DocumentMarker::MarkerTypeIndex markerListIndex = 172 }
240 MarkerTypeToMarkerIndex(newMarker.type()); 173 case DocumentMarker::Grammar: {
241 if (!markers->at(markerListIndex)) { 174 auto it = m_grammar.find(node);
242 markers->at(markerListIndex) = new MarkerList; 175 if (it == m_grammar.end()) {
243 } 176 m_grammar.insert(node, new GrammarMarkerList(this));
244 177 it = m_grammar.find(node);
245 Member<MarkerList>& list = markers->at(markerListIndex); 178 }
246 RenderedDocumentMarker* newRenderedMarker = 179 it->value->insert(newMarker);
247 RenderedDocumentMarker::create(newMarker); 180 break;
248 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) { 181 }
249 list->push_back(newRenderedMarker); 182 case DocumentMarker::TextMatch: {
250 } else { 183 auto it = m_textMatches.find(node);
251 if (newMarker.type() != DocumentMarker::TextMatch && 184 if (it == m_textMatches.end()) {
252 newMarker.type() != DocumentMarker::Composition) { 185 m_textMatches.insert(node, new TextMatchMarkerList(this));
253 mergeOverlapping(list.get(), newRenderedMarker); 186 it = m_textMatches.find(node);
254 } else { 187 }
255 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), 188 it->value->push_back(static_cast<TextMatchMarker*>(newMarker));
256 &newMarker, startsFurther); 189 break;
257 list->insert(pos - list->begin(), newRenderedMarker); 190 }
191 case DocumentMarker::Composition: {
192 auto it = m_compositions.find(node);
193 if (it == m_compositions.end()) {
194 m_compositions.insert(node, new CompositionMarkerList(this));
195 it = m_compositions.find(node);
196 }
197 it->value->insert(newMarker);
198 break;
258 } 199 }
259 } 200 }
260 201
261 // repaint the affected node 202 // repaint the affected node
262 if (node->layoutObject()) { 203 if (node->layoutObject()) {
263 node->layoutObject()->setShouldDoFullPaintInvalidation( 204 node->layoutObject()->setShouldDoFullPaintInvalidation(
264 PaintInvalidationDocumentMarkerChange); 205 PaintInvalidationDocumentMarkerChange);
265 } 206 }
266 } 207 }
267 208
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 209 // 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 210 // the copies. The shift is useful if, e.g., the caller has created the dstNode
289 // from a non-prefix substring of the srcNode. 211 // from a non-prefix substring of the srcNode.
290 void DocumentMarkerController::copyMarkers(Node* srcNode, 212 void DocumentMarkerController::copyMarkers(Node* srcNode,
291 unsigned startOffset, 213 unsigned startOffset,
292 int length, 214 int length,
293 Node* dstNode, 215 Node* dstNode,
294 int delta) { 216 int delta) {
295 if (length <= 0) 217 if (length <= 0)
296 return; 218 return;
297 219
298 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 220 bool docDirty = false;
299 return;
300 DCHECK(!m_markers.isEmpty());
301 221
302 MarkerLists* markers = m_markers.at(srcNode); 222 for (Member<DocumentMarkerList> list : getMarkerListsForNode(srcNode)) {
303 if (!markers) 223 docDirty =
304 return; 224 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 } 225 }
332 226
333 // repaint the affected node 227 // repaint the affected node
334 if (docDirty && dstNode->layoutObject()) { 228 if (docDirty && dstNode->layoutObject()) {
335 dstNode->layoutObject()->setShouldDoFullPaintInvalidation( 229 dstNode->layoutObject()->setShouldDoFullPaintInvalidation(
336 PaintInvalidationDocumentMarkerChange); 230 PaintInvalidationDocumentMarkerChange);
337 } 231 }
338 } 232 }
339 233
340 void DocumentMarkerController::removeMarkers( 234 void DocumentMarkerController::removeMarkers(
341 Node* node, 235 Node* node,
342 unsigned startOffset, 236 unsigned startOffset,
343 int length, 237 int length,
344 DocumentMarker::MarkerTypes markerTypes, 238 DocumentMarker::MarkerTypes markerTypes,
345 RemovePartiallyOverlappingMarkerOrNot 239 RemovePartiallyOverlappingMarkerOrNot
346 shouldRemovePartiallyOverlappingMarker) { 240 shouldRemovePartiallyOverlappingMarker) {
347 if (length <= 0) 241 if (length <= 0)
348 return; 242 return;
349 243
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; 244 bool docDirty = false;
359 size_t emptyListsCount = 0; 245 if (markerTypes.contains(DocumentMarker::Spelling)) {
Xiaocheng 2017/03/18 06:43:14 Please also try to reduce code duplication here.
360 for (size_t markerListIndex = 0; 246 auto it = m_spelling.find(node);
361 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 247 if (it != m_spelling.end()) {
362 ++markerListIndex) { 248 it->value->removeMarkers(startOffset, length,
363 Member<MarkerList>& list = (*markers)[markerListIndex]; 249 shouldRemovePartiallyOverlappingMarker,
364 if (!list || list->isEmpty()) { 250 &docDirty);
365 if (list.get() && list->isEmpty()) 251 if (it->value->empty())
366 list.clear(); 252 m_spelling.remove(node);
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 } 253 }
417 } 254 }
418 255
419 if (emptyListsCount == DocumentMarker::MarkerTypeIndexesCount) { 256 if (markerTypes.contains(DocumentMarker::Grammar)) {
420 m_markers.erase(node); 257 auto it = m_grammar.find(node);
421 if (m_markers.isEmpty()) 258 if (it != m_grammar.end()) {
422 m_possiblyExistingMarkerTypes = 0; 259 it->value->removeMarkers(startOffset, length,
260 shouldRemovePartiallyOverlappingMarker,
261 &docDirty);
262 if (it->value->empty())
263 m_grammar.remove(node);
264 }
265 }
266
267 if (markerTypes.contains(DocumentMarker::TextMatch)) {
268 auto it = m_textMatches.find(node);
269 if (it != m_textMatches.end()) {
270 it->value->removeMarkers(startOffset, length,
271 shouldRemovePartiallyOverlappingMarker,
272 &docDirty);
273 if (it->value->empty())
274 m_textMatches.remove(node);
275 }
276 }
277
278 if (markerTypes.contains(DocumentMarker::Composition)) {
279 auto it = m_compositions.find(node);
280 if (it != m_compositions.end()) {
281 it->value->removeMarkers(startOffset, length,
282 shouldRemovePartiallyOverlappingMarker,
283 &docDirty);
284 if (it->value->empty())
285 m_compositions.remove(node);
286 }
423 } 287 }
424 288
425 // repaint the affected node 289 // repaint the affected node
426 if (docDirty && node->layoutObject()) { 290 if (docDirty && node->layoutObject()) {
427 node->layoutObject()->setShouldDoFullPaintInvalidation( 291 node->layoutObject()->setShouldDoFullPaintInvalidation(
428 PaintInvalidationDocumentMarkerChange); 292 PaintInvalidationDocumentMarkerChange);
429 } 293 }
430 } 294 }
431 295
432 DocumentMarkerVector DocumentMarkerController::markersFor( 296 DocumentMarkerVector DocumentMarkerController::markersFor(
433 Node* node, 297 Node* node,
434 DocumentMarker::MarkerTypes markerTypes) { 298 DocumentMarker::MarkerTypes markerTypes) {
435 DocumentMarkerVector result; 299 DocumentMarkerVector result;
436 300
437 MarkerLists* markers = m_markers.at(node); 301 for (Member<DocumentMarkerList> list :
438 if (!markers) 302 getMarkerListsForNode(node, markerTypes)) {
439 return result; 303 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 } 304 }
452 305
453 std::sort(result.begin(), result.end(), compareByStart); 306 std::sort(result.begin(), result.end(), compareByStart);
454 return result; 307 return result;
455 } 308 }
456 309
457 DocumentMarkerVector DocumentMarkerController::markers() { 310 DocumentMarkerVector DocumentMarkerController::markers() {
458 DocumentMarkerVector result; 311 DocumentMarkerVector result;
459 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) { 312 for (auto it = m_spelling.begin(); it != m_spelling.end(); ++it)
460 MarkerLists* markers = i->value.get(); 313 it->value->appendMarkersToInputList(&result);
461 for (size_t markerListIndex = 0; 314
462 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 315 for (auto it = m_grammar.begin(); it != m_grammar.end(); ++it)
463 ++markerListIndex) { 316 it->value->appendMarkersToInputList(&result);
464 Member<MarkerList>& list = (*markers)[markerListIndex]; 317
465 for (size_t j = 0; list.get() && j < list->size(); ++j) 318 for (auto it = m_textMatches.begin(); it != m_textMatches.end(); ++it)
466 result.push_back(list->at(j).get()); 319 it->value->appendMarkersToInputList(&result);
467 } 320
468 } 321 for (auto it = m_compositions.begin(); it != m_compositions.end(); ++it)
322 it->value->appendMarkersToInputList(&result);
323
469 std::sort(result.begin(), result.end(), compareByStart); 324 std::sort(result.begin(), result.end(), compareByStart);
470 return result; 325 return result;
471 } 326 }
472 327
473 DocumentMarkerVector DocumentMarkerController::markersInRange( 328 DocumentMarkerVector DocumentMarkerController::markersInRange(
474 const EphemeralRange& range, 329 const EphemeralRange& range,
475 DocumentMarker::MarkerTypes markerTypes) { 330 DocumentMarker::MarkerTypes markerTypes) {
476 if (!possiblyHasMarkers(markerTypes))
477 return DocumentMarkerVector();
478 331
479 DocumentMarkerVector foundMarkers; 332 DocumentMarkerVector foundMarkers;
480 333
481 Node* startContainer = range.startPosition().computeContainerNode(); 334 Node* startContainer = range.startPosition().computeContainerNode();
482 DCHECK(startContainer); 335 DCHECK(startContainer);
483 unsigned startOffset = static_cast<unsigned>( 336 unsigned startOffset = static_cast<unsigned>(
484 range.startPosition().computeOffsetInContainerNode()); 337 range.startPosition().computeOffsetInContainerNode());
485 Node* endContainer = range.endPosition().computeContainerNode(); 338 Node* endContainer = range.endPosition().computeContainerNode();
486 DCHECK(endContainer); 339 DCHECK(endContainer);
487 unsigned endOffset = 340 unsigned endOffset =
488 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); 341 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode());
489 342
490 for (Node& node : range.nodes()) { 343 for (Node& node : range.nodes()) {
491 for (DocumentMarker* marker : markersFor(&node)) { 344 for (DocumentMarker* marker : markersFor(&node)) {
492 if (!markerTypes.contains(marker->type())) 345 if (!markerTypes.contains(marker->type()))
493 continue; 346 continue;
494 if (node == startContainer && marker->endOffset() <= startOffset) 347 if (node == startContainer && marker->endOffset() <= startOffset)
495 continue; 348 continue;
496 if (node == endContainer && marker->startOffset() >= endOffset) 349 if (node == endContainer && marker->startOffset() >= endOffset)
497 continue; 350 continue;
498 foundMarkers.push_back(marker); 351 foundMarkers.push_back(marker);
499 } 352 }
500 } 353 }
501 return foundMarkers; 354 return foundMarkers;
502 } 355 }
503 356
504 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( 357 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers() {
505 DocumentMarker::MarkerType markerType) {
506 Vector<IntRect> result; 358 Vector<IntRect> result;
507 359
508 if (!possiblyHasMarkers(markerType))
509 return result;
510 DCHECK(!(m_markers.isEmpty()));
511
512 // outer loop: process each node 360 // outer loop: process each node
513 MarkerMap::iterator end = m_markers.end(); 361 for (auto nodeIterator = m_textMatches.begin();
514 for (MarkerMap::iterator nodeIterator = m_markers.begin(); 362 nodeIterator != m_textMatches.end(); ++nodeIterator) {
515 nodeIterator != end; ++nodeIterator) {
516 // inner loop; process each marker in this node 363 // inner loop; process each marker in this node
517 const Node& node = *nodeIterator->key; 364 const Node& node = *nodeIterator->key;
518 MarkerLists* markers = nodeIterator->value.get(); 365 TextMatchMarkerList* list = nodeIterator->value.get();
519 for (size_t markerListIndex = 0; 366 for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) {
520 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 367 RenderedTextMatchMarker* marker = list->at(markerIndex);
521 ++markerListIndex) { 368 updateTextMatchMarkerRenderedRectIfNeeded(node, *marker);
522 Member<MarkerList>& list = (*markers)[markerListIndex]; 369 if (!marker->isRendered())
523 if (!list || list->isEmpty() || (*list->begin())->type() != markerType)
524 continue; 370 continue;
525 for (unsigned markerIndex = 0; markerIndex < list->size(); 371 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 } 372 }
534 } 373 }
535 374
536 return result; 375 return result;
537 } 376 }
538 377
539 static void invalidatePaintForTickmarks(const Node& node) { 378 static void invalidatePaintForTickmarks(const Node& node) {
540 if (FrameView* frameView = node.document().view()) 379 if (FrameView* frameView = node.document().view())
541 frameView->invalidatePaintForTickmarks(); 380 frameView->invalidatePaintForTickmarks();
542 } 381 }
543 382
544 void DocumentMarkerController::updateMarkerRenderedRectIfNeeded( 383 void DocumentMarkerController::updateTextMatchMarkerRenderedRectIfNeeded(
545 const Node& node, 384 const Node& node,
546 RenderedDocumentMarker& marker) { 385 RenderedTextMatchMarker& marker) {
547 DCHECK(!m_document->view() || !m_document->view()->needsLayout()); 386 DCHECK(!m_document->view() || !m_document->view()->needsLayout());
548 DCHECK(!m_document->needsLayoutTreeUpdate()); 387 DCHECK(!m_document->needsLayoutTreeUpdate());
549 if (!marker.isValid()) 388 if (!marker.isValid())
550 updateMarkerRenderedRect(node, marker); 389 updateTextMatchMarkerRenderedRect(node, marker);
551 } 390 }
552 391
553 void DocumentMarkerController::invalidateRectsForMarkersInNode( 392 void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node) {
554 const Node& node) { 393 if (!m_textMatches.contains(&node))
555 MarkerLists* markers = m_markers.at(&node); 394 return;
556 395
557 for (auto& markerList : *markers) { 396 TextMatchMarkerList& markerList = *(m_textMatches.find(&node)->value);
558 if (!markerList || markerList->isEmpty())
559 continue;
560 397
561 for (auto& marker : *markerList) 398 for (Member<DocumentMarker> marker : markerList) {
562 marker->invalidate(); 399 auto renderedTextMatchMarker =
400 static_cast<RenderedTextMatchMarker*>(marker.get());
401 renderedTextMatchMarker->invalidate();
402 }
563 403
564 if (markerList->front()->type() == DocumentMarker::TextMatch) 404 invalidatePaintForTickmarks(node);
565 invalidatePaintForTickmarks(node);
566 }
567 } 405 }
568 406
569 void DocumentMarkerController::invalidateRectsForAllMarkers() { 407 void DocumentMarkerController::invalidateRectsForAllMarkers() {
570 for (auto& nodeMarkers : m_markers) { 408 for (auto& nodeMarkers : m_textMatches)
571 const Node& node = *nodeMarkers.key; 409 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 } 410 }
584 411
585 DEFINE_TRACE(DocumentMarkerController) { 412 DEFINE_TRACE(DocumentMarkerController) {
586 visitor->trace(m_markers); 413 visitor->trace(m_spelling);
414 visitor->trace(m_grammar);
415 visitor->trace(m_textMatches);
416 visitor->trace(m_compositions);
587 visitor->trace(m_document); 417 visitor->trace(m_document);
588 SynchronousMutationObserver::trace(visitor); 418 SynchronousMutationObserver::trace(visitor);
589 } 419 }
590 420
591 void DocumentMarkerController::removeMarkers( 421 void DocumentMarkerController::removeMarkers(
592 Node* node, 422 Node* node,
593 DocumentMarker::MarkerTypes markerTypes) { 423 DocumentMarker::MarkerTypes markerTypes) {
594 if (!possiblyHasMarkers(markerTypes)) 424 if (markerTypes.contains(DocumentMarker::Spelling))
595 return; 425 m_spelling.remove(node);
596 DCHECK(!m_markers.isEmpty());
597 426
598 MarkerMap::iterator iterator = m_markers.find(node); 427 if (markerTypes.contains(DocumentMarker::Grammar))
599 if (iterator != m_markers.end()) 428 m_grammar.remove(node);
600 removeMarkersFromList(iterator, markerTypes); 429
430 if (markerTypes.contains(DocumentMarker::TextMatch))
431 m_textMatches.remove(node);
432
433 if (markerTypes.contains(DocumentMarker::Composition))
434 m_compositions.remove(node);
435 }
436
437 void DocumentMarkerController::removeMarkers(
438 DocumentMarker::MarkerTypes markerTypes) {
439 if (markerTypes.contains(DocumentMarker::Spelling))
440 m_spelling.clear();
441
442 if (markerTypes.contains(DocumentMarker::Grammar))
443 m_grammar.clear();
444
445 if (markerTypes.contains(DocumentMarker::TextMatch))
446 m_textMatches.clear();
447
448 if (markerTypes.contains(DocumentMarker::Composition))
449 m_compositions.clear();
601 } 450 }
602 451
603 void DocumentMarkerController::removeMarkers( 452 void DocumentMarkerController::removeMarkers(
604 const MarkerRemoverPredicate& shouldRemoveMarker) { 453 const MarkerRemoverPredicate& shouldRemoveMarker) {
605 for (auto& nodeMarkers : m_markers) { 454 for (auto& nodeMarkers : m_spelling) {
606 const Node& node = *nodeMarkers.key; 455 const Node& node = *nodeMarkers.key;
607 if (!node.isTextNode()) // MarkerRemoverPredicate requires a Text node. 456 if (!node.isTextNode())
608 continue; 457 continue;
609 MarkerLists& markers = *nodeMarkers.value; 458 nodeMarkers.value->removeMarkersForWords(static_cast<const Text&>(node),
610 for (size_t markerListIndex = 0; 459 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 } 460 }
698 } 461 }
699 462
700 void DocumentMarkerController::repaintMarkers( 463 void DocumentMarkerController::repaintMarkers(
701 DocumentMarker::MarkerTypes markerTypes) { 464 DocumentMarker::MarkerTypes markerTypes) {
702 if (!possiblyHasMarkers(markerTypes)) 465 HeapHashSet<Member<Node>> nodesToRepaint;
703 return; 466 for (auto& nodeMarkers : m_spelling) {
704 DCHECK(!m_markers.isEmpty()); 467 if (!nodeMarkers.value->empty())
468 nodesToRepaint.insert(nodeMarkers.key.get());
469 }
705 470
706 // outer loop: process each markered node in the document 471 for (auto& nodeMarkers : m_grammar) {
707 MarkerMap::iterator end = m_markers.end(); 472 if (!nodeMarkers.value->empty())
708 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { 473 nodesToRepaint.insert(nodeMarkers.key.get());
709 const Node* node = i->key; 474 }
710 475
711 // inner loop: process each marker in the current node 476 for (auto& nodeMarkers : m_textMatches) {
712 MarkerLists* markers = i->value.get(); 477 if (!nodeMarkers.value->empty())
713 for (size_t markerListIndex = 0; 478 nodesToRepaint.insert(nodeMarkers.key.get());
714 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 479 }
715 ++markerListIndex) {
716 Member<MarkerList>& list = (*markers)[markerListIndex];
717 if (!list || list->isEmpty() ||
718 !markerTypes.contains((*list->begin())->type()))
719 continue;
720 480
721 // cause the node to be redrawn 481 for (auto& nodeMarkers : m_compositions) {
722 if (LayoutObject* layoutObject = node->layoutObject()) { 482 if (!nodeMarkers.value->empty())
723 layoutObject->setShouldDoFullPaintInvalidation( 483 nodesToRepaint.insert(nodeMarkers.key.get());
724 PaintInvalidationDocumentMarkerChange); 484 }
725 break; 485
726 } 486 for (Member<Node> node : nodesToRepaint) {
487 // cause the node to be redrawn
488 if (LayoutObject* layoutObject = node->layoutObject()) {
489 layoutObject->setShouldDoFullPaintInvalidation(
490 PaintInvalidationDocumentMarkerChange);
491 break;
727 } 492 }
728 } 493 }
729 } 494 }
730 495
731 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, 496 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range,
732 bool active) { 497 bool active) {
733 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
734 return false;
735
736 DCHECK(!m_markers.isEmpty());
737
738 Node* const startContainer = range.startPosition().computeContainerNode(); 498 Node* const startContainer = range.startPosition().computeContainerNode();
739 DCHECK(startContainer); 499 DCHECK(startContainer);
740 Node* const endContainer = range.endPosition().computeContainerNode(); 500 Node* const endContainer = range.endPosition().computeContainerNode();
741 DCHECK(endContainer); 501 DCHECK(endContainer);
742 502
743 const unsigned containerStartOffset = 503 const unsigned containerStartOffset =
744 range.startPosition().computeOffsetInContainerNode(); 504 range.startPosition().computeOffsetInContainerNode();
745 const unsigned containerEndOffset = 505 const unsigned containerEndOffset =
746 range.endPosition().computeOffsetInContainerNode(); 506 range.endPosition().computeOffsetInContainerNode();
747 507
748 bool markerFound = false; 508 bool markerFound = false;
749 for (Node& node : range.nodes()) { 509 for (Node& node : range.nodes()) {
750 int startOffset = node == startContainer ? containerStartOffset : 0; 510 int startOffset = node == startContainer ? containerStartOffset : 0;
751 int endOffset = node == endContainer ? containerEndOffset : INT_MAX; 511 int endOffset = node == endContainer ? containerEndOffset : INT_MAX;
752 markerFound |= setMarkersActive(&node, startOffset, endOffset, active); 512 markerFound |= setMarkersActive(&node, startOffset, endOffset, active);
753 } 513 }
754 return markerFound; 514 return markerFound;
755 } 515 }
756 516
517 bool DocumentMarkerController::hasMarkers(Node* node) const {
518 return m_spelling.contains(node) || m_grammar.contains(node) ||
519 m_textMatches.contains(node) || m_compositions.contains(node);
520 }
521
757 bool DocumentMarkerController::setMarkersActive(Node* node, 522 bool DocumentMarkerController::setMarkersActive(Node* node,
758 unsigned startOffset, 523 unsigned startOffset,
759 unsigned endOffset, 524 unsigned endOffset,
760 bool active) { 525 bool active) {
761 MarkerLists* markers = m_markers.at(node);
762 if (!markers)
763 return false;
764
765 bool docDirty = false; 526 bool docDirty = false;
766 Member<MarkerList>& list = 527 if (m_textMatches.contains(node)) {
767 (*markers)[MarkerTypeToMarkerIndex(DocumentMarker::TextMatch)]; 528 docDirty = m_textMatches.at(node)->setTextMatchMarkersActive(
768 if (!list) 529 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 } 530 }
782 531
783 // repaint the affected node 532 if (docDirty && node->layoutObject())
784 if (docDirty && node->layoutObject()) {
785 node->layoutObject()->setShouldDoFullPaintInvalidation( 533 node->layoutObject()->setShouldDoFullPaintInvalidation(
786 PaintInvalidationDocumentMarkerChange); 534 PaintInvalidationDocumentMarkerChange);
787 } 535
788 return docDirty; 536 return docDirty;
789 } 537 }
790 538
791 #ifndef NDEBUG 539 #ifndef NDEBUG
540 static String showMarkerHelper(const DocumentMarkerVector& markers) {
541 StringBuilder builder;
542
543 for (const Member<DocumentMarker> marker : markers) {
544 builder.append(" ");
545 builder.appendNumber(marker->type());
546 builder.append(":[");
547 builder.appendNumber(marker->startOffset());
548 builder.append(":");
549 builder.appendNumber(marker->endOffset());
550 builder.append("](");
551 builder.appendNumber(marker->activeMatch());
552 builder.append(")");
553 }
554
555 return builder.toString();
556 }
557
792 void DocumentMarkerController::showMarkers() const { 558 void DocumentMarkerController::showMarkers() const {
793 StringBuilder builder; 559 StringBuilder builder;
794 MarkerMap::const_iterator end = m_markers.end(); 560
795 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); 561 HeapHashSet<Member<Node>> nodes;
796 nodeIterator != end; ++nodeIterator) { 562 for (const auto& nodeMarkers : m_spelling)
797 const Node* node = nodeIterator->key; 563 nodes.insert(nodeMarkers.key);
564 for (const auto& nodeMarkers : m_grammar)
565 nodes.insert(nodeMarkers.key);
566 for (const auto& nodeMarkers : m_textMatches)
567 nodes.insert(nodeMarkers.key);
568 for (const auto& nodeMarkers : m_compositions)
569 nodes.insert(nodeMarkers.key);
570
571 HeapHashSet<Member<const Node>> nodesWithMarkers;
572 for (Node* node : nodes) {
798 builder.append(String::format("%p", node)); 573 builder.append(String::format("%p", node));
799 MarkerLists* markers = m_markers.at(node); 574
800 for (size_t markerListIndex = 0; 575 if (m_spelling.contains(node)) {
801 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; 576 DocumentMarkerVector markers;
802 ++markerListIndex) { 577 m_spelling.at(node)->appendMarkersToInputList(&markers);
803 Member<MarkerList>& list = (*markers)[markerListIndex]; 578 if (!markers.isEmpty()) {
804 for (unsigned markerIndex = 0; list.get() && markerIndex < list->size(); 579 builder.append(showMarkerHelper(markers));
805 ++markerIndex) { 580 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 } 581 }
817 } 582 }
583
584 if (m_grammar.contains(node)) {
585 DocumentMarkerVector markers;
586 m_grammar.at(node)->appendMarkersToInputList(&markers);
587 if (!markers.isEmpty()) {
588 builder.append(showMarkerHelper(markers));
589 nodesWithMarkers.insert(node);
590 }
591 }
592
593 if (m_textMatches.contains(node)) {
594 DocumentMarkerVector markers;
595 m_textMatches.at(node)->appendMarkersToInputList(&markers);
596 if (!markers.isEmpty()) {
597 builder.append(showMarkerHelper(markers));
598 nodesWithMarkers.insert(node);
599 }
600 }
601
602 if (m_compositions.contains(node)) {
603 DocumentMarkerVector markers;
604 m_compositions.at(node)->appendMarkersToInputList(&markers);
605 if (!markers.isEmpty()) {
606 builder.append(showMarkerHelper(markers));
607 nodesWithMarkers.insert(node);
608 }
609 }
610
818 builder.append("\n"); 611 builder.append("\n");
819 } 612 }
820 LOG(INFO) << m_markers.size() << " nodes have markers:\n" 613 LOG(INFO) << nodesWithMarkers.size() << " nodes have markers:\n"
821 << builder.toString().utf8().data(); 614 << builder.toString().utf8().data();
822 } 615 }
823 #endif 616 #endif
824 617
618 HeapVector<Member<DocumentMarkerList>>
619 DocumentMarkerController::getMarkerListsForNode(
620 Node* node,
621 DocumentMarker::MarkerTypes markerTypes) {
622 HeapVector<Member<DocumentMarkerList>> markerLists;
623
624 if (markerTypes.contains(DocumentMarker::Spelling)) {
625 auto spellingIt = m_spelling.find(node);
626 if (spellingIt != m_spelling.end())
627 markerLists.push_back(spellingIt->value);
628 }
629
630 if (markerTypes.contains(DocumentMarker::Grammar)) {
631 auto grammarIt = m_grammar.find(node);
632 if (grammarIt != m_grammar.end())
633 markerLists.push_back(grammarIt->value);
634 }
635
636 if (markerTypes.contains(DocumentMarker::TextMatch)) {
637 auto textMatchesIt = m_textMatches.find(node);
638 if (textMatchesIt != m_textMatches.end())
639 markerLists.push_back(textMatchesIt->value);
640 }
641
642 if (markerTypes.contains(DocumentMarker::Composition)) {
643 auto compositionsIt = m_compositions.find(node);
644 if (compositionsIt != m_compositions.end())
645 markerLists.push_back(compositionsIt->value);
646 }
647
648 return markerLists;
649 }
650
651 void DocumentMarkerController::removeMarkers(
652 TextIterator& markedText,
653 DocumentMarker::MarkerTypes markerTypes,
654 RemovePartiallyOverlappingMarkerOrNot
655 shouldRemovePartiallyOverlappingMarker) {
656 for (; !markedText.atEnd(); markedText.advance()) {
657 int startOffset = markedText.startOffsetInCurrentContainer();
658 int endOffset = markedText.endOffsetInCurrentContainer();
659 removeMarkers(markedText.currentContainer(), startOffset,
660 endOffset - startOffset, markerTypes,
661 shouldRemovePartiallyOverlappingMarker);
662 }
663 }
664
825 // SynchronousMutationObserver 665 // SynchronousMutationObserver
826 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, 666 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node,
827 unsigned offset, 667 unsigned offset,
828 unsigned oldLength, 668 unsigned oldLength,
829 unsigned newLength) { 669 unsigned newLength) {
830 // If we're doing a pure remove operation, remove the markers in the range 670 bool didShiftMarker = false;
831 // being removed (markers containing, but larger than, the range, will be 671 auto spellingIt = m_spelling.find(node);
Xiaocheng 2017/03/18 06:43:14 De-duplication needed.
832 // split) 672 if (spellingIt != m_spelling.end()) {
833 if (newLength == 0) 673 didShiftMarker =
834 removeMarkers(node, offset, oldLength); 674 spellingIt->value->shiftMarkers(offset, oldLength, newLength) ||
675 didShiftMarker;
676 }
835 677
836 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) 678 auto grammarIt = m_grammar.find(node);
837 return; 679 if (grammarIt != m_grammar.end()) {
838 DCHECK(!m_markers.isEmpty()); 680 didShiftMarker =
681 grammarIt->value->shiftMarkers(offset, oldLength, newLength) ||
682 didShiftMarker;
683 }
839 684
840 MarkerLists* markers = m_markers.at(node); 685 auto textMatchesIt = m_textMatches.find(node);
841 if (!markers) 686 if (textMatchesIt != m_textMatches.end()) {
842 return; 687 didShiftMarker =
688 textMatchesIt->value->shiftMarkers(offset, oldLength, newLength) ||
689 didShiftMarker;
690 }
843 691
844 bool didShiftMarker = false; 692 auto compositionsIt = m_compositions.find(node);
845 for (MarkerList* list : *markers) { 693 if (compositionsIt != m_compositions.end()) {
846 if (!list) 694 didShiftMarker =
847 continue; 695 compositionsIt->value->shiftMarkers(offset, oldLength, newLength) ||
696 didShiftMarker;
697 }
848 698
849 for (MarkerList::iterator it = list->begin(); it != list->end(); ++it) { 699 if (didShiftMarker) {
850 RenderedDocumentMarker& marker = **it; 700 invalidateRectsForMarkersInNode(*node);
851 // algorithm inspired by https://dom.spec.whatwg.org/#concept-cd-replace 701 // repaint the affected node
852 // but with some changes 702 if (node->layoutObject()) {
853 if (marker.startOffset() > offset) { 703 node->layoutObject()->setShouldDoFullPaintInvalidation(
854 // Deviation from the concept-cd-replace algorithm: < instead of <= in 704 PaintInvalidationDocumentMarkerChange);
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 } 705 }
889 } 706 }
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 } 707 }
899 708
900 } // namespace blink 709 } // namespace blink
901 710
902 #ifndef NDEBUG 711 #ifndef NDEBUG
903 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { 712 void showDocumentMarkers(const blink::DocumentMarkerController* controller) {
904 if (controller) 713 if (controller)
905 controller->showMarkers(); 714 controller->showMarkers();
906 } 715 }
907 #endif 716 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698