DocumentMarkerController.cpp [plain text]
#include "config.h"
#include "DocumentMarkerController.h"
#include "NodeTraversal.h"
#include "Range.h"
#include "RenderBlockFlow.h"
#include "RenderText.h"
#include "RenderedDocumentMarker.h"
#include "TextIterator.h"
#include <stdio.h>
namespace WebCore {
inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerTypes types)
{
return m_possiblyExistingMarkerTypes.intersects(types);
}
DocumentMarkerController::DocumentMarkerController()
: m_possiblyExistingMarkerTypes(0)
{
}
DocumentMarkerController::~DocumentMarkerController()
{
}
void DocumentMarkerController::detach()
{
m_markers.clear();
m_possiblyExistingMarkerTypes = 0;
}
void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description)
{
for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
RefPtr<Range> textPiece = markedText.range();
addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description));
}
}
void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type)
{
for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
RefPtr<Range> textPiece = markedText.range();
addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset()));
}
}
void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type)
{
addMarker(node, DocumentMarker(type, startOffset, startOffset + length));
}
void DocumentMarkerController::addMarkerToNode(Node* node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, PassRefPtr<DocumentMarkerDetails> details)
{
addMarker(node, DocumentMarker(type, startOffset, startOffset + length, details));
}
void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activeMatch)
{
for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
RefPtr<Range> textPiece = markedText.range();
unsigned startOffset = textPiece->startOffset();
unsigned endOffset = textPiece->endOffset();
addMarker(textPiece->startContainer(), DocumentMarker(startOffset, endOffset, activeMatch));
if (endOffset > startOffset) {
Node* node = textPiece->startContainer();
Vector<RenderedDocumentMarker*> markers = markersFor(node);
markers[markers.size() - 1]->setRenderedRect(range->boundingBox());
}
}
}
#if PLATFORM(IOS)
void DocumentMarkerController::addMarker(Range* range, DocumentMarker::MarkerType type, const String& description, const Vector<String>& interpretations, const RetainPtr<id>& metadata)
{
for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
RefPtr<Range> textPiece = markedText.range();
addMarker(textPiece->startContainer(), DocumentMarker(type, textPiece->startOffset(), textPiece->endOffset(), description, interpretations, metadata));
}
}
void DocumentMarkerController::addDictationPhraseWithAlternativesMarker(Range* range, const Vector<String>& interpretations)
{
ASSERT(interpretations.size() > 1);
if (interpretations.size() <= 1)
return;
size_t numberOfAlternatives = interpretations.size() - 1;
for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
RefPtr<Range> textPiece = markedText.range();
DocumentMarker marker(DocumentMarker::DictationPhraseWithAlternatives, textPiece->startOffset(), textPiece->endOffset(), "", Vector<String>(numberOfAlternatives), RetainPtr<id>());
for (size_t i = 0; i < numberOfAlternatives; ++i)
marker.setAlternative(interpretations[i + 1], i);
addMarker(textPiece->startContainer(), marker);
}
}
void DocumentMarkerController::addDictationResultMarker(Range* range, const RetainPtr<id>& metadata)
{
for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
RefPtr<Range> textPiece = markedText.range();
addMarker(textPiece->startContainer(), DocumentMarker(DocumentMarker::DictationResult, textPiece->startOffset(), textPiece->endOffset(), String(), Vector<String>(), metadata));
}
}
#endif
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<Range> textPiece = markedText.range();
int startOffset = textPiece->startOffset();
int endOffset = textPiece->endOffset();
removeMarkers(textPiece->startContainer(), startOffset, endOffset - startOffset, markerTypes, shouldRemovePartiallyOverlappingMarker);
}
}
void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMarker)
{
ASSERT(newMarker.endOffset() >= newMarker.startOffset());
if (newMarker.endOffset() == newMarker.startOffset())
return;
if (auto* renderer = node->renderer()) {
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) {
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;
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;
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++;
}
list->insert(i, RenderedDocumentMarker(toInsert));
}
if (node->renderer())
node->renderer()->repaint();
}
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);
if (marker.startOffset() > endOffset)
break;
if (marker.endOffset() < startOffset)
continue;
docDirty = true;
if (marker.startOffset() < startOffset)
marker.setStartOffset(startOffset);
if (marker.endOffset() > endOffset)
marker.setEndOffset(endOffset);
marker.shiftOffsets(delta);
addMarker(dstNode, marker);
}
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()));
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);
if (marker.startOffset() >= endOffset)
break;
if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
i++;
continue;
}
docDirty = true;
list->remove(i);
if (shouldRemovePartiallyOverlappingMarker)
continue;
if (startOffset > marker.startOffset()) {
DocumentMarker newLeft = marker;
newLeft.setEndOffset(startOffset);
list->insert(i, RenderedDocumentMarker(newLeft));
i++;
}
if (marker.endOffset() > endOffset) {
DocumentMarker newRight = marker;
newRight.setStartOffset(endOffset);
list->insert(i, RenderedDocumentMarker(newRight));
i++;
}
}
if (list->isEmpty()) {
m_markers.remove(node);
if (m_markers.isEmpty())
m_possiblyExistingMarkerTypes = 0;
}
if (docDirty && node->renderer())
node->renderer()->repaint();
}
DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType markerType)
{
if (!possiblyHasMarkers(markerType))
return 0;
ASSERT(!(m_markers.isEmpty()));
MarkerMap::iterator end = m_markers.end();
for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
MarkerList* list = nodeIterator->value.get();
unsigned markerCount = list->size();
for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
RenderedDocumentMarker& marker = list->at(markerIndex);
if (marker.type() != markerType)
continue;
if (marker.contains(point))
return ▮
}
}
return 0;
}
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;
}
Vector<RenderedDocumentMarker*> DocumentMarkerController::markersInRange(Range* range, DocumentMarker::MarkerTypes markerTypes)
{
if (!possiblyHasMarkers(markerTypes))
return Vector<RenderedDocumentMarker*>();
Vector<RenderedDocumentMarker*> 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)) {
for (auto* marker : markersFor(node)) {
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;
}
Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType markerType)
{
Vector<IntRect> result;
if (!possiblyHasMarkers(markerType))
return result;
ASSERT(!(m_markers.isEmpty()));
MarkerMap::iterator end = m_markers.end();
for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != end; ++nodeIterator) {
MarkerList* list = nodeIterator->value.get();
unsigned markerCount = list->size();
for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex) {
const RenderedDocumentMarker& marker = list->at(markerIndex);
if (marker.type() != markerType)
continue;
if (!marker.isRendered())
continue;
result.append(marker.renderedRect());
}
}
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())
removeMarkersFromList(iterator, markerTypes);
}
void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerTypes)
{
if (!possiblyHasMarkers(markerTypes))
return;
ASSERT(!m_markers.isEmpty());
Vector<RefPtr<Node>> nodesWithMarkers;
copyKeysToVector(m_markers, nodesWithMarkers);
unsigned size = nodesWithMarkers.size();
for (unsigned i = 0; i < size; ++i) {
MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]);
if (iterator != m_markers.end())
removeMarkersFromList(iterator, markerTypes);
}
m_possiblyExistingMarkerTypes.remove(markerTypes);
}
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);
if (!markerTypes.contains(marker.type())) {
++i;
continue;
}
list->remove(i);
needsRepainting = true;
}
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::repaintMarkers(DocumentMarker::MarkerTypes markerTypes)
{
if (!possiblyHasMarkers(markerTypes))
return;
ASSERT(!m_markers.isEmpty());
MarkerMap::iterator end = m_markers.end();
for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
Node* node = i->key.get();
MarkerList* list = i->value.get();
bool nodeNeedsRepaint = false;
for (size_t i = 0; i != list->size(); ++i) {
DocumentMarker marker = list->at(i);
if (markerTypes.contains(marker.type())) {
nodeNeedsRepaint = true;
break;
}
}
if (!nodeNeedsRepaint)
continue;
if (auto renderer = node->renderer())
renderer->repaint();
}
}
void DocumentMarkerController::invalidateRenderedRectsForMarkersInRect(const LayoutRect& r)
{
MarkerMap::iterator end = m_markers.end();
for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) {
MarkerList* list = i->value.get();
for (size_t listIndex = 0; listIndex < list->size(); ++listIndex)
list->at(listIndex).invalidate(r);
}
}
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.invalidate();
#if !PLATFORM(IOS)
}
#else
} else if (marker.endOffset() > startOffset) {
if (marker.endOffset() + delta <= marker.startOffset()) {
list->remove(i);
continue;
}
marker.setEndOffset(targetEndOffset < node->maxCharacterOffset() ? targetEndOffset : node->maxCharacterOffset());
docDirty = true;
marker.invalidate();
}
#endif
++i;
}
if (docDirty && node->renderer())
node->renderer()->repaint();
}
void DocumentMarkerController::setMarkersActive(Range* range, bool active)
{
if (!possiblyHasMarkers(DocumentMarker::AllMarkers()))
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 = NodeTraversal::next(*node)) {
int startOffset = node == startContainer ? range->startOffset() : 0;
int endOffset = node == endContainer ? range->endOffset() : INT_MAX;
setMarkersActive(node, startOffset, endOffset, active);
}
}
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);
if (marker.startOffset() >= endOffset)
break;
if (marker.endOffset() < startOffset || marker.type() != DocumentMarker::TextMatch)
continue;
marker.setActiveMatch(active);
docDirty = true;
}
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 = NodeTraversal::next(*node)) {
for (auto* marker : markersFor(node)) {
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;
}
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 = NodeTraversal::next(*node)) {
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);
if (marker.startOffset() >= endOffset)
break;
if (marker.endOffset() <= startOffset || !markerTypes.contains(marker.type())) {
i++;
continue;
}
marker.clearDetails();
}
}
}
#if ENABLE(TREE_DEBUGGING)
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->key.get();
fprintf(stderr, "%p", node);
MarkerList* list = nodeIterator->value.get();
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");
}
}
#endif
}
#if ENABLE(TREE_DEBUGGING)
void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
{
if (controller)
controller->showMarkers();
}
#endif