bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, const Text& textNode) const { unsigned start = documentMarker.startOffset(); unsigned length = documentMarker.endOffset() - documentMarker.startOffset(); String markerText = textNode.data().substring(start, length); return m_words.contains(markerText); }
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(); }
PassRefPtr<Range> Internals::markerRangeForNode(Node* node, const String& markerType, unsigned index, ExceptionCode& ec) { DocumentMarker* marker = markerAt(node, markerType, index, ec); if (!marker) return 0; return Range::create(node->document(), node, marker->startOffset(), node, marker->endOffset()); }
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; }
static String selectMisspellingAsync(Frame* selectedFrame, DocumentMarker& marker) { VisibleSelection selection = selectedFrame->selection()->selection(); if (!selection.isCaretOrRange()) return String(); // Caret and range selections always return valid normalized ranges. RefPtr<Range> selectionRange = selection.toNormalizedRange(); Vector<DocumentMarker*> markers = selectedFrame->document()->markers()->markersInRange(selectionRange.get(), DocumentMarker::Spelling | DocumentMarker::Grammar); if (markers.size() != 1) return String(); marker = *markers[0]; // Cloning a range fails only for invalid ranges. RefPtr<Range> markerRange = selectionRange->cloneRange(ASSERT_NO_EXCEPTION); markerRange->setStart(markerRange->startContainer(), marker.startOffset()); markerRange->setEnd(markerRange->endContainer(), marker.endOffset()); if (selection.isCaret()) { selection = VisibleSelection(markerRange.get()); selectedFrame->selection()->setSelection(selection, WordGranularity); selectionRange = selection.toNormalizedRange(); } if (markerRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation) != selectionRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation)) return String(); return markerRange->text(); }
// 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()); MarkerLists* markers = m_markers.get(srcNode); if (!markers) return; bool docDirty = false; for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTypeIndexesCount; ++markerListIndex) { OwnPtr<MarkerList>& list = (*markers)[markerListIndex]; if (!list) continue; unsigned endOffset = startOffset + length - 1; MarkerList::iterator startPos = std::lower_bound(list->begin(), list->end(), startOffset, doesNotInclude); for (MarkerList::iterator i = startPos; i != list->end(); ++i) { DocumentMarker* marker = i->get(); // stop if we are now past the specified range if (marker->startOffset() > endOffset) break; // 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()->doNotUseInvalidatePaintForWholeRendererSynchronously(); }
// 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::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(); }
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::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"); } }
static String selectMisspellingAsync(LocalFrame* selectedFrame, DocumentMarker& marker) { VisibleSelection selection = selectedFrame->selection().selection(); if (!selection.isCaretOrRange()) return String(); // Caret and range selections always return valid normalized ranges. RefPtrWillBeRawPtr<Range> selectionRange = selection.toNormalizedRange(); WillBeHeapVector<DocumentMarker*> markers = selectedFrame->document()->markers().markersInRange(selectionRange.get(), DocumentMarker::MisspellingMarkers()); if (markers.size() != 1) return String(); marker = *markers[0]; // Cloning a range fails only for invalid ranges. RefPtrWillBeRawPtr<Range> markerRange = selectionRange->cloneRange(); markerRange->setStart(markerRange->startContainer(), marker.startOffset()); markerRange->setEnd(markerRange->endContainer(), marker.endOffset()); if (markerRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation) != selectionRange->text().stripWhiteSpace(&IsWhiteSpaceOrPunctuation)) return String(); return markerRange->text(); }
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()); } }
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(); }