/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) * (C) 2006 Alexey Proskuryakov (ap@webkit.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "DocumentMarkerController.h" #include "Node.h" #include "Range.h" #include "TextIterator.h" namespace WebCore { static IntRect placeholderRectForMarker() { return IntRect(-1, -1, -1, -1); } inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types) { return m_possiblyExistingMarkerTypes.intersects(types); } DocumentMarkerController::DocumentMarkerController() : m_possiblyExistingMarkerTypes(0) { } void DocumentMarkerController::detach() { m_possiblyExistingMarkerTypes = 0; if (m_markers.isEmpty()) return; deleteAllValues(m_markers); m_markers.clear(); } void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, String description) { // Use a TextIterator to visit the potentially multiple nodes the range covers. for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { RefPtr textPiece = markedText.range(); int exception = 0; DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception), description, false}; addMarker(textPiece->startContainer(exception), marker); } } void DocumentMarkerController::removeMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker) { for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) { if (!possiblyHasMarkers(markerTypes)) return; ASSERT(!m_markers.isEmpty()); RefPtr textPiece = markedText.range(); int startOffset = textPiece->startOffset(); int endOffset = textPiece->endOffset(); removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker); } } // Markers are stored in order sorted by their start offset. // Markers of the same type do not overlap each other. void DocumentMarkerController::addMarker(Node* node, DocumentMarker newMarker) { ASSERT(newMarker.endOffset >= newMarker.startOffset); if (newMarker.endOffset == newMarker.startOffset) return; m_possiblyExistingMarkerTypes.add(newMarker.type); MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) { vectorPair = new MarkerMapVectorPair; vectorPair->first.append(newMarker); vectorPair->second.append(placeholderRectForMarker()); m_markers.set(node, vectorPair); } else { Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; size_t numMarkers = markers.size(); ASSERT(numMarkers == rects.size()); size_t i; // Iterate over all markers whose start offset is less than or equal to the new marker's. // If one of them is of the same type as the new marker and touches it or intersects with it // (there is at most one), remove it and adjust the new marker's start offset to encompass it. for (i = 0; i < numMarkers; ++i) { DocumentMarker marker = markers[i]; if (marker.startOffset > newMarker.startOffset) break; if (marker.type == newMarker.type && marker.endOffset >= newMarker.startOffset) { newMarker.startOffset = marker.startOffset; markers.remove(i); rects.remove(i); numMarkers--; break; } } size_t j = i; // Iterate over all markers whose end offset is less than or equal to the new marker's, // removing markers of the same type as the new marker which touch it or intersect with it, // adjusting the new marker's end offset to cover them if necessary. while (j < numMarkers) { DocumentMarker marker = markers[j]; if (marker.startOffset > newMarker.endOffset) break; if (marker.type == newMarker.type) { markers.remove(j); rects.remove(j); if (newMarker.endOffset <= marker.endOffset) { newMarker.endOffset = marker.endOffset; break; } numMarkers--; } else j++; } // At this point i points to the node before which we want to insert. markers.insert(i, newMarker); rects.insert(i, placeholderRectForMarker()); } // repaint the affected node if (node->renderer()) node->renderer()->repaint(); } // copies markers from srcNode to dstNode, applying the specified shift delta to the copies. The shift is // useful if, e.g., the caller has created the dstNode from a non-prefix substring of the srcNode. void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset, int length, Node* dstNode, int delta) { if (length <= 0) return; if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) return; ASSERT(!m_markers.isEmpty()); MarkerMapVectorPair* vectorPair = m_markers.get(srcNode); if (!vectorPair) return; ASSERT(vectorPair->first.size() == vectorPair->second.size()); bool docDirty = false; unsigned endOffset = startOffset + length - 1; Vector& markers = vectorPair->first; for (size_t i = 0; i != markers.size(); ++i) { DocumentMarker marker = markers[i]; // stop if we are now past the specified range if (marker.startOffset > endOffset) break; // skip marker that is before the specified range or is the wrong type if (marker.endOffset < startOffset) continue; // pin the marker to the specified range and apply the shift delta docDirty = true; if (marker.startOffset < startOffset) marker.startOffset = startOffset; if (marker.endOffset > endOffset) marker.endOffset = endOffset; marker.startOffset += delta; marker.endOffset += delta; addMarker(dstNode, marker); } // repaint the affected node if (docDirty && dstNode->renderer()) dstNode->renderer()->repaint(); } void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, int length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemovePartiallyOverlappingMarker) { if (length <= 0) return; if (!possiblyHasMarkers(markerTypes)) return; ASSERT(!(m_markers.isEmpty())); MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) return; Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; ASSERT(markers.size() == rects.size()); bool docDirty = false; unsigned endOffset = startOffset + length; for (size_t i = 0; i < markers.size();) { DocumentMarker marker = markers[i]; // markers are returned in order, so stop if we are now past the specified range if (marker.startOffset >= endOffset) break; // skip marker that is wrong type or before target if (marker.endOffset <= startOffset || !markerTypes.contains(marker.type)) { i++; continue; } // at this point we know that marker and target intersect in some way docDirty = true; // pitch the old marker and any associated rect markers.remove(i); rects.remove(i); if (shouldRemovePartiallyOverlappingMarker) // Stop here. Don't add resulting slices back. continue; // add either of the resulting slices that are left after removing target if (startOffset > marker.startOffset) { DocumentMarker newLeft = marker; newLeft.endOffset = startOffset; markers.insert(i, newLeft); rects.insert(i, placeholderRectForMarker()); // i now points to the newly-inserted node, but we want to skip that one i++; } if (marker.endOffset > endOffset) { DocumentMarker newRight = marker; newRight.startOffset = endOffset; markers.insert(i, newRight); rects.insert(i, placeholderRectForMarker()); // i now points to the newly-inserted node, but we want to skip that one i++; } } if (markers.isEmpty()) { ASSERT(rects.isEmpty()); m_markers.remove(node); delete vectorPair; } if (m_markers.isEmpty()) m_possiblyExistingMarkerTypes = 0; // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); } DocumentMarker* DocumentMarkerController::markerContainingPoint(const IntPoint& point, DocumentMarker::MarkerType markerType) { if (!possiblyHasMarkers(markerType)) return 0; ASSERT(!(m_markers.isEmpty())); // outer loop: process each node that contains any markers MarkerMap::iterator end = m_markers.end(); for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { // inner loop; process each marker in this node MarkerMapVectorPair* vectorPair = nodeIterator->second; Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; ASSERT(markers.size() == rects.size()); unsigned markerCount = markers.size(); for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { DocumentMarker& marker = markers[markerIndex]; // skip marker that is wrong type if (marker.type != markerType) continue; IntRect& r = rects[markerIndex]; // skip placeholder rects if (r == placeholderRectForMarker()) continue; if (r.contains(point)) return ▮ } } return 0; } Vector DocumentMarkerController::markersForNode(Node* node) { MarkerMapVectorPair* vectorPair = m_markers.get(node); if (vectorPair) return vectorPair->first; return Vector(); } Vector DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerType markerType) { if (!possiblyHasMarkers(markerType)) return Vector(); Vector foundMarkers; Node* startContainer = range->startContainer(); ASSERT(startContainer); Node* endContainer = range->endContainer(); ASSERT(endContainer); Node* pastLastNode = range->pastLastNode(); for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { Vector markers = markersForNode(node); Vector::const_iterator end = markers.end(); for (Vector::const_iterator it = markers.begin(); it != end; ++it) { if (markerType != it->type) continue; if (node == startContainer && it->endOffset <= static_cast(range->startOffset())) continue; if (node == endContainer && it->startOffset >= static_cast(range->endOffset())) continue; foundMarkers.append(*it); } } return foundMarkers; } Vector DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) { Vector result; if (!possiblyHasMarkers(markerType)) return result; ASSERT(!(m_markers.isEmpty())); // outer loop: process each node MarkerMap::iterator end = m_markers.end(); for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { // inner loop; process each marker in this node MarkerMapVectorPair* vectorPair = nodeIterator->second; Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; ASSERT(markers.size() == rects.size()); unsigned markerCount = markers.size(); for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { DocumentMarker marker = markers[markerIndex]; // skip marker that is wrong type if (marker.type != markerType) continue; IntRect r = rects[markerIndex]; // skip placeholder rects if (r == placeholderRectForMarker()) continue; result.append(r); } } return result; } void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) return; ASSERT(!m_markers.isEmpty()); MarkerMap::iterator iterator = m_markers.find(node); if (iterator != m_markers.end()) removeMarkersFromMarkerMapVectorPair(node, iterator->second, markerTypes); } void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) return; ASSERT(!m_markers.isEmpty()); // outer loop: process each markered node in the document MarkerMap markerMapCopy = m_markers; MarkerMap::iterator end = markerMapCopy.end(); for (MarkerMap::iterator i = markerMapCopy.begin(); i != end; ++i) { Node* node = i->first.get(); MarkerMapVectorPair* vectorPair = i->second; removeMarkersFromMarkerMapVectorPair(node, vectorPair, markerTypes); } m_possiblyExistingMarkerTypes.remove(markerTypes); } // This function may release node and vectorPair. void DocumentMarkerController::removeMarkersFromMarkerMapVectorPair(Node* node, MarkerMapVectorPair* vectorPair, DocumentMarker::MarkerTypes markerTypes) { if (markerTypes == DocumentMarker::AllMarkers()) { delete vectorPair; m_markers.remove(node); if (RenderObject* renderer = node->renderer()) renderer->repaint(); } else { bool needsRepaint = false; Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; ASSERT(markers.size() == rects.size()); for (size_t i = 0; i != markers.size();) { DocumentMarker marker = markers[i]; // skip nodes that are not of the specified type if (!markerTypes.contains(marker.type)) { ++i; continue; } // pitch the old marker markers.remove(i); rects.remove(i); needsRepaint = true; // i now is the index of the next marker } // Redraw the node if it changed. Do this before the node is removed from m_markers, since // m_markers might contain the last reference to the node. if (needsRepaint) { RenderObject* renderer = node->renderer(); if (renderer) renderer->repaint(); } // delete the node's list if it is now empty if (markers.isEmpty()) { ASSERT(rects.isEmpty()); m_markers.remove(node); delete vectorPair; } } if (m_markers.isEmpty()) m_possiblyExistingMarkerTypes = 0; } void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) return; ASSERT(!m_markers.isEmpty()); // outer loop: process each markered node in the document MarkerMap::iterator end = m_markers.end(); for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { Node* node = i->first.get(); // inner loop: process each marker in the current node MarkerMapVectorPair* vectorPair = i->second; Vector& markers = vectorPair->first; bool nodeNeedsRepaint = false; for (size_t i = 0; i != markers.size(); ++i) { DocumentMarker marker = markers[i]; // skip nodes that are not of the specified type if (markerTypes.contains(marker.type)) { nodeNeedsRepaint = true; break; } } if (!nodeNeedsRepaint) continue; // cause the node to be redrawn if (RenderObject* renderer = node->renderer()) renderer->repaint(); } } void DocumentMarkerController::setRenderedRectForMarker(Node* node, const DocumentMarker& marker, const IntRect& r) { MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) { ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about return; } Vector& markers = vectorPair->first; ASSERT(markers.size() == vectorPair->second.size()); unsigned markerCount = markers.size(); for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { DocumentMarker m = markers[markerIndex]; if (m == marker) { vectorPair->second[markerIndex] = r; return; } } ASSERT_NOT_REACHED(); // shouldn't be trying to set the rect for a marker we don't already know about } void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const IntRect& r) { // outer loop: process each markered node in the document MarkerMap::iterator end = m_markers.end(); for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { // inner loop: process each rect in the current node MarkerMapVectorPair* vectorPair = i->second; Vector& rects = vectorPair->second; unsigned rectCount = rects.size(); for (unsigned rectIndex = 0; rectIndex < rectCount; ++rectIndex) if (rects[rectIndex].intersects(r)) rects[rectIndex] = placeholderRectForMarker(); } } void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta) { if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) return; ASSERT(!m_markers.isEmpty()); MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) return; Vector& markers = vectorPair->first; Vector& rects = vectorPair->second; ASSERT(markers.size() == rects.size()); bool docDirty = false; for (size_t i = 0; i != markers.size(); ++i) { DocumentMarker& marker = markers[i]; if (marker.startOffset >= startOffset) { ASSERT((int)marker.startOffset + delta >= 0); marker.startOffset += delta; marker.endOffset += delta; docDirty = true; // Marker moved, so previously-computed rendered rectangle is now invalid rects[i] = placeholderRectForMarker(); } } // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); } void DocumentMarkerController::setMarkersActive(Range* range, bool active) { if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) return; ASSERT(!m_markers.isEmpty()); ExceptionCode ec = 0; Node* startContainer = range->startContainer(ec); Node* endContainer = range->endContainer(ec); Node* pastLastNode = range->pastLastNode(); for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { int startOffset = node == startContainer ? range->startOffset(ec) : 0; int endOffset = node == endContainer ? range->endOffset(ec) : INT_MAX; setMarkersActive(node, startOffset, endOffset, active); } } void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active) { MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) return; Vector& markers = vectorPair->first; ASSERT(markers.size() == vectorPair->second.size()); bool docDirty = false; for (size_t i = 0; i != markers.size(); ++i) { DocumentMarker& marker = markers[i]; // Markers are returned in order, so stop if we are now past the specified range. if (marker.startOffset >= endOffset) break; // Skip marker that is wrong type or before target. if (marker.endOffset < startOffset || marker.type != DocumentMarker::TextMatch) continue; marker.activeMatch = active; docDirty = true; } // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); } bool DocumentMarkerController::hasMarkers(Range* range, DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) return false; ASSERT(!m_markers.isEmpty()); Node* startContainer = range->startContainer(); ASSERT(startContainer); Node* endContainer = range->endContainer(); ASSERT(endContainer); Node* pastLastNode = range->pastLastNode(); for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { Vector markers = markersForNode(node); Vector::const_iterator end = markers.end(); for (Vector::const_iterator it = markers.begin(); it != end; ++it) { if (!markerTypes.contains(it->type)) continue; if (node == startContainer && it->endOffset <= static_cast(range->startOffset())) continue; if (node == endContainer && it->startOffset >= static_cast(range->endOffset())) continue; return true; } } return false; } void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(Range* range, DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) return; ASSERT(!m_markers.isEmpty()); Node* startContainer = range->startContainer(); Node* endContainer = range->endContainer(); Node* pastLastNode = range->pastLastNode(); for (Node* node = range->firstNode(); node != pastLastNode; node = node->traverseNextNode()) { unsigned startOffset = node == startContainer ? range->startOffset() : 0; unsigned endOffset = node == endContainer ? static_cast(range->endOffset()) : std::numeric_limits::max(); MarkerMapVectorPair* vectorPair = m_markers.get(node); if (!vectorPair) continue; Vector& markers = vectorPair->first; for (size_t i = 0; i < markers.size(); ++i) { DocumentMarker& marker = markers[i]; // markers are returned in order, so stop if we are now past the specified range if (marker.startOffset >= endOffset) break; // skip marker that is wrong type or before target if (marker.endOffset <= startOffset || !markerTypes.contains(marker.type)) { i++; continue; } marker.description = String(); } } } #ifndef NDEBUG void DocumentMarkerController::showMarkers() const { fprintf(stderr, "%d nodes have markers:\n", m_markers.size()); MarkerMap::const_iterator end = m_markers.end(); for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) { Node* node = nodeIterator->first.get(); fprintf(stderr, "%p", node); MarkerMapVectorPair* vectorPair = nodeIterator->second; Vector& markers = vectorPair->first; unsigned markerCount = markers.size(); for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) fprintf(stderr, " %d:[%d:%d](%d)", markers[markerIndex].type, markers[markerIndex].startOffset, markers[markerIndex].endOffset, markers[markerIndex].activeMatch); fprintf(stderr, "\n"); } } #endif } // namespace WebCore #ifndef NDEBUG void showDocumentMarkers(const WebCore::DocumentMarkerController* controller) { if (controller) controller->showMarkers(); } #endif