Backout changeset e57b659ee5dd (bug 678842) because the test is wrong, and it should never pass
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 22 Aug 2011 22:27:47 -0400
changeset 75698 562229c22e9782ef0bf2720bbbb98d71c6eb252f
parent 75697 09a0af24837d0c3f571ec450b4432a2b92061fd5
child 75699 ea8d99353056f8e67a22a04e79f9f8b656b950e2
push id21051
push usermlamouri@mozilla.com
push dateTue, 23 Aug 2011 08:20:16 +0000
treeherdermozilla-central@a41b781330a6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs678842
milestone9.0a1
backs oute57b659ee5dd47e38976ab1a200fe466fd539b30
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backout changeset e57b659ee5dd (bug 678842) because the test is wrong, and it should never pass
editor/composer/src/nsEditorSpellCheck.cpp
editor/composer/src/nsEditorSpellCheck.h
editor/composer/test/Makefile.in
editor/composer/test/bug678842_subframe.html
editor/composer/test/test_bug678842.html
editor/idl/nsIEditorSpellCheck.idl
extensions/spellcheck/src/mozInlineSpellChecker.cpp
layout/build/nsLayoutStatics.cpp
toolkit/components/contentprefs/nsContentPrefService.js
toolkit/content/InlineSpellChecker.jsm
--- a/editor/composer/src/nsEditorSpellCheck.cpp
+++ b/editor/composer/src/nsEditorSpellCheck.cpp
@@ -48,23 +48,18 @@
 #include "nsITextServicesDocument.h"
 #include "nsISpellChecker.h"
 #include "nsISelection.h"
 #include "nsIDOMRange.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 
 #include "nsIComponentManager.h"
-#include "nsIContentPrefService.h"
-#include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIChromeRegistry.h"
-#include "nsIPrivateBrowsingService.h"
-#include "nsIContentURIGrouper.h"
-#include "nsNetCID.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsITextServicesFilter.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
@@ -80,245 +75,34 @@ class UpdateDictionnaryHolder {
     }
     ~UpdateDictionnaryHolder() {
       if (mSpellCheck) {
         mSpellCheck->EndUpdateDictionary();
       }
     }
 };
 
