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.
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.
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.
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.
28 #include "DocumentMarkerController.h"
31 #include "ChromeClient.h"
32 #include "MainFrame.h"
33 #include "NodeTraversal.h"
35 #include "RenderBlockFlow.h"
36 #include "RenderLayer.h"
37 #include "RenderText.h"
38 #include "RenderedDocumentMarker.h"
39 #include "TextIterator.h"
44 inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
46 return m_possiblyExistingMarkerTypes.intersects(types);
49 DocumentMarkerController::DocumentMarkerController(Document& document)
50 : m_document(document)
54 DocumentMarkerController::~DocumentMarkerController()
58 void DocumentMarkerController::detach()
61 m_possiblyExistingMarkerTypes = 0;
64 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description)
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));
72 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type)
74 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
75 RefPtr<Range> textPiece = markedText.range();
76 addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset()));
81 void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type)
83 addMarker(node, DocumentMarker(type, startOffset, startOffset + length));
86 void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, RefPtr<DocumentMarkerDetails>&& details)
88 addMarker(node, DocumentMarker(type, startOffset, startOffset + length, WTFMove(details)));
92 void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activeMatch)
94 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
95 RefPtr<Range> textPiece = markedText.range();
96 unsigned startOffset = textPiece->startOffset();
97 unsigned endOffset = textPiece->endOffset();
98 addMarker(&textPiece->startContainer(), DocumentMarker(startOffset, endOffset, activeMatch));
103 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description, const Vector<String>& interpretations, const RetainPtr<id>& metadata)
105 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
106 RefPtr<Range> textPiece = markedText.range();
107 addMarker(&textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description, interpretations, metadata));
111 void DocumentMarkerController::addDictationPhraseWithAlternativesMarker(Range* range, const Vector<String>& interpretations)
113 ASSERT(interpretations.size() > 1);
114 if (interpretations.size() <= 1)
117 size_t numberOfAlternatives = interpretations.size() - 1;
118 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
119 RefPtr<Range> textPiece = markedText.range();
120 DocumentMarker marker(DocumentMarker::DictationPhraseWithAlternatives, textPiece->startOffset(), textPiece->endOffset(), "", Vector<String>(numberOfAlternatives), RetainPtr<id>());
121 for (size_t i = 0; i < numberOfAlternatives; ++i)
122 marker.setAlternative(interpretations[i + 1], i);
123 addMarker(&textPiece->startContainer(), marker);
127 void DocumentMarkerController::addDictationResultMarker(Range* range, const RetainPtr<id>& metadata)
129 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
130 RefPtr<Range> textPiece = markedText.range();
131 addMarker(&textPiece->startContainer(), DocumentMarker(DocumentMarker::DictationResult, textPiece->startOffset(), textPiece->endOffset(), String(), Vector<String>(), metadata));
136 void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
138 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
139 if (!possiblyHasMarkers(markerTypes))
141 ASSERT(!m_markers.isEmpty());
143 RefPtr<Range> textPiece = markedText.range();
144 int startOffset = textPiece->startOffset();
145 int endOffset = textPiece->endOffset();
146 removeMarkers(&textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
150 static void updateRenderedRectsForMarker(RenderedDocumentMarker& marker, Node& node)
152 ASSERT(!node.document().view() || !node.document().view()->needsLayout());
154 // FIXME: We should refactor this so that we don't use Range (because we only have one Node), but still share code with absoluteTextQuads().
155 RefPtr<Range> markerRange = Range::create(node.document(), &node, marker.startOffset(), &node, marker.endOffset());
158 Vector<FloatQuad> absoluteMarkerQuads;
159 markerRange->absoluteTextQuads(absoluteMarkerQuads, true);
161 Vector<FloatRect> absoluteMarkerRects;
162 absoluteMarkerRects.reserveInitialCapacity(absoluteMarkerQuads.size());
163 for (const auto& quad : absoluteMarkerQuads)
164 absoluteMarkerRects.append(quad.boundingBox());
166 marker.setUnclippedAbsoluteRects(absoluteMarkerRects);
169 void DocumentMarkerController::invalidateRectsForAllMarkers()
174 for (auto& markers : m_markers.values()) {
175 for (auto& marker : *markers)
179 if (Page* page = m_document.page())
180 page->chrome().client().didInvalidateDocumentMarkerRects();
183 void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node)
188 MarkerList* markers = m_markers.get(&node);
191 for (auto& marker : *markers)
194 if (Page* page = m_document.page())
195 page->chrome().client().didInvalidateDocumentMarkerRects();
198 static void updateMainFrameLayoutIfNeeded(Document& document)
200 Frame* frame = document.frame();
204 FrameView* mainFrameView = frame->mainFrame().view();
208 mainFrameView->updateLayoutAndStyleIfNeededRecursive();
211 void DocumentMarkerController::updateRectsForInvalidatedMarkersOfType(DocumentMarker::MarkerType markerType)
213 if (!possiblyHasMarkers(markerType))
215 ASSERT(!(m_markers.isEmpty()));
217 bool needsLayoutIfAnyRectsAreDirty = true;
219 for (auto& nodeAndMarkers : m_markers) {
220 Node& node = *nodeAndMarkers.key;
221 for (auto& marker : *nodeAndMarkers.value) {
222 if (marker.type() != markerType)
225 if (marker.isValid())
228 // We'll do up to one layout per call if we have any dirty markers.
229 if (needsLayoutIfAnyRectsAreDirty) {
230 updateMainFrameLayoutIfNeeded(m_document);
231 needsLayoutIfAnyRectsAreDirty = false;
234 updateRenderedRectsForMarker(marker, node);
239 Vector<FloatRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
241 Vector<FloatRect> result;
243 if (!possiblyHasMarkers(markerType))
245 ASSERT(!(m_markers.isEmpty()));
247 Frame* frame = m_document.frame();
250 FrameView* frameView = frame->view();
254 updateRectsForInvalidatedMarkersOfType(markerType);
256 bool isSubframe = !frame->isMainFrame();
257 IntRect subframeClipRect;
259 subframeClipRect = frameView->windowToContents(frameView->windowClipRect());
261 for (auto& nodeAndMarkers : m_markers) {
262 Node& node = *nodeAndMarkers.key;
263 FloatRect overflowClipRect;
264 if (RenderObject* renderer = node.renderer())
265 overflowClipRect = renderer->absoluteClippedOverflowRect();
266 for (auto& marker : *nodeAndMarkers.value) {
267 if (marker.type() != markerType)
270 auto renderedRects = marker.unclippedAbsoluteRects();
272 // Clip document markers by their overflow clip.
273 if (node.renderer()) {
274 for (auto& rect : renderedRects)
275 rect.intersect(overflowClipRect);
278 // Clip subframe document markers by their frame.
280 for (auto& rect : renderedRects)
281 rect.intersect(subframeClipRect);
284 for (const auto& rect : renderedRects) {
294 // Markers are stored in order sorted by their start offset.
295 // Markers of the same type do not overlap each other.
297 void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker)
299 ASSERT(newMarker.endOffset() >= newMarker.startOffset());
300 if (newMarker.endOffset() == newMarker.startOffset())
303 if (auto* renderer = node->renderer()) {
304 // FIXME: Factor the marker painting code out of InlineTextBox and teach simple line layout to use it.
305 if (is<RenderText>(*renderer))
306 downcast<RenderText>(*renderer).ensureLineBoxes();
307 else if (is<RenderBlockFlow>(*renderer))
308 downcast<RenderBlockFlow>(*renderer).ensureLineBoxes();
311 m_possiblyExistingMarkerTypes.add(newMarker.type());
313 std::unique_ptr<MarkerList>& list = m_markers.add(node, nullptr).iterator->value;
316 list = std::make_unique<MarkerList>();
317 list->append(RenderedDocumentMarker(newMarker));
319 } else if (newMarker.type() == DocumentMarker::DictationPhraseWithAlternatives || newMarker.type() == DocumentMarker::DictationResult) {
320 // We don't merge dictation markers.
322 size_t numberOfMarkers = list->size();
323 for (i = 0; i < numberOfMarkers; ++i) {
324 DocumentMarker marker = list->at(i);
325 if (marker.startOffset() > newMarker.startOffset())
328 list->insert(i, RenderedDocumentMarker(newMarker));
331 RenderedDocumentMarker toInsert(newMarker);
332 size_t numMarkers = list->size();
334 // Iterate over all markers whose start offset is less than or equal to the new marker's.
335 // If one of them is of the same type as the new marker and touches it or intersects with it
336 // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
337 for (i = 0; i < numMarkers; ++i) {
338 DocumentMarker marker = list->at(i);
339 if (marker.startOffset() > toInsert.startOffset())
341 if (marker.type() == toInsert.type() && marker.endOffset() >= toInsert.startOffset()) {
342 toInsert.setStartOffset(marker.startOffset());
349 // Iterate over all markers whose end offset is less than or equal to the new marker's,
350 // removing markers of the same type as the new marker which touch it or intersect with it,
351 // adjusting the new marker's end offset to cover them if necessary.
352 while (j < numMarkers) {
353 DocumentMarker marker = list->at(j);
354 if (marker.startOffset() > toInsert.endOffset())
356 if (marker.type() == toInsert.type()) {
358 if (toInsert.endOffset() <= marker.endOffset()) {
359 toInsert.setEndOffset(marker.endOffset());
366 // At this point i points to the node before which we want to insert.
367 list->insert(i, RenderedDocumentMarker(toInsert));
370 if (node->renderer())
371 node->renderer()->repaint();
373 invalidateRectsForMarkersInNode(*node);
376 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is
377 // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
378 void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta)
383 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
385 ASSERT(!m_markers.isEmpty());
387 MarkerList* list = m_markers.get(srcNode);
391 bool docDirty = false;
392 unsigned endOffset = startOffset + length - 1;
393 for (auto& marker : *list) {
394 // stop if we are now past the specified range
395 if (marker.startOffset() > endOffset)
398 // skip marker that is before the specified range or is the wrong type
399 if (marker.endOffset() < startOffset)
402 // pin the marker to the specified range and apply the shift delta
404 if (marker.startOffset() < startOffset)
405 marker.setStartOffset(startOffset);
406 if (marker.endOffset() > endOffset)
407 marker.setEndOffset(endOffset);
408 marker.shiftOffsets(delta);
410 addMarker(dstNode, marker);
413 if (docDirty && dstNode->renderer())
414 dstNode->renderer()->repaint();
417 void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
422 if (!possiblyHasMarkers(markerTypes))
424 ASSERT(!(m_markers.isEmpty()));
426 MarkerList* list = m_markers.get(node);
430 bool docDirty = false;
431 unsigned endOffset = startOffset + length;
432 for (size_t i = 0; i < list->size();) {
433 DocumentMarker marker = list->at(i);
435 // markers are returned in order, so stop if we are now past the specified range
436 if (marker.startOffset() >= endOffset)
439 // skip marker that is wrong type or before target
440 if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
445 // at this point we know that marker and target intersect in some way
448 // pitch the old marker
451 if (shouldRemovePartiallyOverlappingMarker)
452 // Stop here. Don't add resulting slices back.
455 // add either of the resulting slices that are left after removing target
456 if (startOffset > marker.startOffset()) {
457 DocumentMarker newLeft = marker;
458 newLeft.setEndOffset(startOffset);
459 list->insert(i, RenderedDocumentMarker(newLeft));
460 // i now points to the newly-inserted node, but we want to skip that one
463 if (marker.endOffset() > endOffset) {
464 DocumentMarker newRight = marker;
465 newRight.setStartOffset(endOffset);
466 list->insert(i, RenderedDocumentMarker(newRight));
467 // i now points to the newly-inserted node, but we want to skip that one
472 if (list->isEmpty()) {
473 m_markers.remove(node);
474 if (m_markers.isEmpty())
475 m_possiblyExistingMarkerTypes = 0;
478 if (docDirty && node->renderer())
479 node->renderer()->repaint();
482 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
484 if (!possiblyHasMarkers(markerType))
486 ASSERT(!(m_markers.isEmpty()));
488 updateRectsForInvalidatedMarkersOfType(markerType);
490 for (auto& nodeAndMarkers : m_markers) {
491 for (auto& marker : *nodeAndMarkers.value) {
492 if (marker.type() != markerType)
495 if (marker.contains(point))
503 Vector<RenderedDocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes)
505 Vector<RenderedDocumentMarker*> result;
506 MarkerList* list = m_markers.get(node);
510 for (auto& marker : *list) {
511 if (markerTypes.contains(marker.type()))
512 result.append(&marker);
518 Vector<RenderedDocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
520 if (!possiblyHasMarkers(markerTypes))
521 return Vector<RenderedDocumentMarker*>();
523 Vector<RenderedDocumentMarker*> foundMarkers;
525 Node& startContainer = range->startContainer();
526 Node& endContainer = range->endContainer();
528 Node* pastLastNode = range->pastLastNode();
529 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
530 for (auto* marker : markersFor(node)) {
531 if (!markerTypes.contains(marker->type()))
533 if (node == &startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
535 if (node == &endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
537 foundMarkers.append(marker);
543 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
545 if (!possiblyHasMarkers(markerTypes))
547 ASSERT(!m_markers.isEmpty());
549 MarkerMap::iterator iterator = m_markers.find(node);
550 if (iterator != m_markers.end())
551 removeMarkersFromList(iterator, markerTypes);
554 void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
556 if (!possiblyHasMarkers(markerTypes))
558 ASSERT(!m_markers.isEmpty());
560 Vector<RefPtr<Node>> nodesWithMarkers;
561 copyKeysToVector(m_markers, nodesWithMarkers);
562 for (auto& node : nodesWithMarkers) {
563 auto iterator = m_markers.find(node);
564 if (iterator != m_markers.end())
565 removeMarkersFromList(iterator, markerTypes);
568 m_possiblyExistingMarkerTypes.remove(markerTypes);
571 void DocumentMarkerController::removeMarkersFromList(MarkerMap::iterator iterator, DocumentMarker::MarkerTypes markerTypes)
573 bool needsRepainting = false;
574 bool listCanBeRemoved;
576 if (markerTypes == DocumentMarker::AllMarkers()) {
577 needsRepainting = true;
578 listCanBeRemoved = true;
580 MarkerList* list = iterator->value.get();
582 for (size_t i = 0; i != list->size(); ) {
583 DocumentMarker marker = list->at(i);
585 // skip nodes that are not of the specified type
586 if (!markerTypes.contains(marker.type())) {
591 // pitch the old marker
593 needsRepainting = true;
594 // i now is the index of the next marker
597 listCanBeRemoved = list->isEmpty();
600 if (needsRepainting) {
601 if (auto renderer = iterator->key->renderer())
605 if (listCanBeRemoved) {
606 m_markers.remove(iterator);
607 if (m_markers.isEmpty())
608 m_possiblyExistingMarkerTypes = 0;
612 void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
614 if (!possiblyHasMarkers(markerTypes))
616 ASSERT(!m_markers.isEmpty());
618 // outer loop: process each markered node in the document
619 for (auto& marker : m_markers) {
620 Node* node = marker.key.get();
622 // inner loop: process each marker in the current node
623 bool nodeNeedsRepaint = false;
624 for (auto& documentMarker : *marker.value) {
625 if (markerTypes.contains(documentMarker.type())) {
626 nodeNeedsRepaint = true;
631 if (!nodeNeedsRepaint)
634 // cause the node to be redrawn
635 if (auto renderer = node->renderer())
640 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
642 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
644 ASSERT(!m_markers.isEmpty());
646 MarkerList* list = m_markers.get(node);
650 bool didShiftMarker = false;
651 for (size_t i = 0; i != list->size(); ) {
652 RenderedDocumentMarker& marker = list->at(i);
653 // FIXME: How can this possibly be iOS-specific code?
655 int targetStartOffset = marker.startOffset() + delta;
656 int targetEndOffset = marker.endOffset() + delta;
657 if (targetStartOffset >= node->maxCharacterOffset() || targetEndOffset <= 0) {
662 if (marker.startOffset() >= startOffset) {
663 ASSERT((int)marker.startOffset() + delta >= 0);
664 marker.shiftOffsets(delta);
665 didShiftMarker = true;
669 // FIXME: Inserting text inside a DocumentMarker does not grow the marker.
670 // See <https://bugs.webkit.org/show_bug.cgi?id=62504>.
671 } else if (marker.endOffset() > startOffset) {
672 if (marker.endOffset() + delta <= marker.startOffset()) {
676 marker.setEndOffset(targetEndOffset < node->maxCharacterOffset() ? targetEndOffset : node->maxCharacterOffset());
677 didShiftMarker = true;
683 if (didShiftMarker) {
684 invalidateRectsForMarkersInNode(*node);
686 if (node->renderer())
687 node->renderer()->repaint();
691 void DocumentMarkerController::setMarkersActive(Range* range, bool active)
693 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
695 ASSERT(!m_markers.isEmpty());
697 Node& startContainer = range->startContainer();
698 Node& endContainer = range->endContainer();
700 Node* pastLastNode = range->pastLastNode();
702 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
703 int startOffset = node == &startContainer ? range->startOffset() : 0;
704 int endOffset = node == &endContainer ? range->endOffset() : INT_MAX;
705 setMarkersActive(node, startOffset, endOffset, active);
709 void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active)
711 MarkerList* list = m_markers.get(node);
715 bool didActivateMarker = false;
716 for (auto& marker : *list) {
717 // Markers are returned in order, so stop if we are now past the specified range.
718 if (marker.startOffset() >= endOffset)
721 // Skip marker that is wrong type or before target.
722 if (marker.endOffset() < startOffset || marker.type() != DocumentMarker::TextMatch)
725 marker.setActiveMatch(active);
726 didActivateMarker = true;
729 if (didActivateMarker && node->renderer())
730 node->renderer()->repaint();
733 bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes)
735 if (!possiblyHasMarkers(markerTypes))
737 ASSERT(!m_markers.isEmpty());
739 Node& startContainer = range->startContainer();
740 Node& endContainer = range->endContainer();
742 Node* pastLastNode = range->pastLastNode();
743 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
744 for (auto* marker : markersFor(node)) {
745 if (!markerTypes.contains(marker->type()))
747 if (node == &startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
749 if (node == &endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
757 void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
759 if (!possiblyHasMarkers(markerTypes))
761 ASSERT(!m_markers.isEmpty());
763 Node& startContainer = range->startContainer();
764 Node& endContainer = range->endContainer();
766 Node* pastLastNode = range->pastLastNode();
767 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
768 unsigned startOffset = node == &startContainer ? range->startOffset() : 0;
769 unsigned endOffset = node == &endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max();
770 MarkerList* list = m_markers.get(node);
774 for (size_t i = 0; i < list->size(); ++i) {
775 DocumentMarker& marker = list->at(i);
777 // markers are returned in order, so stop if we are now past the specified range
778 if (marker.startOffset() >= endOffset)
781 // skip marker that is wrong type or before target
782 if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
787 marker.clearDetails();
792 #if ENABLE(TREE_DEBUGGING)
793 void DocumentMarkerController::showMarkers() const
795 fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
796 for (auto& marker : m_markers) {
797 Node* node = marker.key.get();
798 fprintf(stderr, "%p", node);
799 for (auto& documentMarker : *marker.value)
800 fprintf(stderr, " %d:[%d:%d](%d)", documentMarker.type(), documentMarker.startOffset(), documentMarker.endOffset(), documentMarker.activeMatch());
802 fprintf(stderr, "\n");
807 } // namespace WebCore
809 #if ENABLE(TREE_DEBUGGING)
810 void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
813 controller->showMarkers();