| 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 6a212b735493137b871943b3c13a82d2c876ee5f..dc8def1b5e6fb6ce551a337b98ed37b4ad57290d 100644
|
| --- a/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp
|
| +++ b/third_party/WebKit/Source/core/editing/spellcheck/IdleSpellCheckCallback.cpp
|
| @@ -13,6 +13,7 @@
|
| #include "core/editing/VisibleUnits.h"
|
| #include "core/editing/commands/UndoStack.h"
|
| #include "core/editing/commands/UndoStep.h"
|
| +#include "core/editing/iterators/CharacterIterator.h"
|
| #include "core/editing/spellcheck/HotModeSpellCheckRequester.h"
|
| #include "core/editing/spellcheck/SpellCheckRequester.h"
|
| #include "core/editing/spellcheck/SpellChecker.h"
|
| @@ -25,6 +26,7 @@ namespace blink {
|
|
|
| namespace {
|
|
|
| +const int kColdModeChunkSize = 16384;
|
| const int kColdModeTimerIntervalMS = 1000;
|
| const int kConsecutiveColdModeTimerIntervalMS = 200;
|
| const int kHotModeRequestTimeoutMS = 200;
|
| @@ -32,12 +34,22 @@ const int kInvalidHandle = -1;
|
| const int kDummyHandleForForcedInvocation = -2;
|
| const double kForcedInvocationDeadlineSeconds = 10;
|
|
|
| +bool shouldCheckNodeInColdMode(Node& node) {
|
| + if (!node.isElementNode())
|
| + return false;
|
| + const Position& position = Position::firstPositionInNode(&node);
|
| + if (!isEditablePosition(position))
|
| + return false;
|
| + return SpellChecker::isSpellCheckingEnabledAt(position);
|
| +}
|
| +
|
| } // namespace
|
|
|
| IdleSpellCheckCallback::~IdleSpellCheckCallback() {}
|
|
|
| DEFINE_TRACE(IdleSpellCheckCallback) {
|
| visitor->trace(m_frame);
|
| + visitor->trace(m_nextNodeInColdMode);
|
| IdleRequestCallback::trace(visitor);
|
| SynchronousMutationObserver::trace(visitor);
|
| }
|
| @@ -52,6 +64,7 @@ IdleSpellCheckCallback::IdleSpellCheckCallback(LocalFrame& frame)
|
| m_needsMoreColdModeInvocationForTesting(false),
|
| m_frame(frame),
|
| m_lastProcessedUndoStepSequence(0),
|
| + m_lastCheckedDOMTreeVersionInColdMode(0),
|
| m_coldModeTimer(TaskRunnerHelper::get(TaskType::UnspecedTimer, &frame),
|
| this,
|
| &IdleSpellCheckCallback::coldModeTimerFired) {}
|
| @@ -151,8 +164,76 @@ void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) {
|
| }
|
| }
|
|
|
| +// TODO(xiaochengh): Deduplicate with SpellChecker::chunkAndMarkAllMisspellings.
|
| +void IdleSpellCheckCallback::chunkAndRequestFullCheckingFor(
|
| + const Element& editable) {
|
| + const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable);
|
| + const int fullLength = TextIterator::rangeLength(fullRange.startPosition(),
|
| + fullRange.endPosition());
|
| +
|
| + // Check the full content if it is short.
|
| + if (fullLength <= kColdModeChunkSize) {
|
| + SpellCheckRequest* fullRequest = SpellCheckRequest::create(fullRange);
|
| + spellCheckRequester().requestCheckingFor(fullRequest);
|
| + return;
|
| + }
|
| +
|
| + // TODO(xiaochengh): Figure out if this is going to cause performance issues.
|
| + // In that case, we need finer-grained control over request generation.
|
| + Position chunkStart = fullRange.startPosition();
|
| + const int chunkLimit = fullLength / kColdModeChunkSize + 1;
|
| + for (int chunkIndex = 0; chunkIndex <= chunkLimit; ++chunkIndex) {
|
| + const Position& chunkEnd =
|
| + calculateCharacterSubrange(
|
| + EphemeralRange(chunkStart, fullRange.endPosition()), 0,
|
| + kColdModeChunkSize)
|
| + .endPosition();
|
| + if (chunkEnd <= chunkStart)
|
| + break;
|
| + const EphemeralRange chunkRange(chunkStart, chunkEnd);
|
| + const EphemeralRange& checkRange =
|
| + chunkIndex >= 1 ? expandEndToSentenceBoundary(chunkRange)
|
| + : expandRangeToSentenceBoundary(chunkRange);
|
| +
|
| + SpellCheckRequest* chunkRequest =
|
| + SpellCheckRequest::create(checkRange, chunkIndex);
|
| + spellCheckRequester().requestCheckingFor(chunkRequest);
|
| +
|
| + chunkStart = checkRange.endPosition();
|
| + }
|
| +}
|
| +
|
| void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) {
|
| - // TODO(xiaochengh): Implementation.
|
| + TRACE_EVENT0("blink", "IdleSpellCheckCallback::coldModeInvocation");
|
| +
|
| + Node* body = frame().document()->body();
|
| + if (!body) {
|
| + m_nextNodeInColdMode = nullptr;
|
| + m_lastCheckedDOMTreeVersionInColdMode =
|
| + frame().document()->domTreeVersion();
|
| + return;
|
| + }
|
| +
|
| + // TODO(xiaochengh): Figure out if this has any performance impact.
|
| + frame().document()->updateStyleAndLayout();
|
| +
|
| + if (m_lastCheckedDOMTreeVersionInColdMode !=
|
| + frame().document()->domTreeVersion())
|
| + m_nextNodeInColdMode = body;
|
| +
|
| + while (m_nextNodeInColdMode && deadline->timeRemaining() > 0) {
|
| + if (!shouldCheckNodeInColdMode(*m_nextNodeInColdMode)) {
|
| + m_nextNodeInColdMode =
|
| + FlatTreeTraversal::next(*m_nextNodeInColdMode, body);
|
| + continue;
|
| + }
|
| +
|
| + chunkAndRequestFullCheckingFor(toElement(*m_nextNodeInColdMode));
|
| + m_nextNodeInColdMode =
|
| + FlatTreeTraversal::nextSkippingChildren(*m_nextNodeInColdMode, body);
|
| + }
|
| +
|
| + m_lastCheckedDOMTreeVersionInColdMode = frame().document()->domTreeVersion();
|
| }
|
|
|
| bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const {
|
| @@ -161,8 +242,7 @@ bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const {
|
| return false;
|
| }
|
|
|
| - // TODO(xiaochengh): Implementation.
|
| - return true;
|
| + return !m_nextNodeInColdMode;
|
| }
|
|
|
| void IdleSpellCheckCallback::handleEvent(IdleDeadline* deadline) {
|
|
|