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

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: Thu Mar 2 16:35:42 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
65 bool IdleSpellCheckCallback::isSpellCheckingEnabled() const { 81 bool IdleSpellCheckCallback::isSpellCheckingEnabled() const {
66 return frame().spellChecker().isSpellCheckingEnabled(); 82 return frame().spellChecker().isSpellCheckingEnabled();
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 141
126 if (!isSpellCheckingEnabled()) { 142 if (!isSpellCheckingEnabled()) {
127 deactivate(); 143 deactivate();
128 return; 144 return;
129 } 145 }
130 146
131 requestInvocation(); 147 requestInvocation();
132 m_state = State::kColdModeRequested; 148 m_state = State::kColdModeRequested;
133 } 149 }
134 150
151 EphemeralRange IdleSpellCheckCallback::calcHotModeCheckingRange(
152 const Element& editable,
153 const Position& refPosition) const {
154 const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable);
155 const int fullLength = TextIterator::rangeLength(fullRange.startPosition(),
yosin_UTC9 2017/03/03 05:34:23 I'm not sure why we don't have TextIterator::range
Xiaocheng 2017/03/03 06:28:28 Maybe we don't need it that much, either...
156 fullRange.endPosition());
157 if (fullLength <= kHotModeChunkSize)
158 return fullRange;
159
160 TextIteratorBehavior behavior = TextIteratorBehavior::Builder()
161 .setEmitsObjectReplacementCharacter(true)
162 .build();
163 BackwardsCharacterIterator backwardIterator(fullRange.startPosition(),
164 refPosition, behavior);
165 backwardIterator.advance(kHotModeChunkSize / 2);
166 const Position& chunkStart = backwardIterator.endPosition();
167 CharacterIterator forwardIterator(refPosition, fullRange.endPosition(),
168 behavior);
169 forwardIterator.advance(kHotModeChunkSize / 2);
170 const Position& chunkEnd = forwardIterator.endPosition();
171 return expandRangeToSentenceBoundary(EphemeralRange(chunkStart, chunkEnd));
172 }
173
174 bool IdleSpellCheckCallback::isTypingInPartialWord(
175 const Element& editable) const {
176 const SelectionInDOMTree& selection =
177 frame().selection().selectionInDOMTree();
178 if (!selection.isCaret())
179 return false;
180 if (rootEditableElementOf(selection.base()) != &editable)
181 return false;
182
183 CompositeEditCommand* typingCommand =
184 frame().editor().lastTypingCommandIfStillOpenForTyping();
185 if (!typingCommand)
186 return false;
187 if (typingCommand->endingSelection().asSelection() != selection)
188 return false;
189 return isInOrAdjacentToWord(selection.base());
190 }
191
192 bool IdleSpellCheckCallback::shouldCheckRootEditableInHotMode(
193 const Element& editable,
194 const Position& refPosition) const {
195 if (!editable.isSpellCheckingEnabled() &&
196 !SpellChecker::isSpellCheckingEnabledAt(refPosition))
197 return false;
198 if (editable.visibleBoundsInVisualViewport().isEmpty())
199 return false;
200 // TODO(xiaochengh): Design more aggressive strategies to reduce checking when
201 // we are just moving selection around without modifying anything.
202 return !isTypingInPartialWord(editable);
203 }
204
205 void IdleSpellCheckCallback::hotModeCheckRootEditable(
yosin_UTC9 2017/03/03 05:34:23 Could you rename this function to start with verb?
Xiaocheng 2017/03/03 06:28:27 Changed to checkRootEditableInHotMode.
206 Element* rootEditable,
207 const Position& refPosition,
208 HeapVector<Member<Element>>* processedRootEditables) {
209 if (!rootEditable || !rootEditable->isConnected())
210 return;
211
212 if (processedRootEditables->contains(rootEditable))
213 return;
214 processedRootEditables->push_back(rootEditable);
215
216 if (!shouldCheckRootEditableInHotMode(*rootEditable, refPosition))
217 return;
218
219 SpellCheckRequest* request = SpellCheckRequest::create(
220 calcHotModeCheckingRange(*rootEditable, refPosition));
221 spellCheckRequester().requestCheckingFor(request);
222 }
223
135 void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) { 224 void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) {
136 // TODO(xiaochengh): Implementation. 225 TRACE_EVENT0("blink", "IdleSpellCheckCallback::hotModeInvocation");
226
227 // TODO(xiaochengh): Figure out if this has any performance impact.
228 frame().document()->updateStyleAndLayout();
229
230 HeapVector<Member<Element>> processedRootEditables;
231
232 const Position& extent = frame().selection().selectionInDOMTree().extent();
233 hotModeCheckRootEditable(rootEditableElementOf(extent), extent,
234 &processedRootEditables);
235
236 uint64_t maxSequenceNumber = m_lastProcessedUndoStepSequence;
237 for (const UndoStep* step : frame().editor().undoStack().undoSteps()) {
238 if (step->sequenceNumber() <= m_lastProcessedUndoStepSequence)
239 break;
240 maxSequenceNumber = std::max(maxSequenceNumber, step->sequenceNumber());
yosin_UTC9 2017/03/03 05:34:23 It is better to update |m_lastProcessUndoStepSeque
Xiaocheng 2017/03/03 06:28:27 Done.
241 if (deadline->timeRemaining() < 0)
242 break;
243 hotModeCheckRootEditable(step->endingRootEditableElement(),
244 step->endingSelection().extent(),
245 &processedRootEditables);
246 }
247
248 m_lastProcessedUndoStepSequence = maxSequenceNumber;
137 } 249 }
138 250
139 void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) { 251 void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) {
140 // TODO(xiaochengh): Implementation. 252 // TODO(xiaochengh): Implementation.
141 } 253 }
142 254
143 bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const { 255 bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const {
144 if (m_needsMoreColdModeInvocationForTesting) { 256 if (m_needsMoreColdModeInvocationForTesting) {
145 m_needsMoreColdModeInvocationForTesting = false; 257 m_needsMoreColdModeInvocationForTesting = false;
146 return false; 258 return false;
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 } 325 }
214 } 326 }
215 327
216 void IdleSpellCheckCallback::skipColdModeTimerForTesting() { 328 void IdleSpellCheckCallback::skipColdModeTimerForTesting() {
217 DCHECK(m_coldModeTimer.isActive()); 329 DCHECK(m_coldModeTimer.isActive());
218 m_coldModeTimer.stop(); 330 m_coldModeTimer.stop();
219 coldModeTimerFired(&m_coldModeTimer); 331 coldModeTimerFired(&m_coldModeTimer);
220 } 332 }
221 333
222 } // namespace blink 334 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698