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::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(); } }
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<DocumentMarker*> markers = markersFor(node); Vector<DocumentMarker*>::const_iterator end = markers.end(); for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) { DocumentMarker* marker = *it; if (!markerTypes.contains(marker->type())) continue; if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset())) continue; if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset())) continue; return true; } } return false; }
Vector<DocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes) { if (!possiblyHasMarkers(markerTypes)) return Vector<DocumentMarker*>(); Vector<DocumentMarker*> 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 = NodeTraversal::next(node)) { Vector<DocumentMarker*> markers = markersFor(node); Vector<DocumentMarker*>::const_iterator end = markers.end(); for (Vector<DocumentMarker*>::const_iterator it = markers.begin(); it != end; ++it) { DocumentMarker* marker = *it; if (!markerTypes.contains(marker->type())) continue; if (node == startContainer && marker->endOffset() <= static_cast<unsigned>(range->startOffset())) continue; if (node == endContainer && marker->startOffset() >= static_cast<unsigned>(range->endOffset())) continue; foundMarkers.append(marker); } } return foundMarkers; }
void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker) { ASSERT(newMarker.endOffset() >= newMarker.startOffset()); if (newMarker.endOffset() == newMarker.startOffset()) return; m_possiblyExistingMarkerTypes.add(newMarker.type()); OwnPtr<MarkerLists>& markers = m_markers.add(node, nullptr).storedValue->value; if (!markers) { markers = adoptPtr(new MarkerLists); markers->grow(DocumentMarker::MarkerTypeIndexesCount); } DocumentMarker::MarkerTypeIndex markerListIndex = MarkerTypeToMarkerIndex(newMarker.type()); if (!markers->at(markerListIndex)) { markers->insert(markerListIndex, adoptPtr(new MarkerList)); } OwnPtr<MarkerList>& list = markers->at(markerListIndex); if (list->isEmpty() || list->last()->endOffset() < newMarker.startOffset()) { list->append(RenderedDocumentMarker::create(newMarker)); } else { DocumentMarker toInsert(newMarker); if (toInsert.type() != DocumentMarker::TextMatch) { mergeOverlapping(list.get(), toInsert); } else { MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), &toInsert, startsFurther); list->insert(pos - list->begin(), RenderedDocumentMarker::create(toInsert)); } } // repaint the affected node if (node->renderer()) node->renderer()->doNotUseInvalidatePaintForWholeRendererSynchronously(); }
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) { const Node* node = nodeIterator->key; fprintf(stderr, "%p", node); MarkerLists* markers = m_markers.get(node); for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) { OwnPtr<MarkerList>& list = (*markers)[markerListIndex]; for (unsigned markerIndex = 0; list.get() && markerIndex < list->size(); ++markerIndex) { DocumentMarker* marker = list->at(markerIndex).get(); fprintf(stderr, " %d:[%d:%d](%d)", marker->type(), marker->startOffset(), marker->endOffset(), marker->activeMatch()); } } fprintf(stderr, "\n"); } }
// This function may release node and vectorPair. void DocumentMarkerController::removeMarkersFromList(Node* node, MarkerList* list, DocumentMarker::MarkerTypes markerTypes) { if (markerTypes == DocumentMarker::AllMarkers()) { delete list; m_markers.remove(node); if (RenderObject* renderer = node->renderer()) renderer->repaint(); } else { bool needsRepaint = false; 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); 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 (list->isEmpty()) { m_markers.remove(node); delete list; } } if (m_markers.isEmpty()) m_possiblyExistingMarkerTypes = 0; }
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 SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(RenderSVGInlineText* textRenderer) { ASSERT(textRenderer); Node* node = textRenderer->node(); if (!node || !node->inDocument()) return; RenderStyle* style = textRenderer->style(); ASSERT(style); AffineTransform fragmentTransform; Document* document = textRenderer->document(); Vector<DocumentMarker*> markers = document->markers()->markersFor(textRenderer->node()); Vector<DocumentMarker*>::iterator markerEnd = markers.end(); for (Vector<DocumentMarker*>::iterator markerIt = markers.begin(); markerIt != markerEnd; ++markerIt) { DocumentMarker* marker = *markerIt; // SVG is only interessted in the TextMatch marker, for now. if (marker->type() != DocumentMarker::TextMatch) continue; FloatRect markerRect; for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (!box->isSVGInlineTextBox()) continue; SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box); int markerStartPosition = max<int>(marker->startOffset() - textBox->start(), 0); int markerEndPosition = min<int>(marker->endOffset() - textBox->start(), textBox->len()); if (markerStartPosition >= markerEndPosition) continue; int fragmentStartPosition = 0; int fragmentEndPosition = 0; const Vector<SVGTextFragment>& fragments = textBox->textFragments(); unsigned textFragmentsSize = fragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { const SVGTextFragment& fragment = fragments.at(i); fragmentStartPosition = markerStartPosition; fragmentEndPosition = markerEndPosition; if (!textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition)) continue; FloatRect fragmentRect = textBox->selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) fragmentRect = fragmentTransform.mapRect(fragmentRect); markerRect.unite(fragmentRect); } } toRenderedDocumentMarker(marker)->setRenderedRect(textRenderer->localToAbsoluteQuad(markerRect).enclosingBoundingBox()); } }
bool SpellChecker::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const { Node* node = findFirstMarkable(m_frame.selection().start().deprecatedNode()); if (!node) return false; unsigned startOffset = static_cast<unsigned>(from); unsigned endOffset = static_cast<unsigned>(from + length); DocumentMarkerVector markers = m_frame.document()->markers().markersFor(node); for (size_t i = 0; i < markers.size(); ++i) { DocumentMarker* marker = markers[i]; if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType) return true; } return false; }
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(); }
void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker) { ASSERT(newMarker.endOffset() >= newMarker.startOffset()); if (newMarker.endOffset() == newMarker.startOffset()) return; if (auto* renderer = node->renderer()) { // FIXME: Factor the marker painting code out of InlineTextBox and teach simple line layout to use it. if (is<RenderText>(*renderer)) downcast<RenderText>(*renderer).ensureLineBoxes(); else if (is<RenderBlockFlow>(*renderer)) downcast<RenderBlockFlow>(*renderer).ensureLineBoxes(); } m_possiblyExistingMarkerTypes.add(newMarker.type()); std::unique_ptr<MarkerList>& list = m_markers.add(node, nullptr).iterator->value; if (!list) { list = std::make_unique<MarkerList>(); list->append(RenderedDocumentMarker(newMarker)); #if PLATFORM(IOS) } else if (newMarker.type() == DocumentMarker::DictationPhraseWithAlternatives || newMarker.type() == DocumentMarker::DictationResult) { // We don't merge dictation markers. size_t i; size_t numberOfMarkers = list->size(); for (i = 0; i < numberOfMarkers; ++i) { DocumentMarker marker = list->at(i); if (marker.startOffset() > newMarker.startOffset()) break; } list->insert(i, RenderedDocumentMarker(newMarker)); #endif } 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(); }