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..b71341cd3d08fdada7204f88b66c9a0f0f50f3b0 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) {} |
@@ -132,8 +148,104 @@ void IdleSpellCheckCallback::coldModeTimerFired(TimerBase*) { |
m_state = State::kColdModeRequested; |
} |
+EphemeralRange IdleSpellCheckCallback::calcHotModeCheckingRange( |
+ const Element& editable, |
+ const Position& refPosition) const { |
+ const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable); |
+ 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...
|
+ fullRange.endPosition()); |
+ if (fullLength <= kHotModeChunkSize) |
+ return fullRange; |
+ |
+ TextIteratorBehavior behavior = TextIteratorBehavior::Builder() |
+ .setEmitsObjectReplacementCharacter(true) |
+ .build(); |
+ 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(); |
+ 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; |
+ |
+ CompositeEditCommand* typingCommand = |
+ frame().editor().lastTypingCommandIfStillOpenForTyping(); |
+ 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( |
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.
|
+ 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); |
+ |
+ uint64_t maxSequenceNumber = m_lastProcessedUndoStepSequence; |
+ for (const UndoStep* step : frame().editor().undoStack().undoSteps()) { |
+ if (step->sequenceNumber() <= m_lastProcessedUndoStepSequence) |
+ break; |
+ 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.
|
+ if (deadline->timeRemaining() < 0) |
+ break; |
+ hotModeCheckRootEditable(step->endingRootEditableElement(), |
+ step->endingSelection().extent(), |
+ &processedRootEditables); |
+ } |
+ |
+ m_lastProcessedUndoStepSequence = maxSequenceNumber; |
} |
void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) { |