зеркало из https://github.com/mozilla/pjs.git
Bug 345112 r+sr=bryner Make the spellchecker work incrementally
This commit is contained in:
Родитель
59bf6c9888
Коммит
d9f5437298
|
@ -61,6 +61,7 @@
|
||||||
#include "nsCOMArray.h"
|
#include "nsCOMArray.h"
|
||||||
#include "nsIDOMText.h"
|
#include "nsIDOMText.h"
|
||||||
#include "nsIDOMNodeList.h"
|
#include "nsIDOMNodeList.h"
|
||||||
|
#include "nsIRunnable.h"
|
||||||
#include "nsISelection.h"
|
#include "nsISelection.h"
|
||||||
#include "nsISelection2.h"
|
#include "nsISelection2.h"
|
||||||
#include "nsISelectionController.h"
|
#include "nsISelectionController.h"
|
||||||
|
@ -75,6 +76,7 @@
|
||||||
#include "nsIContent.h"
|
#include "nsIContent.h"
|
||||||
#include "nsIContentIterator.h"
|
#include "nsIContentIterator.h"
|
||||||
#include "nsCRT.h"
|
#include "nsCRT.h"
|
||||||
|
#include "nsThreadUtils.h"
|
||||||
#include "cattable.h"
|
#include "cattable.h"
|
||||||
#include "nsIPrefService.h"
|
#include "nsIPrefService.h"
|
||||||
#include "nsIPrefBranch.h"
|
#include "nsIPrefBranch.h"
|
||||||
|
@ -83,10 +85,54 @@
|
||||||
|
|
||||||
//#define DEBUG_INLINESPELL
|
//#define DEBUG_INLINESPELL
|
||||||
|
|
||||||
|
// the number of milliseconds that we will take at once to do spellchecking
|
||||||
|
#define INLINESPELL_CHECK_TIMEOUT 50
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
|
|
||||||
static const char kMaxSpellCheckSelectionSize[] = "extensions.spellcheck.inline.max-misspellings";
|
static const char kMaxSpellCheckSelectionSize[] = "extensions.spellcheck.inline.max-misspellings";
|
||||||
|
|
||||||
|
// Event stuff for suspending & resuming checks
|
||||||
|
mozInlineSpellStatus::mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker,
|
||||||
|
nsIDOMRange* aRange,
|
||||||
|
nsIDOMRange* aNoCheckRange,
|
||||||
|
nsIDOMRange* aCreatedRange)
|
||||||
|
: mSpellChecker(aSpellChecker), mRange(aRange), mNoCheckRange(aNoCheckRange),
|
||||||
|
mCreatedRange(aCreatedRange), mWordCount(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class mozInlineSpellResume : public nsRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mozInlineSpellResume(const mozInlineSpellStatus& aStatus) : mStatus(aStatus) {}
|
||||||
|
mozInlineSpellStatus mStatus;
|
||||||
|
nsresult Post()
|
||||||
|
{
|
||||||
|
return NS_DispatchToMainThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHOD Run()
|
||||||
|
{
|
||||||
|
mStatus.mSpellChecker->ResumeCheck(&mStatus);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN(mozInlineSpellChecker)
|
NS_INTERFACE_MAP_BEGIN(mozInlineSpellChecker)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIInlineSpellChecker)
|
NS_INTERFACE_MAP_ENTRY(nsIInlineSpellChecker)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
|
NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
|
||||||
|
@ -109,6 +155,7 @@ mozInlineSpellChecker::mozInlineSpellChecker():mNumWordsInSpellSelection(0),mMax
|
||||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||||
if (prefs)
|
if (prefs)
|
||||||
prefs->GetIntPref(kMaxSpellCheckSelectionSize, &mMaxNumWordsInSpellSelection);
|
prefs->GetIntPref(kMaxSpellCheckSelectionSize, &mMaxNumWordsInSpellSelection);
|
||||||
|
mMaxMisspellingsPerCheck = mMaxNumWordsInSpellSelection * 3 / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
mozInlineSpellChecker::~mozInlineSpellChecker()
|
mozInlineSpellChecker::~mozInlineSpellChecker()
|
||||||
|
@ -308,12 +355,6 @@ mozInlineSpellChecker::SpellCheckAfterEditorChange(
|
||||||
rv = aSelection->GetAnchorOffset(&anchorOffset);
|
rv = aSelection->GetAnchorOffset(&anchorOffset);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// the spell check selection includes all misspelled words
|
|
||||||
nsCOMPtr<nsISelection> spellCheckSelection;
|
|
||||||
rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
CleanupRangesInSelection(spellCheckSelection);
|
|
||||||
|
|
||||||
mozInlineSpellWordUtil wordUtil;
|
mozInlineSpellWordUtil wordUtil;
|
||||||
rv = wordUtil.Init(mEditor);
|
rv = wordUtil.Init(mEditor);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
|
@ -374,13 +415,10 @@ mozInlineSpellChecker::SpellCheckAfterEditorChange(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rangeToCheck) {
|
if (rangeToCheck) {
|
||||||
if (aAction == kOpInsertText) {
|
if (aAction == kOpInsertText)
|
||||||
rv = DoSpellCheck(wordUtil, rangeToCheck, wordRange, rangeToCheck,
|
rv = ScheduleSpellCheck(wordUtil, rangeToCheck, wordRange, rangeToCheck);
|
||||||
spellCheckSelection);
|
else
|
||||||
} else {
|
rv = ScheduleSpellCheck(wordUtil, rangeToCheck, wordRange, nsnull);
|
||||||
rv = DoSpellCheck(wordUtil, rangeToCheck, wordRange, nsnull,
|
|
||||||
spellCheckSelection);
|
|
||||||
}
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,22 +435,16 @@ mozInlineSpellChecker::SpellCheckAfterEditorChange(
|
||||||
nsresult
|
nsresult
|
||||||
mozInlineSpellChecker::SpellCheckRange(nsIDOMRange* aRange)
|
mozInlineSpellChecker::SpellCheckRange(nsIDOMRange* aRange)
|
||||||
{
|
{
|
||||||
|
nsresult rv;
|
||||||
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
|
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
|
||||||
|
|
||||||
nsCOMPtr<nsISelection> spellCheckSelection;
|
|
||||||
nsresult rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
CleanupRangesInSelection(spellCheckSelection);
|
|
||||||
|
|
||||||
if(aRange) {
|
if(aRange) {
|
||||||
mozInlineSpellWordUtil wordUtil;
|
mozInlineSpellWordUtil wordUtil;
|
||||||
rv = wordUtil.Init(mEditor);
|
rv = wordUtil.Init(mEditor);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return NS_OK; // editor doesn't like us
|
return NS_OK; // editor doesn't like us
|
||||||
rv = DoSpellCheck(wordUtil, aRange, nsnull, nsnull, spellCheckSelection);
|
rv = ScheduleSpellCheck(wordUtil, aRange, nsnull, nsnull);
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
// use full range: SpellCheckBetweenNodes will do the somewhat complicated
|
// use full range: SpellCheckBetweenNodes will do the somewhat complicated
|
||||||
// task of creating a range over the element we give it and call
|
// task of creating a range over the element we give it and call
|
||||||
// SpellCheckRange(range,selection) for us
|
// SpellCheckRange(range,selection) for us
|
||||||
|
@ -422,7 +454,7 @@ mozInlineSpellChecker::SpellCheckRange(nsIDOMRange* aRange)
|
||||||
nsCOMPtr<nsIDOMElement> rootElem;
|
nsCOMPtr<nsIDOMElement> rootElem;
|
||||||
rv = editor->GetRootElement(getter_AddRefs(rootElem));
|
rv = editor->GetRootElement(getter_AddRefs(rootElem));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
rv = SpellCheckBetweenNodes(rootElem, 0, rootElem, -1, spellCheckSelection);
|
rv = SpellCheckBetweenNodes(rootElem, 0, rootElem, -1);
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -598,8 +630,7 @@ mozInlineSpellChecker::SpellCheckSelection(nsISelection* aSelection)
|
||||||
// We can consider this word as "added" since we know it has no spell
|
// 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
|
// check range over it that needs to be deleted. All the old ranges
|
||||||
// were cleared above.
|
// were cleared above.
|
||||||
rv = DoSpellCheck(wordUtil, checkRange, nsnull, checkRange,
|
rv = ScheduleSpellCheck(wordUtil, checkRange, nsnull, checkRange);
|
||||||
spellCheckSelection);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -651,7 +682,7 @@ mozInlineSpellChecker::DidSplitNode(nsIDOMNode *aExistingRightNode,
|
||||||
PRInt32 aOffset,
|
PRInt32 aOffset,
|
||||||
nsIDOMNode *aNewLeftNode, nsresult aResult)
|
nsIDOMNode *aNewLeftNode, nsresult aResult)
|
||||||
{
|
{
|
||||||
return SpellCheckBetweenNodes(aNewLeftNode, 0, aNewLeftNode, 0, NULL);
|
return SpellCheckBetweenNodes(aNewLeftNode, 0, aNewLeftNode, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP mozInlineSpellChecker::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
|
NS_IMETHODIMP mozInlineSpellChecker::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
|
||||||
|
@ -662,7 +693,7 @@ NS_IMETHODIMP mozInlineSpellChecker::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOM
|
||||||
NS_IMETHODIMP mozInlineSpellChecker::DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode,
|
NS_IMETHODIMP mozInlineSpellChecker::DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode,
|
||||||
nsIDOMNode *aParent, nsresult aResult)
|
nsIDOMNode *aParent, nsresult aResult)
|
||||||
{
|
{
|
||||||
return SpellCheckBetweenNodes(aRightNode, 0, aRightNode, 0, NULL);
|
return SpellCheckBetweenNodes(aRightNode, 0, aRightNode, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP mozInlineSpellChecker::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString & aString)
|
NS_IMETHODIMP mozInlineSpellChecker::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString & aString)
|
||||||
|
@ -700,26 +731,18 @@ NS_IMETHODIMP mozInlineSpellChecker::DidDeleteSelection(nsISelection *aSelection
|
||||||
// mozInlineSpellChecker::SpellCheckBetweenNodes
|
// mozInlineSpellChecker::SpellCheckBetweenNodes
|
||||||
//
|
//
|
||||||
// Given begin and end positions, this function constructs a range as
|
// Given begin and end positions, this function constructs a range as
|
||||||
// required for DoSpellCheck, which then does the actual checking.
|
// required for ScheduleSpellCheck, which then does the actual checking.
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
mozInlineSpellChecker::SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
|
mozInlineSpellChecker::SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
|
||||||
PRInt32 aStartOffset,
|
PRInt32 aStartOffset,
|
||||||
nsIDOMNode *aEndNode,
|
nsIDOMNode *aEndNode,
|
||||||
PRInt32 aEndOffset,
|
PRInt32 aEndOffset)
|
||||||
nsISelection *aSpellCheckSelection)
|
|
||||||
{
|
{
|
||||||
nsresult res;
|
nsresult res;
|
||||||
nsCOMPtr<nsISelection> spellCheckSelection = aSpellCheckSelection;
|
|
||||||
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
|
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
|
||||||
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
|
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
|
||||||
|
|
||||||
if (!spellCheckSelection)
|
|
||||||
{
|
|
||||||
res = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
|
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMDocument> doc;
|
nsCOMPtr<nsIDOMDocument> doc;
|
||||||
res = editor->GetDocument(getter_AddRefs(doc));
|
res = editor->GetDocument(getter_AddRefs(doc));
|
||||||
NS_ENSURE_SUCCESS(res, res);
|
NS_ENSURE_SUCCESS(res, res);
|
||||||
|
@ -775,7 +798,7 @@ mozInlineSpellChecker::SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
|
||||||
res = wordUtil.Init(mEditor);
|
res = wordUtil.Init(mEditor);
|
||||||
if (NS_FAILED(res))
|
if (NS_FAILED(res))
|
||||||
return NS_OK; // editor doesn't like us
|
return NS_OK; // editor doesn't like us
|
||||||
return DoSpellCheck(wordUtil, range, nsnull, nsnull, spellCheckSelection);
|
return ScheduleSpellCheck(wordUtil, range, nsnull, nsnull);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline PRBool IsNonwordChar(PRUnichar chr)
|
static inline PRBool IsNonwordChar(PRUnichar chr)
|
||||||
|
@ -847,23 +870,59 @@ mozInlineSpellChecker::SkipSpellCheckForNode(nsIDOMNode *aNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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(mozInlineSpellWordUtil& aWordUtil,
|
||||||
|
mozInlineSpellStatus* aStatus)
|
||||||
|
{
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
// the spell check selection includes all misspelled words
|
||||||
|
nsCOMPtr<nsISelection> spellCheckSelection;
|
||||||
|
rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
CleanupRangesInSelection(spellCheckSelection);
|
||||||
|
|
||||||
|
PRBool doneChecking;
|
||||||
|
rv = DoSpellCheck(aWordUtil, spellCheckSelection, aStatus, &doneChecking);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (! doneChecking) {
|
||||||
|
// schedule an event so we can continue spellchecking in the future
|
||||||
|
mozInlineSpellResume* resume = new mozInlineSpellResume(*aStatus);
|
||||||
|
NS_ENSURE_TRUE(resume, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
rv = resume->Post();
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
delete resume;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// mozInlineSpellChecker::DoSpellCheck
|
// mozInlineSpellChecker::DoSpellCheck
|
||||||
//
|
//
|
||||||
// This function checks words intersecting the given range, excluding those
|
// This function checks words intersecting the given range, excluding those
|
||||||
// inside aNoCheckRange (can be NULL). Words inside aNoCheckRange will have
|
// inside mStatus->mNoCheckRange (can be NULL). Words inside aNoCheckRange
|
||||||
// any spell selection removed (this is used to hide the underlining for the
|
// will have any spell selection removed (this is used to hide the
|
||||||
// word that the caret is in). aNoCheckRange should be on word boundaries.
|
// underlining for the word that the caret is in). aNoCheckRange should be
|
||||||
|
// on word boundaries.
|
||||||
//
|
//
|
||||||
// aCreatedRange is a possibly NULL range of new text that was inserted.
|
// mResume->mCreatedRange is a possibly NULL range of new text that was
|
||||||
// Inside this range, we don't bother to check whether things are inside
|
// inserted. Inside this range, we don't bother to check whether things are
|
||||||
// the spellcheck selection, which speeds up large paste operations
|
// inside the spellcheck selection, which speeds up large paste operations
|
||||||
// considerably.
|
// considerably.
|
||||||
//
|
//
|
||||||
// Normal case when editing text by typing
|
// Normal case when editing text by typing
|
||||||
// h e l l o w o r k d h o w a r e y o u
|
// h e l l o w o r k d h o w a r e y o u
|
||||||
// ^ caret
|
// ^ caret
|
||||||
// [-------] aRange
|
// [-------] mRange
|
||||||
// [-------] aNoCheckRange
|
// [-------] mNoCheckRange
|
||||||
// -> does nothing (range is the same as the no check range)
|
// -> does nothing (range is the same as the no check range)
|
||||||
//
|
//
|
||||||
// Case when pasting:
|
// Case when pasting:
|
||||||
|
@ -872,18 +931,22 @@ mozInlineSpellChecker::SkipSpellCheckForNode(nsIDOMNode *aNode,
|
||||||
// ^ caret
|
// ^ caret
|
||||||
// [---] aNoCheckRange
|
// [---] aNoCheckRange
|
||||||
// -> recheck all words in range except those in aNoCheckRange
|
// -> 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,
|
nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
nsIDOMRange *aRange,
|
nsISelection *aSpellCheckSelection,
|
||||||
nsIDOMRange *aNoCheckRange,
|
mozInlineSpellStatus* aStatus,
|
||||||
nsIDOMRange *aCreatedRange,
|
PRBool* aDoneChecking)
|
||||||
nsISelection *aSpellCheckSelection)
|
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIDOMNode> beginNode, endNode;
|
nsCOMPtr<nsIDOMNode> beginNode, endNode;
|
||||||
PRInt32 beginOffset, endOffset;
|
PRInt32 beginOffset, endOffset;
|
||||||
|
*aDoneChecking = PR_TRUE;
|
||||||
|
|
||||||
PRBool iscollapsed;
|
PRBool iscollapsed;
|
||||||
nsresult rv = aRange->GetCollapsed(&iscollapsed);
|
nsresult rv = aStatus->mRange->GetCollapsed(&iscollapsed);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
if (iscollapsed)
|
if (iscollapsed)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -900,27 +963,32 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
|
|
||||||
// set the starting DOM position to be the beginning of our range
|
// set the starting DOM position to be the beginning of our range
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
aRange->GetStartContainer(getter_AddRefs(beginNode));
|
aStatus->mRange->GetStartContainer(getter_AddRefs(beginNode));
|
||||||
aRange->GetStartOffset(&beginOffset);
|
aStatus->mRange->GetStartOffset(&beginOffset);
|
||||||
aRange->GetEndContainer(getter_AddRefs(endNode));
|
aStatus->mRange->GetEndContainer(getter_AddRefs(endNode));
|
||||||
aRange->GetEndOffset(&endOffset);
|
aStatus->mRange->GetEndOffset(&endOffset);
|
||||||
aWordUtil.SetEnd(endNode, endOffset);
|
aWordUtil.SetEnd(endNode, endOffset);
|
||||||
aWordUtil.SetPosition(beginNode, beginOffset);
|
aWordUtil.SetPosition(beginNode, beginOffset);
|
||||||
|
|
||||||
// we need to use IsPointInRange which is on a more specific interface
|
// we need to use IsPointInRange which is on a more specific interface
|
||||||
nsCOMPtr<nsIDOMNSRange> noCheckRange, createdRange;
|
nsCOMPtr<nsIDOMNSRange> noCheckRange, createdRange;
|
||||||
if (aNoCheckRange)
|
if (aStatus->mNoCheckRange)
|
||||||
noCheckRange = do_QueryInterface(aNoCheckRange);
|
noCheckRange = do_QueryInterface(aStatus->mNoCheckRange);
|
||||||
if (aCreatedRange)
|
if (aStatus->mCreatedRange)
|
||||||
createdRange = do_QueryInterface(aCreatedRange);
|
createdRange = do_QueryInterface(aStatus->mCreatedRange);
|
||||||
|
|
||||||
|
PRInt32 wordsSinceTimeCheck = 0;
|
||||||
|
PRTime beginTime = PR_Now();
|
||||||
|
|
||||||
nsAutoString wordText;
|
nsAutoString wordText;
|
||||||
nsCOMPtr<nsIDOMRange> wordRange;
|
nsCOMPtr<nsIDOMRange> wordRange;
|
||||||
PRBool dontCheckWord;
|
PRBool dontCheckWord;
|
||||||
while (NS_SUCCEEDED(aWordUtil.GetNextWord(wordText,
|
while (NS_SUCCEEDED(aWordUtil.GetNextWord(wordText,
|
||||||
getter_AddRefs(wordRange),
|
getter_AddRefs(wordRange),
|
||||||
&dontCheckWord)) &&
|
&dontCheckWord)) &&
|
||||||
wordRange) {
|
wordRange) {
|
||||||
|
wordsSinceTimeCheck ++;
|
||||||
|
|
||||||
// get the range for the current word
|
// get the range for the current word
|
||||||
wordRange->GetStartContainer(getter_AddRefs(beginNode));
|
wordRange->GetStartContainer(getter_AddRefs(beginNode));
|
||||||
wordRange->GetEndContainer(getter_AddRefs(endNode));
|
wordRange->GetEndContainer(getter_AddRefs(endNode));
|
||||||
|
@ -981,13 +1049,63 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
PRBool isMisspelled;
|
PRBool isMisspelled;
|
||||||
aWordUtil.NormalizeWord(wordText);
|
aWordUtil.NormalizeWord(wordText);
|
||||||
rv = mSpellCheck->CheckCurrentWordNoSuggest(wordText.get(), &isMisspelled);
|
rv = mSpellCheck->CheckCurrentWordNoSuggest(wordText.get(), &isMisspelled);
|
||||||
if (isMisspelled)
|
if (isMisspelled) {
|
||||||
|
// misspelled words count extra toward the max
|
||||||
|
wordsSinceTimeCheck += MISSPELLED_WORD_COUNT_PENALTY;
|
||||||
AddRange(aSpellCheckSelection, wordRange);
|
AddRange(aSpellCheckSelection, wordRange);
|
||||||
|
|
||||||
|
aStatus->mWordCount ++;
|
||||||
|
if (aStatus->mWordCount >= mMaxMisspellingsPerCheck ||
|
||||||
|
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() > 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 = PR_FALSE;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
if (! mSpellCheck)
|
||||||
|
return NS_OK; // spell checking has been turned off
|
||||||
|
|
||||||
|
mozInlineSpellWordUtil wordUtil;
|
||||||
|
nsresult rv = wordUtil.Init(mEditor);
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return NS_OK; // editor doesn't like us
|
||||||
|
|
||||||
|
rv = ScheduleSpellCheck(wordUtil, aStatus);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
// give up, FIXME: we may want to re-check the entire document at this
|
||||||
|
// point.
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// mozInlineSpellChecker::IsPointInSelection
|
// mozInlineSpellChecker::IsPointInSelection
|
||||||
//
|
//
|
||||||
|
@ -1213,11 +1331,7 @@ mozInlineSpellChecker::HandleNavigationEvent(nsIDOMEvent* aEvent,
|
||||||
|
|
||||||
if (!isInRange || aForceWordSpellCheck) // selection is moving to a new word, spell check the current word
|
if (!isInRange || aForceWordSpellCheck) // selection is moving to a new word, spell check the current word
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsISelection> spellCheckSelection;
|
rv = ScheduleSpellCheck(wordUtil, currentWordRange, nsnull, nsnull);
|
||||||
GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
|
|
||||||
|
|
||||||
rv = DoSpellCheck(wordUtil, currentWordRange, nsnull, nsnull,
|
|
||||||
spellCheckSelection);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@
|
||||||
#ifndef __mozinlinespellchecker_h__
|
#ifndef __mozinlinespellchecker_h__
|
||||||
#define __mozinlinespellchecker_h__
|
#define __mozinlinespellchecker_h__
|
||||||
|
|
||||||
|
#include "nsAutoPtr.h"
|
||||||
|
#include "nsIDOMRange.h"
|
||||||
#include "nsIEditorSpellCheck.h"
|
#include "nsIEditorSpellCheck.h"
|
||||||
#include "nsIEditActionListener.h"
|
#include "nsIEditActionListener.h"
|
||||||
#include "nsIInlineSpellChecker.h"
|
#include "nsIInlineSpellChecker.h"
|
||||||
|
@ -53,6 +55,26 @@
|
||||||
|
|
||||||
class nsIDOMMouseEventListener;
|
class nsIDOMMouseEventListener;
|
||||||
class mozInlineSpellWordUtil;
|
class mozInlineSpellWordUtil;
|
||||||
|
class mozInlineSpellChecker;
|
||||||
|
class mozInlineSpellResume;
|
||||||
|
|
||||||
|
class mozInlineSpellStatus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker,
|
||||||
|
nsIDOMRange* aRange,
|
||||||
|
nsIDOMRange* aNoCheckRange,
|
||||||
|
nsIDOMRange* aCreatedRange);
|
||||||
|
|
||||||
|
nsRefPtr<mozInlineSpellChecker> mSpellChecker;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMRange> mRange;
|
||||||
|
nsCOMPtr<nsIDOMRange> mNoCheckRange;
|
||||||
|
nsCOMPtr<nsIDOMRange> mCreatedRange;
|
||||||
|
|
||||||
|
// total number of words checked in this sequence
|
||||||
|
PRInt32 mWordCount;
|
||||||
|
};
|
||||||
|
|
||||||
class mozInlineSpellChecker : public nsIInlineSpellChecker, nsIEditActionListener, nsIDOMMouseListener, nsIDOMKeyListener,
|
class mozInlineSpellChecker : public nsIInlineSpellChecker, nsIEditActionListener, nsIDOMMouseListener, nsIDOMKeyListener,
|
||||||
nsSupportsWeakReference
|
nsSupportsWeakReference
|
||||||
|
@ -74,6 +96,13 @@ private:
|
||||||
PRInt32 mNumWordsInSpellSelection;
|
PRInt32 mNumWordsInSpellSelection;
|
||||||
PRInt32 mMaxNumWordsInSpellSelection;
|
PRInt32 mMaxNumWordsInSpellSelection;
|
||||||
|
|
||||||
|
// How many misspellings we can add at once. This is often less than the max
|
||||||
|
// total number of misspellings. When you have a large textarea prepopulated
|
||||||
|
// with text with many misspellings, we can hit this limit. By making it
|
||||||
|
// lower than the total number of misspelled words, new text typed by the
|
||||||
|
// user can also have spellchecking in it.
|
||||||
|
PRInt32 mMaxMisspellingsPerCheck;
|
||||||
|
|
||||||
// we need to keep track of the current text position in the document
|
// we need to keep track of the current text position in the document
|
||||||
// so we can spell check the old word when the user clicks around the document.
|
// so we can spell check the old word when the user clicks around the document.
|
||||||
nsCOMPtr<nsIDOMNode> mCurrentSelectionAnchorNode;
|
nsCOMPtr<nsIDOMNode> mCurrentSelectionAnchorNode;
|
||||||
|
@ -154,8 +183,7 @@ public:
|
||||||
nsresult SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
|
nsresult SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
|
||||||
PRInt32 aStartOffset,
|
PRInt32 aStartOffset,
|
||||||
nsIDOMNode *aEndNode,
|
nsIDOMNode *aEndNode,
|
||||||
PRInt32 aEndOffset,
|
PRInt32 aEndOffset);
|
||||||
nsISelection *aSpellCheckSelection);
|
|
||||||
|
|
||||||
// examines the dom node in question and returns true if the inline spell
|
// examines the dom node in question and returns true if the inline spell
|
||||||
// checker should skip the node (i.e. the text is inside of a block quote
|
// checker should skip the node (i.e. the text is inside of a block quote
|
||||||
|
@ -166,11 +194,21 @@ public:
|
||||||
nsIDOMNode* aPreviousNode, PRInt32 aPreviousOffset,
|
nsIDOMNode* aPreviousNode, PRInt32 aPreviousOffset,
|
||||||
nsISelection* aSpellCheckSelection);
|
nsISelection* aSpellCheckSelection);
|
||||||
|
|
||||||
// spell check the text contained within aRange
|
// spell check the text contained within aRange, potentially scheduling
|
||||||
|
// another check in the future if the time threshold is reached
|
||||||
|
nsresult ScheduleSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
|
mozInlineSpellStatus* aStatus);
|
||||||
|
nsresult ScheduleSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
|
nsIDOMRange *aRange, nsIDOMRange* aNoCheckRange,
|
||||||
|
nsIDOMRange *aCreatedRange) {
|
||||||
|
mozInlineSpellStatus status(this, aRange, aNoCheckRange, aCreatedRange);
|
||||||
|
return ScheduleSpellCheck(aWordUtil, &status);
|
||||||
|
}
|
||||||
|
|
||||||
nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||||
nsIDOMRange *aRange, nsIDOMRange* aNoCheckRange,
|
nsISelection *aSpellCheckSelection,
|
||||||
nsIDOMRange *aCreatedRange,
|
mozInlineSpellStatus* aStatus,
|
||||||
nsISelection *aSpellCheckSelection);
|
PRBool* aDoneChecking);
|
||||||
|
|
||||||
// helper routine to determine if a point is inside of a the passed in selection.
|
// helper routine to determine if a point is inside of a the passed in selection.
|
||||||
nsresult IsPointInSelection(nsISelection *aSelection,
|
nsresult IsPointInSelection(nsISelection *aSelection,
|
||||||
|
@ -192,6 +230,8 @@ public:
|
||||||
|
|
||||||
nsresult GetSpellCheckSelection(nsISelection ** aSpellCheckSelection);
|
nsresult GetSpellCheckSelection(nsISelection ** aSpellCheckSelection);
|
||||||
nsresult SaveCurrentSelectionPosition();
|
nsresult SaveCurrentSelectionPosition();
|
||||||
|
|
||||||
|
nsresult ResumeCheck(mozInlineSpellStatus* aStatus);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __mozinlinespellchecker_h__ */
|
#endif /* __mozinlinespellchecker_h__ */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче