mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 856270 - Update spell checking to use nsIContentPrefService2 (part 1: core changes). r=ehsan, sr=gavin
This commit is contained in:
parent
9a0b22c391
commit
7b004ec642
@ -19,6 +19,7 @@
|
||||
#include "nsIChromeRegistry.h" // for nsIXULChromeRegistry
|
||||
#include "nsIContent.h" // for nsIContent
|
||||
#include "nsIContentPrefService.h" // for nsIContentPrefService, etc
|
||||
#include "nsIContentPrefService2.h" // for nsIContentPrefService2, etc
|
||||
#include "nsIDOMDocument.h" // for nsIDOMDocument
|
||||
#include "nsIDOMElement.h" // for nsIDOMElement
|
||||
#include "nsIDOMRange.h" // for nsIDOMRange
|
||||
@ -62,34 +63,11 @@ class UpdateDictionnaryHolder {
|
||||
|
||||
#define CPS_PREF_NAME NS_LITERAL_STRING("spellcheck.lang")
|
||||
|
||||
class LastDictionary MOZ_FINAL {
|
||||
public:
|
||||
/**
|
||||
* Store current dictionary for editor document url. Use content pref
|
||||
* service.
|
||||
* Gets the URI of aEditor's document.
|
||||
*/
|
||||
NS_IMETHOD StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary);
|
||||
|
||||
/**
|
||||
* Get last stored current dictionary for editor document url.
|
||||
*/
|
||||
NS_IMETHOD FetchLastDictionary(nsIEditor* aEditor, nsAString& aDictionary);
|
||||
|
||||
/**
|
||||
* Forget last current dictionary stored for editor document url.
|
||||
*/
|
||||
NS_IMETHOD ClearCurrentDictionary(nsIEditor* aEditor);
|
||||
|
||||
/**
|
||||
* get uri of editor's document.
|
||||
*
|
||||
*/
|
||||
static nsresult GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI);
|
||||
};
|
||||
|
||||
// static
|
||||
nsresult
|
||||
LastDictionary::GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI)
|
||||
static nsresult
|
||||
GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEditor);
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
@ -124,8 +102,55 @@ GetLoadContext(nsIEditor* aEditor)
|
||||
return loadContext.forget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the dictionary stored in content prefs and maintains state during the
|
||||
* fetch, which is asynchronous.
|
||||
*/
|
||||
class DictionaryFetcher MOZ_FINAL : public nsIContentPrefCallback2
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
DictionaryFetcher(nsEditorSpellCheck* aSpellCheck,
|
||||
nsIEditorSpellCheckCallback* aCallback,
|
||||
uint32_t aGroup)
|
||||
: mCallback(aCallback), mGroup(aGroup), mSpellCheck(aSpellCheck) {}
|
||||
|
||||
NS_IMETHOD Fetch(nsIEditor* aEditor);
|
||||
|
||||
NS_IMETHOD HandleResult(nsIContentPref* aPref)
|
||||
{
|
||||
nsCOMPtr<nsIVariant> value;
|
||||
nsresult rv = aPref->GetValue(getter_AddRefs(value));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
value->GetAsAString(mDictionary);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleCompletion(uint16_t reason)
|
||||
{
|
||||
mSpellCheck->DictionaryFetched(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleError(nsresult error)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEditorSpellCheckCallback> mCallback;
|
||||
uint32_t mGroup;
|
||||
nsString mRootContentLang;
|
||||
nsString mRootDocContentLang;
|
||||
nsString mDictionary;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsEditorSpellCheck> mSpellCheck;
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(DictionaryFetcher, nsIContentPrefCallback2)
|
||||
|
||||
NS_IMETHODIMP
|
||||
LastDictionary::FetchLastDictionary(nsIEditor* aEditor, nsAString& aDictionary)
|
||||
DictionaryFetcher::Fetch(nsIEditor* aEditor)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEditor);
|
||||
|
||||
@ -135,29 +160,28 @@ LastDictionary::FetchLastDictionary(nsIEditor* aEditor, nsAString& aDictionary)
|
||||
rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIContentPrefService> contentPrefService =
|
||||
nsAutoCString docUriSpec;
|
||||
rv = docUri->GetSpec(docUriSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIContentPrefService2> contentPrefService =
|
||||
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
|
||||
uri->SetAsISupports(docUri);
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor);
|
||||
bool hasPref;
|
||||
if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, loadContext, &hasPref)) && hasPref) {
|
||||
nsCOMPtr<nsIVariant> pref;
|
||||
contentPrefService->GetPref(uri, CPS_PREF_NAME, loadContext, nullptr, getter_AddRefs(pref));
|
||||
pref->GetAsAString(aDictionary);
|
||||
} else {
|
||||
aDictionary.Truncate();
|
||||
}
|
||||
rv = contentPrefService->GetByDomainAndName(NS_ConvertUTF8toUTF16(docUriSpec),
|
||||
CPS_PREF_NAME, loadContext,
|
||||
this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LastDictionary::StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary)
|
||||
/**
|
||||
* Stores the current dictionary for aEditor's document URL.
|
||||
*/
|
||||
static nsresult
|
||||
StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEditor);
|
||||
|
||||
@ -167,24 +191,29 @@ LastDictionary::StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDic
|
||||
rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
|
||||
uri->SetAsISupports(docUri);
|
||||
nsAutoCString docUriSpec;
|
||||
rv = docUri->GetSpec(docUriSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
NS_ENSURE_TRUE(prefValue, NS_ERROR_OUT_OF_MEMORY);
|
||||
prefValue->SetAsAString(aDictionary);
|
||||
|
||||
nsCOMPtr<nsIContentPrefService> contentPrefService =
|
||||
nsCOMPtr<nsIContentPrefService2> contentPrefService =
|
||||
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor);
|
||||
return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue, loadContext);
|
||||
return contentPrefService->Set(NS_ConvertUTF8toUTF16(docUriSpec),
|
||||
CPS_PREF_NAME, prefValue, loadContext,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LastDictionary::ClearCurrentDictionary(nsIEditor* aEditor)
|
||||
/**
|
||||
* Forgets the current dictionary stored for aEditor's document URL.
|
||||
*/
|
||||
static nsresult
|
||||
ClearCurrentDictionary(nsIEditor* aEditor)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEditor);
|
||||
|
||||
@ -194,20 +223,19 @@ LastDictionary::ClearCurrentDictionary(nsIEditor* aEditor)
|
||||
rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
|
||||
uri->SetAsISupports(docUri);
|
||||
nsAutoCString docUriSpec;
|
||||
rv = docUri->GetSpec(docUriSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIContentPrefService> contentPrefService =
|
||||
nsCOMPtr<nsIContentPrefService2> contentPrefService =
|
||||
do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor);
|
||||
return contentPrefService->RemovePref(uri, CPS_PREF_NAME, loadContext);
|
||||
return contentPrefService->RemoveByDomainAndName(
|
||||
NS_ConvertUTF8toUTF16(docUriSpec), CPS_PREF_NAME, loadContext, nullptr);
|
||||
}
|
||||
|
||||
LastDictionary* nsEditorSpellCheck::gDictionaryStore = nullptr;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditorSpellCheck)
|
||||
|
||||
@ -226,6 +254,7 @@ nsEditorSpellCheck::nsEditorSpellCheck()
|
||||
: mSuggestedWordIndex(0)
|
||||
, mDictionaryIndex(0)
|
||||
, mEditor(nullptr)
|
||||
, mDictionaryFetcherGroup(0)
|
||||
, mUpdateDictionaryRunning(false)
|
||||
{
|
||||
}
|
||||
@ -261,19 +290,39 @@ nsEditorSpellCheck::CanSpellCheck(bool* _retval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Instances of this class can be used as either runnables or RAII helpers.
|
||||
class CallbackCaller MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit CallbackCaller(nsIEditorSpellCheckCallback* aCallback)
|
||||
: mCallback(aCallback) {}
|
||||
|
||||
~CallbackCaller()
|
||||
{
|
||||
Run();
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mCallback) {
|
||||
mCallback->EditorSpellCheckDone();
|
||||
mCallback = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIEditorSpellCheckCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, bool aEnableSelectionChecking)
|
||||
nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, bool aEnableSelectionChecking, nsIEditorSpellCheckCallback* aCallback)
|
||||
{
|
||||
NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
|
||||
mEditor = aEditor;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
if (!gDictionaryStore) {
|
||||
gDictionaryStore = new LastDictionary();
|
||||
}
|
||||
|
||||
|
||||
// We can spell check with any editor type
|
||||
nsCOMPtr<nsITextServicesDocument>tsDoc =
|
||||
do_CreateInstance("@mozilla.org/textservices/textservicesdocument;1", &rv);
|
||||
@ -345,7 +394,17 @@ nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, bool aEnableSelectionCh
|
||||
|
||||
// do not fail if UpdateCurrentDictionary fails because this method may
|
||||
// succeed later.
|
||||
UpdateCurrentDictionary();
|
||||
rv = UpdateCurrentDictionary(aCallback);
|
||||
if (NS_FAILED(rv) && aCallback) {
|
||||
// However, if it does fail, we still need to call the callback since we
|
||||
// discard the failure. Do it asynchronously so that the caller is always
|
||||
// guaranteed async behavior.
|
||||
nsRefPtr<CallbackCaller> caller = new CallbackCaller(aCallback);
|
||||
NS_ENSURE_STATE(caller);
|
||||
rv = NS_DispatchToMainThread(caller);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -529,8 +588,14 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
|
||||
|
||||
nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
|
||||
|
||||
// The purpose of mUpdateDictionaryRunning is to avoid doing all of this if
|
||||
// UpdateCurrentDictionary's helper method DictionaryFetched, which calls us,
|
||||
// is on the stack.
|
||||
if (!mUpdateDictionaryRunning) {
|
||||
|
||||
// Ignore pending dictionary fetchers by increasing this number.
|
||||
mDictionaryFetcherGroup++;
|
||||
|
||||
nsDefaultStringComparator comparator;
|
||||
nsAutoString langCode;
|
||||
int32_t dashIdx = aDictionary.FindChar('-');
|
||||
@ -543,11 +608,11 @@ nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
|
||||
if (mPreferredLang.IsEmpty() || !nsStyleUtil::DashMatchCompare(mPreferredLang, langCode, comparator)) {
|
||||
// When user sets dictionary manually, we store this value associated
|
||||
// with editor url.
|
||||
gDictionaryStore->StoreCurrentDictionary(mEditor, aDictionary);
|
||||
StoreCurrentDictionary(mEditor, aDictionary);
|
||||
} else {
|
||||
// If user sets a dictionary matching (even partially), lang defined by
|
||||
// document, we consider content pref has been canceled, and we clear it.
|
||||
gDictionaryStore->ClearCurrentDictionary(mEditor);
|
||||
ClearCurrentDictionary(mEditor);
|
||||
}
|
||||
|
||||
// Also store it in as a preference. It will be used as a default value
|
||||
@ -613,14 +678,12 @@ nsEditorSpellCheck::DeleteSuggestedWordList()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsEditorSpellCheck::UpdateCurrentDictionary()
|
||||
nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallback)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
|
||||
|
||||
UpdateDictionnaryHolder holder(this);
|
||||
|
||||
// Get language with html5 algorithm
|
||||
nsCOMPtr<nsIContent> rootContent;
|
||||
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor);
|
||||
@ -634,27 +697,53 @@ nsEditorSpellCheck::UpdateCurrentDictionary()
|
||||
}
|
||||
NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
|
||||
|
||||
mPreferredLang.Truncate();
|
||||
rootContent->GetLang(mPreferredLang);
|
||||
DictionaryFetcher* fetcher = new DictionaryFetcher(this, aCallback,
|
||||
mDictionaryFetcherGroup);
|
||||
rootContent->GetLang(fetcher->mRootContentLang);
|
||||
nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
|
||||
NS_ENSURE_STATE(doc);
|
||||
doc->GetContentLanguage(fetcher->mRootDocContentLang);
|
||||
|
||||
// Tell the spellchecker what dictionary to use:
|
||||
rv = fetcher->Fetch(mEditor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// First try to get dictionary from content prefs. If we have one, do not got
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
|
||||
{
|
||||
nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Important: declare the holder after the callback caller so that the former
|
||||
// is destructed first so that it's not active when the callback is called.
|
||||
CallbackCaller callbackCaller(aFetcher->mCallback);
|
||||
UpdateDictionnaryHolder holder(this);
|
||||
|
||||
if (aFetcher->mGroup < mDictionaryFetcherGroup) {
|
||||
// SetCurrentDictionary was called after the fetch started. Don't overwrite
|
||||
// that dictionary with the fetched one.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPreferredLang.Assign(aFetcher->mRootContentLang);
|
||||
|
||||
// If we successfully fetched a dictionary from content prefs, do not go
|
||||
// further. Use this exact dictionary.
|
||||
nsAutoString dictName;
|
||||
rv = gDictionaryStore->FetchLastDictionary(mEditor, dictName);
|
||||
if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) {
|
||||
dictName.Assign(aFetcher->mDictionary);
|
||||
if (!dictName.IsEmpty()) {
|
||||
if (NS_FAILED(SetCurrentDictionary(dictName))) {
|
||||
// may be dictionary was uninstalled ?
|
||||
gDictionaryStore->ClearCurrentDictionary(mEditor);
|
||||
ClearCurrentDictionary(mEditor);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mPreferredLang.IsEmpty()) {
|
||||
nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
doc->GetContentLanguage(mPreferredLang);
|
||||
mPreferredLang.Assign(aFetcher->mRootDocContentLang);
|
||||
}
|
||||
|
||||
// Then, try to use language computed from element
|
||||
@ -781,8 +870,3 @@ nsEditorSpellCheck::UpdateCurrentDictionary()
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsEditorSpellCheck::ShutDown() {
|
||||
delete gDictionaryStore;
|
||||
}
|
||||
|
@ -25,10 +25,12 @@ class nsITextServicesFilter;
|
||||
{ 0x93, 0x9a, 0xec, 0x63, 0x51, 0xee, 0xa0, 0xcc }\
|
||||
}
|
||||
|
||||
class LastDictionary;
|
||||
class DictionaryFetcher;
|
||||
|
||||
class nsEditorSpellCheck : public nsIEditorSpellCheck
|
||||
{
|
||||
friend class DictionaryFetcher;
|
||||
|
||||
public:
|
||||
nsEditorSpellCheck();
|
||||
virtual ~nsEditorSpellCheck();
|
||||
@ -39,10 +41,6 @@ public:
|
||||
/* Declare all methods in the nsIEditorSpellCheck interface */
|
||||
NS_DECL_NSIEDITORSPELLCHECK
|
||||
|
||||
static LastDictionary* gDictionaryStore;
|
||||
|
||||
static void ShutDown();
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISpellChecker> mSpellChecker;
|
||||
|
||||
@ -61,8 +59,12 @@ protected:
|
||||
|
||||
nsString mPreferredLang;
|
||||
|
||||
uint32_t mDictionaryFetcherGroup;
|
||||
|
||||
bool mUpdateDictionaryRunning;
|
||||
|
||||
nsresult DictionaryFetched(DictionaryFetcher* aFetchState);
|
||||
|
||||
public:
|
||||
void BeginUpdateDictionary() { mUpdateDictionaryRunning = true ;}
|
||||
void EndUpdateDictionary() { mUpdateDictionaryRunning = false ;}
|
||||
|
@ -24,6 +24,7 @@ MOCHITEST_CHROME_FILES = \
|
||||
test_bug434998.xul \
|
||||
test_bug338427.html \
|
||||
test_bug678842.html \
|
||||
test_async_UpdateCurrentDictionary.html \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
72
editor/composer/test/test_async_UpdateCurrentDictionary.html
Normal file
72
editor/composer/test/test_async_UpdateCurrentDictionary.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=856270
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 856270 - Async UpdateCurrentDictionary</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=856270">Mozilla Bug 856270</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<textarea id="editor" spellcheck="true"></textarea>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript;version=1.8">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(start);
|
||||
|
||||
function start() {
|
||||
Components.utils.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm");
|
||||
var textarea = document.getElementById("editor");
|
||||
textarea.focus();
|
||||
|
||||
onSpellCheck(textarea, function () {
|
||||
var isc = textarea.editor.getInlineSpellChecker(false);
|
||||
ok(isc, "Inline spell checker should exist after focus and spell check");
|
||||
var sc = isc.spellChecker;
|
||||
isnot(sc.GetCurrentDictionary(), lang,
|
||||
"Current dictionary should not be set yet.");
|
||||
|
||||
// First, set the lang attribute on the textarea, call Update, and make
|
||||
// sure the spell checker's language was updated appropriately.
|
||||
var lang = "en-US";
|
||||
textarea.setAttribute("lang", lang);
|
||||
sc.UpdateCurrentDictionary(function () {
|
||||
is(sc.GetCurrentDictionary(), lang,
|
||||
"UpdateCurrentDictionary should set the current dictionary.");
|
||||
|
||||
// Second, make some Update calls, but then do a Set. The Set should
|
||||
// effectively cancel the Updates, but the Updates' callbacks should be
|
||||
// called nonetheless.
|
||||
var numCalls = 3;
|
||||
for (var i = 0; i < numCalls; i++) {
|
||||
sc.UpdateCurrentDictionary(function () {
|
||||
is(sc.GetCurrentDictionary(), "",
|
||||
"No dictionary should be active after Update.");
|
||||
if (--numCalls == 0)
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
try {
|
||||
sc.SetCurrentDictionary("testing-XX");
|
||||
}
|
||||
catch (err) {
|
||||
// Set throws NS_ERROR_NOT_AVAILABLE because "testing-XX" isn't really
|
||||
// an available dictionary.
|
||||
}
|
||||
is(sc.GetCurrentDictionary(), "",
|
||||
"No dictionary should be active after Set.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -7,8 +7,9 @@
|
||||
|
||||
interface nsIEditor;
|
||||
interface nsITextServicesFilter;
|
||||
interface nsIEditorSpellCheckCallback;
|
||||
|
||||
[scriptable, uuid(334946c3-0e93-4aac-b662-e1b56f95d68b)]
|
||||
[scriptable, uuid(dd32ef3b-a7d8-43d1-9617-5f2dddbe29eb)]
|
||||
interface nsIEditorSpellCheck : nsISupports
|
||||
{
|
||||
|
||||
@ -30,9 +31,11 @@ interface nsIEditorSpellCheck : nsISupports
|
||||
* set means that we only want to check the current selection in the editor,
|
||||
* (this controls the behavior of GetNextMisspelledWord). For spellchecking
|
||||
* clients with no modal UI (such as inline spellcheckers), this flag doesn't
|
||||
* matter
|
||||
* matter. Initialization is asynchronous and is not complete until the given
|
||||
* callback is called.
|
||||
*/
|
||||
void InitSpellChecker(in nsIEditor editor, in boolean enableSelectionChecking);
|
||||
void InitSpellChecker(in nsIEditor editor, in boolean enableSelectionChecking,
|
||||
[optional] in nsIEditorSpellCheckCallback callback);
|
||||
|
||||
/**
|
||||
* When interactively spell checking the document, this will return the
|
||||
@ -156,8 +159,15 @@ interface nsIEditorSpellCheck : nsISupports
|
||||
|
||||
/**
|
||||
* Update the dictionary in use to be sure it corresponds to what the editor
|
||||
* needs.
|
||||
* needs. The update is asynchronous and is not complete until the given
|
||||
* callback is called.
|
||||
*/
|
||||
void UpdateCurrentDictionary();
|
||||
void UpdateCurrentDictionary([optional] in nsIEditorSpellCheckCallback callback);
|
||||
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(5f0a4bab-8538-4074-89d3-2f0e866a1c0b)]
|
||||
interface nsIEditorSpellCheckCallback : nsISupports
|
||||
{
|
||||
void editorSpellCheckDone();
|
||||
};
|
||||
|
@ -10,8 +10,7 @@ interface nsISelection;
|
||||
interface nsIEditor;
|
||||
interface nsIEditorSpellCheck;
|
||||
|
||||
[scriptable, uuid(df635540-d073-47b8-8678-18776130691d)]
|
||||
|
||||
[scriptable, uuid(b7b7a77c-40c4-4196-b0b7-b0338243b3fe)]
|
||||
interface nsIInlineSpellChecker : nsISupports
|
||||
{
|
||||
readonly attribute nsIEditorSpellCheck spellChecker;
|
||||
@ -40,6 +39,8 @@ interface nsIInlineSpellChecker : nsISupports
|
||||
void ignoreWord(in AString aWord);
|
||||
void ignoreWords([array, size_is(aCount)] in wstring aWordsToIgnore, in unsigned long aCount);
|
||||
void updateCurrentDictionary();
|
||||
|
||||
readonly attribute boolean spellCheckPending;
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
@ -62,6 +62,8 @@
|
||||
#include "nsRange.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsEditor.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -84,6 +86,10 @@ using namespace mozilla::dom;
|
||||
// since this just controls how often we check the current time.
|
||||
#define MISSPELLED_WORD_COUNT_PENALTY 4
|
||||
|
||||
// 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);
|
||||
@ -455,8 +461,10 @@ mozInlineSpellStatus::PositionToCollapsedRange(nsIDOMDocument* aDocument,
|
||||
class mozInlineSpellResume : public nsRunnable
|
||||
{
|
||||
public:
|
||||
mozInlineSpellResume(const mozInlineSpellStatus& aStatus) : mStatus(aStatus) {}
|
||||
mozInlineSpellStatus mStatus;
|
||||
mozInlineSpellResume(const mozInlineSpellStatus& aStatus,
|
||||
uint32_t aDisabledAsyncToken)
|
||||
: mDisabledAsyncToken(aDisabledAsyncToken), mStatus(aStatus) {}
|
||||
|
||||
nsresult Post()
|
||||
{
|
||||
return NS_DispatchToMainThread(this);
|
||||
@ -464,9 +472,17 @@ public:
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
// Discard the resumption if the spell checker was disabled after the
|
||||
// resumption was scheduled.
|
||||
if (mDisabledAsyncToken == mStatus.mSpellChecker->mDisabledAsyncToken) {
|
||||
mStatus.mSpellChecker->ResumeCheck(&mStatus);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mDisabledAsyncToken;
|
||||
mozInlineSpellStatus mStatus;
|
||||
};
|
||||
|
||||
|
||||
@ -494,6 +510,9 @@ mozInlineSpellChecker::SpellCheckingState
|
||||
mozInlineSpellChecker::mozInlineSpellChecker() :
|
||||
mNumWordsInSpellSelection(0),
|
||||
mMaxNumWordsInSpellSelection(250),
|
||||
mNumPendingSpellChecks(0),
|
||||
mNumPendingUpdateCurrentDictionary(0),
|
||||
mDisabledAsyncToken(0),
|
||||
mNeedsCheckAfterNavigation(false),
|
||||
mFullSpellCheckScheduled(false)
|
||||
{
|
||||
@ -650,10 +669,34 @@ NS_IMETHODIMP
|
||||
mozInlineSpellChecker::GetEnableRealTimeSpell(bool* aEnabled)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aEnabled);
|
||||
*aEnabled = mSpellCheck != nullptr;
|
||||
*aEnabled = mSpellCheck != nullptr || mPendingSpellCheck != nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Used as the nsIEditorSpellCheck::InitSpellChecker callback.
|
||||
class InitEditorSpellCheckCallback MOZ_FINAL : public nsIEditorSpellCheckCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit InitEditorSpellCheckCallback(mozInlineSpellChecker* aSpellChecker)
|
||||
: mSpellChecker(aSpellChecker) {}
|
||||
|
||||
NS_IMETHOD EditorSpellCheckDone()
|
||||
{
|
||||
return mSpellChecker ? mSpellChecker->EditorSpellCheckInited() : NS_OK;
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
{
|
||||
mSpellChecker = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<mozInlineSpellChecker> mSpellChecker;
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(InitEditorSpellCheckCallback, nsIEditorSpellCheckCallback)
|
||||
|
||||
// mozInlineSpellChecker::SetEnableRealTimeSpell
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -661,26 +704,106 @@ mozInlineSpellChecker::SetEnableRealTimeSpell(bool aEnabled)
|
||||
{
|
||||
if (!aEnabled) {
|
||||
mSpellCheck = nullptr;
|
||||
return Cleanup(false);
|
||||
|
||||
// Hold on to mEditor since Cleanup nulls it out. See below.
|
||||
nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditor);
|
||||
|
||||
nsresult rv = Cleanup(false);
|
||||
|
||||
// Notify ENDED observers now. If we wait to notify as we normally do when
|
||||
// these async operations finish, then in the meantime the editor may create
|
||||
// another inline spell checker and cause more STARTED and ENDED
|
||||
// notifications to be broadcast. Interleaved notifications for the same
|
||||
// editor but different inline spell checkers could easily confuse
|
||||
// observers. They may receive two consecutive STARTED notifications for
|
||||
// example, which we guarantee will not happen. Plus, mEditor must always
|
||||
// be passed to observers. If we wait to notify, we'd have to hold on to
|
||||
// mEditor because Cleanup nulls it out.
|
||||
|
||||
if (mPendingSpellCheck) {
|
||||
// Cancel the pending editor spell checker initialization.
|
||||
mPendingSpellCheck = nullptr;
|
||||
mPendingInitEditorSpellCheckCallback->Cancel();
|
||||
mPendingInitEditorSpellCheckCallback = nullptr;
|
||||
ChangeNumPendingSpellChecks(-1, editor);
|
||||
}
|
||||
|
||||
if (!mSpellCheck) {
|
||||
nsresult res = NS_OK;
|
||||
nsCOMPtr<nsIEditorSpellCheck> spellchecker = do_CreateInstance("@mozilla.org/editor/editorspellchecker;1", &res);
|
||||
if (NS_SUCCEEDED(res) && spellchecker)
|
||||
{
|
||||
nsCOMPtr<nsITextServicesFilter> filter = do_CreateInstance("@mozilla.org/editor/txtsrvfiltermail;1", &res);
|
||||
spellchecker->SetFilter(filter);
|
||||
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
|
||||
res = spellchecker->InitSpellChecker(editor, false);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
// Increment this token so that pending UpdateCurrentDictionary calls and
|
||||
// scheduled spell checks are discarded when they finish.
|
||||
mDisabledAsyncToken++;
|
||||
|
||||
mSpellCheck = spellchecker;
|
||||
if (mNumPendingUpdateCurrentDictionary > 0) {
|
||||
// Account for pending UpdateCurrentDictionary calls.
|
||||
ChangeNumPendingSpellChecks(-mNumPendingUpdateCurrentDictionary, editor);
|
||||
mNumPendingUpdateCurrentDictionary = 0;
|
||||
}
|
||||
if (mNumPendingSpellChecks > 0) {
|
||||
// If mNumPendingSpellChecks is still > 0 at this point, the remainder is
|
||||
// pending scheduled spell checks.
|
||||
ChangeNumPendingSpellChecks(-mNumPendingSpellChecks, editor);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mSpellCheck) {
|
||||
// spellcheck the current contents. SpellCheckRange doesn't supply a created
|
||||
// range to DoSpellCheck, which in our case is the entire range. But this
|
||||
// optimization doesn't matter because there is nothing in the spellcheck
|
||||
// selection when starting, which triggers a better optimization.
|
||||
return SpellCheckRange(nullptr);
|
||||
}
|
||||
|
||||
if (mPendingSpellCheck) {
|
||||
// The editor spell checker is already being initialized.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mPendingSpellCheck =
|
||||
do_CreateInstance("@mozilla.org/editor/editorspellchecker;1");
|
||||
NS_ENSURE_STATE(mPendingSpellCheck);
|
||||
|
||||
nsCOMPtr<nsITextServicesFilter> filter =
|
||||
do_CreateInstance("@mozilla.org/editor/txtsrvfiltermail;1");
|
||||
if (!filter) {
|
||||
mPendingSpellCheck = nullptr;
|
||||
NS_ENSURE_STATE(filter);
|
||||
}
|
||||
mPendingSpellCheck->SetFilter(filter);
|
||||
|
||||
mPendingInitEditorSpellCheckCallback = new InitEditorSpellCheckCallback(this);
|
||||
if (!mPendingInitEditorSpellCheckCallback) {
|
||||
mPendingSpellCheck = nullptr;
|
||||
NS_ENSURE_STATE(mPendingInitEditorSpellCheckCallback);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEditor> editor = do_QueryReferent(mEditor);
|
||||
nsresult rv = mPendingSpellCheck->InitSpellChecker(
|
||||
editor, false, mPendingInitEditorSpellCheckCallback);
|
||||
if (NS_FAILED(rv)) {
|
||||
mPendingSpellCheck = nullptr;
|
||||
mPendingInitEditorSpellCheckCallback = nullptr;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
ChangeNumPendingSpellChecks(1);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Called when nsIEditorSpellCheck::InitSpellChecker completes.
|
||||
nsresult
|
||||
mozInlineSpellChecker::EditorSpellCheckInited()
|
||||
{
|
||||
NS_ASSERTION(mPendingSpellCheck, "Spell check should be pending!");
|
||||
|
||||
// spell checking is enabled, register our event listeners to track navigation
|
||||
RegisterEventListeners();
|
||||
}
|
||||
}
|
||||
|
||||
mSpellCheck = mPendingSpellCheck;
|
||||
mPendingSpellCheck = nullptr;
|
||||
mPendingInitEditorSpellCheckCallback = nullptr;
|
||||
ChangeNumPendingSpellChecks(-1);
|
||||
|
||||
// spellcheck the current contents. SpellCheckRange doesn't supply a created
|
||||
// range to DoSpellCheck, which in our case is the entire range. But this
|
||||
@ -689,6 +812,39 @@ mozInlineSpellChecker::SetEnableRealTimeSpell(bool aEnabled)
|
||||
return SpellCheckRange(nullptr);
|
||||
}
|
||||
|
||||
// Changes the number of pending spell checks by the given delta. If the number
|
||||
// becomes zero or nonzero, observers are notified. See NotifyObservers for
|
||||
// info on the aEditor parameter.
|
||||
void
|
||||
mozInlineSpellChecker::ChangeNumPendingSpellChecks(int32_t aDelta,
|
||||
nsIEditor* aEditor)
|
||||
{
|
||||
int8_t oldNumPending = mNumPendingSpellChecks;
|
||||
mNumPendingSpellChecks += aDelta;
|
||||
NS_ASSERTION(mNumPendingSpellChecks >= 0,
|
||||
"Unbalanced ChangeNumPendingSpellChecks calls!");
|
||||
if (oldNumPending == 0 && mNumPendingSpellChecks > 0) {
|
||||
NotifyObservers(INLINESPELL_STARTED_TOPIC, aEditor);
|
||||
} else if (oldNumPending > 0 && mNumPendingSpellChecks == 0) {
|
||||
NotifyObservers(INLINESPELL_ENDED_TOPIC, aEditor);
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcasts the given topic to observers. aEditor is passed to observers if
|
||||
// nonnull; otherwise mEditor is passed.
|
||||
void
|
||||
mozInlineSpellChecker::NotifyObservers(const char* aTopic, nsIEditor* aEditor)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (!os)
|
||||
return;
|
||||
nsCOMPtr<nsIEditor> editor = aEditor;
|
||||
if (!editor) {
|
||||
editor = do_QueryReferent(mEditor);
|
||||
}
|
||||
os->NotifyObservers(editor, aTopic, nullptr);
|
||||
}
|
||||
|
||||
// mozInlineSpellChecker::SpellCheckAfterEditorChange
|
||||
//
|
||||
// Called by the editor when nearly anything happens to change the content.
|
||||
@ -1127,17 +1283,21 @@ mozInlineSpellChecker::ScheduleSpellCheck(const mozInlineSpellStatus& aStatus)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mozInlineSpellResume* resume = new mozInlineSpellResume(aStatus);
|
||||
mozInlineSpellResume* resume =
|
||||
new mozInlineSpellResume(aStatus, mDisabledAsyncToken);
|
||||
NS_ENSURE_TRUE(resume, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = resume->Post();
|
||||
if (NS_FAILED(rv)) {
|
||||
delete resume;
|
||||
} else if (aStatus.IsFullSpellCheck()) {
|
||||
} else {
|
||||
if (aStatus.IsFullSpellCheck()) {
|
||||
// We're going to check everything. Suppress further spell-check attempts
|
||||
// until that happens.
|
||||
mFullSpellCheckScheduled = true;
|
||||
}
|
||||
ChangeNumPendingSpellChecks(1);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -1418,6 +1578,24 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// An RAII helper that calls ChangeNumPendingSpellChecks on destruction.
|
||||
class AutoChangeNumPendingSpellChecks
|
||||
{
|
||||
public:
|
||||
AutoChangeNumPendingSpellChecks(mozInlineSpellChecker* aSpellChecker,
|
||||
int32_t aDelta)
|
||||
: mSpellChecker(aSpellChecker), mDelta(aDelta) {}
|
||||
|
||||
~AutoChangeNumPendingSpellChecks()
|
||||
{
|
||||
mSpellChecker->ChangeNumPendingSpellChecks(mDelta);
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<mozInlineSpellChecker> mSpellChecker;
|
||||
int32_t mDelta;
|
||||
};
|
||||
|
||||
// mozInlineSpellChecker::ResumeCheck
|
||||
//
|
||||
// Called by the resume event when it fires. We will try to pick up where
|
||||
@ -1426,6 +1604,12 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
|
||||
nsresult
|
||||
mozInlineSpellChecker::ResumeCheck(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()) {
|
||||
// Allow posting new spellcheck resume events from inside
|
||||
// ResumeCheck, now that we're actually firing.
|
||||
@ -1761,19 +1945,63 @@ nsresult mozInlineSpellChecker::KeyPress(nsIDOMEvent* aKeyEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Used as the nsIEditorSpellCheck::UpdateCurrentDictionary callback.
|
||||
class UpdateCurrentDictionaryCallback MOZ_FINAL : public nsIEditorSpellCheckCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit UpdateCurrentDictionaryCallback(mozInlineSpellChecker* aSpellChecker,
|
||||
uint32_t aDisabledAsyncToken)
|
||||
: mSpellChecker(aSpellChecker), mDisabledAsyncToken(aDisabledAsyncToken) {}
|
||||
|
||||
NS_IMETHOD EditorSpellCheckDone()
|
||||
{
|
||||
// Ignore this callback if SetEnableRealTimeSpell(false) was called after
|
||||
// the UpdateCurrentDictionary call that triggered it.
|
||||
return mSpellChecker->mDisabledAsyncToken > mDisabledAsyncToken ?
|
||||
NS_OK :
|
||||
mSpellChecker->CurrentDictionaryUpdated();
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<mozInlineSpellChecker> mSpellChecker;
|
||||
uint32_t mDisabledAsyncToken;
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(UpdateCurrentDictionaryCallback, nsIEditorSpellCheckCallback)
|
||||
|
||||
NS_IMETHODIMP mozInlineSpellChecker::UpdateCurrentDictionary()
|
||||
{
|
||||
if (!mSpellCheck) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString previousDictionary;
|
||||
if (NS_FAILED(mSpellCheck->GetCurrentDictionary(previousDictionary))) {
|
||||
previousDictionary.Truncate();
|
||||
if (NS_FAILED(mSpellCheck->GetCurrentDictionary(mPreviousDictionary))) {
|
||||
mPreviousDictionary.Truncate();
|
||||
}
|
||||
|
||||
// This might set mSpellCheck to null (bug 793866)
|
||||
nsresult rv = mSpellCheck->UpdateCurrentDictionary();
|
||||
nsRefPtr<UpdateCurrentDictionaryCallback> cb =
|
||||
new UpdateCurrentDictionaryCallback(this, mDisabledAsyncToken);
|
||||
NS_ENSURE_STATE(cb);
|
||||
nsresult rv = mSpellCheck->UpdateCurrentDictionary(cb);
|
||||
if (NS_FAILED(rv)) {
|
||||
cb = nullptr;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
mNumPendingUpdateCurrentDictionary++;
|
||||
ChangeNumPendingSpellChecks(1);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Called when nsIEditorSpellCheck::UpdateCurrentDictionary completes.
|
||||
nsresult mozInlineSpellChecker::CurrentDictionaryUpdated()
|
||||
{
|
||||
mNumPendingUpdateCurrentDictionary--;
|
||||
NS_ASSERTION(mNumPendingUpdateCurrentDictionary >= 0,
|
||||
"CurrentDictionaryUpdated called without corresponding "
|
||||
"UpdateCurrentDictionary call!");
|
||||
ChangeNumPendingSpellChecks(-1);
|
||||
|
||||
nsAutoString currentDictionary;
|
||||
if (!mSpellCheck ||
|
||||
@ -1781,9 +2009,17 @@ NS_IMETHODIMP mozInlineSpellChecker::UpdateCurrentDictionary()
|
||||
currentDictionary.Truncate();
|
||||
}
|
||||
|
||||
if (!previousDictionary.Equals(currentDictionary)) {
|
||||
rv = SpellCheckRange(nullptr);
|
||||
if (!mPreviousDictionary.Equals(currentDictionary)) {
|
||||
nsresult rv = SpellCheckRange(nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
mozInlineSpellChecker::GetSpellCheckPending(bool* aPending)
|
||||
{
|
||||
*aPending = mNumPendingSpellChecks > 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ class nsIDOMMouseEventListener;
|
||||
class mozInlineSpellWordUtil;
|
||||
class mozInlineSpellChecker;
|
||||
class mozInlineSpellResume;
|
||||
class InitEditorSpellCheckCallback;
|
||||
class UpdateCurrentDictionaryCallback;
|
||||
class mozInlineSpellResume;
|
||||
|
||||
class mozInlineSpellStatus
|
||||
{
|
||||
@ -120,6 +123,10 @@ class mozInlineSpellChecker : public nsIInlineSpellChecker,
|
||||
{
|
||||
private:
|
||||
friend class mozInlineSpellStatus;
|
||||
friend class InitEditorSpellCheckCallback;
|
||||
friend class UpdateCurrentDictionaryCallback;
|
||||
friend class AutoChangeNumPendingSpellChecks;
|
||||
friend class mozInlineSpellResume;
|
||||
|
||||
// Access with CanEnableInlineSpellChecking
|
||||
enum SpellCheckingState { SpellCheck_Uninitialized = -1,
|
||||
@ -129,6 +136,7 @@ private:
|
||||
|
||||
nsWeakPtr mEditor;
|
||||
nsCOMPtr<nsIEditorSpellCheck> mSpellCheck;
|
||||
nsCOMPtr<nsIEditorSpellCheck> mPendingSpellCheck;
|
||||
nsCOMPtr<nsIDOMTreeWalker> mTreeWalker;
|
||||
nsCOMPtr<mozISpellI18NUtil> mConverter;
|
||||
|
||||
@ -147,6 +155,24 @@ private:
|
||||
nsCOMPtr<nsIDOMNode> mCurrentSelectionAnchorNode;
|
||||
int32_t mCurrentSelectionOffset;
|
||||
|
||||
// Tracks the number of pending spell checks *and* async operations that may
|
||||
// lead to spell checks, like updating the current dictionary. This is
|
||||
// necessary so that observers can know when to wait for spell check to
|
||||
// complete.
|
||||
int32_t mNumPendingSpellChecks;
|
||||
|
||||
// The number of calls to UpdateCurrentDictionary that haven't finished yet.
|
||||
int32_t mNumPendingUpdateCurrentDictionary;
|
||||
|
||||
// This number is incremented each time the spell checker is disabled so that
|
||||
// pending scheduled spell checks and UpdateCurrentDictionary calls can be
|
||||
// ignored when they finish.
|
||||
uint32_t mDisabledAsyncToken;
|
||||
|
||||
// When mPendingSpellCheck is non-null, this is the callback passed when
|
||||
// it was initialized.
|
||||
nsCOMPtr<InitEditorSpellCheckCallback> mPendingInitEditorSpellCheckCallback;
|
||||
|
||||
// Set when we have spellchecked after the last edit operation. See the
|
||||
// commment at the top of the .cpp file for more info.
|
||||
bool mNeedsCheckAfterNavigation;
|
||||
@ -155,6 +181,9 @@ private:
|
||||
// the whole document.
|
||||
bool mFullSpellCheckScheduled;
|
||||
|
||||
// Maintains state during the asynchronous UpdateCurrentDictionary call.
|
||||
nsString mPreviousDictionary;
|
||||
|
||||
public:
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -228,6 +257,18 @@ public:
|
||||
nsresult SaveCurrentSelectionPosition();
|
||||
|
||||
nsresult ResumeCheck(mozInlineSpellStatus* aStatus);
|
||||
|
||||
protected:
|
||||
|
||||
// called when async nsIEditorSpellCheck methods complete
|
||||
nsresult EditorSpellCheckInited();
|
||||
nsresult CurrentDictionaryUpdated();
|
||||
|
||||
// track the number of pending spell checks and async operations that may lead
|
||||
// to spell checks, notifying observers accordingly
|
||||
void ChangeNumPendingSpellChecks(int32_t aDelta,
|
||||
nsIEditor* aEditor = nullptr);
|
||||
void NotifyObservers(const char* aTopic, nsIEditor* aEditor);
|
||||
};
|
||||
|
||||
#endif /* __mozinlinespellchecker_h__ */
|
||||
|
@ -380,7 +380,6 @@ nsLayoutStatics::Shutdown()
|
||||
nsLayoutUtils::Shutdown();
|
||||
|
||||
nsHyphenationManager::Shutdown();
|
||||
nsEditorSpellCheck::ShutDown();
|
||||
nsDOMMutationObserver::Shutdown();
|
||||
|
||||
AudioChannelService::Shutdown();
|
||||
|
Loading…
Reference in New Issue
Block a user