--- a/editor/composer/src/nsEditorSpellCheck.cpp
+++ b/editor/composer/src/nsEditorSpellCheck.cpp
@@ -48,18 +48,23 @@
#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;
@@ -75,34 +80,245 @@ 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_2(nsEditorSpellCheck,
+NS_IMPL_CYCLE_COLLECTION_3(nsEditorSpellCheck,
+ mEditor,
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;
@@ -130,18 +346,34 @@ 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);
@@ -203,17 +435,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(aEditor);
+ UpdateCurrentDictionary();
return NS_OK;
}
NS_IMETHODIMP
nsEditorSpellCheck::GetNextMisspelledWord(PRUnichar **aNextMisspelledWord)
{
NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
@@ -386,17 +618,39 @@ nsEditorSpellCheck::GetCurrentDictionary
}
NS_IMETHODIMP
nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary)
{
NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
if (!mUpdateDictionaryRunning) {
- mDictWasSetManually = PR_TRUE;
+
+ 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);
}
return mSpellChecker->SetCurrentDictionary(aDictionary);
}
NS_IMETHODIMP
nsEditorSpellCheck::UninitSpellChecker()
{
NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED);
@@ -404,31 +658,16 @@ 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;
}
@@ -437,55 +676,61 @@ nsresult
nsEditorSpellCheck::DeleteSuggestedWordList()
{
mSuggestedWordList.Clear();
mSuggestedWordIndex = 0;
return NS_OK;
}
NS_IMETHODIMP
-nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditor* aEditor)
+nsEditorSpellCheck::UpdateCurrentDictionary()
{
- if (mDictWasSetManually) { // user has set dictionary manually; we better not change it.
- return NS_OK;
- }
-
nsresult rv;
UpdateDictionnaryHolder holder(this);
- // Tell the spellchecker what dictionary to use:
- nsAutoString dictName;
-
- // First, try to get language with html5 algorithm
- nsAutoString editorLang;
-
+ // Get language with html5 algorithm
nsCOMPtr<nsIContent> rootContent;
-
- nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(aEditor);
+ nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor);
if (htmlEditor) {
rootContent = htmlEditor->GetActiveEditingHost();
} else {
nsCOMPtr<nsIDOMElement> rootElement;
- rv = aEditor->GetRootElement(getter_AddRefs(rootElement));
+ rv = mEditor->GetRootElement(getter_AddRefs(rootElement));
NS_ENSURE_SUCCESS(rv, rv);
rootContent = do_QueryInterface(rootElement);
}
NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE);
- rootContent->GetLang(editorLang);
+ mPreferredLang.Truncate();
+ rootContent->GetLang(mPreferredLang);
+
+ // Tell the spellchecker what dictionary to use:
- if (editorLang.IsEmpty()) {
+ // 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 (mPreferredLang.IsEmpty()) {
nsCOMPtr<nsIDocument> doc = rootContent->GetCurrentDoc();
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
- doc->GetContentLanguage(editorLang);
+ doc->GetContentLanguage(mPreferredLang);
}
- if (!editorLang.IsEmpty()) {
- dictName.Assign(editorLang);
+ // Then, try to use language computed from element
+ if (!mPreferredLang.IsEmpty()) {
+ dictName.Assign(mPreferredLang);
}
// otherwise, get language from preferences
if (dictName.IsEmpty()) {
dictName.Assign(Preferences::GetLocalizedString("spellchecker.dictionary"));
}
if (dictName.IsEmpty())
@@ -538,17 +783,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 (editorLang.IsEmpty()) {
+ if (mPreferredLang.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) {
@@ -562,8 +807,13 @@ 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,54 +38,66 @@
* ***** 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,20 +44,22 @@ 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)
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/bug678842_subframe.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="textarea" lang="testing-XXX"></textarea>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/editor/composer/test/test_bug678842.html
@@ -0,0 +1,63 @@
+<!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(3da0ce96-7d3a-48d0-80b7-2d90dab09747)]
+[scriptable, uuid(af84da62-588f-409f-847d-feecc991bd93)]
interface nsIEditorSpellCheck : nsISupports
{
/**
* Returns true if we can enable spellchecking. If there are no available
* dictionaries, this will return false.
*/
boolean canSpellCheck();
@@ -146,24 +146,16 @@ 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();
@@ -187,11 +179,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(in nsIEditor editor);
+ void UpdateCurrentDictionary();
};
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -1760,18 +1760,17 @@ NS_IMETHODIMP mozInlineSpellChecker::Upd
return NS_OK;
}
nsAutoString previousDictionary;
if (NS_FAILED(mSpellCheck->GetCurrentDictionary(previousDictionary))) {
previousDictionary.Truncate();
}
- nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
- nsresult rv = mSpellCheck->UpdateCurrentDictionary(editor);
+ nsresult rv = mSpellCheck->UpdateCurrentDictionary();
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,16 +117,17 @@
#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;
@@ -353,9 +354,10 @@ 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,18 +70,19 @@ 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, which is so far the only need we have identified.
- const NAME_WHITELIST = ["browser.upload.lastDir"];
+ // 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"];
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,17 +255,16 @@ 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;