Chromium Code Reviews| 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) { |