1 : /******* BEGIN LICENSE BLOCK *******
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
15 : * and László Németh (Hunspell). Portions created by the Initial Developers
16 : * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
17 : *
18 : * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca)
19 : * David Einstein (deinst@world.std.com)
20 : * Michiel van Leeuwen (mvl@exedo.nl)
21 : * Caolan McNamara (cmc@openoffice.org)
22 : * László Németh (nemethl@gyorsposta.hu)
23 : * Davide Prina
24 : * Giuseppe Modugno
25 : * Gianluca Turconi
26 : * Simon Brouwer
27 : * Noll Janos
28 : * Biro Arpad
29 : * Goldman Eleonora
30 : * Sarlos Tamas
31 : * Bencsath Boldizsar
32 : * Halacsy Peter
33 : * Dvornik Laszlo
34 : * Gefferth Andras
35 : * Nagy Viktor
36 : * Varga Daniel
37 : * Chris Halls
38 : * Rene Engelhard
39 : * Bram Moolenaar
40 : * Dafydd Jones
41 : * Harri Pitkanen
42 : * Andras Timar
43 : * Tor Lillqvist
44 : * Jesper Kristensen (mail@jesperkristensen.dk)
45 : *
46 : * Alternatively, the contents of this file may be used under the terms of
47 : * either the GNU General Public License Version 2 or later (the "GPL"), or
48 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
49 : * in which case the provisions of the GPL or the LGPL are applicable instead
50 : * of those above. If you wish to allow use of your version of this file only
51 : * under the terms of either the GPL or the LGPL, and not to allow others to
52 : * use your version of this file under the terms of the MPL, indicate your
53 : * decision by deleting the provisions above and replace them with the notice
54 : * and other provisions required by the GPL or the LGPL. If you do not delete
55 : * the provisions above, a recipient may use your version of this file under
56 : * the terms of any one of the MPL, the GPL or the LGPL.
57 : *
58 : ******* END LICENSE BLOCK *******/
59 :
60 : #include "mozHunspell.h"
61 : #include "nsReadableUtils.h"
62 : #include "nsXPIDLString.h"
63 : #include "nsIObserverService.h"
64 : #include "nsISimpleEnumerator.h"
65 : #include "nsIDirectoryEnumerator.h"
66 : #include "nsIFile.h"
67 : #include "nsDirectoryServiceUtils.h"
68 : #include "nsDirectoryServiceDefs.h"
69 : #include "mozISpellI18NManager.h"
70 : #include "nsICharsetConverterManager.h"
71 : #include "nsUnicharUtilCIID.h"
72 : #include "nsUnicharUtils.h"
73 : #include "nsCRT.h"
74 : #include "mozInlineSpellChecker.h"
75 : #include "mozilla/Services.h"
76 : #include <stdlib.h>
77 : #include "nsIMemoryReporter.h"
78 :
79 : static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
80 : static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID);
81 :
82 11 : NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
83 11 : NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
84 :
85 22 : NS_INTERFACE_MAP_BEGIN(mozHunspell)
86 22 : NS_INTERFACE_MAP_ENTRY(mozISpellCheckingEngine)
87 19 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
88 19 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
89 17 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozISpellCheckingEngine)
90 14 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
91 8 : NS_INTERFACE_MAP_END
92 :
93 1464 : NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
94 : mPersonalDictionary,
95 : mEncoder,
96 : mDecoder)
97 :
98 : // Memory reporting stuff.
99 : static PRInt64 gHunspellAllocatedSize = 0;
100 :
101 5007 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(HunspellMallocSizeOfForCounterInc, "hunspell")
102 5015 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN_UN(HunspellMallocSizeOfForCounterDec)
103 :
104 5007 : void HunspellReportMemoryAllocation(void* ptr) {
105 5007 : gHunspellAllocatedSize += HunspellMallocSizeOfForCounterInc(ptr);
106 5007 : }
107 5015 : void HunspellReportMemoryDeallocation(void* ptr) {
108 5015 : gHunspellAllocatedSize -= HunspellMallocSizeOfForCounterDec(ptr);
109 5015 : }
110 0 : static PRInt64 HunspellGetCurrentAllocatedSize() {
111 0 : return gHunspellAllocatedSize;
112 : }
113 :
114 1 : NS_MEMORY_REPORTER_IMPLEMENT(Hunspell,
115 : "explicit/spell-check",
116 : KIND_HEAP,
117 : UNITS_BYTES,
118 : HunspellGetCurrentAllocatedSize,
119 : "Memory used by the Hunspell spell checking engine. This number accounts "
120 : "for the memory in use by Hunspell's internal data structures."
121 3 : )
122 :
123 : nsresult
124 1 : mozHunspell::Init()
125 : {
126 1 : if (!mDictionaries.Init())
127 0 : return NS_ERROR_OUT_OF_MEMORY;
128 :
129 1 : LoadDictionaryList();
130 :
131 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
132 1 : if (obs) {
133 1 : obs->AddObserver(this, "profile-do-change", true);
134 1 : obs->AddObserver(this, "profile-after-change", true);
135 : }
136 :
137 1 : mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell);
138 1 : NS_RegisterMemoryReporter(mHunspellReporter);
139 :
140 1 : return NS_OK;
141 : }
142 :
143 3 : mozHunspell::~mozHunspell()
144 : {
145 1 : mPersonalDictionary = nsnull;
146 1 : delete mHunspell;
147 :
148 1 : NS_UnregisterMemoryReporter(mHunspellReporter);
149 4 : }
150 :
151 : /* attribute wstring dictionary; */
152 0 : NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary)
153 : {
154 0 : NS_ENSURE_ARG_POINTER(aDictionary);
155 :
156 0 : *aDictionary = ToNewUnicode(mDictionary);
157 0 : return *aDictionary ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
158 : }
159 :
160 : /* set the Dictionary.
161 : * This also Loads the dictionary and initializes the converter using the dictionaries converter
162 : */
163 110 : NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary)
164 : {
165 110 : NS_ENSURE_ARG_POINTER(aDictionary);
166 :
167 110 : if (nsDependentString(aDictionary).IsEmpty()) {
168 0 : delete mHunspell;
169 0 : mHunspell = nsnull;
170 0 : mDictionary.AssignLiteral("");
171 0 : mAffixFileName.AssignLiteral("");
172 0 : mLanguage.AssignLiteral("");
173 0 : mDecoder = nsnull;
174 0 : mEncoder = nsnull;
175 :
176 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
177 0 : if (obs) {
178 0 : obs->NotifyObservers(nsnull,
179 : SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
180 0 : nsnull);
181 : }
182 0 : return NS_OK;
183 : }
184 :
185 110 : nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary));
186 110 : if (!affFile)
187 0 : return NS_ERROR_FILE_NOT_FOUND;
188 :
189 220 : nsCAutoString dictFileName, affFileName;
190 :
191 : // XXX This isn't really good. nsIFile->NativePath isn't safe for all
192 : // character sets on Windows.
193 : // A better way would be to QI to nsILocalFile, and get a filehandle
194 : // from there. Only problem is that hunspell wants a path
195 :
196 110 : nsresult rv = affFile->GetNativePath(affFileName);
197 110 : NS_ENSURE_SUCCESS(rv, rv);
198 :
199 110 : if (mAffixFileName.Equals(affFileName.get()))
200 0 : return NS_OK;
201 :
202 110 : dictFileName = affFileName;
203 110 : PRInt32 dotPos = dictFileName.RFindChar('.');
204 110 : if (dotPos == -1)
205 0 : return NS_ERROR_FAILURE;
206 :
207 110 : dictFileName.SetLength(dotPos);
208 110 : dictFileName.AppendLiteral(".dic");
209 :
210 : // SetDictionary can be called multiple times, so we might have a
211 : // valid mHunspell instance which needs cleaned up.
212 110 : delete mHunspell;
213 :
214 110 : mDictionary = aDictionary;
215 110 : mAffixFileName = affFileName;
216 :
217 : mHunspell = new Hunspell(affFileName.get(),
218 220 : dictFileName.get());
219 110 : if (!mHunspell)
220 0 : return NS_ERROR_OUT_OF_MEMORY;
221 :
222 : nsCOMPtr<nsICharsetConverterManager> ccm =
223 220 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
224 110 : NS_ENSURE_SUCCESS(rv, rv);
225 :
226 220 : rv = ccm->GetUnicodeDecoder(mHunspell->get_dic_encoding(),
227 220 : getter_AddRefs(mDecoder));
228 110 : NS_ENSURE_SUCCESS(rv, rv);
229 :
230 220 : rv = ccm->GetUnicodeEncoder(mHunspell->get_dic_encoding(),
231 220 : getter_AddRefs(mEncoder));
232 110 : NS_ENSURE_SUCCESS(rv, rv);
233 :
234 :
235 110 : if (mEncoder)
236 110 : mEncoder->SetOutputErrorBehavior(mEncoder->kOnError_Signal, nsnull, '?');
237 :
238 110 : PRInt32 pos = mDictionary.FindChar('-');
239 110 : if (pos == -1)
240 110 : pos = mDictionary.FindChar('_');
241 :
242 110 : if (pos == -1)
243 96 : mLanguage.Assign(mDictionary);
244 : else
245 14 : mLanguage = Substring(mDictionary, 0, pos);
246 :
247 220 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
248 110 : if (obs) {
249 110 : obs->NotifyObservers(nsnull,
250 : SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION,
251 110 : nsnull);
252 : }
253 :
254 110 : return NS_OK;
255 : }
256 :
257 : /* readonly attribute wstring language; */
258 0 : NS_IMETHODIMP mozHunspell::GetLanguage(PRUnichar **aLanguage)
259 : {
260 0 : NS_ENSURE_ARG_POINTER(aLanguage);
261 :
262 0 : if (mDictionary.IsEmpty())
263 0 : return NS_ERROR_NOT_INITIALIZED;
264 :
265 0 : *aLanguage = ToNewUnicode(mLanguage);
266 0 : return *aLanguage ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
267 : }
268 :
269 : /* readonly attribute boolean providesPersonalDictionary; */
270 0 : NS_IMETHODIMP mozHunspell::GetProvidesPersonalDictionary(bool *aProvidesPersonalDictionary)
271 : {
272 0 : NS_ENSURE_ARG_POINTER(aProvidesPersonalDictionary);
273 :
274 0 : *aProvidesPersonalDictionary = false;
275 0 : return NS_OK;
276 : }
277 :
278 : /* readonly attribute boolean providesWordUtils; */
279 0 : NS_IMETHODIMP mozHunspell::GetProvidesWordUtils(bool *aProvidesWordUtils)
280 : {
281 0 : NS_ENSURE_ARG_POINTER(aProvidesWordUtils);
282 :
283 0 : *aProvidesWordUtils = false;
284 0 : return NS_OK;
285 : }
286 :
287 : /* readonly attribute wstring name; */
288 0 : NS_IMETHODIMP mozHunspell::GetName(PRUnichar * *aName)
289 : {
290 0 : return NS_ERROR_NOT_IMPLEMENTED;
291 : }
292 :
293 : /* readonly attribute wstring copyright; */
294 0 : NS_IMETHODIMP mozHunspell::GetCopyright(PRUnichar * *aCopyright)
295 : {
296 0 : return NS_ERROR_NOT_IMPLEMENTED;
297 : }
298 :
299 : /* attribute mozIPersonalDictionary personalDictionary; */
300 0 : NS_IMETHODIMP mozHunspell::GetPersonalDictionary(mozIPersonalDictionary * *aPersonalDictionary)
301 : {
302 0 : *aPersonalDictionary = mPersonalDictionary;
303 0 : NS_IF_ADDREF(*aPersonalDictionary);
304 0 : return NS_OK;
305 : }
306 :
307 0 : NS_IMETHODIMP mozHunspell::SetPersonalDictionary(mozIPersonalDictionary * aPersonalDictionary)
308 : {
309 0 : mPersonalDictionary = aPersonalDictionary;
310 0 : return NS_OK;
311 : }
312 :
313 : struct AppendNewStruct
314 : {
315 : PRUnichar **dics;
316 : PRUint32 count;
317 : bool failed;
318 : };
319 :
320 : static PLDHashOperator
321 0 : AppendNewString(const nsAString& aString, nsIFile* aFile, void* aClosure)
322 : {
323 0 : AppendNewStruct *ans = (AppendNewStruct*) aClosure;
324 0 : ans->dics[ans->count] = ToNewUnicode(aString);
325 0 : if (!ans->dics[ans->count]) {
326 0 : ans->failed = true;
327 0 : return PL_DHASH_STOP;
328 : }
329 :
330 0 : ++ans->count;
331 0 : return PL_DHASH_NEXT;
332 : }
333 :
334 : /* void GetDictionaryList ([array, size_is (count)] out wstring dictionaries, out PRUint32 count); */
335 0 : NS_IMETHODIMP mozHunspell::GetDictionaryList(PRUnichar ***aDictionaries,
336 : PRUint32 *aCount)
337 : {
338 0 : if (!aDictionaries || !aCount)
339 0 : return NS_ERROR_NULL_POINTER;
340 :
341 : AppendNewStruct ans = {
342 0 : (PRUnichar**) NS_Alloc(sizeof(PRUnichar*) * mDictionaries.Count()),
343 : 0,
344 : false
345 0 : };
346 :
347 : // This pointer is used during enumeration
348 0 : mDictionaries.EnumerateRead(AppendNewString, &ans);
349 :
350 0 : if (ans.failed) {
351 0 : while (ans.count) {
352 0 : --ans.count;
353 0 : NS_Free(ans.dics[ans.count]);
354 : }
355 0 : NS_Free(ans.dics);
356 0 : return NS_ERROR_OUT_OF_MEMORY;
357 : }
358 :
359 0 : *aDictionaries = ans.dics;
360 0 : *aCount = ans.count;
361 :
362 0 : return NS_OK;
363 : }
364 :
365 : void
366 1 : mozHunspell::LoadDictionaryList()
367 : {
368 1 : mDictionaries.Clear();
369 :
370 : nsresult rv;
371 :
372 : nsCOMPtr<nsIProperties> dirSvc =
373 2 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
374 1 : if (!dirSvc)
375 : return;
376 :
377 : // find built in dictionaries
378 2 : nsCOMPtr<nsIFile> dictDir;
379 1 : rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY,
380 1 : NS_GET_IID(nsIFile), getter_AddRefs(dictDir));
381 1 : if (NS_SUCCEEDED(rv)) {
382 0 : LoadDictionariesFromDir(dictDir);
383 : }
384 : else {
385 : // try to load gredir/dictionaries
386 2 : nsCOMPtr<nsIFile> greDir;
387 1 : rv = dirSvc->Get(NS_GRE_DIR,
388 1 : NS_GET_IID(nsIFile), getter_AddRefs(greDir));
389 1 : if (NS_SUCCEEDED(rv)) {
390 1 : greDir->AppendNative(NS_LITERAL_CSTRING("dictionaries"));
391 1 : LoadDictionariesFromDir(greDir);
392 : }
393 :
394 : // try to load appdir/dictionaries only if different than gredir
395 2 : nsCOMPtr<nsIFile> appDir;
396 1 : rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
397 1 : NS_GET_IID(nsIFile), getter_AddRefs(appDir));
398 : bool equals;
399 1 : if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) {
400 1 : appDir->AppendNative(NS_LITERAL_CSTRING("dictionaries"));
401 1 : LoadDictionariesFromDir(appDir);
402 : }
403 : }
404 :
405 : // find dictionaries from extensions requiring restart
406 2 : nsCOMPtr<nsISimpleEnumerator> dictDirs;
407 1 : rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY_LIST,
408 1 : NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dictDirs));
409 1 : if (NS_FAILED(rv))
410 : return;
411 :
412 : bool hasMore;
413 0 : while (NS_SUCCEEDED(dictDirs->HasMoreElements(&hasMore)) && hasMore) {
414 0 : nsCOMPtr<nsISupports> elem;
415 0 : dictDirs->GetNext(getter_AddRefs(elem));
416 :
417 0 : dictDir = do_QueryInterface(elem);
418 0 : if (dictDir)
419 0 : LoadDictionariesFromDir(dictDir);
420 : }
421 :
422 : // find dictionaries from restartless extensions
423 0 : for (PRUint32 i = 0; i < mDynamicDirectories.Count(); i++) {
424 0 : LoadDictionariesFromDir(mDynamicDirectories[i]);
425 : }
426 :
427 : // Now we have finished updating the list of dictionaries, update the current
428 : // dictionary and any editors which may use it.
429 0 : mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
430 :
431 : // Check if the current dictionary is still available.
432 : // If not, try to replace it with another dictionary of the same language.
433 0 : if (!mDictionary.IsEmpty()) {
434 0 : rv = SetDictionary(mDictionary.get());
435 0 : if (NS_SUCCEEDED(rv))
436 : return;
437 : }
438 :
439 : // If the current dictionary has gone, and we don't have a good replacement,
440 : // set no current dictionary.
441 0 : if (!mDictionary.IsEmpty()) {
442 0 : SetDictionary(EmptyString().get());
443 : }
444 : }
445 :
446 : NS_IMETHODIMP
447 3 : mozHunspell::LoadDictionariesFromDir(nsIFile* aDir)
448 : {
449 : nsresult rv;
450 :
451 3 : bool check = false;
452 3 : rv = aDir->Exists(&check);
453 3 : if (NS_FAILED(rv) || !check)
454 0 : return NS_ERROR_UNEXPECTED;
455 :
456 3 : rv = aDir->IsDirectory(&check);
457 3 : if (NS_FAILED(rv) || !check)
458 0 : return NS_ERROR_UNEXPECTED;
459 :
460 6 : nsCOMPtr<nsISimpleEnumerator> e;
461 3 : rv = aDir->GetDirectoryEntries(getter_AddRefs(e));
462 3 : if (NS_FAILED(rv))
463 0 : return NS_ERROR_UNEXPECTED;
464 :
465 6 : nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e));
466 3 : if (!files)
467 0 : return NS_ERROR_UNEXPECTED;
468 :
469 6 : nsCOMPtr<nsIFile> file;
470 571 : while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) {
471 1130 : nsAutoString leafName;
472 565 : file->GetLeafName(leafName);
473 565 : if (!StringEndsWith(leafName, NS_LITERAL_STRING(".dic")))
474 451 : continue;
475 :
476 228 : nsAutoString dict(leafName);
477 114 : dict.SetLength(dict.Length() - 4); // magic length of ".dic"
478 :
479 : // check for the presence of the .aff file
480 114 : leafName = dict;
481 114 : leafName.AppendLiteral(".aff");
482 114 : file->SetLeafName(leafName);
483 114 : rv = file->Exists(&check);
484 114 : if (NS_FAILED(rv) || !check)
485 0 : continue;
486 :
487 : #ifdef DEBUG_bsmedberg
488 : printf("Adding dictionary: %s\n", NS_ConvertUTF16toUTF8(dict).get());
489 : #endif
490 :
491 228 : mDictionaries.Put(dict, file);
492 : }
493 :
494 3 : return NS_OK;
495 : }
496 :
497 1030 : nsresult mozHunspell::ConvertCharset(const PRUnichar* aStr, char ** aDst)
498 : {
499 1030 : NS_ENSURE_ARG_POINTER(aDst);
500 1030 : NS_ENSURE_TRUE(mEncoder, NS_ERROR_NULL_POINTER);
501 :
502 : PRInt32 outLength;
503 1030 : PRInt32 inLength = nsCRT::strlen(aStr);
504 1030 : nsresult rv = mEncoder->GetMaxLength(aStr, inLength, &outLength);
505 1030 : NS_ENSURE_SUCCESS(rv, rv);
506 :
507 1030 : *aDst = (char *) nsMemory::Alloc(sizeof(char) * (outLength+1));
508 1030 : NS_ENSURE_TRUE(*aDst, NS_ERROR_OUT_OF_MEMORY);
509 :
510 1030 : rv = mEncoder->Convert(aStr, &inLength, *aDst, &outLength);
511 1030 : if (NS_SUCCEEDED(rv))
512 1030 : (*aDst)[outLength] = '\0';
513 :
514 1030 : return rv;
515 : }
516 :
517 : /* boolean Check (in wstring word); */
518 1030 : NS_IMETHODIMP mozHunspell::Check(const PRUnichar *aWord, bool *aResult)
519 : {
520 1030 : NS_ENSURE_ARG_POINTER(aWord);
521 1030 : NS_ENSURE_ARG_POINTER(aResult);
522 1030 : NS_ENSURE_TRUE(mHunspell, NS_ERROR_FAILURE);
523 :
524 2060 : nsXPIDLCString charsetWord;
525 1030 : nsresult rv = ConvertCharset(aWord, getter_Copies(charsetWord));
526 1030 : NS_ENSURE_SUCCESS(rv, rv);
527 :
528 1030 : *aResult = !!mHunspell->spell(charsetWord);
529 :
530 :
531 1030 : if (!*aResult && mPersonalDictionary)
532 0 : rv = mPersonalDictionary->Check(aWord, mLanguage.get(), aResult);
533 :
534 1030 : return rv;
535 : }
536 :
537 : /* void Suggest (in wstring word, [array, size_is (count)] out wstring suggestions, out PRUint32 count); */
538 0 : NS_IMETHODIMP mozHunspell::Suggest(const PRUnichar *aWord, PRUnichar ***aSuggestions, PRUint32 *aSuggestionCount)
539 : {
540 0 : NS_ENSURE_ARG_POINTER(aSuggestions);
541 0 : NS_ENSURE_ARG_POINTER(aSuggestionCount);
542 0 : NS_ENSURE_TRUE(mHunspell, NS_ERROR_FAILURE);
543 :
544 : nsresult rv;
545 0 : *aSuggestionCount = 0;
546 :
547 0 : nsXPIDLCString charsetWord;
548 0 : rv = ConvertCharset(aWord, getter_Copies(charsetWord));
549 0 : NS_ENSURE_SUCCESS(rv, rv);
550 :
551 : char ** wlst;
552 0 : *aSuggestionCount = mHunspell->suggest(&wlst, charsetWord);
553 :
554 0 : if (*aSuggestionCount) {
555 0 : *aSuggestions = (PRUnichar **)nsMemory::Alloc(*aSuggestionCount * sizeof(PRUnichar *));
556 0 : if (*aSuggestions) {
557 0 : PRUint32 index = 0;
558 0 : for (index = 0; index < *aSuggestionCount && NS_SUCCEEDED(rv); ++index) {
559 : // Convert the suggestion to utf16
560 0 : PRInt32 inLength = nsCRT::strlen(wlst[index]);
561 : PRInt32 outLength;
562 0 : rv = mDecoder->GetMaxLength(wlst[index], inLength, &outLength);
563 0 : if (NS_SUCCEEDED(rv))
564 : {
565 0 : (*aSuggestions)[index] = (PRUnichar *) nsMemory::Alloc(sizeof(PRUnichar) * (outLength+1));
566 0 : if ((*aSuggestions)[index])
567 : {
568 0 : rv = mDecoder->Convert(wlst[index], &inLength, (*aSuggestions)[index], &outLength);
569 0 : if (NS_SUCCEEDED(rv))
570 0 : (*aSuggestions)[index][outLength] = 0;
571 : }
572 : else
573 0 : rv = NS_ERROR_OUT_OF_MEMORY;
574 : }
575 : }
576 :
577 0 : if (NS_FAILED(rv))
578 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, *aSuggestions); // free the PRUnichar strings up to the point at which the error occurred
579 : }
580 : else // if (*aSuggestions)
581 0 : rv = NS_ERROR_OUT_OF_MEMORY;
582 : }
583 :
584 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(*aSuggestionCount, wlst);
585 0 : return rv;
586 : }
587 :
588 : NS_IMETHODIMP
589 0 : mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
590 : const PRUnichar *aData)
591 : {
592 0 : NS_ASSERTION(!strcmp(aTopic, "profile-do-change")
593 : || !strcmp(aTopic, "profile-after-change"),
594 : "Unexpected observer topic");
595 :
596 0 : LoadDictionaryList();
597 :
598 0 : return NS_OK;
599 : }
600 :
601 : /* void addDirectory(in nsIFile dir); */
602 0 : NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
603 : {
604 0 : mDynamicDirectories.AppendObject(aDir);
605 0 : LoadDictionaryList();
606 0 : return NS_OK;
607 : }
608 :
609 : /* void removeDirectory(in nsIFile dir); */
610 0 : NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir)
611 : {
612 0 : mDynamicDirectories.RemoveObject(aDir);
613 0 : LoadDictionaryList();
614 0 : return NS_OK;
615 4392 : }
|