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