Coverage Report

Created: 2018-09-25 14:53

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