зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1354641 - adjust the value of spell check heuristics. r=masayuki
This patch is mainly for adjusting the value of INLINESPELL_CHECK_TIMEOUT from 50ms to 1ms. The value means how long the main thread is blocked for spelling check, and 50ms is too long. It causes significant delays when a rich content document is loading, and the user tries to type immediately before spell checking is done. With the INLINESPELL_CHECK_TIMEOUT setting to 1ms, it's possible to be too short to less powerful machines. Therefore we add INLINESPELL_MINIMUM_WORDS_BEFORE_TIMEOUT to ensure at least N words were checked. MozReview-Commit-ID: 1KYeFpggZGn --HG-- extra : rebase_source : d7733dd37a923e6d6afb7f88174f352d28d4a3c4
This commit is contained in:
Родитель
47f3443d87
Коммит
3f3888d09f
|
@ -78,20 +78,13 @@ using namespace mozilla::dom;
|
|||
//#define DEBUG_INLINESPELL
|
||||
|
||||
// the number of milliseconds that we will take at once to do spellchecking
|
||||
#define INLINESPELL_CHECK_TIMEOUT 50
|
||||
#define INLINESPELL_CHECK_TIMEOUT 1
|
||||
|
||||
// The number of words to check before we look at the time to see if
|
||||
// INLINESPELL_CHECK_TIMEOUT ms have elapsed. This prevents us from spending
|
||||
// too much time checking the clock. Note that misspelled words count for
|
||||
// more than one word in this calculation.
|
||||
#define INLINESPELL_TIMEOUT_CHECK_FREQUENCY 50
|
||||
|
||||
// This number is the number of checked words a misspelled word counts for
|
||||
// when we're checking the time to see if the alloted time is up for
|
||||
// spellchecking. Misspelled words take longer to process since we have to
|
||||
// create a range, so they count more. The exact number isn't very important
|
||||
// since this just controls how often we check the current time.
|
||||
#define MISSPELLED_WORD_COUNT_PENALTY 4
|
||||
// INLINESPELL_CHECK_TIMEOUT ms have elapsed. This prevents us from getting
|
||||
// stuck and not moving forward because the INLINESPELL_CHECK_TIMEOUT might
|
||||
// be too short to a low-end machine.
|
||||
#define INLINESPELL_MINIMUM_WORDS_BEFORE_TIMEOUT 5
|
||||
|
||||
// These notifications are broadcast when spell check starts and ends. STARTED
|
||||
// must always be followed by ENDED.
|
||||
|
@ -101,7 +94,10 @@ using namespace mozilla::dom;
|
|||
static bool ContentIsDescendantOf(nsINode* aPossibleDescendant,
|
||||
nsINode* aPossibleAncestor);
|
||||
|
||||
static const char kMaxSpellCheckSelectionSize[] = "extensions.spellcheck.inline.max-misspellings";
|
||||
static const char kMaxSpellCheckSelectionSize[] =
|
||||
"extensions.spellcheck.inline.max-misspellings";
|
||||
static const PRTime kMaxSpellCheckTimeInUsec =
|
||||
INLINESPELL_CHECK_TIMEOUT * PR_USEC_PER_MSEC;
|
||||
|
||||
mozInlineSpellStatus::mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker)
|
||||
: mSpellChecker(aSpellChecker), mWordCount(0)
|
||||
|
@ -475,9 +471,12 @@ mozInlineSpellStatus::PositionToCollapsedRange(nsIDOMDocument* aDocument,
|
|||
class mozInlineSpellResume : public Runnable
|
||||
{
|
||||
public:
|
||||
mozInlineSpellResume(const mozInlineSpellStatus& aStatus,
|
||||
mozInlineSpellResume(UniquePtr<mozInlineSpellStatus>&& aStatus,
|
||||
uint32_t aDisabledAsyncToken)
|
||||
: mDisabledAsyncToken(aDisabledAsyncToken), mStatus(aStatus) {}
|
||||
: mDisabledAsyncToken(aDisabledAsyncToken)
|
||||
, mStatus(Move(aStatus))
|
||||
{
|
||||
}
|
||||
|
||||
nsresult Post()
|
||||
{
|
||||
|
@ -488,15 +487,15 @@ public:
|
|||
{
|
||||
// Discard the resumption if the spell checker was disabled after the
|
||||
// resumption was scheduled.
|
||||
if (mDisabledAsyncToken == mStatus.mSpellChecker->mDisabledAsyncToken) {
|
||||
mStatus.mSpellChecker->ResumeCheck(&mStatus);
|
||||
if (mDisabledAsyncToken == mStatus->mSpellChecker->mDisabledAsyncToken) {
|
||||
mStatus->mSpellChecker->ResumeCheck(Move(mStatus));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mDisabledAsyncToken;
|
||||
mozInlineSpellStatus mStatus;
|
||||
UniquePtr<mozInlineSpellStatus> mStatus;
|
||||
};
|
||||
|
||||
// Used as the nsIEditorSpellCheck::InitSpellChecker callback.
|
||||
|
@ -687,7 +686,8 @@ mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking()
|
|||
|
||||
// mozInlineSpellChecker::RegisterEventListeners
|
||||
//
|
||||
// The inline spell checker listens to mouse events and keyboard navigation+ // events.
|
||||
// The inline spell checker listens to mouse events and keyboard navigation
|
||||
// events.
|
||||
|
||||
nsresult
|
||||
mozInlineSpellChecker::RegisterEventListeners()
|
||||
|
@ -888,14 +888,15 @@ mozInlineSpellChecker::SpellCheckAfterEditorChange(
|
|||
rv = aSelection->GetAnchorOffset(&anchorOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForEditorChange((EditAction)aAction,
|
||||
anchorNode, anchorOffset,
|
||||
aPreviousSelectedNode, aPreviousSelectedOffset,
|
||||
aStartNode, aStartOffset,
|
||||
aEndNode, aEndOffset);
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForEditorChange((EditAction)aAction,
|
||||
anchorNode, anchorOffset,
|
||||
aPreviousSelectedNode,
|
||||
aPreviousSelectedOffset,
|
||||
aStartNode, aStartOffset,
|
||||
aEndNode, aEndOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = ScheduleSpellCheck(status);
|
||||
rv = ScheduleSpellCheck(Move(status));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// remember the current caret position after every change
|
||||
|
@ -918,11 +919,11 @@ mozInlineSpellChecker::SpellCheckRange(nsIDOMRange* aRange)
|
|||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
nsRange* range = static_cast<nsRange*>(aRange);
|
||||
nsresult rv = status.InitForRange(range);
|
||||
nsresult rv = status->InitForRange(range);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return ScheduleSpellCheck(status);
|
||||
return ScheduleSpellCheck(Move(status));
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::GetMisspelledWord
|
||||
|
@ -989,10 +990,10 @@ mozInlineSpellChecker::AddWordToDictionary(const nsAString &word)
|
|||
nsresult rv = mSpellCheck->AddWordToDictionary(wordstr.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForSelection();
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForSelection();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return ScheduleSpellCheck(status);
|
||||
return ScheduleSpellCheck(Move(status));
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::RemoveWordFromDictionary
|
||||
|
@ -1006,10 +1007,10 @@ mozInlineSpellChecker::RemoveWordFromDictionary(const nsAString &word)
|
|||
nsresult rv = mSpellCheck->RemoveWordFromDictionary(wordstr.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForRange(nullptr);
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForRange(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return ScheduleSpellCheck(status);
|
||||
return ScheduleSpellCheck(Move(status));
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::IgnoreWord
|
||||
|
@ -1023,10 +1024,10 @@ mozInlineSpellChecker::IgnoreWord(const nsAString &word)
|
|||
nsresult rv = mSpellCheck->IgnoreWordAllOccurrences(wordstr.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForSelection();
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForSelection();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return ScheduleSpellCheck(status);
|
||||
return ScheduleSpellCheck(Move(status));
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::IgnoreWords
|
||||
|
@ -1041,10 +1042,10 @@ mozInlineSpellChecker::IgnoreWords(const char16_t **aWordsToIgnore,
|
|||
for (uint32_t index = 0; index < aCount; index++)
|
||||
mSpellCheck->IgnoreWordAllOccurrences(aWordsToIgnore[index]);
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
nsresult rv = status.InitForSelection();
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
nsresult rv = status->InitForSelection();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return ScheduleSpellCheck(status);
|
||||
return ScheduleSpellCheck(Move(status));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP mozInlineSpellChecker::WillCreateNode(const nsAString & aTag, nsIDOMNode *aParent, int32_t aPosition)
|
||||
|
@ -1224,10 +1225,10 @@ mozInlineSpellChecker::SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
|
|||
if (! range)
|
||||
return NS_OK; // range is empty: nothing to do
|
||||
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForRange(range);
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForRange(range);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return ScheduleSpellCheck(status);
|
||||
return ScheduleSpellCheck(Move(status));
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::ShouldSpellCheckNode
|
||||
|
@ -1312,20 +1313,23 @@ mozInlineSpellChecker::ShouldSpellCheckNode(nsIEditor* aEditor,
|
|||
// the proper structures for calls to DoSpellCheck.
|
||||
|
||||
nsresult
|
||||
mozInlineSpellChecker::ScheduleSpellCheck(const mozInlineSpellStatus& aStatus)
|
||||
mozInlineSpellChecker::ScheduleSpellCheck(UniquePtr<mozInlineSpellStatus>&& aStatus)
|
||||
{
|
||||
if (mFullSpellCheckScheduled) {
|
||||
// Just ignore this; we're going to spell-check everything anyway
|
||||
return NS_OK;
|
||||
}
|
||||
// Cache the value because we are going to move aStatus's ownership to
|
||||
// the new created mozInlineSpellResume instance.
|
||||
bool isFullSpellCheck = aStatus->IsFullSpellCheck();
|
||||
|
||||
RefPtr<mozInlineSpellResume> resume =
|
||||
new mozInlineSpellResume(aStatus, mDisabledAsyncToken);
|
||||
new mozInlineSpellResume(Move(aStatus), mDisabledAsyncToken);
|
||||
NS_ENSURE_TRUE(resume, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = resume->Post();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (aStatus.IsFullSpellCheck()) {
|
||||
if (isFullSpellCheck) {
|
||||
// We're going to check everything. Suppress further spell-check attempts
|
||||
// until that happens.
|
||||
mFullSpellCheckScheduled = true;
|
||||
|
@ -1348,8 +1352,7 @@ mozInlineSpellChecker::ScheduleSpellCheck(const mozInlineSpellStatus& aStatus)
|
|||
|
||||
nsresult
|
||||
mozInlineSpellChecker::DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
|
||||
Selection* aSpellCheckSelection,
|
||||
mozInlineSpellStatus* aStatus)
|
||||
Selection* aSpellCheckSelection)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
@ -1380,8 +1383,8 @@ mozInlineSpellChecker::DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
|
|||
// We use this state object for all calls, and just update its range. Note
|
||||
// that we don't need to call FinishInit since we will be filling in the
|
||||
// necessary information.
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForRange(nullptr);
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForRange(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool doneChecking;
|
||||
|
@ -1390,14 +1393,14 @@ mozInlineSpellChecker::DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
|
|||
// check range over it that needs to be deleted. All the old ranges
|
||||
// were cleared above. We also need to clear the word count so that we
|
||||
// check all words instead of stopping early.
|
||||
status.mRange = ranges[idx];
|
||||
rv = DoSpellCheck(aWordUtil, aSpellCheckSelection, &status,
|
||||
status->mRange = ranges[idx];
|
||||
rv = DoSpellCheck(aWordUtil, aSpellCheckSelection, status,
|
||||
&doneChecking);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(doneChecking,
|
||||
"We gave the spellchecker one word, but it didn't finish checking?!?!");
|
||||
|
||||
status.mWordCount = 0;
|
||||
status->mWordCount = 0;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1436,7 +1439,7 @@ mozInlineSpellChecker::DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
|
|||
|
||||
nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||
Selection *aSpellCheckSelection,
|
||||
mozInlineSpellStatus* aStatus,
|
||||
const UniquePtr<mozInlineSpellStatus>& aStatus,
|
||||
bool* aDoneChecking)
|
||||
{
|
||||
*aDoneChecking = true;
|
||||
|
@ -1485,7 +1488,7 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
if (! editor)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
int32_t wordsSinceTimeCheck = 0;
|
||||
int32_t wordsChecked = 0;
|
||||
PRTime beginTime = PR_Now();
|
||||
|
||||
nsAutoString wordText;
|
||||
|
@ -1495,7 +1498,6 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
getter_AddRefs(wordRange),
|
||||
&dontCheckWord)) &&
|
||||
wordRange) {
|
||||
wordsSinceTimeCheck++;
|
||||
|
||||
// get the range for the current word.
|
||||
nsINode *beginNode;
|
||||
|
@ -1508,6 +1510,25 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
beginOffset = wordRange->GetStartOffset(erv);
|
||||
endOffset = wordRange->GetEndOffset(erv);
|
||||
|
||||
// see if we've done enough words in this round and run out of time.
|
||||
if (wordsChecked >= INLINESPELL_MINIMUM_WORDS_BEFORE_TIMEOUT &&
|
||||
PR_Now() > PRTime(beginTime + kMaxSpellCheckTimeInUsec)) {
|
||||
// stop checking, our time limit has been exceeded.
|
||||
|
||||
// move the range to encompass the stuff that needs checking.
|
||||
nsresult rv = aStatus->mRange->SetStart(endNode, endOffset);
|
||||
if (NS_FAILED(rv)) {
|
||||
// The range might be unhappy because the beginning is after the
|
||||
// end. This is possible when the requested end was in the middle
|
||||
// of a word, just ignore this situation and assume we're done.
|
||||
return NS_OK;
|
||||
}
|
||||
*aDoneChecking = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
wordsChecked++;
|
||||
|
||||
#ifdef DEBUG_INLINESPELL
|
||||
printf("->Got word \"%s\"", NS_ConvertUTF16toUTF8(wordText).get());
|
||||
if (dontCheckWord)
|
||||
|
@ -1561,31 +1582,12 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
|||
|
||||
if (isMisspelled) {
|
||||
// misspelled words count extra toward the max
|
||||
wordsSinceTimeCheck += MISSPELLED_WORD_COUNT_PENALTY;
|
||||
AddRange(aSpellCheckSelection, wordRange);
|
||||
|
||||
aStatus->mWordCount ++;
|
||||
if (aStatus->mWordCount >= mMaxMisspellingsPerCheck ||
|
||||
SpellCheckSelectionIsFull())
|
||||
SpellCheckSelectionIsFull()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// see if we've run out of time, only check every N words for perf
|
||||
if (wordsSinceTimeCheck >= INLINESPELL_TIMEOUT_CHECK_FREQUENCY) {
|
||||
wordsSinceTimeCheck = 0;
|
||||
if (PR_Now() > PRTime(beginTime + INLINESPELL_CHECK_TIMEOUT * PR_USEC_PER_MSEC)) {
|
||||
// stop checking, our time limit has been exceeded
|
||||
|
||||
// move the range to encompass the stuff that needs checking
|
||||
rv = aStatus->mRange->SetStart(endNode, endOffset);
|
||||
if (NS_FAILED(rv)) {
|
||||
// The range might be unhappy because the beginning is after the
|
||||
// end. This is possible when the requested end was in the middle
|
||||
// of a word, just ignore this situation and assume we're done.
|
||||
return NS_OK;
|
||||
}
|
||||
*aDoneChecking = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1617,7 +1619,7 @@ private:
|
|||
// the last resume left off.
|
||||
|
||||
nsresult
|
||||
mozInlineSpellChecker::ResumeCheck(mozInlineSpellStatus* aStatus)
|
||||
mozInlineSpellChecker::ResumeCheck(UniquePtr<mozInlineSpellStatus>&& aStatus)
|
||||
{
|
||||
// Observers should be notified that spell check has ended only after spell
|
||||
// check is done below, but since there are many early returns in this method
|
||||
|
@ -1676,13 +1678,13 @@ mozInlineSpellChecker::ResumeCheck(mozInlineSpellStatus* aStatus)
|
|||
|
||||
bool doneChecking = true;
|
||||
if (aStatus->mOp == mozInlineSpellStatus::eOpSelection)
|
||||
rv = DoSpellCheckSelection(wordUtil, spellCheckSelection, aStatus);
|
||||
rv = DoSpellCheckSelection(wordUtil, spellCheckSelection);
|
||||
else
|
||||
rv = DoSpellCheck(wordUtil, spellCheckSelection, aStatus, &doneChecking);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (! doneChecking)
|
||||
rv = ScheduleSpellCheck(*aStatus);
|
||||
rv = ScheduleSpellCheck(Move(aStatus));
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -1877,14 +1879,15 @@ mozInlineSpellChecker::HandleNavigationEvent(bool aForceWordSpellCheck,
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool shouldPost;
|
||||
mozInlineSpellStatus status(this);
|
||||
rv = status.InitForNavigation(aForceWordSpellCheck, aNewPositionOffset,
|
||||
currentAnchorNode, currentAnchorOffset,
|
||||
mCurrentSelectionAnchorNode, mCurrentSelectionOffset,
|
||||
&shouldPost);
|
||||
auto status = MakeUnique<mozInlineSpellStatus>(this);
|
||||
rv = status->InitForNavigation(aForceWordSpellCheck, aNewPositionOffset,
|
||||
currentAnchorNode, currentAnchorOffset,
|
||||
mCurrentSelectionAnchorNode,
|
||||
mCurrentSelectionOffset,
|
||||
&shouldPost);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (shouldPost) {
|
||||
rv = ScheduleSpellCheck(status);
|
||||
rv = ScheduleSpellCheck(Move(status));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
|
|
@ -218,14 +218,13 @@ public:
|
|||
|
||||
// spell check the text contained within aRange, potentially scheduling
|
||||
// another check in the future if the time threshold is reached
|
||||
nsresult ScheduleSpellCheck(const mozInlineSpellStatus& aStatus);
|
||||
nsresult ScheduleSpellCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
|
||||
|
||||
nsresult DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
|
||||
mozilla::dom::Selection* aSpellCheckSelection,
|
||||
mozInlineSpellStatus* aStatus);
|
||||
mozilla::dom::Selection* aSpellCheckSelection);
|
||||
nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||
mozilla::dom::Selection *aSpellCheckSelection,
|
||||
mozInlineSpellStatus* aStatus,
|
||||
const mozilla::UniquePtr<mozInlineSpellStatus>& aStatus,
|
||||
bool* aDoneChecking);
|
||||
|
||||
// helper routine to determine if a point is inside of the passed in selection.
|
||||
|
@ -253,7 +252,7 @@ public:
|
|||
nsresult GetSpellCheckSelection(nsISelection ** aSpellCheckSelection);
|
||||
nsresult SaveCurrentSelectionPosition();
|
||||
|
||||
nsresult ResumeCheck(mozInlineSpellStatus* aStatus);
|
||||
nsresult ResumeCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
|
||||
|
||||
protected:
|
||||
virtual ~mozInlineSpellChecker();
|
||||
|
|
Загрузка…
Ссылка в новой задаче