DocumentMarkerController.cpp 22.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2001 Dirk Mueller (mueller@kde.org)
 *           (C) 2006 Alexey Proskuryakov (ap@webkit.org)
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"
#include "DocumentMarkerController.h"

#include "Node.h"
#include "Range.h"
32
#include "RenderObject.h"
33
#include "RenderedDocumentMarker.h"
34 35 36 37
#include "TextIterator.h"

namespace WebCore {

38 39
inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
{
40
    return m_possiblyExistingMarkerTypes.intersects(types);
41 42 43 44 45 46 47
}

DocumentMarkerController::DocumentMarkerController()
    : m_possiblyExistingMarkerTypes(0)
{
}

48 49
void DocumentMarkerController::detach()
{
50
    m_possiblyExistingMarkerTypes = 0;
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    if (m_markers.isEmpty())
        return;
    deleteAllValues(m_markers);
    m_markers.clear();
}

void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, String description)
{
    // Use a TextIterator to visit the potentially multiple nodes the range covers.
    for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
        RefPtr<Range> textPiece = markedText.range();
        int exception = 0;
        DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description, false};
        addMarker(textPiece->startContainer(exception), marker);
    }
}

68
void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
69
{
70
    for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
71 72 73 74
        if (!possiblyHasMarkers(markerTypes))
            return;
        ASSERT(!m_markers.isEmpty());

75 76 77 78
        RefPtr<Range> textPiece = markedText.range();
        int startOffset = textPiece->startOffset();
        int endOffset = textPiece->endOffset();
        removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
79 80 81 82 83 84
    }
}

// Markers are stored in order sorted by their start offset.
// Markers of the same type do not overlap each other.

85
void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker) 
86 87 88 89 90
{
    ASSERT(newMarker.endOffset >= newMarker.startOffset);
    if (newMarker.endOffset == newMarker.startOffset)
        return;

91
    m_possiblyExistingMarkerTypes.add(newMarker.type);
92

93
    MarkerList* list = m_markers.get(node);
94

95 96 97 98
    if (!list) {
        list = new MarkerList;
        list->append(RenderedDocumentMarker(newMarker));
        m_markers.set(node, list);
99
    } else {
100 101
        RenderedDocumentMarker toInsert(newMarker);
        size_t numMarkers = list->size();
102 103 104 105 106
        size_t i;
        // Iterate over all markers whose start offset is less than or equal to the new marker's.
        // If one of them is of the same type as the new marker and touches it or intersects with it
        // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
        for (i = 0; i < numMarkers; ++i) {
107 108
            DocumentMarker marker = list->at(i);
            if (marker.startOffset > toInsert.startOffset)
109
                break;
110 111 112
            if (marker.type == toInsert.type && marker.endOffset >= toInsert.startOffset) {
                toInsert.startOffset = marker.startOffset;
                list->remove(i);
113 114 115 116 117 118 119 120 121
                numMarkers--;
                break;
            }
        }
        size_t j = i;
        // Iterate over all markers whose end offset is less than or equal to the new marker's,
        // removing markers of the same type as the new marker which touch it or intersect with it,
        // adjusting the new marker's end offset to cover them if necessary.
        while (j < numMarkers) {
122 123
            DocumentMarker marker = list->at(j);
            if (marker.startOffset > toInsert.endOffset)
124
                break;
125 126 127 128
            if (marker.type == toInsert.type) {
                list->remove(j);
                if (toInsert.endOffset <= marker.endOffset) {
                    toInsert.endOffset = marker.endOffset;
129 130 131 132 133 134 135
                    break;
                }
                numMarkers--;
            } else
                j++;
        }
        // At this point i points to the node before which we want to insert.
136
        list->insert(i, RenderedDocumentMarker(toInsert));
137 138 139 140 141 142 143 144 145
    }

    // repaint the affected node
    if (node->renderer())
        node->renderer()->repaint();
}

