1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,274 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozPersonalDictionary.h" 1.10 +#include "nsIUnicharInputStream.h" 1.11 +#include "nsReadableUtils.h" 1.12 +#include "nsIFile.h" 1.13 +#include "nsAppDirectoryServiceDefs.h" 1.14 +#include "nsICharsetConverterManager.h" 1.15 +#include "nsIObserverService.h" 1.16 +#include "nsIPrefService.h" 1.17 +#include "nsIPrefBranch.h" 1.18 +#include "nsIWeakReference.h" 1.19 +#include "nsCRT.h" 1.20 +#include "nsNetUtil.h" 1.21 +#include "nsStringEnumerator.h" 1.22 +#include "nsUnicharInputStream.h" 1.23 + 1.24 +#define MOZ_PERSONAL_DICT_NAME "persdict.dat" 1.25 + 1.26 +const int kMaxWordLen=256; 1.27 + 1.28 +/** 1.29 + * This is the most braindead implementation of a personal dictionary possible. 1.30 + * There is not much complexity needed, though. It could be made much faster, 1.31 + * and probably should, but I don't see much need for more in terms of interface. 1.32 + * 1.33 + * Allowing personal words to be associated with only certain dictionaries maybe. 1.34 + * 1.35 + * TODO: 1.36 + * Implement the suggestion record. 1.37 + */ 1.38 + 1.39 + 1.40 +NS_IMPL_CYCLE_COLLECTING_ADDREF(mozPersonalDictionary) 1.41 +NS_IMPL_CYCLE_COLLECTING_RELEASE(mozPersonalDictionary) 1.42 + 1.43 +NS_INTERFACE_MAP_BEGIN(mozPersonalDictionary) 1.44 + NS_INTERFACE_MAP_ENTRY(mozIPersonalDictionary) 1.45 + NS_INTERFACE_MAP_ENTRY(nsIObserver) 1.46 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.47 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIPersonalDictionary) 1.48 + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozPersonalDictionary) 1.49 +NS_INTERFACE_MAP_END 1.50 + 1.51 +NS_IMPL_CYCLE_COLLECTION(mozPersonalDictionary, mEncoder) 1.52 + 1.53 +mozPersonalDictionary::mozPersonalDictionary() 1.54 + : mDirty(false) 1.55 +{ 1.56 +} 1.57 + 1.58 +mozPersonalDictionary::~mozPersonalDictionary() 1.59 +{ 1.60 +} 1.61 + 1.62 +nsresult mozPersonalDictionary::Init() 1.63 +{ 1.64 + nsCOMPtr<nsIObserverService> svc = 1.65 + do_GetService("@mozilla.org/observer-service;1"); 1.66 + 1.67 + NS_ENSURE_STATE(svc); 1.68 + // we want to reload the dictionary if the profile switches 1.69 + nsresult rv = svc->AddObserver(this, "profile-do-change", true); 1.70 + NS_ENSURE_SUCCESS(rv, rv); 1.71 + rv = svc->AddObserver(this, "profile-before-change", true); 1.72 + NS_ENSURE_SUCCESS(rv, rv); 1.73 + 1.74 + Load(); 1.75 + 1.76 + return NS_OK; 1.77 +} 1.78 + 1.79 +/* void Load (); */ 1.80 +NS_IMETHODIMP mozPersonalDictionary::Load() 1.81 +{ 1.82 + //FIXME Deinst -- get dictionary name from prefs; 1.83 + nsresult res; 1.84 + nsCOMPtr<nsIFile> theFile; 1.85 + bool dictExists; 1.86 + 1.87 + res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile)); 1.88 + if(NS_FAILED(res)) return res; 1.89 + if(!theFile)return NS_ERROR_FAILURE; 1.90 + res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME)); 1.91 + if(NS_FAILED(res)) return res; 1.92 + res = theFile->Exists(&dictExists); 1.93 + if(NS_FAILED(res)) return res; 1.94 + 1.95 + if (!dictExists) { 1.96 + // Nothing is really wrong... 1.97 + return NS_OK; 1.98 + } 1.99 + 1.100 + nsCOMPtr<nsIInputStream> inStream; 1.101 + NS_NewLocalFileInputStream(getter_AddRefs(inStream), theFile); 1.102 + 1.103 + nsCOMPtr<nsIUnicharInputStream> convStream; 1.104 + res = nsSimpleUnicharStreamFactory::GetInstance()-> 1.105 + CreateInstanceFromUTF8Stream(inStream, getter_AddRefs(convStream)); 1.106 + if(NS_FAILED(res)) return res; 1.107 + 1.108 + // we're rereading to get rid of the old data -- we shouldn't have any, but... 1.109 + mDictionaryTable.Clear(); 1.110 + 1.111 + char16_t c; 1.112 + uint32_t nRead; 1.113 + bool done = false; 1.114 + do{ // read each line of text into the string array. 1.115 + if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) break; 1.116 + while(!done && ((c == '\n') || (c == '\r'))){ 1.117 + if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true; 1.118 + } 1.119 + if (!done){ 1.120 + nsAutoString word; 1.121 + while((c != '\n') && (c != '\r') && !done){ 1.122 + word.Append(c); 1.123 + if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true; 1.124 + } 1.125 + mDictionaryTable.PutEntry(word.get()); 1.126 + } 1.127 + } while(!done); 1.128 + mDirty = false; 1.129 + 1.130 + return res; 1.131 +} 1.132 + 1.133 +// A little helper function to add the key to the list. 1.134 +// This is not threadsafe, and only safe if the consumer does not 1.135 +// modify the list. 1.136 +static PLDHashOperator 1.137 +AddHostToStringArray(nsUnicharPtrHashKey *aEntry, void *aArg) 1.138 +{ 1.139 + static_cast<nsTArray<nsString>*>(aArg)->AppendElement(nsDependentString(aEntry->GetKey())); 1.140 + return PL_DHASH_NEXT; 1.141 +} 1.142 + 1.143 +/* void Save (); */ 1.144 +NS_IMETHODIMP mozPersonalDictionary::Save() 1.145 +{ 1.146 + nsCOMPtr<nsIFile> theFile; 1.147 + nsresult res; 1.148 + 1.149 + if(!mDirty) return NS_OK; 1.150 + 1.151 + //FIXME Deinst -- get dictionary name from prefs; 1.152 + res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile)); 1.153 + if(NS_FAILED(res)) return res; 1.154 + if(!theFile)return NS_ERROR_FAILURE; 1.155 + res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME)); 1.156 + if(NS_FAILED(res)) return res; 1.157 + 1.158 + nsCOMPtr<nsIOutputStream> outStream; 1.159 + NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664); 1.160 + 1.161 + // get a buffered output stream 4096 bytes big, to optimize writes 1.162 + nsCOMPtr<nsIOutputStream> bufferedOutputStream; 1.163 + res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096); 1.164 + if (NS_FAILED(res)) return res; 1.165 + 1.166 + nsTArray<nsString> array(mDictionaryTable.Count()); 1.167 + mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array); 1.168 + 1.169 + uint32_t bytesWritten; 1.170 + nsAutoCString utf8Key; 1.171 + for (uint32_t i = 0; i < array.Length(); ++i ) { 1.172 + CopyUTF16toUTF8(array[i], utf8Key); 1.173 + 1.174 + bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten); 1.175 + bufferedOutputStream->Write("\n", 1, &bytesWritten); 1.176 + } 1.177 + nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream); 1.178 + NS_ASSERTION(safeStream, "expected a safe output stream!"); 1.179 + if (safeStream) { 1.180 + res = safeStream->Finish(); 1.181 + if (NS_FAILED(res)) { 1.182 + NS_WARNING("failed to save personal dictionary file! possible data loss"); 1.183 + } 1.184 + } 1.185 + return res; 1.186 +} 1.187 + 1.188 +/* readonly attribute nsIStringEnumerator GetWordList() */ 1.189 +NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords) 1.190 +{ 1.191 + NS_ENSURE_ARG_POINTER(aWords); 1.192 + *aWords = nullptr; 1.193 + 1.194 + nsTArray<nsString> *array = new nsTArray<nsString>(mDictionaryTable.Count()); 1.195 + if (!array) 1.196 + return NS_ERROR_OUT_OF_MEMORY; 1.197 + 1.198 + mDictionaryTable.EnumerateEntries(AddHostToStringArray, array); 1.199 + 1.200 + array->Sort(); 1.201 + 1.202 + return NS_NewAdoptingStringEnumerator(aWords, array); 1.203 +} 1.204 + 1.205 +/* boolean Check (in wstring word, in wstring language); */ 1.206 +NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t *aLanguage, bool *aResult) 1.207 +{ 1.208 + NS_ENSURE_ARG_POINTER(aWord); 1.209 + NS_ENSURE_ARG_POINTER(aResult); 1.210 + 1.211 + *aResult = (mDictionaryTable.GetEntry(aWord) || mIgnoreTable.GetEntry(aWord)); 1.212 + return NS_OK; 1.213 +} 1.214 + 1.215 +/* void AddWord (in wstring word); */ 1.216 +NS_IMETHODIMP mozPersonalDictionary::AddWord(const char16_t *aWord, const char16_t *aLang) 1.217 +{ 1.218 + mDictionaryTable.PutEntry(aWord); 1.219 + mDirty = true; 1.220 + return NS_OK; 1.221 +} 1.222 + 1.223 +/* void RemoveWord (in wstring word); */ 1.224 +NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const char16_t *aWord, const char16_t *aLang) 1.225 +{ 1.226 + mDictionaryTable.RemoveEntry(aWord); 1.227 + mDirty = true; 1.228 + return NS_OK; 1.229 +} 1.230 + 1.231 +/* void IgnoreWord (in wstring word); */ 1.232 +NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const char16_t *aWord) 1.233 +{ 1.234 + // avoid adding duplicate words to the ignore list 1.235 + if (aWord && !mIgnoreTable.GetEntry(aWord)) 1.236 + mIgnoreTable.PutEntry(aWord); 1.237 + return NS_OK; 1.238 +} 1.239 + 1.240 +/* void EndSession (); */ 1.241 +NS_IMETHODIMP mozPersonalDictionary::EndSession() 1.242 +{ 1.243 + Save(); // save any custom words at the end of a spell check session 1.244 + mIgnoreTable.Clear(); 1.245 + return NS_OK; 1.246 +} 1.247 + 1.248 +/* void AddCorrection (in wstring word, in wstring correction); */ 1.249 +NS_IMETHODIMP mozPersonalDictionary::AddCorrection(const char16_t *word, const char16_t *correction, const char16_t *lang) 1.250 +{ 1.251 + return NS_ERROR_NOT_IMPLEMENTED; 1.252 +} 1.253 + 1.254 +/* void RemoveCorrection (in wstring word, in wstring correction); */ 1.255 +NS_IMETHODIMP mozPersonalDictionary::RemoveCorrection(const char16_t *word, const char16_t *correction, const char16_t *lang) 1.256 +{ 1.257 + return NS_ERROR_NOT_IMPLEMENTED; 1.258 +} 1.259 + 1.260 +/* void GetCorrection (in wstring word, [array, size_is (count)] out wstring words, out uint32_t count); */ 1.261 +NS_IMETHODIMP mozPersonalDictionary::GetCorrection(const char16_t *word, char16_t ***words, uint32_t *count) 1.262 +{ 1.263 + return NS_ERROR_NOT_IMPLEMENTED; 1.264 +} 1.265 + 1.266 +/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */ 1.267 +NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) 1.268 +{ 1.269 + if (!nsCRT::strcmp(aTopic, "profile-do-change")) { 1.270 + Load(); // load automatically clears out the existing dictionary table 1.271 + } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) { 1.272 + Save(); 1.273 + } 1.274 + 1.275 + return NS_OK; 1.276 +} 1.277 +