b77d0517b83b8da0e6a1340015941c7353b91758
[WebKit-https.git] / Source / WebCore / dom / DocumentMarkerController.cpp
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 "DocumentMarkerController.h"
29
30 #include "Node.h"
31 #include "NodeTraversal.h"
32 #include "Range.h"
33 #include "RenderObject.h"
34 #include "RenderedDocumentMarker.h"
35 #include "TextIterator.h"
36
37 #ifndef NDEBUG
38 #include <stdio.h>
39 #endif
40
41 namespace WebCore {
42
43 inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
44 {
45     return m_possiblyExistingMarkerTypes.intersects(types);
46 }
47
48 DocumentMarkerController::DocumentMarkerController()
49     : m_possiblyExistingMarkerTypes(0)
50 {
51 }
52
53 void DocumentMarkerController::detach()
54 {
55     m_possiblyExistingMarkerTypes = 0;
56     if (m_markers.isEmpty())
57         return;
58     deleteAllValues(m_markers);
59     m_markers.clear();
60 }
61
62 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description)
63 {
64     // Use a TextIterator to visit the potentially multiple nodes the range covers.
65     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
66         RefPtr<Range> textPiece = markedText.range();
67         int exception = 0;
68         addMarker(textPiece->startContainer(exception),
69                   DocumentMarker(type, textPiece->startOffset(exception), textPiece->endOffset(exception), description));
70     }
71 }
72
73 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type)
74 {
75     // Use a TextIterator to visit the potentially multiple nodes the range covers.
76     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
77         RefPtr<Range> textPiece = markedText.range();
78         int exception = 0;
79         addMarker(textPiece->startContainer(exception),
80                   DocumentMarker(type, textPiece->startOffset(exception), textPiece->endOffset(exception)));
81     }
82
83 }
84
85 void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type)
86 {
87     addMarker(node, DocumentMarker(type, startOffset, startOffset + length));
88 }
89
90 void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, PassRefPtr<DocumentMarkerDetails> details)
91 {
92     addMarker(node, DocumentMarker(type, startOffset, startOffset + length, details));
93 }
94
95
96 void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activeMatch)
97 {
98     // Use a TextIterator to visit the potentially multiple nodes the range covers.
99     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
100         RefPtr<Range> textPiece = markedText.range();
101         int exception = 0;
102         unsigned startOffset = textPiece->startOffset(exception);
103         unsigned endOffset = textPiece->endOffset(exception);
104         addMarker(textPiece->startContainer(exception), DocumentMarker(startOffset, endOffset, activeMatch));
105         if (endOffset > startOffset) {
106             // Rendered rects for markers in WebKit are not populated until each time
107             // the markers are painted. However, we need it to happen sooner, because
108             // the whole purpose of tickmarks on the scrollbar is to show where
109             // matches off-screen are (that haven't been painted yet).
110             Node* node = textPiece->startContainer(exception);
111             Vector<DocumentMarker*> markers = markersFor(node);
112             static_cast<RenderedDocumentMarker*>(markers[markers.size() - 1])->setRenderedRect(range->boundingBox());
113         }
114     }
115 }
116
117 void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
118 {
119     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
120         if (!possiblyHasMarkers(markerTypes))
121             return;
122         ASSERT(!m_markers.isEmpty());
123
124         RefPtr<Range> textPiece = markedText.range();
125         int startOffset = textPiece->startOffset();
126         int endOffset = textPiece->endOffset();
127         removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
128     }
129 }
130
131 // Markers are stored in order sorted by their start offset.
132 // Markers of the same type do not overlap each other.
133
134 void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker) 
135 {
136     ASSERT(newMarker.endOffset() >= newMarker.startOffset());
137     if (newMarker.endOffset() == newMarker.startOffset())
138         return;
139
140     m_possiblyExistingMarkerTypes.add(newMarker.type());
141
142     MarkerList* list = m_markers.get(node);
143
144     if (!list) {
145         list = new MarkerList;
146         list->append(RenderedDocumentMarker(newMarker));
147         m_markers.set(node, list);
148     } else {
149         RenderedDocumentMarker toInsert(newMarker);
150         size_t numMarkers = list->size();
151         size_t i;
152         // Iterate over all markers whose start offset is less than or equal to the new marker's.
153         // If one of them is of the same type as the new marker and touches it or intersects with it
154         // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
155         for (i = 0; i < numMarkers; ++i) {
156             DocumentMarker marker = list->at(i);
157             if (marker.startOffset() > toInsert.startOffset())
158                 break;
159             if (marker.type() == toInsert.type() && marker.endOffset() >= toInsert.startOffset()) {
160                 toInsert.setStartOffset(marker.startOffset());
161                 list->remove(i);
162                 numMarkers--;
163                 break;
164             }
165         }
166         size_t j = i;
167         // Iterate over all markers whose end offset is less than or equal to the new marker's,
168         // removing markers of the same type as the new marker which touch it or intersect with it,
169         // adjusting the new marker's end offset to cover them if necessary.
170         while (j < numMarkers) {
171             DocumentMarker marker = list->at(j);
172             if (marker.startOffset() > toInsert.endOffset())
173                 break;
174             if (marker.type() == toInsert.type()) {
175                 list->remove(j);
176                 if (toInsert.endOffset() <= marker.endOffset()) {
177                     toInsert.setEndOffset(marker.endOffset());
178                     break;
179                 }
180                 numMarkers--;
181             } else
182                 j++;
183         }
184         // At this point i points to the node before which we want to insert.
185         list->insert(i, RenderedDocumentMarker(toInsert));
186     }
187
188     // repaint the affected node
189     if (node->renderer())
190         node->renderer()->repaint();
191 }
192
193 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies.  The shift is
194 // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
195 void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta)
196 {
197     if (length <= 0)
198         return;
199
200     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
201         return;
202     ASSERT(!m_markers.isEmpty());
203
204     MarkerList* list = m_markers.get(srcNode);
205     if (!list)
206         return;
207
208     bool docDirty = false;
209     unsigned endOffset = startOffset + length - 1;
210     for (size_t i = 0; i != list->size(); ++i) {
211         DocumentMarker marker = list->at(i);
212
213         // stop if we are now past the specified range
214         if (marker.startOffset() > endOffset)
215             break;
216
217         // skip marker that is before the specified range or is the wrong type
218         if (marker.endOffset() < startOffset)
219             continue;
220
221         // pin the marker to the specified range and apply the shift delta
222         docDirty = true;
223         if (marker.startOffset() < startOffset)
224             marker.setStartOffset(startOffset);
225         if (marker.endOffset() > endOffset)
226             marker.setEndOffset(endOffset);
227         marker.shiftOffsets(delta);
228
229         addMarker(dstNode, marker);
230     }
231
232     // repaint the affected node
233     if (docDirty && dstNode->renderer())
234         dstNode->renderer()->repaint();
235 }
236
237 void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
238 {
239     if (length <= 0)
240         return;
241
242     if (!possiblyHasMarkers(markerTypes))
243         return;
244     ASSERT(!(m_markers.isEmpty()));
245
246     MarkerList* list = m_markers.get(node);
247     if (!list)
248         return;
249
250     bool docDirty = false;
251     unsigned endOffset = startOffset + length;
252     for (size_t i = 0; i < list->size();) {
253         DocumentMarker marker = list->at(i);
254
255         // markers are returned in order, so stop if we are now past the specified range
256         if (marker.startOffset() >= endOffset)
257             break;
258
259         // skip marker that is wrong type or before target
260         if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
261             i++;
262             continue;
263         }
264
265         // at this point we know that marker and target intersect in some way
266         docDirty = true;
267
268         // pitch the old marker
269         list->remove(i);
270
271         if (shouldRemovePartiallyOverlappingMarker)
272             // Stop here. Don't add resulting slices back.
273             continue;
274
275         // add either of the resulting slices that are left after removing target
276         if (startOffset > marker.startOffset()) {
277             DocumentMarker newLeft = marker;
278             newLeft.setEndOffset(startOffset);
279             list->insert(i, RenderedDocumentMarker(newLeft));
280             // i now points to the newly-inserted node, but we want to skip that one
281             i++;
282         }
283         if (marker.endOffset() > endOffset) {
284             DocumentMarker newRight = marker;
285             newRight.setStartOffset(endOffset);
286             list->insert(i, RenderedDocumentMarker(newRight));
287             // i now points to the newly-inserted node, but we want to skip that one
288             i++;
289         }
290     }
291
292     if (list->isEmpty()) {
293         m_markers.remove(node);
294         delete list;
295     }
296     
297     if (m_markers.isEmpty())
298         m_possiblyExistingMarkerTypes = 0;
299
300     // repaint the affected node
301     if (docDirty && node->renderer())
302         node->renderer()->repaint();
303 }
304
305 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
306 {
307     if (!possiblyHasMarkers(markerType))
308         return 0;
309     ASSERT(!(m_markers.isEmpty()));
310
311     // outer loop: process each node that contains any markers
312     MarkerMap::iterator end = m_markers.end();
313     for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
314         // inner loop; process each marker in this node
315         MarkerList* list = nodeIterator->value;
316         unsigned markerCount = list->size();
317         for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
318             RenderedDocumentMarker& marker = list->at(markerIndex);
319
320             // skip marker that is wrong type
321             if (marker.type() != markerType)
322                 continue;
323
324             if (marker.contains(point))
325                 return &marker;
326         }
327     }
328
329     return 0;
330 }
331
332 Vector<DocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes)
333 {
334     Vector<DocumentMarker*> result;
335     MarkerList* list = m_markers.get(node);
336     if (!list)
337         return result;
338
339     for (size_t i = 0; i < list->size(); ++i) {
340         if (markerTypes.contains(list->at(i).type()))
341             result.append(&(list->at(i)));
342     }
343
344     return result;
345 }
346
347 // FIXME: Should be removed after all relevant patches are landed
348 Vector<DocumentMarker> DocumentMarkerController::markersForNode(Node* node)
349 {
350     Vector<DocumentMarker> result;
351     MarkerList* list = m_markers.get(node);
352     if (!list)
353         return result;
354
355     for (size_t i = 0; i < list->size(); ++i)
356         result.append(list->at(i));
357
358     return result;
359 }
360
361 Vector<DocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
362 {
363     if (!possiblyHasMarkers(markerTypes))
364         return Vector<DocumentMarker*>();
365
366     Vector<DocumentMarker*> foundMarkers;
367
368     Node* startContainer = range->startContainer();
369     ASSERT(startContainer);
370     Node* endContainer = range->endContainer();
371     ASSERT(endContainer);
372
373     Node* pastLastNode = range->pastLastNode();
374     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
375         Vector<DocumentMarker*> markers = markersFor(node);
376         Vector<DocumentMarker*>::const_iterator end = markers.end();
377         for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) {
378             DocumentMarker* marker = *it;
379             if (!markerTypes.contains(marker->type()))
380                 continue;
381             if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
382                 continue;
383             if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
384                 continue;
385             foundMarkers.append(marker);
386         }
387     }
388     return foundMarkers;
389 }
390
391 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
392 {
393     Vector<IntRect> result;
394
395     if (!possiblyHasMarkers(markerType))
396         return result;
397     ASSERT(!(m_markers.isEmpty()));
398
399     // outer loop: process each node
400     MarkerMap::iterator end = m_markers.end();
401     for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
402         // inner loop; process each marker in this node
403         MarkerList* list = nodeIterator->value;
404         unsigned markerCount = list->size();
405         for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
406             const RenderedDocumentMarker& marker = list->at(markerIndex);
407
408             // skip marker that is wrong type
409             if (marker.type() != markerType)
410                 continue;
411
412             if (!marker.isRendered())
413                 continue;
414
415             result.append(marker.renderedRect());
416         }
417     }
418
419     return result;
420 }
421
422 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
423 {
424     if (!possiblyHasMarkers(markerTypes))
425         return;
426     ASSERT(!m_markers.isEmpty());
427     
428     MarkerMap::iterator iterator = m_markers.find(node);
429     if (iterator != m_markers.end())
430         removeMarkersFromList(node, iterator->value, markerTypes);
431 }
432
433 void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
434 {
435     if (!possiblyHasMarkers(markerTypes))
436         return;
437     ASSERT(!m_markers.isEmpty());
438
439     // outer loop: process each markered node in the document
440     MarkerMap markerMapCopy = m_markers;
441     MarkerMap::iterator end = markerMapCopy.end();
442     for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i)
443         removeMarkersFromList(i->key.get(), i->value, markerTypes);
444     m_possiblyExistingMarkerTypes.remove(markerTypes);
445 }
446
447 // This function may release node and vectorPair.
448 void DocumentMarkerController::removeMarkersFromList(Node* node, MarkerList* list, DocumentMarker::MarkerTypes markerTypes)
449 {
450     if (markerTypes == DocumentMarker::AllMarkers()) {
451         delete list;
452         m_markers.remove(node);
453         if (RenderObject* renderer = node->renderer())
454             renderer->repaint();
455     } else {
456         bool needsRepaint = false;
457         for (size_t i = 0; i != list->size();) {
458             DocumentMarker marker = list->at(i);
459
460             // skip nodes that are not of the specified type
461             if (!markerTypes.contains(marker.type())) {
462                 ++i;
463                 continue;
464             }
465
466             // pitch the old marker
467             list->remove(i);
468             needsRepaint = true;
469             // i now is the index of the next marker
470         }
471
472         // Redraw the node if it changed. Do this before the node is removed from m_markers, since
473         // m_markers might contain the last reference to the node.
474         if (needsRepaint) {
475             RenderObject* renderer = node->renderer();
476             if (renderer)
477                 renderer->repaint();
478         }
479
480         // delete the node's list if it is now empty
481         if (list->isEmpty()) {
482             m_markers.remove(node);
483             delete list;
484         }
485     }
486     if (m_markers.isEmpty())
487         m_possiblyExistingMarkerTypes = 0;
488 }
489
490 void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
491 {
492     if (!possiblyHasMarkers(markerTypes))
493         return;
494     ASSERT(!m_markers.isEmpty());
495
496     // outer loop: process each markered node in the document
497     MarkerMap::iterator end = m_markers.end();
498     for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
499         Node* node = i->key.get();
500
501         // inner loop: process each marker in the current node
502         MarkerList* list = i->value;
503         bool nodeNeedsRepaint = false;
504         for (size_t i = 0; i != list->size(); ++i) {
505             DocumentMarker marker = list->at(i);
506
507             // skip nodes that are not of the specified type
508             if (markerTypes.contains(marker.type())) {
509                 nodeNeedsRepaint = true;
510                 break;
511             }
512         }
513
514         if (!nodeNeedsRepaint)
515             continue;
516
517         // cause the node to be redrawn
518         if (RenderObject* renderer = node->renderer())
519             renderer->repaint();
520     }
521 }
522
523 void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r)
524 {
525     // outer loop: process each markered node in the document
526     MarkerMap::iterator end = m_markers.end();
527     for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
528
529         // inner loop: process each rect in the current node
530         MarkerList* list = i->value;
531         for (size_t listIndex = 0; listIndex < list->size(); ++listIndex)
532             list->at(listIndex).invalidate(r);
533     }
534 }
535
536 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
537 {
538     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
539         return;
540     ASSERT(!m_markers.isEmpty());
541
542     MarkerList* list = m_markers.get(node);
543     if (!list)
544         return;
545
546     bool docDirty = false;
547     for (size_t i = 0; i != list->size(); ++i) {
548         RenderedDocumentMarker& marker = list->at(i);
549         if (marker.startOffset() >= startOffset) {
550             ASSERT((int)marker.startOffset() + delta >= 0);
551             marker.shiftOffsets(delta);
552             docDirty = true;
553
554             // Marker moved, so previously-computed rendered rectangle is now invalid
555             marker.invalidate();
556         }
557     }
558
559     // repaint the affected node
560     if (docDirty && node->renderer())
561         node->renderer()->repaint();
562 }
563
564 void DocumentMarkerController::setMarkersActive(Range* range, bool active)
565 {
566     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
567         return;
568     ASSERT(!m_markers.isEmpty());
569
570     ExceptionCode ec = 0;
571     Node* startContainer = range->startContainer(ec);
572     Node* endContainer = range->endContainer(ec);
573
574     Node* pastLastNode = range->pastLastNode();
575     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
576         int startOffset = node == startContainer ? range->startOffset(ec) : 0;
577         int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX;
578         setMarkersActive(node, startOffset, endOffset, active);
579     }
580 }
581
582 void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active)
583 {
584     MarkerList* list = m_markers.get(node);
585     if (!list)
586         return;
587
588     bool docDirty = false;
589     for (size_t i = 0; i != list->size(); ++i) {
590         DocumentMarker& marker = list->at(i);
591
592         // Markers are returned in order, so stop if we are now past the specified range.
593         if (marker.startOffset() >= endOffset)
594             break;
595
596         // Skip marker that is wrong type or before target.
597         if (marker.endOffset() < startOffset || marker.type() != DocumentMarker::TextMatch)
598             continue;
599
600         marker.setActiveMatch(active);
601         docDirty = true;
602     }
603
604     // repaint the affected node
605     if (docDirty && node->renderer())
606         node->renderer()->repaint();
607 }
608
609 bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes)
610 {
611     if (!possiblyHasMarkers(markerTypes))
612         return false;
613     ASSERT(!m_markers.isEmpty());
614
615     Node* startContainer = range->startContainer();
616     ASSERT(startContainer);
617     Node* endContainer = range->endContainer();
618     ASSERT(endContainer);
619
620     Node* pastLastNode = range->pastLastNode();
621     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
622         Vector<DocumentMarker*> markers = markersFor(node);
623         Vector<DocumentMarker*>::const_iterator end = markers.end();
624         for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) {
625             DocumentMarker* marker = *it;
626             if (!markerTypes.contains(marker->type()))
627                 continue;
628             if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
629                 continue;
630             if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
631                 continue;
632             return true;
633         }
634     }
635     return false;
636 }
637
638 void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
639 {
640     if (!possiblyHasMarkers(markerTypes))
641         return;
642     ASSERT(!m_markers.isEmpty());
643
644     Node* startContainer = range->startContainer();
645     Node* endContainer = range->endContainer();
646
647     Node* pastLastNode = range->pastLastNode();
648     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
649         unsigned startOffset = node == startContainer ? range->startOffset() : 0;
650         unsigned endOffset = node == endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max();
651         MarkerList* list = m_markers.get(node);
652         if (!list)
653             continue;
654
655         for (size_t i = 0; i < list->size(); ++i) {
656             DocumentMarker& marker = list->at(i);
657
658             // markers are returned in order, so stop if we are now past the specified range
659             if (marker.startOffset() >= endOffset)
660                 break;
661
662             // skip marker that is wrong type or before target
663             if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
664                 i++;
665                 continue;
666             }
667
668             marker.clearDetails();
669         }
670     }
671 }
672
673 #ifndef NDEBUG
674 void DocumentMarkerController::showMarkers() const
675 {
676     fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
677     MarkerMap::const_iterator end = m_markers.end();
678     for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
679         Node* node = nodeIterator->key.get();
680         fprintf(stderr, "%p", node);
681         MarkerList* list = nodeIterator->value;
682         for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) {
683             const DocumentMarker& marker = list->at(markerIndex);
684             fprintf(stderr, " %d:[%d:%d](%d)", marker.type(), marker.startOffset(), marker.endOffset(), marker.activeMatch());
685         }
686
687         fprintf(stderr, "\n");
688     }
689 }
690 #endif
691
692 } // namespace WebCore
693
694
695 #ifndef NDEBUG
696 void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
697 {
698     if (controller)
699         controller->showMarkers();
700 }
701 #endif