--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -73,40 +73,36 @@
using namespace mozilla;
using namespace mozilla::dom;
// Set to spew messages to the console about what is happening.
//#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.
#define INLINESPELL_STARTED_TOPIC "inlineSpellChecker-spellCheck-started"
#define INLINESPELL_ENDED_TOPIC "inlineSpellChecker-spellCheck-ended"
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)
{
}
// mozInlineSpellStatus::InitForEditorChange
//
@@ -479,42 +475,42 @@ mozInlineSpellStatus::PositionToCollapse
return NS_OK;
}
// mozInlineSpellResume
class mozInlineSpellResume : public Runnable
{
public:
- mozInlineSpellResume(const mozInlineSpellStatus& aStatus,
+ mozInlineSpellResume(UniquePtr<mozInlineSpellStatus>&& aStatus,
uint32_t aDisabledAsyncToken)
: Runnable("mozInlineSpellResume")
, mDisabledAsyncToken(aDisabledAsyncToken)
- , mStatus(aStatus)
+ , mStatus(Move(aStatus))
{
}
nsresult Post()
{
return NS_DispatchToMainThread(this);
}
NS_IMETHOD Run() override
{
// 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.
class InitEditorSpellCheckCallback final : public nsIEditorSpellCheckCallback
{
~InitEditorSpellCheckCallback() {}
public:
NS_DECL_ISUPPORTS
@@ -695,17 +691,18 @@ mozInlineSpellChecker::CanEnableInlineSp
void // static
mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking()
{
gCanEnableSpellChecking = SpellCheck_Uninitialized;
}
// 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()
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
editor->AddEditActionListener(this);
@@ -896,24 +893,25 @@ mozInlineSpellChecker::SpellCheckAfterEd
// the anchor node is the position of the caret
nsCOMPtr<nsIDOMNode> anchorNode;
rv = aSelection->GetAnchorNode(getter_AddRefs(anchorNode));
NS_ENSURE_SUCCESS(rv, rv);
int32_t anchorOffset;
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
SaveCurrentSelectionPosition();
return NS_OK;
}
// mozInlineSpellChecker::SpellCheckRange
@@ -926,21 +924,21 @@ mozInlineSpellChecker::SpellCheckRange(n
{
if (!mSpellCheck) {
NS_WARNING_ASSERTION(
mPendingSpellCheck,
"Trying to spellcheck, but checking seems to be disabled");
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
NS_IMETHODIMP
mozInlineSpellChecker::GetMisspelledWord(nsIDOMNode *aNode, int32_t aOffset,
nsIDOMRange **newword)
{
@@ -997,72 +995,72 @@ NS_IMETHODIMP
mozInlineSpellChecker::AddWordToDictionary(const nsAString &word)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
nsAutoString wordstr(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
NS_IMETHODIMP
mozInlineSpellChecker::RemoveWordFromDictionary(const nsAString &word)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
nsAutoString wordstr(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
NS_IMETHODIMP
mozInlineSpellChecker::IgnoreWord(const nsAString &word)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
nsAutoString wordstr(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
NS_IMETHODIMP
mozInlineSpellChecker::IgnoreWords(const char16_t **aWordsToIgnore,
uint32_t aCount)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
// add each word to the ignore list and then recheck the document
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)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidCreateNode(const nsAString & aTag, nsIDOMNode *aNode, nsIDOMNode *aParent,
@@ -1239,20 +1237,20 @@ mozInlineSpellChecker::SpellCheckBetween
nsresult rv = MakeSpellCheckRange(aStartNode, aStartOffset,
aEndNode, aEndOffset,
getter_AddRefs(range));
NS_ENSURE_SUCCESS(rv, rv);
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
//
// There are certain conditions when we don't want to spell check a node. In
// particular quotations, moz signatures, etc. This routine returns false
// for these cases.
@@ -1327,30 +1325,33 @@ mozInlineSpellChecker::ShouldSpellCheckN
}
// mozInlineSpellChecker::ScheduleSpellCheck
//
// This is called by code to do the actual spellchecking. We will set up
// 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;
}
ChangeNumPendingSpellChecks(1);
}
return rv;
}
@@ -1363,18 +1364,17 @@ mozInlineSpellChecker::ScheduleSpellChec
// be removed from the selection.
//
// FIXME-PERFORMANCE: This takes as long as it takes and is not resumable.
// Typically, checking this small amount of text is relatively fast, but
// for large numbers of words, a lag may be noticeable.
nsresult
mozInlineSpellChecker::DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
- Selection* aSpellCheckSelection,
- mozInlineSpellStatus* aStatus)
+ Selection* aSpellCheckSelection)
{
nsresult rv;
// clear out mNumWordsInSpellSelection since we'll be rebuilding the ranges.
mNumWordsInSpellSelection = 0;
// Since we could be modifying the ranges for the spellCheckSelection while
// looping on the spell check selection, keep a separate array of range
@@ -1395,34 +1395,34 @@ mozInlineSpellChecker::DoSpellCheckSelec
// provides better performance. By ensuring that no ranges need to be
// removed in DoSpellCheck, we can save checking range inclusion which is
// slow.
aSpellCheckSelection->RemoveAllRanges();
// 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;
for (int32_t idx = 0; idx < count; idx++) {
// We can consider this word as "added" since we know it has no spell
// 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;
}
// mozInlineSpellChecker::DoSpellCheck
//
// This function checks words intersecting the given range, excluding those
@@ -1451,17 +1451,17 @@ mozInlineSpellChecker::DoSpellCheckSelec
// -> recheck all words in range except those in aNoCheckRange
//
// If checking is complete, *aDoneChecking will be set. If there is more
// but we ran out of time, this will be false and the range will be
// updated with the stuff that still needs checking.
nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
Selection *aSpellCheckSelection,
- mozInlineSpellStatus* aStatus,
+ const UniquePtr<mozInlineSpellStatus>& aStatus,
bool* aDoneChecking)
{
*aDoneChecking = true;
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
// get the editor for ShouldSpellCheckNode, this may fail in reasonable
// circumstances since the editor could have gone away
@@ -1500,39 +1500,57 @@ nsresult mozInlineSpellChecker::DoSpellC
}
// aWordUtil.SetPosition flushes pending notifications, check editor again.
// XXX Uhhh, *we're* holding a strong ref to the editor.
editor = do_QueryReferent(mEditor);
if (! editor)
return NS_ERROR_FAILURE;
- int32_t wordsSinceTimeCheck = 0;
+ int32_t wordsChecked = 0;
PRTime beginTime = PR_Now();
nsAutoString wordText;
RefPtr<nsRange> wordRange;
bool dontCheckWord;
while (NS_SUCCEEDED(aWordUtil.GetNextWord(wordText,
getter_AddRefs(wordRange),
&dontCheckWord)) &&
wordRange) {
- wordsSinceTimeCheck++;
// get the range for the current word.
nsINode *beginNode;
nsINode *endNode;
int32_t beginOffset, endOffset;
ErrorResult erv;
beginNode = wordRange->GetStartContainer(erv);
endNode = wordRange->GetEndContainer(erv);
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.
+ #ifdef DEBUG_INLINESPELL
+ printf("We have run out of the time, schedule next round.");
+ #endif
+ // move the range to encompass the stuff that needs checking.
+ nsresult rv = aStatus->mRange->SetStart(beginNode, beginOffset);
+ 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;
+ }
+
#ifdef DEBUG_INLINESPELL
printf("->Got word \"%s\"", NS_ConvertUTF16toUTF8(wordText).get());
if (dontCheckWord)
printf(" (not checking)");
printf("\n");
#endif
// see if there is a spellcheck range that already intersects the word
@@ -1574,43 +1592,26 @@ nsresult mozInlineSpellChecker::DoSpellC
// check spelling and add to selection if misspelled
bool isMisspelled;
aWordUtil.NormalizeWord(wordText);
nsresult rv = mSpellCheck->CheckCurrentWordNoSuggest(wordText.get(), &isMisspelled);
if (NS_FAILED(rv))
continue;
+ wordsChecked++;
+
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;
}
}
}
return NS_OK;
}
// An RAII helper that calls ChangeNumPendingSpellChecks on destruction.
@@ -1632,17 +1633,17 @@ private:
};
// mozInlineSpellChecker::ResumeCheck
//
// Called by the resume event when it fires. We will try to pick up where
// 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
// and the number of pending spell checks must be decremented regardless of
// whether the spell check actually happens, use this RAII object.
AutoChangeNumPendingSpellChecks autoChangeNumPending(this, -1);
if (aStatus->IsFullSpellCheck()) {
@@ -1691,23 +1692,23 @@ mozInlineSpellChecker::ResumeCheck(mozIn
rv = aStatus->FinishInitOnEvent(wordUtil);
NS_ENSURE_SUCCESS(rv, rv);
if (! aStatus->mRange)
return NS_OK; // empty range, nothing to do
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;
}
// mozInlineSpellChecker::IsPointInSelection
//
// Determines if a given (node,offset) point is inside the given
// selection. If so, the specific range of the selection that
// intersects is places in *aRange. (There may be multiple disjoint
@@ -1892,24 +1893,25 @@ mozInlineSpellChecker::HandleNavigationE
nsCOMPtr<nsIDOMNode> currentAnchorNode = mCurrentSelectionAnchorNode;
int32_t currentAnchorOffset = mCurrentSelectionOffset;
// now remember the new focus position resulting from the event
rv = SaveCurrentSelectionPosition();
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);
}
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::HandleEvent(nsIDOMEvent* aEvent)
{