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