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