Line data Source code
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 "nsUnicharUtils.h"
71 : #include "nsCRT.h"
72 : #include "mozInlineSpellChecker.h"
73 : #include "mozilla/Services.h"
74 : #include <stdlib.h>
75 : #include "nsIPrefService.h"
76 : #include "nsIPrefBranch.h"
77 : #include "mozilla/dom/ContentParent.h"
78 :
79 : using mozilla::dom::ContentParent;
80 : using namespace mozilla;
81 :
82 14 : NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell)
83 13 : NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell)
84 :
85 9 : NS_INTERFACE_MAP_BEGIN(mozHunspell)
86 9 : NS_INTERFACE_MAP_ENTRY(mozISpellCheckingEngine)
87 5 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
88 5 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
89 3 : NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter)
90 3 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozISpellCheckingEngine)
91 2 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
92 1 : NS_INTERFACE_MAP_END
93 :
94 0 : NS_IMPL_CYCLE_COLLECTION(mozHunspell, mPersonalDictionary)
95 :
96 : template<> mozilla::Atomic<size_t> mozilla::CountingAllocatorBase<HunspellAllocator>::sAmount(0);
97 :
98 1 : mozHunspell::mozHunspell()
99 1 : : mHunspell(nullptr)
100 : {
101 : #ifdef DEBUG
102 : // There must be only one instance of this class: it reports memory based on
103 : // a single static count in HunspellAllocator.
104 : static bool hasRun = false;
105 1 : MOZ_ASSERT(!hasRun);
106 1 : hasRun = true;
107 : #endif
108 1 : }
109 :
110 : nsresult
111 1 : mozHunspell::Init()
112 : {
113 1 : LoadDictionaryList(false);
114 :
115 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
116 1 : if (obs) {
117 1 : obs->AddObserver(this, "profile-do-change", true);
118 1 : obs->AddObserver(this, "profile-after-change", true);
119 : }
120 :
121 1 : mozilla::RegisterWeakMemoryReporter(this);
122 :
123 2 : return NS_OK;
124 : }
125 :
126 0 : mozHunspell::~mozHunspell()
127 : {
128 0 : mozilla::UnregisterWeakMemoryReporter(this);
129 :
130 0 : mPersonalDictionary = nullptr;
131 0 : delete mHunspell;
132 0 : }
133 :
134 0 : NS_IMETHODIMP mozHunspell::GetDictionary(char16_t **aDictionary)
135 : {
136 0 : NS_ENSURE_ARG_POINTER(aDictionary);
137 :
138 0 : *aDictionary = ToNewUnicode(mDictionary);
139 0 : return *aDictionary ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
140 : }
141 :
142 : /* set the Dictionary.
143 : * This also Loads the dictionary and initializes the converter using the dictionaries converter
144 : */
145 0 : NS_IMETHODIMP mozHunspell::SetDictionary(const char16_t *aDictionary)
146 : {
147 0 : NS_ENSURE_ARG_POINTER(aDictionary);
148 :
149 0 : if (nsDependentString(aDictionary).IsEmpty()) {
150 0 : delete mHunspell;
151 0 : mHunspell = nullptr;
152 0 : mDictionary.Truncate();
153 0 : mAffixFileName.Truncate();
154 0 : mLanguage.Truncate();
155 0 : mDecoder = nullptr;
156 0 : mEncoder = nullptr;
157 :
158 0 : return NS_OK;
159 : }
160 :
161 0 : nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary));
162 0 : if (!affFile)
163 0 : return NS_ERROR_FILE_NOT_FOUND;
164 :
165 0 : nsAutoCString dictFileName, affFileName;
166 :
167 : // XXX This isn't really good. nsIFile->NativePath isn't safe for all
168 : // character sets on Windows.
169 : // A better way would be to QI to nsIFile, and get a filehandle
170 : // from there. Only problem is that hunspell wants a path
171 :
172 0 : nsresult rv = affFile->GetNativePath(affFileName);
173 0 : NS_ENSURE_SUCCESS(rv, rv);
174 :
175 0 : if (mAffixFileName.Equals(affFileName.get()))
176 0 : return NS_OK;
177 :
178 0 : dictFileName = affFileName;
179 0 : int32_t dotPos = dictFileName.RFindChar('.');
180 0 : if (dotPos == -1)
181 0 : return NS_ERROR_FAILURE;
182 :
183 0 : dictFileName.SetLength(dotPos);
184 0 : dictFileName.AppendLiteral(".dic");
185 :
186 : // SetDictionary can be called multiple times, so we might have a
187 : // valid mHunspell instance which needs cleaned up.
188 0 : delete mHunspell;
189 :
190 0 : mDictionary = aDictionary;
191 0 : mAffixFileName = affFileName;
192 :
193 0 : mHunspell = new Hunspell(affFileName.get(),
194 0 : dictFileName.get());
195 0 : if (!mHunspell)
196 0 : return NS_ERROR_OUT_OF_MEMORY;
197 :
198 : auto encoding =
199 0 : Encoding::ForLabelNoReplacement(mHunspell->get_dict_encoding());
200 0 : if (!encoding) {
201 0 : return NS_ERROR_UCONV_NOCONV;
202 : }
203 0 : mEncoder = encoding->NewEncoder();
204 0 : mDecoder = encoding->NewDecoderWithoutBOMHandling();
205 :
206 0 : int32_t pos = mDictionary.FindChar('-');
207 0 : if (pos == -1)
208 0 : pos = mDictionary.FindChar('_');
209 :
210 0 : if (pos == -1)
211 0 : mLanguage.Assign(mDictionary);
212 : else
213 0 : mLanguage = Substring(mDictionary, 0, pos);
214 :
215 0 : return NS_OK;
216 : }
217 :
218 0 : NS_IMETHODIMP mozHunspell::GetLanguage(char16_t **aLanguage)
219 : {
220 0 : NS_ENSURE_ARG_POINTER(aLanguage);
221 :
222 0 : if (mDictionary.IsEmpty())
223 0 : return NS_ERROR_NOT_INITIALIZED;
224 :
225 0 : *aLanguage = ToNewUnicode(mLanguage);
226 0 : return *aLanguage ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
227 : }
228 :
229 0 : NS_IMETHODIMP mozHunspell::GetProvidesPersonalDictionary(bool *aProvidesPersonalDictionary)
230 : {
231 0 : NS_ENSURE_ARG_POINTER(aProvidesPersonalDictionary);
232 :
233 0 : *aProvidesPersonalDictionary = false;
234 0 : return NS_OK;
235 : }
236 :
237 0 : NS_IMETHODIMP mozHunspell::GetProvidesWordUtils(bool *aProvidesWordUtils)
238 : {
239 0 : NS_ENSURE_ARG_POINTER(aProvidesWordUtils);
240 :
241 0 : *aProvidesWordUtils = false;
242 0 : return NS_OK;
243 : }
244 :
245 0 : NS_IMETHODIMP mozHunspell::GetName(char16_t * *aName)
246 : {
247 0 : return NS_ERROR_NOT_IMPLEMENTED;
248 : }
249 :
250 0 : NS_IMETHODIMP mozHunspell::GetCopyright(char16_t * *aCopyright)
251 : {
252 0 : return NS_ERROR_NOT_IMPLEMENTED;
253 : }
254 :
255 0 : NS_IMETHODIMP mozHunspell::GetPersonalDictionary(mozIPersonalDictionary * *aPersonalDictionary)
256 : {
257 0 : *aPersonalDictionary = mPersonalDictionary;
258 0 : NS_IF_ADDREF(*aPersonalDictionary);
259 0 : return NS_OK;
260 : }
261 :
262 0 : NS_IMETHODIMP mozHunspell::SetPersonalDictionary(mozIPersonalDictionary * aPersonalDictionary)
263 : {
264 0 : mPersonalDictionary = aPersonalDictionary;
265 0 : return NS_OK;
266 : }
267 :
268 2 : NS_IMETHODIMP mozHunspell::GetDictionaryList(char16_t ***aDictionaries,
269 : uint32_t *aCount)
270 : {
271 2 : if (!aDictionaries || !aCount)
272 0 : return NS_ERROR_NULL_POINTER;
273 :
274 2 : uint32_t count = 0;
275 : char16_t** dicts =
276 2 : (char16_t**) moz_xmalloc(sizeof(char16_t*) * mDictionaries.Count());
277 :
278 4 : for (auto iter = mDictionaries.Iter(); !iter.Done(); iter.Next()) {
279 2 : dicts[count] = ToNewUnicode(iter.Key());
280 2 : if (!dicts[count]) {
281 0 : while (count) {
282 0 : --count;
283 0 : free(dicts[count]);
284 : }
285 0 : free(dicts);
286 0 : return NS_ERROR_OUT_OF_MEMORY;
287 : }
288 :
289 2 : ++count;
290 : }
291 :
292 2 : *aDictionaries = dicts;
293 2 : *aCount = count;
294 :
295 2 : return NS_OK;
296 : }
297 :
298 : void
299 1 : mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses)
300 : {
301 1 : mDictionaries.Clear();
302 :
303 : nsresult rv;
304 :
305 : nsCOMPtr<nsIProperties> dirSvc =
306 2 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
307 1 : if (!dirSvc)
308 0 : return;
309 :
310 : // find built in dictionaries, or dictionaries specified in
311 : // spellchecker.dictionary_path in prefs
312 2 : nsCOMPtr<nsIFile> dictDir;
313 :
314 : // check preferences first
315 2 : nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
316 1 : if (prefs) {
317 2 : nsCString extDictPath;
318 1 : rv = prefs->GetCharPref("spellchecker.dictionary_path", getter_Copies(extDictPath));
319 1 : if (NS_SUCCEEDED(rv)) {
320 : // set the spellchecker.dictionary_path
321 0 : rv = NS_NewNativeLocalFile(extDictPath, true, getter_AddRefs(dictDir));
322 : }
323 : }
324 1 : if (!dictDir) {
325 : // spellcheck.dictionary_path not found, set internal path
326 2 : rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY,
327 2 : NS_GET_IID(nsIFile), getter_AddRefs(dictDir));
328 : }
329 1 : if (dictDir) {
330 0 : LoadDictionariesFromDir(dictDir);
331 : }
332 : else {
333 : // try to load gredir/dictionaries
334 2 : nsCOMPtr<nsIFile> greDir;
335 2 : rv = dirSvc->Get(NS_GRE_DIR,
336 2 : NS_GET_IID(nsIFile), getter_AddRefs(greDir));
337 1 : if (NS_SUCCEEDED(rv)) {
338 1 : greDir->AppendNative(NS_LITERAL_CSTRING("dictionaries"));
339 1 : LoadDictionariesFromDir(greDir);
340 : }
341 :
342 : // try to load appdir/dictionaries only if different than gredir
343 2 : nsCOMPtr<nsIFile> appDir;
344 2 : rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
345 2 : NS_GET_IID(nsIFile), getter_AddRefs(appDir));
346 : bool equals;
347 1 : if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) {
348 1 : appDir->AppendNative(NS_LITERAL_CSTRING("dictionaries"));
349 1 : LoadDictionariesFromDir(appDir);
350 : }
351 : }
352 :
353 : // find dictionaries in DICPATH
354 1 : char* dicEnv = PR_GetEnv("DICPATH");
355 1 : if (dicEnv) {
356 : // do a two-pass dance so dictionaries are loaded right-to-left as preference
357 0 : nsTArray<nsCOMPtr<nsIFile>> dirs;
358 0 : nsAutoCString env(dicEnv); // assume dicEnv is UTF-8
359 :
360 0 : char* currPath = nullptr;
361 0 : char* nextPaths = env.BeginWriting();
362 0 : while ((currPath = NS_strtok(":", &nextPaths))) {
363 0 : nsCOMPtr<nsIFile> dir;
364 0 : rv = NS_NewNativeLocalFile(nsCString(currPath), true, getter_AddRefs(dir));
365 0 : if (NS_SUCCEEDED(rv)) {
366 0 : dirs.AppendElement(dir);
367 : }
368 : }
369 :
370 : // load them in reverse order so they override each other properly
371 0 : for (int32_t i = dirs.Length() - 1; i >= 0; i--) {
372 0 : LoadDictionariesFromDir(dirs[i]);
373 : }
374 : }
375 :
376 : // find dictionaries from extensions requiring restart
377 2 : nsCOMPtr<nsISimpleEnumerator> dictDirs;
378 2 : rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY_LIST,
379 2 : NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dictDirs));
380 1 : if (NS_FAILED(rv))
381 0 : return;
382 :
383 : bool hasMore;
384 1 : while (NS_SUCCEEDED(dictDirs->HasMoreElements(&hasMore)) && hasMore) {
385 0 : nsCOMPtr<nsISupports> elem;
386 0 : dictDirs->GetNext(getter_AddRefs(elem));
387 :
388 0 : dictDir = do_QueryInterface(elem);
389 0 : if (dictDir)
390 0 : LoadDictionariesFromDir(dictDir);
391 : }
392 :
393 : // find dictionaries from restartless extensions
394 1 : for (int32_t i = 0; i < mDynamicDirectories.Count(); i++) {
395 0 : LoadDictionariesFromDir(mDynamicDirectories[i]);
396 : }
397 :
398 : // Now we have finished updating the list of dictionaries, update the current
399 : // dictionary and any editors which may use it.
400 1 : mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
401 :
402 1 : if (aNotifyChildProcesses) {
403 0 : ContentParent::NotifyUpdatedDictionaries();
404 : }
405 :
406 : // Check if the current dictionary is still available.
407 : // If not, try to replace it with another dictionary of the same language.
408 1 : if (!mDictionary.IsEmpty()) {
409 0 : rv = SetDictionary(mDictionary.get());
410 0 : if (NS_SUCCEEDED(rv))
411 0 : return;
412 : }
413 :
414 : // If the current dictionary has gone, and we don't have a good replacement,
415 : // set no current dictionary.
416 1 : if (!mDictionary.IsEmpty()) {
417 0 : SetDictionary(EmptyString().get());
418 : }
419 : }
420 :
421 : NS_IMETHODIMP
422 2 : mozHunspell::LoadDictionariesFromDir(nsIFile* aDir)
423 : {
424 : nsresult rv;
425 :
426 2 : bool check = false;
427 2 : rv = aDir->Exists(&check);
428 2 : if (NS_FAILED(rv) || !check)
429 1 : return NS_ERROR_UNEXPECTED;
430 :
431 1 : rv = aDir->IsDirectory(&check);
432 1 : if (NS_FAILED(rv) || !check)
433 0 : return NS_ERROR_UNEXPECTED;
434 :
435 2 : nsCOMPtr<nsISimpleEnumerator> e;
436 1 : rv = aDir->GetDirectoryEntries(getter_AddRefs(e));
437 1 : if (NS_FAILED(rv))
438 0 : return NS_ERROR_UNEXPECTED;
439 :
440 2 : nsCOMPtr<nsIDirectoryEnumerator> files(do_QueryInterface(e));
441 1 : if (!files)
442 0 : return NS_ERROR_UNEXPECTED;
443 :
444 2 : nsCOMPtr<nsIFile> file;
445 5 : while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) {
446 3 : nsAutoString leafName;
447 2 : file->GetLeafName(leafName);
448 2 : if (!StringEndsWith(leafName, NS_LITERAL_STRING(".dic")))
449 1 : continue;
450 :
451 2 : nsAutoString dict(leafName);
452 1 : dict.SetLength(dict.Length() - 4); // magic length of ".dic"
453 :
454 : // check for the presence of the .aff file
455 1 : leafName = dict;
456 1 : leafName.AppendLiteral(".aff");
457 1 : file->SetLeafName(leafName);
458 1 : rv = file->Exists(&check);
459 1 : if (NS_FAILED(rv) || !check)
460 0 : continue;
461 :
462 : #ifdef DEBUG_bsmedberg
463 : printf("Adding dictionary: %s\n", NS_ConvertUTF16toUTF8(dict).get());
464 : #endif
465 :
466 : // Replace '_' separator with '-'
467 1 : dict.ReplaceChar("_", '-');
468 :
469 1 : mDictionaries.Put(dict, file);
470 : }
471 :
472 1 : return NS_OK;
473 : }
474 :
475 : nsresult
476 0 : mozHunspell::ConvertCharset(const char16_t* aStr, std::string* aDst)
477 : {
478 0 : NS_ENSURE_ARG_POINTER(aDst);
479 0 : NS_ENSURE_TRUE(mEncoder, NS_ERROR_NULL_POINTER);
480 :
481 0 : auto src = MakeStringSpan(aStr);
482 : CheckedInt<size_t> needed =
483 0 : mEncoder->MaxBufferLengthFromUTF16WithoutReplacement(src.Length());
484 0 : if (!needed.isValid()) {
485 0 : return NS_ERROR_OUT_OF_MEMORY;
486 : }
487 :
488 0 : aDst->resize(needed.value());
489 :
490 0 : char* dstPtr = &aDst->operator[](0);
491 0 : auto dst = MakeSpan(reinterpret_cast<uint8_t*>(dstPtr), needed.value());
492 :
493 : uint32_t result;
494 : size_t read;
495 : size_t written;
496 0 : Tie(result, read, written) =
497 0 : mEncoder->EncodeFromUTF16WithoutReplacement(src, dst, true);
498 : Unused << read;
499 0 : MOZ_ASSERT(result != kOutputFull);
500 0 : if (result != kInputEmpty) {
501 0 : return NS_ERROR_UENC_NOMAPPING;
502 : }
503 0 : aDst->resize(written);
504 0 : mEncoder->Encoding()->NewEncoderInto(*mEncoder);
505 0 : return NS_OK;
506 : }
507 :
508 : NS_IMETHODIMP
509 0 : mozHunspell::CollectReports(nsIHandleReportCallback* aHandleReport,
510 : nsISupports* aData, bool aAnonymize)
511 : {
512 0 : MOZ_COLLECT_REPORT(
513 : "explicit/spell-check", KIND_HEAP, UNITS_BYTES,
514 : HunspellAllocator::MemoryAllocated(),
515 0 : "Memory used by the spell-checking engine.");
516 :
517 0 : return NS_OK;
518 : }
519 :
520 0 : NS_IMETHODIMP mozHunspell::Check(const char16_t *aWord, bool *aResult)
521 : {
522 0 : NS_ENSURE_ARG_POINTER(aWord);
523 0 : NS_ENSURE_ARG_POINTER(aResult);
524 0 : NS_ENSURE_TRUE(mHunspell, NS_ERROR_FAILURE);
525 :
526 0 : std::string charsetWord;
527 0 : nsresult rv = ConvertCharset(aWord, &charsetWord);
528 0 : NS_ENSURE_SUCCESS(rv, rv);
529 :
530 0 : *aResult = mHunspell->spell(charsetWord);
531 :
532 0 : if (!*aResult && mPersonalDictionary)
533 0 : rv = mPersonalDictionary->Check(aWord, mLanguage.get(), aResult);
534 :
535 0 : return rv;
536 : }
537 :
538 0 : NS_IMETHODIMP mozHunspell::Suggest(const char16_t *aWord, char16_t ***aSuggestions, uint32_t *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 : std::string charsetWord;
548 0 : rv = ConvertCharset(aWord, &charsetWord);
549 0 : NS_ENSURE_SUCCESS(rv, rv);
550 :
551 0 : std::vector<std::string> suggestions = mHunspell->suggest(charsetWord);
552 0 : *aSuggestionCount = static_cast<uint32_t>(suggestions.size());
553 :
554 0 : if (*aSuggestionCount) {
555 0 : *aSuggestions = (char16_t **)moz_xmalloc(*aSuggestionCount * sizeof(char16_t *));
556 0 : if (*aSuggestions) {
557 0 : uint32_t index = 0;
558 0 : for (index = 0; index < *aSuggestionCount && NS_SUCCEEDED(rv); ++index) {
559 : // If the IDL used an array of AString, we could use
560 : // Encoding::DecodeWithoutBOMHandling() here.
561 : // Convert the suggestion to utf16
562 0 : Span<const char> charSrc(suggestions[index]);
563 0 : auto src = AsBytes(charSrc);
564 : CheckedInt<size_t> needed =
565 0 : mDecoder->MaxUTF16BufferLength(src.Length());
566 0 : if (!needed.isValid()) {
567 0 : rv = NS_ERROR_OUT_OF_MEMORY;
568 0 : break;
569 : }
570 0 : size_t dstLen = needed.value();
571 0 : needed += 1;
572 0 : needed *= sizeof(char16_t);
573 0 : if (!needed.isValid()) {
574 0 : rv = NS_ERROR_OUT_OF_MEMORY;
575 0 : break;
576 : }
577 0 : (*aSuggestions)[index] = (char16_t*)moz_xmalloc(needed.value());
578 0 : if (!((*aSuggestions)[index])) {
579 0 : rv = NS_ERROR_OUT_OF_MEMORY;
580 0 : break;
581 : }
582 0 : auto dst = MakeSpan((*aSuggestions)[index], dstLen);
583 : uint32_t result;
584 : size_t read;
585 : size_t written;
586 : bool hadErrors;
587 0 : Tie(result, read, written, hadErrors) =
588 0 : mDecoder->DecodeToUTF16(src, dst, true);
589 0 : MOZ_ASSERT(result == kInputEmpty);
590 0 : MOZ_ASSERT(read == src.Length());
591 0 : MOZ_ASSERT(written <= dstLen);
592 : Unused << hadErrors;
593 0 : (*aSuggestions)[index][written] = 0;
594 0 : mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(*mDecoder);
595 : }
596 :
597 0 : if (NS_FAILED(rv))
598 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, *aSuggestions); // free the char16_t strings up to the point at which the error occurred
599 : }
600 : else // if (*aSuggestions)
601 0 : rv = NS_ERROR_OUT_OF_MEMORY;
602 : }
603 :
604 0 : return rv;
605 : }
606 :
607 : NS_IMETHODIMP
608 0 : mozHunspell::Observe(nsISupports* aSubj, const char *aTopic,
609 : const char16_t *aData)
610 : {
611 0 : NS_ASSERTION(!strcmp(aTopic, "profile-do-change")
612 : || !strcmp(aTopic, "profile-after-change"),
613 : "Unexpected observer topic");
614 :
615 0 : LoadDictionaryList(false);
616 :
617 0 : return NS_OK;
618 : }
619 :
620 0 : NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir)
621 : {
622 0 : mDynamicDirectories.AppendObject(aDir);
623 0 : LoadDictionaryList(true);
624 0 : return NS_OK;
625 : }
626 :
627 0 : NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir)
628 : {
629 0 : mDynamicDirectories.RemoveObject(aDir);
630 0 : LoadDictionaryList(true);
631 :
632 : #ifdef MOZ_THUNDERBIRD
633 : /*
634 : * This notification is needed for Thunderbird. Thunderbird derives the dictionary
635 : * from the document's "lang" attribute. If a dictionary is removed,
636 : * we need to change the "lang" attribute.
637 : */
638 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
639 : if (obs) {
640 : obs->NotifyObservers(nullptr,
641 : SPELLCHECK_DICTIONARY_REMOVE_NOTIFICATION,
642 : nullptr);
643 : }
644 : #endif
645 0 : return NS_OK;
646 : }
|