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

Unified 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, 10 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp b/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp
index b9fa99b65a4ccbe7ba81799e9421a167ab920e64..3e1298697d30f33541a3374626a13171ef6537c0 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp
@@ -7,10 +7,13 @@
#include "core/dom/IdleRequestOptions.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/editing/EditingUtilities.h"
+#include "core/editing/Editor.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/VisibleSelection.h"
#include "core/editing/VisibleUnits.h"
#include "core/editing/commands/CompositeEditCommand.h"
+#include "core/editing/commands/TypingCommand.h"
+#include "core/editing/commands/UndoStack.h"
#include "core/editing/commands/UndoStep.h"
#include "core/editing/iterators/BackwardsCharacterIterator.h"
#include "core/editing/iterators/CharacterIterator.h"
@@ -28,6 +31,7 @@ namespace blink {
namespace {
+const int kHotModeChunkSize = 1024;
const int kColdModeTimerIntervalMS = 1000;
const int kConsecutiveColdModeTimerIntervalMS = 200;
const int kRequestTimeoutMS = 200;
@@ -35,6 +39,17 @@ const int kInvalidHandle = -1;
const int kDummyHandleForForcedInvocation = -2;
const double kForcedInvocationDeadlineSeconds = 10;
+bool isInOrAdjacentToWord(const Position& pos) {
+ const VisiblePosition& visiblePos = createVisiblePosition(pos);
+ const VisiblePosition& wordStart = previousWordPosition(visiblePos);
+ if (wordStart.isNull())
+ return false;
+ const VisiblePosition& wordEnd = endOfWord(wordStart);
+ if (wordEnd.isNull())
+ return false;
+ return comparePositions(visiblePos, wordEnd) <= 0;
+}
+
} // namespace
IdleSpellCheckCallback::~IdleSpellCheckCallback() {}
@@ -54,6 +69,7 @@ IdleSpellCheckCallback::IdleSpellCheckCallback(LocalFrame& frame)
m_idleCallbackHandle(kInvalidHandle),
m_needsMoreColdModeInvocationForTesting(false),
m_frame(frame),
+ m_lastProcessedUndoStepSequence(0),
m_coldModeTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame),
this,
&IdleSpellCheckCallback::coldModeTimerFired) {}
@@ -62,6 +78,10 @@ SpellCheckRequester& IdleSpellCheckCallback::spellCheckRequester() const {
return frame().spellChecker().spellCheckRequester();
}
+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...
+ return frame().editor().undoStack();
+}
+
bool IdleSpellCheckCallback::isSpellCheckingEnabled() const {
return frame().spellChecker().isSpellCheckingEnabled();
}
@@ -132,8 +152,111 @@ void IdleSpellCheckCallback::coldModeTimerFired(TimerBase*) {
m_state = State::kColdModeRequested;
}
+EphemeralRange IdleSpellCheckCallback::calcHotModeCheckingRange(
+ const Element& editable,
+ const Position& refPosition) const {
+ const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable);
+ 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.
+ fullRange.endPosition());
+ if (fullLength <= kHotModeChunkSize)
+ return fullRange;
+
+ TextIteratorBehavior behavior = TextIteratorBehavior::Builder()
+ .setEmitsObjectReplacementCharacter(true)
+ .build();
+
yosin_UTC9 2017/03/02 03:38:40 nit: Get rid of an extra blank line.
+ BackwardsCharacterIterator backwardIterator(fullRange.startPosition(),
+ refPosition, behavior);
+ backwardIterator.advance(kHotModeChunkSize / 2);
+ const Position& chunkStart = backwardIterator.endPosition();
+
+ CharacterIterator forwardIterator(refPosition, fullRange.endPosition(),
+ behavior);
+ forwardIterator.advance(kHotModeChunkSize / 2);
+ const Position& chunkEnd = forwardIterator.endPosition();
+
yosin_UTC9 2017/03/02 03:38:40 nit: Get rid of an extra blank line.
Xiaocheng 2017/03/02 04:34:59 Done.
+ return expandRangeToSentenceBoundary(EphemeralRange(chunkStart, chunkEnd));
+}
+
+bool IdleSpellCheckCallback::isTypingInPartialWord(
+ const Element& editable) const {
+ const SelectionInDOMTree& selection =
+ frame().selection().selectionInDOMTree();
+ if (!selection.isCaret())
+ return false;
+ if (rootEditableElementOf(selection.base()) != &editable)
+ return false;
+
+ TypingCommand* typingCommand =
+ TypingCommand::lastTypingCommandIfStillOpenForTyping(&frame());
+ if (!typingCommand)
+ return false;
+ if (typingCommand->endingSelection().asSelection() != selection)
+ return false;
+ return isInOrAdjacentToWord(selection.base());
+}
+
+bool IdleSpellCheckCallback::shouldCheckRootEditableInHotMode(
+ const Element& editable,
+ const Position& refPosition) const {
+ if (!editable.isSpellCheckingEnabled() &&
+ !SpellChecker::isSpellCheckingEnabledAt(refPosition))
+ return false;
+ if (editable.visibleBoundsInVisualViewport().isEmpty())
+ return false;
+ // TODO(xiaochengh): Design more aggressive strategies to reduce checking when
+ // we are just moving selection around without modifying anything.
+ return !isTypingInPartialWord(editable);
+}
+
+void IdleSpellCheckCallback::hotModeCheckRootEditable(
+ Element* rootEditable,
+ const Position& refPosition,
+ HeapVector<Member<Element>>* processedRootEditables) {
+ if (!rootEditable || !rootEditable->isConnected())
+ return;
+
+ if (processedRootEditables->contains(rootEditable))
+ return;
+ processedRootEditables->push_back(rootEditable);
+
+ if (!shouldCheckRootEditableInHotMode(*rootEditable, refPosition))
+ return;
+
+ SpellCheckRequest* request = SpellCheckRequest::create(
+ calcHotModeCheckingRange(*rootEditable, refPosition));
+ spellCheckRequester().requestCheckingFor(request);
+}
+
void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) {
- // TODO(xiaochengh): Implementation.
+ TRACE_EVENT0("blink", "IdleSpellCheckCallback::hotModeInvocation");
+
+ // TODO(xiaochengh): Figure out if this has any performance impact.
+ frame().document()->updateStyleAndLayout();
+
+ HeapVector<Member<Element>> processedRootEditables;
+
+ const Position& extent = frame().selection().selectionInDOMTree().extent();
+ hotModeCheckRootEditable(rootEditableElementOf(extent), extent,
+ &processedRootEditables);
+
+ const UndoStepStack& undoSteps = undoStack().undoSteps();
+ if (undoSteps.isEmpty())
+ return;
+
+ 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
+ if (deadline->timeRemaining() < 0)
+ break;
+ const UndoStep& step = **iter;
+ if (step.sequenceNumber() <= m_lastProcessedUndoStepSequence)
+ break;
+ hotModeCheckRootEditable(step.endingRootEditableElement(),
+ step.endingSelection().extent(),
+ &processedRootEditables);
+ }
+
+ m_lastProcessedUndoStepSequence = std::max(
+ m_lastProcessedUndoStepSequence, undoSteps.back()->sequenceNumber());
}
void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) {

Powered by Google App Engine
This is Rietveld 408576698