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