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 |