// copies markers from srcNode to dstNode, applying the specified shift delta to the copies.  The shift is
// useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
146
void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta)
147 148 149 150
{
    if (length <= 0)
        return;

151
    if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
152 153 154
        return;
    ASSERT(!m_markers.isEmpty());

155 156
    MarkerList* list = m_markers.get(srcNode);
    if (!list)
157 158 159 160
        return;

    bool docDirty = false;
    unsigned endOffset = startOffset + length - 1;
161 162
    for (size_t i = 0; i != list->size(); ++i) {
        DocumentMarker marker = list->at(i);
163 164 165 166 167 168

        // stop if we are now past the specified range
        if (marker.startOffset > endOffset)
            break;

        // skip marker that is before the specified range or is the wrong type
169
        if (marker.endOffset < startOffset)
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
            continue;

        // pin the marker to the specified range and apply the shift delta
        docDirty = true;
        if (marker.startOffset < startOffset)
            marker.startOffset = startOffset;
        if (marker.endOffset > endOffset)
            marker.endOffset = endOffset;
        marker.startOffset += delta;
        marker.endOffset += delta;

        addMarker(dstNode, marker);
    }

    // repaint the affected node
    if (docDirty && dstNode->renderer())
        dstNode->renderer()->repaint();
}

189
void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
190 191 192 193
{
    if (length <= 0)
        return;

194 195 196 197
    if (!possiblyHasMarkers(markerTypes))
        return;
    ASSERT(!(m_markers.isEmpty()));

198 199
    MarkerList* list = m_markers.get(node);
    if (!list)
200 201 202 203
        return;

    bool docDirty = false;
    unsigned endOffset = startOffset + length;
204 205
    for (size_t i = 0; i < list->size();) {
        DocumentMarker marker = list->at(i);
206 207 208 209 210 211

        // markers are returned in order, so stop if we are now past the specified range
        if (marker.startOffset >= endOffset)
            break;

        // skip marker that is wrong type or before target
212
        if (marker.endOffset <= startOffset || !markerTypes.contains(marker.type)) {
213 214 215 216 217 218 219
            i++;
            continue;
        }

        // at this point we know that marker and target intersect in some way
        docDirty = true;

220 221
        // pitch the old marker
        list->remove(i);
222

223 224 225 226
        if (shouldRemovePartiallyOverlappingMarker)
            // Stop here. Don't add resulting slices back.
            continue;

227 228 229 230
        // add either of the resulting slices that are left after removing target
        if (startOffset > marker.startOffset) {
            DocumentMarker newLeft = marker;
            newLeft.endOffset = startOffset;
231
            list->insert(i, RenderedDocumentMarker(newLeft));
232 233 234 235 236 237
            // i now points to the newly-inserted node, but we want to skip that one
            i++;
        }
        if (marker.endOffset > endOffset) {
            DocumentMarker newRight = marker;
            newRight.startOffset = endOffset;
238
            list->insert(i, RenderedDocumentMarker(newRight));
239 240 241 242 243
            // i now points to the newly-inserted node, but we want to skip that one
            i++;
        }
    }

244
    if (list->isEmpty()) {
245
        m_markers.remove(node);
246
        delete list;
247
    }
248 249 250
    
    if (m_markers.isEmpty())
        m_possiblyExistingMarkerTypes = 0;
251 252 253 254 255 256 257 258

    // repaint the affected node
    if (docDirty && node->renderer())
        node->renderer()->repaint();
}

DocumentMarker* DocumentMarkerController::markerContainingPoint(const IntPoint& point, DocumentMarker::MarkerType markerType)
{
259 260 261 262
    if (!possiblyHasMarkers(markerType))
        return 0;
    ASSERT(!(m_markers.isEmpty()));

263 264 265 266
    // outer loop: process each node that contains any markers
    MarkerMap::iterator end = m_markers.end();
    for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
        // inner loop; process each marker in this node
267 268
        MarkerList* list = nodeIterator->second;
        unsigned markerCount = list->size();
269
        for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
270
            RenderedDocumentMarker& marker = list->at(markerIndex);
271 272

            // skip marker that is wrong type
273
            if (marker.type != markerType)
274 275
                continue;

276
            if (marker.contains(point))
277 278 279 280 281 282 283 284 285
                return &marker;
        }
    }

    return 0;
}

