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