blob: 99155c5f5128db4a3e5e62b1177480dbe8dbe1c1 [file] [log] [blame]
dbates@webkit.org858331b2010-08-22 21:41:37 +00001/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
darin@apple.comb13b7de2020-04-06 17:21:02 +00006 * Copyright (C) 2004-2020 Apple Inc. All rights reserved.
dbates@webkit.org858331b2010-08-22 21:41:37 +00007 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 *
25 */
26
27#include "config.h"
28#include "DocumentMarkerController.h"
29
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +000030#include "Chrome.h"
31#include "ChromeClient.h"
cdumez@apple.comc518f7e2018-04-03 18:01:41 +000032#include "Frame.h"
antti@apple.com5d47b582012-12-11 00:13:29 +000033#include "NodeTraversal.h"
achristensen@apple.come5945672016-06-13 21:58:53 +000034#include "Page.h"
antti@apple.comee542e02014-05-22 13:05:20 +000035#include "RenderBlockFlow.h"
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +000036#include "RenderLayer.h"
antti@apple.comee542e02014-05-22 13:05:20 +000037#include "RenderText.h"
morrita@google.com64550052011-04-27 23:09:01 +000038#include "RenderedDocumentMarker.h"
dbates@webkit.org858331b2010-08-22 21:41:37 +000039#include "TextIterator.h"
rniwa@webkit.orgb0156602011-06-23 01:20:45 +000040#include <stdio.h>
rniwa@webkit.orgb0156602011-06-23 01:20:45 +000041
dbates@webkit.org858331b2010-08-22 21:41:37 +000042namespace WebCore {
43
dbates@webkit.orgbad49a42017-07-18 02:43:07 +000044inline bool DocumentMarkerController::possiblyHasMarkers(OptionSet<DocumentMarker::MarkerType> types)
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +000045{
antti@apple.coma10740e2018-08-13 19:16:10 +000046 return m_possiblyExistingMarkerTypes.containsAny(types);
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +000047}
48
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +000049DocumentMarkerController::DocumentMarkerController(Document& document)
50 : m_document(document)
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +000051{
52}
53
dbates@webkit.orgf21f3ae2017-10-19 23:48:45 +000054DocumentMarkerController::~DocumentMarkerController() = default;
darin@apple.com10abec92013-05-07 00:45:31 +000055
dbates@webkit.org858331b2010-08-22 21:41:37 +000056void DocumentMarkerController::detach()
57{
dbates@webkit.org858331b2010-08-22 21:41:37 +000058 m_markers.clear();
dbates@webkit.orgbad49a42017-07-18 02:43:07 +000059 m_possiblyExistingMarkerTypes = { };
dbates@webkit.org858331b2010-08-22 21:41:37 +000060}
61
darin@apple.comb13b7de2020-04-06 17:21:02 +000062auto DocumentMarkerController::collectTextRanges(const SimpleRange& range) -> Vector<TextRange>
antti@apple.comf6a8d6f2019-09-18 15:41:09 +000063{
darin@apple.comb13b7de2020-04-06 17:21:02 +000064 Vector<TextRange> ranges;
65 for (TextIterator iterator(range); !iterator.atEnd(); iterator.advance()) {
66 auto currentRange = iterator.range();
67 ranges.append({ WTFMove(currentRange.start.container), { currentRange.start.offset, currentRange.end.offset } });
dbates@webkit.org858331b2010-08-22 21:41:37 +000068 }
darin@apple.comb13b7de2020-04-06 17:21:02 +000069 return ranges;
dbates@webkit.org858331b2010-08-22 21:41:37 +000070}
71
darin@apple.comb13b7de2020-04-06 17:21:02 +000072void DocumentMarkerController::addMarker(const SimpleRange& range, DocumentMarker::MarkerType type, const DocumentMarker::Data& data)
dbates@webkit.org252c4092013-12-17 01:32:12 +000073{
darin@apple.comb13b7de2020-04-06 17:21:02 +000074 for (auto& textPiece : collectTextRanges(range))
75 addMarker(textPiece.node, { type, textPiece.range, DocumentMarker::Data { data } });
dbates@webkit.org252c4092013-12-17 01:32:12 +000076}
77
darin@apple.comb13b7de2020-04-06 17:21:02 +000078void DocumentMarkerController::addMarker(Text& node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, DocumentMarker::Data&& data)
dbates@webkit.org252c4092013-12-17 01:32:12 +000079{
darin@apple.comb13b7de2020-04-06 17:21:02 +000080 addMarker(node, { type, { startOffset, startOffset + length }, WTFMove(data) });
dbates@webkit.org252c4092013-12-17 01:32:12 +000081}
82
darin@apple.comb13b7de2020-04-06 17:21:02 +000083void DocumentMarkerController::addDraggedContentMarker(const SimpleRange& range)
dbates@webkit.org252c4092013-12-17 01:32:12 +000084{
darin@apple.comb13b7de2020-04-06 17:21:02 +000085 // FIXME: Since the marker is already stored in a map keyed by node, we can probably change things around so we don't have to also store the node in the marker.
86 for (auto& textPiece : collectTextRanges(range))
87 addMarker(textPiece.node, { DocumentMarker::DraggedContent, textPiece.range, RefPtr<Node> { textPiece.node.ptr() } });
dbates@webkit.org252c4092013-12-17 01:32:12 +000088}
darin@apple.com961f2a52016-12-31 09:51:29 +000089
darin@apple.comfed758c2020-07-26 19:12:30 +000090void DocumentMarkerController::removeMarkers(const SimpleRange& range, OptionSet<DocumentMarker::MarkerType> types, RemovePartiallyOverlappingMarker overlapRule)
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +000091{
darin@apple.comb13b7de2020-04-06 17:21:02 +000092 filterMarkers(range, nullptr, types, overlapRule);
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +000093}
94
wenson_hsieh@apple.com65fd03b2022-03-12 00:56:15 +000095void DocumentMarkerController::filterMarkers(const SimpleRange& range, const Function<FilterMarkerResult(const DocumentMarker&)>& filter, OptionSet<DocumentMarker::MarkerType> types, RemovePartiallyOverlappingMarker overlapRule)
timothy_horton@apple.comacbaa982019-03-20 07:32:40 +000096{
darin@apple.comb13b7de2020-04-06 17:21:02 +000097 for (auto& textPiece : collectTextRanges(range)) {
98 if (!possiblyHasMarkers(types))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +000099 return;
100 ASSERT(!m_markers.isEmpty());
darin@apple.comb13b7de2020-04-06 17:21:02 +0000101 removeMarkers(textPiece.node, textPiece.range, types, filter, overlapRule);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000102 }
103}
104
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000105static void updateRenderedRectsForMarker(RenderedDocumentMarker& marker, Node& node)
106{
107 ASSERT(!node.document().view() || !node.document().view()->needsLayout());
darin@apple.com888df882020-08-22 16:16:03 +0000108 marker.setUnclippedAbsoluteRects(boundingBoxes(RenderObject::absoluteTextQuads(makeSimpleRange(node, marker), RenderObject::BoundingRectBehavior::UseSelectionHeight)));
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000109}
110
111void DocumentMarkerController::invalidateRectsForAllMarkers()
112{
113 if (!hasMarkers())
114 return;
115
darin@apple.comb13b7de2020-04-06 17:21:02 +0000116 for (auto& nodeMarkers : m_markers.values()) {
117 for (auto& marker : *nodeMarkers)
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000118 marker.invalidate();
119 }
120
121 if (Page* page = m_document.page())
122 page->chrome().client().didInvalidateDocumentMarkerRects();
123}
124
125void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node)
126{
127 if (!hasMarkers())
128 return;
129
darin@apple.comb13b7de2020-04-06 17:21:02 +0000130 auto markers = m_markers.get(&node);
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000131 ASSERT(markers);
132
133 for (auto& marker : *markers)
134 marker.invalidate();
135
136 if (Page* page = m_document.page())
137 page->chrome().client().didInvalidateDocumentMarkerRects();
138}
139
140static void updateMainFrameLayoutIfNeeded(Document& document)
141{
142 Frame* frame = document.frame();
143 if (!frame)
144 return;
145
146 FrameView* mainFrameView = frame->mainFrame().view();
147 if (!mainFrameView)
148 return;
149
150 mainFrameView->updateLayoutAndStyleIfNeededRecursive();
151}
152
darin@apple.comb13b7de2020-04-06 17:21:02 +0000153void DocumentMarkerController::updateRectsForInvalidatedMarkersOfType(DocumentMarker::MarkerType type)
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000154{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000155 if (!possiblyHasMarkers(type))
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000156 return;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000157 ASSERT(!m_markers.isEmpty());
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000158
darin@apple.comb13b7de2020-04-06 17:21:02 +0000159 bool updatedLayout = false;
160 for (auto& nodeMarkers : m_markers) {
161 for (auto& marker : *nodeMarkers.value) {
162 if (marker.type() != type || marker.isValid())
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000163 continue;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000164 if (!updatedLayout) {
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000165 updateMainFrameLayoutIfNeeded(m_document);
darin@apple.comb13b7de2020-04-06 17:21:02 +0000166 updatedLayout = true;
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000167 }
darin@apple.comb13b7de2020-04-06 17:21:02 +0000168 updateRenderedRectsForMarker(marker, *nodeMarkers.key);
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000169 }
170 }
171}
172
darin@apple.comb13b7de2020-04-06 17:21:02 +0000173Vector<FloatRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker::MarkerType type)
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000174{
175 Vector<FloatRect> result;
176
darin@apple.comb13b7de2020-04-06 17:21:02 +0000177 if (!possiblyHasMarkers(type))
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000178 return result;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000179 ASSERT(!m_markers.isEmpty());
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000180
jiewen_tan@apple.comd9755672017-10-06 20:10:25 +0000181 RefPtr<Frame> frame = m_document.frame();
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000182 if (!frame)
183 return result;
184 FrameView* frameView = frame->view();
185 if (!frameView)
186 return result;
187
darin@apple.comb13b7de2020-04-06 17:21:02 +0000188 updateRectsForInvalidatedMarkersOfType(type);
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000189
190 bool isSubframe = !frame->isMainFrame();
191 IntRect subframeClipRect;
192 if (isSubframe)
193 subframeClipRect = frameView->windowToContents(frameView->windowClipRect());
194
darin@apple.comb13b7de2020-04-06 17:21:02 +0000195 for (auto& nodeMarkers : m_markers) {
196 auto renderer = nodeMarkers.key->renderer();
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000197 FloatRect overflowClipRect;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000198 if (renderer)
clord@igalia.come75ff1e2021-05-20 18:08:36 +0000199 overflowClipRect = renderer->absoluteClippedOverflowRectForRepaint();
darin@apple.comb13b7de2020-04-06 17:21:02 +0000200 for (auto& marker : *nodeMarkers.value) {
201 if (marker.type() != type)
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000202 continue;
203
204 auto renderedRects = marker.unclippedAbsoluteRects();
205
206 // Clip document markers by their overflow clip.
darin@apple.comb13b7de2020-04-06 17:21:02 +0000207 if (renderer) {
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000208 for (auto& rect : renderedRects)
209 rect.intersect(overflowClipRect);
210 }
211
212 // Clip subframe document markers by their frame.
213 if (isSubframe) {
214 for (auto& rect : renderedRects)
215 rect.intersect(subframeClipRect);
216 }
217
218 for (const auto& rect : renderedRects) {
219 if (!rect.isEmpty())
220 result.append(rect);
221 }
222 }
223 }
224
225 return result;
226}
227
darin@apple.comb13b7de2020-04-06 17:21:02 +0000228static bool shouldInsertAsSeparateMarker(const DocumentMarker& marker)
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +0000229{
timothy_horton@apple.comacbaa982019-03-20 07:32:40 +0000230#if ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING)
darin@apple.comb13b7de2020-04-06 17:21:02 +0000231 if (marker.type() == DocumentMarker::PlatformTextChecking)
timothy_horton@apple.comacbaa982019-03-20 07:32:40 +0000232 return true;
233#endif
234
ap@apple.com1e8475922018-10-18 21:38:50 +0000235#if PLATFORM(IOS_FAMILY)
darin@apple.comb13b7de2020-04-06 17:21:02 +0000236 if (marker.type() == DocumentMarker::DictationPhraseWithAlternatives || marker.type() == DocumentMarker::DictationResult)
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +0000237 return true;
238#endif
darin@apple.comb13b7de2020-04-06 17:21:02 +0000239
240 if (marker.type() == DocumentMarker::DraggedContent)
commit-queue@webkit.org3543f362021-10-13 16:17:09 +0000241 return is<RenderReplaced>(std::get<RefPtr<Node>>(marker.data())->renderer());
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +0000242
243 return false;
244}
245
dbates@webkit.org858331b2010-08-22 21:41:37 +0000246// Markers are stored in order sorted by their start offset.
247// Markers of the same type do not overlap each other.
248
darin@apple.comb13b7de2020-04-06 17:21:02 +0000249void DocumentMarkerController::addMarker(Node& node, DocumentMarker&& newMarker)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000250{
morrita@google.com6391bb32011-05-19 01:38:03 +0000251 ASSERT(newMarker.endOffset() >= newMarker.startOffset());
252 if (newMarker.endOffset() == newMarker.startOffset())
dbates@webkit.org858331b2010-08-22 21:41:37 +0000253 return;
254
antti@apple.com9223bcc2018-08-31 17:59:01 +0000255 m_possiblyExistingMarkerTypes.add(newMarker.type());
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000256
darin@apple.comb13b7de2020-04-06 17:21:02 +0000257 auto& list = m_markers.add(&node, nullptr).iterator->value;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000258
morrita@google.com64550052011-04-27 23:09:01 +0000259 if (!list) {
darin@apple.comb13b7de2020-04-06 17:21:02 +0000260 list = makeUnique<Vector<RenderedDocumentMarker>>();
261 list->append(RenderedDocumentMarker(WTFMove(newMarker)));
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +0000262 } else if (shouldInsertAsSeparateMarker(newMarker)) {
dbates@webkit.org252c4092013-12-17 01:32:12 +0000263 // We don't merge dictation markers.
264 size_t i;
265 size_t numberOfMarkers = list->size();
266 for (i = 0; i < numberOfMarkers; ++i) {
267 DocumentMarker marker = list->at(i);
268 if (marker.startOffset() > newMarker.startOffset())
269 break;
270 }
darin@apple.comb13b7de2020-04-06 17:21:02 +0000271 list->insert(i, RenderedDocumentMarker(WTFMove(newMarker)));
dbates@webkit.org858331b2010-08-22 21:41:37 +0000272 } else {
darin@apple.comb13b7de2020-04-06 17:21:02 +0000273 RenderedDocumentMarker toInsert(WTFMove(newMarker));
morrita@google.com64550052011-04-27 23:09:01 +0000274 size_t numMarkers = list->size();
dbates@webkit.org858331b2010-08-22 21:41:37 +0000275 size_t i;
276 // Iterate over all markers whose start offset is less than or equal to the new marker's.
277 // If one of them is of the same type as the new marker and touches it or intersects with it
278 // (there is at most one), remove it and adjust the new marker's start offset to encompass it.
279 for (i = 0; i < numMarkers; ++i) {
morrita@google.com64550052011-04-27 23:09:01 +0000280 DocumentMarker marker = list->at(i);
morrita@google.com6391bb32011-05-19 01:38:03 +0000281 if (marker.startOffset() > toInsert.startOffset())
dbates@webkit.org858331b2010-08-22 21:41:37 +0000282 break;
morrita@google.com6391bb32011-05-19 01:38:03 +0000283 if (marker.type() == toInsert.type() && marker.endOffset() >= toInsert.startOffset()) {
284 toInsert.setStartOffset(marker.startOffset());
morrita@google.com64550052011-04-27 23:09:01 +0000285 list->remove(i);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000286 numMarkers--;
287 break;
288 }
289 }
290 size_t j = i;
291 // Iterate over all markers whose end offset is less than or equal to the new marker's,
292 // removing markers of the same type as the new marker which touch it or intersect with it,
293 // adjusting the new marker's end offset to cover them if necessary.
294 while (j < numMarkers) {
morrita@google.com64550052011-04-27 23:09:01 +0000295 DocumentMarker marker = list->at(j);
morrita@google.com6391bb32011-05-19 01:38:03 +0000296 if (marker.startOffset() > toInsert.endOffset())
dbates@webkit.org858331b2010-08-22 21:41:37 +0000297 break;
morrita@google.com6391bb32011-05-19 01:38:03 +0000298 if (marker.type() == toInsert.type()) {
morrita@google.com64550052011-04-27 23:09:01 +0000299 list->remove(j);
morrita@google.com6391bb32011-05-19 01:38:03 +0000300 if (toInsert.endOffset() <= marker.endOffset()) {
301 toInsert.setEndOffset(marker.endOffset());
dbates@webkit.org858331b2010-08-22 21:41:37 +0000302 break;
303 }
304 numMarkers--;
305 } else
306 j++;
307 }
308 // At this point i points to the node before which we want to insert.
morrita@google.com64550052011-04-27 23:09:01 +0000309 list->insert(i, RenderedDocumentMarker(toInsert));
dbates@webkit.org858331b2010-08-22 21:41:37 +0000310 }
311
darin@apple.com9ee60972019-01-21 19:01:19 +0000312 if (node.renderer())
313 node.renderer()->repaint();
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000314
darin@apple.com9ee60972019-01-21 19:01:19 +0000315 invalidateRectsForMarkersInNode(node);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000316}
317
darin@apple.comb13b7de2020-04-06 17:21:02 +0000318// Copies markers from source to destination, applying the specified shift delta to the copies. The shift is
319// useful if, e.g., the caller has created the destination from a non-prefix substring of the source.
320void DocumentMarkerController::copyMarkers(Node& source, OffsetRange range, Node& destination)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000321{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000322 if (range.start >= range.end)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000323 return;
324
dbates@webkit.orgbad49a42017-07-18 02:43:07 +0000325 if (!possiblyHasMarkers(DocumentMarker::allMarkers()))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000326 return;
327 ASSERT(!m_markers.isEmpty());
328
darin@apple.comb13b7de2020-04-06 17:21:02 +0000329 auto list = m_markers.get(&source);
morrita@google.com64550052011-04-27 23:09:01 +0000330 if (!list)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000331 return;
332
darin@apple.comb13b7de2020-04-06 17:21:02 +0000333 bool needRepaint = false;
hs85.jeong@samsung.com22102292015-10-30 17:04:07 +0000334 for (auto& marker : *list) {
darin@apple.comb13b7de2020-04-06 17:21:02 +0000335 // Stop if we are now past the specified range.
336 if (marker.startOffset() >= range.end)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000337 break;
338
darin@apple.comb13b7de2020-04-06 17:21:02 +0000339 // Skip marker that is before the specified range.
340 if (marker.endOffset() < range.start)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000341 continue;
342
darin@apple.comb13b7de2020-04-06 17:21:02 +0000343 // Pin the marker to the specified range and apply the shift delta.
344 auto copiedMarker = marker;
345 if (copiedMarker.startOffset() < range.start)
346 copiedMarker.setStartOffset(range.start);
347 if (copiedMarker.endOffset() >= range.end)
348 copiedMarker.setEndOffset(range.end);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000349
darin@apple.comb13b7de2020-04-06 17:21:02 +0000350 addMarker(destination, WTFMove(copiedMarker));
351 needRepaint = true;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000352 }
353
darin@apple.comb13b7de2020-04-06 17:21:02 +0000354 if (needRepaint) {
355 if (auto renderer = destination.renderer())
356 renderer->repaint();
357 }
dbates@webkit.org858331b2010-08-22 21:41:37 +0000358}
359
wenson_hsieh@apple.com65fd03b2022-03-12 00:56:15 +0000360void DocumentMarkerController::removeMarkers(Node& node, OffsetRange range, OptionSet<DocumentMarker::MarkerType> types, const Function<FilterMarkerResult(const DocumentMarker&)>& filter, RemovePartiallyOverlappingMarker overlapRule)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000361{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000362 if (range.start >= range.end)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000363 return;
364
darin@apple.comb13b7de2020-04-06 17:21:02 +0000365 if (!possiblyHasMarkers(types))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000366 return;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000367 ASSERT(!m_markers.isEmpty());
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000368
darin@apple.comb13b7de2020-04-06 17:21:02 +0000369 auto list = m_markers.get(&node);
morrita@google.com64550052011-04-27 23:09:01 +0000370 if (!list)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000371 return;
372
darin@apple.comb13b7de2020-04-06 17:21:02 +0000373 bool needRepaint = false;
374 for (size_t i = 0; i < list->size(); ) {
375 auto& marker = list->at(i);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000376
377 // markers are returned in order, so stop if we are now past the specified range
darin@apple.comb13b7de2020-04-06 17:21:02 +0000378 if (marker.startOffset() >= range.end)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000379 break;
380
381 // skip marker that is wrong type or before target
darin@apple.comb13b7de2020-04-06 17:21:02 +0000382 if (marker.endOffset() <= range.start || !types.contains(marker.type())) {
dbates@webkit.org858331b2010-08-22 21:41:37 +0000383 i++;
384 continue;
385 }
386
wenson_hsieh@apple.com65fd03b2022-03-12 00:56:15 +0000387 if (filter && filter(marker) == FilterMarkerResult::Keep) {
timothy_horton@apple.comacbaa982019-03-20 07:32:40 +0000388 i++;
389 continue;
390 }
391
darin@apple.comb13b7de2020-04-06 17:21:02 +0000392 // At this point we know that marker and target intersect in some way.
393 needRepaint = true;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000394
darin@apple.comb13b7de2020-04-06 17:21:02 +0000395 DocumentMarker copiedMarker = marker;
morrita@google.com64550052011-04-27 23:09:01 +0000396 list->remove(i);
darin@apple.comfed758c2020-07-26 19:12:30 +0000397 if (overlapRule == RemovePartiallyOverlappingMarker::Yes)
mitz@apple.comd157c062011-02-04 01:40:37 +0000398 continue;
399
darin@apple.comb13b7de2020-04-06 17:21:02 +0000400 // Add either of the resulting slices that remain after removing target.
401 if (range.start > copiedMarker.startOffset()) {
402 auto newLeft = copiedMarker;
403 newLeft.setEndOffset(range.start);
404 list->insert(i, RenderedDocumentMarker(WTFMove(newLeft)));
dbates@webkit.org858331b2010-08-22 21:41:37 +0000405 i++;
406 }
darin@apple.comb13b7de2020-04-06 17:21:02 +0000407 if (copiedMarker.endOffset() > range.end) {
408 copiedMarker.setStartOffset(range.end);
409 list->insert(i, RenderedDocumentMarker(WTFMove(copiedMarker)));
dbates@webkit.org858331b2010-08-22 21:41:37 +0000410 i++;
411 }
412 }
413
morrita@google.com64550052011-04-27 23:09:01 +0000414 if (list->isEmpty()) {
darin@apple.com9ee60972019-01-21 19:01:19 +0000415 m_markers.remove(&node);
darin@apple.com10abec92013-05-07 00:45:31 +0000416 if (m_markers.isEmpty())
dbates@webkit.orgbad49a42017-07-18 02:43:07 +0000417 m_possiblyExistingMarkerTypes = { };
dbates@webkit.org858331b2010-08-22 21:41:37 +0000418 }
419
darin@apple.comb13b7de2020-04-06 17:21:02 +0000420 if (needRepaint) {
421 if (auto renderer = node.renderer())
422 renderer->repaint();
423 }
dbates@webkit.org858331b2010-08-22 21:41:37 +0000424}
425
darin@apple.comb13b7de2020-04-06 17:21:02 +0000426DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoint& point, DocumentMarker::MarkerType type)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000427{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000428 if (!possiblyHasMarkers(type))
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000429 return nullptr;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000430 ASSERT(!m_markers.isEmpty());
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000431
darin@apple.comb13b7de2020-04-06 17:21:02 +0000432 updateRectsForInvalidatedMarkersOfType(type);
433 for (auto& nodeMarkers : m_markers.values()) {
434 for (auto& marker : *nodeMarkers) {
435 if (marker.type() == type && marker.contains(point))
dbates@webkit.org858331b2010-08-22 21:41:37 +0000436 return &marker;
437 }
438 }
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000439 return nullptr;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000440}
441
darin@apple.comb13b7de2020-04-06 17:21:02 +0000442Vector<RenderedDocumentMarker*> DocumentMarkerController::markersFor(Node& node, OptionSet<DocumentMarker::MarkerType> types)
morrita@google.comc07dd582011-05-27 07:56:25 +0000443{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000444 if (!possiblyHasMarkers(types))
wenson_hsieh@apple.com3acff532017-05-04 22:28:22 +0000445 return { };
446
cdumez@apple.com426611c2014-10-20 05:17:06 +0000447 Vector<RenderedDocumentMarker*> result;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000448 auto list = m_markers.get(&node);
morrita@google.comc07dd582011-05-27 07:56:25 +0000449 if (!list)
450 return result;
451
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000452 for (auto& marker : *list) {
darin@apple.comb13b7de2020-04-06 17:21:02 +0000453 if (types.contains(marker.type()))
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000454 result.append(&marker);
commit-queue@webkit.org35045ab2011-11-10 06:10:59 +0000455 }
morrita@google.comc07dd582011-05-27 07:56:25 +0000456
457 return result;
458}
459
darin@apple.comb13b7de2020-04-06 17:21:02 +0000460void DocumentMarkerController::forEach(const SimpleRange& range, OptionSet<DocumentMarker::MarkerType> types, Function<bool(RenderedDocumentMarker&)> function)
commit-queue@webkit.orgaad7ca22011-04-06 16:09:28 +0000461{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000462 if (!possiblyHasMarkers(types))
463 return;
464 ASSERT(!m_markers.isEmpty());
commit-queue@webkit.orgaad7ca22011-04-06 16:09:28 +0000465
darin@apple.comb13b7de2020-04-06 17:21:02 +0000466 for (auto& node : intersectingNodes(range)) {
467 if (auto list = m_markers.get(&node)) {
468 auto offsetRange = characterDataOffsetRange(range, node);
469 for (auto& marker : *list) {
470 // Markers are stored in order, so stop if we are now past the specified range.
471 if (marker.startOffset() >= offsetRange.end)
472 break;
473 if (marker.endOffset() > offsetRange.start && types.contains(marker.type())) {
474 if (function(marker))
475 return;
476 }
477 }
commit-queue@webkit.orgaad7ca22011-04-06 16:09:28 +0000478 }
479 }
commit-queue@webkit.orgaad7ca22011-04-06 16:09:28 +0000480}
481
darin@apple.comb13b7de2020-04-06 17:21:02 +0000482Vector<RenderedDocumentMarker*> DocumentMarkerController::markersInRange(const SimpleRange& range, OptionSet<DocumentMarker::MarkerType> types)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000483{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000484 // FIXME: Consider making forEach public and changing callers to use that function instead of this one.
485 Vector<RenderedDocumentMarker*> markers;
486 forEach(range, types, [&] (RenderedDocumentMarker& marker) {
487 markers.append(&marker);
488 return false;
489 });
490 return markers;
491}
492
493void DocumentMarkerController::removeMarkers(Node& node, OptionSet<DocumentMarker::MarkerType> types)
494{
495 if (!possiblyHasMarkers(types))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000496 return;
497 ASSERT(!m_markers.isEmpty());
498
darin@apple.com9ee60972019-01-21 19:01:19 +0000499 auto iterator = m_markers.find(&node);
commit-queue@webkit.org08abac62010-10-28 23:45:26 +0000500 if (iterator != m_markers.end())
darin@apple.comb13b7de2020-04-06 17:21:02 +0000501 removeMarkersFromList(iterator, types);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000502}
503
darin@apple.comb13b7de2020-04-06 17:21:02 +0000504void DocumentMarkerController::removeMarkers(OptionSet<DocumentMarker::MarkerType> types)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000505{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000506 if (!possiblyHasMarkers(types))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000507 return;
508 ASSERT(!m_markers.isEmpty());
509
darin@apple.comb13b7de2020-04-06 17:21:02 +0000510 for (auto& node : copyToVector(m_markers.keys()))
511 removeMarkersFromList(m_markers.find(node), types);
512 m_possiblyExistingMarkerTypes.remove(types);
commit-queue@webkit.org08abac62010-10-28 23:45:26 +0000513}
514
darin@apple.comb13b7de2020-04-06 17:21:02 +0000515void DocumentMarkerController::removeMarkersFromList(MarkerMap::iterator iterator, OptionSet<DocumentMarker::MarkerType> types)
commit-queue@webkit.org08abac62010-10-28 23:45:26 +0000516{
darin@apple.com10abec92013-05-07 00:45:31 +0000517 bool needsRepainting = false;
518 bool listCanBeRemoved;
519
darin@apple.comb13b7de2020-04-06 17:21:02 +0000520 if (types == DocumentMarker::allMarkers()) {
darin@apple.com10abec92013-05-07 00:45:31 +0000521 needsRepainting = true;
522 listCanBeRemoved = true;
commit-queue@webkit.org08abac62010-10-28 23:45:26 +0000523 } else {
darin@apple.comb13b7de2020-04-06 17:21:02 +0000524 auto list = iterator->value.get();
darin@apple.com10abec92013-05-07 00:45:31 +0000525
526 for (size_t i = 0; i != list->size(); ) {
morrita@google.com64550052011-04-27 23:09:01 +0000527 DocumentMarker marker = list->at(i);
dbates@webkit.org858331b2010-08-22 21:41:37 +0000528
529 // skip nodes that are not of the specified type
darin@apple.comb13b7de2020-04-06 17:21:02 +0000530 if (!types.contains(marker.type())) {
dbates@webkit.org858331b2010-08-22 21:41:37 +0000531 ++i;
532 continue;
533 }
534
535 // pitch the old marker
morrita@google.com64550052011-04-27 23:09:01 +0000536 list->remove(i);
darin@apple.com10abec92013-05-07 00:45:31 +0000537 needsRepainting = true;
commit-queue@webkit.org08abac62010-10-28 23:45:26 +0000538 // i now is the index of the next marker
dbates@webkit.org858331b2010-08-22 21:41:37 +0000539 }
540
darin@apple.com10abec92013-05-07 00:45:31 +0000541 listCanBeRemoved = list->isEmpty();
dbates@webkit.org858331b2010-08-22 21:41:37 +0000542 }
darin@apple.com10abec92013-05-07 00:45:31 +0000543
544 if (needsRepainting) {
darin@apple.com8cdf7122013-09-30 02:40:50 +0000545 if (auto renderer = iterator->key->renderer())
darin@apple.com10abec92013-05-07 00:45:31 +0000546 renderer->repaint();
547 }
548
549 if (listCanBeRemoved) {
550 m_markers.remove(iterator);
551 if (m_markers.isEmpty())
dbates@webkit.orgbad49a42017-07-18 02:43:07 +0000552 m_possiblyExistingMarkerTypes = { };
darin@apple.com10abec92013-05-07 00:45:31 +0000553 }
dbates@webkit.org858331b2010-08-22 21:41:37 +0000554}
555
darin@apple.comb13b7de2020-04-06 17:21:02 +0000556void DocumentMarkerController::repaintMarkers(OptionSet<DocumentMarker::MarkerType> types)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000557{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000558 if (!possiblyHasMarkers(types))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000559 return;
560 ASSERT(!m_markers.isEmpty());
561
darin@apple.comb13b7de2020-04-06 17:21:02 +0000562 for (auto& nodeMarkers : m_markers) {
563 for (auto& marker : *nodeMarkers.value) {
564 if (types.contains(marker.type())) {
565 if (auto renderer = nodeMarkers.key->renderer())
566 renderer->repaint();
dbates@webkit.org858331b2010-08-22 21:41:37 +0000567 break;
568 }
569 }
dbates@webkit.org858331b2010-08-22 21:41:37 +0000570 }
571}
572
darin@apple.com9ee60972019-01-21 19:01:19 +0000573void DocumentMarkerController::shiftMarkers(Node& node, unsigned startOffset, int delta)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000574{
dbates@webkit.orgbad49a42017-07-18 02:43:07 +0000575 if (!possiblyHasMarkers(DocumentMarker::allMarkers()))
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000576 return;
577 ASSERT(!m_markers.isEmpty());
578
darin@apple.comb13b7de2020-04-06 17:21:02 +0000579 auto list = m_markers.get(&node);
morrita@google.com64550052011-04-27 23:09:01 +0000580 if (!list)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000581 return;
582
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000583 bool didShiftMarker = false;
dbates@webkit.org252c4092013-12-17 01:32:12 +0000584 for (size_t i = 0; i != list->size(); ) {
darin@apple.comb13b7de2020-04-06 17:21:02 +0000585 auto& marker = list->at(i);
586
ap@apple.com1e8475922018-10-18 21:38:50 +0000587#if PLATFORM(IOS_FAMILY)
darin@apple.comb13b7de2020-04-06 17:21:02 +0000588 // FIXME: No obvious reason this should be iOS-specific. Remove the #if at some point.
darin@apple.comb04482e2020-05-20 03:13:49 +0000589 auto targetStartOffset = clampTo<unsigned>(static_cast<int>(marker.startOffset()) + delta);
590 auto targetEndOffset = clampTo<unsigned>(static_cast<int>(marker.endOffset()) + delta);
591 if (targetStartOffset >= node.length() || targetEndOffset <= 0) {
dbates@webkit.org252c4092013-12-17 01:32:12 +0000592 list->remove(i);
593 continue;
594 }
595#endif
darin@apple.comb13b7de2020-04-06 17:21:02 +0000596
morrita@google.com6391bb32011-05-19 01:38:03 +0000597 if (marker.startOffset() >= startOffset) {
598 ASSERT((int)marker.startOffset() + delta >= 0);
599 marker.shiftOffsets(delta);
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000600 didShiftMarker = true;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000601 }
darin@apple.comb13b7de2020-04-06 17:21:02 +0000602#if PLATFORM(IOS_FAMILY)
603 // FIXME: No obvious reason this should be iOS-specific. Remove the #if at some point.
604 else if (marker.endOffset() > startOffset) {
darin@apple.comb04482e2020-05-20 03:13:49 +0000605 if (targetEndOffset <= marker.startOffset()) {
dbates@webkit.org252c4092013-12-17 01:32:12 +0000606 list->remove(i);
607 continue;
608 }
darin@apple.comb04482e2020-05-20 03:13:49 +0000609 marker.setEndOffset(std::min(targetEndOffset, node.length()));
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000610 didShiftMarker = true;
dbates@webkit.org252c4092013-12-17 01:32:12 +0000611 }
612#endif
darin@apple.comb13b7de2020-04-06 17:21:02 +0000613
dbates@webkit.org252c4092013-12-17 01:32:12 +0000614 ++i;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000615 }
616
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000617 if (didShiftMarker) {
darin@apple.com9ee60972019-01-21 19:01:19 +0000618 invalidateRectsForMarkersInNode(node);
darin@apple.comb13b7de2020-04-06 17:21:02 +0000619 if (auto renderer = node.renderer())
620 renderer->repaint();
timothy_horton@apple.com7c91b652015-09-30 19:36:37 +0000621 }
dbates@webkit.org858331b2010-08-22 21:41:37 +0000622}
623
darin@apple.comb13b7de2020-04-06 17:21:02 +0000624bool DocumentMarkerController::hasMarkers(const SimpleRange& range, OptionSet<DocumentMarker::MarkerType> types)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000625{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000626 bool foundMarker = false;
627 forEach(range, types, [&] (RenderedDocumentMarker&) {
628 foundMarker = true;
629 return true;
630 });
631 return foundMarker;
dbates@webkit.org858331b2010-08-22 21:41:37 +0000632}
633
darin@apple.comb13b7de2020-04-06 17:21:02 +0000634void DocumentMarkerController::clearDescriptionOnMarkersIntersectingRange(const SimpleRange& range, OptionSet<DocumentMarker::MarkerType> types)
dbates@webkit.org858331b2010-08-22 21:41:37 +0000635{
darin@apple.comb13b7de2020-04-06 17:21:02 +0000636 forEach(range, types, [&] (RenderedDocumentMarker& marker) {
637 marker.clearData();
commit-queue@webkit.org08abac62010-10-28 23:45:26 +0000638 return false;
darin@apple.comb13b7de2020-04-06 17:21:02 +0000639 });
commit-queue@webkit.org5396fd92011-03-01 20:31:40 +0000640}
641
darin@apple.comfed758c2020-07-26 19:12:30 +0000642void addMarker(const SimpleRange& range, DocumentMarker::MarkerType type, const DocumentMarker::Data& data)
643{
darin@apple.comc8b89492020-08-28 21:37:08 +0000644 range.start.document().markers().addMarker(range, type, data);
darin@apple.comfed758c2020-07-26 19:12:30 +0000645}
646
647void addMarker(Text& node, unsigned startOffset, unsigned length, DocumentMarker::MarkerType type, DocumentMarker::Data&& data)
648{
649 node.document().markers().addMarker(node, startOffset, length, type, WTFMove(data));
650}
651
652void removeMarkers(const SimpleRange& range, OptionSet<DocumentMarker::MarkerType> types, RemovePartiallyOverlappingMarker policy)
653{
darin@apple.comc8b89492020-08-28 21:37:08 +0000654 range.start.document().markers().removeMarkers(range, types, policy);
darin@apple.comfed758c2020-07-26 19:12:30 +0000655}
656
darin@apple.comf91ef172020-08-03 00:47:52 +0000657SimpleRange makeSimpleRange(Node& node, const DocumentMarker& marker)
darin@apple.com5d3decc2020-08-01 15:50:36 +0000658{
659 unsigned startOffset = marker.startOffset();
660 unsigned endOffset = marker.endOffset();
661 return { { node, startOffset }, { node, endOffset } };
662}
663
simon.fraser@apple.comc9f96132015-03-06 18:20:40 +0000664#if ENABLE(TREE_DEBUGGING)
darin@apple.comb13b7de2020-04-06 17:21:02 +0000665
morrita@google.com7a18fee2010-12-13 06:06:19 +0000666void DocumentMarkerController::showMarkers() const
667{
zalan@apple.com29eeb9d2017-06-28 16:45:14 +0000668 fprintf(stderr, "%d nodes have markers:\n", m_markers.size());
darin@apple.comb13b7de2020-04-06 17:21:02 +0000669 for (auto& nodeMarkers : m_markers) {
670 fprintf(stderr, "%p", nodeMarkers.key.get());
671 for (auto& marker : *nodeMarkers.value)
672 fprintf(stderr, " %d:[%d:%d]", marker.type(), marker.startOffset(), marker.endOffset());
673 fputc('\n', stderr);
morrita@google.com7a18fee2010-12-13 06:06:19 +0000674 }
675}
darin@apple.comb13b7de2020-04-06 17:21:02 +0000676
morrita@google.com7a18fee2010-12-13 06:06:19 +0000677#endif
678
dbates@webkit.org858331b2010-08-22 21:41:37 +0000679} // namespace WebCore
morrita@google.com7a18fee2010-12-13 06:06:19 +0000680
simon.fraser@apple.comc9f96132015-03-06 18:20:40 +0000681#if ENABLE(TREE_DEBUGGING)
darin@apple.comb13b7de2020-04-06 17:21:02 +0000682
morrita@google.com7a18fee2010-12-13 06:06:19 +0000683void showDocumentMarkers(const WebCore::DocumentMarkerController* controller)
684{
685 if (controller)
686 controller->showMarkers();
687}
darin@apple.comb13b7de2020-04-06 17:21:02 +0000688
morrita@google.com7a18fee2010-12-13 06:06:19 +0000689#endif