Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "mozPersonalDictionary.h" |
michael@0 | 7 | #include "nsIUnicharInputStream.h" |
michael@0 | 8 | #include "nsReadableUtils.h" |
michael@0 | 9 | #include "nsIFile.h" |
michael@0 | 10 | #include "nsAppDirectoryServiceDefs.h" |
michael@0 | 11 | #include "nsICharsetConverterManager.h" |
michael@0 | 12 | #include "nsIObserverService.h" |
michael@0 | 13 | #include "nsIPrefService.h" |
michael@0 | 14 | #include "nsIPrefBranch.h" |
michael@0 | 15 | #include "nsIWeakReference.h" |
michael@0 | 16 | #include "nsCRT.h" |
michael@0 | 17 | #include "nsNetUtil.h" |
michael@0 | 18 | #include "nsStringEnumerator.h" |
michael@0 | 19 | #include "nsUnicharInputStream.h" |
michael@0 | 20 | |
michael@0 | 21 | #define MOZ_PERSONAL_DICT_NAME "persdict.dat" |
michael@0 | 22 | |
michael@0 | 23 | const int kMaxWordLen=256; |
michael@0 | 24 | |
michael@0 | 25 | /** |
michael@0 | 26 | * This is the most braindead implementation of a personal dictionary possible. |
michael@0 | 27 | * There is not much complexity needed, though. It could be made much faster, |
michael@0 | 28 | * and probably should, but I don't see much need for more in terms of interface. |
michael@0 | 29 | * |
michael@0 | 30 | * Allowing personal words to be associated with only certain dictionaries maybe. |
michael@0 | 31 | * |
michael@0 | 32 | * TODO: |
michael@0 | 33 | * Implement the suggestion record. |
michael@0 | 34 | */ |
michael@0 | 35 | |
michael@0 | 36 | |
michael@0 | 37 | NS_IMPL_CYCLE_COLLECTING_ADDREF(mozPersonalDictionary) |
michael@0 | 38 | NS_IMPL_CYCLE_COLLECTING_RELEASE(mozPersonalDictionary) |
michael@0 | 39 | |
michael@0 | 40 | NS_INTERFACE_MAP_BEGIN(mozPersonalDictionary) |
michael@0 | 41 | NS_INTERFACE_MAP_ENTRY(mozIPersonalDictionary) |
michael@0 | 42 | NS_INTERFACE_MAP_ENTRY(nsIObserver) |
michael@0 | 43 | NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
michael@0 | 44 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIPersonalDictionary) |
michael@0 | 45 | NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozPersonalDictionary) |
michael@0 | 46 | NS_INTERFACE_MAP_END |
michael@0 | 47 | |
michael@0 | 48 | NS_IMPL_CYCLE_COLLECTION(mozPersonalDictionary, mEncoder) |
michael@0 | 49 | |
michael@0 | 50 | mozPersonalDictionary::mozPersonalDictionary() |
michael@0 | 51 | : mDirty(false) |
michael@0 | 52 | { |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | mozPersonalDictionary::~mozPersonalDictionary() |
michael@0 | 56 | { |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | nsresult mozPersonalDictionary::Init() |
michael@0 | 60 | { |
michael@0 | 61 | nsCOMPtr<nsIObserverService> svc = |
michael@0 | 62 | do_GetService("@mozilla.org/observer-service;1"); |
michael@0 | 63 | |
michael@0 | 64 | NS_ENSURE_STATE(svc); |
michael@0 | 65 | // we want to reload the dictionary if the profile switches |
michael@0 | 66 | nsresult rv = svc->AddObserver(this, "profile-do-change", true); |
michael@0 | 67 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 68 | rv = svc->AddObserver(this, "profile-before-change", true); |
michael@0 | 69 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 70 | |
michael@0 | 71 | Load(); |
michael@0 | 72 | |
michael@0 | 73 | return NS_OK; |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | /* void Load (); */ |
michael@0 | 77 | NS_IMETHODIMP mozPersonalDictionary::Load() |
michael@0 | 78 | { |
michael@0 | 79 | //FIXME Deinst -- get dictionary name from prefs; |
michael@0 | 80 | nsresult res; |
michael@0 | 81 | nsCOMPtr<nsIFile> theFile; |
michael@0 | 82 | bool dictExists; |
michael@0 | 83 | |
michael@0 | 84 | res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile)); |
michael@0 | 85 | if(NS_FAILED(res)) return res; |
michael@0 | 86 | if(!theFile)return NS_ERROR_FAILURE; |
michael@0 | 87 | res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME)); |
michael@0 | 88 | if(NS_FAILED(res)) return res; |
michael@0 | 89 | res = theFile->Exists(&dictExists); |
michael@0 | 90 | if(NS_FAILED(res)) return res; |
michael@0 | 91 | |
michael@0 | 92 | if (!dictExists) { |
michael@0 | 93 | // Nothing is really wrong... |
michael@0 | 94 | return NS_OK; |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | nsCOMPtr<nsIInputStream> inStream; |
michael@0 | 98 | NS_NewLocalFileInputStream(getter_AddRefs(inStream), theFile); |
michael@0 | 99 | |
michael@0 | 100 | nsCOMPtr<nsIUnicharInputStream> convStream; |
michael@0 | 101 | res = nsSimpleUnicharStreamFactory::GetInstance()-> |
michael@0 | 102 | CreateInstanceFromUTF8Stream(inStream, getter_AddRefs(convStream)); |
michael@0 | 103 | if(NS_FAILED(res)) return res; |
michael@0 | 104 | |
michael@0 | 105 | // we're rereading to get rid of the old data -- we shouldn't have any, but... |
michael@0 | 106 | mDictionaryTable.Clear(); |
michael@0 | 107 | |
michael@0 | 108 | char16_t c; |
michael@0 | 109 | uint32_t nRead; |
michael@0 | 110 | bool done = false; |
michael@0 | 111 | do{ // read each line of text into the string array. |
michael@0 | 112 | if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) break; |
michael@0 | 113 | while(!done && ((c == '\n') || (c == '\r'))){ |
michael@0 | 114 | if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true; |
michael@0 | 115 | } |
michael@0 | 116 | if (!done){ |
michael@0 | 117 | nsAutoString word; |
michael@0 | 118 | while((c != '\n') && (c != '\r') && !done){ |
michael@0 | 119 | word.Append(c); |
michael@0 | 120 | if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true; |
michael@0 | 121 | } |
michael@0 | 122 | mDictionaryTable.PutEntry(word.get()); |
michael@0 | 123 | } |
michael@0 | 124 | } while(!done); |
michael@0 | 125 | mDirty = false; |
michael@0 | 126 | |
michael@0 | 127 | return res; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | // A little helper function to add the key to the list. |
michael@0 | 131 | // This is not threadsafe, and only safe if the consumer does not |
michael@0 | 132 | // modify the list. |
michael@0 | 133 | static PLDHashOperator |
michael@0 | 134 | AddHostToStringArray(nsUnicharPtrHashKey *aEntry, void *aArg) |
michael@0 | 135 | { |
michael@0 | 136 | static_cast<nsTArray<nsString>*>(aArg)->AppendElement(nsDependentString(aEntry->GetKey())); |
michael@0 | 137 | return PL_DHASH_NEXT; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | /* void Save (); */ |
michael@0 | 141 | NS_IMETHODIMP mozPersonalDictionary::Save() |
michael@0 | 142 | { |
michael@0 | 143 | nsCOMPtr<nsIFile> theFile; |
michael@0 | 144 | nsresult res; |
michael@0 | 145 | |
michael@0 | 146 | if(!mDirty) return NS_OK; |
michael@0 | 147 | |
michael@0 | 148 | //FIXME Deinst -- get dictionary name from prefs; |
michael@0 | 149 | res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(theFile)); |
michael@0 | 150 | if(NS_FAILED(res)) return res; |
michael@0 | 151 | if(!theFile)return NS_ERROR_FAILURE; |
michael@0 | 152 | res = theFile->Append(NS_LITERAL_STRING(MOZ_PERSONAL_DICT_NAME)); |
michael@0 | 153 | if(NS_FAILED(res)) return res; |
michael@0 | 154 | |
michael@0 | 155 | nsCOMPtr<nsIOutputStream> outStream; |
michael@0 | 156 | NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664); |
michael@0 | 157 | |
michael@0 | 158 | // get a buffered output stream 4096 bytes big, to optimize writes |
michael@0 | 159 | nsCOMPtr<nsIOutputStream> bufferedOutputStream; |
michael@0 | 160 | res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096); |
michael@0 | 161 | if (NS_FAILED(res)) return res; |
michael@0 | 162 | |
michael@0 | 163 | nsTArray<nsString> array(mDictionaryTable.Count()); |
michael@0 | 164 | mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array); |
michael@0 | 165 | |
michael@0 | 166 | uint32_t bytesWritten; |
michael@0 | 167 | nsAutoCString utf8Key; |
michael@0 | 168 | for (uint32_t i = 0; i < array.Length(); ++i ) { |
michael@0 | 169 | CopyUTF16toUTF8(array[i], utf8Key); |
michael@0 | 170 | |
michael@0 | 171 | bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten); |
michael@0 | 172 | bufferedOutputStream->Write("\n", 1, &bytesWritten); |
michael@0 | 173 | } |
michael@0 | 174 | nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream); |
michael@0 | 175 | NS_ASSERTION(safeStream, "expected a safe output stream!"); |
michael@0 | 176 | if (safeStream) { |
michael@0 | 177 | res = safeStream->Finish(); |
michael@0 | 178 | if (NS_FAILED(res)) { |
michael@0 | 179 | NS_WARNING("failed to save personal dictionary file! possible data loss"); |
michael@0 | 180 | } |
michael@0 | 181 | } |
michael@0 | 182 | return res; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /* readonly attribute nsIStringEnumerator GetWordList() */ |
michael@0 | 186 | NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords) |
michael@0 | 187 | { |
michael@0 | 188 | NS_ENSURE_ARG_POINTER(aWords); |
michael@0 | 189 | *aWords = nullptr; |
michael@0 | 190 | |
michael@0 | 191 | nsTArray<nsString> *array = new nsTArray<nsString>(mDictionaryTable.Count()); |
michael@0 | 192 | if (!array) |
michael@0 | 193 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 194 | |
michael@0 | 195 | mDictionaryTable.EnumerateEntries(AddHostToStringArray, array); |
michael@0 | 196 | |
michael@0 | 197 | array->Sort(); |
michael@0 | 198 | |
michael@0 | 199 | return NS_NewAdoptingStringEnumerator(aWords, array); |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | /* boolean Check (in wstring word, in wstring language); */ |
michael@0 | 203 | NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t *aLanguage, bool *aResult) |
michael@0 | 204 | { |
michael@0 | 205 | NS_ENSURE_ARG_POINTER(aWord); |
michael@0 | 206 | NS_ENSURE_ARG_POINTER(aResult); |
michael@0 | 207 | |
michael@0 | 208 | *aResult = (mDictionaryTable.GetEntry(aWord) || mIgnoreTable.GetEntry(aWord)); |
michael@0 | 209 | return NS_OK; |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | /* void AddWord (in wstring word); */ |
michael@0 | 213 | NS_IMETHODIMP mozPersonalDictionary::AddWord(const char16_t *aWord, const char16_t *aLang) |
michael@0 | 214 | { |
michael@0 | 215 | mDictionaryTable.PutEntry(aWord); |
michael@0 | 216 | mDirty = true; |
michael@0 | 217 | return NS_OK; |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | /* void RemoveWord (in wstring word); */ |
michael@0 | 221 | NS_IMETHODIMP mozPersonalDictionary::RemoveWord(const char16_t *aWord, const char16_t *aLang) |
michael@0 | 222 | { |
michael@0 | 223 | mDictionaryTable.RemoveEntry(aWord); |
michael@0 | 224 | mDirty = true; |
michael@0 | 225 | return NS_OK; |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | /* void IgnoreWord (in wstring word); */ |
michael@0 | 229 | NS_IMETHODIMP mozPersonalDictionary::IgnoreWord(const char16_t *aWord) |
michael@0 | 230 | { |
michael@0 | 231 | // avoid adding duplicate words to the ignore list |
michael@0 | 232 | if (aWord && !mIgnoreTable.GetEntry(aWord)) |
michael@0 | 233 | mIgnoreTable.PutEntry(aWord); |
michael@0 | 234 | return NS_OK; |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | /* void EndSession (); */ |
michael@0 | 238 | NS_IMETHODIMP mozPersonalDictionary::EndSession() |
michael@0 | 239 | { |
michael@0 | 240 | Save(); // save any custom words at the end of a spell check session |
michael@0 | 241 | mIgnoreTable.Clear(); |
michael@0 | 242 | return NS_OK; |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | /* void AddCorrection (in wstring word, in wstring correction); */ |
michael@0 | 246 | NS_IMETHODIMP mozPersonalDictionary::AddCorrection(const char16_t *word, const char16_t *correction, const char16_t *lang) |
michael@0 | 247 | { |
michael@0 | 248 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | /* void RemoveCorrection (in wstring word, in wstring correction); */ |
michael@0 | 252 | NS_IMETHODIMP mozPersonalDictionary::RemoveCorrection(const char16_t *word, const char16_t *correction, const char16_t *lang) |
michael@0 | 253 | { |
michael@0 | 254 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | /* void GetCorrection (in wstring word, [array, size_is (count)] out wstring words, out uint32_t count); */ |
michael@0 | 258 | NS_IMETHODIMP mozPersonalDictionary::GetCorrection(const char16_t *word, char16_t ***words, uint32_t *count) |
michael@0 | 259 | { |
michael@0 | 260 | return NS_ERROR_NOT_IMPLEMENTED; |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */ |
michael@0 | 264 | NS_IMETHODIMP mozPersonalDictionary::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) |
michael@0 | 265 | { |
michael@0 | 266 | if (!nsCRT::strcmp(aTopic, "profile-do-change")) { |
michael@0 | 267 | Load(); // load automatically clears out the existing dictionary table |
michael@0 | 268 | } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) { |
michael@0 | 269 | Save(); |
michael@0 | 270 | } |
michael@0 | 271 | |
michael@0 | 272 | return NS_OK; |
michael@0 | 273 | } |
michael@0 | 274 |