LCOV - code coverage report
Current view: top level - extensions/spellcheck/hunspell/glue - mozHunspell.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 100 303 33.0 %
Date: 2017-07-14 16:53:18 Functions: 8 29 27.6 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13