extensions/spellcheck/src/mozSpellChecker.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5
michael@0 6 #include "mozSpellChecker.h"
michael@0 7 #include "nsIServiceManager.h"
michael@0 8 #include "mozISpellI18NManager.h"
michael@0 9 #include "nsIStringEnumerator.h"
michael@0 10 #include "nsICategoryManager.h"
michael@0 11 #include "nsISupportsPrimitives.h"
michael@0 12 #include "nsISimpleEnumerator.h"
michael@0 13
michael@0 14 #define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
michael@0 15
michael@0 16 NS_IMPL_CYCLE_COLLECTING_ADDREF(mozSpellChecker)
michael@0 17 NS_IMPL_CYCLE_COLLECTING_RELEASE(mozSpellChecker)
michael@0 18
michael@0 19 NS_INTERFACE_MAP_BEGIN(mozSpellChecker)
michael@0 20 NS_INTERFACE_MAP_ENTRY(nsISpellChecker)
michael@0 21 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISpellChecker)
michael@0 22 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozSpellChecker)
michael@0 23 NS_INTERFACE_MAP_END
michael@0 24
michael@0 25 NS_IMPL_CYCLE_COLLECTION(mozSpellChecker,
michael@0 26 mTsDoc,
michael@0 27 mPersonalDictionary)
michael@0 28
michael@0 29 mozSpellChecker::mozSpellChecker()
michael@0 30 {
michael@0 31 }
michael@0 32
michael@0 33 mozSpellChecker::~mozSpellChecker()
michael@0 34 {
michael@0 35 if(mPersonalDictionary){
michael@0 36 // mPersonalDictionary->Save();
michael@0 37 mPersonalDictionary->EndSession();
michael@0 38 }
michael@0 39 mSpellCheckingEngine = nullptr;
michael@0 40 mPersonalDictionary = nullptr;
michael@0 41 }
michael@0 42
michael@0 43 nsresult
michael@0 44 mozSpellChecker::Init()
michael@0 45 {
michael@0 46 mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
michael@0 47
michael@0 48 mSpellCheckingEngine = nullptr;
michael@0 49
michael@0 50 return NS_OK;
michael@0 51 }
michael@0 52
michael@0 53 NS_IMETHODIMP
michael@0 54 mozSpellChecker::SetDocument(nsITextServicesDocument *aDoc, bool aFromStartofDoc)
michael@0 55 {
michael@0 56 mTsDoc = aDoc;
michael@0 57 mFromStart = aFromStartofDoc;
michael@0 58 return NS_OK;
michael@0 59 }
michael@0 60
michael@0 61
michael@0 62 NS_IMETHODIMP
michael@0 63 mozSpellChecker::NextMisspelledWord(nsAString &aWord, nsTArray<nsString> *aSuggestions)
michael@0 64 {
michael@0 65 if(!aSuggestions||!mConverter)
michael@0 66 return NS_ERROR_NULL_POINTER;
michael@0 67
michael@0 68 int32_t selOffset;
michael@0 69 int32_t begin,end;
michael@0 70 nsresult result;
michael@0 71 result = SetupDoc(&selOffset);
michael@0 72 bool isMisspelled,done;
michael@0 73 if (NS_FAILED(result))
michael@0 74 return result;
michael@0 75
michael@0 76 while( NS_SUCCEEDED(mTsDoc->IsDone(&done)) && !done )
michael@0 77 {
michael@0 78 nsString str;
michael@0 79 result = mTsDoc->GetCurrentTextBlock(&str);
michael@0 80
michael@0 81 if (NS_FAILED(result))
michael@0 82 return result;
michael@0 83 do{
michael@0 84 result = mConverter->FindNextWord(str.get(),str.Length(),selOffset,&begin,&end);
michael@0 85 if(NS_SUCCEEDED(result)&&(begin != -1)){
michael@0 86 const nsAString &currWord = Substring(str, begin, end - begin);
michael@0 87 result = CheckWord(currWord, &isMisspelled, aSuggestions);
michael@0 88 if(isMisspelled){
michael@0 89 aWord = currWord;
michael@0 90 mTsDoc->SetSelection(begin, end-begin);
michael@0 91 // After ScrollSelectionIntoView(), the pending notifications might
michael@0 92 // be flushed and PresShell/PresContext/Frames may be dead.
michael@0 93 // See bug 418470.
michael@0 94 mTsDoc->ScrollSelectionIntoView();
michael@0 95 return NS_OK;
michael@0 96 }
michael@0 97 }
michael@0 98 selOffset = end;
michael@0 99 }while(end != -1);
michael@0 100 mTsDoc->NextBlock();
michael@0 101 selOffset=0;
michael@0 102 }
michael@0 103 return NS_OK;
michael@0 104 }
michael@0 105
michael@0 106 NS_IMETHODIMP
michael@0 107 mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled, nsTArray<nsString> *aSuggestions)
michael@0 108 {
michael@0 109 nsresult result;
michael@0 110 bool correct;
michael@0 111 if(!mSpellCheckingEngine)
michael@0 112 return NS_ERROR_NULL_POINTER;
michael@0 113
michael@0 114 *aIsMisspelled = false;
michael@0 115 result = mSpellCheckingEngine->Check(PromiseFlatString(aWord).get(), &correct);
michael@0 116 NS_ENSURE_SUCCESS(result, result);
michael@0 117 if(!correct){
michael@0 118 if(aSuggestions){
michael@0 119 uint32_t count,i;
michael@0 120 char16_t **words;
michael@0 121
michael@0 122 result = mSpellCheckingEngine->Suggest(PromiseFlatString(aWord).get(), &words, &count);
michael@0 123 NS_ENSURE_SUCCESS(result, result);
michael@0 124 for(i=0;i<count;i++){
michael@0 125 aSuggestions->AppendElement(nsDependentString(words[i]));
michael@0 126 }
michael@0 127
michael@0 128 if (count)
michael@0 129 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words);
michael@0 130 }
michael@0 131 *aIsMisspelled = true;
michael@0 132 }
michael@0 133 return NS_OK;
michael@0 134 }
michael@0 135
michael@0 136 NS_IMETHODIMP
michael@0 137 mozSpellChecker::Replace(const nsAString &aOldWord, const nsAString &aNewWord, bool aAllOccurrences)
michael@0 138 {
michael@0 139 if(!mConverter)
michael@0 140 return NS_ERROR_NULL_POINTER;
michael@0 141
michael@0 142 nsAutoString newWord(aNewWord); // sigh
michael@0 143
michael@0 144 if(aAllOccurrences){
michael@0 145 int32_t selOffset;
michael@0 146 int32_t startBlock,currentBlock,currOffset;
michael@0 147 int32_t begin,end;
michael@0 148 bool done;
michael@0 149 nsresult result;
michael@0 150 nsAutoString str;
michael@0 151
michael@0 152 // find out where we are
michael@0 153 result = SetupDoc(&selOffset);
michael@0 154 if(NS_FAILED(result))
michael@0 155 return result;
michael@0 156 result = GetCurrentBlockIndex(mTsDoc,&startBlock);
michael@0 157 if(NS_FAILED(result))
michael@0 158 return result;
michael@0 159
michael@0 160 //start at the beginning
michael@0 161 result = mTsDoc->FirstBlock();
michael@0 162 currOffset=0;
michael@0 163 currentBlock = 0;
michael@0 164 while( NS_SUCCEEDED(mTsDoc->IsDone(&done)) && !done )
michael@0 165 {
michael@0 166 result = mTsDoc->GetCurrentTextBlock(&str);
michael@0 167 do{
michael@0 168 result = mConverter->FindNextWord(str.get(),str.Length(),currOffset,&begin,&end);
michael@0 169 if(NS_SUCCEEDED(result)&&(begin != -1)){
michael@0 170 if (aOldWord.Equals(Substring(str, begin, end-begin))) {
michael@0 171 // if we are before the current selection point but in the same block
michael@0 172 // move the selection point forwards
michael@0 173 if((currentBlock == startBlock)&&(begin < selOffset)){
michael@0 174 selOffset +=
michael@0 175 int32_t(aNewWord.Length()) - int32_t(aOldWord.Length());
michael@0 176 if(selOffset < begin) selOffset=begin;
michael@0 177 }
michael@0 178 mTsDoc->SetSelection(begin, end-begin);
michael@0 179 mTsDoc->InsertText(&newWord);
michael@0 180 mTsDoc->GetCurrentTextBlock(&str);
michael@0 181 end += (aNewWord.Length() - aOldWord.Length()); // recursion was cute in GEB, not here.
michael@0 182 }
michael@0 183 }
michael@0 184 currOffset = end;
michael@0 185 }while(currOffset != -1);
michael@0 186 mTsDoc->NextBlock();
michael@0 187 currentBlock++;
michael@0 188 currOffset=0;
michael@0 189 }
michael@0 190
michael@0 191 // We are done replacing. Put the selection point back where we found it (or equivalent);
michael@0 192 result = mTsDoc->FirstBlock();
michael@0 193 currentBlock = 0;
michael@0 194 while(( NS_SUCCEEDED(mTsDoc->IsDone(&done)) && !done ) &&(currentBlock < startBlock)){
michael@0 195 mTsDoc->NextBlock();
michael@0 196 }
michael@0 197
michael@0 198 //After we have moved to the block where the first occurrence of replace was done, put the
michael@0 199 //selection to the next word following it. In case there is no word following it i.e if it happens
michael@0 200 //to be the last word in that block, then move to the next block and put the selection to the
michael@0 201 //first word in that block, otherwise when the Setupdoc() is called, it queries the LastSelectedBlock()
michael@0 202 //and the selection offset of the last occurrence of the replaced word is taken instead of the first
michael@0 203 //occurrence and things get messed up as reported in the bug 244969
michael@0 204
michael@0 205 if( NS_SUCCEEDED(mTsDoc->IsDone(&done)) && !done ){
michael@0 206 nsString str;
michael@0 207 result = mTsDoc->GetCurrentTextBlock(&str);
michael@0 208 result = mConverter->FindNextWord(str.get(),str.Length(),selOffset,&begin,&end);
michael@0 209 if(end == -1)
michael@0 210 {
michael@0 211 mTsDoc->NextBlock();
michael@0 212 selOffset=0;
michael@0 213 result = mTsDoc->GetCurrentTextBlock(&str);
michael@0 214 result = mConverter->FindNextWord(str.get(),str.Length(),selOffset,&begin,&end);
michael@0 215 mTsDoc->SetSelection(begin, 0);
michael@0 216 }
michael@0 217 else
michael@0 218 mTsDoc->SetSelection(begin, 0);
michael@0 219 }
michael@0 220 }
michael@0 221 else{
michael@0 222 mTsDoc->InsertText(&newWord);
michael@0 223 }
michael@0 224 return NS_OK;
michael@0 225 }
michael@0 226
michael@0 227 NS_IMETHODIMP
michael@0 228 mozSpellChecker::IgnoreAll(const nsAString &aWord)
michael@0 229 {
michael@0 230 if(mPersonalDictionary){
michael@0 231 mPersonalDictionary->IgnoreWord(PromiseFlatString(aWord).get());
michael@0 232 }
michael@0 233 return NS_OK;
michael@0 234 }
michael@0 235
michael@0 236 NS_IMETHODIMP
michael@0 237 mozSpellChecker::AddWordToPersonalDictionary(const nsAString &aWord)
michael@0 238 {
michael@0 239 nsresult res;
michael@0 240 char16_t empty=0;
michael@0 241 if (!mPersonalDictionary)
michael@0 242 return NS_ERROR_NULL_POINTER;
michael@0 243 res = mPersonalDictionary->AddWord(PromiseFlatString(aWord).get(),&empty);
michael@0 244 return res;
michael@0 245 }
michael@0 246
michael@0 247 NS_IMETHODIMP
michael@0 248 mozSpellChecker::RemoveWordFromPersonalDictionary(const nsAString &aWord)
michael@0 249 {
michael@0 250 nsresult res;
michael@0 251 char16_t empty=0;
michael@0 252 if (!mPersonalDictionary)
michael@0 253 return NS_ERROR_NULL_POINTER;
michael@0 254 res = mPersonalDictionary->RemoveWord(PromiseFlatString(aWord).get(),&empty);
michael@0 255 return res;
michael@0 256 }
michael@0 257
michael@0 258 NS_IMETHODIMP
michael@0 259 mozSpellChecker::GetPersonalDictionary(nsTArray<nsString> *aWordList)
michael@0 260 {
michael@0 261 if(!aWordList || !mPersonalDictionary)
michael@0 262 return NS_ERROR_NULL_POINTER;
michael@0 263
michael@0 264 nsCOMPtr<nsIStringEnumerator> words;
michael@0 265 mPersonalDictionary->GetWordList(getter_AddRefs(words));
michael@0 266
michael@0 267 bool hasMore;
michael@0 268 nsAutoString word;
michael@0 269 while (NS_SUCCEEDED(words->HasMore(&hasMore)) && hasMore) {
michael@0 270 words->GetNext(word);
michael@0 271 aWordList->AppendElement(word);
michael@0 272 }
michael@0 273 return NS_OK;
michael@0 274 }
michael@0 275
michael@0 276 NS_IMETHODIMP
michael@0 277 mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList)
michael@0 278 {
michael@0 279 nsresult rv;
michael@0 280
michael@0 281 // For catching duplicates
michael@0 282 nsClassHashtable<nsStringHashKey, nsCString> dictionaries;
michael@0 283
michael@0 284 nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines;
michael@0 285 rv = GetEngineList(&spellCheckingEngines);
michael@0 286 NS_ENSURE_SUCCESS(rv, rv);
michael@0 287
michael@0 288 for (int32_t i = 0; i < spellCheckingEngines.Count(); i++) {
michael@0 289 nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i];
michael@0 290
michael@0 291 uint32_t count = 0;
michael@0 292 char16_t **words = nullptr;
michael@0 293 engine->GetDictionaryList(&words, &count);
michael@0 294 for (uint32_t k = 0; k < count; k++) {
michael@0 295 nsAutoString dictName;
michael@0 296
michael@0 297 dictName.Assign(words[k]);
michael@0 298
michael@0 299 // Skip duplicate dictionaries. Only take the first one
michael@0 300 // for each name.
michael@0 301 if (dictionaries.Get(dictName, nullptr))
michael@0 302 continue;
michael@0 303
michael@0 304 dictionaries.Put(dictName, nullptr);
michael@0 305
michael@0 306 if (!aDictionaryList->AppendElement(dictName)) {
michael@0 307 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words);
michael@0 308 return NS_ERROR_OUT_OF_MEMORY;
michael@0 309 }
michael@0 310 }
michael@0 311
michael@0 312 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words);
michael@0 313 }
michael@0 314
michael@0 315 return NS_OK;
michael@0 316 }
michael@0 317
michael@0 318 NS_IMETHODIMP
michael@0 319 mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
michael@0 320 {
michael@0 321 if (!mSpellCheckingEngine) {
michael@0 322 aDictionary.AssignLiteral("");
michael@0 323 return NS_OK;
michael@0 324 }
michael@0 325
michael@0 326 nsXPIDLString dictname;
michael@0 327 mSpellCheckingEngine->GetDictionary(getter_Copies(dictname));
michael@0 328 aDictionary = dictname;
michael@0 329 return NS_OK;
michael@0 330 }
michael@0 331
michael@0 332 NS_IMETHODIMP
michael@0 333 mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
michael@0 334 {
michael@0 335 // Calls to mozISpellCheckingEngine::SetDictionary might destroy us
michael@0 336 nsRefPtr<mozSpellChecker> kungFuDeathGrip = this;
michael@0 337
michael@0 338 mSpellCheckingEngine = nullptr;
michael@0 339
michael@0 340 if (aDictionary.IsEmpty()) {
michael@0 341 return NS_OK;
michael@0 342 }
michael@0 343
michael@0 344 nsresult rv;
michael@0 345 nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines;
michael@0 346 rv = GetEngineList(&spellCheckingEngines);
michael@0 347 NS_ENSURE_SUCCESS(rv, rv);
michael@0 348
michael@0 349 for (int32_t i = 0; i < spellCheckingEngines.Count(); i++) {
michael@0 350 // We must set mSpellCheckingEngine before we call SetDictionary, since
michael@0 351 // SetDictionary calls back to this spell checker to check if the
michael@0 352 // dictionary was set
michael@0 353 mSpellCheckingEngine = spellCheckingEngines[i];
michael@0 354
michael@0 355 rv = mSpellCheckingEngine->SetDictionary(PromiseFlatString(aDictionary).get());
michael@0 356
michael@0 357 if (NS_SUCCEEDED(rv)) {
michael@0 358 nsCOMPtr<mozIPersonalDictionary> personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
michael@0 359 mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get());
michael@0 360
michael@0 361 nsXPIDLString language;
michael@0 362 nsCOMPtr<mozISpellI18NManager> serv(do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &rv));
michael@0 363 NS_ENSURE_SUCCESS(rv, rv);
michael@0 364 return serv->GetUtil(language.get(),getter_AddRefs(mConverter));
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 mSpellCheckingEngine = nullptr;
michael@0 369
michael@0 370 // We could not find any engine with the requested dictionary
michael@0 371 return NS_ERROR_NOT_AVAILABLE;
michael@0 372 }
michael@0 373
michael@0 374 NS_IMETHODIMP
michael@0 375 mozSpellChecker::CheckCurrentDictionary()
michael@0 376 {
michael@0 377 // If the current dictionary has been uninstalled, we need to stop using it.
michael@0 378 // This happens when there is a current engine, but that engine has no
michael@0 379 // current dictionary.
michael@0 380
michael@0 381 if (!mSpellCheckingEngine) {
michael@0 382 // We didn't have a current dictionary
michael@0 383 return NS_OK;
michael@0 384 }
michael@0 385
michael@0 386 nsXPIDLString dictname;
michael@0 387 mSpellCheckingEngine->GetDictionary(getter_Copies(dictname));
michael@0 388
michael@0 389 if (!dictname.IsEmpty()) {
michael@0 390 // We still have a current dictionary
michael@0 391 return NS_OK;
michael@0 392 }
michael@0 393
michael@0 394 // We had a current dictionary, but it has gone, so we cannot use it anymore.
michael@0 395 mSpellCheckingEngine = nullptr;
michael@0 396 return NS_OK;
michael@0 397 }
michael@0 398
michael@0 399 nsresult
michael@0 400 mozSpellChecker::SetupDoc(int32_t *outBlockOffset)
michael@0 401 {
michael@0 402 nsresult rv;
michael@0 403
michael@0 404 nsITextServicesDocument::TSDBlockSelectionStatus blockStatus;
michael@0 405 int32_t selOffset;
michael@0 406 int32_t selLength;
michael@0 407 *outBlockOffset = 0;
michael@0 408
michael@0 409 if (!mFromStart)
michael@0 410 {
michael@0 411 rv = mTsDoc->LastSelectedBlock(&blockStatus, &selOffset, &selLength);
michael@0 412 if (NS_SUCCEEDED(rv) && (blockStatus != nsITextServicesDocument::eBlockNotFound))
michael@0 413 {
michael@0 414 switch (blockStatus)
michael@0 415 {
michael@0 416 case nsITextServicesDocument::eBlockOutside: // No TB in S, but found one before/after S.
michael@0 417 case nsITextServicesDocument::eBlockPartial: // S begins or ends in TB but extends outside of TB.
michael@0 418 // the TS doc points to the block we want.
michael@0 419 *outBlockOffset = selOffset + selLength;
michael@0 420 break;
michael@0 421
michael@0 422 case nsITextServicesDocument::eBlockInside: // S extends beyond the start and end of TB.
michael@0 423 // we want the block after this one.
michael@0 424 rv = mTsDoc->NextBlock();
michael@0 425 *outBlockOffset = 0;
michael@0 426 break;
michael@0 427
michael@0 428 case nsITextServicesDocument::eBlockContains: // TB contains entire S.
michael@0 429 *outBlockOffset = selOffset + selLength;
michael@0 430 break;
michael@0 431
michael@0 432 case nsITextServicesDocument::eBlockNotFound: // There is no text block (TB) in or before the selection (S).
michael@0 433 default:
michael@0 434 NS_NOTREACHED("Shouldn't ever get this status");
michael@0 435 }
michael@0 436 }
michael@0 437 else //failed to get last sel block. Just start at beginning
michael@0 438 {
michael@0 439 rv = mTsDoc->FirstBlock();
michael@0 440 *outBlockOffset = 0;
michael@0 441 }
michael@0 442
michael@0 443 }
michael@0 444 else // we want the first block
michael@0 445 {
michael@0 446 rv = mTsDoc->FirstBlock();
michael@0 447 mFromStart = false;
michael@0 448 }
michael@0 449 return rv;
michael@0 450 }
michael@0 451
michael@0 452
michael@0 453 // utility method to discover which block we're in. The TSDoc interface doesn't give
michael@0 454 // us this, because it can't assume a read-only document.
michael@0 455 // shamelessly stolen from nsTextServicesDocument
michael@0 456 nsresult
michael@0 457 mozSpellChecker::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *outBlockIndex)
michael@0 458 {
michael@0 459 int32_t blockIndex = 0;
michael@0 460 bool isDone = false;
michael@0 461 nsresult result = NS_OK;
michael@0 462
michael@0 463 do
michael@0 464 {
michael@0 465 aDoc->PrevBlock();
michael@0 466
michael@0 467 result = aDoc->IsDone(&isDone);
michael@0 468
michael@0 469 if (!isDone)
michael@0 470 blockIndex ++;
michael@0 471
michael@0 472 } while (NS_SUCCEEDED(result) && !isDone);
michael@0 473
michael@0 474 *outBlockIndex = blockIndex;
michael@0 475
michael@0 476 return result;
michael@0 477 }
michael@0 478
michael@0 479 nsresult
michael@0 480 mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellCheckingEngines)
michael@0 481 {
michael@0 482 nsresult rv;
michael@0 483 bool hasMoreEngines;
michael@0 484
michael@0 485 nsCOMPtr<nsICategoryManager> catMgr = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
michael@0 486 if (!catMgr)
michael@0 487 return NS_ERROR_NULL_POINTER;
michael@0 488
michael@0 489 nsCOMPtr<nsISimpleEnumerator> catEntries;
michael@0 490
michael@0 491 // Get contract IDs of registrated external spell-check engines and
michael@0 492 // append one of HunSpell at the end.
michael@0 493 rv = catMgr->EnumerateCategory("spell-check-engine", getter_AddRefs(catEntries));
michael@0 494 if (NS_FAILED(rv))
michael@0 495 return rv;
michael@0 496
michael@0 497 while (catEntries->HasMoreElements(&hasMoreEngines), hasMoreEngines){
michael@0 498 nsCOMPtr<nsISupports> elem;
michael@0 499 rv = catEntries->GetNext(getter_AddRefs(elem));
michael@0 500
michael@0 501 nsCOMPtr<nsISupportsCString> entry = do_QueryInterface(elem, &rv);
michael@0 502 if (NS_FAILED(rv))
michael@0 503 return rv;
michael@0 504
michael@0 505 nsCString contractId;
michael@0 506 rv = entry->GetData(contractId);
michael@0 507 if (NS_FAILED(rv))
michael@0 508 return rv;
michael@0 509
michael@0 510 // Try to load spellchecker engine. Ignore errors silently
michael@0 511 // except for the last one (HunSpell).
michael@0 512 nsCOMPtr<mozISpellCheckingEngine> engine =
michael@0 513 do_GetService(contractId.get(), &rv);
michael@0 514 if (NS_SUCCEEDED(rv)) {
michael@0 515 aSpellCheckingEngines->AppendObject(engine);
michael@0 516 }
michael@0 517 }
michael@0 518
michael@0 519 // Try to load HunSpell spellchecker engine.
michael@0 520 nsCOMPtr<mozISpellCheckingEngine> engine =
michael@0 521 do_GetService(DEFAULT_SPELL_CHECKER, &rv);
michael@0 522 if (NS_FAILED(rv)) {
michael@0 523 // Fail if not succeeded to load HunSpell. Ignore errors
michael@0 524 // for external spellcheck engines.
michael@0 525 return rv;
michael@0 526 }
michael@0 527 aSpellCheckingEngines->AppendObject(engine);
michael@0 528
michael@0 529 return NS_OK;
michael@0 530 }

mercurial