-#define CPS_PREF_NAME NS_LITERAL_STRING("spellcheck.lang")
-
-class LastDictionary : public nsIObserver, public nsSupportsWeakReference {
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  LastDictionary();
-
-  /**
-   * Store current dictionary for editor document url. Use content pref
-   * service. Or, if in private mode, store this information in memory.
-   */
-  NS_IMETHOD StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary);
-
-  /**
-   * Get last stored current dictionary for editor document url.
-   */
-  NS_IMETHOD FetchLastDictionary(nsIEditor* aEditor, nsAString& aDictionary);
-
-  /**
-   * Forget last current dictionary stored for editor document url.
-   */
-  NS_IMETHOD ClearCurrentDictionary(nsIEditor* aEditor);
-
-  /**
-   * get uri of editor's document.
-   *
-   */
-  static nsresult GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI);
-
-  PRBool mInPrivateBrowsing;
-
-  // During private browsing, dictionaries are stored in memory
-  nsDataHashtable<nsStringHashKey, nsString> mMemoryStorage;
-};
-
-NS_IMPL_ISUPPORTS2(LastDictionary, nsIObserver, nsISupportsWeakReference)
-
-LastDictionary::LastDictionary():
-  mInPrivateBrowsing(PR_FALSE)
-{  
-  nsCOMPtr<nsIPrivateBrowsingService> pbService =
-    do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
-  if (pbService) {
-    pbService->GetPrivateBrowsingEnabled(&mInPrivateBrowsing);
-    mMemoryStorage.Init();
-  }
-}
-
-// static
-nsresult
-LastDictionary::GetDocumentURI(nsIEditor* aEditor, nsIURI * *aURI)
-{
-  NS_ENSURE_ARG_POINTER(aEditor);
-  NS_ENSURE_ARG_POINTER(aURI);
-
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  aEditor->GetDocument(getter_AddRefs(domDoc));
-  NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
-
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
-  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-
-  nsCOMPtr<nsIURI> docUri = doc->GetDocumentURI();
-  NS_ENSURE_TRUE(docUri, NS_ERROR_FAILURE);
-
-  *aURI = docUri;
-  NS_ADDREF(*aURI);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-LastDictionary::FetchLastDictionary(nsIEditor* aEditor, nsAString& aDictionary)
-{
-  NS_ENSURE_ARG_POINTER(aEditor);
-
-  nsresult rv;
-
-  nsCOMPtr<nsIURI> docUri;
-  rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (mInPrivateBrowsing) {
-    nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService =
-      do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID);
-    NS_ENSURE_TRUE(hostnameGrouperService, NS_ERROR_NOT_AVAILABLE);
-    nsString group;
-    hostnameGrouperService->Group(docUri, group);
-    nsAutoString lastDict;
-    if (mMemoryStorage.Get(group, &lastDict)) {
-      aDictionary.Assign(lastDict);
-    } else {
-      aDictionary.Truncate();
-    }
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIContentPrefService> contentPrefService =
-    do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_AVAILABLE);
-
-  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
-  uri->SetAsISupports(docUri);
-
-  PRBool hasPref;
-  if (NS_SUCCEEDED(contentPrefService->HasPref(uri, CPS_PREF_NAME, &hasPref)) && hasPref) {
-    nsCOMPtr<nsIVariant> pref;
-    contentPrefService->GetPref(uri, CPS_PREF_NAME, nsnull, getter_AddRefs(pref));
-    pref->GetAsAString(aDictionary);
-  } else {
-    aDictionary.Truncate();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-LastDictionary::StoreCurrentDictionary(nsIEditor* aEditor, const nsAString& aDictionary)
-{
-  NS_ENSURE_ARG_POINTER(aEditor);
-
-  nsresult rv;
-
-  nsCOMPtr<nsIURI> docUri;
-  rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
- if (mInPrivateBrowsing) {
-    nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService =
-      do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID);
-    NS_ENSURE_TRUE(hostnameGrouperService, NS_ERROR_NOT_AVAILABLE);
-    nsString group;
-    hostnameGrouperService->Group(docUri, group);
-
-    if (mMemoryStorage.Put(group, nsString(aDictionary))) {
-      return NS_OK;
-    } else {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
-  uri->SetAsISupports(docUri);
-
-  nsCOMPtr<nsIWritableVariant> prefValue = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  NS_ENSURE_TRUE(prefValue, NS_ERROR_OUT_OF_MEMORY);
-  prefValue->SetAsAString(aDictionary);
-
-  nsCOMPtr<nsIContentPrefService> contentPrefService =
-    do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED);
-
-  return contentPrefService->SetPref(uri, CPS_PREF_NAME, prefValue);
-}
-
-NS_IMETHODIMP
-LastDictionary::ClearCurrentDictionary(nsIEditor* aEditor)
-{
-  NS_ENSURE_ARG_POINTER(aEditor);
-
-  nsresult rv;
-
-  nsCOMPtr<nsIURI> docUri;
-  rv = GetDocumentURI(aEditor, getter_AddRefs(docUri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIContentURIGrouper> hostnameGrouperService =
-      do_GetService(NS_HOSTNAME_GROUPER_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(hostnameGrouperService, NS_ERROR_NOT_AVAILABLE);
-
-  nsString group;
-  hostnameGrouperService->Group(docUri, group);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (mMemoryStorage.IsInitialized()) {
-    mMemoryStorage.Remove(group);
-  }
-
-  nsCOMPtr<nsIWritableVariant> uri = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  NS_ENSURE_TRUE(uri, NS_ERROR_OUT_OF_MEMORY);
-  uri->SetAsISupports(docUri);
-
-  nsCOMPtr<nsIContentPrefService> contentPrefService =
-    do_GetService(NS_CONTENT_PREF_SERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(contentPrefService, NS_ERROR_NOT_INITIALIZED);
-
-  return contentPrefService->RemovePref(uri, CPS_PREF_NAME);
-}
-
-NS_IMETHODIMP
-LastDictionary::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData)
-{
-  if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
-    if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) {
-      mInPrivateBrowsing = PR_TRUE;
-    } else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
-      mInPrivateBrowsing = PR_FALSE;
-      if (mMemoryStorage.IsInitialized()) {
-        mMemoryStorage.Clear();
-      }
-    }
-  } 
-  return NS_OK;
-}
-
-LastDictionary* nsEditorSpellCheck::gDictionaryStore = nsnull;
-
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditorSpellCheck)
 
 NS_INTERFACE_MAP_BEGIN(nsEditorSpellCheck)
   NS_INTERFACE_MAP_ENTRY(nsIEditorSpellCheck)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditorSpellCheck)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsEditorSpellCheck)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_3(nsEditorSpellCheck,
-                           mEditor,
+NS_IMPL_CYCLE_COLLECTION_2(nsEditorSpellCheck,
                            mSpellChecker,
                            mTxtSrvFilter)
 
 nsEditorSpellCheck::nsEditorSpellCheck()
   : mSuggestedWordIndex(0)
   , mDictionaryIndex(0)
-  , mEditor(nsnull)
   , mUpdateDictionaryRunning(PR_FALSE)
+  , mDictWasSetManually(PR_FALSE)
 {
 }
 
 nsEditorSpellCheck::~nsEditorSpellCheck()
 {
   // Make sure we blow the spellchecker away, just in
   // case it hasn't been destroyed already.
   mSpellChecker = nsnull;
@@ -346,34 +130,18 @@ nsEditorSpellCheck::CanSpellCheck(PRBool
 
   *_retval = (dictList.Length() > 0);
   return NS_OK;
 }
 
 NS_IMETHODIMP    
 nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, PRBool aEnableSelectionChecking)
 {
-  NS_ENSURE_TRUE(aEditor, NS_ERROR_NULL_POINTER);
-  mEditor = aEditor;
-
   nsresult rv;
 
-  if (!gDictionaryStore) {
-    gDictionaryStore = new LastDictionary();
-    if (gDictionaryStore) {
-      NS_ADDREF(gDictionaryStore);
-      nsCOMPtr<nsIObserverService> observerService =
-        mozilla::services::GetObserverService();
-      if (observerService) {
-        observerService->AddObserver(gDictionaryStore, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE);
-      }
-    }
-  }
-
-
   // We can spell check with any editor type
   nsCOMPtr<nsITextServicesDocument>tsDoc =
      do_CreateInstance("@mozilla.org/textservices/textservicesdocument;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ENSURE_TRUE(tsDoc, NS_ERROR_NULL_POINTER);
 
   tsDoc->SetFilter(mTxtSrvFilter);
@@ -435,17 +203,17 @@ nsEditorSpellCheck::InitSpellChecker(nsI
 
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NULL_POINTER);
 
   rv = mSpellChecker->SetDocument(tsDoc, PR_TRUE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // do not fail if UpdateCurrentDictionary fails because this method may
   // succeed later.
-  UpdateCurrentDictionary();
+  UpdateCurrentDictionary(aEditor);
   return NS_OK;
 }
 
 NS_IMETHODIMP    
 nsEditorSpellCheck::GetNextMisspelledWord(PRUnichar **aNextMisspelledWord)
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
@@ -618,39 +386,17 @@ nsEditorSpellCheck::GetCurrentDictionary
 }
 
 NS_IMETHODIMP    
 nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
 
   if (!mUpdateDictionaryRunning) {
-
-    nsDefaultStringComparator comparator;
-    nsAutoString langCode;
-    PRInt32 dashIdx = aDictionary.FindChar('-');
-    if (dashIdx != -1) {
-      langCode.Assign(Substring(aDictionary, 0, dashIdx));
-    } else {
-      langCode.Assign(aDictionary);
-    }
-
-    if (mPreferredLang.IsEmpty() || !nsStyleUtil::DashMatchCompare(mPreferredLang, langCode, comparator)) {
-      // When user sets dictionary manually, we store this value associated
-      // with editor url.
-      gDictionaryStore->StoreCurrentDictionary(mEditor, aDictionary);
-    } else {
-      // If user sets a dictionary matching (even partially), lang defined by
-      // document, we consider content pref has been canceled, and we clear it.
-      gDictionaryStore->ClearCurrentDictionary(mEditor);
-    }
-
-    // Also store it in as a preference. It will be used as a default value
-    // when everything else fails.
-    Preferences::SetString("spellchecker.dictionary", aDictionary);
+    mDictWasSetManually = PR_TRUE;
   }
   return mSpellChecker->SetCurrentDictionary(aDictionary);
 }
 
 NS_IMETHODIMP    
 nsEditorSpellCheck::UninitSpellChecker()
 {
   NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
@@ -658,16 +404,31 @@ nsEditorSpellCheck::UninitSpellChecker()
   // Cleanup - kill the spell checker
   DeleteSuggestedWordList();
   mDictionaryList.Clear();
   mDictionaryIndex = 0;
   mSpellChecker = 0;
   return NS_OK;
 }
 
+// Save the last set dictionary to the user's preferences.
+NS_IMETHODIMP
+nsEditorSpellCheck::SaveDefaultDictionary()
+{
+  if (!mDictWasSetManually) {
+    return NS_OK;
+  }
+
+  nsAutoString dictName;
+  nsresult rv = GetCurrentDictionary(dictName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return Preferences::SetString("spellchecker.dictionary", dictName);
+}
+
 
 /* void setFilter (in nsITextServicesFilter filter); */
 NS_IMETHODIMP 
 nsEditorSpellCheck::SetFilter(nsITextServicesFilter *filter)
 {
   mTxtSrvFilter = filter;
   return NS_OK;
 }
@@ -676,61 +437,55 @@ nsresult
 nsEditorSpellCheck::DeleteSuggestedWordList()
 {
   mSuggestedWordList.Clear();
   mSuggestedWordIndex = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsEditorSpellCheck::UpdateCurrentDictionary()
+nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditor* aEditor)
 {
+  if (mDictWasSetManually) { // user has set dictionary manually; we better not change it.
+    return NS_OK;
+  }
+
   nsresult rv;
 
   UpdateDictionnaryHolder holder(this);
 
-  // Get language with html5 algorithm
+  // Tell the spellchecker what dictionary to use:
+  nsAutoString dictName;
+
+  // First, try to get language with html5 algorithm
+  nsAutoString editorLang;
+
   nsCOMPtr<nsIContent> rootContent;
-  nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor);
+
+  nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(aEditor);
   if (htmlEditor) {
     rootContent = htmlEditor->GetActiveEditingHost();
   } else {
     nsCOMPtr<nsIDOMElement> rootElement;
-    rv = mEditor->GetRootElement(getter_AddRefs(rootElement));
+    rv = aEditor->GetRootElement(getter_AddRefs(rootElement));
     NS_ENSURE_SUCCESS(rv, rv);
     rootContent = do_QueryInterface(rootElement);
   }
   NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
 
-  mPreferredLang.Truncate();
-  rootContent->GetLang(mPreferredLang);
-
-  // Tell the spellchecker what dictionary to use:
+  rootContent->GetLang(editorLang);
 
-  // First try to get dictionary from content prefs. If we have one, do not got
-  // further. Use this exact dictionary.
-  nsAutoString dictName;
-  rv = gDictionaryStore->FetchLastDictionary(mEditor, dictName);
-  if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) {
-    if (NS_FAILED(SetCurrentDictionary(dictName))) { 
-      // may be dictionary was uninstalled ?
-      gDictionaryStore->ClearCurrentDictionary(mEditor);
-    }
-    return NS_OK;
+  if (editorLang.IsEmpty()) {
+    nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
+    NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+    doc->GetContentLanguage(editorLang);
   }
 
-  if (mPreferredLang.IsEmpty()) {
-    nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
-    NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-    doc->GetContentLanguage(mPreferredLang);
-  }
-
-  // Then, try to use language computed from element
-  if (!mPreferredLang.IsEmpty()) {
-    dictName.Assign(mPreferredLang);
+  if (!editorLang.IsEmpty()) {
+    dictName.Assign(editorLang);
   }
 
   // otherwise, get language from preferences
   if (dictName.IsEmpty()) {
     dictName.Assign(Preferences::GetLocalizedString("spellchecker.dictionary"));
   }
 
   if (dictName.IsEmpty())
@@ -783,17 +538,17 @@ nsEditorSpellCheck::UpdateCurrentDiction
         }
       }
     }
   }
 
   // If we have not set dictionary, and the editable element doesn't have a
   // lang attribute, we try to get a dictionary. First try, en-US. If it does
   // not work, pick the first one.
