1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 *
25 */
26
27 #include "config.h"
28 #include "core/dom/DocumentMarkerController.h"
29
30 #include "core/dom/Node.h"
31 #include "core/dom/NodeTraversal.h"
32 #include "core/dom/Range.h"
33 #include "core/dom/RenderedDocumentMarker.h"
34 #include "core/dom/Text.h"
35 #include "core/editing/TextIterator.h"
36 #include "core/rendering/RenderObject.h"
37
38 #ifndef NDEBUG
39 #include <stdio.h>
40 #endif
41
42 namespace blink {
43
MarkerRemoverPredicate(const Vector<String> & words)44 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words)
45 : m_words(words)
46 {
47 }
48
operator ()(const DocumentMarker & documentMarker,const Text & textNode) const49 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, const Text& textNode) const
50 {
51 unsigned start = documentMarker.startOffset();
52 unsigned length = documentMarker.endOffset() - documentMarker.startOffset();
53
54 String markerText = textNode.data().substring(start, length);
55 return m_words.contains(markerText);
56 }
57
58 namespace {
59
MarkerTypeToMarkerIndex(DocumentMarker::MarkerType type)60 DocumentMarker::MarkerTypeIndex MarkerTypeToMarkerIndex(DocumentMarker::MarkerType type)
61 {
62 switch (type) {
63 case DocumentMarker::Spelling:
64 return DocumentMarker::SpellingMarkerIndex;
65 case DocumentMarker::Grammar:
66 return DocumentMarker::GramarMarkerIndex;
67 case DocumentMarker::TextMatch:
68 return DocumentMarker::TextMatchMarkerIndex;
69 case DocumentMarker::InvisibleSpellcheck:
70 return DocumentMarker::InvisibleSpellcheckMarkerIndex;
71 }
72
73 ASSERT_NOT_REACHED();
74 return DocumentMarker::SpellingMarkerIndex;
75 }
76
77 } // namespace
78
possiblyHasMarkers(DocumentMarker::MarkerTypes types)79 inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
80 {
81 return m_possiblyExistingMarkerTypes.intersects(types);
82 }
83
DocumentMarkerController()84 DocumentMarkerController::DocumentMarkerController()
85 : m_possiblyExistingMarkerTypes(0)
86 {
87 }
88
89 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(DocumentMarkerController);
90
clear()91 void DocumentMarkerController::clear()
92 {
93 m_markers.clear();
94 m_possiblyExistingMarkerTypes = 0;
95 }
96
addMarker(Range * range,DocumentMarker::MarkerType type,const String & description,uint32_t hash)97 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description, uint32_t hash)
98 {
99 // Use a TextIterator to visit the potentially multiple nodes the range covers.
100 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
101 addMarker(markedText.startContainer(), DocumentMarker(type, markedText.startOffset(), markedText.endOffset(), description, hash));
102 }
103 }
104
addMarker(const Position & start,const Position & end,DocumentMarker::MarkerType type,const String & description,uint32_t hash)105 void DocumentMarkerController::addMarker(const Position& start, const Position& end, DocumentMarker::MarkerType type, const String& description, uint32_t hash)
106 {
107 // Use a TextIterator to visit the potentially multiple nodes the range covers.
108 for (TextIterator markedText(start, end); !markedText.atEnd(); markedText.advance()) {
109 addMarker(markedText.startContainer(), DocumentMarker(type, markedText.startOffset(), markedText.endOffset(), description, hash));
110 }
111 }
112
addTextMatchMarker(const Range * range,bool activeMatch)113 void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activeMatch)
114 {
115 // Use a TextIterator to visit the potentially multiple nodes the range covers.
116 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
117 unsigned startOffset = markedText.startOffset();
118 unsigned endOffset = markedText.endOffset();
119 addMarker(markedText.startContainer(), DocumentMarker(startOffset, endOffset, activeMatch));
120 if (endOffset > startOffset) {
121 // Rendered rects for markers in WebKit are not populated until each time
122 // the markers are painted. However, we need it to happen sooner, because
123 // the whole purpose of tickmarks on the scrollbar is to show where
124 // matches off-screen are (that haven't been painted yet).
125 Node* node = markedText.startContainer();
126 DocumentMarkerVector markers = markersFor(node);
127 toRenderedDocumentMarker(markers[markers.size() - 1])->setRenderedRect(range->boundingBox());
128 }
129 }
130 }
131
prepareForDestruction()132 void DocumentMarkerController::prepareForDestruction()
133 {
134 clear();
135 }
136
removeMarkers(TextIterator & markedText,DocumentMarker::MarkerTypes markerTypes,RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)137 void DocumentMarkerController::removeMarkers(TextIterator& markedText, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
138 {
139 for (; !markedText.atEnd(); markedText.advance()) {
140 if (!possiblyHasMarkers(markerTypes))
141 return;
142 ASSERT(!m_markers.isEmpty());
143
144 int startOffset = markedText.startOffset();
145 int endOffset = markedText.endOffset();
146 removeMarkers(markedText.startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
147 }
148 }
149
removeMarkers(Range * range,DocumentMarker::MarkerTypes markerTypes,RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)150 void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
151 {
152 TextIterator markedText(range);
153 DocumentMarkerController::removeMarkers(markedText, markerTypes, shouldRemovePartiallyOverlappingMarker);
154 }
155
removeMarkers(const Position & start,const Position & end,DocumentMarker::MarkerTypes markerTypes,RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)156 void DocumentMarkerController::removeMarkers(const Position& start, const Position& end, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
157 {
158 TextIterator markedText(start, end);
159 DocumentMarkerController::removeMarkers(markedText, markerTypes, shouldRemovePartiallyOverlappingMarker);
160 }
161
startsFurther(const OwnPtrWillBeMember<RenderedDocumentMarker> & lhv,const DocumentMarker * rhv)162 static bool startsFurther(const OwnPtrWillBeMember<RenderedDocumentMarker>& lhv, const DocumentMarker* rhv)
163 {
164 return lhv->startOffset() < rhv->startOffset();
165 }
166
startsAfter(const OwnPtrWillBeMember<RenderedDocumentMarker> & marker,size_t startOffset)167 static bool startsAfter(const OwnPtrWillBeMember<RenderedDocumentMarker>& marker, size_t startOffset)
168 {
169 return marker->startOffset() < startOffset;
170 }
171
endsBefore(size_t startOffset,const OwnPtrWillBeMember<RenderedDocumentMarker> & rhv)172 static bool endsBefore(size_t startOffset, const OwnPtrWillBeMember<RenderedDocumentMarker>& rhv)
173 {
174 return startOffset < rhv->endOffset();
175 }
176
compareByStart(const RawPtrWillBeMember<DocumentMarker> & lhv,const RawPtrWillBeMember<DocumentMarker> & rhv)177 static bool compareByStart(const RawPtrWillBeMember<DocumentMarker>& lhv, const RawPtrWillBeMember<DocumentMarker>& rhv)
178 {
179 return lhv->startOffset() < rhv->startOffset();
180 }
181
doesNotOverlap(const OwnPtrWillBeMember<RenderedDocumentMarker> & lhv,const DocumentMarker * rhv)182 static bool doesNotOverlap(const OwnPtrWillBeMember<RenderedDocumentMarker>& lhv, const DocumentMarker* rhv)
183 {
184 return lhv->endOffset() < rhv->startOffset();
185 }
186
doesNotInclude(const OwnPtrWillBeMember<RenderedDocumentMarker> & marker,size_t startOffset)187 static bool doesNotInclude(const OwnPtrWillBeMember<RenderedDocumentMarker>& marker, size_t startOffset)
188 {
189 return marker->endOffset() < startOffset;
190 }
191
192 // Markers are stored in order sorted by their start offset.
193 // Markers of the same type do not overlap each other.
194
addMarker(Node * node,const DocumentMarker & newMarker)195 void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker)
196 {
197 ASSERT(newMarker.endOffset() >= newMarker.startOffset());
198 if (newMarker.endOffset() == newMarker.startOffset())
199 return;
200
201 m_possiblyExistingMarkerTypes.add(newMarker.type());
202
203 OwnPtrWillBeMember<MarkerLists>& markers = m_markers.add(node, nullptr).storedValue->value;
204 if (!markers) {
205 markers = adoptPtrWillBeNoop(new MarkerLists);
206 markers->grow(DocumentMarker::MarkerTypeIndexesCount);
207 }
208
209 DocumentMarker::MarkerTypeIndex markerListIndex = MarkerTypeToMarkerIndex(newMarker.type());
210 if (!markers->at(markerListIndex)) {
211 markers->insert(markerListIndex, adoptPtrWillBeNoop(new MarkerList));
212 }
213
214 OwnPtrWillBeMember<MarkerList>& list = markers->at(markerListIndex);
215 if (list->isEmpty() || list->last()->endOffset() < newMarker.startOffset()) {
216 list->append(RenderedDocumentMarker::create(newMarker));
217 } else {
218 DocumentMarker toInsert(newMarker);
219 if (toInsert.type() != DocumentMarker::TextMatch) {
220 mergeOverlapping(list.get(), toInsert);
221 } else {
222 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), &toInsert, startsFurther);
223 list->insert(pos - list->begin(), RenderedDocumentMarker::create(toInsert));
224 }
225 }
226
227 // repaint the affected node
228 if (node->renderer())
229 node->renderer()->setShouldDoFullPaintInvalidation(true);
230 }
231
mergeOverlapping(MarkerList * list,DocumentMarker & toInsert)232 void DocumentMarkerController::mergeOverlapping(MarkerList* list, DocumentMarker& toInsert)
233 {
234 MarkerList::iterator firstOverlapping = std::lower_bound(list->begin(), list->end(), &toInsert, doesNotOverlap);
235 size_t index = firstOverlapping - list->begin();
236 list->insert(index, RenderedDocumentMarker::create(toInsert));
237 MarkerList::iterator inserted = list->begin() + index;
238 firstOverlapping = inserted + 1;
239 for (MarkerList::iterator i = firstOverlapping; i != list->end() && (*i)->startOffset() <= (*inserted)->endOffset(); ) {
240 (*inserted)->setStartOffset(std::min((*inserted)->startOffset(), (*i)->startOffset()));
241 (*inserted)->setEndOffset(std::max((*inserted)->endOffset(), (*i)->endOffset()));
242 list->remove(i - list->begin());
243 }
244 }
245
246 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is
247 // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
copyMarkers(Node * srcNode,unsigned startOffset,int length,Node * dstNode,int delta)248 void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta)
249 {
250 if (length <= 0)
251 return;
252
253 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
254 return;
255 ASSERT(!m_markers.isEmpty());
256
257 MarkerLists* markers = m_markers.get(srcNode);
258 if (!markers)
259 return;
260
261 bool docDirty = false;
262 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
263 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
264 if (!list)
265 continue;
266
267 unsigned endOffset = startOffset + length - 1;
268 MarkerList::iterator startPos = std::lower_bound(list->begin(), list->end(), startOffset, doesNotInclude);
269 for (MarkerList::iterator i = startPos; i != list->end(); ++i) {
270 DocumentMarker* marker = i->get();
271
272 // stop if we are now past the specified range
273 if (marker->startOffset() > endOffset)
274 break;
275
276 // pin the marker to the specified range and apply the shift delta
277 docDirty = true;
278 if (marker->startOffset() < startOffset)
279 marker->setStartOffset(startOffset);
280 if (marker->endOffset() > endOffset)
281 marker->setEndOffset(endOffset);
282 marker->shiftOffsets(delta);
283
284 addMarker(dstNode, *marker);
285 }
286 }
287
288 // repaint the affected node
289 if (docDirty && dstNode->renderer())
290 dstNode->renderer()->setShouldDoFullPaintInvalidation(true);
291 }
292
removeMarkers(Node * node,unsigned startOffset,int length,DocumentMarker::MarkerTypes markerTypes,RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)293 void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
294 {
295 if (length <= 0)
296 return;
297
298 if (!possiblyHasMarkers(markerTypes))
299 return;
300 ASSERT(!(m_markers.isEmpty()));
301
302 MarkerLists* markers = m_markers.get(node);
303 if (!markers)
304 return;
305
306 bool docDirty = false;
307 size_t emptyListsCount = 0;
308 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
309 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
310 if (!list || list->isEmpty()) {
311 if (list.get() && list->isEmpty())
312 list.clear();
313 ++emptyListsCount;
314 continue;
315 }
316 if (!markerTypes.contains((*list->begin())->type()))
317 continue;
318 unsigned endOffset = startOffset + length;
319 MarkerList::iterator startPos = std::upper_bound(list->begin(), list->end(), startOffset, endsBefore);
320 for (MarkerList::iterator i = startPos; i != list->end(); ) {
321 DocumentMarker marker(*i->get());
322
323 // markers are returned in order, so stop if we are now past the specified range
324 if (marker.startOffset() >= endOffset)
325 break;
326
327 // at this point we know that marker and target intersect in some way
328 docDirty = true;
329
330 // pitch the old marker
331 list->remove(i - list->begin());
332
333 if (shouldRemovePartiallyOverlappingMarker) {
334 // Stop here. Don't add resulting slices back.
335 continue;
336 }
337
338 // add either of the resulting slices that are left after removing target
339 if (startOffset > marker.startOffset()) {
340 DocumentMarker newLeft = marker;
341 newLeft.setEndOffset(startOffset);
342 size_t insertIndex = i - list->begin();
343 list->insert(insertIndex, RenderedDocumentMarker::create(newLeft));
344 // Move to the marker after the inserted one.
345 i = list->begin() + insertIndex + 1;
346 }
347 if (marker.endOffset() > endOffset) {
348 DocumentMarker newRight = marker;
349 newRight.setStartOffset(endOffset);
350 size_t insertIndex = i - list->begin();
351 list->insert(insertIndex, RenderedDocumentMarker::create(newRight));
352 // Move to the marker after the inserted one.
353 i = list->begin() + insertIndex + 1;
354 }
355 }
356
357 if (list->isEmpty()) {
358 list.clear();
359 ++emptyListsCount;
360 }
361 }
362
363 if (emptyListsCount == DocumentMarker::MarkerTypeIndexesCount) {
364 m_markers.remove(node);
365 if (m_markers.isEmpty())
366 m_possiblyExistingMarkerTypes = 0;
367 }
368
369 // repaint the affected node
370 if (docDirty && node->renderer())
371 node->renderer()->setShouldDoFullPaintInvalidation(true);
372 }
373
markerContainingPoint(const LayoutPoint & point,DocumentMarker::MarkerType markerType)374 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
375 {
376 if (!possiblyHasMarkers(markerType))
377 return 0;
378 ASSERT(!(m_markers.isEmpty()));
379
380 // outer loop: process each node that contains any markers
381 MarkerMap::iterator end = m_markers.end();
382 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
383 // inner loop; process each marker in this node
384 MarkerLists* markers = nodeIterator->value.get();
385 OwnPtrWillBeMember<MarkerList>& list = (*markers)[MarkerTypeToMarkerIndex(markerType)];
386 unsigned markerCount = list.get() ? list->size() : 0;
387 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
388 RenderedDocumentMarker* marker = list->at(markerIndex).get();
389 if (marker->contains(point))
390 return marker;
391 }
392 }
393
394 return 0;
395 }
396
markersFor(Node * node,DocumentMarker::MarkerTypes markerTypes)397 DocumentMarkerVector DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes)
398 {
399 DocumentMarkerVector result;
400
401 MarkerLists* markers = m_markers.get(node);
402 if (!markers)
403 return result;
404
405 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
406 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
407 if (!list || list->isEmpty() || !markerTypes.contains((*list->begin())->type()))
408 continue;
409
410 for (size_t i = 0; i < list->size(); ++i)
411 result.append(list->at(i).get());
412 }
413
414 std::sort(result.begin(), result.end(), compareByStart);
415 return result;
416 }
417
markers()418 DocumentMarkerVector DocumentMarkerController::markers()
419 {
420 DocumentMarkerVector result;
421 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) {
422 MarkerLists* markers = i->value.get();
423 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
424 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
425 for (size_t j = 0; list.get() && j < list->size(); ++j)
426 result.append(list->at(j).get());
427 }
428 }
429 std::sort(result.begin(), result.end(), compareByStart);
430 return result;
431 }
432
markersInRange(Range * range,DocumentMarker::MarkerTypes markerTypes)433 DocumentMarkerVector DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
434 {
435 if (!possiblyHasMarkers(markerTypes))
436 return DocumentMarkerVector();
437
438 DocumentMarkerVector foundMarkers;
439
440 Node* startContainer = range->startContainer();
441 ASSERT(startContainer);
442 Node* endContainer = range->endContainer();
443 ASSERT(endContainer);
444
445 Node* pastLastNode = range->pastLastNode();
446 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
447 DocumentMarkerVector markers = markersFor(node);
448 DocumentMarkerVector::const_iterator end = markers.end();
449 for (DocumentMarkerVector::const_iterator it = markers.begin(); it != end; ++it) {
450 DocumentMarker* marker = *it;
451 if (!markerTypes.contains(marker->type()))
452 continue;
453 if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
454 continue;
455 if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
456 continue;
457 foundMarkers.append(marker);
458 }
459 }
460 return foundMarkers;
461 }
462
renderedRectsForMarkers(DocumentMarker::MarkerType markerType)463 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
464 {
465 Vector<IntRect> result;
466
467 if (!possiblyHasMarkers(markerType))
468 return result;
469 ASSERT(!(m_markers.isEmpty()));
470
471 // outer loop: process each node
472 MarkerMap::iterator end = m_markers.end();
473 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
474 // inner loop; process each marker in this node
475 MarkerLists* markers = nodeIterator->value.get();
476 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
477 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
478 if (!list || list->isEmpty() || (*list->begin())->type() != markerType)
479 continue;
480 for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) {
481 RenderedDocumentMarker* marker = list->at(markerIndex).get();
482 if (!marker->isRendered())
483 continue;
484 result.append(marker->renderedRect());
485 }
486 }
487 }
488
489 return result;
490 }
491
trace(Visitor * visitor)492 void DocumentMarkerController::trace(Visitor* visitor)
493 {
494 #if ENABLE(OILPAN)
495 visitor->trace(m_markers);
496 #endif
497 }
498
removeMarkers(Node * node,DocumentMarker::MarkerTypes markerTypes)499 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
500 {
501 if (!possiblyHasMarkers(markerTypes))
502 return;
503 ASSERT(!m_markers.isEmpty());
504
505 MarkerMap::iterator iterator = m_markers.find(node);
506 if (iterator != m_markers.end())
507 removeMarkersFromList(iterator, markerTypes);
508 }
509
removeMarkers(const MarkerRemoverPredicate & shouldRemoveMarker)510 void DocumentMarkerController::removeMarkers(const MarkerRemoverPredicate& shouldRemoveMarker)
511 {
512 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) {
513 MarkerLists* markers = i->value.get();
514 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
515 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
516
517 WillBeHeapVector<RawPtrWillBeMember<RenderedDocumentMarker> > markersToBeRemoved;
518 for (size_t j = 0; list.get() && j < list->size(); ++j) {
519 if (i->key->isTextNode() && shouldRemoveMarker(*list->at(j).get(), static_cast<const Text&>(*i->key)))
520 markersToBeRemoved.append(list->at(j).get());
521 }
522
523 for (size_t j = 0; j < markersToBeRemoved.size(); ++j)
524 list->remove(list->find(markersToBeRemoved[j].get()));
525 }
526 }
527 }
528
removeMarkers(DocumentMarker::MarkerTypes markerTypes)529 void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
530 {
531 if (!possiblyHasMarkers(markerTypes))
532 return;
533 ASSERT(!m_markers.isEmpty());
534
535 Vector<const Node*> nodesWithMarkers;
536 copyKeysToVector(m_markers, nodesWithMarkers);
537 unsigned size = nodesWithMarkers.size();
538 for (unsigned i = 0; i < size; ++i) {
539 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]);
540 if (iterator != m_markers.end())
541 removeMarkersFromList(iterator, markerTypes);
542 }
543
544 m_possiblyExistingMarkerTypes.remove(markerTypes);
545 }
546
removeMarkersFromList(MarkerMap::iterator iterator,DocumentMarker::MarkerTypes markerTypes)547 void DocumentMarkerController::removeMarkersFromList(MarkerMap::iterator iterator, DocumentMarker::MarkerTypes markerTypes)
548 {
549 bool needsRepainting = false;
550 bool nodeCanBeRemoved;
551
552 size_t emptyListsCount = 0;
553 if (markerTypes == DocumentMarker::AllMarkers()) {
554 needsRepainting = true;
555 nodeCanBeRemoved = true;
556 } else {
557 MarkerLists* markers = iterator->value.get();
558
559 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
560 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
561 if (!list || list->isEmpty()) {
562 if (list.get() && list->isEmpty())
563 list.clear();
564 ++emptyListsCount;
565 continue;
566 }
567 if (markerTypes.contains((*list->begin())->type())) {
568 list->clear();
569 list.clear();
570 ++emptyListsCount;
571 needsRepainting = true;
572 }
573 }
574
575 nodeCanBeRemoved = emptyListsCount == DocumentMarker::MarkerTypeIndexesCount;
576 }
577
578 if (needsRepainting) {
579 if (RenderObject* renderer = iterator->key->renderer())
580 renderer->setShouldDoFullPaintInvalidation(true);
581 }
582
583 if (nodeCanBeRemoved) {
584 m_markers.remove(iterator);
585 if (m_markers.isEmpty())
586 m_possiblyExistingMarkerTypes = 0;
587 }
588 }
589
repaintMarkers(DocumentMarker::MarkerTypes markerTypes)590 void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
591 {
592 if (!possiblyHasMarkers(markerTypes))
593 return;
594 ASSERT(!m_markers.isEmpty());
595
596 // outer loop: process each markered node in the document
597 MarkerMap::iterator end = m_markers.end();
598 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
599 const Node* node = i->key;
600
601 // inner loop: process each marker in the current node
602 MarkerLists* markers = i->value.get();
603 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
604 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
605 if (!list || list->isEmpty() || !markerTypes.contains((*list->begin())->type()))
606 continue;
607
608 // cause the node to be redrawn
609 if (RenderObject* renderer = node->renderer()) {
610 renderer->setShouldDoFullPaintInvalidation(true);
611 break;
612 }
613 }
614 }
615 }
616
invalidateRenderedRectsForMarkersInRect(const LayoutRect & r)617 void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r)
618 {
619 // outer loop: process each markered node in the document
620 MarkerMap::iterator end = m_markers.end();
621 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
622
623 // inner loop: process each rect in the current node
624 MarkerLists* markers = i->value.get();
625 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
626 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
627 for (size_t markerIndex = 0; list.get() && markerIndex < list->size(); ++markerIndex)
628 list->at(markerIndex)->invalidate(r);
629 }
630 }
631 }
632
shiftMarkers(Node * node,unsigned startOffset,int delta)633 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
634 {
635 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
636 return;
637 ASSERT(!m_markers.isEmpty());
638
639 MarkerLists* markers = m_markers.get(node);
640 if (!markers)
641 return;
642
643 bool docDirty = false;
644 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
645 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
646 if (!list)
647 continue;
648 MarkerList::iterator startPos = std::lower_bound(list->begin(), list->end(), startOffset, startsAfter);
649 for (MarkerList::iterator marker = startPos; marker != list->end(); ++marker) {
650 #if ENABLE(ASSERT)
651 int startOffset = (*marker)->startOffset();
652 ASSERT(startOffset + delta >= 0);
653 #endif
654 (*marker)->shiftOffsets(delta);
655 docDirty = true;
656
657 // Marker moved, so previously-computed rendered rectangle is now invalid
658 (*marker)->invalidate();
659 }
660 }
661
662 // repaint the affected node
663 if (docDirty && node->renderer())
664 node->renderer()->setShouldDoFullPaintInvalidation(true);
665 }
666
setMarkersActive(Range * range,bool active)667 void DocumentMarkerController::setMarkersActive(Range* range, bool active)
668 {
669 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
670 return;
671 ASSERT(!m_markers.isEmpty());
672
673 Node* startContainer = range->startContainer();
674 Node* endContainer = range->endContainer();
675
676 Node* pastLastNode = range->pastLastNode();
677
678 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
679 int startOffset = node == startContainer ? range->startOffset() : 0;
680 int endOffset = node == endContainer ? range->endOffset() : INT_MAX;
681 setMarkersActive(node, startOffset, endOffset, active);
682 }
683 }
684
setMarkersActive(Node * node,unsigned startOffset,unsigned endOffset,bool active)685 void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active)
686 {
687 MarkerLists* markers = m_markers.get(node);
688 if (!markers)
689 return;
690
691 bool docDirty = false;
692 OwnPtrWillBeMember<MarkerList>& list = (*markers)[MarkerTypeToMarkerIndex(DocumentMarker::TextMatch)];
693 if (!list)
694 return;
695 MarkerList::iterator startPos = std::upper_bound(list->begin(), list->end(), startOffset, endsBefore);
696 for (MarkerList::iterator marker = startPos; marker != list->end(); ++marker) {
697
698 // Markers are returned in order, so stop if we are now past the specified range.
699 if ((*marker)->startOffset() >= endOffset)
700 break;
701
702 (*marker)->setActiveMatch(active);
703 docDirty = true;
704 }
705
706 // repaint the affected node
707 if (docDirty && node->renderer())
708 node->renderer()->setShouldDoFullPaintInvalidation(true);
709 }
710
hasMarkers(Range * range,DocumentMarker::MarkerTypes markerTypes)711 bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes)
712 {
713 if (!possiblyHasMarkers(markerTypes))
714 return false;
715 ASSERT(!m_markers.isEmpty());
716
717 Node* startContainer = range->startContainer();
718 ASSERT(startContainer);
719 Node* endContainer = range->endContainer();
720 ASSERT(endContainer);
721
722 Node* pastLastNode = range->pastLastNode();
723 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
724 DocumentMarkerVector markers = markersFor(node);
725 DocumentMarkerVector::const_iterator end = markers.end();
726 for (DocumentMarkerVector::const_iterator it = markers.begin(); it != end; ++it) {
727 DocumentMarker* marker = *it;
728 if (!markerTypes.contains(marker->type()))
729 continue;
730 if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
731 continue;
732 if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
733 continue;
734 return true;
735 }
736 }
737 return false;
738 }
739
740 #ifndef NDEBUG
showMarkers() const741 void DocumentMarkerController::showMarkers() const
742 {
743 fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
744 MarkerMap::const_iterator end = m_markers.end();
745 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
746 const Node* node = nodeIterator->key;
747 fprintf(stderr, "%p", node);
748 MarkerLists* markers = m_markers.get(node);
749 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) {
750 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex];
751 for (unsigned markerIndex = 0; list.get() && markerIndex < list->size(); ++markerIndex) {
752 DocumentMarker* marker = list->at(markerIndex).get();
753 fprintf(stderr, " %d:[%d:%d](%d)", marker->type(), marker->startOffset(), marker->endOffset(), marker->activeMatch());
754 }
755 }
756
757 fprintf(stderr, "\n");
758 }
759 }
760 #endif
761
762 } // namespace blink
763
764 #ifndef NDEBUG
showDocumentMarkers(const blink::DocumentMarkerController * controller)765 void showDocumentMarkers(const blink::DocumentMarkerController* controller)
766 {
767 if (controller)
768 controller->showMarkers();
769 }
770 #endif
771