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) { |