Vector<DocumentMarker> DocumentMarkerController::markersForNode(Node* node)
{
286 287 288 289 290 291 292 293 294
    Vector<DocumentMarker> result;
    MarkerList* list = m_markers.get(node);
    if (!list)
        return result;

    for (size_t i = 0; i < list->size(); ++i)
        result.append(list->at(i));

    return result;
295 296
}

297
Vector<DocumentMarker> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
298
{
299
    if (!possiblyHasMarkers(markerTypes))
300 301 302 303 304 305 306 307 308 309 310 311 312 313
        return Vector<DocumentMarker>();

    Vector<DocumentMarker> foundMarkers;

    Node* startContainer = range->startContainer();
    ASSERT(startContainer);
    Node* endContainer = range->endContainer();
    ASSERT(endContainer);

    Node* pastLastNode = range->pastLastNode();
    for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
        Vector<DocumentMarker> markers = markersForNode(node);
        Vector<DocumentMarker>::const_iterator end = markers.end();
        for (Vector<DocumentMarker>::const_iterator it = markers.begin(); it != end; ++it) {
314
            if (!markerTypes.contains(it->type))
315 316 317 318 319 320 321 322 323 324 325
                continue;
            if (node == startContainer && it->endOffset <= static_cast<unsigned>(range->startOffset()))
                continue;
            if (node == endContainer && it->startOffset >= static_cast<unsigned>(range->endOffset()))
                continue;
            foundMarkers.append(*it);
        }
    }
    return foundMarkers;
}

326 327 328 329
Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
{
    Vector<IntRect> result;

330 331 332 333
    if (!possiblyHasMarkers(markerType))
        return result;
    ASSERT(!(m_markers.isEmpty()));

334 335 336 337
    // outer loop: process each node
    MarkerMap::iterator end = m_markers.end();
    for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
        // inner loop; process each marker in this node
338 339
        MarkerList* list = nodeIterator->second;
        unsigned markerCount = list->size();
340
        for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
341
            const RenderedDocumentMarker& marker = list->at(markerIndex);
342 343

            // skip marker that is wrong type
344
            if (marker.type != markerType)
345 346
                continue;

347
            if (!marker.isRendered())
348 349
                continue;

350
            result.append(marker.renderedRect());
351 352 353 354 355 356
        }
    }

    return result;
}

357
void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
358
{
359 360 361 362
    if (!possiblyHasMarkers(markerTypes))
        return;
    ASSERT(!m_markers.isEmpty());
    
363 364
    MarkerMap::iterator iterator = m_markers.find(node);
    if (iterator != m_markers.end())
365
        removeMarkersFromList(node, iterator->second, markerTypes);
366 367
}

368
void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
369
{
370 371 372 373
    if (!possiblyHasMarkers(markerTypes))
        return;
    ASSERT(!m_markers.isEmpty());

374 375 376
    // outer loop: process each markered node in the document
    MarkerMap markerMapCopy = m_markers;
    MarkerMap::iterator end = markerMapCopy.end();
377 378
    for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i)
        removeMarkersFromList(i->first.get(), i->second, markerTypes);
379
    m_possiblyExistingMarkerTypes.remove(markerTypes);
380 381 382
}

