| OLD | NEW |
| (Empty) |
| 1 /* | |
| 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) | |
| 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights
reserved. | |
| 7 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t
orchmobile.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 "core/dom/DocumentMarkerController.h" | |
| 29 | |
| 30 #include "core/dom/Node.h" | |
| 31 #include "core/dom/NodeTraversal.h" | |
| 32 #include "core/dom/Range.h" | |
| 33 #include "core/dom/RenderedDocumentMarker.h" | |
| 34 #include "core/dom/Text.h" | |
| 35 #include "core/editing/iterators/TextIterator.h" | |
| 36 #include "core/frame/FrameView.h" | |
| 37 #include "core/layout/LayoutObject.h" | |
| 38 | |
| 39 #ifndef NDEBUG | |
| 40 #include <stdio.h> | |
| 41 #endif | |
| 42 | |
| 43 namespace blink { | |
| 44 | |
| 45 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) | |
| 46 : m_words(words) | |
| 47 { | |
| 48 } | |
| 49 | |
| 50 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, co
nst Text& textNode) const | |
| 51 { | |
| 52 unsigned start = documentMarker.startOffset(); | |
| 53 unsigned length = documentMarker.endOffset() - documentMarker.startOffset(); | |
| 54 | |
| 55 String markerText = textNode.data().substring(start, length); | |
| 56 return m_words.contains(markerText); | |
| 57 } | |
| 58 | |
| 59 namespace { | |
| 60 | |
| 61 DocumentMarker::MarkerTypeIndex MarkerTypeToMarkerIndex(DocumentMarker::MarkerTy
pe type) | |
| 62 { | |
| 63 switch (type) { | |
| 64 case DocumentMarker::Spelling: | |
| 65 return DocumentMarker::SpellingMarkerIndex; | |
| 66 case DocumentMarker::Grammar: | |
| 67 return DocumentMarker::GramarMarkerIndex; | |
| 68 case DocumentMarker::TextMatch: | |
| 69 return DocumentMarker::TextMatchMarkerIndex; | |
| 70 case DocumentMarker::InvisibleSpellcheck: | |
| 71 return DocumentMarker::InvisibleSpellcheckMarkerIndex; | |
| 72 } | |
| 73 | |
| 74 ASSERT_NOT_REACHED(); | |
| 75 return DocumentMarker::SpellingMarkerIndex; | |
| 76 } | |
| 77 | |
| 78 } // namespace | |
| 79 | |
| 80 inline bool DocumentMarkerController::possiblyHasMarkers(DocumentMarker::MarkerT
ypes types) | |
| 81 { | |
| 82 return m_possiblyExistingMarkerTypes.intersects(types); | |
| 83 } | |
| 84 | |
| 85 DocumentMarkerController::DocumentMarkerController() | |
| 86 : m_possiblyExistingMarkerTypes(0) | |
| 87 { | |
| 88 } | |
| 89 | |
| 90 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(DocumentMarkerController); | |
| 91 | |
| 92 void DocumentMarkerController::clear() | |
| 93 { | |
| 94 m_markers.clear(); | |
| 95 m_possiblyExistingMarkerTypes = 0; | |
| 96 } | |
| 97 | |
| 98 void DocumentMarkerController::addMarker(const Position& start, const Position&
end, DocumentMarker::MarkerType type, const String& description, uint32_t hash) | |
| 99 { | |
| 100 // Use a TextIterator to visit the potentially multiple nodes the range cove
rs. | |
| 101 for (TextIterator markedText(start, end); !markedText.atEnd(); markedText.ad
vance()) { | |
| 102 addMarker(markedText.currentContainer(), DocumentMarker(type, markedText
.startOffsetInCurrentContainer(), markedText.endOffsetInCurrentContainer(), desc
ription, hash)); | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 void DocumentMarkerController::addTextMatchMarker(const Range* range, bool activ
eMatch) | |
| 107 { | |
| 108 // Use a TextIterator to visit the potentially multiple nodes the range cove
rs. | |
| 109 for (TextIterator markedText(range->startPosition(), range->endPosition());
!markedText.atEnd(); markedText.advance()) | |
| 110 addMarker(markedText.currentContainer(), DocumentMarker(markedText.start
OffsetInCurrentContainer(), markedText.endOffsetInCurrentContainer(), activeMatc
h)); | |
| 111 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a
throttling algorithm. crbug.com/6819. | |
| 112 } | |
| 113 | |
| 114 void DocumentMarkerController::prepareForDestruction() | |
| 115 { | |
| 116 clear(); | |
| 117 } | |
| 118 | |
| 119 void DocumentMarkerController::removeMarkers(TextIterator& markedText, DocumentM
arker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldRemo
vePartiallyOverlappingMarker) | |
| 120 { | |
| 121 for (; !markedText.atEnd(); markedText.advance()) { | |
| 122 if (!possiblyHasMarkers(markerTypes)) | |
| 123 return; | |
| 124 ASSERT(!m_markers.isEmpty()); | |
| 125 | |
| 126 int startOffset = markedText.startOffsetInCurrentContainer(); | |
| 127 int endOffset = markedText.endOffsetInCurrentContainer(); | |
| 128 removeMarkers(markedText.currentContainer(), startOffset, endOffset - st
artOffset, markerTypes, shouldRemovePartiallyOverlappingMarker); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 void DocumentMarkerController::removeMarkers(const EphemeralRange& range, Docume
ntMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMarkerOrNot shouldR
emovePartiallyOverlappingMarker) | |
| 133 { | |
| 134 TextIterator markedText(range.startPosition(), range.endPosition()); | |
| 135 DocumentMarkerController::removeMarkers(markedText, markerTypes, shouldRemov
ePartiallyOverlappingMarker); | |
| 136 } | |
| 137 | |
| 138 static bool startsFurther(const OwnPtrWillBeMember<RenderedDocumentMarker>& lhv,
const DocumentMarker* rhv) | |
| 139 { | |
| 140 return lhv->startOffset() < rhv->startOffset(); | |
| 141 } | |
| 142 | |
| 143 static bool startsAfter(const OwnPtrWillBeMember<RenderedDocumentMarker>& marker
, size_t startOffset) | |
| 144 { | |
| 145 return marker->startOffset() < startOffset; | |
| 146 } | |
| 147 | |
| 148 static bool endsBefore(size_t startOffset, const OwnPtrWillBeMember<RenderedDocu
mentMarker>& rhv) | |
| 149 { | |
| 150 return startOffset < rhv->endOffset(); | |
| 151 } | |
| 152 | |
| 153 static bool compareByStart(const RawPtrWillBeMember<DocumentMarker>& lhv, const
RawPtrWillBeMember<DocumentMarker>& rhv) | |
| 154 { | |
| 155 return lhv->startOffset() < rhv->startOffset(); | |
| 156 } | |
| 157 | |
| 158 static bool doesNotOverlap(const OwnPtrWillBeMember<RenderedDocumentMarker>& lhv
, const DocumentMarker* rhv) | |
| 159 { | |
| 160 return lhv->endOffset() < rhv->startOffset(); | |
| 161 } | |
| 162 | |
| 163 static bool doesNotInclude(const OwnPtrWillBeMember<RenderedDocumentMarker>& mar
ker, size_t startOffset) | |
| 164 { | |
| 165 return marker->endOffset() < startOffset; | |
| 166 } | |
| 167 | |
| 168 static bool updateMarkerRenderedRect(Node* node, RenderedDocumentMarker& marker) | |
| 169 { | |
| 170 RefPtrWillBeRawPtr<Range> range = Range::create(node->document()); | |
| 171 // The offsets of the marker may be out-dated, so check for exceptions. | |
| 172 TrackExceptionState exceptionState; | |
| 173 range->setStart(node, marker.startOffset(), exceptionState); | |
| 174 if (!exceptionState.hadException()) | |
| 175 range->setEnd(node, marker.endOffset(), IGNORE_EXCEPTION); | |
| 176 if (exceptionState.hadException()) | |
| 177 return marker.invalidateRenderedRect(); | |
| 178 return marker.setRenderedRect(LayoutRect(range->boundingBox())); | |
| 179 } | |
| 180 | |
| 181 // Markers are stored in order sorted by their start offset. | |
| 182 // Markers of the same type do not overlap each other. | |
| 183 | |
| 184 void DocumentMarkerController::addMarker(Node* node, const DocumentMarker& newMa
rker) | |
| 185 { | |
| 186 ASSERT(newMarker.endOffset() >= newMarker.startOffset()); | |
| 187 if (newMarker.endOffset() == newMarker.startOffset()) | |
| 188 return; | |
| 189 | |
| 190 m_possiblyExistingMarkerTypes.add(newMarker.type()); | |
| 191 | |
| 192 OwnPtrWillBeMember<MarkerLists>& markers = m_markers.add(node, nullptr).stor
edValue->value; | |
| 193 if (!markers) { | |
| 194 markers = adoptPtrWillBeNoop(new MarkerLists); | |
| 195 markers->grow(DocumentMarker::MarkerTypeIndexesCount); | |
| 196 } | |
| 197 | |
| 198 DocumentMarker::MarkerTypeIndex markerListIndex = MarkerTypeToMarkerIndex(ne
wMarker.type()); | |
| 199 if (!markers->at(markerListIndex)) { | |
| 200 markers->insert(markerListIndex, adoptPtrWillBeNoop(new MarkerList)); | |
| 201 } | |
| 202 | |
| 203 OwnPtrWillBeMember<MarkerList>& list = markers->at(markerListIndex); | |
| 204 OwnPtrWillBeRawPtr<RenderedDocumentMarker> newRenderedMarker = RenderedDocum
entMarker::create(newMarker); | |
| 205 updateMarkerRenderedRect(node, *newRenderedMarker); | |
| 206 if (list->isEmpty() || list->last()->endOffset() < newMarker.startOffset())
{ | |
| 207 list->append(newRenderedMarker.release()); | |
| 208 } else { | |
| 209 if (newMarker.type() != DocumentMarker::TextMatch) { | |
| 210 mergeOverlapping(list.get(), newRenderedMarker.release()); | |
| 211 } else { | |
| 212 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end
(), &newMarker, startsFurther); | |
| 213 list->insert(pos - list->begin(), newRenderedMarker.release()); | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 // repaint the affected node | |
| 218 if (node->layoutObject()) | |
| 219 node->layoutObject()->setShouldDoFullPaintInvalidation(); | |
| 220 } | |
| 221 | |
| 222 void DocumentMarkerController::mergeOverlapping(MarkerList* list, PassOwnPtrWill
BeRawPtr<RenderedDocumentMarker> toInsert) | |
| 223 { | |
| 224 MarkerList::iterator firstOverlapping = std::lower_bound(list->begin(), list
->end(), toInsert.get(), doesNotOverlap); | |
| 225 size_t index = firstOverlapping - list->begin(); | |
| 226 list->insert(index, toInsert); | |
| 227 MarkerList::iterator inserted = list->begin() + index; | |
| 228 firstOverlapping = inserted + 1; | |
| 229 for (MarkerList::iterator i = firstOverlapping; i != list->end() && (*i)->st
artOffset() <= (*inserted)->endOffset(); ) { | |
| 230 (*inserted)->setStartOffset(std::min((*inserted)->startOffset(), (*i)->s
tartOffset())); | |
| 231 (*inserted)->setEndOffset(std::max((*inserted)->endOffset(), (*i)->endOf
fset())); | |
| 232 list->remove(i - list->begin()); | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 // copies markers from srcNode to dstNode, applying the specified shift delta to
the copies. The shift is | |
| 237 // useful if, e.g., the caller has created the dstNode from a non-prefix substri
ng of the srcNode. | |
| 238 void DocumentMarkerController::copyMarkers(Node* srcNode, unsigned startOffset,
int length, Node* dstNode, int delta) | |
| 239 { | |
| 240 if (length <= 0) | |
| 241 return; | |
| 242 | |
| 243 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 244 return; | |
| 245 ASSERT(!m_markers.isEmpty()); | |
| 246 | |
| 247 MarkerLists* markers = m_markers.get(srcNode); | |
| 248 if (!markers) | |
| 249 return; | |
| 250 | |
| 251 bool docDirty = false; | |
| 252 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTyp
eIndexesCount; ++markerListIndex) { | |
| 253 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 254 if (!list) | |
| 255 continue; | |
| 256 | |
| 257 unsigned endOffset = startOffset + length - 1; | |
| 258 MarkerList::iterator startPos = std::lower_bound(list->begin(), list->en
d(), startOffset, doesNotInclude); | |
| 259 for (MarkerList::iterator i = startPos; i != list->end(); ++i) { | |
| 260 DocumentMarker* marker = i->get(); | |
| 261 | |
| 262 // stop if we are now past the specified range | |
| 263 if (marker->startOffset() > endOffset) | |
| 264 break; | |
| 265 | |
| 266 // pin the marker to the specified range and apply the shift delta | |
| 267 docDirty = true; | |
| 268 if (marker->startOffset() < startOffset) | |
| 269 marker->setStartOffset(startOffset); | |
| 270 if (marker->endOffset() > endOffset) | |
| 271 marker->setEndOffset(endOffset); | |
| 272 marker->shiftOffsets(delta); | |
| 273 | |
| 274 addMarker(dstNode, *marker); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 // repaint the affected node | |
| 279 if (docDirty && dstNode->layoutObject()) | |
| 280 dstNode->layoutObject()->setShouldDoFullPaintInvalidation(); | |
| 281 } | |
| 282 | |
| 283 void DocumentMarkerController::removeMarkers(Node* node, unsigned startOffset, i
nt length, DocumentMarker::MarkerTypes markerTypes, RemovePartiallyOverlappingMa
rkerOrNot shouldRemovePartiallyOverlappingMarker) | |
| 284 { | |
| 285 if (length <= 0) | |
| 286 return; | |
| 287 | |
| 288 if (!possiblyHasMarkers(markerTypes)) | |
| 289 return; | |
| 290 ASSERT(!(m_markers.isEmpty())); | |
| 291 | |
| 292 MarkerLists* markers = m_markers.get(node); | |
| 293 if (!markers) | |
| 294 return; | |
| 295 | |
| 296 bool docDirty = false; | |
| 297 size_t emptyListsCount = 0; | |
| 298 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTyp
eIndexesCount; ++markerListIndex) { | |
| 299 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 300 if (!list || list->isEmpty()) { | |
| 301 if (list.get() && list->isEmpty()) | |
| 302 list.clear(); | |
| 303 ++emptyListsCount; | |
| 304 continue; | |
| 305 } | |
| 306 if (!markerTypes.contains((*list->begin())->type())) | |
| 307 continue; | |
| 308 unsigned endOffset = startOffset + length; | |
| 309 MarkerList::iterator startPos = std::upper_bound(list->begin(), list->en
d(), startOffset, endsBefore); | |
| 310 for (MarkerList::iterator i = startPos; i != list->end(); ) { | |
| 311 DocumentMarker marker(*i->get()); | |
| 312 | |
| 313 // markers are returned in order, so stop if we are now past the spe
cified range | |
| 314 if (marker.startOffset() >= endOffset) | |
| 315 break; | |
| 316 | |
| 317 // at this point we know that marker and target intersect in some wa
y | |
| 318 docDirty = true; | |
| 319 | |
| 320 // pitch the old marker | |
| 321 list->remove(i - list->begin()); | |
| 322 | |
| 323 if (shouldRemovePartiallyOverlappingMarker) { | |
| 324 // Stop here. Don't add resulting slices back. | |
| 325 continue; | |
| 326 } | |
| 327 | |
| 328 // add either of the resulting slices that are left after removing t
arget | |
| 329 if (startOffset > marker.startOffset()) { | |
| 330 DocumentMarker newLeft = marker; | |
| 331 newLeft.setEndOffset(startOffset); | |
| 332 size_t insertIndex = i - list->begin(); | |
| 333 list->insert(insertIndex, RenderedDocumentMarker::create(newLeft
)); | |
| 334 // Move to the marker after the inserted one. | |
| 335 i = list->begin() + insertIndex + 1; | |
| 336 } | |
| 337 if (marker.endOffset() > endOffset) { | |
| 338 DocumentMarker newRight = marker; | |
| 339 newRight.setStartOffset(endOffset); | |
| 340 size_t insertIndex = i - list->begin(); | |
| 341 list->insert(insertIndex, RenderedDocumentMarker::create(newRigh
t)); | |
| 342 // Move to the marker after the inserted one. | |
| 343 i = list->begin() + insertIndex + 1; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 if (list->isEmpty()) { | |
| 348 list.clear(); | |
| 349 ++emptyListsCount; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 if (emptyListsCount == DocumentMarker::MarkerTypeIndexesCount) { | |
| 354 m_markers.remove(node); | |
| 355 if (m_markers.isEmpty()) | |
| 356 m_possiblyExistingMarkerTypes = 0; | |
| 357 } | |
| 358 | |
| 359 // repaint the affected node | |
| 360 if (docDirty && node->layoutObject()) | |
| 361 node->layoutObject()->setShouldDoFullPaintInvalidation(); | |
| 362 } | |
| 363 | |
| 364 DocumentMarker* DocumentMarkerController::markerContainingPoint(const LayoutPoin
t& point, DocumentMarker::MarkerType markerType) | |
| 365 { | |
| 366 if (!possiblyHasMarkers(markerType)) | |
| 367 return 0; | |
| 368 ASSERT(!(m_markers.isEmpty())); | |
| 369 | |
| 370 // outer loop: process each node that contains any markers | |
| 371 MarkerMap::iterator end = m_markers.end(); | |
| 372 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != e
nd; ++nodeIterator) { | |
| 373 // inner loop; process each marker in this node | |
| 374 MarkerLists* markers = nodeIterator->value.get(); | |
| 375 OwnPtrWillBeMember<MarkerList>& list = (*markers)[MarkerTypeToMarkerInde
x(markerType)]; | |
| 376 unsigned markerCount = list.get() ? list->size() : 0; | |
| 377 for (unsigned markerIndex = 0; markerIndex < markerCount; ++markerIndex)
{ | |
| 378 RenderedDocumentMarker* marker = list->at(markerIndex).get(); | |
| 379 if (marker->contains(point)) | |
| 380 return marker; | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 return 0; | |
| 385 } | |
| 386 | |
| 387 DocumentMarkerVector DocumentMarkerController::markersFor(Node* node, DocumentMa
rker::MarkerTypes markerTypes) | |
| 388 { | |
| 389 DocumentMarkerVector result; | |
| 390 | |
| 391 MarkerLists* markers = m_markers.get(node); | |
| 392 if (!markers) | |
| 393 return result; | |
| 394 | |
| 395 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTyp
eIndexesCount; ++markerListIndex) { | |
| 396 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 397 if (!list || list->isEmpty() || !markerTypes.contains((*list->begin())->
type())) | |
| 398 continue; | |
| 399 | |
| 400 for (size_t i = 0; i < list->size(); ++i) | |
| 401 result.append(list->at(i).get()); | |
| 402 } | |
| 403 | |
| 404 std::sort(result.begin(), result.end(), compareByStart); | |
| 405 return result; | |
| 406 } | |
| 407 | |
| 408 DocumentMarkerVector DocumentMarkerController::markers() | |
| 409 { | |
| 410 DocumentMarkerVector result; | |
| 411 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) { | |
| 412 MarkerLists* markers = i->value.get(); | |
| 413 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::Marke
rTypeIndexesCount; ++markerListIndex) { | |
| 414 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 415 for (size_t j = 0; list.get() && j < list->size(); ++j) | |
| 416 result.append(list->at(j).get()); | |
| 417 } | |
| 418 } | |
| 419 std::sort(result.begin(), result.end(), compareByStart); | |
| 420 return result; | |
| 421 } | |
| 422 | |
| 423 DocumentMarkerVector DocumentMarkerController::markersInRange(const EphemeralRan
ge& range, DocumentMarker::MarkerTypes markerTypes) | |
| 424 { | |
| 425 if (!possiblyHasMarkers(markerTypes)) | |
| 426 return DocumentMarkerVector(); | |
| 427 | |
| 428 DocumentMarkerVector foundMarkers; | |
| 429 | |
| 430 Node* startContainer = range.startPosition().computeContainerNode(); | |
| 431 ASSERT(startContainer); | |
| 432 unsigned startOffset = static_cast<unsigned>(range.startPosition().computeOf
fsetInContainerNode()); | |
| 433 Node* endContainer = range.endPosition().computeContainerNode(); | |
| 434 ASSERT(endContainer); | |
| 435 unsigned endOffset = static_cast<unsigned>(range.endPosition().computeOffset
InContainerNode()); | |
| 436 | |
| 437 Node* pastLastNode = range.endPosition().nodeAsRangePastLastNode(); | |
| 438 for (Node* node = range.startPosition().nodeAsRangeFirstNode(); node != past
LastNode; node = NodeTraversal::next(*node)) { | |
| 439 for (DocumentMarker* marker : markersFor(node)) { | |
| 440 if (!markerTypes.contains(marker->type())) | |
| 441 continue; | |
| 442 if (node == startContainer && marker->endOffset() <= startOffset) | |
| 443 continue; | |
| 444 if (node == endContainer && marker->startOffset() >= endOffset) | |
| 445 continue; | |
| 446 foundMarkers.append(marker); | |
| 447 } | |
| 448 } | |
| 449 return foundMarkers; | |
| 450 } | |
| 451 | |
| 452 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers(DocumentMarker
::MarkerType markerType) | |
| 453 { | |
| 454 Vector<IntRect> result; | |
| 455 | |
| 456 if (!possiblyHasMarkers(markerType)) | |
| 457 return result; | |
| 458 ASSERT(!(m_markers.isEmpty())); | |
| 459 | |
| 460 // outer loop: process each node | |
| 461 MarkerMap::iterator end = m_markers.end(); | |
| 462 for (MarkerMap::iterator nodeIterator = m_markers.begin(); nodeIterator != e
nd; ++nodeIterator) { | |
| 463 // inner loop; process each marker in this node | |
| 464 MarkerLists* markers = nodeIterator->value.get(); | |
| 465 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::Marke
rTypeIndexesCount; ++markerListIndex) { | |
| 466 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 467 if (!list || list->isEmpty() || (*list->begin())->type() != markerTy
pe) | |
| 468 continue; | |
| 469 for (unsigned markerIndex = 0; markerIndex < list->size(); ++markerI
ndex) { | |
| 470 RenderedDocumentMarker* marker = list->at(markerIndex).get(); | |
| 471 if (!marker->isRendered()) | |
| 472 continue; | |
| 473 result.append(marker->renderedRect()); | |
| 474 } | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 return result; | |
| 479 } | |
| 480 | |
| 481 static void invalidatePaintForTickmarks(const Node& node) | |
| 482 { | |
| 483 if (FrameView* frameView = node.document().view()) | |
| 484 frameView->invalidatePaintForTickmarks(); | |
| 485 } | |
| 486 | |
| 487 void DocumentMarkerController::updateRenderedRectsForMarkers() | |
| 488 { | |
| 489 for (auto& nodeMarkers : m_markers) { | |
| 490 const Node* node = nodeMarkers.key; | |
| 491 for (auto& markerList : *nodeMarkers.value) { | |
| 492 if (!markerList) | |
| 493 continue; | |
| 494 bool markersChanged = false; | |
| 495 for (auto& marker : *markerList) | |
| 496 markersChanged |= updateMarkerRenderedRect(const_cast<Node*>(nod
e), *marker); | |
| 497 | |
| 498 if (markersChanged && markerList->first()->type() == DocumentMarker:
:TextMatch) | |
| 499 invalidatePaintForTickmarks(*node); | |
| 500 } | |
| 501 } | |
| 502 } | |
| 503 | |
| 504 DEFINE_TRACE(DocumentMarkerController) | |
| 505 { | |
| 506 #if ENABLE(OILPAN) | |
| 507 visitor->trace(m_markers); | |
| 508 #endif | |
| 509 } | |
| 510 | |
| 511 void DocumentMarkerController::removeMarkers(Node* node, DocumentMarker::MarkerT
ypes markerTypes) | |
| 512 { | |
| 513 if (!possiblyHasMarkers(markerTypes)) | |
| 514 return; | |
| 515 ASSERT(!m_markers.isEmpty()); | |
| 516 | |
| 517 MarkerMap::iterator iterator = m_markers.find(node); | |
| 518 if (iterator != m_markers.end()) | |
| 519 removeMarkersFromList(iterator, markerTypes); | |
| 520 } | |
| 521 | |
| 522 void DocumentMarkerController::removeMarkers(const MarkerRemoverPredicate& shoul
dRemoveMarker) | |
| 523 { | |
| 524 for (auto& nodeMarkers : m_markers) { | |
| 525 const Node& node = *nodeMarkers.key; | |
| 526 if (!node.isTextNode()) // MarkerRemoverPredicate requires a Text node. | |
| 527 continue; | |
| 528 MarkerLists& markers = *nodeMarkers.value; | |
| 529 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::Marke
rTypeIndexesCount; ++markerListIndex) { | |
| 530 OwnPtrWillBeMember<MarkerList>& list = markers[markerListIndex]; | |
| 531 if (!list) | |
| 532 continue; | |
| 533 bool removedMarkers = false; | |
| 534 for (size_t j = list->size(); j > 0; --j) { | |
| 535 if (shouldRemoveMarker(*list->at(j - 1), static_cast<const Text&
>(node))) { | |
| 536 list->remove(j - 1); | |
| 537 removedMarkers = true; | |
| 538 } | |
| 539 } | |
| 540 if (removedMarkers && markerListIndex == DocumentMarker::TextMatchMa
rkerIndex) | |
| 541 invalidatePaintForTickmarks(node); | |
| 542 } | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 void DocumentMarkerController::removeMarkers(DocumentMarker::MarkerTypes markerT
ypes) | |
| 547 { | |
| 548 if (!possiblyHasMarkers(markerTypes)) | |
| 549 return; | |
| 550 ASSERT(!m_markers.isEmpty()); | |
| 551 | |
| 552 Vector<const Node*> nodesWithMarkers; | |
| 553 copyKeysToVector(m_markers, nodesWithMarkers); | |
| 554 unsigned size = nodesWithMarkers.size(); | |
| 555 for (unsigned i = 0; i < size; ++i) { | |
| 556 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); | |
| 557 if (iterator != m_markers.end()) | |
| 558 removeMarkersFromList(iterator, markerTypes); | |
| 559 } | |
| 560 | |
| 561 m_possiblyExistingMarkerTypes.remove(markerTypes); | |
| 562 } | |
| 563 | |
| 564 void DocumentMarkerController::removeMarkersFromList(MarkerMap::iterator iterato
r, DocumentMarker::MarkerTypes markerTypes) | |
| 565 { | |
| 566 bool needsRepainting = false; | |
| 567 bool nodeCanBeRemoved; | |
| 568 | |
| 569 size_t emptyListsCount = 0; | |
| 570 if (markerTypes == DocumentMarker::AllMarkers()) { | |
| 571 needsRepainting = true; | |
| 572 nodeCanBeRemoved = true; | |
| 573 } else { | |
| 574 MarkerLists* markers = iterator->value.get(); | |
| 575 | |
| 576 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::Marke
rTypeIndexesCount; ++markerListIndex) { | |
| 577 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 578 if (!list || list->isEmpty()) { | |
| 579 if (list.get() && list->isEmpty()) | |
| 580 list.clear(); | |
| 581 ++emptyListsCount; | |
| 582 continue; | |
| 583 } | |
| 584 if (markerTypes.contains((*list->begin())->type())) { | |
| 585 list->clear(); | |
| 586 list.clear(); | |
| 587 ++emptyListsCount; | |
| 588 needsRepainting = true; | |
| 589 } | |
| 590 } | |
| 591 | |
| 592 nodeCanBeRemoved = emptyListsCount == DocumentMarker::MarkerTypeIndexesC
ount; | |
| 593 } | |
| 594 | |
| 595 if (needsRepainting) { | |
| 596 const Node& node = *iterator->key; | |
| 597 if (LayoutObject* layoutObject = node.layoutObject()) | |
| 598 layoutObject->setShouldDoFullPaintInvalidation(); | |
| 599 invalidatePaintForTickmarks(node); | |
| 600 } | |
| 601 | |
| 602 if (nodeCanBeRemoved) { | |
| 603 m_markers.remove(iterator); | |
| 604 if (m_markers.isEmpty()) | |
| 605 m_possiblyExistingMarkerTypes = 0; | |
| 606 } | |
| 607 } | |
| 608 | |
| 609 void DocumentMarkerController::repaintMarkers(DocumentMarker::MarkerTypes marker
Types) | |
| 610 { | |
| 611 if (!possiblyHasMarkers(markerTypes)) | |
| 612 return; | |
| 613 ASSERT(!m_markers.isEmpty()); | |
| 614 | |
| 615 // outer loop: process each markered node in the document | |
| 616 MarkerMap::iterator end = m_markers.end(); | |
| 617 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { | |
| 618 const Node* node = i->key; | |
| 619 | |
| 620 // inner loop: process each marker in the current node | |
| 621 MarkerLists* markers = i->value.get(); | |
| 622 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::Marke
rTypeIndexesCount; ++markerListIndex) { | |
| 623 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 624 if (!list || list->isEmpty() || !markerTypes.contains((*list->begin(
))->type())) | |
| 625 continue; | |
| 626 | |
| 627 // cause the node to be redrawn | |
| 628 if (LayoutObject* layoutObject = node->layoutObject()) { | |
| 629 layoutObject->setShouldDoFullPaintInvalidation(); | |
| 630 break; | |
| 631 } | |
| 632 } | |
| 633 } | |
| 634 } | |
| 635 | |
| 636 void DocumentMarkerController::shiftMarkers(Node* node, unsigned startOffset, in
t delta) | |
| 637 { | |
| 638 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 639 return; | |
| 640 ASSERT(!m_markers.isEmpty()); | |
| 641 | |
| 642 MarkerLists* markers = m_markers.get(node); | |
| 643 if (!markers) | |
| 644 return; | |
| 645 | |
| 646 bool docDirty = false; | |
| 647 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::MarkerTyp
eIndexesCount; ++markerListIndex) { | |
| 648 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 649 if (!list) | |
| 650 continue; | |
| 651 MarkerList::iterator startPos = std::lower_bound(list->begin(), list->en
d(), startOffset, startsAfter); | |
| 652 for (MarkerList::iterator marker = startPos; marker != list->end(); ++ma
rker) { | |
| 653 #if ENABLE(ASSERT) | |
| 654 int startOffset = (*marker)->startOffset(); | |
| 655 ASSERT(startOffset + delta >= 0); | |
| 656 #endif | |
| 657 (*marker)->shiftOffsets(delta); | |
| 658 docDirty = true; | |
| 659 | |
| 660 updateMarkerRenderedRect(node, **marker); | |
| 661 } | |
| 662 } | |
| 663 | |
| 664 // repaint the affected node | |
| 665 if (docDirty && node->layoutObject()) | |
| 666 node->layoutObject()->setShouldDoFullPaintInvalidation(); | |
| 667 } | |
| 668 | |
| 669 void DocumentMarkerController::setMarkersActive(Range* range, bool active) | |
| 670 { | |
| 671 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 672 return; | |
| 673 ASSERT(!m_markers.isEmpty()); | |
| 674 | |
| 675 Node* startContainer = range->startContainer(); | |
| 676 Node* endContainer = range->endContainer(); | |
| 677 | |
| 678 Node* pastLastNode = range->pastLastNode(); | |
| 679 | |
| 680 for (Node* node = range->firstNode(); node != pastLastNode; node = NodeTrave
rsal::next(*node)) { | |
| 681 int startOffset = node == startContainer ? range->startOffset() : 0; | |
| 682 int endOffset = node == endContainer ? range->endOffset() : INT_MAX; | |
| 683 setMarkersActive(node, startOffset, endOffset, active); | |
| 684 } | |
| 685 } | |
| 686 | |
| 687 void DocumentMarkerController::setMarkersActive(Node* node, unsigned startOffset
, unsigned endOffset, bool active) | |
| 688 { | |
| 689 MarkerLists* markers = m_markers.get(node); | |
| 690 if (!markers) | |
| 691 return; | |
| 692 | |
| 693 bool docDirty = false; | |
| 694 OwnPtrWillBeMember<MarkerList>& list = (*markers)[MarkerTypeToMarkerIndex(Do
cumentMarker::TextMatch)]; | |
| 695 if (!list) | |
| 696 return; | |
| 697 MarkerList::iterator startPos = std::upper_bound(list->begin(), list->end(),
startOffset, endsBefore); | |
| 698 for (MarkerList::iterator marker = startPos; marker != list->end(); ++marker
) { | |
| 699 | |
| 700 // Markers are returned in order, so stop if we are now past the specifi
ed range. | |
| 701 if ((*marker)->startOffset() >= endOffset) | |
| 702 break; | |
| 703 | |
| 704 (*marker)->setActiveMatch(active); | |
| 705 docDirty = true; | |
| 706 } | |
| 707 | |
| 708 // repaint the affected node | |
| 709 if (docDirty && node->layoutObject()) | |
| 710 node->layoutObject()->setShouldDoFullPaintInvalidation(); | |
| 711 } | |
| 712 | |
| 713 #ifndef NDEBUG | |
| 714 void DocumentMarkerController::showMarkers() const | |
| 715 { | |
| 716 fprintf(stderr, "%d nodes have markers:\n", m_markers.size()); | |
| 717 MarkerMap::const_iterator end = m_markers.end(); | |
| 718 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); nodeIterato
r != end; ++nodeIterator) { | |
| 719 const Node* node = nodeIterator->key; | |
| 720 fprintf(stderr, "%p", node); | |
| 721 MarkerLists* markers = m_markers.get(node); | |
| 722 for (size_t markerListIndex = 0; markerListIndex < DocumentMarker::Marke
rTypeIndexesCount; ++markerListIndex) { | |
| 723 OwnPtrWillBeMember<MarkerList>& list = (*markers)[markerListIndex]; | |
| 724 for (unsigned markerIndex = 0; list.get() && markerIndex < list->siz
e(); ++markerIndex) { | |
| 725 DocumentMarker* marker = list->at(markerIndex).get(); | |
| 726 fprintf(stderr, " %d:[%d:%d](%d)", marker->type(), marker->start
Offset(), marker->endOffset(), marker->activeMatch()); | |
| 727 } | |
| 728 } | |
| 729 | |
| 730 fprintf(stderr, "\n"); | |
| 731 } | |
| 732 } | |
| 733 #endif | |
| 734 | |
| 735 } // namespace blink | |
| 736 | |
| 737 #ifndef NDEBUG | |
| 738 void showDocumentMarkers(const blink::DocumentMarkerController* controller) | |
| 739 { | |
| 740 if (controller) | |
| 741 controller->showMarkers(); | |
| 742 } | |
| 743 #endif | |
| OLD | NEW |