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"
32 #include "RenderObject.h"
33 #include "RenderedDocumentMarker.h"
34 #include "TextIterator.h"
42 inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
44 return m_possiblyExistingMarkerTypes.intersects(types);
47 DocumentMarkerController::DocumentMarkerController()
48 : m_possiblyExistingMarkerTypes(0)
52 void DocumentMarkerController::detach()
54 m_possiblyExistingMarkerTypes = 0;
55 if (m_markers.isEmpty())
57 deleteAllValues(m_markers);
61 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description)
63 // Use a TextIterator to visit the potentially multiple nodes the range covers.
64 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
65 RefPtr<Range> textPiece = markedText.range();
67 addMarker(textPiece->startContainer(exception),
68 DocumentMarker(type, textPiece->startOffset(exception), textPiece->endOffset(exception), description));
72 void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type)
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();
78 addMarker(textPiece->startContainer(exception),
79 DocumentMarker(type, textPiece->startOffset(exception), textPiece->endOffset(exception)));
85 void DocumentMarkerController::addTextMatchMarker(Range* range, bool activeMatch)
87 // Use a TextIterator to visit the potentially multiple nodes the range covers.
88 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
89 RefPtr<Range> textPiece = markedText.range();
91 unsigned startOffset = textPiece->startOffset(exception);
92 unsigned endOffset = textPiece->endOffset(exception);
93 addMarker(textPiece->startContainer(exception), DocumentMarker(startOffset, endOffset, activeMatch));
94 if (endOffset > startOffset) {
95 // Rendered rects for markers in WebKit are not populated until each time
96 // the markers are painted. However, we need it to happen sooner, because
97 // the whole purpose of tickmarks on the scrollbar is to show where
98 // matches off-screen are (that haven't been painted yet).
99 Node* node = textPiece->startContainer(exception);
100 Vector<DocumentMarker*> markers = markersFor(node);
101 static_cast<RenderedDocumentMarker*>(markers[markers.size() - 1])->setRenderedRect(range->boundingBox());
106 void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
108 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
109 if (!possiblyHasMarkers(markerTypes))
111 ASSERT(!m_markers.isEmpty());
113 RefPtr<Range> textPiece = markedText.range();
114 int startOffset = textPiece->startOffset();
115 int endOffset = textPiece->endOffset();
116 removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
120 // Markers are stored in order sorted by their start offset.
121 // Markers of the same type do not overlap each other.
123 void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker)
125 ASSERT(newMarker.endOffset() >= newMarker.startOffset());
126 if (newMarker.endOffset() == newMarker.startOffset())
129 m_possiblyExistingMarkerTypes.add(newMarker.type());
131 MarkerList* list = m_markers.get(node);
134 list = new MarkerList;
135 list->append(RenderedDocumentMarker(newMarker));
136 m_markers.set(node, list);
138 RenderedDocumentMarker toInsert(newMarker);
139 size_t numMarkers = list->size();
141 // Iterate over all markers whose start offset is less than or equal to the new marker's.
142 // If one of them is of the same type as the new marker and touches it or intersects with it
143 // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
144 for (i = 0; i < numMarkers; ++i) {
145 DocumentMarker marker = list->at(i);
146 if (marker.startOffset() > toInsert.startOffset())
148 if (marker.type() == toInsert.type() && marker.endOffset() >= toInsert.startOffset()) {
149 toInsert.setStartOffset(marker.startOffset());
156 // Iterate over all markers whose end offset is less than or equal to the new marker's,
157 // removing markers of the same type as the new marker which touch it or intersect with it,
158 // adjusting the new marker's end offset to cover them if necessary.
159 while (j < numMarkers) {
160 DocumentMarker marker = list->at(j);
161 if (marker.startOffset() > toInsert.endOffset())
163 if (marker.type() == toInsert.type()) {
165 if (toInsert.endOffset() <= marker.endOffset()) {
166 toInsert.setEndOffset(marker.endOffset());
173 // At this point i points to the node before which we want to insert.
174 list->insert(i, RenderedDocumentMarker(toInsert));
177 // repaint the affected node
178 if (node->renderer())
179 node->renderer()->repaint();
182 // copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is
183 // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode.
184 void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta)
189 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
191 ASSERT(!m_markers.isEmpty());
193 MarkerList* list = m_markers.get(srcNode);
197 bool docDirty = false;
198 unsigned endOffset = startOffset + length - 1;
199 for (size_t i = 0; i != list->size(); ++i) {
200 DocumentMarker marker = list->at(i);
202 // stop if we are now past the specified range
203 if (marker.startOffset() > endOffset)
206 // skip marker that is before the specified range or is the wrong type
207 if (marker.endOffset() < startOffset)
210 // pin the marker to the specified range and apply the shift delta
212 if (marker.startOffset() < startOffset)
213 marker.setStartOffset(startOffset);
214 if (marker.endOffset() > endOffset)
215 marker.setEndOffset(endOffset);
216 marker.shiftOffsets(delta);
218 addMarker(dstNode, marker);
221 // repaint the affected node
222 if (docDirty && dstNode->renderer())
223 dstNode->renderer()->repaint();
226 void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker)
231 if (!possiblyHasMarkers(markerTypes))
233 ASSERT(!(m_markers.isEmpty()));
235 MarkerList* list = m_markers.get(node);
239 bool docDirty = false;
240 unsigned endOffset = startOffset + length;
241 for (size_t i = 0; i < list->size();) {
242 DocumentMarker marker = list->at(i);
244 // markers are returned in order, so stop if we are now past the specified range
245 if (marker.startOffset() >= endOffset)
248 // skip marker that is wrong type or before target
249 if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
254 // at this point we know that marker and target intersect in some way
257 // pitch the old marker
260 if (shouldRemovePartiallyOverlappingMarker)
261 // Stop here. Don't add resulting slices back.
264 // add either of the resulting slices that are left after removing target
265 if (startOffset > marker.startOffset()) {
266 DocumentMarker newLeft = marker;
267 newLeft.setEndOffset(startOffset);
268 list->insert(i, RenderedDocumentMarker(newLeft));
269 // i now points to the newly-inserted node, but we want to skip that one
272 if (marker.endOffset() > endOffset) {
273 DocumentMarker newRight = marker;
274 newRight.setStartOffset(endOffset);
275 list->insert(i, RenderedDocumentMarker(newRight));
276 // i now points to the newly-inserted node, but we want to skip that one
281 if (list->isEmpty()) {
282 m_markers.remove(node);
286 if (m_markers.isEmpty())
287 m_possiblyExistingMarkerTypes = 0;
289 // repaint the affected node
290 if (docDirty && node->renderer())
291 node->renderer()->repaint();
294 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
296 if (!possiblyHasMarkers(markerType))
298 ASSERT(!(m_markers.isEmpty()));
300 // outer loop: process each node that contains any markers
301 MarkerMap::iterator end = m_markers.end();
302 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
303 // inner loop; process each marker in this node
304 MarkerList* list = nodeIterator->second;
305 unsigned markerCount = list->size();
306 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
307 RenderedDocumentMarker& marker = list->at(markerIndex);
309 // skip marker that is wrong type
310 if (marker.type() != markerType)
313 if (marker.contains(point))
321 Vector<DocumentMarker*> DocumentMarkerController::markersFor(Node* node)
323 Vector<DocumentMarker*> result;
324 MarkerList* list = m_markers.get(node);
328 for (size_t i = 0; i < list->size(); ++i)
329 result.append(&(list->at(i)));
334 // FIXME: Should be removed after all relevant patches are landed
335 Vector<DocumentMarker> DocumentMarkerController::markersForNode(Node* node)
337 Vector<DocumentMarker> result;
338 MarkerList* list = m_markers.get(node);
342 for (size_t i = 0; i < list->size(); ++i)
343 result.append(list->at(i));
348 Vector<DocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
350 if (!possiblyHasMarkers(markerTypes))
351 return Vector<DocumentMarker*>();
353 Vector<DocumentMarker*> foundMarkers;
355 Node* startContainer = range->startContainer();
356 ASSERT(startContainer);
357 Node* endContainer = range->endContainer();
358 ASSERT(endContainer);
360 Node* pastLastNode = range->pastLastNode();
361 for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
362 Vector<DocumentMarker*> markers = markersFor(node);
363 Vector<DocumentMarker*>::const_iterator end = markers.end();
364 for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) {
365 DocumentMarker* marker = *it;
366 if (!markerTypes.contains(marker->type()))
368 if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
370 if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
372 foundMarkers.append(marker);
378 Vector<LayoutRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
380 Vector<LayoutRect> result;
382 if (!possiblyHasMarkers(markerType))
384 ASSERT(!(m_markers.isEmpty()));
386 // outer loop: process each node
387 MarkerMap::iterator end = m_markers.end();
388 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
389 // inner loop; process each marker in this node
390 MarkerList* list = nodeIterator->second;
391 unsigned markerCount = list->size();
392 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
393 const RenderedDocumentMarker& marker = list->at(markerIndex);
395 // skip marker that is wrong type
396 if (marker.type() != markerType)
399 if (!marker.isRendered())
402 result.append(marker.renderedRect());
409 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes)
411 if (!possiblyHasMarkers(markerTypes))
413 ASSERT(!m_markers.isEmpty());
415 MarkerMap::iterator iterator = m_markers.find(node);
416 if (iterator != m_markers.end())
417 removeMarkersFromList(node, iterator->second, markerTypes);
420 void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
422 if (!possiblyHasMarkers(markerTypes))
424 ASSERT(!m_markers.isEmpty());
426 // outer loop: process each markered node in the document
427 MarkerMap markerMapCopy = m_markers;
428 MarkerMap::iterator end = markerMapCopy.end();
429 for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i)
430 removeMarkersFromList(i->first.get(), i->second, markerTypes);
431 m_possiblyExistingMarkerTypes.remove(markerTypes);
434 // This function may release node and vectorPair.
435 void DocumentMarkerController::removeMarkersFromList(Node* node, MarkerList* list, DocumentMarker::MarkerTypes markerTypes)
437 if (markerTypes == DocumentMarker::AllMarkers()) {
439 m_markers.remove(node);
440 if (RenderObject* renderer = node->renderer())
443 bool needsRepaint = false;
444 for (size_t i = 0; i != list->size();) {
445 DocumentMarker marker = list->at(i);
447 // skip nodes that are not of the specified type
448 if (!markerTypes.contains(marker.type())) {
453 // pitch the old marker
456 // i now is the index of the next marker
459 // Redraw the node if it changed. Do this before the node is removed from m_markers, since
460 // m_markers might contain the last reference to the node.
462 RenderObject* renderer = node->renderer();
467 // delete the node's list if it is now empty
468 if (list->isEmpty()) {
469 m_markers.remove(node);
473 if (m_markers.isEmpty())
474 m_possiblyExistingMarkerTypes = 0;
477 void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
479 if (!possiblyHasMarkers(markerTypes))
481 ASSERT(!m_markers.isEmpty());
483 // outer loop: process each markered node in the document
484 MarkerMap::iterator end = m_markers.end();
485 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
486 Node* node = i->first.get();
488 // inner loop: process each marker in the current node
489 MarkerList* list = i->second;
490 bool nodeNeedsRepaint = false;
491 for (size_t i = 0; i != list->size(); ++i) {
492 DocumentMarker marker = list->at(i);
494 // skip nodes that are not of the specified type
495 if (markerTypes.contains(marker.type())) {
496 nodeNeedsRepaint = true;
501 if (!nodeNeedsRepaint)
504 // cause the node to be redrawn
505 if (RenderObject* renderer = node->renderer())
510 void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r)
512 // outer loop: process each markered node in the document
513 MarkerMap::iterator end = m_markers.end();
514 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
516 // inner loop: process each rect in the current node
517 MarkerList* list = i->second;
518 for (size_t listIndex = 0; listIndex < list->size(); ++listIndex)
519 list->at(listIndex).invalidate(r);
523 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta)
525 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
527 ASSERT(!m_markers.isEmpty());
529 MarkerList* list = m_markers.get(node);
533 bool docDirty = false;
534 for (size_t i = 0; i != list->size(); ++i) {
535 RenderedDocumentMarker& marker = list->at(i);
536 if (marker.startOffset() >= startOffset) {
537 ASSERT((int)marker.startOffset() + delta >= 0);
538 marker.shiftOffsets(delta);
541 // Marker moved, so previously-computed rendered rectangle is now invalid
546 // repaint the affected node
547 if (docDirty && node->renderer())
548 node->renderer()->repaint();
551 void DocumentMarkerController::setMarkersActive(Range* range, bool active)
553 if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
555 ASSERT(!m_markers.isEmpty());
557 ExceptionCode ec = 0;
558 Node* startContainer = range->startContainer(ec);
559 Node* endContainer = range->endContainer(ec);
561 Node* pastLastNode = range->pastLastNode();
562 for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
563 int startOffset = node == startContainer ? range->startOffset(ec) : 0;
564 int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX;
565 setMarkersActive(node, startOffset, endOffset, active);
569 void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active)
571 MarkerList* list = m_markers.get(node);
575 bool docDirty = false;
576 for (size_t i = 0; i != list->size(); ++i) {
577 DocumentMarker& marker = list->at(i);
579 // Markers are returned in order, so stop if we are now past the specified range.
580 if (marker.startOffset() >= endOffset)
583 // Skip marker that is wrong type or before target.
584 if (marker.endOffset() < startOffset || marker.type() != DocumentMarker::TextMatch)
587 marker.setActiveMatch(active);
591 // repaint the affected node
592 if (docDirty && node->renderer())
593 node->renderer()->repaint();
596 bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes)
598 if (!possiblyHasMarkers(markerTypes))
600 ASSERT(!m_markers.isEmpty());
602 Node* startContainer = range->startContainer();
603 ASSERT(startContainer);
604 Node* endContainer = range->endContainer();
605 ASSERT(endContainer);
607 Node* pastLastNode = range->pastLastNode();
608 for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
609 Vector<DocumentMarker*> markers = markersFor(node);
610 Vector<DocumentMarker*>::const_iterator end = markers.end();
611 for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) {
612 DocumentMarker* marker = *it;
613 if (!markerTypes.contains(marker->type()))
615 if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset()))
617 if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset()))
625 void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
627 if (!possiblyHasMarkers(markerTypes))
629 ASSERT(!m_markers.isEmpty());
631 Node* startContainer = range->startContainer();
632 Node* endContainer = range->endContainer();
634 Node* pastLastNode = range->pastLastNode();
635 for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) {
636 unsigned startOffset = node == startContainer ? range->startOffset() : 0;
637 unsigned endOffset = node == endContainer ? static_cast<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max();
638 MarkerList* list = m_markers.get(node);
642 for (size_t i = 0; i < list->size(); ++i) {
643 DocumentMarker& marker = list->at(i);
645 // markers are returned in order, so stop if we are now past the specified range
646 if (marker.startOffset() >= endOffset)
649 // skip marker that is wrong type or before target
650 if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
655 marker.clearDetails();
661 void DocumentMarkerController::showMarkers() const
663 fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
664 MarkerMap::const_iterator end = m_markers.end();
665 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
666 Node* node = nodeIterator->first.get();
667 fprintf(stderr, "%p", node);
668 MarkerList* list = nodeIterator->second;
669 for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) {
670 const DocumentMarker& marker = list->at(markerIndex);
671 fprintf(stderr, " %d:[%d:%d](%d)", marker.type(), marker.startOffset(), marker.endOffset(), marker.activeMatch());
674 fprintf(stderr, "\n");
679 } // namespace WebCore
683 void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
686 controller->showMarkers();