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