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 ▮ 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