void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta) { if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) return; ASSERT(!m_markers.isEmpty()); MarkerList* list = m_markers.get(node); if (!list) return; bool docDirty = false; for (size_t i = 0; i != list->size(); ) { RenderedDocumentMarker& marker = list->at(i); #if PLATFORM(IOS) int targetStartOffset = marker.startOffset() + delta; int targetEndOffset = marker.endOffset() + delta; if (targetStartOffset >= node->maxCharacterOffset() || targetEndOffset <= 0) { list->remove(i); continue; } #endif if (marker.startOffset() >= startOffset) { ASSERT((int)marker.startOffset() + delta >= 0); marker.shiftOffsets(delta); docDirty = true; // Marker moved, so previously-computed rendered rectangle is now invalid marker.invalidate(); #if !PLATFORM(IOS) } #else // FIXME: Inserting text inside a DocumentMarker does not grow the marker. // See <https://bugs.webkit.org/show_bug.cgi?id=62504>. } else if (marker.endOffset() > startOffset) {
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->key.get(); // inner loop: process each marker in the current node MarkerList* list = i->value.get(); bool nodeNeedsRepaint = false; for (size_t i = 0; i != list->size(); ++i) { DocumentMarker marker = list->at(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 (auto renderer = node->renderer()) renderer->repaint(); } }
Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType) { Vector<IntRect> 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 MarkerList* list = nodeIterator->value.get(); unsigned markerCount = list->size(); for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { const RenderedDocumentMarker& marker = list->at(markerIndex); // skip marker that is wrong type if (marker.type() != markerType) continue; if (!marker.isRendered()) continue; result.append(marker.renderedRect()); } } return result; }
DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& 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 MarkerList* list = nodeIterator->value.get(); unsigned markerCount = list->size(); for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) { RenderedDocumentMarker& marker = list->at(markerIndex); // skip marker that is wrong type if (marker.type() != markerType) continue; if (marker.contains(point)) return ▮ } } return 0; }
PassRefPtr<SpellcheckRangeList> DocumentMarkerController::userSpellingMarkersForNode(Node* node) const { RefPtr<SpellcheckRangeList> result = SpellcheckRangeList::create(); Node* spellingNode = userSpellingNode(node); if (!spellingNode) return result; MarkerList* list = m_markers.get(spellingNode); if (!list) return result; for (size_t i = 0; i < list->size(); ++i) { const DocumentMarker& marker = list->at(i); if (marker.length() > 0) { RefPtr<DOMStringList> suggestions = DOMStringList::create(); Vector<String> descriptions; marker.description().split('\n', descriptions); for (size_t j = 0; j < descriptions.size(); ++j) suggestions->append(descriptions[j]); result->append(SpellcheckRange::create(marker.startOffset(), marker.length(), suggestions.release(), 0)); } } return result; }
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<unsigned>(range->endOffset()) : std::numeric_limits<unsigned>::max(); MarkerList* list = m_markers.get(node); if (!list) continue; for (size_t i = 0; i < list->size(); ++i) { DocumentMarker& marker = list->at(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.clearDescription(); } } }
void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset, unsigned endOffset, bool active) { MarkerList* list = m_markers.get(node); if (!list) return; bool docDirty = false; for (size_t i = 0; i != list->size(); ++i) { DocumentMarker& marker = list->at(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.setActiveMatch(active); docDirty = true; } // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); }
void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, int delta) { if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) return; ASSERT(!m_markers.isEmpty()); MarkerList* list = m_markers.get(node); if (!list) return; bool docDirty = false; for (size_t i = 0; i != list->size(); ++i) { RenderedDocumentMarker& marker = list->at(i); if (marker.startOffset() >= startOffset) { ASSERT((int)marker.startOffset() + delta >= 0); marker.shiftOffsets(delta); docDirty = true; // Marker moved, so previously-computed rendered rectangle is now invalid marker.invalidate(); } } // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); }
void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker) { ASSERT(newMarker.endOffset() >= newMarker.startOffset()); if (newMarker.endOffset() == newMarker.startOffset()) return; m_possiblyExistingMarkerTypes.add(newMarker.type()); MarkerList* list = m_markers.get(node); if (!list) { list = new MarkerList; list->append(RenderedDocumentMarker(newMarker)); m_markers.set(node, list); } else { RenderedDocumentMarker toInsert(newMarker); size_t numMarkers = list->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 = list->at(i); if (marker.startOffset() > toInsert.startOffset()) break; if (marker.type() == toInsert.type() && marker.endOffset() >= toInsert.startOffset()) { toInsert.setStartOffset(marker.startOffset()); list->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 = list->at(j); if (marker.startOffset() > toInsert.endOffset()) break; if (marker.type() == toInsert.type()) { list->remove(j); if (toInsert.endOffset() <= marker.endOffset()) { toInsert.setEndOffset(marker.endOffset()); break; } numMarkers--; } else j++; } // At this point i points to the node before which we want to insert. list->insert(i, RenderedDocumentMarker(toInsert)); } // repaint the affected node if (node->renderer()) node->renderer()->repaint(); }
void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& 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 MarkerList* list = i->value.get(); for (size_t listIndex = 0; listIndex < list->size(); ++listIndex) list->at(listIndex).invalidate(r); } }
// FIXME: Should be removed after all relevant patches are landed Vector<DocumentMarker> DocumentMarkerController::markersForNode(Node* node) { Vector<DocumentMarker> result; MarkerList* list = m_markers.get(node); if (!list) return result; for (size_t i = 0; i < list->size(); ++i) result.append(list->at(i)); return result; }
Vector<RenderedDocumentMarker*> DocumentMarkerController::markersFor(Node* node, DocumentMarker::MarkerTypes markerTypes) { Vector<RenderedDocumentMarker*> result; MarkerList* list = m_markers.get(node); if (!list) return result; for (size_t i = 0; i < list->size(); ++i) { if (markerTypes.contains(list->at(i).type())) result.append(&(list->at(i))); } return result; }
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); MarkerList* list = nodeIterator->second; for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerIndex) { const DocumentMarker& marker = list->at(markerIndex); fprintf(stderr, " %d:[%d:%d](%d)", marker.type(), marker.startOffset(), marker.endOffset(), marker.activeMatch()); } fprintf(stderr, "\n"); } }
// 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()); MarkerList* list = m_markers.get(srcNode); if (!list) return; bool docDirty = false; unsigned endOffset = startOffset + length - 1; for (size_t i = 0; i != list->size(); ++i) { DocumentMarker marker = list->at(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.setStartOffset(startOffset); if (marker.endOffset() > endOffset) marker.setEndOffset(endOffset); marker.shiftOffsets(delta); addMarker(dstNode, marker); } // repaint the affected node if (docDirty && dstNode->renderer()) dstNode->renderer()->repaint(); }
void DocumentMarkerController::removeMarkersFromList(MarkerMap::iterator iterator, DocumentMarker::MarkerTypes markerTypes) { bool needsRepainting = false; bool listCanBeRemoved; if (markerTypes == DocumentMarker::AllMarkers()) { needsRepainting = true; listCanBeRemoved = true; } else { MarkerList* list = iterator->value.get(); for (size_t i = 0; i != list->size(); ) { DocumentMarker marker = list->at(i); // skip nodes that are not of the specified type if (!markerTypes.contains(marker.type())) { ++i; continue; } // pitch the old marker list->remove(i); needsRepainting = true; // i now is the index of the next marker } listCanBeRemoved = list->isEmpty(); } if (needsRepainting) { if (auto renderer = iterator->key->renderer()) renderer->repaint(); } if (listCanBeRemoved) { m_markers.remove(iterator); if (m_markers.isEmpty()) m_possiblyExistingMarkerTypes = 0; } }
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())); MarkerList* list = m_markers.get(node); if (!list) return; bool docDirty = false; unsigned endOffset = startOffset + length; for (size_t i = 0; i < list->size();) { DocumentMarker marker = list->at(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 list->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.setEndOffset(startOffset); list->insert(i, RenderedDocumentMarker(newLeft)); // i now points to the newly-inserted node, but we want to skip that one i++; } if (marker.endOffset() > endOffset) { DocumentMarker newRight = marker; newRight.setStartOffset(endOffset); list->insert(i, RenderedDocumentMarker(newRight)); // i now points to the newly-inserted node, but we want to skip that one i++; } } if (list->isEmpty()) { m_markers.remove(node); if (m_markers.isEmpty()) m_possiblyExistingMarkerTypes = 0; } // repaint the affected node if (docDirty && node->renderer()) node->renderer()->repaint(); }