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 <stdlib.h> // for getenv |
michael@0 | 7 | |
michael@0 | 8 | #include "mozilla/Attributes.h" // for MOZ_FINAL |
michael@0 | 9 | #include "mozilla/Preferences.h" // for Preferences |
michael@0 | 10 | #include "mozilla/Services.h" // for GetXULChromeRegistryService |
michael@0 | 11 | #include "mozilla/dom/Element.h" // for Element |
michael@0 | 12 | #include "mozilla/mozalloc.h" // for operator delete, etc |
michael@0 | 13 | #include "nsAString.h" // for nsAString_internal::IsEmpty, etc |
michael@0 | 14 | #include "nsComponentManagerUtils.h" // for do_CreateInstance |
michael@0 | 15 | #include "nsDebug.h" // for NS_ENSURE_TRUE, etc |
michael@0 | 16 | #include "nsDependentSubstring.h" // for Substring |
michael@0 | 17 | #include "nsEditorSpellCheck.h" |
michael@0 | 18 | #include "nsError.h" // for NS_ERROR_NOT_INITIALIZED, etc |
michael@0 | 19 | #include "nsIChromeRegistry.h" // for nsIXULChromeRegistry |
michael@0 | 20 | #include "nsIContent.h" // for nsIContent |
michael@0 | 21 | #include "nsIContentPrefService.h" // for nsIContentPrefService, etc |
michael@0 | 22 | #include "nsIContentPrefService2.h" // for nsIContentPrefService2, etc |
michael@0 | 23 | #include "nsIDOMDocument.h" // for nsIDOMDocument |
michael@0 | 24 | #include "nsIDOMElement.h" // for nsIDOMElement |
michael@0 | 25 | #include "nsIDOMRange.h" // for nsIDOMRange |
michael@0 | 26 | #include "nsIDocument.h" // for nsIDocument |
michael@0 | 27 | #include "nsIEditor.h" // for nsIEditor |
michael@0 | 28 | #include "nsIHTMLEditor.h" // for nsIHTMLEditor |
michael@0 | 29 | #include "nsILoadContext.h" |
michael@0 | 30 | #include "nsISelection.h" // for nsISelection |
michael@0 | 31 | #include "nsISpellChecker.h" // for nsISpellChecker, etc |
michael@0 | 32 | #include "nsISupportsBase.h" // for nsISupports |
michael@0 | 33 | #include "nsISupportsUtils.h" // for NS_ADDREF |
michael@0 | 34 | #include "nsITextServicesDocument.h" // for nsITextServicesDocument |
michael@0 | 35 | #include "nsITextServicesFilter.h" // for nsITextServicesFilter |
michael@0 | 36 | #include "nsIURI.h" // for nsIURI |
michael@0 | 37 | #include "nsIVariant.h" // for nsIWritableVariant, etc |
michael@0 | 38 | #include "nsLiteralString.h" // for NS_LITERAL_STRING, etc |
michael@0 | 39 | #include "nsMemory.h" // for nsMemory |
michael@0 | 40 | #include "nsReadableUtils.h" // for ToNewUnicode, EmptyString, etc |
michael@0 | 41 | #include "nsServiceManagerUtils.h" // for do_GetService |
michael@0 | 42 | #include "nsString.h" // for nsAutoString, nsString, etc |
michael@0 | 43 | #include "nsStringFwd.h" // for nsAFlatString |
michael@0 | 44 | #include "nsStyleUtil.h" // for nsStyleUtil |
michael@0 | 45 | |
michael@0 | 46 | using namespace mozilla; |
michael@0 | 47 | |
michael@0 | 48 | class UpdateDictionnaryHolder { |
michael@0 | 49 | private: |
michael@0 | 50 | nsEditorSpellCheck* mSpellCheck; |
michael@0 | 51 | public: |
michael@0 | 52 | UpdateDictionnaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) { |
michael@0 | 53 | if (mSpellCheck) { |
michael@0 | 54 | mSpellCheck->BeginUpdateDictionary(); |
michael@0 | 55 | } |
michael@0 | 56 | } |
michael@0 | 57 | ~UpdateDictionnaryHolder() { |
michael@0 | 58 | if (mSpellCheck) { |
michael@0 | 59 | mSpellCheck->EndUpdateDictionary(); |
michael@0 | 60 | } |
michael@0 | 61 | } |
michael@0 | 62 | }; |
michael@0 | 63 | |
michael@0 | 64 | #define CPS_PREF_NAME NS_LITERAL_STRING("spellcheck.lang") |
michael@0 | 65 | |
michael@0 | 66 | /** |
michael@0 | 67 | * Gets the URI of aEditor's document. |
michael@0 | 68 | */ |
michael@0 | 69 | static nsresult |
michael@0 | 70 | GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI) |
michael@0 | 71 | { |
michael@0 | 72 | NS_ENSURE_ARG_POINTER(aEditor); |
michael@0 | 73 | NS_ENSURE_ARG_POINTER(aURI); |
michael@0 | 74 | |
michael@0 | 75 | nsCOMPtr<nsIDOMDocument> domDoc; |
michael@0 | 76 | aEditor->GetDocument(getter_AddRefs(domDoc)); |
michael@0 | 77 | NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); |
michael@0 | 78 | |
michael@0 | 79 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); |
michael@0 | 80 | NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); |
michael@0 | 81 | |
michael@0 | 82 | nsCOMPtr<nsIURI> docUri = doc->GetDocumentURI(); |
michael@0 | 83 | NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE); |
michael@0 | 84 | |
michael@0 | 85 | *aURI = docUri; |
michael@0 | 86 | NS_ADDREF(*aURI); |
michael@0 | 87 | return NS_OK; |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | static already_AddRefed<nsILoadContext> |
michael@0 | 91 | GetLoadContext(nsIEditor* aEditor) |
michael@0 | 92 | { |
michael@0 | 93 | nsCOMPtr<nsIDOMDocument> domDoc; |
michael@0 | 94 | aEditor->GetDocument(getter_AddRefs(domDoc)); |
michael@0 | 95 | NS_ENSURE_TRUE(domDoc, nullptr); |
michael@0 | 96 | |
michael@0 | 97 | nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); |
michael@0 | 98 | NS_ENSURE_TRUE(doc, nullptr); |
michael@0 | 99 | |
michael@0 | 100 | nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext(); |
michael@0 | 101 | return loadContext.forget(); |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | /** |
michael@0 | 105 | * Fetches the dictionary stored in content prefs and maintains state during the |
michael@0 | 106 | * fetch, which is asynchronous. |
michael@0 | 107 | */ |
michael@0 | 108 | class DictionaryFetcher MOZ_FINAL : public nsIContentPrefCallback2 |
michael@0 | 109 | { |
michael@0 | 110 | public: |
michael@0 | 111 | NS_DECL_ISUPPORTS |
michael@0 | 112 | |
michael@0 | 113 | DictionaryFetcher(nsEditorSpellCheck* aSpellCheck, |
michael@0 | 114 | nsIEditorSpellCheckCallback* aCallback, |
michael@0 | 115 | uint32_t aGroup) |
michael@0 | 116 | : mCallback(aCallback), mGroup(aGroup), mSpellCheck(aSpellCheck) {} |
michael@0 | 117 | |
michael@0 | 118 | NS_IMETHOD Fetch(nsIEditor* aEditor); |
michael@0 | 119 | |
michael@0 | 120 | NS_IMETHOD HandleResult(nsIContentPref* aPref) |
michael@0 | 121 | { |
michael@0 | 122 | nsCOMPtr<nsIVariant> value; |
michael@0 | 123 | nsresult rv = aPref->GetValue(getter_AddRefs(value)); |
michael@0 | 124 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 125 | value->GetAsAString(mDictionary); |
michael@0 | 126 | return NS_OK; |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | NS_IMETHOD HandleCompletion(uint16_t reason) |
michael@0 | 130 | { |
michael@0 | 131 | mSpellCheck->DictionaryFetched(this); |
michael@0 | 132 | return NS_OK; |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | NS_IMETHOD HandleError(nsresult error) |
michael@0 | 136 | { |
michael@0 | 137 | return NS_OK; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | nsCOMPtr<nsIEditorSpellCheckCallback> mCallback; |
michael@0 | 141 | uint32_t mGroup; |
michael@0 | 142 | nsString mRootContentLang; |
michael@0 | 143 | nsString mRootDocContentLang; |
michael@0 | 144 | nsString mDictionary; |
michael@0 | 145 | |
michael@0 | 146 | private: |
michael@0 | 147 | nsRefPtr<nsEditorSpellCheck> mSpellCheck; |
michael@0 | 148 | }; |
michael@0 | 149 | NS_IMPL_ISUPPORTS(DictionaryFetcher, nsIContentPrefCallback2) |
michael@0 | 150 | |
michael@0 | 151 | NS_IMETHODIMP |
michael@0 | 152 | DictionaryFetcher::Fetch(nsIEditor* aEditor) |
michael@0 | 153 | { |
michael@0 | 154 | NS_ENSURE_ARG_POINTER(aEditor); |
michael@0 | 155 | |
michael@0 | 156 | nsresult rv; |
michael@0 | 157 | |
michael@0 | 158 | nsCOMPtr<nsIURI> docUri; |
michael@0 | 159 | rv = GetDocumentURI(aEditor, getter_AddRefs(docUri)); |
michael@0 | 160 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 161 | |
michael@0 | 162 | nsAutoCString docUriSpec; |
michael@0 | 163 | rv = docUri->GetSpec(docUriSpec); |
michael@0 | 164 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 165 | |
michael@0 | 166 | nsCOMPtr<nsIContentPrefService2> contentPrefService = |
michael@0 | 167 | do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); |
michael@0 | 168 | NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_AVAILABLE); |
michael@0 | 169 | |
michael@0 | 170 | nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor); |
michael@0 | 171 | rv = contentPrefService->GetByDomainAndName(NS_ConvertUTF8toUTF16(docUriSpec), |
michael@0 | 172 | CPS_PREF_NAME, loadContext, |
michael@0 | 173 | this); |
michael@0 | 174 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 175 | |
michael@0 | 176 | return NS_OK; |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | /** |
michael@0 | 180 | * Stores the current dictionary for aEditor's document URL. |
michael@0 | 181 | */ |
michael@0 | 182 | static nsresult |
michael@0 | 183 | StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary) |
michael@0 | 184 | { |
michael@0 | 185 | NS_ENSURE_ARG_POINTER(aEditor); |
michael@0 | 186 | |
michael@0 | 187 | nsresult rv; |
michael@0 | 188 | |
michael@0 | 189 | nsCOMPtr<nsIURI> docUri; |
michael@0 | 190 | rv = GetDocumentURI(aEditor, getter_AddRefs(docUri)); |
michael@0 | 191 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 192 | |
michael@0 | 193 | nsAutoCString docUriSpec; |
michael@0 | 194 | rv = docUri->GetSpec(docUriSpec); |
michael@0 | 195 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 196 | |
michael@0 | 197 | nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID); |
michael@0 | 198 | NS_ENSURE_TRUE(prefValue, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 199 | prefValue->SetAsAString(aDictionary); |
michael@0 | 200 | |
michael@0 | 201 | nsCOMPtr<nsIContentPrefService2> contentPrefService = |
michael@0 | 202 | do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); |
michael@0 | 203 | NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 204 | |
michael@0 | 205 | nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor); |
michael@0 | 206 | return contentPrefService->Set(NS_ConvertUTF8toUTF16(docUriSpec), |
michael@0 | 207 | CPS_PREF_NAME, prefValue, loadContext, |
michael@0 | 208 | nullptr); |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | /** |
michael@0 | 212 | * Forgets the current dictionary stored for aEditor's document URL. |
michael@0 | 213 | */ |
michael@0 | 214 | static nsresult |
michael@0 | 215 | ClearCurrentDictionary(nsIEditor* aEditor) |
michael@0 | 216 | { |
michael@0 | 217 | NS_ENSURE_ARG_POINTER(aEditor); |
michael@0 | 218 | |
michael@0 | 219 | nsresult rv; |
michael@0 | 220 | |
michael@0 | 221 | nsCOMPtr<nsIURI> docUri; |
michael@0 | 222 | rv = GetDocumentURI(aEditor, getter_AddRefs(docUri)); |
michael@0 | 223 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 224 | |
michael@0 | 225 | nsAutoCString docUriSpec; |
michael@0 | 226 | rv = docUri->GetSpec(docUriSpec); |
michael@0 | 227 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 228 | |
michael@0 | 229 | nsCOMPtr<nsIContentPrefService2> contentPrefService = |
michael@0 | 230 | do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID); |
michael@0 | 231 | NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 232 | |
michael@0 | 233 | nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor); |
michael@0 | 234 | return contentPrefService->RemoveByDomainAndName( |
michael@0 | 235 | NS_ConvertUTF8toUTF16(docUriSpec), CPS_PREF_NAME, loadContext, nullptr); |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck) |
michael@0 | 239 | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditorSpellCheck) |
michael@0 | 240 | |
michael@0 | 241 | NS_INTERFACE_MAP_BEGIN(nsEditorSpellCheck) |
michael@0 | 242 | NS_INTERFACE_MAP_ENTRY(nsIEditorSpellCheck) |
michael@0 | 243 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditorSpellCheck) |
michael@0 | 244 | NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsEditorSpellCheck) |
michael@0 | 245 | NS_INTERFACE_MAP_END |
michael@0 | 246 | |
michael@0 | 247 | NS_IMPL_CYCLE_COLLECTION(nsEditorSpellCheck, |
michael@0 | 248 | mEditor, |
michael@0 | 249 | mSpellChecker, |
michael@0 | 250 | mTxtSrvFilter) |
michael@0 | 251 | |
michael@0 | 252 | nsEditorSpellCheck::nsEditorSpellCheck() |
michael@0 | 253 | : mSuggestedWordIndex(0) |
michael@0 | 254 | , mDictionaryIndex(0) |
michael@0 | 255 | , mEditor(nullptr) |
michael@0 | 256 | , mDictionaryFetcherGroup(0) |
michael@0 | 257 | , mUpdateDictionaryRunning(false) |
michael@0 | 258 | { |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | nsEditorSpellCheck::~nsEditorSpellCheck() |
michael@0 | 262 | { |
michael@0 | 263 | // Make sure we blow the spellchecker away, just in |
michael@0 | 264 | // case it hasn't been destroyed already. |
michael@0 | 265 | mSpellChecker = nullptr; |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | // The problem is that if the spell checker does not exist, we can not tell |
michael@0 | 269 | // which dictionaries are installed. This function works around the problem, |
michael@0 | 270 | // allowing callers to ask if we can spell check without actually doing so (and |
michael@0 | 271 | // enabling or disabling UI as necessary). This just creates a spellcheck |
michael@0 | 272 | // object if needed and asks it for the dictionary list. |
michael@0 | 273 | NS_IMETHODIMP |
michael@0 | 274 | nsEditorSpellCheck::CanSpellCheck(bool* _retval) |
michael@0 | 275 | { |
michael@0 | 276 | nsresult rv; |
michael@0 | 277 | nsCOMPtr<nsISpellChecker> spellChecker; |
michael@0 | 278 | if (! mSpellChecker) { |
michael@0 | 279 | spellChecker = do_CreateInstance(NS_SPELLCHECKER_CONTRACTID, &rv); |
michael@0 | 280 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 281 | } else { |
michael@0 | 282 | spellChecker = mSpellChecker; |
michael@0 | 283 | } |
michael@0 | 284 | nsTArray<nsString> dictList; |
michael@0 | 285 | rv = spellChecker->GetDictionaryList(&dictList); |
michael@0 | 286 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 287 | |
michael@0 | 288 | *_retval = (dictList.Length() > 0); |
michael@0 | 289 | return NS_OK; |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | // Instances of this class can be used as either runnables or RAII helpers. |
michael@0 | 293 | class CallbackCaller MOZ_FINAL : public nsRunnable |
michael@0 | 294 | { |
michael@0 | 295 | public: |
michael@0 | 296 | explicit CallbackCaller(nsIEditorSpellCheckCallback* aCallback) |
michael@0 | 297 | : mCallback(aCallback) {} |
michael@0 | 298 | |
michael@0 | 299 | ~CallbackCaller() |
michael@0 | 300 | { |
michael@0 | 301 | Run(); |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | NS_IMETHOD Run() |
michael@0 | 305 | { |
michael@0 | 306 | if (mCallback) { |
michael@0 | 307 | mCallback->EditorSpellCheckDone(); |
michael@0 | 308 | mCallback = nullptr; |
michael@0 | 309 | } |
michael@0 | 310 | return NS_OK; |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | private: |
michael@0 | 314 | nsCOMPtr<nsIEditorSpellCheckCallback> mCallback; |
michael@0 | 315 | }; |
michael@0 | 316 | |
michael@0 | 317 | NS_IMETHODIMP |
michael@0 | 318 | nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, bool aEnableSelectionChecking, nsIEditorSpellCheckCallback* aCallback) |
michael@0 | 319 | { |
michael@0 | 320 | NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER); |
michael@0 | 321 | mEditor = aEditor; |
michael@0 | 322 | |
michael@0 | 323 | nsresult rv; |
michael@0 | 324 | |
michael@0 | 325 | // We can spell check with any editor type |
michael@0 | 326 | nsCOMPtr<nsITextServicesDocument>tsDoc = |
michael@0 | 327 | do_CreateInstance("@mozilla.org/textservices/textservicesdocument;1", &rv); |
michael@0 | 328 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 329 | |
michael@0 | 330 | NS_ENSURE_TRUE(tsDoc, NS_ERROR_NULL_POINTER); |
michael@0 | 331 | |
michael@0 | 332 | tsDoc->SetFilter(mTxtSrvFilter); |
michael@0 | 333 | |
michael@0 | 334 | // Pass the editor to the text services document |
michael@0 | 335 | rv = tsDoc->InitWithEditor(aEditor); |
michael@0 | 336 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 337 | |
michael@0 | 338 | if (aEnableSelectionChecking) { |
michael@0 | 339 | // Find out if the section is collapsed or not. |
michael@0 | 340 | // If it isn't, we want to spellcheck just the selection. |
michael@0 | 341 | |
michael@0 | 342 | nsCOMPtr<nsISelection> selection; |
michael@0 | 343 | |
michael@0 | 344 | rv = aEditor->GetSelection(getter_AddRefs(selection)); |
michael@0 | 345 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 346 | NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); |
michael@0 | 347 | |
michael@0 | 348 | int32_t count = 0; |
michael@0 | 349 | |
michael@0 | 350 | rv = selection->GetRangeCount(&count); |
michael@0 | 351 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 352 | |
michael@0 | 353 | if (count > 0) { |
michael@0 | 354 | nsCOMPtr<nsIDOMRange> range; |
michael@0 | 355 | |
michael@0 | 356 | rv = selection->GetRangeAt(0, getter_AddRefs(range)); |
michael@0 | 357 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 358 | |
michael@0 | 359 | bool collapsed = false; |
michael@0 | 360 | rv = range->GetCollapsed(&collapsed); |
michael@0 | 361 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 362 | |
michael@0 | 363 | if (!collapsed) { |
michael@0 | 364 | // We don't want to touch the range in the selection, |
michael@0 | 365 | // so create a new copy of it. |
michael@0 | 366 | |
michael@0 | 367 | nsCOMPtr<nsIDOMRange> rangeBounds; |
michael@0 | 368 | rv = range->CloneRange(getter_AddRefs(rangeBounds)); |
michael@0 | 369 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 370 | NS_ENSURE_TRUE(rangeBounds, NS_ERROR_FAILURE); |
michael@0 | 371 | |
michael@0 | 372 | // Make sure the new range spans complete words. |
michael@0 | 373 | |
michael@0 | 374 | rv = tsDoc->ExpandRangeToWordBoundaries(rangeBounds); |
michael@0 | 375 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 376 | |
michael@0 | 377 | // Now tell the text services that you only want |
michael@0 | 378 | // to iterate over the text in this range. |
michael@0 | 379 | |
michael@0 | 380 | rv = tsDoc->SetExtent(rangeBounds); |
michael@0 | 381 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 382 | } |
michael@0 | 383 | } |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | mSpellChecker = do_CreateInstance(NS_SPELLCHECKER_CONTRACTID, &rv); |
michael@0 | 387 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 388 | |
michael@0 | 389 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NULL_POINTER); |
michael@0 | 390 | |
michael@0 | 391 | rv = mSpellChecker->SetDocument(tsDoc, true); |
michael@0 | 392 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 393 | |
michael@0 | 394 | // do not fail if UpdateCurrentDictionary fails because this method may |
michael@0 | 395 | // succeed later. |
michael@0 | 396 | rv = UpdateCurrentDictionary(aCallback); |
michael@0 | 397 | if (NS_FAILED(rv) && aCallback) { |
michael@0 | 398 | // However, if it does fail, we still need to call the callback since we |
michael@0 | 399 | // discard the failure. Do it asynchronously so that the caller is always |
michael@0 | 400 | // guaranteed async behavior. |
michael@0 | 401 | nsRefPtr<CallbackCaller> caller = new CallbackCaller(aCallback); |
michael@0 | 402 | NS_ENSURE_STATE(caller); |
michael@0 | 403 | rv = NS_DispatchToMainThread(caller); |
michael@0 | 404 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | return NS_OK; |
michael@0 | 408 | } |
michael@0 | 409 | |
michael@0 | 410 | NS_IMETHODIMP |
michael@0 | 411 | nsEditorSpellCheck::GetNextMisspelledWord(char16_t **aNextMisspelledWord) |
michael@0 | 412 | { |
michael@0 | 413 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 414 | |
michael@0 | 415 | nsAutoString nextMisspelledWord; |
michael@0 | 416 | |
michael@0 | 417 | DeleteSuggestedWordList(); |
michael@0 | 418 | // Beware! This may flush notifications via synchronous |
michael@0 | 419 | // ScrollSelectionIntoView. |
michael@0 | 420 | nsresult rv = mSpellChecker->NextMisspelledWord(nextMisspelledWord, |
michael@0 | 421 | &mSuggestedWordList); |
michael@0 | 422 | |
michael@0 | 423 | *aNextMisspelledWord = ToNewUnicode(nextMisspelledWord); |
michael@0 | 424 | return rv; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | NS_IMETHODIMP |
michael@0 | 428 | nsEditorSpellCheck::GetSuggestedWord(char16_t **aSuggestedWord) |
michael@0 | 429 | { |
michael@0 | 430 | nsAutoString word; |
michael@0 | 431 | if ( mSuggestedWordIndex < int32_t(mSuggestedWordList.Length())) |
michael@0 | 432 | { |
michael@0 | 433 | *aSuggestedWord = ToNewUnicode(mSuggestedWordList[mSuggestedWordIndex]); |
michael@0 | 434 | mSuggestedWordIndex++; |
michael@0 | 435 | } else { |
michael@0 | 436 | // A blank string signals that there are no more strings |
michael@0 | 437 | *aSuggestedWord = ToNewUnicode(EmptyString()); |
michael@0 | 438 | } |
michael@0 | 439 | return NS_OK; |
michael@0 | 440 | } |
michael@0 | 441 | |
michael@0 | 442 | NS_IMETHODIMP |
michael@0 | 443 | nsEditorSpellCheck::CheckCurrentWord(const char16_t *aSuggestedWord, |
michael@0 | 444 | bool *aIsMisspelled) |
michael@0 | 445 | { |
michael@0 | 446 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 447 | |
michael@0 | 448 | DeleteSuggestedWordList(); |
michael@0 | 449 | return mSpellChecker->CheckWord(nsDependentString(aSuggestedWord), |
michael@0 | 450 | aIsMisspelled, &mSuggestedWordList); |
michael@0 | 451 | } |
michael@0 | 452 | |
michael@0 | 453 | NS_IMETHODIMP |
michael@0 | 454 | nsEditorSpellCheck::CheckCurrentWordNoSuggest(const char16_t *aSuggestedWord, |
michael@0 | 455 | bool *aIsMisspelled) |
michael@0 | 456 | { |
michael@0 | 457 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 458 | |
michael@0 | 459 | return mSpellChecker->CheckWord(nsDependentString(aSuggestedWord), |
michael@0 | 460 | aIsMisspelled, nullptr); |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | NS_IMETHODIMP |
michael@0 | 464 | nsEditorSpellCheck::ReplaceWord(const char16_t *aMisspelledWord, |
michael@0 | 465 | const char16_t *aReplaceWord, |
michael@0 | 466 | bool allOccurrences) |
michael@0 | 467 | { |
michael@0 | 468 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 469 | |
michael@0 | 470 | return mSpellChecker->Replace(nsDependentString(aMisspelledWord), |
michael@0 | 471 | nsDependentString(aReplaceWord), allOccurrences); |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | NS_IMETHODIMP |
michael@0 | 475 | nsEditorSpellCheck::IgnoreWordAllOccurrences(const char16_t *aWord) |
michael@0 | 476 | { |
michael@0 | 477 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 478 | |
michael@0 | 479 | return mSpellChecker->IgnoreAll(nsDependentString(aWord)); |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | NS_IMETHODIMP |
michael@0 | 483 | nsEditorSpellCheck::GetPersonalDictionary() |
michael@0 | 484 | { |
michael@0 | 485 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 486 | |
michael@0 | 487 | // We can spell check with any editor type |
michael@0 | 488 | mDictionaryList.Clear(); |
michael@0 | 489 | mDictionaryIndex = 0; |
michael@0 | 490 | return mSpellChecker->GetPersonalDictionary(&mDictionaryList); |
michael@0 | 491 | } |
michael@0 | 492 | |
michael@0 | 493 | NS_IMETHODIMP |
michael@0 | 494 | nsEditorSpellCheck::GetPersonalDictionaryWord(char16_t **aDictionaryWord) |
michael@0 | 495 | { |
michael@0 | 496 | if ( mDictionaryIndex < int32_t( mDictionaryList.Length())) |
michael@0 | 497 | { |
michael@0 | 498 | *aDictionaryWord = ToNewUnicode(mDictionaryList[mDictionaryIndex]); |
michael@0 | 499 | mDictionaryIndex++; |
michael@0 | 500 | } else { |
michael@0 | 501 | // A blank string signals that there are no more strings |
michael@0 | 502 | *aDictionaryWord = ToNewUnicode(EmptyString()); |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | return NS_OK; |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | NS_IMETHODIMP |
michael@0 | 509 | nsEditorSpellCheck::AddWordToDictionary(const char16_t *aWord) |
michael@0 | 510 | { |
michael@0 | 511 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 512 | |
michael@0 | 513 | return mSpellChecker->AddWordToPersonalDictionary(nsDependentString(aWord)); |
michael@0 | 514 | } |
michael@0 | 515 | |
michael@0 | 516 | NS_IMETHODIMP |
michael@0 | 517 | nsEditorSpellCheck::RemoveWordFromDictionary(const char16_t *aWord) |
michael@0 | 518 | { |
michael@0 | 519 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 520 | |
michael@0 | 521 | return mSpellChecker->RemoveWordFromPersonalDictionary(nsDependentString(aWord)); |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | NS_IMETHODIMP |
michael@0 | 525 | nsEditorSpellCheck::GetDictionaryList(char16_t ***aDictionaryList, uint32_t *aCount) |
michael@0 | 526 | { |
michael@0 | 527 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 528 | |
michael@0 | 529 | NS_ENSURE_TRUE(aDictionaryList && aCount, NS_ERROR_NULL_POINTER); |
michael@0 | 530 | |
michael@0 | 531 | *aDictionaryList = 0; |
michael@0 | 532 | *aCount = 0; |
michael@0 | 533 | |
michael@0 | 534 | nsTArray<nsString> dictList; |
michael@0 | 535 | |
michael@0 | 536 | nsresult rv = mSpellChecker->GetDictionaryList(&dictList); |
michael@0 | 537 | |
michael@0 | 538 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 539 | |
michael@0 | 540 | char16_t **tmpPtr = 0; |
michael@0 | 541 | |
michael@0 | 542 | if (dictList.Length() < 1) |
michael@0 | 543 | { |
michael@0 | 544 | // If there are no dictionaries, return an array containing |
michael@0 | 545 | // one element and a count of one. |
michael@0 | 546 | |
michael@0 | 547 | tmpPtr = (char16_t **)nsMemory::Alloc(sizeof(char16_t *)); |
michael@0 | 548 | |
michael@0 | 549 | NS_ENSURE_TRUE(tmpPtr, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 550 | |
michael@0 | 551 | *tmpPtr = 0; |
michael@0 | 552 | *aDictionaryList = tmpPtr; |
michael@0 | 553 | *aCount = 0; |
michael@0 | 554 | |
michael@0 | 555 | return NS_OK; |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | tmpPtr = (char16_t **)nsMemory::Alloc(sizeof(char16_t *) * dictList.Length()); |
michael@0 | 559 | |
michael@0 | 560 | NS_ENSURE_TRUE(tmpPtr, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 561 | |
michael@0 | 562 | *aDictionaryList = tmpPtr; |
michael@0 | 563 | *aCount = dictList.Length(); |
michael@0 | 564 | |
michael@0 | 565 | uint32_t i; |
michael@0 | 566 | |
michael@0 | 567 | for (i = 0; i < *aCount; i++) |
michael@0 | 568 | { |
michael@0 | 569 | tmpPtr[i] = ToNewUnicode(dictList[i]); |
michael@0 | 570 | } |
michael@0 | 571 | |
michael@0 | 572 | return rv; |
michael@0 | 573 | } |
michael@0 | 574 | |
michael@0 | 575 | NS_IMETHODIMP |
michael@0 | 576 | nsEditorSpellCheck::GetCurrentDictionary(nsAString& aDictionary) |
michael@0 | 577 | { |
michael@0 | 578 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 579 | |
michael@0 | 580 | return mSpellChecker->GetCurrentDictionary(aDictionary); |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | NS_IMETHODIMP |
michael@0 | 584 | nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary) |
michael@0 | 585 | { |
michael@0 | 586 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 587 | |
michael@0 | 588 | nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this; |
michael@0 | 589 | |
michael@0 | 590 | // The purpose of mUpdateDictionaryRunning is to avoid doing all of this if |
michael@0 | 591 | // UpdateCurrentDictionary's helper method DictionaryFetched, which calls us, |
michael@0 | 592 | // is on the stack. |
michael@0 | 593 | if (!mUpdateDictionaryRunning) { |
michael@0 | 594 | |
michael@0 | 595 | // Ignore pending dictionary fetchers by increasing this number. |
michael@0 | 596 | mDictionaryFetcherGroup++; |
michael@0 | 597 | |
michael@0 | 598 | nsDefaultStringComparator comparator; |
michael@0 | 599 | nsAutoString langCode; |
michael@0 | 600 | int32_t dashIdx = aDictionary.FindChar('-'); |
michael@0 | 601 | if (dashIdx != -1) { |
michael@0 | 602 | langCode.Assign(Substring(aDictionary, 0, dashIdx)); |
michael@0 | 603 | } else { |
michael@0 | 604 | langCode.Assign(aDictionary); |
michael@0 | 605 | } |
michael@0 | 606 | |
michael@0 | 607 | if (mPreferredLang.IsEmpty() || !nsStyleUtil::DashMatchCompare(mPreferredLang, langCode, comparator)) { |
michael@0 | 608 | // When user sets dictionary manually, we store this value associated |
michael@0 | 609 | // with editor url. |
michael@0 | 610 | StoreCurrentDictionary(mEditor, aDictionary); |
michael@0 | 611 | } else { |
michael@0 | 612 | // If user sets a dictionary matching (even partially), lang defined by |
michael@0 | 613 | // document, we consider content pref has been canceled, and we clear it. |
michael@0 | 614 | ClearCurrentDictionary(mEditor); |
michael@0 | 615 | } |
michael@0 | 616 | |
michael@0 | 617 | // Also store it in as a preference. It will be used as a default value |
michael@0 | 618 | // when everything else fails. |
michael@0 | 619 | Preferences::SetString("spellchecker.dictionary", aDictionary); |
michael@0 | 620 | } |
michael@0 | 621 | return mSpellChecker->SetCurrentDictionary(aDictionary); |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | NS_IMETHODIMP |
michael@0 | 625 | nsEditorSpellCheck::CheckCurrentDictionary() |
michael@0 | 626 | { |
michael@0 | 627 | mSpellChecker->CheckCurrentDictionary(); |
michael@0 | 628 | |
michael@0 | 629 | // Check if our current dictionary is still available. |
michael@0 | 630 | nsAutoString currentDictionary; |
michael@0 | 631 | nsresult rv = GetCurrentDictionary(currentDictionary); |
michael@0 | 632 | if (NS_SUCCEEDED(rv) && !currentDictionary.IsEmpty()) { |
michael@0 | 633 | return NS_OK; |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | // If our preferred current dictionary has gone, pick another one. |
michael@0 | 637 | nsTArray<nsString> dictList; |
michael@0 | 638 | rv = mSpellChecker->GetDictionaryList(&dictList); |
michael@0 | 639 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 640 | |
michael@0 | 641 | if (dictList.Length() > 0) { |
michael@0 | 642 | rv = SetCurrentDictionary(dictList[0]); |
michael@0 | 643 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | return NS_OK; |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | NS_IMETHODIMP |
michael@0 | 650 | nsEditorSpellCheck::UninitSpellChecker() |
michael@0 | 651 | { |
michael@0 | 652 | NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); |
michael@0 | 653 | |
michael@0 | 654 | // Cleanup - kill the spell checker |
michael@0 | 655 | DeleteSuggestedWordList(); |
michael@0 | 656 | mDictionaryList.Clear(); |
michael@0 | 657 | mDictionaryIndex = 0; |
michael@0 | 658 | mSpellChecker = 0; |
michael@0 | 659 | return NS_OK; |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | |
michael@0 | 663 | /* void setFilter (in nsITextServicesFilter filter); */ |
michael@0 | 664 | NS_IMETHODIMP |
michael@0 | 665 | nsEditorSpellCheck::SetFilter(nsITextServicesFilter *filter) |
michael@0 | 666 | { |
michael@0 | 667 | mTxtSrvFilter = filter; |
michael@0 | 668 | return NS_OK; |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | nsresult |
michael@0 | 672 | nsEditorSpellCheck::DeleteSuggestedWordList() |
michael@0 | 673 | { |
michael@0 | 674 | mSuggestedWordList.Clear(); |
michael@0 | 675 | mSuggestedWordIndex = 0; |
michael@0 | 676 | return NS_OK; |
michael@0 | 677 | } |
michael@0 | 678 | |
michael@0 | 679 | NS_IMETHODIMP |
michael@0 | 680 | nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallback) |
michael@0 | 681 | { |
michael@0 | 682 | nsresult rv; |
michael@0 | 683 | |
michael@0 | 684 | nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this; |
michael@0 | 685 | |
michael@0 | 686 | // Get language with html5 algorithm |
michael@0 | 687 | nsCOMPtr<nsIContent> rootContent; |
michael@0 | 688 | nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor); |
michael@0 | 689 | if (htmlEditor) { |
michael@0 | 690 | rootContent = htmlEditor->GetActiveEditingHost(); |
michael@0 | 691 | } else { |
michael@0 | 692 | nsCOMPtr<nsIDOMElement> rootElement; |
michael@0 | 693 | rv = mEditor->GetRootElement(getter_AddRefs(rootElement)); |
michael@0 | 694 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 695 | rootContent = do_QueryInterface(rootElement); |
michael@0 | 696 | } |
michael@0 | 697 | NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE); |
michael@0 | 698 | |
michael@0 | 699 | DictionaryFetcher* fetcher = new DictionaryFetcher(this, aCallback, |
michael@0 | 700 | mDictionaryFetcherGroup); |
michael@0 | 701 | rootContent->GetLang(fetcher->mRootContentLang); |
michael@0 | 702 | nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc(); |
michael@0 | 703 | NS_ENSURE_STATE(doc); |
michael@0 | 704 | doc->GetContentLanguage(fetcher->mRootDocContentLang); |
michael@0 | 705 | |
michael@0 | 706 | rv = fetcher->Fetch(mEditor); |
michael@0 | 707 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 708 | |
michael@0 | 709 | return NS_OK; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | nsresult |
michael@0 | 713 | nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher) |
michael@0 | 714 | { |
michael@0 | 715 | nsRefPtr<nsEditorSpellCheck> kungFuDeathGrip = this; |
michael@0 | 716 | |
michael@0 | 717 | nsresult rv = NS_OK; |
michael@0 | 718 | |
michael@0 | 719 | // Important: declare the holder after the callback caller so that the former |
michael@0 | 720 | // is destructed first so that it's not active when the callback is called. |
michael@0 | 721 | CallbackCaller callbackCaller(aFetcher->mCallback); |
michael@0 | 722 | UpdateDictionnaryHolder holder(this); |
michael@0 | 723 | |
michael@0 | 724 | if (aFetcher->mGroup < mDictionaryFetcherGroup) { |
michael@0 | 725 | // SetCurrentDictionary was called after the fetch started. Don't overwrite |
michael@0 | 726 | // that dictionary with the fetched one. |
michael@0 | 727 | return NS_OK; |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | mPreferredLang.Assign(aFetcher->mRootContentLang); |
michael@0 | 731 | |
michael@0 | 732 | // If we successfully fetched a dictionary from content prefs, do not go |
michael@0 | 733 | // further. Use this exact dictionary. |
michael@0 | 734 | nsAutoString dictName; |
michael@0 | 735 | dictName.Assign(aFetcher->mDictionary); |
michael@0 | 736 | if (!dictName.IsEmpty()) { |
michael@0 | 737 | if (NS_FAILED(SetCurrentDictionary(dictName))) { |
michael@0 | 738 | // may be dictionary was uninstalled ? |
michael@0 | 739 | ClearCurrentDictionary(mEditor); |
michael@0 | 740 | } |
michael@0 | 741 | return NS_OK; |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | if (mPreferredLang.IsEmpty()) { |
michael@0 | 745 | mPreferredLang.Assign(aFetcher->mRootDocContentLang); |
michael@0 | 746 | } |
michael@0 | 747 | |
michael@0 | 748 | // Then, try to use language computed from element |
michael@0 | 749 | if (!mPreferredLang.IsEmpty()) { |
michael@0 | 750 | dictName.Assign(mPreferredLang); |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | // otherwise, get language from preferences |
michael@0 | 754 | nsAutoString preferedDict(Preferences::GetLocalizedString("spellchecker.dictionary")); |
michael@0 | 755 | if (dictName.IsEmpty()) { |
michael@0 | 756 | dictName.Assign(preferedDict); |
michael@0 | 757 | } |
michael@0 | 758 | |
michael@0 | 759 | if (dictName.IsEmpty()) |
michael@0 | 760 | { |
michael@0 | 761 | // Prefs didn't give us a dictionary name, so just get the current |
michael@0 | 762 | // locale and use that as the default dictionary name! |
michael@0 | 763 | |
michael@0 | 764 | nsCOMPtr<nsIXULChromeRegistry> packageRegistry = |
michael@0 | 765 | mozilla::services::GetXULChromeRegistryService(); |
michael@0 | 766 | |
michael@0 | 767 | if (packageRegistry) { |
michael@0 | 768 | nsAutoCString utf8DictName; |
michael@0 | 769 | rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"), |
michael@0 | 770 | utf8DictName); |
michael@0 | 771 | AppendUTF8toUTF16(utf8DictName, dictName); |
michael@0 | 772 | } |
michael@0 | 773 | } |
michael@0 | 774 | |
michael@0 | 775 | if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) { |
michael@0 | 776 | rv = SetCurrentDictionary(dictName); |
michael@0 | 777 | if (NS_FAILED(rv)) { |
michael@0 | 778 | // required dictionary was not available. Try to get a dictionary |
michael@0 | 779 | // matching at least language part of dictName: |
michael@0 | 780 | |
michael@0 | 781 | nsAutoString langCode; |
michael@0 | 782 | int32_t dashIdx = dictName.FindChar('-'); |
michael@0 | 783 | if (dashIdx != -1) { |
michael@0 | 784 | langCode.Assign(Substring(dictName, 0, dashIdx)); |
michael@0 | 785 | } else { |
michael@0 | 786 | langCode.Assign(dictName); |
michael@0 | 787 | } |
michael@0 | 788 | |
michael@0 | 789 | nsDefaultStringComparator comparator; |
michael@0 | 790 | |
michael@0 | 791 | // try dictionary.spellchecker preference if it starts with langCode (and |
michael@0 | 792 | // if we haven't tried it already) |
michael@0 | 793 | if (!preferedDict.IsEmpty() && !dictName.Equals(preferedDict) && |
michael@0 | 794 | nsStyleUtil::DashMatchCompare(preferedDict, langCode, comparator)) { |
michael@0 | 795 | rv = SetCurrentDictionary(preferedDict); |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | // Otherwise, try langCode (if we haven't tried it already) |
michael@0 | 799 | if (NS_FAILED(rv)) { |
michael@0 | 800 | if (!dictName.Equals(langCode) && !preferedDict.Equals(langCode)) { |
michael@0 | 801 | rv = SetCurrentDictionary(langCode); |
michael@0 | 802 | } |
michael@0 | 803 | } |
michael@0 | 804 | |
michael@0 | 805 | // Otherwise, try any available dictionary aa-XX |
michael@0 | 806 | if (NS_FAILED(rv)) { |
michael@0 | 807 | // loop over avaible dictionaries; if we find one with required |
michael@0 | 808 | // language, use it |
michael@0 | 809 | nsTArray<nsString> dictList; |
michael@0 | 810 | rv = mSpellChecker->GetDictionaryList(&dictList); |
michael@0 | 811 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 812 | int32_t i, count = dictList.Length(); |
michael@0 | 813 | for (i = 0; i < count; i++) { |
michael@0 | 814 | nsAutoString dictStr(dictList.ElementAt(i)); |
michael@0 | 815 | |
michael@0 | 816 | if (dictStr.Equals(dictName) || |
michael@0 | 817 | dictStr.Equals(preferedDict) || |
michael@0 | 818 | dictStr.Equals(langCode)) { |
michael@0 | 819 | // We have already tried it |
michael@0 | 820 | continue; |
michael@0 | 821 | } |
michael@0 | 822 | |
michael@0 | 823 | if (nsStyleUtil::DashMatchCompare(dictStr, langCode, comparator) && |
michael@0 | 824 | NS_SUCCEEDED(SetCurrentDictionary(dictStr))) { |
michael@0 | 825 | break; |
michael@0 | 826 | } |
michael@0 | 827 | } |
michael@0 | 828 | } |
michael@0 | 829 | } |
michael@0 | 830 | } |
michael@0 | 831 | |
michael@0 | 832 | // If we have not set dictionary, and the editable element doesn't have a |
michael@0 | 833 | // lang attribute, we try to get a dictionary. First try LANG environment variable, |
michael@0 | 834 | // then en-US. If it does not work, pick the first one. |
michael@0 | 835 | if (mPreferredLang.IsEmpty()) { |
michael@0 | 836 | nsAutoString currentDictionary; |
michael@0 | 837 | rv = GetCurrentDictionary(currentDictionary); |
michael@0 | 838 | if (NS_FAILED(rv) || currentDictionary.IsEmpty()) { |
michael@0 | 839 | // Try to get current dictionary from environment variable LANG |
michael@0 | 840 | char* env_lang = getenv("LANG"); |
michael@0 | 841 | if (env_lang != nullptr) { |
michael@0 | 842 | nsString lang = NS_ConvertUTF8toUTF16(env_lang); |
michael@0 | 843 | // Strip trailing charset if there is any |
michael@0 | 844 | int32_t dot_pos = lang.FindChar('.'); |
michael@0 | 845 | if (dot_pos != -1) { |
michael@0 | 846 | lang = Substring(lang, 0, dot_pos - 1); |
michael@0 | 847 | } |
michael@0 | 848 | rv = SetCurrentDictionary(lang); |
michael@0 | 849 | } |
michael@0 | 850 | if (NS_FAILED(rv)) { |
michael@0 | 851 | rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US")); |
michael@0 | 852 | if (NS_FAILED(rv)) { |
michael@0 | 853 | nsTArray<nsString> dictList; |
michael@0 | 854 | rv = mSpellChecker->GetDictionaryList(&dictList); |
michael@0 | 855 | if (NS_SUCCEEDED(rv) && dictList.Length() > 0) { |
michael@0 | 856 | SetCurrentDictionary(dictList[0]); |
michael@0 | 857 | } |
michael@0 | 858 | } |
michael@0 | 859 | } |
michael@0 | 860 | } |
michael@0 | 861 | } |
michael@0 | 862 | |
michael@0 | 863 | // If an error was thrown while setting the dictionary, just |
michael@0 | 864 | // fail silently so that the spellchecker dialog is allowed to come |
michael@0 | 865 | // up. The user can manually reset the language to their choice on |
michael@0 | 866 | // the dialog if it is wrong. |
michael@0 | 867 | |
michael@0 | 868 | DeleteSuggestedWordList(); |
michael@0 | 869 | |
michael@0 | 870 | return NS_OK; |
michael@0 | 871 | } |