--- 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>