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