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 2c400a479fa4ad213609139d7e6f4af24c4694fb..a4ed8e2ffe51dd20e3f27f9053ccb00171bd6dc5 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 kRequestTimeoutMS = 200; |
@@ -38,6 +40,7 @@ IdleSpellCheckCallback::~IdleSpellCheckCallback() {} |
DEFINE_TRACE(IdleSpellCheckCallback) { |
visitor->trace(m_frame); |
+ visitor->trace(m_nextNodeInColdMode); |
IdleRequestCallback::trace(visitor); |
SynchronousMutationObserver::trace(visitor); |
} |
@@ -52,6 +55,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) {} |
@@ -152,8 +156,71 @@ void IdleSpellCheckCallback::hotModeInvocation(IdleDeadline* deadline) { |
} |
} |
+// TODO(xiaochengh): Deduplicate with SpellChecker::chunkAndMarkAllMisspellings. |
+void IdleSpellCheckCallback::chunkAndRequestFullCheckingFor( |
+ const Element& editable) { |
+ const EphemeralRange& fullRange = EphemeralRange::rangeOfContents(editable); |
+ int fullLength = TextIterator::rangeLength(fullRange.startPosition(), |
yosin_UTC9
2017/03/07 01:47:03
nit: s/int/const int/
Xiaocheng
2017/03/07 04:20:30
Done.
|
+ 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(); |
+ int chunkLimit = fullLength / kColdModeChunkSize + 1; |
yosin_UTC9
2017/03/07 01:47:03
nit: s/int/const int/
Xiaocheng
2017/03/07 04:20:30
Done.
|
+ for (int requestNum = 0; requestNum <= chunkLimit; ++requestNum) { |
yosin_UTC9
2017/03/07 01:47:03
nit: s/requestNum/chunkNumber/
or
nit: s/requestNu
Xiaocheng
2017/03/07 04:20:30
Done.
|
+ Position chunkEnd = calculateCharacterSubrange( |
yosin_UTC9
2017/03/07 01:47:03
nit: s/Position/const Position/
Xiaocheng
2017/03/07 04:20:29
Done.
|
+ EphemeralRange(chunkStart, fullRange.endPosition()), |
+ 0, kColdModeChunkSize) |
+ .endPosition(); |
+ if (chunkEnd <= chunkStart) |
+ break; |
+ EphemeralRange chunkRange(chunkStart, chunkEnd); |
yosin_UTC9
2017/03/07 01:47:03
nit: s/EphemeralRange/const EphmeralRange/
Xiaocheng
2017/03/07 04:20:30
Done.
|
+ EphemeralRange checkRange = requestNum |
yosin_UTC9
2017/03/07 01:47:03
nit: s/EphmeralRange/const EphemeralRange&/ to avo
Xiaocheng
2017/03/07 04:20:30
Done.
|
+ ? expandEndToSentenceBoundary(chunkRange) |
+ : expandRangeToSentenceBoundary(chunkRange); |
+ |
+ SpellCheckRequest* chunkRequest = |
+ SpellCheckRequest::create(checkRange, requestNum); |
+ spellCheckRequester().requestCheckingFor(chunkRequest); |
+ |
+ chunkStart = checkRange.endPosition(); |
+ } |
+} |
+ |
void IdleSpellCheckCallback::coldModeInvocation(IdleDeadline* deadline) { |
- // TODO(xiaochengh): Implementation. |
+ TRACE_EVENT0("blink", "IdleSpellCheckCallback::coldModeInvocation"); |
+ |
+ // TODO(xiaochengh): Figure out if this has any performance impact. |
+ frame().document()->updateStyleAndLayout(); |
+ |
+ Node* body = frame().document()->body(); |
yosin_UTC9
2017/03/07 01:47:03
|body| can be null, e.g. no BODY element.
We may w
Xiaocheng
2017/03/07 04:20:30
Done.
|
+ if (m_lastCheckedDOMTreeVersionInColdMode != |
+ frame().document()->domTreeVersion()) |
+ m_nextNodeInColdMode = body; |
+ |
+ while (m_nextNodeInColdMode && deadline->timeRemaining() > 0) { |
+ const Position& refPosition = |
+ Position::firstPositionInNode(m_nextNodeInColdMode); |
+ if (m_nextNodeInColdMode->isElementNode() && |
+ isEditablePosition(refPosition) && |
+ SpellChecker::isSpellCheckingEnabledAt(refPosition)) { |
+ chunkAndRequestFullCheckingFor(toElement(*m_nextNodeInColdMode)); |
+ m_nextNodeInColdMode = |
+ FlatTreeTraversal::nextSkippingChildren(*m_nextNodeInColdMode, body); |
+ } else { |
yosin_UTC9
2017/03/07 01:47:03
Prefer early-continue style to shallow indentation
Xiaocheng
2017/03/07 04:20:29
Done by introducing |shouldCheckNodeInColdMode()|
|
+ m_nextNodeInColdMode = |
+ FlatTreeTraversal::next(*m_nextNodeInColdMode, body); |
+ } |
+ } |
+ |
+ m_lastCheckedDOMTreeVersionInColdMode = frame().document()->domTreeVersion(); |
} |
bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const { |
@@ -162,8 +229,7 @@ bool IdleSpellCheckCallback::coldModeFinishesFullDocument() const { |
return false; |
} |
- // TODO(xiaochengh): Implementation. |
- return true; |
+ return !m_nextNodeInColdMode; |
} |
void IdleSpellCheckCallback::handleEvent(IdleDeadline* deadline) { |