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