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

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

Issue 2720193002: Implement hot mode invocation for idle time spell checker (Closed)
Patch Set: Wed Mar 1 16:02:57 PST 2017 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/IdleSpellCheckCallback.h" 5 #include "core/editing/spellcheck/IdleSpellCheckCallback.h"
6 6
7 #include "core/dom/IdleRequestOptions.h" 7 #include "core/dom/IdleRequestOptions.h"
8 #include "core/dom/TaskRunnerHelper.h" 8 #include "core/dom/TaskRunnerHelper.h"
9 #include "core/editing/EditingUtilities.h" 9 #include "core/editing/EditingUtilities.h"
10 #include "core/editing/Editor.h"
10 #include "core/editing/FrameSelection.h" 11 #include "core/editing/FrameSelection.h"
11 #include "core/editing/VisibleSelection.h" 12 #include "core/editing/VisibleSelection.h"
12 #include "core/editing/VisibleUnits.h" 13 #include "core/editing/VisibleUnits.h"
13 #include "core/editing/commands/CompositeEditCommand.h" 14 #include "core/editing/commands/CompositeEditCommand.h"
15 #include "core/editing/commands/TypingCommand.h"
16 #include "core/editing/commands/UndoStack.h"
14 #include "core/editing/commands/UndoStep.h" 17 #include "core/editing/commands/UndoStep.h"
15 #include "core/editing/iterators/BackwardsCharacterIterator.h" 18 #include "core/editing/iterators/BackwardsCharacterIterator.h"
16 #include "core/editing/iterators/CharacterIterator.h" 19 #include "core/editing/iterators/CharacterIterator.h"
17 #include "core/editing/spellcheck/SpellCheckRequester.h" 20 #include "core/editing/spellcheck/SpellCheckRequester.h"
18 #include "core/editing/spellcheck/SpellChecker.h" 21 #include "core/editing/spellcheck/SpellChecker.h"
19 #include "core/frame/FrameView.h" 22 #include "core/frame/FrameView.h"
20 #include "core/frame/LocalFrame.h" 23 #include "core/frame/LocalFrame.h"
21 #include "core/html/TextControlElement.h" 24 #include "core/html/TextControlElement.h"
22 #include "core/layout/LayoutObject.h" 25 #include "core/layout/LayoutObject.h"
23 #include "platform/RuntimeEnabledFeatures.h" 26 #include "platform/RuntimeEnabledFeatures.h"
24 #include "platform/instrumentation/tracing/TraceEvent.h" 27 #include "platform/instrumentation/tracing/TraceEvent.h"
25 #include "wtf/CurrentTime.h" 28 #include "wtf/CurrentTime.h"
26 29
27 namespace blink { 30 namespace blink {
28 31
29 namespace { 32 namespace {
30 33
34 const int kHotModeChunkSize = 1024;
31 const int kColdModeTimerIntervalMS = 1000; 35 const int kColdModeTimerIntervalMS = 1000;
32 const int kConsecutiveColdModeTimerIntervalMS = 200; 36 const int kConsecutiveColdModeTimerIntervalMS = 200;
33 const int kRequestTimeoutMS = 200; 37 const int kRequestTimeoutMS = 200;
34 const int kInvalidHandle = -1; 38 const int kInvalidHandle = -1;
35 const int kDummyHandleForForcedInvocation = -2; 39 const int kDummyHandleForForcedInvocation = -2;
36 const double kForcedInvocationDeadlineSeconds = 10; 40 const double kForcedInvocationDeadlineSeconds = 10;
37 41
42 bool isInOrAdjacentToWord(const Position& pos) {
43 const VisiblePosition& visiblePos = createVisiblePosition(pos);
44 const VisiblePosition& wordStart = previousWordPosition(visiblePos);
45 if (wordStart.isNull())
46 return false;
47 const VisiblePosition& wordEnd = endOfWord(wordStart);
48 if (wordEnd.isNull())
49 return false;
50 return comparePositions(visiblePos, wordEnd) <= 0;
51 }
52
38 } // namespace 53 } // namespace
39 54
40 IdleSpellCheckCallback::~IdleSpellCheckCallback() {} 55 IdleSpellCheckCallback::~IdleSpellCheckCallback() {}
41 56
42 DEFINE_TRACE(IdleSpellCheckCallback) { 57 DEFINE_TRACE(IdleSpellCheckCallback) {
43 visitor->trace(m_frame); 58 visitor->trace(m_frame);
44 IdleRequestCallback::trace(visitor); 59 IdleRequestCallback::trace(visitor);
45 SynchronousMutationObserver::trace(visitor); 60 SynchronousMutationObserver::trace(visitor);
46 } 61 }
47 62
48 IdleSpellCheckCallback* IdleSpellCheckCallback::create(LocalFrame& frame) { 63 IdleSpellCheckCallback* IdleSpellCheckCallback::create(LocalFrame& frame) {
49 return new IdleSpellCheckCallback(frame); 64 return new IdleSpellCheckCallback(frame);
50 } 65 }
51 66
52 IdleSpellCheckCallback::IdleSpellCheckCallback(LocalFrame& frame) 67 IdleSpellCheckCallback::IdleSpellCheckCallback(LocalFrame& frame)
53 : m_state(State::kInactive), 68 : m_state(State::kInactive),
54 m_idleCallbackHandle(kInvalidHandle), 69 m_idleCallbackHandle(kInvalidHandle),
55 m_needsMoreColdModeInvocationForTesting(false), 70 m_needsMoreColdModeInvocationForTesting(false),
56 m_frame(frame), 71 m_frame(frame),
72 m_lastProcessedUndoStepSequence(0),
57 m_coldModeTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame), 73 m_coldModeTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame),
58 this, 74 this,
59 &IdleSpellCheckCallback::coldModeTimerFired) {} 75 &IdleSpellCheckCallback::coldModeTimerFired) {}
60 76
61 SpellCheckRequester& IdleSpellCheckCallback::spellCheckRequester() const { 77 SpellCheckRequester& IdleSpellCheckCallback::spellCheckRequester() const {
62 return frame().spellChecker().spellCheckRequester(); 78 return frame().spellChecker().spellCheckRequester();
63 } 79 }
64 80
81 UndoStack& IdleSpellCheckCallback::undoStack() const {
yosin_UTC9 2017/03/02 03:38:41 This should be |const UndoStack&|
Xiaocheng 2017/03/02 04:34:59 This one can be changed to const. Interesting...
82 return frame().editor().undoStack();
83 }
84
65 bool IdleSpellCheckCallback::isSpellCheckingEnabled() const { 85 bool IdleSpellCheckCallback::isSpellCheckingEnabled() const {
66 return frame().spellChecker().isSpellCheckingEnabled(); 86 return frame().spellChecker().isSpellCheckingEnabled();
67 } 87 }
68 88
69 void IdleSpellCheckCallback::requestInvocation() { 89 void IdleSpellCheckCallback::requestInvocation() {
70 DCHECK_EQ(m_idleCallbackHandle, kInvalidHandle); 90 DCHECK_EQ(m_idleCallbackHandle, kInvalidHandle);
71 91
72 IdleRequestOptions options; 92 IdleRequestOptions options;
73 options.setTimeout(kRequestTimeoutMS); 93 options.setTimeout(kRequestTimeoutMS);
74 m_idleCallbackHandle = frame().document()->requestIdleCallback(this, options); 94 m_idleCallbackHandle = frame().document()->requestIdleCallback(this, options);
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 145
126 if (!isSpellCheckingEnabled()) { 146 if (!isSpellCheckingEnabled()) {
127 deactivate(); 147 deactivate();
128 return; 148 return;
129 } 149 }
130 150
131 requestInvocation(); 151 requestInvocation();
132 m_state = State::kColdModeRequested; 152 m_state = State::kColdModeRequested;
133 } 153 }
134 154
155 EphemeralRange IdleSpellCheckCallback::calcHotModeCheckingRange(
156 const Element& editable,
157 const Position& refPosition) const {
158 const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable);
159 int fullLength = TextIterator::rangeLength(fullRange.startPosition(),
yosin_UTC9 2017/03/02 03:38:41 nit: s/int/const int/
Xiaocheng 2017/03/02 04:34:59 Done.
160 fullRange.endPosition());
161 if (fullLength <= kHotModeChunkSize)
162 return fullRange;
163
164 TextIteratorBehavior behavior = TextIteratorBehavior::Builder()
165 .setEmitsObjectReplacementCharacter(true)
166 .build();
167
yosin_UTC9 2017/03/02 03:38:40 nit: Get rid of an extra blank line.
168 BackwardsCharacterIterator backwardIterator(fullRange.startPosition(),
169 refPosition, behavior);
170 backwardIterator.advance(kHotModeChunkSize / 2);
171 const Position& chunkStart = backwardIterator.endPosition();
172
173 CharacterIterator forwardIterator(refPosition, fullRange.endPosition(),
174 behavior);
175 forwardIterator.advance(kHotModeChunkSize / 2);
176 const Position& chunkEnd = forwardIterator.endPosition();
177
yosin_UTC9 2017/03/02 03:38:40 nit: Get rid of an extra blank line.
Xiaocheng 2017/03/02 04:34:59 Done.
178 return expandRangeToSentenceBoundary(EphemeralRange(chunkStart, chunkEnd));
179 }
180
181 bool IdleSpellCheckCallback::isTypingInPartialWord(
182 const Element& editable) const {
183 const SelectionInDOMTree& selection =
184 frame().selection().selectionInDOMTree();
185 if (!selection.isCaret())
186 return false;
187 if (rootEditableElementOf(selection.base()) != &editable)
188 return false;
189
190 TypingCommand* typingCommand =
191 TypingCommand::lastTypingCommandIfStillOpenForTyping(&frame());
192 if (!typingCommand)
193 return false;
194 if (typingCommand->endingSelection().asSelection() != selection)
195 return false;
196 return isInOrAdjacentToWord(selection.base());
197 }
198
199 bool IdleSpellCheckCallback::shouldCheckRootEditableInHotMode(
200 const Element& editable,
201 const Position& refPosition) const {
202 if (!editable.isSpellCheckingEnabled() &&
203 !SpellChecker::isSpellCheckingEnabledAt(refPosition))
204 return false;
205 if (editable.visibleBoundsInVisualViewport().isEmpty())
206 return false;
207 // TODO(xiaochengh): Design more aggressive strategies to reduce checking when
208 // we are just moving selection around without modifying anything.
209 return !isTypingInPartialWord(editable);
210 }
211
212 void IdleSpellCheckCallback::hotModeCheckRootEditable(
213 Element* rootEditable,
214 const Position& refPosition,
215 HeapVector<Member<Element>>* processedRootEditables) {
216 if (!rootEditable || !rootEditable->isConnected())
217 return;
218
219 if (processedRootEditables->contains(rootEditable))
220 return;
221 processedRootEditables->push_back(rootEditable);
222
223 if (!shouldCheckRootEditableInHotMode(*rootEditable, refPosition))
224 return;
225
226 SpellCheckRequest* request = SpellCheckRequest::create(
227 calcHotModeCheckingRange(*rootEditable, refPosition));
228 spellCheckRequester().requestCheckingFor(request);
229 }
230
135 void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) { 231 void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) {
136 // TODO(xiaochengh): Implementation. 232 TRACE_EVENT0("blink", "IdleSpellCheckCallback::hotModeInvocation");
233
234 // TODO(xiaochengh): Figure out if this has any performance impact.
235 frame().document()->updateStyleAndLayout();
236
237 HeapVector<Member<Element>> processedRootEditables;
238
239 const Position& extent = frame().selection().selectionInDOMTree().extent();
240 hotModeCheckRootEditable(rootEditableElementOf(extent), extent,
241 &processedRootEditables);
242
243 const UndoStepStack& undoSteps = undoStack().undoSteps();
244 if (undoSteps.isEmpty())
245 return;
246
247 for (auto iter = undoSteps.rbegin(); iter != undoSteps.rend(); ++iter) {
yosin_UTC9 2017/03/02 03:38:41 Could you we import |base::Reversed()|[1] into Bli
Xiaocheng 2017/03/02 04:34:59 Never done such thing before... Can I follow the
yosin_UTC9 2017/03/02 04:41:48 Yes. It also helps others not only us. BTW, we do
Xiaocheng 2017/03/02 04:57:52 Thanks. I'll do this for now and add a TODO to use
248 if (deadline->timeRemaining() < 0)
249 break;
250 const UndoStep& step = **iter;
251 if (step.sequenceNumber() <= m_lastProcessedUndoStepSequence)
252 break;
253 hotModeCheckRootEditable(step.endingRootEditableElement(),
254 step.endingSelection().extent(),
255 &processedRootEditables);
256 }
257
258 m_lastProcessedUndoStepSequence = std::max(
259 m_lastProcessedUndoStepSequence, undoSteps.back()->sequenceNumber());
137 } 260 }
138 261
139 void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) { 262 void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) {
140 // TODO(xiaochengh): Implementation. 263 // TODO(xiaochengh): Implementation.
141 } 264 }
142 265
143 bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const { 266 bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const {
144 if (m_needsMoreColdModeInvocationForTesting) { 267 if (m_needsMoreColdModeInvocationForTesting) {
145 m_needsMoreColdModeInvocationForTesting = false; 268 m_needsMoreColdModeInvocationForTesting = false;
146 return false; 269 return false;
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 } 336 }
214 } 337 }
215 338
216 void IdleSpellCheckCallback::skipColdModeTimerForTesting() { 339 void IdleSpellCheckCallback::skipColdModeTimerForTesting() {
217 DCHECK(m_coldModeTimer.isActive()); 340 DCHECK(m_coldModeTimer.isActive());
218 m_coldModeTimer.stop(); 341 m_coldModeTimer.stop();
219 coldModeTimerFired(&m_coldModeTimer); 342 coldModeTimerFired(&m_coldModeTimer);
220 } 343 }
221 344
222 } // namespace blink 345 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698