// This function may release node and vectorPair.
383
void DocumentMarkerController::removeMarkersFromList(Node* node, MarkerList* list, DocumentMarker::MarkerTypes markerTypes)
384
{
385
    if (markerTypes == DocumentMarker::AllMarkers()) {
386
        delete list;
387 388 389 390 391
        m_markers.remove(node);
        if (RenderObject* renderer = node->renderer())
            renderer->repaint();
    } else {
        bool needsRepaint = false;
392 393
        for (size_t i = 0; i != list->size();) {
            DocumentMarker marker = list->at(i);
394 395

            // skip nodes that are not of the specified type
396
            if (!markerTypes.contains(marker.type)) {
397 398 399 400 401
                ++i;
                continue;
            }

            // pitch the old marker
402
            list->remove(i);
403 404
            needsRepaint = true;
            // i now is the index of the next marker
405 406
        }

407
        // Redraw the node if it changed. Do this before the node is removed from m_markers, since
408
        // m_markers might contain the last reference to the node.
409
        if (needsRepaint) {
410 411 412 413 414 415
            RenderObject* renderer = node->renderer();
            if (renderer)
                renderer->repaint();
        }

        // delete the node's list if it is now empty
416
        if (list->isEmpty()) {
417
            m_markers.remove(node);
418
            delete list;
419 420
        }
    }
421 422
    if (m_markers.isEmpty())
        m_possiblyExistingMarkerTypes = 0;
423 424
}

425
void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
426
{
427
    if (!possiblyHasMarkers(markerTypes))
428 429 430
        return;
    ASSERT(!m_markers.isEmpty());

431 432 433 434 435 436
    // outer loop: process each markered node in the document
    MarkerMap::iterator end = m_markers.end();
    for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
        Node* node = i->first.get();

        // inner loop: process each marker in the current node
437
        MarkerList* list = i->second;
438
        bool nodeNeedsRepaint = false;
439 440
        for (size_t i = 0; i != list->size(); ++i) {
            DocumentMarker marker = list->at(i);
441 442

            // skip nodes that are not of the specified type
443
            if (markerTypes.contains(marker.type)) {
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
                nodeNeedsRepaint = true;
                break;
            }
        }

        if (!nodeNeedsRepaint)
            continue;

        // cause the node to be redrawn
        if (RenderObject* renderer = node->renderer())
            renderer->repaint();
    }
}

void DocumentMarkerController::setRenderedRectForMarker(Node* node, const DocumentMarker& marker, const IntRect& r)
{
460 461
    MarkerList* list = m_markers.get(node);
    if (!list) {
462 463 464 465
        ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about
        return;
    }

466 467 468
    size_t markerCount = list->size();
    for (size_t markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
        RenderedDocumentMarker& m = list->at(markerIndex);
469
        if (m == marker) {
470
            m.setRenderedRect(r);
471 472 473 474 475 476 477 478 479 480 481 482 483 484
            return;
        }
    }

    ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about
}

void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const IntRect& r)
{
    // outer loop: process each markered node in the document
    MarkerMap::iterator end = m_markers.end();
    for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {

        // inner loop: process each rect in the current node
485 486 487
        MarkerList* list = i->second;
        for (size_t listIndex = 0; listIndex < list->size(); ++listIndex)
            list->at(listIndex).invalidate(r);
488 489 490
    }
}

491
void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
492
{
493
    if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
494 495 496
        return;
    ASSERT(!m_markers.isEmpty());

497 498
    MarkerList* list = m_markers.get(node);
    if (!list)
499 500 501
        return;

    bool docDirty = false;
502 503
    for (size_t i = 0; i != list->size(); ++i) {
        RenderedDocumentMarker& marker = list->at(i);
504
        if (marker.startOffset >= startOffset) {
505 506 507 508 509 510
            ASSERT((int)marker.startOffset + delta >= 0);
            marker.startOffset += delta;
            marker.endOffset += delta;
            docDirty = true;

            // Marker moved, so previously-computed rendered rectangle is now invalid
511
            marker.invalidate();
512 513 514 515 516 517 518 519 520 521
        }
    }

    // repaint the affected node
    if (docDirty && node->renderer())
        node->renderer()->repaint();
}

void DocumentMarkerController::setMarkersActive(Range* range, bool active)
{
522
    if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
523
        return;
524
    ASSERT(!m_markers.isEmpty());
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539

    ExceptionCode ec = 0;
    Node* startContainer = range->startContainer(ec);
    Node* endContainer = range->endContainer(ec);

    Node* pastLastNode = range->pastLastNode();
    for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
        int startOffset = node == startContainer ? range->startOffset(ec) : 0;
        int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX;
        setMarkersActive(node, startOffset, endOffset, active);
    }
}

