/* vim: set ts=2 sts=2 sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using mozilla::AssertedCast; using mozilla::GenericPromise; using mozilla::LogLevel; using mozilla::RemoteSpellcheckEngineChild; using mozilla::TextServicesDocument; using mozilla::dom::ContentChild;
if (mEngine) {
MOZ_ASSERT(XRE_IsContentProcess());
RemoteSpellcheckEngineChild::Send__delete__(mEngine);
MOZ_ASSERT(!mEngine);
}
}
nsresult mozSpellChecker::Init() {
mSpellCheckingEngine = nullptr; if (XRE_IsContentProcess()) {
mozilla::dom::ContentChild* contentChild =
mozilla::dom::ContentChild::GetSingleton();
MOZ_ASSERT(contentChild); // mEngine is nulled in RemoteSpellcheckEngineChild(), so we don't need to // worry about SendPRemoveSpellcheckEngineConstructor failing
mEngine = new RemoteSpellcheckEngineChild(this);
MOZ_ALWAYS_TRUE(
contentChild->SendPRemoteSpellcheckEngineConstructor(mEngine));
} else {
mPersonalDictionary =
do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
}
nsTArray<bool> misspells;
misspells.SetCapacity(aWords.Length()); for (auto& word : aWords) { bool misspelled;
nsresult rv = CheckWord(word, &misspelled, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { return mozilla::CheckWordPromise::CreateAndReject(rv, __func__);
}
misspells.AppendElement(misspelled);
} return mozilla::CheckWordPromise::CreateAndResolve(std::move(misspells),
__func__);
}
nsresult mozSpellChecker::CheckWord(const nsAString& aWord, bool* aIsMisspelled,
nsTArray<nsString>* aSuggestions) { if (XRE_IsContentProcess()) { // Use async version (CheckWords or Suggest) on content process return NS_ERROR_FAILURE;
}
nsresult result; bool correct;
if (!mSpellCheckingEngine) { return NS_ERROR_NULL_POINTER;
}
*aIsMisspelled = false;
result = mSpellCheckingEngine->Check(aWord, &correct);
NS_ENSURE_SUCCESS(result, result); if (!correct) { if (aSuggestions) {
result = mSpellCheckingEngine->Suggest(aWord, *aSuggestions);
NS_ENSURE_SUCCESS(result, result);
}
*aIsMisspelled = true;
} return NS_OK;
}
// find out where we are
result = SetupDoc(&selOffset); if (NS_WARN_IF(NS_FAILED(result))) { return result;
}
result = GetCurrentBlockIndex(mTextServicesDocument, &startBlock); if (NS_WARN_IF(NS_FAILED(result))) { return result;
}
// start at the beginning
result = mTextServicesDocument->FirstBlock(); if (NS_WARN_IF(NS_FAILED(result))) { return result;
}
int32_t currOffset = 0;
int32_t currentBlock = 0;
int32_t wordLengthDifference =
AssertedCast<int32_t>(static_cast<int64_t>(aNewWord.Length()) - static_cast<int64_t>(aOldWord.Length())); while (NS_SUCCEEDED(mTextServicesDocument->IsDone(&done)) && !done) {
nsAutoString str;
mTextServicesDocument->GetCurrentTextBlock(str); while (mConverter->FindNextWord(str, currOffset, &begin, &end)) { if (aOldWord.Equals(Substring(str, begin, end - begin))) { // if we are before the current selection point but in the same // block move the selection point forwards if (currentBlock == startBlock && begin < selOffset) {
selOffset += wordLengthDifference; if (selOffset < begin) {
selOffset = begin;
}
} // Don't keep running if selecting or inserting text fails because // it may cause infinite loop. if (NS_WARN_IF(NS_FAILED(
MOZ_KnownLive(mTextServicesDocument)
->SetSelection(AssertedCast<uint32_t>(begin),
AssertedCast<uint32_t>(end - begin))))) { return NS_ERROR_FAILURE;
} if (NS_WARN_IF(NS_FAILED(
MOZ_KnownLive(mTextServicesDocument)->InsertText(aNewWord)))) { return NS_ERROR_FAILURE;
}
mTextServicesDocument->GetCurrentTextBlock(str);
end += wordLengthDifference; // recursion was cute in GEB, not here.
}
currOffset = end;
}
mTextServicesDocument->NextBlock();
currentBlock++;
currOffset = 0;
}
// We are done replacing. Put the selection point back where we found it // (or equivalent);
result = mTextServicesDocument->FirstBlock(); if (NS_WARN_IF(NS_FAILED(result))) { return result;
}
currentBlock = 0; while (NS_SUCCEEDED(mTextServicesDocument->IsDone(&done)) && !done &&
currentBlock < startBlock) {
mTextServicesDocument->NextBlock();
}
// After we have moved to the block where the first occurrence of replace // was done, put the selection to the next word following it. In case there // is no word following it i.e if it happens to be the last word in that // block, then move to the next block and put the selection to the first // word in that block, otherwise when the Setupdoc() is called, it queries // the LastSelectedBlock() and the selection offset of the last occurrence // of the replaced word is taken instead of the first occurrence and things // get messed up as reported in the bug 244969
for (int32_t i = 0; i < spellCheckingEngines.Count(); i++) {
nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i];
nsTArray<nsCString> dictNames;
engine->GetDictionaryList(dictNames); for (auto& dictName : dictNames) { // Skip duplicate dictionaries. Only take the first one // for each name. if (!dictionaries.EnsureInserted(dictName)) continue;
nsTArray<nsCString> dictionaries;
dictionaries.AppendElement(aDictionary); for (int32_t i = 0; i < spellCheckingEngines.Count(); i++) { // We must set mSpellCheckingEngine before we call SetDictionaries, since // SetDictionaries calls back to this spell checker to check if the // dictionary was set
mSpellCheckingEngine = spellCheckingEngines[i];
rv = mSpellCheckingEngine->SetDictionaries(dictionaries);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<mozIPersonalDictionary> personalDictionary =
do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
mSpellCheckingEngine->SetPersonalDictionary(personalDictionary);
mConverter = new mozEnglishWordUtils; return NS_OK;
}
}
mSpellCheckingEngine = nullptr;
// We could not find any engine with the requested dictionary return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<GenericPromise> mozSpellChecker::SetCurrentDictionaries( const nsTArray<nsCString>& aDictionaries) { if (XRE_IsContentProcess()) { if (!mEngine) {
mCurrentDictionaries.Clear(); return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
}
// mCurrentDictionaries will be set by RemoteSpellCheckEngineChild return mEngine->SetCurrentDictionaries(aDictionaries);
}
// Calls to mozISpellCheckingEngine::SetDictionary might destroy us
RefPtr<mozSpellChecker> kungFuDeathGrip = this;
mSpellCheckingEngine = nullptr;
if (aDictionaries.IsEmpty()) { return GenericPromise::CreateAndResolve(true, __func__);
}
for (int32_t i = 0; i < spellCheckingEngines.Count(); i++) { // We must set mSpellCheckingEngine before we call SetDictionaries, since // SetDictionaries calls back to this spell checker to check if the // dictionary was set
mSpellCheckingEngine = spellCheckingEngines[i];
rv = mSpellCheckingEngine->SetDictionaries(aDictionaries);
if (NS_SUCCEEDED(rv)) {
mCurrentDictionaries = aDictionaries.Clone();
if (XRE_IsContentProcess()) { if (!mEngine) {
mCurrentDictionaries.Clear(); return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
}
// mCurrentDictionaries will be set by RemoteSpellCheckEngineChild return mEngine->SetCurrentDictionaryFromList(aList);
}
for (auto& dictionary : aList) {
nsresult rv = SetCurrentDictionary(dictionary); if (NS_SUCCEEDED(rv)) { return GenericPromise::CreateAndResolve(true, __func__);
}
} // We could not find any engine with the requested dictionary return GenericPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
}
if (!mFromStart) {
uint32_t selOffset, selLength;
rv = MOZ_KnownLive(mTextServicesDocument)
->LastSelectedBlock(&blockStatus, &selOffset, &selLength); if (NS_SUCCEEDED(rv) &&
blockStatus !=
TextServicesDocument::BlockSelectionStatus::eBlockNotFound) { switch (blockStatus) { // No TB in S, but found one before/after S. case TextServicesDocument::BlockSelectionStatus::eBlockOutside: // S begins or ends in TB but extends outside of TB. case TextServicesDocument::BlockSelectionStatus::eBlockPartial: // the TS doc points to the block we want. if (NS_WARN_IF(selOffset == UINT32_MAX) ||
NS_WARN_IF(selLength == UINT32_MAX)) {
rv = mTextServicesDocument->FirstBlock();
*outBlockOffset = 0; break;
}
*outBlockOffset = AssertedCast<int32_t>(selOffset + selLength); break;
// S extends beyond the start and end of TB. case TextServicesDocument::BlockSelectionStatus::eBlockInside: // we want the block after this one.
rv = mTextServicesDocument->NextBlock();
*outBlockOffset = 0; break;
// TB contains entire S. case TextServicesDocument::BlockSelectionStatus::eBlockContains: if (NS_WARN_IF(selOffset == UINT32_MAX) ||
NS_WARN_IF(selLength == UINT32_MAX)) {
rv = mTextServicesDocument->FirstBlock();
*outBlockOffset = 0; break;
}
*outBlockOffset = AssertedCast<int32_t>(selOffset + selLength); break;
// There is no text block (TB) in or before the selection (S). case TextServicesDocument::BlockSelectionStatus::eBlockNotFound: default:
MOZ_ASSERT_UNREACHABLE("Shouldn't ever get this status");
}
} // Failed to get last sel block. Just start at beginning else {
rv = mTextServicesDocument->FirstBlock();
*outBlockOffset = 0;
}
} // We want the first block else {
rv = mTextServicesDocument->FirstBlock();
mFromStart = false;
} return rv;
}
// utility method to discover which block we're in. The TSDoc interface doesn't // give us this, because it can't assume a read-only document. shamelessly // stolen from nsTextServicesDocument
nsresult mozSpellChecker::GetCurrentBlockIndex(
TextServicesDocument* aTextServicesDocument, int32_t* aOutBlockIndex) {
int32_t blockIndex = 0; bool isDone = false;
nsresult result = NS_OK;
do {
aTextServicesDocument->PrevBlock();
result = aTextServicesDocument->IsDone(&isDone); if (!isDone) {
blockIndex++;
}
} while (NS_SUCCEEDED(result) && !isDone);
nsCOMPtr<nsICategoryManager> catMgr =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID); if (!catMgr) return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsISimpleEnumerator> catEntries;
// Get contract IDs of registrated external spell-check engines and // append one of HunSpell at the end.
rv = catMgr->EnumerateCategory("spell-check-engine",
getter_AddRefs(catEntries)); if (NS_FAILED(rv)) return rv;
while (NS_SUCCEEDED(catEntries->HasMoreElements(&hasMoreEngines)) &&
hasMoreEngines) {
nsCOMPtr<nsISupports> elem;
rv = catEntries->GetNext(getter_AddRefs(elem));
nsCOMPtr<nsISupportsCString> entry = do_QueryInterface(elem, &rv); if (NS_FAILED(rv)) return rv;
nsCString contractId;
rv = entry->GetData(contractId); if (NS_FAILED(rv)) return rv;
// Try to load spellchecker engine. Ignore errors silently // except for the last one (HunSpell).
nsCOMPtr<mozISpellCheckingEngine> engine =
do_GetService(contractId.get(), &rv); if (NS_SUCCEEDED(rv)) {
aSpellCheckingEngines->AppendObject(engine);
}
}
// Try to load HunSpell spellchecker engine.
nsCOMPtr<mozISpellCheckingEngine> engine =
do_GetService(DEFAULT_SPELL_CHECKER, &rv); if (NS_FAILED(rv)) { // Fail if not succeeded to load HunSpell. Ignore errors // for external spellcheck engines. return rv;
}
aSpellCheckingEngines->AppendObject(engine);
return NS_OK;
}
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.