LCOV - code coverage report
Current view: top level - editor/composer - nsEditorSpellCheck.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 413 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 57 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 sts=2 sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <stdlib.h>                     // for getenv
       8             : 
       9             : #include "mozilla/Attributes.h"         // for final
      10             : #include "mozilla/Preferences.h"        // for Preferences
      11             : #include "mozilla/dom/Element.h"        // for Element
      12             : #include "mozilla/dom/Selection.h"
      13             : #include "mozilla/intl/LocaleService.h" // for retrieving app locale
      14             : #include "mozilla/mozalloc.h"           // for operator delete, etc
      15             : #include "nsAString.h"                  // for nsAString::IsEmpty, etc
      16             : #include "nsComponentManagerUtils.h"    // for do_CreateInstance
      17             : #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
      18             : #include "nsDependentSubstring.h"       // for Substring
      19             : #include "nsEditorSpellCheck.h"
      20             : #include "nsError.h"                    // for NS_ERROR_NOT_INITIALIZED, etc
      21             : #include "nsIContent.h"                 // for nsIContent
      22             : #include "nsIContentPrefService.h"      // for nsIContentPrefService, etc
      23             : #include "nsIContentPrefService2.h"     // for nsIContentPrefService2, etc
      24             : #include "nsIDOMDocument.h"             // for nsIDOMDocument
      25             : #include "nsIDOMElement.h"              // for nsIDOMElement
      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 "nsThreadUtils.h"              // for GetMainThreadSerialEventTarget
      38             : #include "nsVariant.h"                  // for nsIWritableVariant, etc
      39             : #include "nsLiteralString.h"            // for NS_LITERAL_STRING, etc
      40             : #include "nsMemory.h"                   // for nsMemory
      41             : #include "nsRange.h"
      42             : #include "nsReadableUtils.h"            // for ToNewUnicode, EmptyString, etc
      43             : #include "nsServiceManagerUtils.h"      // for do_GetService
      44             : #include "nsString.h"                   // for nsAutoString, nsString, etc
      45             : #include "nsStringFwd.h"                // for nsAFlatString
      46             : #include "nsStyleUtil.h"                // for nsStyleUtil
      47             : #include "nsXULAppAPI.h"                // for XRE_GetProcessType
      48             : #include "nsIPlaintextEditor.h"         // for editor flags
      49             : 
      50             : using namespace mozilla;
      51             : using namespace mozilla::dom;
      52             : using mozilla::intl::LocaleService;
      53             : 
      54             : class UpdateDictionaryHolder {
      55             :   private:
      56             :     nsEditorSpellCheck* mSpellCheck;
      57             :   public:
      58             :     explicit UpdateDictionaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) {
      59             :       if (mSpellCheck) {
      60             :         mSpellCheck->BeginUpdateDictionary();
      61             :       }
      62             :     }
      63             :     ~UpdateDictionaryHolder() {
      64             :       if (mSpellCheck) {
      65             :         mSpellCheck->EndUpdateDictionary();
      66             :       }
      67             :     }
      68             : };
      69             : 
      70             : #define CPS_PREF_NAME NS_LITERAL_STRING("spellcheck.lang")
      71             : 
      72             : /**
      73             :  * Gets the URI of aEditor's document.
      74             :  */
      75             : static nsresult
      76           0 : GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI)
      77             : {
      78           0 :   NS_ENSURE_ARG_POINTER(aEditor);
      79           0 :   NS_ENSURE_ARG_POINTER(aURI);
      80             : 
      81           0 :   nsCOMPtr<nsIDOMDocument> domDoc;
      82           0 :   aEditor->GetDocument(getter_AddRefs(domDoc));
      83           0 :   NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
      84             : 
      85           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
      86           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
      87             : 
      88           0 :   nsCOMPtr<nsIURI> docUri = doc->GetDocumentURI();
      89           0 :   NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE);
      90             : 
      91           0 :   *aURI = docUri;
      92           0 :   NS_ADDREF(*aURI);
      93           0 :   return NS_OK;
      94             : }
      95             : 
      96             : static already_AddRefed<nsILoadContext>
      97           0 : GetLoadContext(nsIEditor* aEditor)
      98             : {
      99           0 :   nsCOMPtr<nsIDOMDocument> domDoc;
     100           0 :   aEditor->GetDocument(getter_AddRefs(domDoc));
     101           0 :   NS_ENSURE_TRUE(domDoc, nullptr);
     102             : 
     103           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
     104           0 :   NS_ENSURE_TRUE(doc, nullptr);
     105             : 
     106           0 :   nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
     107           0 :   return loadContext.forget();
     108             : }
     109             : 
     110             : /**
     111             :  * Fetches the dictionary stored in content prefs and maintains state during the
     112             :  * fetch, which is asynchronous.
     113             :  */
     114             : class DictionaryFetcher final : public nsIContentPrefCallback2
     115             : {
     116             : public:
     117             :   NS_DECL_ISUPPORTS
     118             : 
     119           0 :   DictionaryFetcher(nsEditorSpellCheck* aSpellCheck,
     120             :                     nsIEditorSpellCheckCallback* aCallback,
     121             :                     uint32_t aGroup)
     122           0 :     : mCallback(aCallback), mGroup(aGroup), mSpellCheck(aSpellCheck) {}
     123             : 
     124             :   NS_IMETHOD Fetch(nsIEditor* aEditor);
     125             : 
     126           0 :   NS_IMETHOD HandleResult(nsIContentPref* aPref) override
     127             :   {
     128           0 :     nsCOMPtr<nsIVariant> value;
     129           0 :     nsresult rv = aPref->GetValue(getter_AddRefs(value));
     130           0 :     NS_ENSURE_SUCCESS(rv, rv);
     131           0 :     value->GetAsAString(mDictionary);
     132           0 :     return NS_OK;
     133             :   }
     134             : 
     135           0 :   NS_IMETHOD HandleCompletion(uint16_t reason) override
     136             :   {
     137           0 :     mSpellCheck->DictionaryFetched(this);
     138           0 :     return NS_OK;
     139             :   }
     140             : 
     141           0 :   NS_IMETHOD HandleError(nsresult error) override
     142             :   {
     143           0 :     return NS_OK;
     144             :   }
     145             : 
     146             :   nsCOMPtr<nsIEditorSpellCheckCallback> mCallback;
     147             :   uint32_t mGroup;
     148             :   nsString mRootContentLang;
     149             :   nsString mRootDocContentLang;
     150             :   nsString mDictionary;
     151             : 
     152             : private:
     153           0 :   ~DictionaryFetcher() {}
     154             : 
     155             :   RefPtr<nsEditorSpellCheck> mSpellCheck;
     156             : };
     157           0 : NS_IMPL_ISUPPORTS(DictionaryFetcher, nsIContentPrefCallback2)
     158             : 
     159             : NS_IMETHODIMP
     160           0 : DictionaryFetcher::Fetch(nsIEditor* aEditor)
     161             : {
     162           0 :   NS_ENSURE_ARG_POINTER(aEditor);
     163             : 
     164             :   nsresult rv;
     165             : 
     166           0 :   nsCOMPtr<nsIURI> docUri;
     167           0 :   rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
     168           0 :   NS_ENSURE_SUCCESS(rv, rv);
     169             : 
     170           0 :   nsAutoCString docUriSpec;
     171           0 :   rv = docUri->GetSpec(docUriSpec);
     172           0 :   NS_ENSURE_SUCCESS(rv, rv);
     173             : 
     174             :   nsCOMPtr<nsIContentPrefService2> contentPrefService =
     175           0 :     do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
     176           0 :   NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_AVAILABLE);
     177             : 
     178           0 :   nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor);
     179           0 :   rv = contentPrefService->GetByDomainAndName(NS_ConvertUTF8toUTF16(docUriSpec),
     180           0 :                                               CPS_PREF_NAME, loadContext,
     181           0 :                                               this);
     182           0 :   NS_ENSURE_SUCCESS(rv, rv);
     183             : 
     184           0 :   return NS_OK;
     185             : }
     186             : 
     187             : /**
     188             :  * Stores the current dictionary for aEditor's document URL.
     189             :  */
     190             : static nsresult
     191           0 : StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary)
     192             : {
     193           0 :   NS_ENSURE_ARG_POINTER(aEditor);
     194             : 
     195             :   nsresult rv;
     196             : 
     197           0 :   nsCOMPtr<nsIURI> docUri;
     198           0 :   rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
     199           0 :   NS_ENSURE_SUCCESS(rv, rv);
     200             : 
     201           0 :   nsAutoCString docUriSpec;
     202           0 :   rv = docUri->GetSpec(docUriSpec);
     203           0 :   NS_ENSURE_SUCCESS(rv, rv);
     204             : 
     205           0 :   RefPtr<nsVariant> prefValue = new nsVariant();
     206           0 :   prefValue->SetAsAString(aDictionary);
     207             : 
     208             :   nsCOMPtr<nsIContentPrefService2> contentPrefService =
     209           0 :     do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
     210           0 :   NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED);
     211             : 
     212           0 :   nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor);
     213           0 :   return contentPrefService->Set(NS_ConvertUTF8toUTF16(docUriSpec),
     214           0 :                                  CPS_PREF_NAME, prefValue, loadContext,
     215           0 :                                  nullptr);
     216             : }
     217             : 
     218             : /**
     219             :  * Forgets the current dictionary stored for aEditor's document URL.
     220             :  */
     221             : static nsresult
     222           0 : ClearCurrentDictionary(nsIEditor* aEditor)
     223             : {
     224           0 :   NS_ENSURE_ARG_POINTER(aEditor);
     225             : 
     226             :   nsresult rv;
     227             : 
     228           0 :   nsCOMPtr<nsIURI> docUri;
     229           0 :   rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
     230           0 :   NS_ENSURE_SUCCESS(rv, rv);
     231             : 
     232           0 :   nsAutoCString docUriSpec;
     233           0 :   rv = docUri->GetSpec(docUriSpec);
     234           0 :   NS_ENSURE_SUCCESS(rv, rv);
     235             : 
     236             :   nsCOMPtr<nsIContentPrefService2> contentPrefService =
     237           0 :     do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
     238           0 :   NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED);
     239             : 
     240           0 :   nsCOMPtr<nsILoadContext> loadContext = GetLoadContext(aEditor);
     241           0 :   return contentPrefService->RemoveByDomainAndName(
     242           0 :     NS_ConvertUTF8toUTF16(docUriSpec), CPS_PREF_NAME, loadContext, nullptr);
     243             : }
     244             : 
     245           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck)
     246           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditorSpellCheck)
     247             : 
     248           0 : NS_INTERFACE_MAP_BEGIN(nsEditorSpellCheck)
     249           0 :   NS_INTERFACE_MAP_ENTRY(nsIEditorSpellCheck)
     250           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditorSpellCheck)
     251           0 :   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsEditorSpellCheck)
     252           0 : NS_INTERFACE_MAP_END
     253             : 
     254           0 : NS_IMPL_CYCLE_COLLECTION(nsEditorSpellCheck,
     255             :                          mEditor,
     256             :                          mSpellChecker,
     257             :                          mTxtSrvFilter)
     258             : 
     259           0 : nsEditorSpellCheck::nsEditorSpellCheck()
     260             :   : mSuggestedWordIndex(0)
     261             :   , mDictionaryIndex(0)
     262             :   , mEditor(nullptr)
     263             :   , mDictionaryFetcherGroup(0)
     264           0 :   , mUpdateDictionaryRunning(false)
     265             : {
     266           0 : }
     267             : 
     268           0 : nsEditorSpellCheck::~nsEditorSpellCheck()
     269             : {
     270             :   // Make sure we blow the spellchecker away, just in
     271             :   // case it hasn't been destroyed already.
     272           0 :   mSpellChecker = nullptr;
     273           0 : }
     274             : 
     275             : // The problem is that if the spell checker does not exist, we can not tell
     276             : // which dictionaries are installed. This function works around the problem,
     277             : // allowing callers to ask if we can spell check without actually doing so (and
     278             : // enabling or disabling UI as necessary). This just creates a spellcheck
     279             : // object if needed and asks it for the dictionary list.
     280             : NS_IMETHODIMP
     281           0 : nsEditorSpellCheck::CanSpellCheck(bool* _retval)
     282             : {
     283             :   nsresult rv;
     284           0 :   nsCOMPtr<nsISpellChecker> spellChecker;
     285           0 :   if (! mSpellChecker) {
     286           0 :     spellChecker = do_CreateInstance(NS_SPELLCHECKER_CONTRACTID, &rv);
     287           0 :     NS_ENSURE_SUCCESS(rv, rv);
     288             :   } else {
     289           0 :     spellChecker = mSpellChecker;
     290             :   }
     291           0 :   nsTArray<nsString> dictList;
     292           0 :   rv = spellChecker->GetDictionaryList(&dictList);
     293           0 :   NS_ENSURE_SUCCESS(rv, rv);
     294             : 
     295           0 :   *_retval = (dictList.Length() > 0);
     296           0 :   return NS_OK;
     297             : }
     298             : 
     299             : // Instances of this class can be used as either runnables or RAII helpers.
     300             : class CallbackCaller final : public Runnable
     301             : {
     302             : public:
     303           0 :   explicit CallbackCaller(nsIEditorSpellCheckCallback* aCallback)
     304           0 :     : mozilla::Runnable("CallbackCaller")
     305           0 :     , mCallback(aCallback)
     306             :   {
     307           0 :   }
     308             : 
     309           0 :   ~CallbackCaller()
     310           0 :   {
     311           0 :     Run();
     312           0 :   }
     313             : 
     314           0 :   NS_IMETHOD Run() override
     315             :   {
     316           0 :     if (mCallback) {
     317           0 :       mCallback->EditorSpellCheckDone();
     318           0 :       mCallback = nullptr;
     319             :     }
     320           0 :     return NS_OK;
     321             :   }
     322             : 
     323             : private:
     324             :   nsCOMPtr<nsIEditorSpellCheckCallback> mCallback;
     325             : };
     326             : 
     327             : NS_IMETHODIMP
     328           0 : nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, bool aEnableSelectionChecking, nsIEditorSpellCheckCallback* aCallback)
     329             : {
     330           0 :   NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
     331           0 :   mEditor = aEditor;
     332             : 
     333           0 :   nsCOMPtr<nsIDOMDocument> domDoc;
     334           0 :   mEditor->GetDocument(getter_AddRefs(domDoc));
     335           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
     336           0 :   NS_ENSURE_STATE(doc);
     337             : 
     338             :   nsresult rv;
     339             : 
     340             :   // We can spell check with any editor type
     341             :   nsCOMPtr<nsITextServicesDocument>tsDoc =
     342           0 :      do_CreateInstance("@mozilla.org/textservices/textservicesdocument;1", &rv);
     343           0 :   NS_ENSURE_SUCCESS(rv, rv);
     344             : 
     345           0 :   NS_ENSURE_TRUE(tsDoc, NS_ERROR_NULL_POINTER);
     346             : 
     347           0 :   tsDoc->SetFilter(mTxtSrvFilter);
     348             : 
     349             :   // Pass the editor to the text services document
     350           0 :   rv = tsDoc->InitWithEditor(aEditor);
     351           0 :   NS_ENSURE_SUCCESS(rv, rv);
     352             : 
     353           0 :   if (aEnableSelectionChecking) {
     354             :     // Find out if the section is collapsed or not.
     355             :     // If it isn't, we want to spellcheck just the selection.
     356             : 
     357           0 :     nsCOMPtr<nsISelection> domSelection;
     358           0 :     aEditor->GetSelection(getter_AddRefs(domSelection));
     359           0 :     if (NS_WARN_IF(!domSelection)) {
     360           0 :       return NS_ERROR_FAILURE;
     361             :     }
     362           0 :     RefPtr<Selection> selection = domSelection->AsSelection();
     363             : 
     364           0 :     int32_t count = 0;
     365             : 
     366           0 :     rv = selection->GetRangeCount(&count);
     367           0 :     NS_ENSURE_SUCCESS(rv, rv);
     368             : 
     369           0 :     if (count > 0) {
     370           0 :       RefPtr<nsRange> range = selection->GetRangeAt(0);
     371           0 :       NS_ENSURE_STATE(range);
     372             : 
     373           0 :       bool collapsed = false;
     374           0 :       rv = range->GetCollapsed(&collapsed);
     375           0 :       NS_ENSURE_SUCCESS(rv, rv);
     376             : 
     377           0 :       if (!collapsed) {
     378             :         // We don't want to touch the range in the selection,
     379             :         // so create a new copy of it.
     380             : 
     381           0 :         RefPtr<nsRange> rangeBounds = range->CloneRange();
     382             : 
     383             :         // Make sure the new range spans complete words.
     384             : 
     385           0 :         rv = tsDoc->ExpandRangeToWordBoundaries(rangeBounds);
     386           0 :         NS_ENSURE_SUCCESS(rv, rv);
     387             : 
     388             :         // Now tell the text services that you only want
     389             :         // to iterate over the text in this range.
     390             : 
     391           0 :         rv = tsDoc->SetExtent(rangeBounds);
     392           0 :         NS_ENSURE_SUCCESS(rv, rv);
     393             :       }
     394             :     }
     395             :   }
     396             : 
     397           0 :   mSpellChecker = do_CreateInstance(NS_SPELLCHECKER_CONTRACTID, &rv);
     398           0 :   NS_ENSURE_SUCCESS(rv, rv);
     399             : 
     400           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NULL_POINTER);
     401             : 
     402           0 :   rv = mSpellChecker->SetDocument(tsDoc, true);
     403           0 :   NS_ENSURE_SUCCESS(rv, rv);
     404             : 
     405             :   // do not fail if UpdateCurrentDictionary fails because this method may
     406             :   // succeed later.
     407           0 :   rv = UpdateCurrentDictionary(aCallback);
     408           0 :   if (NS_FAILED(rv) && aCallback) {
     409             :     // However, if it does fail, we still need to call the callback since we
     410             :     // discard the failure.  Do it asynchronously so that the caller is always
     411             :     // guaranteed async behavior.
     412           0 :     RefPtr<CallbackCaller> caller = new CallbackCaller(aCallback);
     413           0 :     rv = doc->Dispatch("nsEditorSpellCheck::CallbackCaller",
     414           0 :                        TaskCategory::Other, caller.forget());
     415           0 :     NS_ENSURE_SUCCESS(rv, rv);
     416             :   }
     417             : 
     418           0 :   return NS_OK;
     419             : }
     420             : 
     421             : NS_IMETHODIMP
     422           0 : nsEditorSpellCheck::GetNextMisspelledWord(char16_t **aNextMisspelledWord)
     423             : {
     424           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     425             : 
     426           0 :   nsAutoString nextMisspelledWord;
     427             : 
     428           0 :   DeleteSuggestedWordList();
     429             :   // Beware! This may flush notifications via synchronous
     430             :   // ScrollSelectionIntoView.
     431           0 :   nsresult rv = mSpellChecker->NextMisspelledWord(nextMisspelledWord,
     432           0 :                                                   &mSuggestedWordList);
     433             : 
     434           0 :   *aNextMisspelledWord = ToNewUnicode(nextMisspelledWord);
     435           0 :   return rv;
     436             : }
     437             : 
     438             : NS_IMETHODIMP
     439           0 : nsEditorSpellCheck::GetSuggestedWord(char16_t **aSuggestedWord)
     440             : {
     441           0 :   nsAutoString word;
     442             :   // XXX This is buggy if mSuggestedWordList.Length() is over INT32_MAX.
     443           0 :   if (mSuggestedWordIndex < static_cast<int32_t>(mSuggestedWordList.Length())) {
     444           0 :     *aSuggestedWord = ToNewUnicode(mSuggestedWordList[mSuggestedWordIndex]);
     445           0 :     mSuggestedWordIndex++;
     446             :   } else {
     447             :     // A blank string signals that there are no more strings
     448           0 :     *aSuggestedWord = ToNewUnicode(EmptyString());
     449             :   }
     450           0 :   return NS_OK;
     451             : }
     452             : 
     453             : NS_IMETHODIMP
     454           0 : nsEditorSpellCheck::CheckCurrentWord(const char16_t *aSuggestedWord,
     455             :                                      bool *aIsMisspelled)
     456             : {
     457           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     458             : 
     459           0 :   DeleteSuggestedWordList();
     460           0 :   return mSpellChecker->CheckWord(nsDependentString(aSuggestedWord),
     461           0 :                                   aIsMisspelled, &mSuggestedWordList);
     462             : }
     463             : 
     464             : NS_IMETHODIMP
     465           0 : nsEditorSpellCheck::CheckCurrentWordNoSuggest(const char16_t *aSuggestedWord,
     466             :                                               bool *aIsMisspelled)
     467             : {
     468           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     469             : 
     470           0 :   return mSpellChecker->CheckWord(nsDependentString(aSuggestedWord),
     471           0 :                                   aIsMisspelled, nullptr);
     472             : }
     473             : 
     474             : NS_IMETHODIMP
     475           0 : nsEditorSpellCheck::ReplaceWord(const char16_t *aMisspelledWord,
     476             :                                 const char16_t *aReplaceWord,
     477             :                                 bool             allOccurrences)
     478             : {
     479           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     480             : 
     481           0 :   return mSpellChecker->Replace(nsDependentString(aMisspelledWord),
     482           0 :                                 nsDependentString(aReplaceWord), allOccurrences);
     483             : }
     484             : 
     485             : NS_IMETHODIMP
     486           0 : nsEditorSpellCheck::IgnoreWordAllOccurrences(const char16_t *aWord)
     487             : {
     488           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     489             : 
     490           0 :   return mSpellChecker->IgnoreAll(nsDependentString(aWord));
     491             : }
     492             : 
     493             : NS_IMETHODIMP
     494           0 : nsEditorSpellCheck::GetPersonalDictionary()
     495             : {
     496           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     497             : 
     498             :    // We can spell check with any editor type
     499           0 :   mDictionaryList.Clear();
     500           0 :   mDictionaryIndex = 0;
     501           0 :   return mSpellChecker->GetPersonalDictionary(&mDictionaryList);
     502             : }
     503             : 
     504             : NS_IMETHODIMP
     505           0 : nsEditorSpellCheck::GetPersonalDictionaryWord(char16_t **aDictionaryWord)
     506             : {
     507             :   // XXX This is buggy if mDictionaryList.Length() is over INT32_MAX.
     508           0 :   if (mDictionaryIndex < static_cast<int32_t>(mDictionaryList.Length())) {
     509           0 :     *aDictionaryWord = ToNewUnicode(mDictionaryList[mDictionaryIndex]);
     510           0 :     mDictionaryIndex++;
     511             :   } else {
     512             :     // A blank string signals that there are no more strings
     513           0 :     *aDictionaryWord = ToNewUnicode(EmptyString());
     514             :   }
     515             : 
     516           0 :   return NS_OK;
     517             : }
     518             : 
     519             : NS_IMETHODIMP
     520           0 : nsEditorSpellCheck::AddWordToDictionary(const char16_t *aWord)
     521             : {
     522           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     523             : 
     524           0 :   return mSpellChecker->AddWordToPersonalDictionary(nsDependentString(aWord));
     525             : }
     526             : 
     527             : NS_IMETHODIMP
     528           0 : nsEditorSpellCheck::RemoveWordFromDictionary(const char16_t *aWord)
     529             : {
     530           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     531             : 
     532           0 :   return mSpellChecker->RemoveWordFromPersonalDictionary(nsDependentString(aWord));
     533             : }
     534             : 
     535             : NS_IMETHODIMP
     536           0 : nsEditorSpellCheck::GetDictionaryList(char16_t ***aDictionaryList, uint32_t *aCount)
     537             : {
     538           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     539             : 
     540           0 :   NS_ENSURE_TRUE(aDictionaryList && aCount, NS_ERROR_NULL_POINTER);
     541             : 
     542           0 :   *aDictionaryList = 0;
     543           0 :   *aCount          = 0;
     544             : 
     545           0 :   nsTArray<nsString> dictList;
     546             : 
     547           0 :   nsresult rv = mSpellChecker->GetDictionaryList(&dictList);
     548             : 
     549           0 :   NS_ENSURE_SUCCESS(rv, rv);
     550             : 
     551           0 :   char16_t **tmpPtr = 0;
     552             : 
     553           0 :   if (dictList.IsEmpty()) {
     554             :     // If there are no dictionaries, return an array containing
     555             :     // one element and a count of one.
     556             : 
     557           0 :     tmpPtr = (char16_t **)moz_xmalloc(sizeof(char16_t *));
     558             : 
     559           0 :     NS_ENSURE_TRUE(tmpPtr, NS_ERROR_OUT_OF_MEMORY);
     560             : 
     561           0 :     *tmpPtr          = 0;
     562           0 :     *aDictionaryList = tmpPtr;
     563           0 :     *aCount          = 0;
     564             : 
     565           0 :     return NS_OK;
     566             :   }
     567             : 
     568           0 :   tmpPtr = (char16_t **)moz_xmalloc(sizeof(char16_t *) * dictList.Length());
     569             : 
     570           0 :   NS_ENSURE_TRUE(tmpPtr, NS_ERROR_OUT_OF_MEMORY);
     571             : 
     572           0 :   *aDictionaryList = tmpPtr;
     573           0 :   *aCount          = dictList.Length();
     574             : 
     575           0 :   for (uint32_t i = 0; i < *aCount; i++) {
     576           0 :     tmpPtr[i] = ToNewUnicode(dictList[i]);
     577             :   }
     578             : 
     579           0 :   return rv;
     580             : }
     581             : 
     582             : NS_IMETHODIMP
     583           0 : nsEditorSpellCheck::GetCurrentDictionary(nsAString& aDictionary)
     584             : {
     585           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     586             : 
     587           0 :   return mSpellChecker->GetCurrentDictionary(aDictionary);
     588             : }
     589             : 
     590             : NS_IMETHODIMP
     591           0 : nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
     592             : {
     593           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     594             : 
     595           0 :   RefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
     596             : 
     597             :   // The purpose of mUpdateDictionaryRunning is to avoid doing all of this if
     598             :   // UpdateCurrentDictionary's helper method DictionaryFetched, which calls us,
     599             :   // is on the stack. In other words: Only do this, if the user manually selected a
     600             :   // dictionary to use.
     601           0 :   if (!mUpdateDictionaryRunning) {
     602             : 
     603             :     // Ignore pending dictionary fetchers by increasing this number.
     604           0 :     mDictionaryFetcherGroup++;
     605             : 
     606           0 :     uint32_t flags = 0;
     607           0 :     mEditor->GetFlags(&flags);
     608           0 :     if (!(flags & nsIPlaintextEditor::eEditorMailMask)) {
     609           0 :       if (!aDictionary.IsEmpty() && (mPreferredLang.IsEmpty() ||
     610           0 :           !mPreferredLang.Equals(aDictionary,
     611           0 :                                  nsCaseInsensitiveStringComparator()))) {
     612             :         // When user sets dictionary manually, we store this value associated
     613             :         // with editor url, if it doesn't match the document language exactly.
     614             :         // For example on "en" sites, we need to store "en-GB", otherwise
     615             :         // the language might jump back to en-US although the user explicitly
     616             :         // chose otherwise.
     617           0 :         StoreCurrentDictionary(mEditor, aDictionary);
     618             : #ifdef DEBUG_DICT
     619             :         printf("***** Writing content preferences for |%s|\n",
     620             :                NS_ConvertUTF16toUTF8(aDictionary).get());
     621             : #endif
     622             :       } else {
     623             :         // If user sets a dictionary matching the language defined by
     624             :         // document, we consider content pref has been canceled, and we clear it.
     625           0 :         ClearCurrentDictionary(mEditor);
     626             : #ifdef DEBUG_DICT
     627             :         printf("***** Clearing content preferences for |%s|\n",
     628             :                NS_ConvertUTF16toUTF8(aDictionary).get());
     629             : #endif
     630             :       }
     631             : 
     632             :       // Also store it in as a preference, so we can use it as a fallback.
     633             :       // We don't want this for mail composer because it uses
     634             :       // "spellchecker.dictionary" as a preference.
     635           0 :       Preferences::SetString("spellchecker.dictionary", aDictionary);
     636             : #ifdef DEBUG_DICT
     637             :       printf("***** Storing spellchecker.dictionary |%s|\n",
     638             :              NS_ConvertUTF16toUTF8(aDictionary).get());
     639             : #endif
     640             :     }
     641             :   }
     642           0 :   return mSpellChecker->SetCurrentDictionary(aDictionary);
     643             : }
     644             : 
     645             : NS_IMETHODIMP
     646           0 : nsEditorSpellCheck::UninitSpellChecker()
     647             : {
     648           0 :   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
     649             : 
     650             :   // Cleanup - kill the spell checker
     651           0 :   DeleteSuggestedWordList();
     652           0 :   mDictionaryList.Clear();
     653           0 :   mDictionaryIndex = 0;
     654           0 :   mSpellChecker = nullptr;
     655           0 :   return NS_OK;
     656             : }
     657             : 
     658             : 
     659             : NS_IMETHODIMP
     660           0 : nsEditorSpellCheck::SetFilter(nsITextServicesFilter *filter)
     661             : {
     662           0 :   mTxtSrvFilter = filter;
     663           0 :   return NS_OK;
     664             : }
     665             : 
     666             : nsresult
     667           0 : nsEditorSpellCheck::DeleteSuggestedWordList()
     668             : {
     669           0 :   mSuggestedWordList.Clear();
     670           0 :   mSuggestedWordIndex = 0;
     671           0 :   return NS_OK;
     672             : }
     673             : 
     674             : NS_IMETHODIMP
     675           0 : nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallback)
     676             : {
     677           0 :   if (NS_WARN_IF(!mSpellChecker)) {
     678           0 :     return NS_ERROR_NOT_INITIALIZED;
     679             :   }
     680             : 
     681             :   nsresult rv;
     682             : 
     683           0 :   RefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
     684             : 
     685             :   // Get language with html5 algorithm
     686           0 :   nsCOMPtr<nsIContent> rootContent;
     687           0 :   nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor);
     688           0 :   if (htmlEditor) {
     689           0 :     rootContent = htmlEditor->GetActiveEditingHost();
     690             :   } else {
     691           0 :     nsCOMPtr<nsIDOMElement> rootElement;
     692           0 :     rv = mEditor->GetRootElement(getter_AddRefs(rootElement));
     693           0 :     NS_ENSURE_SUCCESS(rv, rv);
     694           0 :     rootContent = do_QueryInterface(rootElement);
     695             :   }
     696             : 
     697             :   // Try to get topmost document's document element for embedded mail editor.
     698           0 :   uint32_t flags = 0;
     699           0 :   mEditor->GetFlags(&flags);
     700           0 :   if (flags & nsIPlaintextEditor::eEditorMailMask) {
     701           0 :     nsCOMPtr<nsIDocument> ownerDoc = rootContent->OwnerDoc();
     702           0 :     NS_ENSURE_TRUE(ownerDoc, NS_ERROR_FAILURE);
     703           0 :     nsIDocument* parentDoc = ownerDoc->GetParentDocument();
     704           0 :     if (parentDoc) {
     705           0 :       rootContent = do_QueryInterface(parentDoc->GetDocumentElement());
     706             :     }
     707             :   }
     708             : 
     709           0 :   if (!rootContent) {
     710           0 :     return NS_ERROR_FAILURE;
     711             :   }
     712             : 
     713             :   RefPtr<DictionaryFetcher> fetcher =
     714           0 :     new DictionaryFetcher(this, aCallback, mDictionaryFetcherGroup);
     715           0 :   rootContent->GetLang(fetcher->mRootContentLang);
     716           0 :   nsCOMPtr<nsIDocument> doc = rootContent->GetUncomposedDoc();
     717           0 :   NS_ENSURE_STATE(doc);
     718           0 :   doc->GetContentLanguage(fetcher->mRootDocContentLang);
     719             : 
     720           0 :   rv = fetcher->Fetch(mEditor);
     721           0 :   NS_ENSURE_SUCCESS(rv, rv);
     722             : 
     723           0 :   return NS_OK;
     724             : }
     725             : 
     726             : // Helper function that iterates over the list of dictionaries and sets the one
     727             : // that matches based on a given comparison type.
     728             : void
     729           0 : nsEditorSpellCheck::BuildDictionaryList(const nsAString& aDictName,
     730             :                                         const nsTArray<nsString>& aDictList,
     731             :                                         enum dictCompare aCompareType,
     732             :                                         nsTArray<nsString>& aOutList)
     733             : {
     734           0 :   for (uint32_t i = 0; i < aDictList.Length(); i++) {
     735           0 :     nsAutoString dictStr(aDictList.ElementAt(i));
     736           0 :     bool equals = false;
     737           0 :     switch (aCompareType) {
     738             :       case DICT_NORMAL_COMPARE:
     739           0 :         equals = aDictName.Equals(dictStr);
     740           0 :         break;
     741             :       case DICT_COMPARE_CASE_INSENSITIVE:
     742           0 :         equals = aDictName.Equals(dictStr, nsCaseInsensitiveStringComparator());
     743           0 :         break;
     744             :       case DICT_COMPARE_DASHMATCH:
     745           0 :         equals = nsStyleUtil::DashMatchCompare(dictStr, aDictName, nsCaseInsensitiveStringComparator());
     746           0 :         break;
     747             :     }
     748           0 :     if (equals) {
     749           0 :       aOutList.AppendElement(dictStr);
     750             : #ifdef DEBUG_DICT
     751             :       if (NS_SUCCEEDED(rv)) {
     752             :         printf("***** Trying |%s|.\n", NS_ConvertUTF16toUTF8(dictStr).get());
     753             :       }
     754             : #endif
     755             :       // We always break here. We tried to set the dictionary to an existing
     756             :       // dictionary from the list. This must work, if it doesn't, there is
     757             :       // no point trying another one.
     758           0 :       return;
     759             :     }
     760             :   }
     761             : }
     762             : 
     763             : nsresult
     764           0 : nsEditorSpellCheck::DictionaryFetched(DictionaryFetcher* aFetcher)
     765             : {
     766           0 :   MOZ_ASSERT(aFetcher);
     767           0 :   RefPtr<nsEditorSpellCheck> kungFuDeathGrip = this;
     768             : 
     769           0 :   BeginUpdateDictionary();
     770             : 
     771           0 :   if (aFetcher->mGroup < mDictionaryFetcherGroup) {
     772             :     // SetCurrentDictionary was called after the fetch started.  Don't overwrite
     773             :     // that dictionary with the fetched one.
     774           0 :     EndUpdateDictionary();
     775           0 :     if (aFetcher->mCallback) {
     776           0 :       aFetcher->mCallback->EditorSpellCheckDone();
     777             :     }
     778           0 :     return NS_OK;
     779             :   }
     780             : 
     781             :   /*
     782             :    * We try to derive the dictionary to use based on the following priorities:
     783             :    * 1) Content preference, so the language the user set for the site before.
     784             :    *    (Introduced in bug 678842 and corrected in bug 717433.)
     785             :    * 2) Language set by the website, or any other dictionary that partly
     786             :    *    matches that. (Introduced in bug 338427.)
     787             :    *    Eg. if the website is "en-GB", a user who only has "en-US" will get
     788             :    *    that. If the website is generic "en", the user will get one of the
     789             :    *    "en-*" installed, (almost) at random.
     790             :    *    However, we prefer what is stored in "spellchecker.dictionary",
     791             :    *    so if the user chose "en-AU" before, they will get "en-AU" on a plain
     792             :    *    "en" site. (Introduced in bug 682564.)
     793             :    * 3) The value of "spellchecker.dictionary" which reflects a previous
     794             :    *    language choice of the user (on another site).
     795             :    *    (This was the original behaviour before the aforementioned bugs
     796             :    *    landed).
     797             :    * 4) The user's locale.
     798             :    * 5) Use the current dictionary that is currently set.
     799             :    * 6) The content of the "LANG" environment variable (if set).
     800             :    * 7) The first spell check dictionary installed.
     801             :    */
     802             : 
     803             :   // Get the language from the element or its closest parent according to:
     804             :   // https://html.spec.whatwg.org/#attr-lang
     805             :   // This is used in SetCurrentDictionary.
     806           0 :   mPreferredLang.Assign(aFetcher->mRootContentLang);
     807             : #ifdef DEBUG_DICT
     808             :   printf("***** mPreferredLang (element) |%s|\n",
     809             :          NS_ConvertUTF16toUTF8(mPreferredLang).get());
     810             : #endif
     811             : 
     812             :   // If no luck, try the "Content-Language" header.
     813           0 :   if (mPreferredLang.IsEmpty()) {
     814           0 :     mPreferredLang.Assign(aFetcher->mRootDocContentLang);
     815             : #ifdef DEBUG_DICT
     816             :     printf("***** mPreferredLang (content-language) |%s|\n",
     817             :            NS_ConvertUTF16toUTF8(mPreferredLang).get());
     818             : #endif
     819             :   }
     820             : 
     821             :   // We obtain a list of available dictionaries.
     822           0 :   AutoTArray<nsString, 8> dictList;
     823           0 :   nsresult rv = mSpellChecker->GetDictionaryList(&dictList);
     824           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     825           0 :     EndUpdateDictionary();
     826           0 :     if (aFetcher->mCallback) {
     827           0 :       aFetcher->mCallback->EditorSpellCheckDone();
     828             :     }
     829           0 :     return rv;
     830             :   }
     831             : 
     832             :   // Priority 1:
     833             :   // If we successfully fetched a dictionary from content prefs, do not go
     834             :   // further. Use this exact dictionary.
     835             :   // Don't use content preferences for editor with eEditorMailMask flag.
     836           0 :   nsAutoString dictName;
     837             :   uint32_t flags;
     838           0 :   mEditor->GetFlags(&flags);
     839           0 :   if (!(flags & nsIPlaintextEditor::eEditorMailMask)) {
     840           0 :     dictName.Assign(aFetcher->mDictionary);
     841           0 :     if (!dictName.IsEmpty()) {
     842           0 :       AutoTArray<nsString, 1> tryDictList;
     843           0 :       BuildDictionaryList(dictName, dictList, DICT_NORMAL_COMPARE, tryDictList);
     844             : 
     845           0 :       RefPtr<nsEditorSpellCheck> self = this;
     846           0 :       RefPtr<DictionaryFetcher> fetcher = aFetcher;
     847           0 :       mSpellChecker->SetCurrentDictionaryFromList(tryDictList)->Then(
     848             :         GetMainThreadSerialEventTarget(),
     849             :         __func__,
     850           0 :         [self, fetcher]() {
     851             : #ifdef DEBUG_DICT
     852             :           printf("***** Assigned from content preferences |%s|\n",
     853             :                  NS_ConvertUTF16toUTF8(dictName).get());
     854             : #endif
     855             :           // We take an early exit here, so let's not forget to clear the word
     856             :           // list.
     857           0 :           self->DeleteSuggestedWordList();
     858             : 
     859           0 :           self->EndUpdateDictionary();
     860           0 :           if (fetcher->mCallback) {
     861           0 :             fetcher->mCallback->EditorSpellCheckDone();
     862             :           }
     863           0 :         },
     864           0 :         [self, fetcher]() {
     865             :           // May be dictionary was uninstalled ?
     866             :           // Clear the content preference and continue.
     867           0 :           ClearCurrentDictionary(self->mEditor);
     868             : 
     869             :           // Priority 2 or later will handled by the following
     870           0 :           self->SetFallbackDictionary(fetcher);
     871           0 :         });
     872           0 :       return NS_OK;
     873             :     }
     874             :   }
     875           0 :   SetFallbackDictionary(aFetcher);
     876           0 :   return NS_OK;
     877             : }
     878             : 
     879             : void
     880           0 : nsEditorSpellCheck::SetFallbackDictionary(DictionaryFetcher* aFetcher)
     881             : {
     882           0 :   MOZ_ASSERT(mUpdateDictionaryRunning);
     883             : 
     884           0 :   AutoTArray<nsString, 6> tryDictList;
     885             : 
     886             :   // We obtain a list of available dictionaries.
     887           0 :   AutoTArray<nsString, 8> dictList;
     888           0 :   nsresult rv = mSpellChecker->GetDictionaryList(&dictList);
     889           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     890           0 :     EndUpdateDictionary();
     891           0 :     if (aFetcher->mCallback) {
     892           0 :       aFetcher->mCallback->EditorSpellCheckDone();
     893             :     }
     894           0 :     return;
     895             :   }
     896             : 
     897             :   // Priority 2:
     898             :   // After checking the content preferences, we use the language of the element
     899             :   // or document.
     900           0 :   nsAutoString dictName(mPreferredLang);
     901             : #ifdef DEBUG_DICT
     902             :   printf("***** Assigned from element/doc |%s|\n",
     903             :          NS_ConvertUTF16toUTF8(dictName).get());
     904             : #endif
     905             : 
     906             :   // Get the preference value.
     907           0 :   nsAutoString preferredDict;
     908           0 :   preferredDict = Preferences::GetLocalizedString("spellchecker.dictionary");
     909             : 
     910           0 :   if (!dictName.IsEmpty()) {
     911             :     // RFC 5646 explicitly states that matches should be case-insensitive.
     912             :     BuildDictionaryList(dictName, dictList, DICT_COMPARE_CASE_INSENSITIVE,
     913           0 :                         tryDictList);
     914             : 
     915             : #ifdef DEBUG_DICT
     916             :     printf("***** Trying from element/doc |%s| \n",
     917             :            NS_ConvertUTF16toUTF8(dictName).get());
     918             : #endif
     919             : 
     920             :     // Required dictionary was not available. Try to get a dictionary
     921             :     // matching at least language part of dictName.
     922           0 :     nsAutoString langCode;
     923           0 :     int32_t dashIdx = dictName.FindChar('-');
     924           0 :     if (dashIdx != -1) {
     925           0 :       langCode.Assign(Substring(dictName, 0, dashIdx));
     926             :     } else {
     927           0 :       langCode.Assign(dictName);
     928             :     }
     929             : 
     930             :     // Try dictionary.spellchecker preference, if it starts with langCode,
     931             :     // so we don't just get any random dictionary matching the language.
     932           0 :     if (!preferredDict.IsEmpty() &&
     933           0 :         nsStyleUtil::DashMatchCompare(preferredDict, langCode, nsDefaultStringComparator())) {
     934             : #ifdef DEBUG_DICT
     935             :       printf("***** Trying preference value |%s| since it matches language code\n",
     936             :              NS_ConvertUTF16toUTF8(preferredDict).get());
     937             : #endif
     938             :       BuildDictionaryList(preferredDict, dictList,
     939           0 :                           DICT_COMPARE_CASE_INSENSITIVE, tryDictList);
     940             :     }
     941             : 
     942             :     // Use any dictionary with the required language.
     943             : #ifdef DEBUG_DICT
     944             :     printf("***** Trying to find match for language code |%s|\n",
     945             :            NS_ConvertUTF16toUTF8(langCode).get());
     946             : #endif
     947             :     BuildDictionaryList(langCode, dictList, DICT_COMPARE_DASHMATCH,
     948           0 :                         tryDictList);
     949             :   }
     950             : 
     951             :   // Priority 3:
     952             :   // If the document didn't supply a dictionary or the setting failed,
     953             :   // try the user preference next.
     954           0 :   if (!preferredDict.IsEmpty()) {
     955             : #ifdef DEBUG_DICT
     956             :     printf("***** Trying preference value |%s|\n",
     957             :            NS_ConvertUTF16toUTF8(preferredDict).get());
     958             : #endif
     959             :     BuildDictionaryList(preferredDict, dictList, DICT_NORMAL_COMPARE,
     960           0 :                              tryDictList);
     961             :   }
     962             : 
     963             :   // Priority 4:
     964             :   // As next fallback, try the current locale.
     965           0 :   nsAutoCString utf8DictName;
     966           0 :   LocaleService::GetInstance()->GetAppLocaleAsLangTag(utf8DictName);
     967             : 
     968           0 :   CopyUTF8toUTF16(utf8DictName, dictName);
     969             : #ifdef DEBUG_DICT
     970             :   printf("***** Trying locale |%s|\n",
     971             :          NS_ConvertUTF16toUTF8(dictName).get());
     972             : #endif
     973             :   BuildDictionaryList(dictName, dictList, DICT_COMPARE_CASE_INSENSITIVE,
     974           0 :                       tryDictList);
     975             : 
     976             :   // Priority 5:
     977             :   // If we have a current dictionary and we don't have no item in try list,
     978             :   // don't try anything else.
     979           0 :   nsAutoString currentDictionary;
     980           0 :   GetCurrentDictionary(currentDictionary);
     981           0 :   if (!currentDictionary.IsEmpty() && tryDictList.IsEmpty()) {
     982             : #ifdef DEBUG_DICT
     983             :     printf("***** Retrieved current dict |%s|\n",
     984             :            NS_ConvertUTF16toUTF8(currentDictionary).get());
     985             : #endif
     986           0 :     EndUpdateDictionary();
     987           0 :     if (aFetcher->mCallback) {
     988           0 :       aFetcher->mCallback->EditorSpellCheckDone();
     989             :     }
     990           0 :     return;
     991             :   }
     992             : 
     993             :   // Priority 6:
     994             :   // Try to get current dictionary from environment variable LANG.
     995             :   // LANG = language[_territory][.charset]
     996           0 :   char* env_lang = getenv("LANG");
     997           0 :   if (env_lang) {
     998           0 :     nsString lang = NS_ConvertUTF8toUTF16(env_lang);
     999             :     // Strip trailing charset, if there is any.
    1000           0 :     int32_t dot_pos = lang.FindChar('.');
    1001           0 :     if (dot_pos != -1) {
    1002           0 :       lang = Substring(lang, 0, dot_pos);
    1003             :     }
    1004             : 
    1005           0 :     int32_t underScore = lang.FindChar('_');
    1006           0 :     if (underScore != -1) {
    1007           0 :       lang.Replace(underScore, 1, '-');
    1008             : #ifdef DEBUG_DICT
    1009             :       printf("***** Trying LANG from environment |%s|\n",
    1010             :              NS_ConvertUTF16toUTF8(lang).get());
    1011             : #endif
    1012             :       BuildDictionaryList(lang, dictList, DICT_COMPARE_CASE_INSENSITIVE,
    1013           0 :                           tryDictList);
    1014             :     }
    1015             :   }
    1016             : 
    1017             :   // Priority 7:
    1018             :   // If it does not work, pick the first one.
    1019           0 :   if (!dictList.IsEmpty()) {
    1020           0 :     BuildDictionaryList(dictList[0], dictList, DICT_NORMAL_COMPARE,
    1021           0 :                         tryDictList);
    1022             : #ifdef DEBUG_DICT
    1023             :     printf("***** Trying first of list |%s|\n",
    1024             :            NS_ConvertUTF16toUTF8(dictList[0]).get());
    1025             : #endif
    1026             :   }
    1027             : 
    1028           0 :   RefPtr<nsEditorSpellCheck> self = this;
    1029           0 :   RefPtr<DictionaryFetcher> fetcher = aFetcher;
    1030           0 :   mSpellChecker->SetCurrentDictionaryFromList(tryDictList)->Then(
    1031             :     GetMainThreadSerialEventTarget(),
    1032             :     __func__,
    1033           0 :     [self, fetcher]() {
    1034             :       // If an error was thrown while setting the dictionary, just
    1035             :       // fail silently so that the spellchecker dialog is allowed to come
    1036             :       // up. The user can manually reset the language to their choice on
    1037             :       // the dialog if it is wrong.
    1038           0 :       self->DeleteSuggestedWordList();
    1039           0 :       self->EndUpdateDictionary();
    1040           0 :       if (fetcher->mCallback) {
    1041           0 :         fetcher->mCallback->EditorSpellCheckDone();
    1042             :       }
    1043           0 :     });
    1044             : }

Generated by: LCOV version 1.13