void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active)
{
540 541
    MarkerList* list = m_markers.get(node);
    if (!list)
542 543 544
        return;

    bool docDirty = false;
545 546
    for (size_t i = 0; i != list->size(); ++i) {
        DocumentMarker& marker = list->at(i);
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564

        // Markers are returned in order, so stop if we are now past the specified range.
        if (marker.startOffset >= endOffset)
            break;

        // Skip marker that is wrong type or before target.
        if (marker.endOffset < startOffset || marker.type != DocumentMarker::TextMatch)
            continue;

        marker.activeMatch = active;
        docDirty = true;
    }

    // repaint the affected node
    if (docDirty && node->renderer())
        node->renderer()->repaint();
}

565 566
bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes)
{
567
    if (!possiblyHasMarkers(markerTypes))
568
        return false;
569
    ASSERT(!m_markers.isEmpty());
570 571 572 573 574 575 576 577 578 579 580

    Node* startContainer = range->startContainer();
    ASSERT(startContainer);
    Node* endContainer = range->endContainer();
    ASSERT(endContainer);

    Node* pastLastNode = range->pastLastNode();
    for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
        Vector<DocumentMarker> markers = markersForNode(node);
        Vector<DocumentMarker>::const_iterator end = markers.end();
        for (Vector<DocumentMarker>::const_iterator it = markers.begin(); it != end; ++it) {
581
            if (!markerTypes.contains(it->type))
582
                continue;
583 584 585 586 587
            if (node == startContainer && it->endOffset <= static_cast<unsigned>(range->startOffset()))
                continue;
            if (node == endContainer && it->startOffset >= static_cast<unsigned>(range->endOffset()))
                continue;
            return true;
588 589 590 591
        }
    }
    return false;
}
592 593 594 595 596 597 598 599 600 601 602 603 604

void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
{
    if (!possiblyHasMarkers(markerTypes))
        return;
    ASSERT(!m_markers.isEmpty());

    Node* startContainer = range->startContainer();
    Node* endContainer = range->endContainer();

    Node* pastLastNode = range->pastLastNode();
    for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
        unsigned startOffset = node == startContainer ? range->startOffset() : 0;
mitz@apple.com's avatar
mitz@apple.com committed
605
        unsigned endOffset = node == endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max();
606 607
        MarkerList* list = m_markers.get(node);
        if (!list)
608 609
            continue;

610 611
        for (size_t i = 0; i < list->size(); ++i) {
            DocumentMarker& marker = list->at(i);
612 613 614 615 616 617

            // markers are returned in order, so stop if we are now past the specified range
            if (marker.startOffset >= endOffset)
                break;

            // skip marker that is wrong type or before target
618
            if (marker.endOffset <= startOffset || !markerTypes.contains(marker.type)) {
619 620 621 622 623 624 625 626 627
                i++;
                continue;
            }

            marker.description = String();
        }
    }
}

628 629 630 631 632 633 634 635
#ifndef NDEBUG
void DocumentMarkerController::showMarkers() const
{
    fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
    MarkerMap::const_iterator end = m_markers.end();
    for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
        Node* node = nodeIterator->first.get();
        fprintf(stderr, "%p", node);
636 637 638
        MarkerList* list = nodeIterator->second;
        for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex)
            fprintf(stderr, " %d:[%d:%d](%d)", list->at(markerIndex).type, list->at(markerIndex).startOffset, list->at(markerIndex).endOffset, list->at(markerIndex).activeMatch);
639 640 641 642 643
        fprintf(stderr, "\n");
    }
}
#endif

644
} // namespace WebCore
645 646 647 648 649 650 651 652 653


#ifndef NDEBUG
void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
{
    if (controller)
        controller->showMarkers();
}
#endif