editor/composer/src/nsEditorSpellCheck.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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 }

mercurial