-  if (mPreferredLang.IsEmpty()) {
+  if (editorLang.IsEmpty()) {
     nsAutoString currentDictionary;
     rv = GetCurrentDictionary(currentDictionary);
     if (NS_FAILED(rv) || currentDictionary.IsEmpty()) {
       rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US"));
       if (NS_FAILED(rv)) {
         nsTArray<nsString> dictList;
         rv = mSpellChecker->GetDictionaryList(&dictList);
         if (NS_SUCCEEDED(rv) && dictList.Length() > 0) {
@@ -807,13 +562,8 @@ nsEditorSpellCheck::UpdateCurrentDiction
   // fail silently so that the spellchecker dialog is allowed to come
   // up. The user can manually reset the language to their choice on
   // the dialog if it is wrong.
 
   DeleteSuggestedWordList();
 
   return NS_OK;
 }
-
-void 
-nsEditorSpellCheck::ShutDown() {
-  NS_IF_RELEASE(gDictionaryStore);
-}
--- a/editor/composer/src/nsEditorSpellCheck.h
+++ b/editor/composer/src/nsEditorSpellCheck.h
@@ -38,66 +38,54 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsEditorSpellCheck_h___
 #define nsEditorSpellCheck_h___
 
 
 #include "nsIEditorSpellCheck.h"
 #include "nsISpellChecker.h"
-#include "nsIObserver.h"
-#include "nsIURI.h"
-#include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsDataHashtable.h"
 
 #define NS_EDITORSPELLCHECK_CID                     \
 { /* {75656ad9-bd13-4c5d-939a-ec6351eea0cc} */        \
     0x75656ad9, 0xbd13, 0x4c5d,                       \
     { 0x93, 0x9a, 0xec, 0x63, 0x51, 0xee, 0xa0, 0xcc }\
 }
 
-class LastDictionary;
-
 class nsEditorSpellCheck : public nsIEditorSpellCheck
 {
 public:
   nsEditorSpellCheck();
   virtual ~nsEditorSpellCheck();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsEditorSpellCheck)
 
   /* Declare all methods in the nsIEditorSpellCheck interface */
   NS_DECL_NSIEDITORSPELLCHECK
 
-  static LastDictionary* gDictionaryStore;
-
-  static void ShutDown();
-
 protected:
   nsCOMPtr<nsISpellChecker> mSpellChecker;
 
   nsTArray<nsString>  mSuggestedWordList;
   PRInt32        mSuggestedWordIndex;
 
   // these are the words in the current personal dictionary,
   // GetPersonalDictionary must be called to load them.
   nsTArray<nsString>  mDictionaryList;
   PRInt32        mDictionaryIndex;
 
   nsresult       DeleteSuggestedWordList();
 
   nsCOMPtr<nsITextServicesFilter> mTxtSrvFilter;
-  nsCOMPtr<nsIEditor> mEditor;
-
-  nsString mPreferredLang;
 
   PRPackedBool mUpdateDictionaryRunning;
+  PRPackedBool mDictWasSetManually;
 
 public:
   void BeginUpdateDictionary() { mUpdateDictionaryRunning = PR_TRUE ;}
   void EndUpdateDictionary() { mUpdateDictionaryRunning = PR_FALSE ;}
 };
 
 #endif // nsEditorSpellCheck_h___
 
--- a/editor/composer/test/Makefile.in
+++ b/editor/composer/test/Makefile.in
@@ -44,22 +44,20 @@ relativesrcdir  = editor/composer/test
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_bug348497.html \
 		test_bug384147.html \
 		test_bug389350.html \
 		test_bug519928.html \
-		bug678842_subframe.html \
 		$(NULL)
 
 _CHROME_TEST_FILES = \
 		test_bug434998.xul \
 		test_bug338427.html \
-		test_bug678842.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
deleted file mode 100644
--- a/editor/composer/test/bug678842_subframe.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-</head>
-<body>
-<textarea id="textarea" lang="testing-XXX"></textarea>
-</body>
-</html>
deleted file mode 100644
--- a/editor/composer/test/test_bug678842.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=678842
--->
-<head>
-  <title>Test for Bug 678842</title>
-  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
-</head>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=678842">Mozilla Bug 678842</a>
-<p id="display"></p>
-<iframe id="content"></iframe>
-  
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-/** Test for Bug 678842 **/
-// just a fake test
-SimpleTest.waitForExplicitFinish();
-var content = document.getElementById('content');
-// load a subframe containing an editor with a defined unknown lang. At first
-// load, it will set dictionary to en-US. At second load, it will return current
-// dictionary. So, we can check, dictionary is correctly remembered between
-// loads.
-
-var firstLoad = true;
-
-var loadListener = function(evt) {
-  var doc = evt.target.contentDocument;
-  var elem = doc.getElementById('textarea');
-  var editor = elem.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor;
-  editor.setSpellcheckUserOverride(true);
-  var inlineSpellChecker = editor.getInlineSpellChecker(true);
-  var spellchecker = inlineSpellChecker.spellChecker;
-  var currentDictonary = "";
-  try {
-    currentDictonary = spellchecker.GetCurrentDictionary();
-  } catch(e) {}
-
-  if (!currentDictonary) {
-    spellchecker.SetCurrentDictionary('en-US');
-  }
-
-  if (firstLoad) {
-    firstLoad = false;
-    is (currentDictonary, "", "unexpected lang " + currentDictonary);
-    content.src = 'http://mochi.test:8888/tests/editor/composer/test/bug678842_subframe.html?firstload=false';
-  } else {
-    is (currentDictonary, "en-US", "unexpected lang " + currentDictonary + " instead of en-US");
-    content.removeEventListener('load', loadListener, false);
-  }
-}
-
-content.addEventListener('load', loadListener, false);
-
-content.src = 'http://mochi.test:8888/tests/editor/composer/test/bug678842_subframe.html?firstload=true';
-
-</script>
-</pre>
-</body>
-</html>
--- a/editor/idl/nsIEditorSpellCheck.idl
+++ b/editor/idl/nsIEditorSpellCheck.idl
@@ -36,17 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
  
 #include "nsISupports.idl"
 
 interface nsIEditor;
 interface nsITextServicesFilter;
 
-[scriptable, uuid(af84da62-588f-409f-847d-feecc991bd93)]
+[scriptable, uuid(3da0ce96-7d3a-48d0-80b7-2d90dab09747)]
 interface nsIEditorSpellCheck : nsISupports
 {
 
  /**
    * Returns true if we can enable spellchecking. If there are no available
    * dictionaries, this will return false.
    */
   boolean       canSpellCheck();
@@ -146,16 +146,24 @@ interface nsIEditorSpellCheck : nsISuppo
   AString       GetCurrentDictionary();
 
   /**
    * @see nsISpellChecker::SetCurrentDictionary
    */
   void          SetCurrentDictionary(in AString dictionary);
 
   /**
+   * Call to save the currently selected dictionary as the default. The
+   * function UninitSpellChecker will also do this, but that function may not
+   * be called in some situations. This function allows the caller to force the
+   * default right now.
+   */
+  void          saveDefaultDictionary();
+
+  /**
    * Call this to free up the spell checking object. It will also save the
    * current selected language as the default for future use.
    *
    * If you have called CanSpellCheck but not InitSpellChecker, you can still
    * call this function to clear the cached spell check object, and no
    * preference saving will happen.
    */
   void          UninitSpellChecker();
@@ -179,11 +187,11 @@ interface nsIEditorSpellCheck : nsISuppo
    * invalid.
    */
   boolean       CheckCurrentWordNoSuggest(in wstring suggestedWord);
 
   /**
    * Update the dictionary in use to be sure it corresponds to what the editor
    * needs.
    */
-  void          UpdateCurrentDictionary();
+  void          UpdateCurrentDictionary(in nsIEditor editor);
 
 };
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -1760,17 +1760,18 @@ NS_IMETHODIMP mozInlineSpellChecker::Upd
     return NS_OK;
   }
 
   nsAutoString previousDictionary;
   if (NS_FAILED(mSpellCheck->GetCurrentDictionary(previousDictionary))) {
     previousDictionary.Truncate();
   }
 
-  nsresult rv = mSpellCheck->UpdateCurrentDictionary();
+  nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
+  nsresult rv = mSpellCheck->UpdateCurrentDictionary(editor);
 
   nsAutoString currentDictionary;
   if (NS_FAILED(mSpellCheck->GetCurrentDictionary(currentDictionary))) {
     currentDictionary.Truncate();
   }
 
   if (!previousDictionary.Equals(currentDictionary)) {
       rv = SpellCheckRange(nsnull);
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -117,17 +117,16 @@
 
 #include "nsCycleCollector.h"
 #include "nsJSEnvironment.h"
 #include "nsContentSink.h"
 #include "nsFrameMessageManager.h"
 #include "nsRefreshDriver.h"
 
 #include "nsHyphenationManager.h"
-#include "nsEditorSpellCheck.h"
 #include "nsDOMMemoryReporter.h"
 
 extern void NS_ShutdownChainItemPool();
 
 using namespace mozilla;
 
 nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
 
@@ -354,10 +353,9 @@ nsLayoutStatics::Shutdown()
 
   nsFrameList::Shutdown();
 
   nsHTMLInputElement::DestroyUploadLastDir();
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
-  nsEditorSpellCheck::ShutDown();
 }
--- a/toolkit/components/contentprefs/nsContentPrefService.js
+++ b/toolkit/components/contentprefs/nsContentPrefService.js
@@ -70,19 +70,18 @@ function electrolify(service) {
       // and using that to discover all the websites visited, etc.
       // Also there are both potential race conditions (if two processes
       // set more than one value in succession, and the values
       // only make sense together), as well as security issues, if
       // a compromised content process can send arbitrary setPref
       // messages. The whitelist contains only those settings that
       // are not at risk for either.
       // We currently whitelist saving/reading the last directory of file
-      // uploads, and the last current spellchecker dictionary which are so far
-      // the only need we have identified.
-      const NAME_WHITELIST = ["browser.upload.lastDir", "spellcheck.lang"];
+      // uploads, which is so far the only need we have identified.
+      const NAME_WHITELIST = ["browser.upload.lastDir"];
       if (NAME_WHITELIST.indexOf(json.name) == -1)
         return { succeeded: false };
 
       switch (aMessage.name) {
         case "ContentPref:getPref":
           return { succeeded: true,
                    value: service.getPref(json.group, json.name, json.value) };
 
--- a/toolkit/content/InlineSpellChecker.jsm
+++ b/toolkit/content/InlineSpellChecker.jsm
@@ -255,16 +255,17 @@ InlineSpellChecker.prototype = {
 
   // callback for selecting a dictionary
   selectDictionary: function(index)
   {
     if (! this.mInlineSpellChecker || index < 0 || index >= this.mDictionaryNames.length)
       return;
     var spellchecker = this.mInlineSpellChecker.spellChecker;
     spellchecker.SetCurrentDictionary(this.mDictionaryNames[index]);
+    spellchecker.saveDefaultDictionary();
     this.mInlineSpellChecker.spellCheckRange(null); // causes recheck
   },
 
   // callback for selecting a suggesteed replacement
   replaceMisspelling: function(index)
   {
     if (! this.mInlineSpellChecker || ! this.mOverMisspelling)
       return;