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 17 matching lines...) Expand all Loading... | |
| 28 | 28 |
| 29 #include "core/editing/markers/DocumentMarkerController.h" | 29 #include "core/editing/markers/DocumentMarkerController.h" |
| 30 | 30 |
| 31 #include "core/dom/Node.h" | 31 #include "core/dom/Node.h" |
| 32 #include "core/dom/NodeTraversal.h" | 32 #include "core/dom/NodeTraversal.h" |
| 33 #include "core/dom/Range.h" | 33 #include "core/dom/Range.h" |
| 34 #include "core/dom/Text.h" | 34 #include "core/dom/Text.h" |
| 35 #include "core/editing/iterators/TextIterator.h" | 35 #include "core/editing/iterators/TextIterator.h" |
| 36 #include "core/editing/markers/RenderedDocumentMarker.h" | 36 #include "core/editing/markers/RenderedDocumentMarker.h" |
| 37 #include "core/frame/FrameView.h" | 37 #include "core/frame/FrameView.h" |
| 38 #include "core/frame/LocalFrame.h" | |
| 38 #include "core/layout/LayoutObject.h" | 39 #include "core/layout/LayoutObject.h" |
| 39 #include <algorithm> | 40 #include <algorithm> |
| 40 | 41 |
| 41 #ifndef NDEBUG | 42 #ifndef NDEBUG |
| 42 #include <stdio.h> | 43 #include <stdio.h> |
| 43 #endif | 44 #endif |
| 44 | 45 |
| 45 namespace blink { | 46 namespace blink { |
| 46 | 47 |
| 47 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) | 48 MarkerRemoverPredicate::MarkerRemoverPredicate(const Vector<String>& words) |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 64 case DocumentMarker::Spelling: | 65 case DocumentMarker::Spelling: |
| 65 return DocumentMarker::SpellingMarkerIndex; | 66 return DocumentMarker::SpellingMarkerIndex; |
| 66 case DocumentMarker::Grammar: | 67 case DocumentMarker::Grammar: |
| 67 return DocumentMarker::GrammarMarkerIndex; | 68 return DocumentMarker::GrammarMarkerIndex; |
| 68 case DocumentMarker::TextMatch: | 69 case DocumentMarker::TextMatch: |
| 69 return DocumentMarker::TextMatchMarkerIndex; | 70 return DocumentMarker::TextMatchMarkerIndex; |
| 70 case DocumentMarker::InvisibleSpellcheck: | 71 case DocumentMarker::InvisibleSpellcheck: |
| 71 return DocumentMarker::InvisibleSpellcheckMarkerIndex; | 72 return DocumentMarker::InvisibleSpellcheckMarkerIndex; |
| 72 case DocumentMarker::Composition: | 73 case DocumentMarker::Composition: |
| 73 return DocumentMarker::CompositionMarkerIndex; | 74 return DocumentMarker::CompositionMarkerIndex; |
| 75 case DocumentMarker::Suggestion: | |
| 76 return DocumentMarker::SuggestionMarkerIndex; | |
| 77 case DocumentMarker::SuggestionBackgroundHighlight: | |
| 78 return DocumentMarker::SuggestionBackgroundHighlightMarkerIndex; | |
| 74 } | 79 } |
| 75 | 80 |
| 76 NOTREACHED(); | 81 NOTREACHED(); |
| 77 return DocumentMarker::SpellingMarkerIndex; | 82 return DocumentMarker::SpellingMarkerIndex; |
| 78 } | 83 } |
| 79 | 84 |
| 80 } // namespace | 85 } // namespace |
| 81 | 86 |
| 82 inline bool DocumentMarkerController::possiblyHasMarkers( | 87 inline bool DocumentMarkerController::possiblyHasMarkers( |
| 83 DocumentMarker::MarkerTypes types) { | 88 DocumentMarker::MarkerTypes types) { |
| 84 return m_possiblyExistingMarkerTypes.intersects(types); | 89 return m_possiblyExistingMarkerTypes.intersects(types); |
| 85 } | 90 } |
| 86 | 91 |
| 87 DocumentMarkerController::DocumentMarkerController(const Document& document) | 92 DocumentMarkerController::DocumentMarkerController(const Document& document) |
| 88 : m_possiblyExistingMarkerTypes(0), m_document(&document) {} | 93 : m_possiblyExistingMarkerTypes(0), |
| 94 m_document(&document), | |
| 95 m_nextSuggestionMarkerID(0) {} | |
| 89 | 96 |
| 90 void DocumentMarkerController::clear() { | 97 void DocumentMarkerController::clear() { |
| 91 m_markers.clear(); | 98 m_markers.clear(); |
| 92 m_possiblyExistingMarkerTypes = 0; | 99 m_possiblyExistingMarkerTypes = 0; |
| 100 m_nextSuggestionMarkerID = 0; | |
| 93 } | 101 } |
| 94 | 102 |
| 95 void DocumentMarkerController::addMarker(const Position& start, | 103 void DocumentMarkerController::addMarker(const Position& start, |
| 96 const Position& end, | 104 const Position& end, |
| 97 DocumentMarker::MarkerType type, | 105 DocumentMarker::MarkerType type, |
| 98 const String& description, | 106 const String& description, |
| 99 uint32_t hash) { | 107 uint32_t hash) { |
| 100 // Use a TextIterator to visit the potentially multiple nodes the range | 108 // Use a TextIterator to visit the potentially multiple nodes the range |
| 101 // covers. | 109 // covers. |
| 102 for (TextIterator markedText(start, end); !markedText.atEnd(); | 110 for (TextIterator markedText(start, end); !markedText.atEnd(); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 117 for (TextIterator markedText(range.startPosition(), range.endPosition()); | 125 for (TextIterator markedText(range.startPosition(), range.endPosition()); |
| 118 !markedText.atEnd(); markedText.advance()) | 126 !markedText.atEnd(); markedText.advance()) |
| 119 addMarker( | 127 addMarker( |
| 120 markedText.currentContainer(), | 128 markedText.currentContainer(), |
| 121 DocumentMarker(markedText.startOffsetInCurrentContainer(), | 129 DocumentMarker(markedText.startOffsetInCurrentContainer(), |
| 122 markedText.endOffsetInCurrentContainer(), activeMatch)); | 130 markedText.endOffsetInCurrentContainer(), activeMatch)); |
| 123 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a | 131 // Don't invalidate tickmarks here. TextFinder invalidates tickmarks using a |
| 124 // throttling algorithm. crbug.com/6819. | 132 // throttling algorithm. crbug.com/6819. |
| 125 } | 133 } |
| 126 | 134 |
| 127 void DocumentMarkerController::addCompositionMarker(const Position& start, | 135 void DocumentMarkerController::addCompositionMarker( |
| 128 const Position& end, | 136 const Position& start, |
| 129 Color underlineColor, | 137 const Position& end, |
| 130 bool thick, | 138 Color underlineColor, |
| 131 Color backgroundColor) { | 139 bool thick, |
| 140 Color backgroundColor, | |
| 141 const std::vector<std::string>& suggestions) { | |
|
esprehn
2017/01/31 22:41:35
ditto
rlanday
2017/01/31 23:30:09
Ok
| |
| 142 DCHECK(!m_document->needsLayoutTreeUpdate()); | |
| 143 for (TextIterator markedText(start, end); !markedText.atEnd(); | |
| 144 markedText.advance()) { | |
| 145 addMarker(markedText.currentContainer(), | |
| 146 DocumentMarker(markedText.startOffsetInCurrentContainer(), | |
| 147 markedText.endOffsetInCurrentContainer(), | |
| 148 underlineColor, thick, backgroundColor, | |
| 149 suggestions, m_nextSuggestionMarkerID++)); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 void DocumentMarkerController::addSuggestionBackgroundHighlightMarker( | |
| 154 const Position& start, | |
| 155 const Position& end, | |
| 156 Color underlineColor, | |
| 157 bool thick, | |
| 158 Color backgroundColor) { | |
| 132 DCHECK(!m_document->needsLayoutTreeUpdate()); | 159 DCHECK(!m_document->needsLayoutTreeUpdate()); |
| 133 | 160 |
| 134 for (TextIterator markedText(start, end); !markedText.atEnd(); | 161 for (TextIterator markedText(start, end); !markedText.atEnd(); |
| 135 markedText.advance()) | 162 markedText.advance()) { |
| 136 addMarker(markedText.currentContainer(), | 163 addMarker(markedText.currentContainer(), |
| 137 DocumentMarker(markedText.startOffsetInCurrentContainer(), | 164 DocumentMarker(DocumentMarker::SuggestionBackgroundHighlight, |
| 165 markedText.startOffsetInCurrentContainer(), | |
| 138 markedText.endOffsetInCurrentContainer(), | 166 markedText.endOffsetInCurrentContainer(), |
| 139 underlineColor, thick, backgroundColor)); | 167 underlineColor, thick, backgroundColor)); |
| 168 } | |
| 140 } | 169 } |
| 141 | 170 |
| 142 void DocumentMarkerController::prepareForDestruction() { | 171 void DocumentMarkerController::prepareForDestruction() { |
| 143 clear(); | 172 clear(); |
| 144 } | 173 } |
| 145 | 174 |
| 146 void DocumentMarkerController::removeMarkers( | 175 void DocumentMarkerController::removeMarkers( |
| 147 TextIterator& markedText, | 176 TextIterator& markedText, |
| 148 DocumentMarker::MarkerTypes markerTypes, | 177 DocumentMarker::MarkerTypes markerTypes, |
| 149 RemovePartiallyOverlappingMarkerOrNot | 178 RemovePartiallyOverlappingMarkerOrNot |
| 150 shouldRemovePartiallyOverlappingMarker) { | 179 shouldRemovePartiallyOverlappingMarker) { |
| 151 for (; !markedText.atEnd(); markedText.advance()) { | 180 for (; !markedText.atEnd(); markedText.advance()) { |
| 152 if (!possiblyHasMarkers(markerTypes)) | 181 if (!possiblyHasMarkers(markerTypes)) |
| 153 return; | 182 return; |
| 154 DCHECK(!m_markers.isEmpty()); | 183 DCHECK(!m_markers.isEmpty()); |
| 155 | 184 |
| 156 int startOffset = markedText.startOffsetInCurrentContainer(); | 185 int startOffset = markedText.startOffsetInCurrentContainer(); |
| 157 int endOffset = markedText.endOffsetInCurrentContainer(); | 186 int endOffset = markedText.endOffsetInCurrentContainer(); |
| 187 | |
| 158 removeMarkers(markedText.currentContainer(), startOffset, | 188 removeMarkers(markedText.currentContainer(), startOffset, |
| 159 endOffset - startOffset, markerTypes, | 189 endOffset - startOffset, markerTypes, |
| 160 shouldRemovePartiallyOverlappingMarker); | 190 shouldRemovePartiallyOverlappingMarker); |
| 161 } | 191 } |
| 162 } | 192 } |
| 163 | 193 |
| 164 void DocumentMarkerController::removeMarkers( | 194 void DocumentMarkerController::removeMarkers( |
| 165 const EphemeralRange& range, | 195 const EphemeralRange& range, |
| 166 DocumentMarker::MarkerTypes markerTypes, | 196 DocumentMarker::MarkerTypes markerTypes, |
| 167 RemovePartiallyOverlappingMarkerOrNot | 197 RemovePartiallyOverlappingMarkerOrNot |
| 168 shouldRemovePartiallyOverlappingMarker) { | 198 shouldRemovePartiallyOverlappingMarker) { |
| 169 DCHECK(!m_document->needsLayoutTreeUpdate()); | 199 DCHECK(!m_document->needsLayoutTreeUpdate()); |
| 170 | 200 |
| 171 TextIterator markedText(range.startPosition(), range.endPosition()); | 201 TextIterator markedText(range.startPosition(), range.endPosition()); |
| 172 DocumentMarkerController::removeMarkers( | 202 DocumentMarkerController::removeMarkers( |
| 173 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); | 203 markedText, markerTypes, shouldRemovePartiallyOverlappingMarker); |
| 174 } | 204 } |
| 175 | 205 |
| 176 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, | 206 static bool startsFurther(const Member<RenderedDocumentMarker>& lhv, |
| 177 const DocumentMarker* rhv) { | 207 const DocumentMarker* rhv) { |
| 178 return lhv->startOffset() < rhv->startOffset(); | 208 return lhv->startOffset() < rhv->startOffset(); |
| 179 } | 209 } |
| 180 | 210 |
| 181 static bool startsAfter(const Member<RenderedDocumentMarker>& marker, | |
| 182 size_t startOffset) { | |
| 183 return marker->startOffset() < startOffset; | |
| 184 } | |
| 185 | |
| 186 static bool compareByStart(const Member<DocumentMarker>& lhv, | 211 static bool compareByStart(const Member<DocumentMarker>& lhv, |
| 187 const Member<DocumentMarker>& rhv) { | 212 const Member<DocumentMarker>& rhv) { |
| 188 return lhv->startOffset() < rhv->startOffset(); | 213 return lhv->startOffset() < rhv->startOffset(); |
| 189 } | 214 } |
| 190 | 215 |
| 191 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv, | 216 static bool doesNotOverlap(const Member<RenderedDocumentMarker>& lhv, |
| 192 const DocumentMarker* rhv) { | 217 const DocumentMarker* rhv) { |
| 193 return lhv->endOffset() < rhv->startOffset(); | 218 return lhv->endOffset() < rhv->startOffset(); |
| 194 } | 219 } |
| 195 | 220 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 markers->at(markerListIndex) = new MarkerList; | 267 markers->at(markerListIndex) = new MarkerList; |
| 243 } | 268 } |
| 244 | 269 |
| 245 Member<MarkerList>& list = markers->at(markerListIndex); | 270 Member<MarkerList>& list = markers->at(markerListIndex); |
| 246 RenderedDocumentMarker* newRenderedMarker = | 271 RenderedDocumentMarker* newRenderedMarker = |
| 247 RenderedDocumentMarker::create(newMarker); | 272 RenderedDocumentMarker::create(newMarker); |
| 248 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) { | 273 if (list->isEmpty() || list->back()->endOffset() < newMarker.startOffset()) { |
| 249 list->push_back(newRenderedMarker); | 274 list->push_back(newRenderedMarker); |
| 250 } else { | 275 } else { |
| 251 if (newMarker.type() != DocumentMarker::TextMatch && | 276 if (newMarker.type() != DocumentMarker::TextMatch && |
| 252 newMarker.type() != DocumentMarker::Composition) { | 277 newMarker.type() != DocumentMarker::Composition && |
| 278 newMarker.type() != DocumentMarker::Suggestion) { | |
| 253 mergeOverlapping(list.get(), newRenderedMarker); | 279 mergeOverlapping(list.get(), newRenderedMarker); |
| 254 } else { | 280 } else { |
| 255 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), | 281 MarkerList::iterator pos = std::lower_bound(list->begin(), list->end(), |
| 256 &newMarker, startsFurther); | 282 &newMarker, startsFurther); |
| 257 list->insert(pos - list->begin(), newRenderedMarker); | 283 list->insert(pos - list->begin(), newRenderedMarker); |
| 258 } | 284 } |
| 259 } | 285 } |
| 260 | 286 |
| 261 // repaint the affected node | 287 // repaint the affected node |
| 262 if (node->layoutObject()) | 288 if (node->layoutObject()) |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 341 unsigned startOffset, | 367 unsigned startOffset, |
| 342 int length, | 368 int length, |
| 343 DocumentMarker::MarkerTypes markerTypes, | 369 DocumentMarker::MarkerTypes markerTypes, |
| 344 RemovePartiallyOverlappingMarkerOrNot | 370 RemovePartiallyOverlappingMarkerOrNot |
| 345 shouldRemovePartiallyOverlappingMarker) { | 371 shouldRemovePartiallyOverlappingMarker) { |
| 346 if (length <= 0) | 372 if (length <= 0) |
| 347 return; | 373 return; |
| 348 | 374 |
| 349 if (!possiblyHasMarkers(markerTypes)) | 375 if (!possiblyHasMarkers(markerTypes)) |
| 350 return; | 376 return; |
| 377 | |
| 351 DCHECK(!(m_markers.isEmpty())); | 378 DCHECK(!(m_markers.isEmpty())); |
| 352 | 379 |
| 353 MarkerLists* markers = m_markers.get(node); | 380 MarkerLists* markers = m_markers.get(node); |
| 354 if (!markers) | 381 if (!markers) |
| 355 return; | 382 return; |
| 356 | 383 |
| 357 bool docDirty = false; | 384 bool docDirty = false; |
| 358 size_t emptyListsCount = 0; | 385 size_t emptyListsCount = 0; |
| 359 for (size_t markerListIndex = 0; | 386 for (size_t markerListIndex = 0; |
| 360 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 387 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 462 result.push_back(list->at(j).get()); | 489 result.push_back(list->at(j).get()); |
| 463 } | 490 } |
| 464 } | 491 } |
| 465 std::sort(result.begin(), result.end(), compareByStart); | 492 std::sort(result.begin(), result.end(), compareByStart); |
| 466 return result; | 493 return result; |
| 467 } | 494 } |
| 468 | 495 |
| 469 DocumentMarkerVector DocumentMarkerController::markersInRange( | 496 DocumentMarkerVector DocumentMarkerController::markersInRange( |
| 470 const EphemeralRange& range, | 497 const EphemeralRange& range, |
| 471 DocumentMarker::MarkerTypes markerTypes) { | 498 DocumentMarker::MarkerTypes markerTypes) { |
| 499 return markersInRangeInternal(range, markerTypes, false); | |
| 500 } | |
| 501 | |
| 502 DocumentMarkerVector DocumentMarkerController::markersInRangeInclusive( | |
| 503 const EphemeralRange& range, | |
| 504 DocumentMarker::MarkerTypes markerTypes) { | |
| 505 return markersInRangeInternal(range, markerTypes, true); | |
| 506 } | |
| 507 | |
| 508 DocumentMarkerVector DocumentMarkerController::markersInRangeInternal( | |
| 509 const EphemeralRange& range, | |
| 510 DocumentMarker::MarkerTypes markerTypes, | |
| 511 bool includeSpansTouchingEndpoints) { | |
| 472 if (!possiblyHasMarkers(markerTypes)) | 512 if (!possiblyHasMarkers(markerTypes)) |
| 473 return DocumentMarkerVector(); | 513 return DocumentMarkerVector(); |
| 474 | 514 |
| 475 DocumentMarkerVector foundMarkers; | 515 DocumentMarkerVector foundMarkers; |
| 476 | 516 |
| 477 Node* startContainer = range.startPosition().computeContainerNode(); | 517 Node* startContainer = range.startPosition().computeContainerNode(); |
| 478 DCHECK(startContainer); | 518 if (!startContainer) |
| 519 return DocumentMarkerVector(); | |
| 520 | |
| 479 unsigned startOffset = static_cast<unsigned>( | 521 unsigned startOffset = static_cast<unsigned>( |
| 480 range.startPosition().computeOffsetInContainerNode()); | 522 range.startPosition().computeOffsetInContainerNode()); |
| 523 | |
| 481 Node* endContainer = range.endPosition().computeContainerNode(); | 524 Node* endContainer = range.endPosition().computeContainerNode(); |
| 482 DCHECK(endContainer); | 525 if (!endContainer) |
| 526 return DocumentMarkerVector(); | |
| 527 | |
| 483 unsigned endOffset = | 528 unsigned endOffset = |
| 484 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); | 529 static_cast<unsigned>(range.endPosition().computeOffsetInContainerNode()); |
| 485 | 530 |
| 486 for (Node& node : range.nodes()) { | 531 for (Node& node : range.nodes()) { |
| 487 for (DocumentMarker* marker : markersFor(&node)) { | 532 for (DocumentMarker* marker : markersFor(&node)) { |
| 488 if (!markerTypes.contains(marker->type())) | 533 if (!markerTypes.contains(marker->type())) |
| 489 continue; | 534 continue; |
| 490 if (node == startContainer && marker->endOffset() <= startOffset) | 535 if (node == startContainer) { |
| 491 continue; | 536 if (marker->endOffset() < startOffset || |
| 492 if (node == endContainer && marker->startOffset() >= endOffset) | 537 (marker->endOffset() == startOffset && |
| 493 continue; | 538 !includeSpansTouchingEndpoints)) |
| 539 continue; | |
| 540 } | |
| 541 if (node == endContainer) { | |
| 542 if (marker->startOffset() > endOffset || | |
| 543 (marker->startOffset() == endOffset && | |
| 544 !includeSpansTouchingEndpoints)) | |
| 545 continue; | |
| 546 } | |
| 494 foundMarkers.push_back(marker); | 547 foundMarkers.push_back(marker); |
| 495 } | 548 } |
| 496 } | 549 } |
| 497 return foundMarkers; | 550 return foundMarkers; |
| 498 } | 551 } |
| 499 | 552 |
| 500 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( | 553 Vector<IntRect> DocumentMarkerController::renderedRectsForMarkers( |
| 501 DocumentMarker::MarkerType markerType) { | 554 DocumentMarker::MarkerType markerType) { |
| 502 Vector<IntRect> result; | 555 Vector<IntRect> result; |
| 503 | 556 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 634 unsigned size = nodesWithMarkers.size(); | 687 unsigned size = nodesWithMarkers.size(); |
| 635 for (unsigned i = 0; i < size; ++i) { | 688 for (unsigned i = 0; i < size; ++i) { |
| 636 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); | 689 MarkerMap::iterator iterator = m_markers.find(nodesWithMarkers[i]); |
| 637 if (iterator != m_markers.end()) | 690 if (iterator != m_markers.end()) |
| 638 removeMarkersFromList(iterator, markerTypes); | 691 removeMarkersFromList(iterator, markerTypes); |
| 639 } | 692 } |
| 640 | 693 |
| 641 m_possiblyExistingMarkerTypes.remove(markerTypes); | 694 m_possiblyExistingMarkerTypes.remove(markerTypes); |
| 642 } | 695 } |
| 643 | 696 |
| 697 void DocumentMarkerController::removeSuggestionMarkersByID( | |
| 698 const Vector<int>& idsToRemove) { | |
| 699 for (auto& nodeMarkers : m_markers) { | |
| 700 MarkerLists& markers = *nodeMarkers.value; | |
| 701 Member<MarkerList>& suggestionMarkerList = | |
| 702 markers[DocumentMarker::SuggestionMarkerIndex]; | |
| 703 if (!suggestionMarkerList) | |
| 704 continue; | |
| 705 bool removedMarkers = false; | |
| 706 for (size_t j = suggestionMarkerList->size(); j > 0; --j) { | |
| 707 if (idsToRemove.contains( | |
| 708 suggestionMarkerList->at(j - 1)->suggestionMarkerID())) { | |
| 709 suggestionMarkerList->remove(j - 1); | |
| 710 removedMarkers = true; | |
| 711 } | |
| 712 } | |
| 713 } | |
| 714 } | |
| 715 | |
| 716 void DocumentMarkerController::removeMarkersForWordsAffectedByEditing( | |
| 717 DocumentMarker::MarkerTypes markerTypes, | |
| 718 bool doNotRemoveIfSelectionAtWordBoundary) { | |
| 719 DCHECK(m_document->frame()->selection().isAvailable()); | |
| 720 TRACE_EVENT0( | |
| 721 "blink", | |
| 722 "DocumentMarkerController::removeMarkersForWordsAffectedByEditing"); | |
| 723 | |
| 724 Document* document = m_document->frame()->document(); | |
| 725 DCHECK(document); | |
| 726 | |
| 727 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | |
| 728 // needs to be audited. See http://crbug.com/590369 for more details. | |
| 729 document->updateStyleAndLayoutIgnorePendingStylesheets(); | |
| 730 | |
| 731 // We want to remove the markers from a word if an editing command will change | |
| 732 // the word. This can happen in one of several scenarios: | |
| 733 // 1. Insert in the middle of a word. | |
| 734 // 2. Appending non whitespace at the beginning of word. | |
| 735 // 3. Appending non whitespace at the end of word. | |
| 736 // Note that, appending only whitespaces at the beginning or end of word won't | |
| 737 // change the word, so we don't need to remove the markers on that word. Of | |
| 738 // course, if current selection is a range, we potentially will edit two words | |
| 739 // that fall on the boundaries of selection, and remove words between the | |
| 740 // selection boundaries. | |
| 741 VisiblePosition startOfSelection = | |
| 742 m_document->frame()->selection().selection().visibleStart(); | |
| 743 VisiblePosition endOfSelection = | |
| 744 m_document->frame()->selection().selection().visibleEnd(); | |
| 745 if (startOfSelection.isNull()) | |
| 746 return; | |
| 747 // First word is the word that ends after or on the start of selection. | |
| 748 VisiblePosition startOfFirstWord = | |
| 749 startOfWord(startOfSelection, LeftWordIfOnBoundary); | |
| 750 VisiblePosition endOfFirstWord = | |
| 751 endOfWord(startOfSelection, LeftWordIfOnBoundary); | |
| 752 // Last word is the word that begins before or on the end of selection | |
| 753 VisiblePosition startOfLastWord = | |
| 754 startOfWord(endOfSelection, RightWordIfOnBoundary); | |
| 755 VisiblePosition endOfLastWord = | |
| 756 endOfWord(endOfSelection, RightWordIfOnBoundary); | |
| 757 | |
| 758 if (startOfFirstWord.isNull()) { | |
| 759 startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); | |
| 760 endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); | |
| 761 } | |
| 762 | |
| 763 if (endOfLastWord.isNull()) { | |
| 764 startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); | |
| 765 endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); | |
| 766 } | |
| 767 | |
| 768 // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the | |
| 769 // start of selection, we choose next word as the first word. | |
| 770 if (doNotRemoveIfSelectionAtWordBoundary && | |
| 771 endOfFirstWord.deepEquivalent() == startOfSelection.deepEquivalent()) { | |
| 772 startOfFirstWord = nextWordPosition(startOfFirstWord); | |
| 773 endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); | |
| 774 if (startOfFirstWord.deepEquivalent() == endOfSelection.deepEquivalent()) | |
| 775 return; | |
| 776 } | |
| 777 | |
| 778 // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at | |
| 779 // the end of selection, we choose previous word as the last word. | |
| 780 if (doNotRemoveIfSelectionAtWordBoundary && | |
| 781 startOfLastWord.deepEquivalent() == endOfSelection.deepEquivalent()) { | |
| 782 startOfLastWord = previousWordPosition(startOfLastWord); | |
| 783 endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); | |
| 784 if (endOfLastWord.deepEquivalent() == startOfSelection.deepEquivalent()) | |
| 785 return; | |
| 786 } | |
| 787 | |
| 788 if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || | |
| 789 startOfLastWord.isNull() || endOfLastWord.isNull()) | |
| 790 return; | |
| 791 | |
| 792 const Position& removeMarkerStart = startOfFirstWord.deepEquivalent(); | |
| 793 const Position& removeMarkerEnd = endOfLastWord.deepEquivalent(); | |
| 794 if (removeMarkerStart > removeMarkerEnd) { | |
| 795 // editing/inserting/insert-br-008.html and more reach here. | |
| 796 // TODO(yosin): To avoid |DCHECK(removeMarkerStart <= removeMarkerEnd)| | |
| 797 // in |EphemeralRange| constructor, we have this if-statement. Once we | |
| 798 // fix |startOfWord()| and |endOfWord()|, we should remove this | |
| 799 // if-statement. | |
| 800 return; | |
| 801 } | |
| 802 | |
| 803 // Now we remove markers on everything between startOfFirstWord and | |
| 804 // endOfLastWord. However, if an autocorrection change a single word to | |
| 805 // multiple words, we want to remove correction mark from all the resulted | |
| 806 // words even we only edit one of them. For example, assuming autocorrection | |
| 807 // changes "avantgarde" to "avant garde", we will have CorrectionIndicator | |
| 808 // marker on both words and on the whitespace between them. If we then edit | |
| 809 // garde, we would like to remove the marker from word "avant" and whitespace | |
| 810 // as well. So we need to get the continous range of of marker that contains | |
| 811 // the word in question, and remove marker on that whole range. | |
| 812 const EphemeralRange wordRange(removeMarkerStart, removeMarkerEnd); | |
| 813 document->markers().removeMarkers( | |
| 814 wordRange, markerTypes, | |
| 815 DocumentMarkerController::RemovePartiallyOverlappingMarker); | |
| 816 } | |
| 817 | |
| 644 void DocumentMarkerController::removeMarkersFromList( | 818 void DocumentMarkerController::removeMarkersFromList( |
| 645 MarkerMap::iterator iterator, | 819 MarkerMap::iterator iterator, |
| 646 DocumentMarker::MarkerTypes markerTypes) { | 820 DocumentMarker::MarkerTypes markerTypes) { |
| 647 bool needsRepainting = false; | 821 bool needsRepainting = false; |
| 648 bool nodeCanBeRemoved; | 822 bool nodeCanBeRemoved; |
| 649 | 823 |
| 650 size_t emptyListsCount = 0; | 824 size_t emptyListsCount = 0; |
| 651 if (markerTypes == DocumentMarker::AllMarkers()) { | 825 if (markerTypes == DocumentMarker::AllMarkers()) { |
| 652 needsRepainting = true; | 826 needsRepainting = true; |
| 653 nodeCanBeRemoved = true; | 827 nodeCanBeRemoved = true; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 714 // cause the node to be redrawn | 888 // cause the node to be redrawn |
| 715 if (LayoutObject* layoutObject = node->layoutObject()) { | 889 if (LayoutObject* layoutObject = node->layoutObject()) { |
| 716 layoutObject->setShouldDoFullPaintInvalidation(); | 890 layoutObject->setShouldDoFullPaintInvalidation(); |
| 717 break; | 891 break; |
| 718 } | 892 } |
| 719 } | 893 } |
| 720 } | 894 } |
| 721 } | 895 } |
| 722 | 896 |
| 723 void DocumentMarkerController::shiftMarkers(Node* node, | 897 void DocumentMarkerController::shiftMarkers(Node* node, |
| 724 unsigned startOffset, | 898 int startOffset, |
| 725 int delta) { | 899 int prevLength, |
| 900 int newLength) { | |
| 901 DCHECK_GE(startOffset, 0); | |
| 902 DCHECK_GE(prevLength, 0); | |
| 903 DCHECK_GE(newLength, 0); | |
| 904 | |
| 726 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) | 905 if (!possiblyHasMarkers(DocumentMarker::AllMarkers())) |
| 727 return; | 906 return; |
| 728 DCHECK(!m_markers.isEmpty()); | 907 DCHECK(!m_markers.isEmpty()); |
| 729 | 908 |
| 730 MarkerLists* markers = m_markers.get(node); | 909 MarkerLists* markers = m_markers.get(node); |
| 731 if (!markers) | 910 if (!markers) |
| 732 return; | 911 return; |
| 733 | 912 |
| 734 bool didShiftMarker = false; | 913 bool didShiftMarker = false; |
| 735 for (size_t markerListIndex = 0; | 914 for (size_t markerListIndex = 0; |
| 736 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; | 915 markerListIndex < DocumentMarker::MarkerTypeIndexesCount; |
| 737 ++markerListIndex) { | 916 ++markerListIndex) { |
| 738 Member<MarkerList>& list = (*markers)[markerListIndex]; | 917 Member<MarkerList>& list = (*markers)[markerListIndex]; |
| 739 if (!list) | 918 if (!list) |
| 740 continue; | 919 continue; |
| 741 MarkerList::iterator startPos = | 920 |
| 742 std::lower_bound(list->begin(), list->end(), startOffset, startsAfter); | 921 // algorithm from https://dom.spec.whatwg.org/#concept-cd-replace |
|
rlanday
2017/01/31 19:50:22
This is fancy logic that lets you replace part of
| |
| 743 for (MarkerList::iterator marker = startPos; marker != list->end(); | 922 for (MarkerList::iterator marker = list->begin(); marker != list->end(); |
| 744 ++marker) { | 923 ++marker) { |
| 745 #if DCHECK_IS_ON() | 924 if ((*marker)->startOffset() > (unsigned)startOffset) { |
| 746 int startOffset = (*marker)->startOffset(); | 925 if ((*marker)->startOffset() <= (unsigned)(startOffset + prevLength)) { |
| 747 DCHECK_GE(startOffset + delta, 0); | 926 // Marker start was in the replaced text. Move to beginning of new |
| 748 #endif | 927 // text |
| 749 (*marker)->shiftOffsets(delta); | 928 (*marker)->setStartOffset(startOffset); |
| 929 } else { | |
| 930 // Marker start was after the replaced text. Shift by length | |
| 931 // difference | |
| 932 unsigned oldStartOffset = (*marker)->startOffset(); | |
| 933 (*marker)->setStartOffset(oldStartOffset + newLength - prevLength); | |
| 934 } | |
| 935 } | |
| 936 | |
| 937 if ((*marker)->endOffset() > (unsigned)startOffset) { | |
| 938 if ((*marker)->endOffset() <= (unsigned)(startOffset + prevLength)) { | |
| 939 // Marker end was in the replaced text. Move to beginning of new text | |
| 940 (*marker)->setEndOffset(startOffset); | |
| 941 } else { | |
| 942 // Marker end was after the replaced text. Shift by length difference | |
| 943 unsigned oldEndOffset = (*marker)->endOffset(); | |
| 944 (*marker)->setEndOffset(oldEndOffset + newLength - prevLength); | |
| 945 } | |
| 946 } | |
| 947 | |
| 750 didShiftMarker = true; | 948 didShiftMarker = true; |
| 751 } | 949 } |
| 752 } | 950 } |
| 753 | 951 |
| 754 if (didShiftMarker) { | 952 if (didShiftMarker) { |
| 755 invalidateRectsForMarkersInNode(*node); | 953 invalidateRectsForMarkersInNode(*node); |
| 756 // repaint the affected node | 954 // repaint the affected node |
| 757 if (node->layoutObject()) | 955 if (node->layoutObject()) |
| 758 node->layoutObject()->setShouldDoFullPaintInvalidation(); | 956 node->layoutObject()->setShouldDoFullPaintInvalidation(); |
| 759 } | 957 } |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 849 #endif | 1047 #endif |
| 850 | 1048 |
| 851 } // namespace blink | 1049 } // namespace blink |
| 852 | 1050 |
| 853 #ifndef NDEBUG | 1051 #ifndef NDEBUG |
| 854 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { | 1052 void showDocumentMarkers(const blink::DocumentMarkerController* controller) { |
| 855 if (controller) | 1053 if (controller) |
| 856 controller->showMarkers(); | 1054 controller->showMarkers(); |
| 857 } | 1055 } |
| 858 #endif | 1056 #endif |
| OLD | NEW |