Text markers don't paint on simple lines
[WebKit-https.git] / Source / WebCore / dom / DocumentMarkerController.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  *           (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  */
26
27 #include "config.h"
28 #include "DocumentMarkerController.h"
29
30 #include "NodeTraversal.h"
31 #include "Range.h"
32 #include "RenderBlockFlow.h"
33 #include "RenderText.h"
34 #include "RenderedDocumentMarker.h"
35 #include "TextIterator.h"
36 #include <stdio.h>
37
38 namespace WebCore {
39
40 inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
41 {
42     return m_possiblyExistingMarkerTypes.intersects(types);
43 }
44
45 DocumentMarkerController::DocumentMarkerController()
46     : m_possiblyExistingMarkerTypes(0)
47 {
48 }
49
50 DocumentMarkerController::~DocumentMarkerController()
51 {
52 }
53
54 void DocumentMarkerController::detach()
55 {
56     m_markers.clear();
57     m_possiblyExistingMarkerTypes = 0;
58 }
59
60 void 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
69 void 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
79 void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type)
80 {
81     addMarker(node, DocumentMarker(type, startOffset, startOffset + length));
82 }
83
84 void 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
90 void 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
110 #if PLATFORM(IOS)
111 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, String description, const Vector<String>& interpretations, const RetainPtr<id>& metadata)
112 {
113     // Use a TextIterator to visit the potentially multiple nodes the range covers.
114     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
115         RefPtr<Range> textPiece = markedText.range();
116         addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description, interpretations, metadata));
117     }
118 }
119
120 void DocumentMarkerController::addDictationPhraseWithAlternativesMarker(Range* range, const Vector<String>& interpretations)
121 {
122     ASSERT(interpretations.size() > 1);
123     if (interpretations.size() <= 1)
124         return;
125
126     size_t numberOfAlternatives = interpretations.size() - 1;
127     // Use a TextIterator to visit the potentially multiple nodes the range covers.
128     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
129         RefPtr<Range> textPiece = markedText.range();
130         DocumentMarker marker(DocumentMarker::DictationPhraseWithAlternatives, textPiece->startOffset(), textPiece->endOffset(), "", Vector<String>(numberOfAlternatives), RetainPtr<id>());
131         for (size_t i = 0; i < numberOfAlternatives; ++i)
132             marker.setAlternative(interpretations[i + 1], i);
133         addMarker(textPiece->startContainer(), marker);
134     }
135 }
136
137 void DocumentMarkerController::addDictationResultMarker(Range* range, const RetainPtr<id>& metadata)
138 {
139     // Use a TextIterator to visit the potentially multiple nodes the range covers.
140     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
141         RefPtr<Range> textPiece = markedText.range();
142         addMarker(textPiece->startContainer(), DocumentMarker(DocumentMarker::DictationResult, textPiece->startOffset(), textPiece->endOffset(), String(), Vector<String>(), metadata));
143     }
144 }
145 #endif
146
147 void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
148 {
149     for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
150         if (!possiblyHasMarkers(markerTypes))
151             return;
152         ASSERT(!m_markers.isEmpty());
153
154         RefPtr<Range> textPiece = markedText.range();
155         int startOffset = textPiece->startOffset();
156         int endOffset = textPiece->endOffset();
157         removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
158     }
159 }
160
161 // Markers are stored in order sorted by their start offset.
162 // Markers of the same type do not overlap each other.
163
164 void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker) 
165 {
166     ASSERT(newMarker.endOffset() >= newMarker.startOffset());
167     if (newMarker.endOffset() == newMarker.startOffset())
168         return;
169
170     if (auto* renderer = node->renderer()) {
171         // FIXME: Factor the marker painting code out of InlineTextBox and teach simple line layout to use it.
172         if (renderer->isText())
173             toRenderText(*renderer).ensureLineBoxes();
174         else if (renderer->isRenderBlockFlow())
175             toRenderBlockFlow(*renderer).ensureLineBoxes();
176     }
177
178     m_possiblyExistingMarkerTypes.add(newMarker.type());
179
180     std::unique_ptr<MarkerList>& list = m_markers.add(node, nullptr).iterator->value;
181
182     if (!list) {
183         list = std::make_unique<MarkerList>();
184         list->append(RenderedDocumentMarker(newMarker));
185 #if PLATFORM(IOS)
186     } else if (newMarker.type() == DocumentMarker::DictationPhraseWithAlternatives || newMarker.type() == DocumentMarker::DictationResult) {
187         // We don't merge dictation markers.
188         size_t i;
189         size_t numberOfMarkers = list->size();
190         for (i = 0; i < numberOfMarkers; ++i) {
191             DocumentMarker marker = list->at(i);
192             if (marker.startOffset() > newMarker.startOffset())
193                 break;
194         }
195         list->insert(i, RenderedDocumentMarker(newMarker));
196 #endif
197     } else {
198         RenderedDocumentMarker toInsert(newMarker);
199         size_t numMarkers = list->size();
200         size_t i;
201         // Iterate over all markers whose start offset is less than or equal to the new marker's.
202         // If one of them is of the same type as the new marker and touches it or intersects with it
203         // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
204         for (i = 0; i < numMarkers; ++i) {
205             DocumentMarker marker = list->at(i);
206             if (marker.startOffset() > toInsert.startOffset())
207                 break;
208             if (marker.type() == toInsert.type() && marker.endOffset() >= toInsert.startOffset()) {
209                 toInsert.setStartOffset(marker.startOffset());
210                 list->remove(i);
211                 numMarkers--;
212                 break;
213             }
214         }
215         size_t j = i;
216         // Iterate over all markers whose end offset is less than or equal to the new marker's,
217         // removing markers of the same type as the new marker which touch it or intersect with it,
218         // adjusting the new marker's end offset to cover them if necessary.
219         while (j < numMarkers) {
220             DocumentMarker marker = list->at(j);
221             if (marker.startOffset() > toInsert.endOffset())
222                 break;
223             if (marker.type() == toInsert.type()) {
224                 list->remove(j);
225                 if (toInsert.endOffset() <= marker.endOffset()) {
226                     toInsert.setEndOffset(marker.endOffset());
227                     break;
228                 }
229                 numMarkers--;
230             } else
231                 j++;
232         }
233         // At this point i points to the node before which we want to insert.
234         list->insert(i, RenderedDocumentMarker(toInsert));
235     }
236
237     // repaint the affected node
238     if (node->renderer())
239         node->renderer()->repaint();
240 }
241
242 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies.  The shift is
243 // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
244 void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta)
245 {
246     if (length <= 0)
247         return;
248
249     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
250         return;
251     ASSERT(!m_markers.isEmpty());
252
253     MarkerList* list = m_markers.get(srcNode);
254     if (!list)
255         return;
256
257     bool docDirty = false;
258     unsigned endOffset = startOffset + length - 1;
259     for (size_t i = 0; i != list->size(); ++i) {
260         DocumentMarker marker = list->at(i);
261
262         // stop if we are now past the specified range
263         if (marker.startOffset() > endOffset)
264             break;
265
266         // skip marker that is before the specified range or is the wrong type
267         if (marker.endOffset() < startOffset)
268             continue;
269
270         // pin the marker to the specified range and apply the shift delta
271         docDirty = true;
272         if (marker.startOffset() < startOffset)
273             marker.setStartOffset(startOffset);
274         if (marker.endOffset() > endOffset)
275             marker.setEndOffset(endOffset);
276         marker.shiftOffsets(delta);
277
278         addMarker(dstNode, marker);
279     }
280
281     // repaint the affected node
282     if (docDirty && dstNode->renderer())
283         dstNode->renderer()->repaint();
284 }
285
286 void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
287 {
288     if (length <= 0)
289         return;
290
291     if (!possiblyHasMarkers(markerTypes))
292         return;
293     ASSERT(!(m_markers.isEmpty()));
294
295     MarkerList* list = m_markers.get(node);
296     if (!list)
297         return;
298
299     bool docDirty = false;
300     unsigned endOffset = startOffset + length;
301     for (size_t i = 0; i < list->size();) {
302         DocumentMarker marker = list->at(i);
303
304         // markers are returned in order, so stop if we are now past the specified range
305         if (marker.startOffset() >= endOffset)
306             break;
307
308         // skip marker that is wrong type or before target
309         if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
310             i++;
311             continue;
312         }
313
314         // at this point we know that marker and target intersect in some way
315         docDirty = true;
316
317         // pitch the old marker
318         list->remove(i);
319
320         if (shouldRemovePartiallyOverlappingMarker)
321             // Stop here. Don't add resulting slices back.
322             continue;
323
324         // add either of the resulting slices that are left after removing target
325         if (startOffset > marker.startOffset()) {
326             DocumentMarker newLeft = marker;
327             newLeft.setEndOffset(startOffset);
328             list->insert(i, RenderedDocumentMarker(newLeft));
329             // i now points to the newly-inserted node, but we want to skip that one
330             i++;
331         }
332         if (marker.endOffset() > endOffset) {
333             DocumentMarker newRight = marker;
334             newRight.setStartOffset(endOffset);
335             list->insert(i, RenderedDocumentMarker(newRight));
336             // i now points to the newly-inserted node, but we want to skip that one
337             i++;
338         }
339     }
340
341     if (list->isEmpty()) {
342         m_markers.remove(node);
343         if (m_markers.isEmpty())
344             m_possiblyExistingMarkerTypes = 0;
345     }
346
347     // repaint the affected node
348     if (docDirty && node->renderer())
349         node->renderer()->repaint();
350 }
351
352 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
353 {
354     if (!possiblyHasMarkers(markerType))
355         return 0;
356     ASSERT(!(m_markers.isEmpty()));
357
358     // outer loop: process each node that contains any markers
359     MarkerMap::iterator end = m_markers.end();
360     for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
361         // inner loop; process each marker in this node
362         MarkerList* list = nodeIterator->value.get();
363         unsigned markerCount = list->size();
364         for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
365             RenderedDocumentMarker& marker = list->at(markerIndex);
366
367             // skip marker that is wrong type
368             if (marker.type() != markerType)
369                 continue;
370
371             if (marker.contains(point))
372                 return &marker;
373         }
374     }
375
376     return 0;
377 }
378
379 Vector<DocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes)
380 {
381     Vector<DocumentMarker*> result;
382     MarkerList* list = m_markers.get(node);
383     if (!list)
384         return result;
385
386     for (size_t i = 0; i < list->size(); ++i) {
387         if (markerTypes.contains(list->at(i).type()))
388             result.append(&(list->at(i)));
389     }
390
391     return result;
392 }
393
394 Vector<DocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
395 {
396     if (!possiblyHasMarkers(markerTypes))
397         return Vector<DocumentMarker*>();
398
399     Vector<DocumentMarker*> foundMarkers;
400
401     Node* startContainer = range->startContainer();
402     ASSERT(startContainer);
403     Node* endContainer = range->endContainer();
404     ASSERT(endContainer);
405
406     Node* pastLastNode = range->pastLastNode();
407     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
408         Vector<DocumentMarker*> markers = markersFor(node);
409         Vector<DocumentMarker*>::const_iterator end = markers.end();
410         for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) {
411             DocumentMarker* marker = *it;
412             if (!markerTypes.contains(marker->type()))
413                 continue;
414             if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
415                 continue;
416             if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
417                 continue;
418             foundMarkers.append(marker);
419         }
420     }
421     return foundMarkers;
422 }
423
424 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
425 {
426     Vector<IntRect> result;
427
428     if (!possiblyHasMarkers(markerType))
429         return result;
430     ASSERT(!(m_markers.isEmpty()));
431
432     // outer loop: process each node
433     MarkerMap::iterator end = m_markers.end();
434     for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
435         // inner loop; process each marker in this node
436         MarkerList* list = nodeIterator->value.get();
437         unsigned markerCount = list->size();
438         for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
439             const RenderedDocumentMarker& marker = list->at(markerIndex);
440
441             // skip marker that is wrong type
442             if (marker.type() != markerType)
443                 continue;
444
445             if (!marker.isRendered())
446                 continue;
447
448             result.append(marker.renderedRect());
449         }
450     }
451
452     return result;
453 }
454
455 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
456 {
457     if (!possiblyHasMarkers(markerTypes))
458         return;
459     ASSERT(!m_markers.isEmpty());
460     
461     MarkerMap::iterator iterator = m_markers.find(node);
462     if (iterator != m_markers.end())
463         removeMarkersFromList(iterator, markerTypes);
464 }
465
466 void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
467 {
468     if (!possiblyHasMarkers(markerTypes))
469         return;
470     ASSERT(!m_markers.isEmpty());
471
472     Vector<RefPtr<Node>> nodesWithMarkers;
473     copyKeysToVector(m_markers, nodesWithMarkers);
474     unsigned size = nodesWithMarkers.size();
475     for (unsigned i = 0; i < size; ++i) {
476         MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]);
477         if (iterator != m_markers.end())
478             removeMarkersFromList(iterator, markerTypes);
479     }
480
481     m_possiblyExistingMarkerTypes.remove(markerTypes);
482 }
483
484 void DocumentMarkerController::removeMarkersFromList(MarkerMap::iterator iterator, DocumentMarker::MarkerTypes markerTypes)
485 {
486     bool needsRepainting = false;
487     bool listCanBeRemoved;
488
489     if (markerTypes == DocumentMarker::AllMarkers()) {
490         needsRepainting = true;
491         listCanBeRemoved = true;
492     } else {
493         MarkerList* list = iterator->value.get();
494
495         for (size_t i = 0; i != list->size(); ) {
496             DocumentMarker marker = list->at(i);
497
498             // skip nodes that are not of the specified type
499             if (!markerTypes.contains(marker.type())) {
500                 ++i;
501                 continue;
502             }
503
504             // pitch the old marker
505             list->remove(i);
506             needsRepainting = true;
507             // i now is the index of the next marker
508         }
509
510         listCanBeRemoved = list->isEmpty();
511     }
512
513     if (needsRepainting) {
514         if (auto renderer = iterator->key->renderer())
515             renderer->repaint();
516     }
517
518     if (listCanBeRemoved) {
519         m_markers.remove(iterator);
520         if (m_markers.isEmpty())
521             m_possiblyExistingMarkerTypes = 0;
522     }
523 }
524
525 void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
526 {
527     if (!possiblyHasMarkers(markerTypes))
528         return;
529     ASSERT(!m_markers.isEmpty());
530
531     // outer loop: process each markered node in the document
532     MarkerMap::iterator end = m_markers.end();
533     for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
534         Node* node = i->key.get();
535
536         // inner loop: process each marker in the current node
537         MarkerList* list = i->value.get();
538         bool nodeNeedsRepaint = false;
539         for (size_t i = 0; i != list->size(); ++i) {
540             DocumentMarker marker = list->at(i);
541
542             // skip nodes that are not of the specified type
543             if (markerTypes.contains(marker.type())) {
544                 nodeNeedsRepaint = true;
545                 break;
546             }
547         }
548
549         if (!nodeNeedsRepaint)
550             continue;
551
552         // cause the node to be redrawn
553         if (auto renderer = node->renderer())
554             renderer->repaint();
555     }
556 }
557
558 void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r)
559 {
560     // outer loop: process each markered node in the document
561     MarkerMap::iterator end = m_markers.end();
562     for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
563
564         // inner loop: process each rect in the current node
565         MarkerList* list = i->value.get();
566         for (size_t listIndex = 0; listIndex < list->size(); ++listIndex)
567             list->at(listIndex).invalidate(r);
568     }
569 }
570
571 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
572 {
573     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
574         return;
575     ASSERT(!m_markers.isEmpty());
576
577     MarkerList* list = m_markers.get(node);
578     if (!list)
579         return;
580
581     bool docDirty = false;
582     for (size_t i = 0; i != list->size(); ) {
583         RenderedDocumentMarker& marker = list->at(i);
584 #if PLATFORM(IOS)
585         int targetStartOffset = marker.startOffset() + delta;
586         int targetEndOffset = marker.endOffset() + delta;
587         if (targetStartOffset >= node->maxCharacterOffset() || targetEndOffset <= 0) {
588             list->remove(i);
589             continue;
590         }
591 #endif
592         if (marker.startOffset() >= startOffset) {
593             ASSERT((int)marker.startOffset() + delta >= 0);
594             marker.shiftOffsets(delta);
595             docDirty = true;
596
597             // Marker moved, so previously-computed rendered rectangle is now invalid
598             marker.invalidate();
599 #if !PLATFORM(IOS)
600         }
601 #else
602         // FIXME: Inserting text inside a DocumentMarker does not grow the marker.
603         // See <https://bugs.webkit.org/show_bug.cgi?id=62504>.
604         } else if (marker.endOffset() > startOffset) {
605             if (marker.endOffset() + delta <= marker.startOffset()) {
606                 list->remove(i);
607                 continue;
608             }
609             marker.setEndOffset(targetEndOffset < node->maxCharacterOffset() ? targetEndOffset : node->maxCharacterOffset());
610             docDirty = true;
611
612             // Marker moved, so previously-computed rendered rectangle is now invalid
613             marker.invalidate();
614         }
615 #endif
616         ++i;
617     }
618
619     // repaint the affected node
620     if (docDirty && node->renderer())
621         node->renderer()->repaint();
622 }
623
624 void DocumentMarkerController::setMarkersActive(Range* range, bool active)
625 {
626     if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
627         return;
628     ASSERT(!m_markers.isEmpty());
629
630     Node* startContainer = range->startContainer();
631     Node* endContainer = range->endContainer();
632
633     Node* pastLastNode = range->pastLastNode();
634
635     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
636         int startOffset = node == startContainer ? range->startOffset() : 0;
637         int endOffset = node == endContainer ? range->endOffset() : INT_MAX;
638         setMarkersActive(node, startOffset, endOffset, active);
639     }
640 }
641
642 void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active)
643 {
644     MarkerList* list = m_markers.get(node);
645     if (!list)
646         return;
647
648     bool docDirty = false;
649     for (size_t i = 0; i != list->size(); ++i) {
650         DocumentMarker& marker = list->at(i);
651
652         // Markers are returned in order, so stop if we are now past the specified range.
653         if (marker.startOffset() >= endOffset)
654             break;
655
656         // Skip marker that is wrong type or before target.
657         if (marker.endOffset() < startOffset || marker.type() != DocumentMarker::TextMatch)
658             continue;
659
660         marker.setActiveMatch(active);
661         docDirty = true;
662     }
663
664     // repaint the affected node
665     if (docDirty && node->renderer())
666         node->renderer()->repaint();
667 }
668
669 bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes)
670 {
671     if (!possiblyHasMarkers(markerTypes))
672         return false;
673     ASSERT(!m_markers.isEmpty());
674
675     Node* startContainer = range->startContainer();
676     ASSERT(startContainer);
677     Node* endContainer = range->endContainer();
678     ASSERT(endContainer);
679
680     Node* pastLastNode = range->pastLastNode();
681     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
682         Vector<DocumentMarker*> markers = markersFor(node);
683         Vector<DocumentMarker*>::const_iterator end = markers.end();
684         for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) {
685             DocumentMarker* marker = *it;
686             if (!markerTypes.contains(marker->type()))
687                 continue;
688             if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
689                 continue;
690             if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
691                 continue;
692             return true;
693         }
694     }
695     return false;
696 }
697
698 void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
699 {
700     if (!possiblyHasMarkers(markerTypes))
701         return;
702     ASSERT(!m_markers.isEmpty());
703
704     Node* startContainer = range->startContainer();
705     Node* endContainer = range->endContainer();
706
707     Node* pastLastNode = range->pastLastNode();
708     for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(node)) {
709         unsigned startOffset = node == startContainer ? range->startOffset() : 0;
710         unsigned endOffset = node == endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max();
711         MarkerList* list = m_markers.get(node);
712         if (!list)
713             continue;
714
715         for (size_t i = 0; i < list->size(); ++i) {
716             DocumentMarker& marker = list->at(i);
717
718             // markers are returned in order, so stop if we are now past the specified range
719             if (marker.startOffset() >= endOffset)
720                 break;
721
722             // skip marker that is wrong type or before target
723             if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
724                 i++;
725                 continue;
726             }
727
728             marker.clearDetails();
729         }
730     }
731 }
732
733 #ifndef NDEBUG
734 void DocumentMarkerController::showMarkers() const
735 {
736     fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
737     MarkerMap::const_iterator end = m_markers.end();
738     for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
739         Node* node = nodeIterator->key.get();
740         fprintf(stderr, "%p", node);
741         MarkerList* list = nodeIterator->value.get();
742         for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) {
743             const DocumentMarker& marker = list->at(markerIndex);
744             fprintf(stderr, " %d:[%d:%d](%d)", marker.type(), marker.startOffset(), marker.endOffset(), marker.activeMatch());
745         }
746
747         fprintf(stderr, "\n");
748     }
749 }
750 #endif
751
752 } // namespace WebCore
753
754 #ifndef NDEBUG
755 void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
756 {
757     if (controller)
758         controller->showMarkers();
759 }
760 #endif