☠☠ backed out by db9e99d537f2 ☠ ☠ | |
author | Jesper Kristensen <bugzilla@jesperkristensen.dk> |
Mon, 05 Sep 2011 18:44:34 -0400 | |
changeset 76566 | a422b9ff0a9eb3fe278e0866faccb4a4016f40c5 |
parent 76565 | 0959354c6d6a6ff96df8d859e8e7befd0f2bdce0 |
child 76567 | 4771660af6b2a802d5d897dd9062f318660c66ba |
push id | 21120 |
push user | khuey@mozilla.com |
push date | Tue, 06 Sep 2011 10:53:34 +0000 |
treeherder | mozilla-central@1f5cd567c93a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | ehsan |
bugs | 591780 |
milestone | 9.0a1 |
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
|
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -316,20 +316,18 @@ nsContextMenu.prototype = { !this.onTextInput && top.gBidiUI); }, initSpellingItems: function() { var canSpell = InlineSpellCheckerUI.canSpellCheck; var onMisspelling = InlineSpellCheckerUI.overMisspelling; this.showItem("spell-check-enabled", canSpell); this.showItem("spell-separator", canSpell || this.onEditableArea); - if (canSpell) { - document.getElementById("spell-check-enabled") - .setAttribute("checked", InlineSpellCheckerUI.enabled); - } + document.getElementById("spell-check-enabled") + .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled); this.showItem("spell-add-to-dictionary", onMisspelling); // suggestion list this.showItem("spell-suggestions-separator", onMisspelling); if (onMisspelling) { var suggestionsSeparator = document.getElementById("spell-add-to-dictionary");
--- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -645,16 +645,24 @@ nsEditorSpellCheck::SetCurrentDictionary // 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::GetSpellChecker(nsISpellChecker **aSpellChecker) +{ + *aSpellChecker = mSpellChecker; + NS_IF_ADDREF(*aSpellChecker); + return NS_OK; +} + NS_IMETHODIMP nsEditorSpellCheck::UninitSpellChecker() { NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); // Cleanup - kill the spell checker DeleteSuggestedWordList(); mDictionaryList.Clear(); @@ -813,21 +821,17 @@ nsEditorSpellCheck::UpdateCurrentDiction // lang attribute, we try to get a dictionary. First try, en-US. If it does // not work, pick the first one. 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) { - SetCurrentDictionary(dictList[0]); - } + mSpellChecker->CheckCurrentDictionary(); } } } // If an error was thrown while setting the dictionary, just // 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.
--- a/editor/idl/nsIEditorSpellCheck.idl +++ b/editor/idl/nsIEditorSpellCheck.idl @@ -35,21 +35,30 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" interface nsIEditor; interface nsITextServicesFilter; +%{C++ +#include "nsISpellChecker.h" +%} +[ptr] native nsISpellChecker(nsISpellChecker); -[scriptable, uuid(af84da62-588f-409f-847d-feecc991bd93)] +[scriptable, uuid(334946c3-0e93-4aac-b662-e1b56f95d68b)] interface nsIEditorSpellCheck : nsISupports { + /** + * Get the spell checker used by this editor. + */ + [noscript] readonly attribute nsISpellChecker spellChecker; + /** * Returns true if we can enable spellchecking. If there are no available * dictionaries, this will return false. */ boolean canSpellCheck(); /** * Turns on the spell checker for the given editor. enableSelectionChecking
--- a/editor/libeditor/base/Makefile.in +++ b/editor/libeditor/base/Makefile.in @@ -89,9 +89,10 @@ FORCE_STATIC_LIB = 1 include $(topsrcdir)/config/rules.mk INCLUDES += \ -I$(topsrcdir)/editor/libeditor/text \ -I$(topsrcdir)/content/base/src \ -I$(topsrcdir)/content/events/src \ -I$(topsrcdir)/layout/style \ + -I$(topsrcdir)/extensions/spellcheck/src \ $(NULL)
--- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -19,16 +19,17 @@ * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf <pp@ludusdesign.com> * Daniel Glazman <glazman@netscape.com> * Masayuki Nakano <masayuki@d-toybox.com> * Mats Palmgren <matspal@gmail.com> + * Jesper Kristensen <mail@jesperkristensen.dk> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -43,16 +44,21 @@ #include "nsIDOMDocument.h" #include "nsIDOMHTMLElement.h" #include "nsIDOMNSHTMLElement.h" #include "nsIDOMNSEvent.h" #include "nsIMEStateManager.h" #include "nsFocusManager.h" #include "nsUnicharUtils.h" #include "nsReadableUtils.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "mozISpellCheckingEngine.h" +#include "nsIEditorSpellCheck.h" +#include "mozInlineSpellChecker.h" #include "nsIDOMText.h" #include "nsIDOMElement.h" #include "nsIDOMAttr.h" #include "nsIDOMNode.h" #include "nsIDOMDocumentFragment.h" #include "nsIDOMNamedNodeMap.h" #include "nsIDOMNodeList.h" @@ -202,16 +208,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN( NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEventListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor) NS_INTERFACE_MAP_ENTRY(nsIPhonetic) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport) NS_INTERFACE_MAP_ENTRY(nsIEditor) + NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditor) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor) NS_IMETHODIMP @@ -295,16 +302,23 @@ nsEditor::PostCreate() // nuke the modification count, so the doc appears unmodified // do this before we notify listeners ResetModificationCount(); // update the UI with our state NotifyDocumentListeners(eDocumentCreated); NotifyDocumentListeners(eDocumentStateChanged); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, + PR_FALSE); + } } // update nsTextStateManager and caret if we have focus nsCOMPtr<nsIContent> focusedContent = GetFocusedContent(); if (focusedContent) { nsCOMPtr<nsIPresShell> ps = GetPresShell(); NS_ASSERTION(ps, "no pres shell even though we have focus"); NS_ENSURE_TRUE(ps, NS_ERROR_UNEXPECTED); @@ -407,16 +421,22 @@ nsEditor::GetDesiredSpellCheckState() } NS_IMETHODIMP nsEditor::PreDestroy(PRBool aDestroyingFrames) { if (mDidPreDestroy) return NS_OK; + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION); + } + // Let spellchecker clean up its observers etc. It is important not to // actually free the spellchecker here, since the spellchecker could have // caused flush notifications, which could have gotten here if a textbox // is being removed. Setting the spellchecker to NULL could free the // object that is still in use! It will be freed when the editor is // destroyed. if (mInlineSpellChecker) mInlineSpellChecker->Cleanup(aDestroyingFrames); @@ -1278,16 +1298,23 @@ NS_IMETHODIMP nsEditor::GetInlineSpellCh if (mDidPreDestroy) { // Don't allow people to get or create the spell checker once the editor // is going away. *aInlineSpellChecker = nsnull; return autoCreate ? NS_ERROR_NOT_AVAILABLE : NS_OK; } + // We don't want to show the spell checking UI if there are no spell check dictionaries available. + PRBool canSpell = mozInlineSpellChecker::CanEnableInlineSpellChecking(); + if (!canSpell) { + *aInlineSpellChecker = nsnull; + return NS_ERROR_FAILURE; + } + nsresult rv; if (!mInlineSpellChecker && autoCreate) { mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } if (mInlineSpellChecker) { rv = mInlineSpellChecker->Init(this); @@ -1296,27 +1323,59 @@ NS_IMETHODIMP nsEditor::GetInlineSpellCh NS_ENSURE_SUCCESS(rv, rv); } NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker); return NS_OK; } +NS_IMETHODIMP nsEditor::Observe(nsISupports* aSubj, const char *aTopic, + const PRUnichar *aData) +{ + NS_ASSERTION(!strcmp(aTopic, + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION), + "Unexpected observer topic"); + + // When mozInlineSpellChecker::CanEnableInlineSpellChecking changes + SyncRealTimeSpell(); + + // When nsIEditorSpellCheck::GetCurrentDictionary changes + if (mInlineSpellChecker) { + // if the current dictionary is no longer available, find another one + nsCOMPtr<nsIEditorSpellCheck> editorSpellCheck; + mInlineSpellChecker->GetSpellChecker(getter_AddRefs(editorSpellCheck)); + if (editorSpellCheck) { + nsCOMPtr<nsISpellChecker> spellChecker; + editorSpellCheck->GetSpellChecker(getter_AddRefs(spellChecker)); + spellChecker->CheckCurrentDictionary(); + } + + // update the inline spell checker to reflect the new current dictionary + mInlineSpellChecker->SpellCheckRange(nsnull); // causes recheck + } + + return NS_OK; +} + NS_IMETHODIMP nsEditor::SyncRealTimeSpell() { NS_TIME_FUNCTION; PRBool enable = GetDesiredSpellCheckState(); + // Initializes mInlineSpellChecker nsCOMPtr<nsIInlineSpellChecker> spellChecker; GetInlineSpellChecker(enable, getter_AddRefs(spellChecker)); - if (spellChecker) { - spellChecker->SetEnableRealTimeSpell(enable); + if (mInlineSpellChecker) { + // We might have a mInlineSpellChecker even if there are no dictionaries + // available since we don't destroy the mInlineSpellChecker when the last + // dictionariy is removed, but in that case spellChecker is null + mInlineSpellChecker->SetEnableRealTimeSpell(enable && spellChecker); } return NS_OK; } NS_IMETHODIMP nsEditor::SetSpellcheckUserOverride(PRBool enable) { mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
--- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -61,16 +61,17 @@ #include "nsIDOMElement.h" #include "nsSelectionState.h" #include "nsIEditorSpellCheck.h" #include "nsIInlineSpellChecker.h" #include "nsIDOMEventTarget.h" #include "nsStubMutationObserver.h" #include "nsIViewManager.h" #include "nsCycleCollectionParticipant.h" +#include "nsIObserver.h" class nsIDOMCharacterData; class nsIDOMRange; class nsIPresShell; class ChangeAttributeTxn; class CreateElementTxn; class InsertElementTxn; class DeleteElementTxn; @@ -95,16 +96,17 @@ class nsIDOMNSEvent; /** implementation of an editor object. it will be the controller/focal point * for the main editor services. i.e. the GUIManager, publishing, transaction * manager, event interfaces. the idea for the event interfaces is to have them * delegate the actual commands to the editor independent of the XPFE implementation. */ class nsEditor : public nsIEditor, public nsIEditorIMESupport, public nsSupportsWeakReference, + public nsIObserver, public nsIPhonetic { public: enum IterDirection { kIterForward, kIterBackward @@ -148,16 +150,19 @@ public: already_AddRefed<nsIPresShell> GetPresShell(); void NotifyEditorObservers(); /* ------------ nsIEditor methods -------------- */ NS_DECL_NSIEDITOR /* ------------ nsIEditorIMESupport methods -------------- */ NS_DECL_NSIEDITORIMESUPPORT + /* ------------ nsIObserver methods -------------- */ + NS_DECL_NSIOBSERVER + // nsIPhonetic NS_DECL_NSIPHONETIC public: NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert, nsCOMPtr<nsIDOMNode> *aInOutNode,
--- a/editor/txtsvc/public/nsISpellChecker.h +++ b/editor/txtsvc/public/nsISpellChecker.h @@ -39,19 +39,19 @@ #define nsISpellChecker_h__ #include "nsISupports.h" #include "nsTArray.h" #define NS_SPELLCHECKER_CONTRACTID "@mozilla.org/spellchecker;1" #define NS_ISPELLCHECKER_IID \ -{ /* E75AC48C-E948-452E-8DB3-30FEE29FE3D2 */ \ -0xe75ac48c, 0xe948, 0x452e, \ - { 0x8d, 0xb3, 0x30, 0xfe, 0xe2, 0x9f, 0xe3, 0xd2 } } +{ /* 27bff957-b486-40ae-9f5d-af0cdd211868 */ \ +0x27bff957, 0xb486, 0x40ae, \ + { 0x9f, 0x5d, 0xaf, 0x0c, 0xdd, 0x21, 0x18, 0x68 } } class nsITextServicesDocument; class nsString; /** * A generic interface for a spelling checker. */ class nsISpellChecker : public nsISupports{ @@ -141,14 +141,20 @@ public: /** * Tells the spellchecker to use a specific dictionary. * @param aDictionary a string that is in the list returned * by GetDictionaryList() or an empty string. If aDictionary is * empty string, spellchecker will be disabled. */ NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary) = 0; + + /** + * Call this on any change in installed dictionaries to ensure that the spell + * checker is not using a current dictionary which is no longer available. + */ + NS_IMETHOD CheckCurrentDictionary() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsISpellChecker, NS_ISPELLCHECKER_IID) #endif // nsISpellChecker_h__
--- a/extensions/spellcheck/Makefile.in +++ b/extensions/spellcheck/Makefile.in @@ -39,9 +39,13 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = spellchecker DIRS = idl locales hunspell src +ifdef ENABLE_TESTS +DIRS += tests +endif + include $(topsrcdir)/config/rules.mk
--- a/extensions/spellcheck/hunspell/src/Makefile.in +++ b/extensions/spellcheck/hunspell/src/Makefile.in @@ -66,11 +66,13 @@ CPPSRCS += affentry.cpp \ # This variable is referenced in configure.in. Make sure to change that file # too if you need to change this variable. DEFINES = -DHUNSPELL_STATIC endif include $(topsrcdir)/config/rules.mk +INCLUDES += -I$(topsrcdir)/extensions/spellcheck/src + ifdef MOZ_NATIVE_HUNSPELL CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS) endif
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp +++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp @@ -36,16 +36,17 @@ * Varga Daniel * Chris Halls * Rene Engelhard * Bram Moolenaar * Dafydd Jones * Harri Pitkanen * Andras Timar * Tor Lillqvist + * Jesper Kristensen (mail@jesperkristensen.dk) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -65,16 +66,18 @@ #include "nsIFile.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "mozISpellI18NManager.h" #include "nsICharsetConverterManager.h" #include "nsUnicharUtilCIID.h" #include "nsUnicharUtils.h" #include "nsCRT.h" +#include "mozInlineSpellChecker.h" +#include "mozilla/Services.h" #include <stdlib.h> #include "nsIMemoryReporter.h" static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID); NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell) NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell) @@ -117,18 +120,17 @@ NS_MEMORY_REPORTER_IMPLEMENT(Hunspell, nsresult mozHunspell::Init() { if (!mDictionaries.Init()) return NS_ERROR_OUT_OF_MEMORY; LoadDictionaryList(); - nsCOMPtr<nsIObserverService> obs = - do_GetService("@mozilla.org/observer-service;1"); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "profile-do-change", PR_TRUE); } mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell); NS_RegisterMemoryReporter(mHunspellReporter); return NS_OK; @@ -142,60 +144,58 @@ mozHunspell::~mozHunspell() NS_UnregisterMemoryReporter(mHunspellReporter); } /* attribute wstring dictionary; */ NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); - if (mDictionary.IsEmpty()) - return NS_ERROR_NOT_INITIALIZED; - *aDictionary = ToNewUnicode(mDictionary); return *aDictionary ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } /* set the Dictionary. * This also Loads the dictionary and initializes the converter using the dictionaries converter */ NS_IMETHODIMP mozHunspell::SetDictionary(const PRUnichar *aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); - if (mDictionary.Equals(aDictionary)) - return NS_OK; - nsIFile* affFile = mDictionaries.GetWeak(nsDependentString(aDictionary)); if (!affFile) return NS_ERROR_FILE_NOT_FOUND; nsCAutoString dictFileName, affFileName; // XXX This isn't really good. nsIFile->NativePath isn't safe for all // character sets on Windows. // A better way would be to QI to nsILocalFile, and get a filehandle // from there. Only problem is that hunspell wants a path nsresult rv = affFile->GetNativePath(affFileName); NS_ENSURE_SUCCESS(rv, rv); + if (mAffixFileName.Equals(affFileName.get())) + return NS_OK; + dictFileName = affFileName; PRInt32 dotPos = dictFileName.RFindChar('.'); if (dotPos == -1) return NS_ERROR_FAILURE; dictFileName.SetLength(dotPos); dictFileName.AppendLiteral(".dic"); // SetDictionary can be called multiple times, so we might have a // valid mHunspell instance which needs cleaned up. delete mHunspell; mDictionary = aDictionary; + mAffixFileName = affFileName; mHunspell = new Hunspell(affFileName.get(), dictFileName.get()); if (!mHunspell) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); @@ -217,16 +217,23 @@ NS_IMETHODIMP mozHunspell::SetDictionary if (pos == -1) pos = mDictionary.FindChar('_'); if (pos == -1) mLanguage.Assign(mDictionary); else mLanguage = Substring(mDictionary, 0, pos); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nsnull, + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, + nsnull); + } + return NS_OK; } /* readonly attribute wstring language; */ NS_IMETHODIMP mozHunspell::GetLanguage(PRUnichar **aLanguage) { NS_ENSURE_ARG_POINTER(aLanguage); @@ -328,28 +335,37 @@ NS_IMETHODIMP mozHunspell::GetDictionary } *aDictionaries = ans.dics; *aCount = ans.count; return NS_OK; } +static PLDHashOperator +FindFirstString(const nsAString& aString, nsIFile* aFile, void* aClosure) +{ + nsAString *dic = (nsAString*) aClosure; + dic->Assign(aString); + return PL_DHASH_STOP; +} + void mozHunspell::LoadDictionaryList() { mDictionaries.Clear(); nsresult rv; nsCOMPtr<nsIProperties> dirSvc = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); if (!dirSvc) return; + // find built in dictionaries nsCOMPtr<nsIFile> dictDir; rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY, NS_GET_IID(nsIFile), getter_AddRefs(dictDir)); if (NS_SUCCEEDED(rv)) { LoadDictionariesFromDir(dictDir); } else { // try to load gredir/dictionaries @@ -367,31 +383,77 @@ mozHunspell::LoadDictionaryList() NS_GET_IID(nsIFile), getter_AddRefs(appDir)); PRBool equals; if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) { appDir->AppendNative(NS_LITERAL_CSTRING("dictionaries")); LoadDictionariesFromDir(appDir); } } + // find dictionaries from extensions requiring restart nsCOMPtr<nsISimpleEnumerator> dictDirs; rv = dirSvc->Get(DICTIONARY_SEARCH_DIRECTORY_LIST, NS_GET_IID(nsISimpleEnumerator), getter_AddRefs(dictDirs)); if (NS_FAILED(rv)) return; PRBool hasMore; while (NS_SUCCEEDED(dictDirs->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr<nsISupports> elem; dictDirs->GetNext(getter_AddRefs(elem)); dictDir = do_QueryInterface(elem); if (dictDir) LoadDictionariesFromDir(dictDir); } + + // find dictionaries from restartless extensions + for (PRUint32 i = 0; i < mDynamicDirectories.Count(); i++) { + LoadDictionariesFromDir(mDynamicDirectories[i]); + } + + // Now we have finished updating the list of dictionaries, update the current + // dictionary and any editors which may use it. + mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking(); + + // If the current dictionary has gone, try to replace it with another + // dictionary of the same language + if (!mDictionary.IsEmpty()) { + rv = SetDictionary(mDictionary.get()); + if (NS_SUCCEEDED(rv)) + return; + } + + // If we didn't find a dictionary equal to the current dictionary or we had + // no current dictionary, just pick an arbitrary dictionary. + nsAutoString firstDictionary; + mDictionaries.EnumerateRead(FindFirstString, &firstDictionary); + if (!firstDictionary.IsEmpty()) { + rv = SetDictionary(firstDictionary.get()); + if (NS_SUCCEEDED(rv)) + return; + } + + // If there are no dictionaries, set no current dictionary + if (!mDictionary.IsEmpty()) { + delete mHunspell; + mHunspell = nsnull; + mDictionary.AssignLiteral(""); + mAffixFileName.AssignLiteral(""); + mLanguage.AssignLiteral(""); + mDecoder = nsnull; + mEncoder = nsnull; + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nsnull, + SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION, + nsnull); + } + } } NS_IMETHODIMP mozHunspell::LoadDictionariesFromDir(nsIFile* aDir) { nsresult rv; PRBool check = PR_FALSE; @@ -537,8 +599,24 @@ mozHunspell::Observe(nsISupports* aSubj, { NS_ASSERTION(!strcmp(aTopic, "profile-do-change"), "Unexpected observer topic"); LoadDictionaryList(); return NS_OK; } + +/* void addDirectory(in nsIFile dir); */ +NS_IMETHODIMP mozHunspell::AddDirectory(nsIFile *aDir) +{ + mDynamicDirectories.AppendObject(aDir); + LoadDictionaryList(); + return NS_OK; +} + +/* void removeDirectory(in nsIFile dir); */ +NS_IMETHODIMP mozHunspell::RemoveDirectory(nsIFile *aDir) +{ + mDynamicDirectories.RemoveObject(aDir); + LoadDictionaryList(); + return NS_OK; +}
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h +++ b/extensions/spellcheck/hunspell/src/mozHunspell.h @@ -36,16 +36,17 @@ * Varga Daniel * Chris Halls * Rene Engelhard * Bram Moolenaar * Dafydd Jones * Harri Pitkanen * Andras Timar * Tor Lillqvist + * Jesper Kristensen (mail@jesperkristensen.dk) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -59,16 +60,17 @@ #ifndef mozHunspell_h__ #define mozHunspell_h__ #include <hunspell.hxx> #include "mozISpellCheckingEngine.h" #include "mozIPersonalDictionary.h" #include "nsString.h" #include "nsCOMPtr.h" +#include "nsCOMArray.h" #include "nsIObserver.h" #include "nsIUnicodeEncoder.h" #include "nsIUnicodeDecoder.h" #include "nsInterfaceHashtable.h" #include "nsWeakReference.h" #include "nsCycleCollectionParticipant.h" #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1" @@ -104,15 +106,19 @@ protected: nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary; nsCOMPtr<nsIUnicodeEncoder> mEncoder; nsCOMPtr<nsIUnicodeDecoder> mDecoder; // Hashtable matches dictionary name to .aff file nsInterfaceHashtable<nsStringHashKey, nsIFile> mDictionaries; nsString mDictionary; nsString mLanguage; + nsCString mAffixFileName; + + // dynamic dirs used to search for dictionaries + nsCOMArray<nsIFile> mDynamicDirectories; Hunspell *mHunspell; nsIMemoryReporter* mHunspellReporter; }; #endif
--- a/extensions/spellcheck/idl/mozISpellCheckingEngine.idl +++ b/extensions/spellcheck/idl/mozISpellCheckingEngine.idl @@ -15,16 +15,17 @@ * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * David Einstein. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Jesper Kristensen <mail@jesperkristensen.dk> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -35,30 +36,41 @@ * * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" interface nsIFile; interface mozIPersonalDictionary; -[scriptable, uuid(6eb307d6-3567-481a-971a-feb666b8ae72)] +[scriptable, uuid(8ba643a4-7ddc-4662-b976-7ec123843f10)] /** * This interface represents a SpellChecker. */ interface mozISpellCheckingEngine : nsISupports { /** * The name of the current dictionary + * + * Whenever getDictionaryList is not empty, this attribute contains a value + * from that list. Whenever getDictionaryList is empty, this attribute + * contains the empty string. Setting this attribute to a value not in + * getDictionaryList will throw NS_ERROR_FILE_NOT_FOUND. + * + * The spellcheck engine will send a notification with + * "spellcheck-dictionary-update" as topic when this changes. */ attribute wstring dictionary; /** * The language this spellchecker is using when checking + * + * The spellcheck engine will send a notification with + * "spellcheck-dictionary-update" as topic when this changes. */ readonly attribute wstring language; /** * Does the engine provide its own personal dictionary? */ readonly attribute boolean providesPersonalDictionary; @@ -84,26 +96,45 @@ interface mozISpellCheckingEngine : nsIS /** * Get the list of dictionaries */ void getDictionaryList([array, size_is(count)] out wstring dictionaries, out PRUint32 count); /** * check a word + * + * The spellcheck engine will send a notification with + * "spellcheck-dictionary-update" as topic when this changes. */ boolean check(in wstring word); /** * get a list of suggestions for a misspelled word + * + * The spellcheck engine will send a notification with + * "spellcheck-dictionary-update" as topic when this changes. */ void suggest(in wstring word,[array, size_is(count)] out wstring suggestions, out PRUint32 count); /** * Load dictionaries from the specified dir */ void loadDictionariesFromDir(in nsIFile dir); + + /** + * Add dictionaries from a directory to the spell checker + */ + void addDirectory(in nsIFile dir); + + /** + * Remove dictionaries from a directory from the spell checker + */ + void removeDirectory(in nsIFile dir); }; %{C++ #define DICTIONARY_SEARCH_DIRECTORY "DictD" #define DICTIONARY_SEARCH_DIRECTORY_LIST "DictDL" + +#define SPELLCHECK_DICTIONARY_UPDATE_NOTIFICATION \ + "spellcheck-dictionary-update" %}
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -595,19 +595,19 @@ nsresult mozInlineSpellChecker::Cleanup( // This function can be called to see if it seems likely that we can enable // spellchecking before actually creating the InlineSpellChecking objects. // // The problem is that we can't get the dictionary list without actually // creating a whole bunch of spellchecking objects. This function tries to // do that and caches the result so we don't have to keep allocating those // objects if there are no dictionaries or spellchecking. // -// This caching will prevent adding dictionaries at runtime if we start out -// with no dictionaries! Installing dictionaries as extensions will require -// a restart anyway, so it shouldn't be a problem. +// Whenever dictionaries are added or removed at runtime, this value must be +// updated before an observer notification is sent out about the change, to +// avoid editors getting a wrong cached result. PRBool // static mozInlineSpellChecker::CanEnableInlineSpellChecking() { nsresult rv; if (gCanEnableSpellChecking == SpellCheck_Uninitialized) { gCanEnableSpellChecking = SpellCheck_NotAvailable; @@ -620,16 +620,22 @@ mozInlineSpellChecker::CanEnableInlineSp NS_ENSURE_SUCCESS(rv, PR_FALSE); if (canSpellCheck) gCanEnableSpellChecking = SpellCheck_Available; } return (gCanEnableSpellChecking == SpellCheck_Available); } +void // static +mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking() +{ + gCanEnableSpellChecking = SpellCheck_Uninitialized; +} + // mozInlineSpellChecker::RegisterEventListeners // // The inline spell checker listens to mouse events and keyboard navigation+ // events. nsresult mozInlineSpellChecker::RegisterEventListeners() { nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
--- a/extensions/spellcheck/src/mozInlineSpellChecker.h +++ b/extensions/spellcheck/src/mozInlineSpellChecker.h @@ -224,18 +224,20 @@ private: public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIEDITACTIONLISTENER NS_DECL_NSIINLINESPELLCHECKER NS_DECL_NSIDOMEVENTLISTENER NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener) - // returns true if it looks likely that we can enable real-time spell checking + // returns true if there are any spell checking dictionaries available static PRBool CanEnableInlineSpellChecking(); + // update the cached value whenever the list of available dictionaries changes + static void UpdateCanEnableInlineSpellChecking(); nsresult Blur(nsIDOMEvent* aEvent); nsresult MouseClick(nsIDOMEvent* aMouseEvent); nsresult KeyPress(nsIDOMEvent* aKeyEvent); mozInlineSpellChecker(); virtual ~mozInlineSpellChecker();
--- a/extensions/spellcheck/src/mozSpellChecker.cpp +++ b/extensions/spellcheck/src/mozSpellChecker.cpp @@ -13,16 +13,17 @@ * * The Original Code is Mozilla Spellchecker Component. * * The Initial Developer of the Original Code is David Einstein. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): David Einstein Deinst@world.std.com + * Jesper Kristensen <mail@jesperkristensen.dk> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -72,19 +73,16 @@ mozSpellChecker::~mozSpellChecker() } nsresult mozSpellChecker::Init() { mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); mSpellCheckingEngine = nsnull; - mCurrentEngineContractId = nsnull; - mDictionariesMap.Init(); - InitSpellCheckDictionaryMap(); return NS_OK; } NS_IMETHODIMP mozSpellChecker::SetDocument(nsITextServicesDocument *aDoc, PRBool aFromStartofDoc) { mTsDoc = aDoc; @@ -302,45 +300,55 @@ mozSpellChecker::GetPersonalDictionary(n nsAutoString word; while (NS_SUCCEEDED(words->HasMore(&hasMore)) && hasMore) { words->GetNext(word); aWordList->AppendElement(word); } return NS_OK; } -struct AppendNewStruct -{ - nsTArray<nsString> *dictionaryList; - PRBool failed; -}; - -static PLDHashOperator -AppendNewString(const nsAString& aString, nsCString*, void* aClosure) -{ - AppendNewStruct *ans = (AppendNewStruct*) aClosure; - - if (!ans->dictionaryList->AppendElement(aString)) - { - ans->failed = PR_TRUE; - return PL_DHASH_STOP; - } - - return PL_DHASH_NEXT; -} - NS_IMETHODIMP mozSpellChecker::GetDictionaryList(nsTArray<nsString> *aDictionaryList) { - AppendNewStruct ans = {aDictionaryList, PR_FALSE}; + nsresult rv; + + // For catching duplicates + nsClassHashtable<nsStringHashKey, nsCString> dictionaries; + dictionaries.Init(); + + nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines; + rv = GetEngineList(&spellCheckingEngines); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { + nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i]; - mDictionariesMap.EnumerateRead(AppendNewString, &ans); + PRUint32 count = 0; + PRUnichar **words = NULL; + engine->GetDictionaryList(&words, &count); + for (PRUint32 k = 0; k < count; k++) { + nsAutoString dictName; + + dictName.Assign(words[k]); - if (ans.failed) - return NS_ERROR_OUT_OF_MEMORY; + // Skip duplicate dictionaries. Only take the first one + // for each name. + if (dictionaries.Get(dictName, NULL)) + continue; + + dictionaries.Put(dictName, NULL); + + if (!aDictionaryList->AppendElement(dictName)) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); + return NS_ERROR_OUT_OF_MEMORY; + } + } + + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); + } return NS_OK; } NS_IMETHODIMP mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary) { nsXPIDLString dictname; @@ -351,54 +359,97 @@ mozSpellChecker::GetCurrentDictionary(ns mSpellCheckingEngine->GetDictionary(getter_Copies(dictname)); aDictionary = dictname; return NS_OK; } NS_IMETHODIMP mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary) { - nsresult rv; - nsCString *contractId; + mSpellCheckingEngine = nsnull; if (aDictionary.IsEmpty()) { - mCurrentEngineContractId = nsnull; - mSpellCheckingEngine = nsnull; return NS_OK; } - if (!mDictionariesMap.Get(aDictionary, &contractId)){ - NS_WARNING("Dictionary not found"); - return NS_ERROR_NOT_AVAILABLE; - } + nsresult rv; + nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines; + rv = GetEngineList(&spellCheckingEngines); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { + nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i]; - if (!mCurrentEngineContractId || !mCurrentEngineContractId->Equals(*contractId)){ - mSpellCheckingEngine = do_GetService(contractId->get(), &rv); - if (NS_FAILED(rv)) - return rv; + rv = engine->SetDictionary(PromiseFlatString(aDictionary).get()); + if (NS_SUCCEEDED(rv)) { + mSpellCheckingEngine = engine; - mCurrentEngineContractId = contractId; + nsCOMPtr<mozIPersonalDictionary> personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); + mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get()); + + return NS_OK; + } } - nsresult res; - res = mSpellCheckingEngine->SetDictionary(PromiseFlatString(aDictionary).get()); - if(NS_FAILED(res)){ - NS_WARNING("Dictionary load failed"); - return res; + // We could not find any engine with the requested dictionary + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +mozSpellChecker::CheckCurrentDictionary() +{ + // Check if the current engine has any dictionaries available. If not, + // the last dictionary has just been uninstalled, and we need to stop using + // the engine. + if (mSpellCheckingEngine) { + nsXPIDLString dictname; + + mSpellCheckingEngine->GetDictionary(getter_Copies(dictname)); + + // We still have a dictionary, so keep using that. + if (!dictname.IsEmpty()) { + return NS_OK; + } + + // Our current dictionary has gone, so we cannot use the engine anymore. + mSpellCheckingEngine = nsnull; } - mSpellCheckingEngine->SetPersonalDictionary(mPersonalDictionary); + // We have no current engine. Pick one. + nsresult rv; + nsCOMArray<mozISpellCheckingEngine> spellCheckingEngines; + rv = GetEngineList(&spellCheckingEngines); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < spellCheckingEngines.Count(); i++) { + nsCOMPtr<mozISpellCheckingEngine> engine = spellCheckingEngines[i]; + + nsXPIDLString dictname; + + engine->GetDictionary(getter_Copies(dictname)); + + if (!dictname.IsEmpty()) { + mSpellCheckingEngine = engine; - nsXPIDLString language; - - nsCOMPtr<mozISpellI18NManager> serv(do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &res)); - if(serv && NS_SUCCEEDED(res)){ - res = serv->GetUtil(language.get(),getter_AddRefs(mConverter)); + nsCOMPtr<mozIPersonalDictionary> personalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1"); + mSpellCheckingEngine->SetPersonalDictionary(personalDictionary.get()); + + nsXPIDLString language; + nsresult rv; + nsCOMPtr<mozISpellI18NManager> serv = do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &rv); + if(serv && NS_SUCCEEDED(rv)) { + serv->GetUtil(language.get(), getter_AddRefs(mConverter)); + } + + return NS_OK; + } } - return res; + + // There are no dictionaries available + return NS_OK; } nsresult mozSpellChecker::SetupDoc(PRInt32 *outBlockOffset) { nsresult rv; nsITextServicesDocument::TSDBlockSelectionStatus blockStatus; @@ -472,21 +523,20 @@ mozSpellChecker::GetCurrentBlockIndex(ns } while (NS_SUCCEEDED(result) && !isDone); *outBlockIndex = blockIndex; return result; } nsresult -mozSpellChecker::InitSpellCheckDictionaryMap() +mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellCheckingEngines) { nsresult rv; PRBool hasMoreEngines; - nsTArray<nsCString> contractIds; nsCOMPtr<nsICategoryManager> catMgr = do_GetService(NS_CATEGORYMANAGER_CONTRACTID); if (!catMgr) return NS_ERROR_NULL_POINTER; nsCOMPtr<nsISimpleEnumerator> catEntries; // Get contract IDs of registrated external spell-check engines and @@ -503,57 +553,29 @@ mozSpellChecker::InitSpellCheckDictionar if (NS_FAILED(rv)) return rv; nsCString contractId; rv = entry->GetData(contractId); if (NS_FAILED(rv)) return rv; - contractIds.AppendElement(contractId); - } - - contractIds.AppendElement(NS_LITERAL_CSTRING(DEFAULT_SPELL_CHECKER)); - - // Retrieve dictionaries from all available spellcheckers and - // fill mDictionariesMap hash (only the first dictionary with the - // each name is used). - for (PRUint32 i=0;i < contractIds.Length();i++){ - PRUint32 count,k; - PRUnichar **words; - - const nsCString& contractId = contractIds[i]; - // Try to load spellchecker engine. Ignore errors silently // except for the last one (HunSpell). nsCOMPtr<mozISpellCheckingEngine> engine = do_GetService(contractId.get(), &rv); - if (NS_FAILED(rv)){ - // Fail if not succeeded to load HunSpell. Ignore errors - // for external spellcheck engines. - if (i==contractIds.Length()-1){ - return rv; - } - - continue; + if (NS_SUCCEEDED(rv)) { + aSpellCheckingEngines->AppendObject(engine); } - - engine->GetDictionaryList(&words,&count); - for(k=0;k<count;k++){ - nsAutoString dictName; + } - dictName.Assign(words[k]); - - nsCString dictCName = NS_ConvertUTF16toUTF8(dictName); - - // Skip duplicate dictionaries. Only take the first one - // for each name. - if (mDictionariesMap.Get(dictName, NULL)) - continue; - - mDictionariesMap.Put(dictName, new nsCString(contractId)); - } - - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, words); + // Try to load HunSpell spellchecker engine. + nsCOMPtr<mozISpellCheckingEngine> engine = + do_GetService(DEFAULT_SPELL_CHECKER, &rv); + if (NS_FAILED(rv)) { + // Fail if not succeeded to load HunSpell. Ignore errors + // for external spellcheck engines. + return rv; } + aSpellCheckingEngines->AppendObject(engine); return NS_OK; }
--- a/extensions/spellcheck/src/mozSpellChecker.h +++ b/extensions/spellcheck/src/mozSpellChecker.h @@ -15,16 +15,17 @@ * The Original Code is Mozilla Spellchecker Component. * * The Initial Developer of the Original Code is * David Einstein. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): David Einstein Deinst@world.std.com + * Jesper Kristensen <mail@jesperkristensen.dk> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -34,16 +35,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef mozSpellChecker_h__ #define mozSpellChecker_h__ #include "nsCOMPtr.h" +#include "nsCOMArray.h" #include "nsISpellChecker.h" #include "nsString.h" #include "nsITextServicesDocument.h" #include "mozIPersonalDictionary.h" #include "mozISpellCheckingEngine.h" #include "nsClassHashtable.h" #include "nsVoidArray.h" #include "nsTArray.h" @@ -70,29 +72,25 @@ public: NS_IMETHOD AddWordToPersonalDictionary(const nsAString &aWord); NS_IMETHOD RemoveWordFromPersonalDictionary(const nsAString &aWord); NS_IMETHOD GetPersonalDictionary(nsTArray<nsString> *aWordList); NS_IMETHOD GetDictionaryList(nsTArray<nsString> *aDictionaryList); NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary); NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary); + NS_IMETHOD CheckCurrentDictionary(); protected: nsCOMPtr<mozISpellI18NUtil> mConverter; nsCOMPtr<nsITextServicesDocument> mTsDoc; nsCOMPtr<mozIPersonalDictionary> mPersonalDictionary; - // Hastable maps directory name to the spellchecker contract ID - nsClassHashtable<nsStringHashKey, nsCString> mDictionariesMap; - - nsString mDictionaryName; - nsCString *mCurrentEngineContractId; nsCOMPtr<mozISpellCheckingEngine> mSpellCheckingEngine; PRBool mFromStart; nsresult SetupDoc(PRInt32 *outBlockOffset); nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, PRInt32 *outBlockIndex); - nsresult InitSpellCheckDictionaryMap(); + nsresult GetEngineList(nsCOMArray<mozISpellCheckingEngine> *aDictionaryList); }; #endif // mozSpellChecker_h__
--- a/extensions/spellcheck/src/mozSpellCheckerFactory.cpp +++ b/extensions/spellcheck/src/mozSpellCheckerFactory.cpp @@ -54,49 +54,17 @@ 0x8227F019, 0xAFC7, 0x461e, 0x9fe5d975, 0x9bd, 0x44aa, \ { 0xa0, 0x1a, 0x66, 0x40, 0x2e, 0xa2, 0x86, 0x57} } NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozHunspell, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(mozHunspellDirProvider) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozSpellChecker, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(mozPersonalDictionary, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(mozSpellI18NManager) - -// This special constructor for the inline spell checker asks the inline -// spell checker if we can create spell checking objects at all (ie, if there -// are any dictionaries loaded) before trying to create one. The static -// CanEnableInlineSpellChecking caches the value so this will be faster (we -// have to run this code for every edit box we create, as well as for every -// right click in those edit boxes). -static nsresult -mozInlineSpellCheckerConstructor(nsISupports *aOuter, REFNSIID aIID, - void **aResult) -{ - if (! mozInlineSpellChecker::CanEnableInlineSpellChecking()) - return NS_ERROR_FAILURE; - - nsresult rv; - - *aResult = NULL; - if (NULL != aOuter) { - rv = NS_ERROR_NO_AGGREGATION; - return rv; - } - - mozInlineSpellChecker* inst = new mozInlineSpellChecker(); - if (NULL == inst) { - rv = NS_ERROR_OUT_OF_MEMORY; - return rv; - } - NS_ADDREF(inst); - rv = inst->QueryInterface(aIID, aResult); - NS_RELEASE(inst); - - return rv; -} +NS_GENERIC_FACTORY_CONSTRUCTOR(mozInlineSpellChecker) NS_DEFINE_NAMED_CID(MOZ_HUNSPELL_CID); NS_DEFINE_NAMED_CID(HUNSPELLDIRPROVIDER_CID); NS_DEFINE_NAMED_CID(NS_SPELLCHECKER_CID); NS_DEFINE_NAMED_CID(MOZ_PERSONALDICTIONARY_CID); NS_DEFINE_NAMED_CID(MOZ_SPELLI18NMANAGER_CID); NS_DEFINE_NAMED_CID(MOZ_INLINESPELLCHECKER_CID);
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/Makefile.in @@ -0,0 +1,48 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = extensions/spellcheck/tests + +include $(DEPTH)/config/autoconf.mk + +DIRS = chrome + +include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/Makefile.in @@ -0,0 +1,54 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = extensions/spellcheck/tests/chrome + +include $(DEPTH)/config/autoconf.mk + +DIRS = base map + +include $(topsrcdir)/config/rules.mk + +_TEST_FILES = test_add_remove_dictionaries.xul \ + $(NULL) + +libs:: $(_TEST_FILES) + $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/base/Makefile.in @@ -0,0 +1,52 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = extensions/spellcheck/tests/chrome/base + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_TEST_FILES = base_utf.dic \ + base_utf.aff \ + $(NULL) + +libs:: $(_TEST_FILES) + $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/base/base_utf.aff @@ -0,0 +1,198 @@ +# OpenOffice.org’s en_US.aff file +# with Unicode apostrophe: ’ + +SET UTF-8 +TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ' + +MAXNGRAMSUGS 1 +WORDCHARS .'’ + +PFX A Y 1 +PFX A 0 re . + +PFX I Y 1 +PFX I 0 in . + +PFX U Y 1 +PFX U 0 un . + +PFX C Y 1 +PFX C 0 de . + +PFX E Y 1 +PFX E 0 dis . + +PFX F Y 1 +PFX F 0 con . + +PFX K Y 1 +PFX K 0 pro . + +SFX V N 2 +SFX V e ive e +SFX V 0 ive [^e] + +SFX N Y 3 +SFX N e ion e +SFX N y ication y +SFX N 0 en [^ey] + +SFX X Y 3 +SFX X e ions e +SFX X y ications y +SFX X 0 ens [^ey] + +SFX H N 2 +SFX H y ieth y +SFX H 0 th [^y] + +SFX Y Y 1 +SFX Y 0 ly . + +SFX G Y 2 +SFX G e ing e +SFX G 0 ing [^e] + +SFX J Y 2 +SFX J e ings e +SFX J 0 ings [^e] + +SFX D Y 4 +SFX D 0 d e +SFX D y ied [^aeiou]y +SFX D 0 ed [^ey] +SFX D 0 ed [aeiou]y + +SFX T N 4 +SFX T 0 st e +SFX T y iest [^aeiou]y +SFX T 0 est [aeiou]y +SFX T 0 est [^ey] + +SFX R Y 4 +SFX R 0 r e +SFX R y ier [^aeiou]y +SFX R 0 er [aeiou]y +SFX R 0 er [^ey] + +SFX Z Y 4 +SFX Z 0 rs e +SFX Z y iers [^aeiou]y +SFX Z 0 ers [aeiou]y +SFX Z 0 ers [^ey] + +SFX S Y 4 +SFX S y ies [^aeiou]y +SFX S 0 s [aeiou]y +SFX S 0 es [sxzh] +SFX S 0 s [^sxzhy] + +SFX P Y 3 +SFX P y iness [^aeiou]y +SFX P 0 ness [aeiou]y +SFX P 0 ness [^y] + +SFX M Y 1 +SFX M 0 's . + +SFX B Y 3 +SFX B 0 able [^aeiou] +SFX B 0 able ee +SFX B e able [^aeiou]e + +SFX L Y 1 +SFX L 0 ment . + +REP 88 +REP a ei +REP ei a +REP a ey +REP ey a +REP ai ie +REP ie ai +REP are air +REP are ear +REP are eir +REP air are +REP air ere +REP ere air +REP ere ear +REP ere eir +REP ear are +REP ear air +REP ear ere +REP eir are +REP eir ere +REP ch te +REP te ch +REP ch ti +REP ti ch +REP ch tu +REP tu ch +REP ch s +REP s ch +REP ch k +REP k ch +REP f ph +REP ph f +REP gh f +REP f gh +REP i igh +REP igh i +REP i uy +REP uy i +REP i ee +REP ee i +REP j di +REP di j +REP j gg +REP gg j +REP j ge +REP ge j +REP s ti +REP ti s +REP s ci +REP ci s +REP k cc +REP cc k +REP k qu +REP qu k +REP kw qu +REP o eau +REP eau o +REP o ew +REP ew o +REP oo ew +REP ew oo +REP ew ui +REP ui ew +REP oo ui +REP ui oo +REP ew u +REP u ew +REP oo u +REP u oo +REP u oe +REP oe u +REP u ieu +REP ieu u +REP ue ew +REP ew ue +REP uff ough +REP oo ieu +REP ieu oo +REP ier ear +REP ear ier +REP ear air +REP air ear +REP w qu +REP qu w +REP z ss +REP ss z +REP shun tion +REP shun sion +REP shun cion +McDonalds’sá/w +McDonald’sszá/g3) st:McDonaldâs po:noun_prs is:TRANS +McDonald’sszal/g4) st:McDonaldâs po:noun_prs is:INSTR +McDonald’ssal/w
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/base/base_utf.dic @@ -0,0 +1,29 @@ +28 +created/U +create/XKVNGADS +imply/GNSDX +natural/PUY +like/USPBY +convey/BDGS +look/GZRDS +text +hello +said +sawyer +NASA +rotten +day +tomorrow +seven +FAQ/SM +can’t +doesn’t +etc +won’t +lip +text +horrifying +speech +suggest +uncreate/V +Hunspell
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/map/Makefile.in @@ -0,0 +1,52 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = extensions/spellcheck/tests/chrome/map + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_TEST_FILES = maputf.dic \ + maputf.aff \ + $(NULL) + +libs:: $(_TEST_FILES) + $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/map/maputf.aff @@ -0,0 +1,11 @@ +# With MAP suggestion, Hunspell can add missing accents to a word. + +SET UTF-8 + +# switch off ngram suggestion for testing +MAXNGRAMSUGS 0 + +MAP 3 +MAP uúü +MAP öóo +MAP ß(ss)
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/map/maputf.dic @@ -0,0 +1,4 @@ +3 +Frühstück +tükörfúró +groß
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul @@ -0,0 +1,110 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window title="Add and remove dictionaries test" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="RunTest();"> + <title>Add and remove dictionaries test</title> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <script type="application/javascript"> + <![CDATA[ +SimpleTest.waitForExplicitFinish(); + +function getMisspelledWords(editor) { + return editor.selectionController.getSelection(Components.interfaces.nsISelectionController.SELECTION_SPELLCHECK).toString(); +} + +function getDictionaryList(editor) { + var spellchecker = editor.getInlineSpellChecker(true).spellChecker; + var o1 = {}; + spellchecker.GetDictionaryList(o1, {}); + return o1.value; +} + +function getCurrentDictionary(editor) { + var spellchecker = editor.getInlineSpellChecker(true).spellChecker; + return spellchecker.GetCurrentDictionary(); +} + +function setCurrentDictionary(editor, dictionary) { + var spellchecker = editor.getInlineSpellChecker(true).spellChecker; + spellchecker.SetCurrentDictionary(dictionary); +} + +function RunTest() { + var editor = document.getElementById('textbox').editor; + + var dir = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsIFile); + dir.append("chrome"); + dir.append("extensions"); + dir.append("spellcheck"); + dir.append("tests"); + dir.append("chrome"); + + var hunspell = Components + .classes["@mozilla.org/spellchecker/engine;1"] + .getService(Components.interfaces.mozISpellCheckingEngine); + + // install base dictionary + var base = dir.clone(); + base.append("base"); + ok(base.exists()); + hunspell.addDirectory(base); + + // install map dictionary + var map = dir.clone(); + map.append("map"); + ok(map.exists()); + hunspell.addDirectory(map); + + // test that base and map dictionaries are available + var dicts = getDictionaryList(editor); + isnot(dicts.indexOf("base_utf"), -1, "base is available"); + isnot(dicts.indexOf("maputf"), -1, "map is available"); + + // select base dictionary + setCurrentDictionary(editor, "base_utf"); + + SimpleTest.executeSoon(function() { + // test that base dictionary is in use + is(getMisspelledWords(editor), "Frühstück" + "qwertyu", "base misspellings"); + is(getCurrentDictionary(editor), "base_utf", "current dictionary"); + + // select map dictionary + setCurrentDictionary(editor, "maputf"); + + SimpleTest.executeSoon(function() { + // test that map dictionary is in use + is(getMisspelledWords(editor), "created" + "imply" + "tomorrow" + "qwertyu", "map misspellings"); + is(getCurrentDictionary(editor), "maputf", "current dictionary"); + + // uninstall map dictionary + hunspell.removeDirectory(map); + + SimpleTest.executeSoon(function() { + // test that map dictionary is not in use + isnot(getMisspelledWords(editor), "created" + "imply" + "tomorrow" + "qwertyu", "map misspellings"); + isnot(getCurrentDictionary(editor), "maputf", "current dictionary"); + + // test that base dictionary is available and map dictionary is unavailable + var dicts = getDictionaryList(editor); + isnot(dicts.indexOf("base_utf"), -1, "base is available"); + is(dicts.indexOf("maputf"), -1, "map is unavailable"); + + // uninstall base dictionary + hunspell.removeDirectory(base); + + SimpleTest.finish(); + }); + }); + }); +} + ]]> + </script> + <textbox id="textbox" spellcheck="true" value="created imply Frühstück tomorrow qwertyu"/> +</window>