Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * (C) 2001 Dirk Mueller (mueller@kde.org) | 4 * (C) 2001 Dirk Mueller (mueller@kde.org) |
| 5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org) | 5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
| 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights | 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights |
| 7 * reserved. | 7 * reserved. |
| 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. | 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| 9 * (http://www.torchmobile.com/) | 9 * (http://www.torchmobile.com/) |
| 10 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 10 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 * | 21 * |
| 22 * You should have received a copy of the GNU Library General Public License | 22 * You should have received a copy of the GNU Library General Public License |
| 23 * along with this library; see the file COPYING.LIB. If not, write to | 23 * along with this library; see the file COPYING.LIB. If not, write to |
| 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 25 * Boston, MA 02110-1301, USA. | 25 * Boston, MA 02110-1301, USA. |
| 26 * | 26 * |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 #include "core/editing/markers/DocumentMarkerController.h" | 29 #include "core/editing/markers/DocumentMarkerController.h" |
| 30 | 30 |
| 31 #include <algorithm> | |
| 31 #include "core/dom/Node.h" | 32 #include "core/dom/Node.h" |
| 32 #include "core/dom/NodeTraversal.h" | 33 #include "core/dom/NodeTraversal.h" |
| 33 #include "core/dom/Range.h" | 34 #include "core/dom/Range.h" |
| 34 #include "core/dom/Text.h" | 35 #include "core/dom/Text.h" |
| 35 #include "core/editing/iterators/TextIterator.h" | 36 #include "core/editing/iterators/TextIterator.h" |
| 36 #include "core/editing/markers/RenderedDocumentMarker.h" | 37 #include "core/editing/markers/CompositionMarkerList.h" |
| 38 #include "core/editing/markers/DocumentMarkerList.h" | |
| 39 #include "core/editing/markers/EditingMarkerList.h" | |
| 40 #include "core/editing/markers/SpellCheckMarkerList.h" | |
| 41 #include "core/editing/markers/TextMatchMarkerList.h" | |
| 37 #include "core/frame/FrameView.h" | 42 #include "core/frame/FrameView.h" |
| 38 #include "core/layout/LayoutObject.h" | 43 #include "core/layout/LayoutObject.h" |
| 39 #include <algorithm> | |
| 40 | 44 |
| 41 #ifndef NDEBUG | 45 #ifndef NDEBUG |
| 42 #include <stdio.h> | 46 #include <stdio.h> |
| 43 #endif | 47 #endif |
| 44 | 48 |
| 45 namespace blink { | 49 namespace blink { |
| 46 | 50 |
| 47 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) | 51 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) |
| 48 : m_words(words) {} | 52 : m_words(words) {} |
| 49 | 53 |
| 50 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, | 54 bool MarkerRemoverPredicate::operator()(const DocumentMarker& documentMarker, |
| 51 const Text& textNode) const { | 55 const Text& textNode) const { |
| 52 unsigned start = documentMarker.startOffset(); | 56 unsigned start = documentMarker.startOffset(); |
| 53 unsigned length = documentMarker.endOffset() - documentMarker.startOffset(); | 57 unsigned length = documentMarker.endOffset() - documentMarker.startOffset(); |
| 54 | 58 |
| 55 String markerText = textNode.data().substring(start, length); | 59 String markerText = textNode.data().substring(start, length); |
| 56 return m_words.contains(markerText); | 60 return m_words.contains(markerText); |
| 57 } | 61 } |
| 58 | 62 |
| 59 namespace { | 63 DocumentMarkerController::DocumentMarkerController(Document& document) |
| 60 | 64 : m_document(&document) { |
| 61 DocumentMarker::MarkerTypeIndex MarkerTypeToMarkerIndex( | 65 setContext(&document); |
| 62 DocumentMarker::MarkerType type) { | |
| 63 switch (type) { | |
| 64 case DocumentMarker::Spelling: | |
| 65 return DocumentMarker::SpellingMarkerIndex; | |
| 66 case DocumentMarker::Grammar: | |
| 67 return DocumentMarker::GrammarMarkerIndex; | |
| 68 case DocumentMarker::TextMatch: | |
| 69 return DocumentMarker::TextMatchMarkerIndex; | |
| 70 case DocumentMarker::Composition: | |
| 71 return DocumentMarker::CompositionMarkerIndex; | |
| 72 } | |
| 73 | |
| 74 NOTREACHED(); | |
| 75 return DocumentMarker::SpellingMarkerIndex; | |
| 76 } | 66 } |
| 77 | 67 |
| 78 } // namespace | |
| 79 | |
| 80 inline bool DocumentMarkerController::possiblyHasMarkers( | |
| 81 DocumentMarker::MarkerTypes types) { | |
| 82 return m_possiblyExistingMarkerTypes.intersects(types); | |
| 83 } | |
| 84 | |
| 85 DocumentMarkerController::DocumentMarkerController(const Document& document) | |
| 86 : m_possiblyExistingMarkerTypes(0), m_document(&document) {} | |
| 87 | |
| 88 void DocumentMarkerController::clear() { | 68 void DocumentMarkerController::clear() { |
| 89 m_markers.clear(); | 69 m_spelling.clear(); |
| 90 m_possiblyExistingMarkerTypes = 0; | 70 m_grammar.clear(); |
| 71 m_textMatches.clear(); | |
| 72 m_compositions.clear(); | |
| 91 } | 73 } |
| 92 | 74 |
| 93 void DocumentMarkerController::addMarker(const Position& start, | 75 void DocumentMarkerController::addMarker(const Position& start, |
| 94 const Position& end, | 76 const Position& end, |
| 95 DocumentMarker::MarkerType type, | 77 DocumentMarker::MarkerType type, |
| 96 const String& description) { | 78 const String& description) { |
| 97 // Use a TextIterator to visit the potentially multiple nodes the range | 79 // Use a TextIterator to visit the potentially multiple nodes the range |
| 98 // covers. | 80 // covers. |
| 99 for (TextIterator markedText(start, end); !markedText.atEnd(); | 81 for (TextIterator markedText(start, end); !markedText.atEnd(); |
| 100 markedText.advance()) { | 82 markedText.advance()) { |
| 101 addMarker( | 83 addMarker(markedText.currentContainer(), |
| 102 markedText.currentContainer(), | 84 new DocumentMarker( |
| 103 DocumentMarker(type, markedText.startOffsetInCurrentContainer(), | 85 type, markedText.startOffsetInCurrentContainer(), |
| 104 markedText.endOffsetInCurrentContainer(), description)); | 86 markedText.endOffsetInCurrentContainer(), description)); |
| 105 } | 87 } |
| 106 } | 88 } |
| 107 | 89 |
| 108 void DocumentMarkerController::addTextMatchMarker(const EphemeralRange& range, | 90 void DocumentMarkerController::addTextMatchMarker(const EphemeralRange& range, |
| 109 bool activeMatch) { | 91 bool activeMatch) { |
| 110 DCHECK(!m_document->needsLayoutTreeUpdate()); | 92 DCHECK(!m_document->needsLayoutTreeUpdate()); |
| 111 | 93 |
| 112 // Use a TextIterator to visit the potentially multiple nodes the range | 94 // Use a TextIterator to visit the potentially multiple nodes the range |
| 113 // covers. | 95 // covers. |
| 114 for (TextIterator markedText(range.startPosition(), range.endPosition()); | 96 for (TextIterator markedText(range.startPosition(), range.endPosition()); |
| 115 !markedText.atEnd(); markedText.advance()) | 97 !markedText.atEnd(); markedText.advance()) { |
| 116 addMarker( | 98 addMarker(markedText.currentContainer(), |
| 117 markedText.currentContainer(), | 99 new DocumentMarker(markedText.startOffsetInCurrentContainer(), |
| 118 DocumentMarker(markedText.startOffsetInCurrentContainer(), | 100 markedText.endOffsetInCurrentContainer(), |
| 119 markedText.endOffsetInCurrentContainer(), activeMatch)); | 101 activeMatch)); |
| 102 } | |
| 120 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a | 103 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a |
| 121 // throttling algorithm. crbug.com/6819. | 104 // throttling algorithm. crbug.com/6819. |
| 122 } | 105 } |
| 123 | 106 |
| 124 void DocumentMarkerController::addCompositionMarker(const Position& start, | 107 void DocumentMarkerController::addCompositionMarker(const Position& start, |
| 125 const Position& end, | 108 const Position& end, |
| 126 Color underlineColor, | 109 Color underlineColor, |
| 127 bool thick, | 110 bool thick, |
| 128 Color backgroundColor) { | 111 Color backgroundColor) { |
| 129 DCHECK(!m_document->needsLayoutTreeUpdate()); | 112 DCHECK(!m_document->needsLayoutTreeUpdate()); |
| 130 | 113 |
| 131 for (TextIterator markedText(start, end); !markedText.atEnd(); | 114 for (TextIterator markedText(start, end); !markedText.atEnd(); |
| 132 markedText.advance()) | 115 markedText.advance()) { |
| 133 addMarker(markedText.currentContainer(), | 116 addMarker(markedText.currentContainer(), |
| 134 DocumentMarker(markedText.startOffsetInCurrentContainer(), | 117 new DocumentMarker(markedText.startOffsetInCurrentContainer(), |
| 135 markedText.endOffsetInCurrentContainer(), | 118 markedText.endOffsetInCurrentContainer(), |
| 136 underlineColor, thick, backgroundColor)); | 119 underlineColor, thick, backgroundColor)); |
| 120 } | |
| 137 } | 121 } |
| 138 | 122 |
| 139 void DocumentMarkerController::prepareForDestruction() { | 123 void DocumentMarkerController::prepareForDestruction() { |
| 140 clear(); | 124 clear(); |
| 141 } | 125 } |
| 142 | 126 |
| 143 void DocumentMarkerController::removeMarkers( | 127 void DocumentMarkerController::removeMarkers( |
| 144 TextIterator& markedText, | |
| 145 DocumentMarker::MarkerTypes markerTypes, | |
| 146 RemovePartiallyOverlappingMarkerOrNot | |
| 147 shouldRemovePartiallyOverlappingMarker) { | |
| 148 for (; !markedText.atEnd(); markedText.advance()) { | |
| 149 if (!possiblyHasMarkers(markerTypes)) | |
| 150 return; | |
| 151 DCHECK(!m_markers.isEmpty()); | |
| 152 | |
| 153 int startOffset = markedText.startOffsetInCurrentContainer(); | |
| 154 int endOffset = markedText.endOffsetInCurrentContainer(); | |
| 155 removeMarkers(markedText.currentContainer(), startOffset, | |
| 156 endOffset - startOffset, markerTypes, | |
| 157 shouldRemovePartiallyOverlappingMarker); | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 void DocumentMarkerController::removeMarkers( | |
| 162 const EphemeralRange& range, | 128 const EphemeralRange& range, |
| 163 DocumentMarker::MarkerTypes markerTypes, | 129 DocumentMarker::MarkerTypes markerTypes, |
| 164 RemovePartiallyOverlappingMarkerOrNot | 130 RemovePartiallyOverlappingMarkerOrNot |
| 165 shouldRemovePartiallyOverlappingMarker) { | 131 shouldRemovePartiallyOverlappingMarker) { |
| 166 DCHECK(!m_document->needsLayoutTreeUpdate()); | 132 DCHECK(!m_document->needsLayoutTreeUpdate()); |
| 167 | 133 |
| 168 TextIterator markedText(range.startPosition(), range.endPosition()); | 134 TextIterator markedText(range.startPosition(), range.endPosition()); |
| 169 DocumentMarkerController::removeMarkers( | 135 DocumentMarkerController::removeMarkers( |
| 170 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); | 136 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); |
| 171 } | 137 } |
| 172 | 138 |
| 173 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, | |
| 174 const DocumentMarker* rhv) { | |
| 175 return lhv->startOffset() < rhv->startOffset(); | |
| 176 } | |
| 177 | |
| 178 static bool startsAfter(const Member<RenderedDocumentMarker>& marker, | |
| 179 size_t startOffset) { | |
| 180 return marker->startOffset() < startOffset; | |
| 181 } | |
| 182 | |
| 183 static bool endsBefore(size_t startOffset, | |
| 184 const Member<RenderedDocumentMarker>& rhv) { | |
| 185 return startOffset < rhv->endOffset(); | |
| 186 } | |
| 187 | |
| 188 static bool compareByStart(const Member<DocumentMarker>& lhv, | 139 static bool compareByStart(const Member<DocumentMarker>& lhv, |
| 189 const Member<DocumentMarker>& rhv) { | 140 const Member<DocumentMarker>& rhv) { |
| 190 return lhv->startOffset() < rhv->startOffset(); | 141 return lhv->startOffset() < rhv->startOffset(); |
| 191 } | 142 } |
| 192 | 143 |
| 193 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv, | |
| 194 const DocumentMarker* rhv) { | |
| 195 return lhv->endOffset() < rhv->startOffset(); | |
| 196 } | |
| 197 | |
| 198 static bool doesNotInclude(const Member<RenderedDocumentMarker>& marker, | |
| 199 size_t startOffset) { | |
| 200 return marker->endOffset() < startOffset; | |
| 201 } | |
| 202 | |
| 203 static void updateMarkerRenderedRect(const Node& node, | |
| 204 RenderedDocumentMarker& marker) { | |
| 205 Range* range = Range::create(node.document()); | |
| 206 // The offsets of the marker may be out-dated, so check for exceptions. | |
| 207 DummyExceptionStateForTesting exceptionState; | |
| 208 range->setStart(&const_cast<Node&>(node), marker.startOffset(), | |
| 209 exceptionState); | |
| 210 if (!exceptionState.hadException()) { | |
| 211 range->setEnd(&const_cast<Node&>(node), marker.endOffset(), | |
| 212 IGNORE_EXCEPTION_FOR_TESTING); | |
| 213 } | |
| 214 if (!exceptionState.hadException()) { | |
| 215 // TODO(yosin): Once we have a |EphemeralRange| version of |boundingBox()|, | |
| 216 // we should use it instead of |Range| version. | |
| 217 marker.setRenderedRect(LayoutRect(range->boundingBox())); | |
| 218 } else { | |
| 219 marker.nullifyRenderedRect(); | |
| 220 } | |
| 221 range->dispose(); | |
| 222 } | |
| 223 | |
| 224 // Markers are stored in order sorted by their start offset. | |
| 225 // Markers of the same type do not overlap each other. | |
| 226 | |
| 227 void DocumentMarkerController::addMarker(Node* node, | 144 void DocumentMarkerController::addMarker(Node* node, |
| 228 const DocumentMarker& newMarker) { | 145 DocumentMarker* newMarker) { |
| 229 DCHECK_GE(newMarker.endOffset(), newMarker.startOffset()); | 146 DCHECK_GE(newMarker->endOffset(), newMarker->startOffset()); |
| 230 if (newMarker.endOffset() == newMarker.startOffset()) | 147 if (newMarker->endOffset() == newMarker->startOffset()) |
| 231 return; | 148 return; |
| 232 | 149 |
| 233 m_possiblyExistingMarkerTypes.add(newMarker.type()); | 150 switch (newMarker->type()) { |
| 234 | 151 case DocumentMarker::TextMatch: { |
| 235 Member<MarkerLists>& markers = | 152 auto it = m_textMatches.find(node); |
| 236 m_markers.insert(node, nullptr).storedValue->value; | 153 if (it == m_textMatches.end()) { |
| 237 if (!markers) { | 154 m_textMatches.insert(node, new TextMatchMarkerList(this)); |
| 238 markers = new MarkerLists; | 155 it = m_textMatches.find(node); |
| 239 markers->grow(DocumentMarker::MarkerTypeIndexesCount); | 156 } |
| 240 } | 157 static_cast<TextMatchMarkerList*>(it->value.get())->push_back(newMarker); |
| 241 | 158 break; |
| 242 DocumentMarker::MarkerTypeIndex markerListIndex = | 159 } |
| 243 MarkerTypeToMarkerIndex(newMarker.type()); | 160 default: { |
| 244 if (!markers->at(markerListIndex)) { | 161 MarkerMap& markerMap = markerMapForType(newMarker->type()); |
| 245 markers->at(markerListIndex) = new MarkerList; | 162 auto it = markerMap.find(node); |
| 246 } | 163 if (it == markerMap.end()) { |
| 247 | 164 markerMap.insert(node, createMarkerListOfType(newMarker->type())); |
| 248 Member<MarkerList>& list = markers->at(markerListIndex); | 165 it = markerMap.find(node); |
| 249 RenderedDocumentMarker* newRenderedMarker = | 166 } |
| 250 RenderedDocumentMarker::create(newMarker); | 167 static_cast<EditingMarkerList*>(it->value.get())->insert(newMarker); |
| 251 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) { | |
| 252 list->push_back(newRenderedMarker); | |
| 253 } else { | |
| 254 if (newMarker.type() != DocumentMarker::TextMatch && | |
| 255 newMarker.type() != DocumentMarker::Composition) { | |
| 256 mergeOverlapping(list.get(), newRenderedMarker); | |
| 257 } else { | |
| 258 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), | |
| 259 &newMarker, startsFurther); | |
| 260 list->insert(pos - list->begin(), newRenderedMarker); | |
| 261 } | 168 } |
| 262 } | 169 } |
| 263 | 170 |
| 264 // repaint the affected node | 171 // repaint the affected node |
| 265 if (node->layoutObject()) { | 172 if (node->layoutObject()) { |
| 266 node->layoutObject()->setShouldDoFullPaintInvalidation( | 173 node->layoutObject()->setShouldDoFullPaintInvalidation( |
| 267 PaintInvalidationDocumentMarkerChange); | 174 PaintInvalidationDocumentMarkerChange); |
| 268 } | 175 } |
| 269 } | 176 } |
| 270 | 177 |
| 271 void DocumentMarkerController::mergeOverlapping( | |
| 272 MarkerList* list, | |
| 273 RenderedDocumentMarker* toInsert) { | |
| 274 MarkerList::iterator firstOverlapping = | |
| 275 std::lower_bound(list->begin(), list->end(), toInsert, doesNotOverlap); | |
| 276 size_t index = firstOverlapping - list->begin(); | |
| 277 list->insert(index, toInsert); | |
| 278 MarkerList::iterator inserted = list->begin() + index; | |
| 279 firstOverlapping = inserted + 1; | |
| 280 for (MarkerList::iterator i = firstOverlapping; | |
| 281 i != list->end() && (*i)->startOffset() <= (*inserted)->endOffset();) { | |
| 282 (*inserted)->setStartOffset( | |
| 283 std::min((*inserted)->startOffset(), (*i)->startOffset())); | |
| 284 (*inserted)->setEndOffset( | |
| 285 std::max((*inserted)->endOffset(), (*i)->endOffset())); | |
| 286 list->remove(i - list->begin()); | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 // copies markers from srcNode to dstNode, applying the specified shift delta to | 178 // copies markers from srcNode to dstNode, applying the specified shift delta to |
| 291 // the copies. The shift is useful if, e.g., the caller has created the dstNode | 179 // the copies. The shift is useful if, e.g., the caller has created the dstNode |
| 292 // from a non-prefix substring of the srcNode. | 180 // from a non-prefix substring of the srcNode. |
| 293 void DocumentMarkerController::copyMarkers(Node* srcNode, | 181 void DocumentMarkerController::copyMarkers(Node* srcNode, |
| 294 unsigned startOffset, | 182 unsigned startOffset, |
| 295 int length, | 183 int length, |
| 296 Node* dstNode, | 184 Node* dstNode, |
| 297 int delta) { | 185 int delta) { |
| 298 if (length <= 0) | 186 if (length <= 0) |
| 299 return; | 187 return; |
| 300 | 188 |
| 301 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | 189 bool docDirty = false; |
| 302 return; | |
| 303 DCHECK(!m_markers.isEmpty()); | |
| 304 | 190 |
| 305 MarkerLists* markers = m_markers.at(srcNode); | 191 for (Member<DocumentMarkerList> list : getMarkerListsForNode(srcNode)) { |
| 306 if (!markers) | 192 docDirty = |
| 307 return; | 193 list->copyMarkers(startOffset, length, dstNode, delta) || docDirty; |
| 308 | |
| 309 bool docDirty = false; | |
| 310 for (size_t markerListIndex = 0; | |
| 311 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 312 ++markerListIndex) { | |
| 313 Member<MarkerList>& list = (*markers)[markerListIndex]; | |
| 314 if (!list) | |
| 315 continue; | |
| 316 | |
| 317 unsigned endOffset = startOffset + length - 1; | |
| 318 MarkerList::iterator startPos = std::lower_bound( | |
| 319 list->begin(), list->end(), startOffset, doesNotInclude); | |
| 320 for (MarkerList::iterator i = startPos; i != list->end(); ++i) { | |
| 321 DocumentMarker* marker = i->get(); | |
| 322 | |
| 323 // stop if we are now past the specified range | |
| 324 if (marker->startOffset() > endOffset) | |
| 325 break; | |
| 326 | |
| 327 // pin the marker to the specified range and apply the shift delta | |
| 328 docDirty = true; | |
| 329 if (marker->startOffset() < startOffset) | |
| 330 marker->setStartOffset(startOffset); | |
| 331 if (marker->endOffset() > endOffset) | |
| 332 marker->setEndOffset(endOffset); | |
| 333 marker->shiftOffsets(delta); | |
| 334 | |
| 335 addMarker(dstNode, *marker); | |
| 336 } | |
| 337 } | 194 } |
| 338 | 195 |
| 339 // repaint the affected node | 196 // repaint the affected node |
| 340 if (docDirty && dstNode->layoutObject()) { | 197 if (docDirty && dstNode->layoutObject()) { |
| 341 dstNode->layoutObject()->setShouldDoFullPaintInvalidation( | 198 dstNode->layoutObject()->setShouldDoFullPaintInvalidation( |
| 342 PaintInvalidationDocumentMarkerChange); | 199 PaintInvalidationDocumentMarkerChange); |
| 343 } | 200 } |
| 344 } | 201 } |
| 345 | 202 |
| 346 void DocumentMarkerController::removeMarkers( | 203 void DocumentMarkerController::removeMarkers( |
| 347 Node* node, | 204 Node* node, |
| 348 unsigned startOffset, | 205 unsigned startOffset, |
| 349 int length, | 206 int length, |
| 350 DocumentMarker::MarkerTypes markerTypes, | 207 DocumentMarker::MarkerTypes markerTypes, |
| 351 RemovePartiallyOverlappingMarkerOrNot | 208 RemovePartiallyOverlappingMarkerOrNot |
| 352 shouldRemovePartiallyOverlappingMarker) { | 209 shouldRemovePartiallyOverlappingMarker) { |
| 353 if (length <= 0) | 210 if (length <= 0) |
| 354 return; | 211 return; |
| 355 | 212 |
| 356 if (!possiblyHasMarkers(markerTypes)) | |
| 357 return; | |
| 358 DCHECK(!(m_markers.isEmpty())); | |
| 359 | |
| 360 MarkerLists* markers = m_markers.at(node); | |
| 361 if (!markers) | |
| 362 return; | |
| 363 | |
| 364 bool docDirty = false; | 213 bool docDirty = false; |
| 365 size_t emptyListsCount = 0; | 214 for (size_t markerListIndex = static_cast<DocumentMarker::MarkerType>(0); |
| 366 for (size_t markerListIndex = 0; | |
| 367 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 215 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; |
| 368 ++markerListIndex) { | 216 ++markerListIndex) { |
| 369 Member<MarkerList>& list = (*markers)[markerListIndex]; | 217 DocumentMarker::MarkerType type = |
| 370 if (!list || list->isEmpty()) { | 218 static_cast<DocumentMarker::MarkerType>(1 << markerListIndex); |
|
rlanday
2017/03/21 21:19:06
Iterating over the MarkerTypes like this feels slo
| |
| 371 if (list.get() && list->isEmpty()) | 219 if (markerTypes.contains(type)) { |
| 372 list.clear(); | 220 MarkerMap& markerMap = markerMapForType(type); |
| 373 ++emptyListsCount; | 221 auto it = markerMap.find(node); |
| 374 continue; | 222 if (it != markerMap.end()) { |
| 375 } | 223 it->value->removeMarkers(startOffset, length, |
| 376 if (!markerTypes.contains((*list->begin())->type())) | 224 shouldRemovePartiallyOverlappingMarker, |
| 377 continue; | 225 &docDirty); |
| 378 unsigned endOffset = startOffset + length; | 226 if (it->value->empty()) |
| 379 MarkerList::iterator startPos = | 227 markerMap.erase(node); |
| 380 std::upper_bound(list->begin(), list->end(), startOffset, endsBefore); | |
| 381 for (MarkerList::iterator i = startPos; i != list->end();) { | |
| 382 DocumentMarker marker(*i->get()); | |
| 383 | |
| 384 // markers are returned in order, so stop if we are now past the specified | |
| 385 // range | |
| 386 if (marker.startOffset() >= endOffset) | |
| 387 break; | |
| 388 | |
| 389 // at this point we know that marker and target intersect in some way | |
| 390 docDirty = true; | |
| 391 | |
| 392 // pitch the old marker | |
| 393 list->remove(i - list->begin()); | |
| 394 | |
| 395 if (shouldRemovePartiallyOverlappingMarker) { | |
| 396 // Stop here. Don't add resulting slices back. | |
| 397 continue; | |
| 398 } | |
| 399 | |
| 400 // add either of the resulting slices that are left after removing target | |
| 401 if (startOffset > marker.startOffset()) { | |
| 402 DocumentMarker newLeft = marker; | |
| 403 newLeft.setEndOffset(startOffset); | |
| 404 size_t insertIndex = i - list->begin(); | |
| 405 list->insert(insertIndex, RenderedDocumentMarker::create(newLeft)); | |
| 406 // Move to the marker after the inserted one. | |
| 407 i = list->begin() + insertIndex + 1; | |
| 408 } | |
| 409 if (marker.endOffset() > endOffset) { | |
| 410 DocumentMarker newRight = marker; | |
| 411 newRight.setStartOffset(endOffset); | |
| 412 size_t insertIndex = i - list->begin(); | |
| 413 list->insert(insertIndex, RenderedDocumentMarker::create(newRight)); | |
| 414 // Move to the marker after the inserted one. | |
| 415 i = list->begin() + insertIndex + 1; | |
| 416 } | 228 } |
| 417 } | 229 } |
| 418 | |
| 419 if (list->isEmpty()) { | |
| 420 list.clear(); | |
| 421 ++emptyListsCount; | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 if (emptyListsCount == DocumentMarker::MarkerTypeIndexesCount) { | |
| 426 m_markers.erase(node); | |
| 427 if (m_markers.isEmpty()) | |
| 428 m_possiblyExistingMarkerTypes = 0; | |
| 429 } | 230 } |
| 430 | 231 |
| 431 // repaint the affected node | 232 // repaint the affected node |
| 432 if (docDirty && node->layoutObject()) { | 233 if (docDirty && node->layoutObject()) { |
| 433 node->layoutObject()->setShouldDoFullPaintInvalidation( | 234 node->layoutObject()->setShouldDoFullPaintInvalidation( |
| 434 PaintInvalidationDocumentMarkerChange); | 235 PaintInvalidationDocumentMarkerChange); |
| 435 } | 236 } |
| 436 } | 237 } |
| 437 | 238 |
| 438 DocumentMarkerVector DocumentMarkerController::markersFor( | 239 DocumentMarkerVector DocumentMarkerController::markersFor( |
| 439 Node* node, | 240 Node* node, |
| 440 DocumentMarker::MarkerTypes markerTypes) { | 241 DocumentMarker::MarkerTypes markerTypes) { |
| 441 DocumentMarkerVector result; | 242 DocumentMarkerVector result; |
| 442 | 243 |
| 443 MarkerLists* markers = m_markers.at(node); | 244 for (Member<DocumentMarkerList> list : |
| 444 if (!markers) | 245 getMarkerListsForNode(node, markerTypes)) { |
| 445 return result; | 246 list->appendMarkersToInputList(&result); |
| 446 | |
| 447 for (size_t markerListIndex = 0; | |
| 448 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 449 ++markerListIndex) { | |
| 450 Member<MarkerList>& list = (*markers)[markerListIndex]; | |
| 451 if (!list || list->isEmpty() || | |
| 452 !markerTypes.contains((*list->begin())->type())) | |
| 453 continue; | |
| 454 | |
| 455 for (size_t i = 0; i < list->size(); ++i) | |
| 456 result.push_back(list->at(i).get()); | |
| 457 } | 247 } |
| 458 | 248 |
| 459 std::sort(result.begin(), result.end(), compareByStart); | 249 std::sort(result.begin(), result.end(), compareByStart); |
| 460 return result; | 250 return result; |
| 461 } | 251 } |
| 462 | 252 |
| 463 DocumentMarkerVector DocumentMarkerController::markers() { | 253 DocumentMarkerVector DocumentMarkerController::markers() { |
| 464 DocumentMarkerVector result; | 254 DocumentMarkerVector result; |
| 465 for (MarkerMap::iterator i = m_markers.begin(); i != m_markers.end(); ++i) { | 255 for (auto it = m_spelling.begin(); it != m_spelling.end(); ++it) |
| 466 MarkerLists* markers = i->value.get(); | 256 it->value->appendMarkersToInputList(&result); |
| 467 for (size_t markerListIndex = 0; | 257 |
| 468 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 258 for (auto it = m_grammar.begin(); it != m_grammar.end(); ++it) |
| 469 ++markerListIndex) { | 259 it->value->appendMarkersToInputList(&result); |
| 470 Member<MarkerList>& list = (*markers)[markerListIndex]; | 260 |
| 471 for (size_t j = 0; list.get() && j < list->size(); ++j) | 261 for (auto it = m_textMatches.begin(); it != m_textMatches.end(); ++it) |
| 472 result.push_back(list->at(j).get()); | 262 it->value->appendMarkersToInputList(&result); |
| 473 } | 263 |
| 474 } | 264 for (auto it = m_compositions.begin(); it != m_compositions.end(); ++it) |
| 265 it->value->appendMarkersToInputList(&result); | |
| 266 | |
| 475 std::sort(result.begin(), result.end(), compareByStart); | 267 std::sort(result.begin(), result.end(), compareByStart); |
| 476 return result; | 268 return result; |
| 477 } | 269 } |
| 478 | 270 |
| 479 DocumentMarkerVector DocumentMarkerController::markersInRange( | 271 DocumentMarkerVector DocumentMarkerController::markersInRange( |
| 480 const EphemeralRange& range, | 272 const EphemeralRange& range, |
| 481 DocumentMarker::MarkerTypes markerTypes) { | 273 DocumentMarker::MarkerTypes markerTypes) { |
| 482 if (!possiblyHasMarkers(markerTypes)) | |
| 483 return DocumentMarkerVector(); | |
| 484 | 274 |
| 485 DocumentMarkerVector foundMarkers; | 275 DocumentMarkerVector foundMarkers; |
| 486 | 276 |
| 487 Node* startContainer = range.startPosition().computeContainerNode(); | 277 Node* startContainer = range.startPosition().computeContainerNode(); |
| 488 DCHECK(startContainer); | 278 DCHECK(startContainer); |
| 489 unsigned startOffset = static_cast<unsigned>( | 279 unsigned startOffset = static_cast<unsigned>( |
| 490 range.startPosition().computeOffsetInContainerNode()); | 280 range.startPosition().computeOffsetInContainerNode()); |
| 491 Node* endContainer = range.endPosition().computeContainerNode(); | 281 Node* endContainer = range.endPosition().computeContainerNode(); |
| 492 DCHECK(endContainer); | 282 DCHECK(endContainer); |
| 493 unsigned endOffset = | 283 unsigned endOffset = |
| 494 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); | 284 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); |
| 495 | 285 |
| 496 for (Node& node : range.nodes()) { | 286 for (Node& node : range.nodes()) { |
| 497 for (DocumentMarker* marker : markersFor(&node)) { | 287 for (DocumentMarker* marker : markersFor(&node)) { |
| 498 if (!markerTypes.contains(marker->type())) | 288 if (!markerTypes.contains(marker->type())) |
| 499 continue; | 289 continue; |
| 500 if (node == startContainer && marker->endOffset() <= startOffset) | 290 if (node == startContainer && marker->endOffset() <= startOffset) |
| 501 continue; | 291 continue; |
| 502 if (node == endContainer && marker->startOffset() >= endOffset) | 292 if (node == endContainer && marker->startOffset() >= endOffset) |
| 503 continue; | 293 continue; |
| 504 foundMarkers.push_back(marker); | 294 foundMarkers.push_back(marker); |
| 505 } | 295 } |
| 506 } | 296 } |
| 507 return foundMarkers; | 297 return foundMarkers; |
| 508 } | 298 } |
| 509 | 299 |
| 300 // This method is only ever called with the type DocumentMarker::TextMatch | |
| 301 // TODO(rlanday): remove the param | |
| 510 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( | 302 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( |
| 511 DocumentMarker::MarkerType markerType) { | 303 DocumentMarker::MarkerType) { |
| 512 Vector<IntRect> result; | 304 Vector<IntRect> result; |
| 513 | 305 for (auto nodeIterator = m_textMatches.begin(); |
| 514 if (!possiblyHasMarkers(markerType)) | 306 nodeIterator != m_textMatches.end(); ++nodeIterator) { |
| 515 return result; | |
| 516 DCHECK(!(m_markers.isEmpty())); | |
| 517 | |
| 518 // outer loop: process each node | |
| 519 MarkerMap::iterator end = m_markers.end(); | |
| 520 for (MarkerMap::iterator nodeIterator = m_markers.begin(); | |
| 521 nodeIterator != end; ++nodeIterator) { | |
| 522 // inner loop; process each marker in this node | |
| 523 const Node& node = *nodeIterator->key; | 307 const Node& node = *nodeIterator->key; |
| 524 MarkerLists* markers = nodeIterator->value.get(); | 308 TextMatchMarkerList* list = |
| 525 for (size_t markerListIndex = 0; | 309 static_cast<TextMatchMarkerList*>(nodeIterator->value.get()); |
| 526 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 310 #if DCHECK_IS_ON() |
| 527 ++markerListIndex) { | 311 if (!list->empty()) { |
| 528 Member<MarkerList>& list = (*markers)[markerListIndex]; | 312 DCHECK(!m_document->view() || !m_document->view()->needsLayout()); |
| 529 if (!list || list->isEmpty() || (*list->begin())->type() != markerType) | 313 DCHECK(!m_document->needsLayoutTreeUpdate()); |
| 530 continue; | |
| 531 for (unsigned markerIndex = 0; markerIndex < list->size(); | |
| 532 ++markerIndex) { | |
| 533 RenderedDocumentMarker* marker = list->at(markerIndex).get(); | |
| 534 updateMarkerRenderedRectIfNeeded(node, *marker); | |
| 535 if (!marker->isRendered()) | |
| 536 continue; | |
| 537 result.push_back(marker->renderedRect()); | |
| 538 } | |
| 539 } | 314 } |
| 315 #endif | |
| 316 list->appendRenderedRectsToInputList(node, &result); | |
| 540 } | 317 } |
| 541 | 318 |
| 542 return result; | 319 return result; |
| 543 } | 320 } |
| 544 | 321 |
| 545 static void invalidatePaintForTickmarks(const Node& node) { | 322 static void invalidatePaintForTickmarks(const Node& node) { |
| 546 if (FrameView* frameView = node.document().view()) | 323 if (FrameView* frameView = node.document().view()) |
| 547 frameView->invalidatePaintForTickmarks(); | 324 frameView->invalidatePaintForTickmarks(); |
| 548 } | 325 } |
| 549 | 326 |
| 550 void DocumentMarkerController::updateMarkerRenderedRectIfNeeded( | 327 void DocumentMarkerController::invalidateRectsForMarkersInNode(Node& node) { |
| 551 const Node& node, | 328 if (!m_textMatches.contains(&node)) |
| 552 RenderedDocumentMarker& marker) { | 329 return; |
| 553 DCHECK(!m_document->view() || !m_document->view()->needsLayout()); | |
| 554 DCHECK(!m_document->needsLayoutTreeUpdate()); | |
| 555 if (!marker.isValid()) | |
| 556 updateMarkerRenderedRect(node, marker); | |
| 557 } | |
| 558 | 330 |
| 559 void DocumentMarkerController::invalidateRectsForMarkersInNode( | 331 static_cast<TextMatchMarkerList*>(m_textMatches.find(&node)->value.get()) |
| 560 const Node& node) { | 332 ->invalidateRects(); |
| 561 MarkerLists* markers = m_markers.at(&node); | 333 invalidatePaintForTickmarks(node); |
| 562 | |
| 563 for (auto& markerList : *markers) { | |
| 564 if (!markerList || markerList->isEmpty()) | |
| 565 continue; | |
| 566 | |
| 567 for (auto& marker : *markerList) | |
| 568 marker->invalidate(); | |
| 569 | |
| 570 if (markerList->front()->type() == DocumentMarker::TextMatch) | |
| 571 invalidatePaintForTickmarks(node); | |
| 572 } | |
| 573 } | 334 } |
| 574 | 335 |
| 575 void DocumentMarkerController::invalidateRectsForAllMarkers() { | 336 void DocumentMarkerController::invalidateRectsForAllMarkers() { |
| 576 for (auto& nodeMarkers : m_markers) { | 337 for (auto& nodeMarkers : m_textMatches) |
| 577 const Node& node = *nodeMarkers.key; | 338 invalidateRectsForMarkersInNode(*nodeMarkers.key); |
| 578 for (auto& markerList : *nodeMarkers.value) { | |
| 579 if (!markerList || markerList->isEmpty()) | |
| 580 continue; | |
| 581 | |
| 582 for (auto& marker : *markerList) | |
| 583 marker->invalidate(); | |
| 584 | |
| 585 if (markerList->front()->type() == DocumentMarker::TextMatch) | |
| 586 invalidatePaintForTickmarks(node); | |
| 587 } | |
| 588 } | |
| 589 } | 339 } |
| 590 | 340 |
| 591 DEFINE_TRACE(DocumentMarkerController) { | 341 DEFINE_TRACE(DocumentMarkerController) { |
| 592 visitor->trace(m_markers); | 342 visitor->trace(m_spelling); |
| 343 visitor->trace(m_grammar); | |
| 344 visitor->trace(m_textMatches); | |
| 345 visitor->trace(m_compositions); | |
| 593 visitor->trace(m_document); | 346 visitor->trace(m_document); |
| 347 SynchronousMutationObserver::trace(visitor); | |
| 594 } | 348 } |
| 595 | 349 |
| 596 void DocumentMarkerController::removeMarkers( | 350 void DocumentMarkerController::removeMarkers( |
| 597 Node* node, | 351 Node* node, |
| 598 DocumentMarker::MarkerTypes markerTypes) { | 352 DocumentMarker::MarkerTypes markerTypes) { |
| 599 if (!possiblyHasMarkers(markerTypes)) | 353 if (markerTypes.contains(DocumentMarker::Spelling)) |
| 600 return; | 354 m_spelling.erase(node); |
| 601 DCHECK(!m_markers.isEmpty()); | |
| 602 | 355 |
| 603 MarkerMap::iterator iterator = m_markers.find(node); | 356 if (markerTypes.contains(DocumentMarker::Grammar)) |
| 604 if (iterator != m_markers.end()) | 357 m_grammar.erase(node); |
| 605 removeMarkersFromList(iterator, markerTypes); | 358 |
| 359 if (markerTypes.contains(DocumentMarker::TextMatch)) | |
| 360 m_textMatches.erase(node); | |
| 361 | |
| 362 if (markerTypes.contains(DocumentMarker::Composition)) | |
| 363 m_compositions.erase(node); | |
| 364 } | |
| 365 | |
| 366 void DocumentMarkerController::removeMarkers( | |
| 367 DocumentMarker::MarkerTypes markerTypes) { | |
| 368 if (markerTypes.contains(DocumentMarker::Spelling)) | |
| 369 m_spelling.clear(); | |
| 370 | |
| 371 if (markerTypes.contains(DocumentMarker::Grammar)) | |
| 372 m_grammar.clear(); | |
| 373 | |
| 374 if (markerTypes.contains(DocumentMarker::TextMatch)) | |
| 375 m_textMatches.clear(); | |
| 376 | |
| 377 if (markerTypes.contains(DocumentMarker::Composition)) | |
| 378 m_compositions.clear(); | |
| 606 } | 379 } |
| 607 | 380 |
| 608 void DocumentMarkerController::removeMarkers( | 381 void DocumentMarkerController::removeMarkers( |
| 609 const MarkerRemoverPredicate& shouldRemoveMarker) { | 382 const MarkerRemoverPredicate& shouldRemoveMarker) { |
| 610 for (auto& nodeMarkers : m_markers) { | 383 for (auto& nodeMarkers : m_spelling) { |
| 611 const Node& node = *nodeMarkers.key; | 384 const Node& node = *nodeMarkers.key; |
| 612 if (!node.isTextNode()) // MarkerRemoverPredicate requires a Text node. | 385 if (!node.isTextNode()) |
| 613 continue; | 386 continue; |
| 614 MarkerLists& markers = *nodeMarkers.value; | 387 static_cast<SpellCheckMarkerList*>(nodeMarkers.value.get()) |
| 615 for (size_t markerListIndex = 0; | 388 ->removeMarkersForWords(static_cast<const Text&>(node), |
| 616 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 389 shouldRemoveMarker.m_words); |
| 617 ++markerListIndex) { | |
| 618 Member<MarkerList>& list = markers[markerListIndex]; | |
| 619 if (!list) | |
| 620 continue; | |
| 621 bool removedMarkers = false; | |
| 622 for (size_t j = list->size(); j > 0; --j) { | |
| 623 if (shouldRemoveMarker(*list->at(j - 1), | |
| 624 static_cast<const Text&>(node))) { | |
| 625 list->remove(j - 1); | |
| 626 removedMarkers = true; | |
| 627 } | |
| 628 } | |
| 629 if (removedMarkers && | |
| 630 markerListIndex == DocumentMarker::TextMatchMarkerIndex) | |
| 631 invalidatePaintForTickmarks(node); | |
| 632 } | |
| 633 } | |
| 634 } | |
| 635 | |
| 636 void DocumentMarkerController::removeMarkers( | |
| 637 DocumentMarker::MarkerTypes markerTypes) { | |
| 638 if (!possiblyHasMarkers(markerTypes)) | |
| 639 return; | |
| 640 DCHECK(!m_markers.isEmpty()); | |
| 641 | |
| 642 HeapVector<Member<const Node>> nodesWithMarkers; | |
| 643 copyKeysToVector(m_markers, nodesWithMarkers); | |
| 644 unsigned size = nodesWithMarkers.size(); | |
| 645 for (unsigned i = 0; i < size; ++i) { | |
| 646 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); | |
| 647 if (iterator != m_markers.end()) | |
| 648 removeMarkersFromList(iterator, markerTypes); | |
| 649 } | |
| 650 | |
| 651 m_possiblyExistingMarkerTypes.remove(markerTypes); | |
| 652 } | |
| 653 | |
| 654 void DocumentMarkerController::removeMarkersFromList( | |
| 655 MarkerMap::iterator iterator, | |
| 656 DocumentMarker::MarkerTypes markerTypes) { | |
| 657 bool needsRepainting = false; | |
| 658 bool nodeCanBeRemoved; | |
| 659 | |
| 660 size_t emptyListsCount = 0; | |
| 661 if (markerTypes == DocumentMarker::AllMarkers()) { | |
| 662 needsRepainting = true; | |
| 663 nodeCanBeRemoved = true; | |
| 664 } else { | |
| 665 MarkerLists* markers = iterator->value.get(); | |
| 666 | |
| 667 for (size_t markerListIndex = 0; | |
| 668 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 669 ++markerListIndex) { | |
| 670 Member<MarkerList>& list = (*markers)[markerListIndex]; | |
| 671 if (!list || list->isEmpty()) { | |
| 672 if (list.get() && list->isEmpty()) | |
| 673 list.clear(); | |
| 674 ++emptyListsCount; | |
| 675 continue; | |
| 676 } | |
| 677 if (markerTypes.contains((*list->begin())->type())) { | |
| 678 list->clear(); | |
| 679 list.clear(); | |
| 680 ++emptyListsCount; | |
| 681 needsRepainting = true; | |
| 682 } | |
| 683 } | |
| 684 | |
| 685 nodeCanBeRemoved = | |
| 686 emptyListsCount == DocumentMarker::MarkerTypeIndexesCount; | |
| 687 } | |
| 688 | |
| 689 if (needsRepainting) { | |
| 690 const Node& node = *iterator->key; | |
| 691 if (LayoutObject* layoutObject = node.layoutObject()) { | |
| 692 layoutObject->setShouldDoFullPaintInvalidation( | |
| 693 PaintInvalidationDocumentMarkerChange); | |
| 694 } | |
| 695 invalidatePaintForTickmarks(node); | |
| 696 } | |
| 697 | |
| 698 if (nodeCanBeRemoved) { | |
| 699 m_markers.erase(iterator); | |
| 700 if (m_markers.isEmpty()) | |
| 701 m_possiblyExistingMarkerTypes = 0; | |
| 702 } | 390 } |
| 703 } | 391 } |
| 704 | 392 |
| 705 void DocumentMarkerController::repaintMarkers( | 393 void DocumentMarkerController::repaintMarkers( |
| 706 DocumentMarker::MarkerTypes markerTypes) { | 394 DocumentMarker::MarkerTypes markerTypes) { |
| 707 if (!possiblyHasMarkers(markerTypes)) | 395 HeapHashSet<Member<Node>> nodesToRepaint; |
| 708 return; | 396 for (auto& nodeMarkers : m_spelling) { |
| 709 DCHECK(!m_markers.isEmpty()); | 397 if (!nodeMarkers.value->empty()) |
| 710 | 398 nodesToRepaint.insert(nodeMarkers.key.get()); |
| 711 // outer loop: process each markered node in the document | |
| 712 MarkerMap::iterator end = m_markers.end(); | |
| 713 for (MarkerMap::iterator i = m_markers.begin(); i != end; ++i) { | |
| 714 const Node* node = i->key; | |
| 715 | |
| 716 // inner loop: process each marker in the current node | |
| 717 MarkerLists* markers = i->value.get(); | |
| 718 for (size_t markerListIndex = 0; | |
| 719 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 720 ++markerListIndex) { | |
| 721 Member<MarkerList>& list = (*markers)[markerListIndex]; | |
| 722 if (!list || list->isEmpty() || | |
| 723 !markerTypes.contains((*list->begin())->type())) | |
| 724 continue; | |
| 725 | |
| 726 // cause the node to be redrawn | |
| 727 if (LayoutObject* layoutObject = node->layoutObject()) { | |
| 728 layoutObject->setShouldDoFullPaintInvalidation( | |
| 729 PaintInvalidationDocumentMarkerChange); | |
| 730 break; | |
| 731 } | |
| 732 } | |
| 733 } | |
| 734 } | |
| 735 | |
| 736 void DocumentMarkerController::shiftMarkers(Node* node, | |
| 737 unsigned startOffset, | |
| 738 int delta) { | |
| 739 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 740 return; | |
| 741 DCHECK(!m_markers.isEmpty()); | |
| 742 | |
| 743 MarkerLists* markers = m_markers.at(node); | |
| 744 if (!markers) | |
| 745 return; | |
| 746 | |
| 747 bool didShiftMarker = false; | |
| 748 for (size_t markerListIndex = 0; | |
| 749 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 750 ++markerListIndex) { | |
| 751 Member<MarkerList>& list = (*markers)[markerListIndex]; | |
| 752 if (!list) | |
| 753 continue; | |
| 754 MarkerList::iterator startPos = | |
| 755 std::lower_bound(list->begin(), list->end(), startOffset, startsAfter); | |
| 756 for (MarkerList::iterator marker = startPos; marker != list->end(); | |
| 757 ++marker) { | |
| 758 #if DCHECK_IS_ON() | |
| 759 int startOffset = (*marker)->startOffset(); | |
| 760 DCHECK_GE(startOffset + delta, 0); | |
| 761 #endif | |
| 762 (*marker)->shiftOffsets(delta); | |
| 763 didShiftMarker = true; | |
| 764 } | |
| 765 } | 399 } |
| 766 | 400 |
| 767 if (didShiftMarker) { | 401 for (auto& nodeMarkers : m_grammar) { |
| 768 invalidateRectsForMarkersInNode(*node); | 402 if (!nodeMarkers.value->empty()) |
| 769 // repaint the affected node | 403 nodesToRepaint.insert(nodeMarkers.key.get()); |
| 770 if (node->layoutObject()) { | 404 } |
| 771 node->layoutObject()->setShouldDoFullPaintInvalidation( | 405 |
| 406 for (auto& nodeMarkers : m_textMatches) { | |
| 407 if (!nodeMarkers.value->empty()) | |
| 408 nodesToRepaint.insert(nodeMarkers.key.get()); | |
| 409 } | |
| 410 | |
| 411 for (auto& nodeMarkers : m_compositions) { | |
| 412 if (!nodeMarkers.value->empty()) | |
| 413 nodesToRepaint.insert(nodeMarkers.key.get()); | |
| 414 } | |
| 415 | |
| 416 for (Member<Node> node : nodesToRepaint) { | |
| 417 // cause the node to be redrawn | |
| 418 if (LayoutObject* layoutObject = node->layoutObject()) { | |
| 419 layoutObject->setShouldDoFullPaintInvalidation( | |
| 772 PaintInvalidationDocumentMarkerChange); | 420 PaintInvalidationDocumentMarkerChange); |
| 421 break; | |
| 773 } | 422 } |
| 774 } | 423 } |
| 775 } | 424 } |
| 776 | 425 |
| 777 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, | 426 bool DocumentMarkerController::setMarkersActive(const EphemeralRange& range, |
| 778 bool active) { | 427 bool active) { |
| 779 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | |
| 780 return false; | |
| 781 | |
| 782 DCHECK(!m_markers.isEmpty()); | |
| 783 | |
| 784 Node* const startContainer = range.startPosition().computeContainerNode(); | 428 Node* const startContainer = range.startPosition().computeContainerNode(); |
| 785 DCHECK(startContainer); | 429 DCHECK(startContainer); |
| 786 Node* const endContainer = range.endPosition().computeContainerNode(); | 430 Node* const endContainer = range.endPosition().computeContainerNode(); |
| 787 DCHECK(endContainer); | 431 DCHECK(endContainer); |
| 788 | 432 |
| 789 const unsigned containerStartOffset = | 433 const unsigned containerStartOffset = |
| 790 range.startPosition().computeOffsetInContainerNode(); | 434 range.startPosition().computeOffsetInContainerNode(); |
| 791 const unsigned containerEndOffset = | 435 const unsigned containerEndOffset = |
| 792 range.endPosition().computeOffsetInContainerNode(); | 436 range.endPosition().computeOffsetInContainerNode(); |
| 793 | 437 |
| 794 bool markerFound = false; | 438 bool markerFound = false; |
| 795 for (Node& node : range.nodes()) { | 439 for (Node& node : range.nodes()) { |
| 796 int startOffset = node == startContainer ? containerStartOffset : 0; | 440 int startOffset = node == startContainer ? containerStartOffset : 0; |
| 797 int endOffset = node == endContainer ? containerEndOffset : INT_MAX; | 441 int endOffset = node == endContainer ? containerEndOffset : INT_MAX; |
| 798 markerFound |= setMarkersActive(&node, startOffset, endOffset, active); | 442 markerFound |= setMarkersActive(&node, startOffset, endOffset, active); |
| 799 } | 443 } |
| 800 return markerFound; | 444 return markerFound; |
| 801 } | 445 } |
| 802 | 446 |
| 447 bool DocumentMarkerController::hasMarkers(Node* node) const { | |
| 448 return m_spelling.contains(node) || m_grammar.contains(node) || | |
| 449 m_textMatches.contains(node) || m_compositions.contains(node); | |
| 450 } | |
| 451 | |
| 803 bool DocumentMarkerController::setMarkersActive(Node* node, | 452 bool DocumentMarkerController::setMarkersActive(Node* node, |
| 804 unsigned startOffset, | 453 unsigned startOffset, |
| 805 unsigned endOffset, | 454 unsigned endOffset, |
| 806 bool active) { | 455 bool active) { |
| 807 MarkerLists* markers = m_markers.at(node); | |
| 808 if (!markers) | |
| 809 return false; | |
| 810 | |
| 811 bool docDirty = false; | 456 bool docDirty = false; |
| 812 Member<MarkerList>& list = | 457 if (m_textMatches.contains(node)) { |
| 813 (*markers)[MarkerTypeToMarkerIndex(DocumentMarker::TextMatch)]; | 458 docDirty = static_cast<TextMatchMarkerList*>(m_textMatches.at(node)) |
| 814 if (!list) | 459 ->setTextMatchMarkersActive(startOffset, endOffset, active); |
| 815 return false; | 460 } |
| 816 MarkerList::iterator startPos = | 461 |
| 817 std::upper_bound(list->begin(), list->end(), startOffset, endsBefore); | 462 if (docDirty && node->layoutObject()) |
| 818 for (MarkerList::iterator marker = startPos; marker != list->end(); | |
| 819 ++marker) { | |
| 820 // Markers are returned in order, so stop if we are now past the specified | |
| 821 // range. | |
| 822 if ((*marker)->startOffset() >= endOffset) | |
| 823 break; | |
| 824 | |
| 825 (*marker)->setActiveMatch(active); | |
| 826 docDirty = true; | |
| 827 } | |
| 828 | |
| 829 // repaint the affected node | |
| 830 if (docDirty && node->layoutObject()) { | |
| 831 node->layoutObject()->setShouldDoFullPaintInvalidation( | 463 node->layoutObject()->setShouldDoFullPaintInvalidation( |
| 832 PaintInvalidationDocumentMarkerChange); | 464 PaintInvalidationDocumentMarkerChange); |
| 833 } | 465 |
| 834 return docDirty; | 466 return docDirty; |
| 835 } | 467 } |
| 836 | 468 |
| 837 #ifndef NDEBUG | 469 #ifndef NDEBUG |
| 470 static String showMarkerHelper(const DocumentMarkerVector& markers) { | |
| 471 StringBuilder builder; | |
| 472 | |
| 473 for (const Member<DocumentMarker> marker : markers) { | |
| 474 builder.append(" "); | |
| 475 builder.appendNumber(marker->type()); | |
| 476 builder.append(":["); | |
| 477 builder.appendNumber(marker->startOffset()); | |
| 478 builder.append(":"); | |
| 479 builder.appendNumber(marker->endOffset()); | |
| 480 builder.append("]("); | |
| 481 builder.appendNumber(marker->activeMatch()); | |
| 482 builder.append(")"); | |
| 483 } | |
| 484 | |
| 485 return builder.toString(); | |
| 486 } | |
| 487 | |
| 838 void DocumentMarkerController::showMarkers() const { | 488 void DocumentMarkerController::showMarkers() const { |
| 839 StringBuilder builder; | 489 StringBuilder builder; |
| 840 MarkerMap::const_iterator end = m_markers.end(); | 490 |
| 841 for (MarkerMap::const_iterator nodeIterator = m_markers.begin(); | 491 HeapHashSet<Member<Node>> nodes; |
| 842 nodeIterator != end; ++nodeIterator) { | 492 for (const auto& nodeMarkers : m_spelling) |
| 843 const Node* node = nodeIterator->key; | 493 nodes.insert(nodeMarkers.key); |
| 494 for (const auto& nodeMarkers : m_grammar) | |
| 495 nodes.insert(nodeMarkers.key); | |
| 496 for (const auto& nodeMarkers : m_textMatches) | |
| 497 nodes.insert(nodeMarkers.key); | |
| 498 for (const auto& nodeMarkers : m_compositions) | |
| 499 nodes.insert(nodeMarkers.key); | |
| 500 | |
| 501 HeapHashSet<Member<const Node>> nodesWithMarkers; | |
| 502 for (Node* node : nodes) { | |
| 844 builder.append(String::format("%p", node)); | 503 builder.append(String::format("%p", node)); |
| 845 MarkerLists* markers = m_markers.at(node); | 504 |
| 846 for (size_t markerListIndex = 0; | 505 if (m_spelling.contains(node)) { |
| 847 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 506 DocumentMarkerVector markers; |
| 848 ++markerListIndex) { | 507 m_spelling.at(node)->appendMarkersToInputList(&markers); |
| 849 Member<MarkerList>& list = (*markers)[markerListIndex]; | 508 if (!markers.isEmpty()) { |
| 850 for (unsigned markerIndex = 0; list.get() && markerIndex < list->size(); | 509 builder.append(showMarkerHelper(markers)); |
| 851 ++markerIndex) { | 510 nodesWithMarkers.insert(node); |
| 852 DocumentMarker* marker = list->at(markerIndex).get(); | 511 } |
| 853 builder.append(" "); | 512 } |
| 854 builder.appendNumber(marker->type()); | 513 |
| 855 builder.append(":["); | 514 if (m_grammar.contains(node)) { |
| 856 builder.appendNumber(marker->startOffset()); | 515 DocumentMarkerVector markers; |
| 857 builder.append(":"); | 516 m_grammar.at(node)->appendMarkersToInputList(&markers); |
| 858 builder.appendNumber(marker->endOffset()); | 517 if (!markers.isEmpty()) { |
| 859 builder.append("]("); | 518 builder.append(showMarkerHelper(markers)); |
| 860 builder.appendNumber(marker->activeMatch()); | 519 nodesWithMarkers.insert(node); |
| 861 builder.append(")"); | 520 } |
| 862 } | 521 } |
| 863 } | 522 |
| 523 if (m_textMatches.contains(node)) { | |
| 524 DocumentMarkerVector markers; | |
| 525 m_textMatches.at(node)->appendMarkersToInputList(&markers); | |
| 526 if (!markers.isEmpty()) { | |
| 527 builder.append(showMarkerHelper(markers)); | |
| 528 nodesWithMarkers.insert(node); | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 if (m_compositions.contains(node)) { | |
| 533 DocumentMarkerVector markers; | |
| 534 m_compositions.at(node)->appendMarkersToInputList(&markers); | |
| 535 if (!markers.isEmpty()) { | |
| 536 builder.append(showMarkerHelper(markers)); | |
| 537 nodesWithMarkers.insert(node); | |
| 538 } | |
| 539 } | |
| 540 | |
| 864 builder.append("\n"); | 541 builder.append("\n"); |
| 865 } | 542 } |
| 866 LOG(INFO) << m_markers.size() << " nodes have markers:\n" | 543 LOG(INFO) << nodesWithMarkers.size() << " nodes have markers:\n" |
| 867 << builder.toString().utf8().data(); | 544 << builder.toString().utf8().data(); |
| 868 } | 545 } |
| 869 #endif | 546 #endif |
| 870 | 547 |
| 548 DocumentMarkerList* DocumentMarkerController::createMarkerListOfType( | |
| 549 DocumentMarker::MarkerType type) { | |
| 550 switch (type) { | |
| 551 case DocumentMarker::Spelling: | |
| 552 return new SpellCheckMarkerList(this, DocumentMarker::Spelling); | |
| 553 case DocumentMarker::Grammar: | |
| 554 return new SpellCheckMarkerList(this, DocumentMarker::Grammar); | |
| 555 case DocumentMarker::TextMatch: | |
| 556 return new TextMatchMarkerList(this); | |
| 557 case DocumentMarker::Composition: | |
| 558 return new CompositionMarkerList(this); | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 HeapVector<Member<DocumentMarkerList>> | |
| 563 DocumentMarkerController::getMarkerListsForNode( | |
| 564 Node* node, | |
| 565 DocumentMarker::MarkerTypes markerTypes) { | |
| 566 HeapVector<Member<DocumentMarkerList>> markerLists; | |
| 567 | |
| 568 if (markerTypes.contains(DocumentMarker::Spelling)) { | |
| 569 auto spellingIt = m_spelling.find(node); | |
| 570 if (spellingIt != m_spelling.end()) | |
| 571 markerLists.push_back(spellingIt->value); | |
| 572 } | |
| 573 | |
| 574 if (markerTypes.contains(DocumentMarker::Grammar)) { | |
| 575 auto grammarIt = m_grammar.find(node); | |
| 576 if (grammarIt != m_grammar.end()) | |
| 577 markerLists.push_back(grammarIt->value); | |
| 578 } | |
| 579 | |
| 580 if (markerTypes.contains(DocumentMarker::TextMatch)) { | |
| 581 auto textMatchesIt = m_textMatches.find(node); | |
| 582 if (textMatchesIt != m_textMatches.end()) | |
| 583 markerLists.push_back(textMatchesIt->value); | |
| 584 } | |
| 585 | |
| 586 if (markerTypes.contains(DocumentMarker::Composition)) { | |
| 587 auto compositionsIt = m_compositions.find(node); | |
| 588 if (compositionsIt != m_compositions.end()) | |
| 589 markerLists.push_back(compositionsIt->value); | |
| 590 } | |
| 591 | |
| 592 return markerLists; | |
| 593 } | |
| 594 | |
| 595 DocumentMarkerController::MarkerMap& DocumentMarkerController::markerMapForType( | |
| 596 DocumentMarker::MarkerType type) { | |
| 597 switch (type) { | |
| 598 case DocumentMarker::Spelling: | |
| 599 return m_spelling; | |
| 600 case DocumentMarker::Grammar: | |
| 601 return m_grammar; | |
| 602 case DocumentMarker::TextMatch: | |
| 603 return m_textMatches; | |
| 604 case DocumentMarker::Composition: | |
| 605 return m_compositions; | |
| 606 } | |
| 607 } | |
| 608 | |
| 609 void DocumentMarkerController::removeMarkers( | |
| 610 TextIterator& markedText, | |
| 611 DocumentMarker::MarkerTypes markerTypes, | |
| 612 RemovePartiallyOverlappingMarkerOrNot | |
| 613 shouldRemovePartiallyOverlappingMarker) { | |
| 614 for (; !markedText.atEnd(); markedText.advance()) { | |
| 615 int startOffset = markedText.startOffsetInCurrentContainer(); | |
| 616 int endOffset = markedText.endOffsetInCurrentContainer(); | |
| 617 removeMarkers(markedText.currentContainer(), startOffset, | |
| 618 endOffset - startOffset, markerTypes, | |
| 619 shouldRemovePartiallyOverlappingMarker); | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 // SynchronousMutationObserver | |
| 624 void DocumentMarkerController::didUpdateCharacterData(CharacterData* node, | |
| 625 unsigned offset, | |
| 626 unsigned oldLength, | |
| 627 unsigned newLength) { | |
| 628 bool didShiftMarker = false; | |
| 629 for (size_t markerListIndex = static_cast<DocumentMarker::MarkerType>(0); | |
| 630 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | |
| 631 ++markerListIndex) { | |
| 632 DocumentMarker::MarkerType type = | |
| 633 static_cast<DocumentMarker::MarkerType>(1 << markerListIndex); | |
| 634 MarkerMap& markerMap = markerMapForType(type); | |
| 635 | |
| 636 auto it = markerMap.find(node); | |
| 637 if (it != markerMap.end()) { | |
| 638 didShiftMarker = it->value->shiftMarkers(offset, oldLength, newLength) || | |
| 639 didShiftMarker; | |
| 640 } | |
| 641 } | |
| 642 | |
| 643 if (didShiftMarker) { | |
| 644 invalidateRectsForMarkersInNode(*node); | |
| 645 // repaint the affected node | |
| 646 if (node->layoutObject()) { | |
| 647 node->layoutObject()->setShouldDoFullPaintInvalidation( | |
| 648 PaintInvalidationDocumentMarkerChange); | |
| 649 } | |
| 650 } | |
| 651 } | |
| 652 | |
| 871 } // namespace blink | 653 } // namespace blink |
| 872 | 654 |
| 873 #ifndef NDEBUG | 655 #ifndef NDEBUG |
| 874 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { | 656 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { |
| 875 if (controller) | 657 if (controller) |
| 876 controller->showMarkers(); | 658 controller->showMarkers(); |
| 877 } | 659 } |
| 878 #endif | 660 #endif |
| OLD | NEW |