Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(149)

Side by Side Diff: third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.cpp

Issue 2768393003: Use finer-grained checking in cold mode (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/editing/spellcheck/ColdModeSpellCheckRequester.h" 5 #include "core/editing/spellcheck/ColdModeSpellCheckRequester.h"
6 6
7 #include "core/dom/Element.h" 7 #include "core/dom/Element.h"
8 #include "core/dom/IdleDeadline.h" 8 #include "core/dom/IdleDeadline.h"
9 #include "core/editing/EditingUtilities.h" 9 #include "core/editing/EditingUtilities.h"
10 #include "core/editing/VisibleUnits.h" 10 #include "core/editing/VisibleUnits.h"
11 #include "core/editing/iterators/CharacterIterator.h" 11 #include "core/editing/iterators/CharacterIterator.h"
12 #include "core/editing/spellcheck/SpellCheckRequester.h" 12 #include "core/editing/spellcheck/SpellCheckRequester.h"
13 #include "core/editing/spellcheck/SpellChecker.h" 13 #include "core/editing/spellcheck/SpellChecker.h"
14 #include "core/frame/LocalFrame.h" 14 #include "core/frame/LocalFrame.h"
15 #include "platform/instrumentation/tracing/TraceEvent.h" 15 #include "platform/instrumentation/tracing/TraceEvent.h"
16 16
17 namespace blink { 17 namespace blink {
18 18
19 namespace { 19 namespace {
20 20
21 const int kColdModeChunkSize = 16384; // in UTF16 code units 21 const int kColdModeChunkSize = 16384; // in UTF16 code units
22 const int kInvalidLength = -1;
23 const int kInvalidChunkIndex = -1;
22 24
23 bool shouldCheckNode(const Node& node) { 25 bool shouldCheckNode(const Node& node) {
24 if (!node.isElementNode()) 26 if (!node.isElementNode())
25 return false; 27 return false;
26 // TODO(editing-dev): Make |Position| constructors take const parameters. 28 // TODO(editing-dev): Make |Position| constructors take const parameters.
27 const Position& position = 29 const Position& position =
28 Position::firstPositionInNode(const_cast<Node*>(&node)); 30 Position::firstPositionInNode(const_cast<Node*>(&node));
29 if (!isEditablePosition(position)) 31 if (!isEditablePosition(position))
30 return false; 32 return false;
31 return SpellChecker::isSpellCheckingEnabledAt(position); 33 return SpellChecker::isSpellCheckingEnabledAt(position);
32 } 34 }
33 35
34 } // namespace 36 } // namespace
35 37
36 ColdModeSpellCheckRequester::~ColdModeSpellCheckRequester() = default; 38 ColdModeSpellCheckRequester::~ColdModeSpellCheckRequester() = default;
37 39
38 // static 40 // static
39 ColdModeSpellCheckRequester* ColdModeSpellCheckRequester::create( 41 ColdModeSpellCheckRequester* ColdModeSpellCheckRequester::create(
40 LocalFrame& frame) { 42 LocalFrame& frame) {
41 return new ColdModeSpellCheckRequester(frame); 43 return new ColdModeSpellCheckRequester(frame);
42 } 44 }
43 45
44 DEFINE_TRACE(ColdModeSpellCheckRequester) { 46 DEFINE_TRACE(ColdModeSpellCheckRequester) {
45 visitor->trace(m_frame); 47 visitor->trace(m_frame);
46 visitor->trace(m_nextNode); 48 visitor->trace(m_nextNode);
49 visitor->trace(m_currentRootEditable);
50 visitor->trace(m_currentChunkStart);
47 } 51 }
48 52
49 ColdModeSpellCheckRequester::ColdModeSpellCheckRequester(LocalFrame& frame) 53 ColdModeSpellCheckRequester::ColdModeSpellCheckRequester(LocalFrame& frame)
50 : m_frame(frame), 54 : m_frame(frame),
51 m_lastCheckedDOMTreeVersion(0), 55 m_lastCheckedDOMTreeVersion(0),
52 m_needsMoreInvocationForTesting(false) {} 56 m_needsMoreInvocationForTesting(false) {}
53 57
54 bool ColdModeSpellCheckRequester::fullDocumentChecked() const { 58 bool ColdModeSpellCheckRequester::fullDocumentChecked() const {
55 if (m_needsMoreInvocationForTesting) { 59 if (m_needsMoreInvocationForTesting) {
56 m_needsMoreInvocationForTesting = false; 60 m_needsMoreInvocationForTesting = false;
57 return false; 61 return false;
58 } 62 }
59 return !m_nextNode; 63 return !m_nextNode;
60 } 64 }
61 65
62 SpellCheckRequester& ColdModeSpellCheckRequester::spellCheckRequester() const { 66 SpellCheckRequester& ColdModeSpellCheckRequester::spellCheckRequester() const {
63 return frame().spellChecker().spellCheckRequester(); 67 return frame().spellChecker().spellCheckRequester();
64 } 68 }
65 69
66 // TODO(xiaochengh): Deduplicate with SpellChecker::chunkAndMarkAllMisspellings.
67 void ColdModeSpellCheckRequester::chunkAndRequestFullCheckingFor(
68 const Element& editable) {
69 const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable);
70 const int fullLength = TextIterator::rangeLength(fullRange.startPosition(),
71 fullRange.endPosition());
72
73 // Check the full content if it is short.
74 if (fullLength <= kColdModeChunkSize) {
75 spellCheckRequester().requestCheckingFor(fullRange);
76 return;
77 }
78
79 // TODO(xiaochengh): Figure out if this is going to cause performance issues.
80 // In that case, we need finer-grained control over request generation.
81 Position chunkStart = fullRange.startPosition();
82 const int chunkLimit = fullLength / kColdModeChunkSize + 1;
83 for (int chunkIndex = 0; chunkIndex <= chunkLimit; ++chunkIndex) {
84 const Position& chunkEnd =
85 calculateCharacterSubrange(
86 EphemeralRange(chunkStart, fullRange.endPosition()), 0,
87 kColdModeChunkSize)
88 .endPosition();
89 if (chunkEnd <= chunkStart)
90 break;
91 const EphemeralRange chunkRange(chunkStart, chunkEnd);
92 const EphemeralRange& checkRange =
93 chunkIndex >= 1 ? expandEndToSentenceBoundary(chunkRange)
94 : expandRangeToSentenceBoundary(chunkRange);
95
96 spellCheckRequester().requestCheckingFor(checkRange, chunkIndex);
97
98 chunkStart = checkRange.endPosition();
99 }
100 }
101
102 void ColdModeSpellCheckRequester::invoke(IdleDeadline* deadline) { 70 void ColdModeSpellCheckRequester::invoke(IdleDeadline* deadline) {
103 TRACE_EVENT0("blink", "ColdModeSpellCheckRequester::invoke"); 71 TRACE_EVENT0("blink", "ColdModeSpellCheckRequester::invoke");
104 72
105 Node* body = frame().document()->body(); 73 Node* body = frame().document()->body();
106 if (!body) { 74 if (!body) {
107 m_nextNode = nullptr; 75 resetCheckingProgress();
108 m_lastCheckedDOMTreeVersion = frame().document()->domTreeVersion(); 76 m_lastCheckedDOMTreeVersion = frame().document()->domTreeVersion();
109 return; 77 return;
110 } 78 }
111 79
112 // TODO(xiaochengh): Figure out if this has any performance impact. 80 // TODO(xiaochengh): Figure out if this has any performance impact.
113 frame().document()->updateStyleAndLayout(); 81 frame().document()->updateStyleAndLayout();
114 82
115 if (m_lastCheckedDOMTreeVersion != frame().document()->domTreeVersion()) 83 if (m_lastCheckedDOMTreeVersion != frame().document()->domTreeVersion())
116 m_nextNode = body; 84 resetCheckingProgress();
117 85
118 // TODO(xiaochengh): Figure out if such frequent calls of |timeRemaining()| 86 while (m_nextNode && deadline->timeRemaining() > 0)
119 // have any performance impact. We might not want to check remaining time 87 step();
120 // so frequently in a page with millions of nodes.
121 while (m_nextNode && deadline->timeRemaining() > 0) {
122 if (!shouldCheckNode(*m_nextNode)) {
123 m_nextNode = FlatTreeTraversal::next(*m_nextNode, body);
124 continue;
125 }
126
127 chunkAndRequestFullCheckingFor(toElement(*m_nextNode));
128 m_nextNode = FlatTreeTraversal::nextSkippingChildren(*m_nextNode, body);
129 }
130
131 m_lastCheckedDOMTreeVersion = frame().document()->domTreeVersion(); 88 m_lastCheckedDOMTreeVersion = frame().document()->domTreeVersion();
132 } 89 }
133 90
91 void ColdModeSpellCheckRequester::resetCheckingProgress() {
92 m_nextNode = frame().document()->body();
93 m_currentRootEditable = nullptr;
94 m_currentFullLength = kInvalidLength;
95 m_currentChunkIndex = kInvalidChunkIndex;
96 m_currentChunkStart = Position();
97 }
98
99 void ColdModeSpellCheckRequester::step() {
100 if (!m_nextNode)
101 return;
102
103 if (!m_currentRootEditable) {
104 searchForNextRootEditable();
105 return;
106 }
107
108 if (m_currentFullLength == kInvalidLength) {
109 initializeForCurrentRootEditable();
110 return;
111 }
112
113 DCHECK(m_currentChunkIndex != kInvalidChunkIndex);
114 requestCheckingForNextChunk();
115 }
116
117 void ColdModeSpellCheckRequester::searchForNextRootEditable() {
118 // TODO(xiaochengh): Figure out if such small steps, which result in frequent
119 // calls of |timeRemaining()|, have any performance impact. We might not want
120 // to check remaining time so frequently in a page with millions of nodes.
121
122 if (shouldCheckNode(*m_nextNode)) {
123 m_currentRootEditable = toElement(m_nextNode);
124 return;
125 }
126
127 m_nextNode = FlatTreeTraversal::next(*m_nextNode, frame().document()->body());
yosin_UTC9 2017/03/24 06:38:42 We need to call |m_nextNode->updateDistribution()|
Xiaocheng 2017/03/24 21:18:43 This function is always called with clean layout,
128 }
129
130 void ColdModeSpellCheckRequester::initializeForCurrentRootEditable() {
131 const EphemeralRange& fullRange =
132 EphemeralRange::rangeOfContents(*m_currentRootEditable);
133 m_currentFullLength = TextIterator::rangeLength(fullRange.startPosition(),
yosin_UTC9 2017/03/24 06:38:42 Can we use DOM tree base character iterator? Or us
Xiaocheng 2017/03/24 21:18:43 Sorry I don't understand. Could you give more deta
yosin_UTC9 2017/03/27 04:44:14 TextIterator::rangeLength() is expensive and Plain
134 fullRange.endPosition());
135
136 m_currentChunkIndex = 0;
137 m_currentChunkStart = fullRange.startPosition();
138 }
139
140 void ColdModeSpellCheckRequester::requestCheckingForNextChunk() {
141 // Check the full content if it is short.
142 if (m_currentFullLength <= kColdModeChunkSize) {
143 spellCheckRequester().requestCheckingFor(
144 EphemeralRange::rangeOfContents(*m_currentRootEditable));
145 finishCheckingCurrentRootEditable();
146 return;
147 }
148
149 const Position& chunkEnd =
150 calculateCharacterSubrange(
151 EphemeralRange(m_currentChunkStart,
152 Position::lastPositionInNode(m_currentRootEditable)),
153 0, kColdModeChunkSize)
154 .endPosition();
155 if (chunkEnd <= m_currentChunkStart) {
156 finishCheckingCurrentRootEditable();
157 return;
158 }
159 const EphemeralRange chunkRange(m_currentChunkStart, chunkEnd);
160 const EphemeralRange& checkRange = expandEndToSentenceBoundary(chunkRange);
161 spellCheckRequester().requestCheckingFor(checkRange, m_currentChunkIndex);
162
163 m_currentChunkStart = checkRange.endPosition();
164 ++m_currentChunkIndex;
165
166 if (m_currentChunkIndex * kColdModeChunkSize >= m_currentFullLength)
167 finishCheckingCurrentRootEditable();
168 }
169
170 void ColdModeSpellCheckRequester::finishCheckingCurrentRootEditable() {
171 DCHECK_EQ(m_nextNode, m_currentRootEditable);
172 m_nextNode = FlatTreeTraversal::nextSkippingChildren(
173 *m_nextNode, frame().document()->body());
174
175 m_currentRootEditable = nullptr;
176 m_currentFullLength = kInvalidLength;
177 m_currentChunkIndex = kInvalidChunkIndex;
178 m_currentChunkStart = Position();
179 }
180
134 } // namespace blink 181 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/spellcheck/ColdModeSpellCheckRequester.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698