Mozilla/mozilla/extensions/spellcheck/src/mozInlineSpellChecker.cpp
bienvenu%nventure.com 5441688ad8 fix crash entering smiley<space><space> on unix, sr=mscott, 328606
git-svn-id: svn://10.0.0.236/trunk@191400 18797224-902f-48f8-a5cc-f745e15eee43
2006-02-28 15:53:07 +00:00

1361 lines
44 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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 Real-time Spellchecking
*
* The Initial Developer of the Original Code is Mozdev Group, Inc.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Neil Deakin (neil@mozdevgroup.com)
* Scott MacGregor (mscott@mozilla.org)
*
* 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
* 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 ***** */
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsArray.h"
#include "nsIServiceManager.h"
#include "nsIEnumerator.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "mozISpellI18NManager.h"
#include "mozInlineSpellChecker.h"
#include "nsIDOMKeyEvent.h"
#include "nsIPlaintextEditor.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDocumentRange.h"
#include "nsIDOMNode.h"
#include "nsIDOMNSUIEvent.h"
#include "nsIDOMElement.h"
#include "nsIDOMText.h"
#include "nsIDOMNodeList.h"
#include "nsISelection.h"
#include "nsISelectionController.h"
#include "nsITextServicesDocument.h"
#include "nsITextServicesFilter.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMDocumentTraversal.h"
#include "nsIDOMNodeFilter.h"
#include "nsIDOMEventReceiver.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "nsCRT.h"
#include "cattable.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
static const char kMaxSpellCheckSelectionSize[] = "extensions.spellcheck.inline.max-misspellings";
NS_INTERFACE_MAP_BEGIN(mozInlineSpellChecker)
NS_INTERFACE_MAP_ENTRY(nsIInlineSpellChecker)
NS_INTERFACE_MAP_ENTRY(nsIEditActionListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMKeyListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(mozInlineSpellChecker)
NS_IMPL_RELEASE(mozInlineSpellChecker)
mozInlineSpellChecker::SpellCheckingState
mozInlineSpellChecker::gCanEnableSpellChecking =
mozInlineSpellChecker::SpellCheck_Uninitialized;
mozInlineSpellChecker::mozInlineSpellChecker():mNumWordsInSpellSelection(0), mMaxNumWordsInSpellSelection(250)
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs)
prefs->GetIntPref(kMaxSpellCheckSelectionSize, &mMaxNumWordsInSpellSelection);
}
mozInlineSpellChecker::~mozInlineSpellChecker()
{
}
NS_IMETHODIMP
mozInlineSpellChecker::GetSpellChecker(nsIEditorSpellCheck **aSpellCheck)
{
*aSpellCheck = mSpellCheck;
NS_IF_ADDREF(*aSpellCheck);
return NS_OK;
}
NS_IMETHODIMP
mozInlineSpellChecker::Init(nsIEditor *aEditor)
{
mEditor = do_GetWeakReference(aEditor);
return NS_OK;
}
nsresult mozInlineSpellChecker::Cleanup()
{
return UnregisterEventListeners();
}
// mozInlineSpellChecker::CanEnableInlineSpellChecking
//
// 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.
PRBool mozInlineSpellChecker::CanEnableInlineSpellChecking() // static
{
nsresult rv;
if (gCanEnableSpellChecking == SpellCheck_Uninitialized) {
gCanEnableSpellChecking = SpellCheck_NotAvailable;
nsCOMPtr<nsIEditorSpellCheck> spellchecker =
do_CreateInstance("@mozilla.org/editor/editorspellchecker;1", &rv);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
PRBool canSpellCheck = PR_FALSE;
rv = spellchecker->CanSpellCheck(&canSpellCheck);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
if (canSpellCheck)
gCanEnableSpellChecking = SpellCheck_Available;
}
return (gCanEnableSpellChecking == SpellCheck_Available);
}
// the inline spell checker listens to mouse events and keyboard navigation events
nsresult mozInlineSpellChecker::RegisterEventListeners()
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
editor->AddEditActionListener(this);
nsCOMPtr<nsIDOMDocument> doc;
nsresult rv = editor->GetDocument(getter_AddRefs(doc));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMEventReceiver> eventReceiver = do_QueryInterface(doc, &rv);
NS_ENSURE_SUCCESS(rv, rv);
eventReceiver->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMMouseListener*, this), NS_GET_IID(nsIDOMMouseListener));
eventReceiver->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMKeyListener*, this), NS_GET_IID(nsIDOMKeyListener));
return NS_OK;
}
nsresult mozInlineSpellChecker::UnregisterEventListeners()
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
editor->RemoveEditActionListener(this);
nsCOMPtr<nsIDOMDocument> doc;
editor->GetDocument(getter_AddRefs(doc));
NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIDOMEventReceiver> eventReceiver = do_QueryInterface(doc);
NS_ENSURE_TRUE(eventReceiver, NS_ERROR_NULL_POINTER);
eventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMMouseListener*, this), NS_GET_IID(nsIDOMMouseListener));
eventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMKeyListener*, this), NS_GET_IID(nsIDOMKeyListener));
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::GetEnableRealTimeSpell(PRBool * aEnabled)
{
NS_ENSURE_ARG_POINTER(aEnabled);
*aEnabled = mSpellCheck != nsnull;
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::SetEnableRealTimeSpell(PRBool aEnabled)
{
nsresult res = NS_OK;
if (aEnabled) {
if (!mSpellCheck) {
nsCOMPtr<nsIEditorSpellCheck> spellchecker = do_CreateInstance("@mozilla.org/editor/editorspellchecker;1", &res);
if (NS_SUCCEEDED(res) && spellchecker)
{
nsCOMPtr<nsITextServicesFilter> filter = do_CreateInstance("@mozilla.org/editor/txtsrvfiltermail;1", &res);
spellchecker->SetFilter(filter);
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
res = spellchecker->InitSpellChecker(editor, PR_FALSE);
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsITextServicesDocument> tsDoc = do_CreateInstance("@mozilla.org/textservices/textservicesdocument;1", &res);
NS_ENSURE_SUCCESS(res, res);
res = tsDoc->SetFilter(filter);
NS_ENSURE_SUCCESS(res, res);
res = tsDoc->InitWithEditor(editor);
NS_ENSURE_SUCCESS(res, res);
mTextServicesDocument = tsDoc;
mSpellCheck = spellchecker;
// spell checking is enabled, register our event listeners to track navigation
RegisterEventListeners();
}
}
// spellcheck the current content
res = SpellCheckRange(nsnull);
}
else
{
nsCOMPtr<nsISelection> spellCheckSelection;
res = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(res, res);
spellCheckSelection->RemoveAllRanges();
mNumWordsInSpellSelection = 0;
UnregisterEventListeners();
mSpellCheck = nsnull;
}
return res;
}
NS_IMETHODIMP mozInlineSpellChecker::SpellCheckAfterEditorChange(PRInt32 action, nsISelection *aSelection,
nsIDOMNode *previousSelectedNode, PRInt32 previousSelectedOffset,
nsIDOMNode *aStartNode, PRInt32 aStartOffset, nsIDOMNode *aEndNode,
PRInt32 aEndOffset)
{
NS_ENSURE_ARG_POINTER(aSelection);
if (!mSpellCheck)
return NS_OK; // disabling spell checking is not an error
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
if (!editor)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMNode> anchorNode;
nsresult res = aSelection->GetAnchorNode(getter_AddRefs(anchorNode));
NS_ENSURE_SUCCESS(res, res);
PRInt32 anchorOffset;
res = aSelection->GetAnchorOffset(&anchorOffset);
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsISelection> spellCheckSelection;
res = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(res, res);
CleanupRangesInSelection(spellCheckSelection);
switch (action)
{
case kOpInsertBreak:
{
// if we just inserted a line break, we need to finish spell checking the last word on the previous
// line since in this case the line break is really a end of word terminator...
res = AdjustSpellHighlighting(previousSelectedNode, previousSelectedOffset, spellCheckSelection, PR_FALSE);
break;
}
case kOpMakeList:
case kOpIndent:
case kOpOutdent:
case kOpAlign:
case kOpRemoveList:
case kOpMakeDefListItem:
res = SpellCheckBetweenNodes(aStartNode, aStartOffset, aEndNode, aEndOffset, spellCheckSelection);
break;
case kOpInsertText:
case kOpInsertIMEText:
{
PRInt32 offset = previousSelectedOffset;
if (anchorNode == previousSelectedNode)
{
if (offset == anchorOffset)
offset--;
res = AdjustSpellHighlighting(anchorNode, offset, spellCheckSelection, PR_FALSE);
}
else
res = SpellCheckBetweenNodes(aStartNode, aStartOffset, anchorNode, offset, spellCheckSelection);
break;
}
case kOpHTMLPaste:
// spell check the inserted nodes caused by the space.
res = SpellCheckBetweenNodes(aStartNode, aStartOffset, aEndNode, aEndOffset, spellCheckSelection);
break;
case kOpDeleteSelection:
{
// this is what happens when the delete or backspace key is pressed
PRInt32 offset = (anchorOffset > 0) ? (anchorOffset - 1) : anchorOffset;
res = AdjustSpellHighlighting(anchorNode, offset, spellCheckSelection, PR_TRUE);
break;
}
case kOpInsertQuotation:
// spell check the text at the previous caret position, but no need to
// check the quoted text itself.
res = AdjustSpellHighlighting(previousSelectedNode, previousSelectedOffset - 1,
spellCheckSelection, PR_FALSE);
break;
case kOpRemoveTextProperty:
case kOpResetTextProperties:
res = SpellCheckSelection(aSelection);
break;
case kOpMakeBasicBlock:
// this doesn't adjust the selection, nor add or insert any text, so
// no words need to be spell-checked
res = SpellCheckBetweenNodes(aStartNode,aStartOffset,aEndNode,aEndOffset, spellCheckSelection);
break;
case kOpLoadHTML:
{
// spell check the entire document
SpellCheckRange(nsnull);
break;
}
case kOpInsertElement:
PRBool iscollapsed;
aSelection->GetIsCollapsed(&iscollapsed);
if (iscollapsed)
res = AdjustSpellHighlighting(anchorNode, anchorOffset, spellCheckSelection, PR_FALSE);
else
res = SpellCheckSelection(aSelection);
break;
case kOpSetTextProperty:
res = SpellCheckSelection(aSelection);
break;
case kOpUndo:
case kOpRedo:
// XXX handling these cases is quite hard -- this isn't quite right
if (anchorNode == previousSelectedNode)
res = SpellCheckBetweenNodes(anchorNode, PR_MIN(anchorOffset, previousSelectedOffset),
anchorNode, PR_MAX(anchorOffset, previousSelectedOffset), spellCheckSelection);
else
{
nsCOMPtr<nsIDOMElement> rootElem;
res = editor->GetRootElement(getter_AddRefs(rootElem));
NS_ENSURE_SUCCESS(res, res);
res = SpellCheckBetweenNodes(rootElem, 0, rootElem, -1, spellCheckSelection);
}
break;
}
// remember the current cursor position after every change..
SaveCurrentSelectionPosition();
return res;
}
// supply a NULL range and this will check the entire editor
NS_IMETHODIMP mozInlineSpellChecker::SpellCheckRange(nsIDOMRange *aRange)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsISelection> spellCheckSelection;
nsresult rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(rv, rv);
CleanupRangesInSelection(spellCheckSelection);
if(aRange) {
// use the given range
rv = SpellCheckRange(aRange,spellCheckSelection);
} else
{
// use full range: SpellCheckBetweenNodes will do the somewhat complicated
// task of creating a range over the element we give it and call
// SpellCheckRange(range,selection) for us
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
if (!editor)
return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMElement> rootElem;
rv = editor->GetRootElement(getter_AddRefs(rootElem));
NS_ENSURE_SUCCESS(rv, rv);
rv = SpellCheckBetweenNodes(rootElem, 0, rootElem, -1, spellCheckSelection);
}
return rv;
}
NS_IMETHODIMP mozInlineSpellChecker::GetMispelledWord(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMRange **newword)
{
nsCOMPtr<nsISelection> spellCheckSelection;
nsresult res = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(res, res);
return IsPointInSelection(spellCheckSelection, aNode, aOffset, newword);
}
NS_IMETHODIMP mozInlineSpellChecker::ReplaceWord(nsIDOMNode *aNode, PRInt32 aOffset, const nsAString &newword)
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(newword.Length() != 0, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMRange> range;
nsresult res = GetMispelledWord(aNode, aOffset, getter_AddRefs(range));
NS_ENSURE_SUCCESS(res, res);
if (range)
{
range->DeleteContents();
nsCOMPtr<nsISelection> selection;
res = editor->GetSelection(getter_AddRefs(selection));
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMNode> container;
res = range->GetStartContainer(getter_AddRefs(container));
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMCharacterData> chars = do_QueryInterface(container);
if (chars)
{
PRInt32 offset;
res = range->GetStartOffset(&offset);
NS_ENSURE_SUCCESS(res, res);
chars->InsertData(offset,newword);
selection->Collapse(container, offset + newword.Length());
}
else
{
nsCOMPtr<nsIDOMDocument> doc;
res = editor->GetDocument(getter_AddRefs(doc));
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMText> newtext;
res = doc->CreateTextNode(newword,getter_AddRefs(newtext));
NS_ENSURE_SUCCESS(res, res);
res = range->InsertNode(newtext);
NS_ENSURE_SUCCESS(res, res);
selection->Collapse(newtext, newword.Length());
}
}
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::AddWordToDictionary(const nsAString &word)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
nsAutoString wordstr(word);
nsresult res = mSpellCheck->AddWordToDictionary(wordstr.get());
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsISelection> spellCheckSelection;
nsresult rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(rv, rv);
return SpellCheckSelection(spellCheckSelection);
}
NS_IMETHODIMP mozInlineSpellChecker::IgnoreWord(const nsAString &word)
{
NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED);
nsAutoString wordstr(word);
nsresult res = mSpellCheck->IgnoreWordAllOccurrences(wordstr.get());
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsISelection> spellCheckSelection;
nsresult rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(rv, rv);
return SpellCheckSelection(spellCheckSelection);
}
NS_IMETHODIMP mozInlineSpellChecker::IgnoreWords(const PRUnichar **aWordsToIgnore, PRUint32 aCount)
{
// add each word to the ignore list and then recheck the document
for (PRUint32 index = 0; index < aCount; index++)
mSpellCheck->IgnoreWordAllOccurrences(aWordsToIgnore[index]);
nsCOMPtr<nsISelection> spellCheckSelection;
nsresult rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(rv, rv);
return SpellCheckSelection(spellCheckSelection);
}
// When the user ignores a word or adds a word to the dictionary, we used
// to re check the entire document, starting at the root element and walking through all the nodes.
// Optimization: The only words in the document that would change as a result of these actions
// are words that are already in the spell check selection (i.e. words previsouly marked as misspelled).
// Spell check the spell check selection instead of the entire document for ignore word and add word
nsresult mozInlineSpellChecker::SpellCheckSelection(nsISelection * aSelection)
{
NS_ENSURE_ARG_POINTER(aSelection);
nsCOMPtr<nsISelection> spellCheckSelection;
nsresult rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(rv, rv);
// if we are going to be checking the spell check selection, then
// clear out mNumWordsInSpellSelection since we'll be rebuilding the ranges.
if (aSelection == spellCheckSelection.get())
mNumWordsInSpellSelection = 0;
// Optimize for the case where aSelection is in fact the spell check selection.
// Since we could be modifying the ranges for the spellCheckSelection while looping
// on the spell check selection, keep a separate array of range elements inside the selection
PRInt32 count;
nsCOMPtr <nsIMutableArray> ranges = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
aSelection->GetRangeCount(&count);
PRInt32 index;
for (index = 0; index < count; index++)
{
nsCOMPtr<nsIDOMRange> checkRange;
aSelection->GetRangeAt(index, getter_AddRefs(checkRange));
if (checkRange)
ranges->AppendElement(checkRange, PR_FALSE);
}
// now loop over these ranges and spell check each range
nsCOMPtr<nsIDOMNode> startNode;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 startOffset, endOffset;
nsCOMPtr<nsIDOMRange> checkRange;
for (index = 0; index < count; index++)
{
checkRange = do_QueryElementAt(ranges, index);
if (checkRange)
{
checkRange->GetStartContainer(getter_AddRefs(startNode));
checkRange->GetEndContainer(getter_AddRefs(endNode));
checkRange->GetStartOffset(&startOffset);
checkRange->GetEndOffset(&endOffset);
rv |= SpellCheckBetweenNodes(startNode, startOffset, endNode, endOffset, spellCheckSelection);
}
}
return rv;
}
NS_IMETHODIMP mozInlineSpellChecker::WillCreateNode(const nsAString & aTag, nsIDOMNode *aParent, PRInt32 aPosition)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidCreateNode(const nsAString & aTag, nsIDOMNode *aNode, nsIDOMNode *aParent,
PRInt32 aPosition, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent,
PRInt32 aPosition)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent,
PRInt32 aPosition, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::WillDeleteNode(nsIDOMNode *aChild)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset,
nsIDOMNode *aNewLeftNode, nsresult aResult)
{
return SpellCheckBetweenNodes(aNewLeftNode, 0, aNewLeftNode, 0, NULL);
}
NS_IMETHODIMP mozInlineSpellChecker::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode,
nsIDOMNode *aParent, nsresult aResult)
{
return SpellCheckBetweenNodes(aRightNode, 0, aRightNode, 0, NULL);
}
NS_IMETHODIMP mozInlineSpellChecker::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAString & aString)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset,
const nsAString & aString, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::WillDeleteSelection(nsISelection *aSelection)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidDeleteSelection(nsISelection *aSelection)
{
return NS_OK;
}
nsresult
mozInlineSpellChecker::SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
PRInt32 aStartOffset,
nsIDOMNode *aEndNode,
PRInt32 aEndOffset,
nsISelection *aSpellCheckSelection)
{
nsresult res;
nsCOMPtr<nsISelection> spellCheckSelection = aSpellCheckSelection;
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
if (!spellCheckSelection)
{
res = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
NS_ENSURE_SUCCESS(res, res);
}
nsCOMPtr<nsIDOMDocument> doc;
res = editor->GetDocument(getter_AddRefs(doc));
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsIDOMDocumentRange> docrange = do_QueryInterface(doc);
NS_ENSURE_TRUE(docrange, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMRange> range;
res = docrange->CreateRange(getter_AddRefs(range));
NS_ENSURE_SUCCESS(res, res);
if (aEndOffset == -1)
{
nsCOMPtr<nsIDOMNodeList> childNodes;
res = aEndNode->GetChildNodes(getter_AddRefs(childNodes));
NS_ENSURE_SUCCESS(res, res);
PRUint32 childCount;
res = childNodes->GetLength(&childCount);
NS_ENSURE_SUCCESS(res, res);
aEndOffset = childCount;
}
range->SetStart(aStartNode,aStartOffset);
if (aEndOffset)
range->SetEnd(aEndNode, aEndOffset);
else
range->SetEndAfter(aEndNode);
// HACK: try to avoid an assertion later on in the code when we iterate
// over a range with only one character in it. if the range is really only one
// character, bail out early..
nsCOMPtr<nsIDOMNode> startNode;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 startOffset;
PRInt32 endOffset;
range->GetStartContainer(getter_AddRefs(startNode));
range->GetStartOffset(&startOffset);
range->GetEndContainer(getter_AddRefs(endNode));
range->GetEndOffset(&endOffset);
if (startNode == endNode && startOffset == endOffset)
return NS_OK; // don't call adjust spell highlighting for a single character
return SpellCheckRange(range,spellCheckSelection);
}
static inline PRBool IsNonwordChar(PRUnichar chr)
{
// a non-word character is one that can end a word, such as whitespace or
// most punctuation.
// mscott: We probably need to modify this to make it work for non ascii
// based languages... but then again our spell checker doesn't support
// multi byte languages anyway.
// jshin: one way to make the word boundary checker more generic is to
// use 'word breaker(s)' in intl.
// Need to fix callers (of IsNonwordChar) to pass PRUint32
return ((chr != '\'') && (GetCat(PRUint32(chr)) != 5));
}
// There are certain conditions when we don't want to spell check a node. In particular
// quotations, moz signatures, etc. This routine returns false for these cases.
nsresult mozInlineSpellChecker::SkipSpellCheckForNode(nsIDOMNode *aNode, PRBool *checkSpelling)
{
*checkSpelling = PR_TRUE;
NS_ENSURE_ARG_POINTER(aNode);
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
PRUint32 flags;
editor->GetFlags(&flags);
if (flags & nsIPlaintextEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> parent;
aNode->GetParentNode(getter_AddRefs(parent));
while (parent)
{
nsCOMPtr<nsIDOMElement> parentElement = do_QueryInterface(parent);
if (!parentElement)
break;
nsAutoString parentTagName;
parentElement->GetTagName(parentTagName);
if (parentTagName.Equals(NS_LITERAL_STRING("blockquote"), nsCaseInsensitiveStringComparator()))
{
*checkSpelling = PR_FALSE;
break;
}
else if (parentTagName.Equals(NS_LITERAL_STRING("pre"), nsCaseInsensitiveStringComparator()))
{
nsAutoString classname;
parentElement->GetAttribute(NS_LITERAL_STRING("class"),classname);
if (classname.Equals(NS_LITERAL_STRING("moz-signature")))
*checkSpelling = PR_FALSE;
}
nsCOMPtr<nsIDOMNode> nextParent;
parent->GetParentNode(getter_AddRefs(nextParent));
parent = nextParent;
}
}
return NS_OK;
}
nsresult mozInlineSpellChecker::EnsureConverter()
{
nsresult res = NS_OK;
if (!mConverter)
{
nsCOMPtr<mozISpellI18NManager> manager(do_GetService("@mozilla.org/spellchecker/i18nmanager;1", &res));
if (manager && NS_SUCCEEDED(res))
{
nsXPIDLString language;
res = manager->GetUtil(language.get(), getter_AddRefs(mConverter));
}
}
return res;
}
// takes a point in a text node and generates a range for the word containing that point. Note:
// aWordRange will be NULL if we don't have a word.
nsresult mozInlineSpellChecker::GenerateRangeForSurroundingWord(nsIDOMNode * aNode, PRInt32 aOffset, nsIDOMRange ** aWordRange)
{
NS_ENSURE_ARG_POINTER(aNode);
PRUint32 textLength = 0;
const PRUnichar *textChars = nsnull;
nsAutoString text;
nsresult rv = aNode->GetNodeValue(text);
NS_ENSURE_SUCCESS(rv, rv);
rv = EnsureConverter();
NS_ENSURE_SUCCESS(rv, rv);
textChars = text.get();
textLength = text.Length();
if (aOffset == -1 || aOffset >= textLength)
aOffset = textLength - 1;
if (aOffset < 0)
aOffset = 0;
PRInt32 currentOffset = aOffset;
if (currentOffset && textChars[currentOffset] == ' ')
currentOffset--;
// back up our offset into the text node until we hit the beginning of the text OR
// we find white space (NOT punctuation)
while (currentOffset && textChars[currentOffset] != ' ')
currentOffset--;
PRInt32 lastWordBeginPosition = -1;
PRInt32 lastWordEndPos = -1;
PRInt32 begin;
PRInt32 end;
do
{
rv = mConverter->FindNextWord(textChars, textLength, currentOffset, &begin, &end);
if (NS_SUCCEEDED(rv) && (begin != -1))
{
lastWordBeginPosition = begin;
lastWordEndPos = end;
currentOffset = lastWordEndPos;
}
} while (begin != -1 && currentOffset < aOffset);
// only return the range if our original point in the string is inside the word
if (aOffset >= lastWordBeginPosition && aOffset <= lastWordEndPos)
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIDOMDocument> doc;
rv = editor->GetDocument(getter_AddRefs(doc));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMDocumentRange> docrange = do_QueryInterface(doc);
nsCOMPtr<nsIDOMRange> range;
rv = docrange->CreateRange(aWordRange);
NS_ENSURE_SUCCESS(rv, rv);
(*aWordRange)->SetStart(aNode, lastWordBeginPosition);
(*aWordRange)->SetEnd(aNode, lastWordEndPos);
}
else
*aWordRange = nsnull;
return NS_OK;
}
nsresult
mozInlineSpellChecker::AdjustSpellHighlighting(nsIDOMNode *aNode,
PRInt32 aOffset,
nsISelection *aSpellCheckSelection,
PRBool isDeletion)
{
NS_ENSURE_TRUE(aNode, NS_OK);
nsCOMPtr<nsIDOMNode> currentNode = aNode;
nsresult rv = NS_OK;
do
{
PRUint16 nodeType;
rv = currentNode->GetNodeType(&nodeType);
NS_ENSURE_SUCCESS(rv, rv);
if (nodeType == nsIDOMNode::TEXT_NODE)
break;
nsCOMPtr<nsIDOMNodeList> childNodes;
rv = currentNode->GetChildNodes(getter_AddRefs(childNodes));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> child;
rv = childNodes->Item(aOffset, getter_AddRefs(child));
NS_ENSURE_SUCCESS(rv, rv);
currentNode = child;
aOffset = 0;
} while (currentNode);
// make sure we found a text node
if (!currentNode)
return NS_OK;
// HACK: special case deletion when we had to walk up the dom tree to find the previous text node.
// Reset the offset to be the end of this previous text node instead of the beginning
if (isDeletion && currentNode != aNode)
{
nsAutoString text;
rv = currentNode->GetNodeValue(text);
if (text.Length())
aOffset = text.Length() - 1;
}
nsCOMPtr<nsIDOMRange> wordRange;
rv = GenerateRangeForSurroundingWord(currentNode, aOffset, getter_AddRefs(wordRange));
// if we don't have a word range to examine, then bail out early.
if (!wordRange || aOffset < 0)
return NS_OK;
// if the user just started typing inside of an existing word, remove that word from the spell check
// selection list (if it was even there)
if (!EndOfAWord(currentNode, aOffset) && !isDeletion)
RemoveCurrentWordFromSpellSelection(aSpellCheckSelection, wordRange);
// if we aren't at the end of a word then don't kick off inline spell checking
// if we are processing a delete character, proceed so we can clear the spell check underline of the
// current word.
// Extra logic: if we are handling a deletion and the current character (after the delete) is a end of word
// delimter (like a space) then don't clear the underline selection of the word. This fixes the case where
// you type: "helllo world". Put the cursor to the left of 'w' and hit back space, as long as there is white
// space between the cursor and the last character in the previous word (helllo) we shouldn't clear the highlight
// selection on helllo.
if ((!EndOfAWord(currentNode, aOffset) && !isDeletion) || (isDeletion && EndOfAWord(currentNode, aOffset)))
return NS_OK;
PRBool checkSpelling;
SkipSpellCheckForNode(currentNode, &checkSpelling);
if (!checkSpelling)
return NS_OK;
rv = RemoveCurrentWordFromSpellSelection(aSpellCheckSelection, wordRange);
// for the case of deletion, if the cursor is inside of a spell check selection (i.e.
// the user typed some text then a word terminator and hit backspace to fix the spelling)
// we should clear the red underline from that word since they are now editing it again.
if (isDeletion)
return NS_OK;
// expand the current selection point to a word range
PRBool isMisspelled = PR_FALSE;
nsAutoString word;
wordRange->ToString(word);
// empty words should be skipped
if (word.IsEmpty())
return NS_OK;
rv = mSpellCheck->CheckCurrentWordNoSuggest(word.get(),&isMisspelled);
NS_ENSURE_SUCCESS(rv, rv);
if (isMisspelled)
AddRange(aSpellCheckSelection, wordRange);
return NS_OK;
}
// SpellCheckRange --> Looks for words that span aRange and spell checks each individual word.
nsresult mozInlineSpellChecker::SpellCheckRange(nsIDOMRange *aRange, nsISelection *aSpellCheckSelection)
{
nsCOMPtr<nsIDOMRange> selectionRange;
nsresult res = aRange->CloneRange(getter_AddRefs(selectionRange));
NS_ENSURE_SUCCESS(res, res);
PRBool iscollapsed;
res = aRange->GetCollapsed(&iscollapsed);
NS_ENSURE_SUCCESS(res, res);
res = mTextServicesDocument->SetExtent(selectionRange);
NS_ENSURE_SUCCESS(res, res);
res = EnsureConverter();
NS_ENSURE_SUCCESS(res, res);
PRBool done, isMisspelled;
PRInt32 begin, end, startOffset, endOffset;
PRUint32 selOffset = 0;
nsCOMPtr<nsIDOMNode> startNode;
nsCOMPtr<nsIDOMNode> endNode;
while (!SpellCheckSelectionIsFull() && NS_SUCCEEDED(mTextServicesDocument->IsDone(&done)) && !done)
{
nsAutoString textblock;
res = mTextServicesDocument->GetCurrentTextBlock(&textblock);
NS_ENSURE_SUCCESS(res, res);
do
{
const PRUnichar *textChars = textblock.get();
PRInt32 textLength = textblock.Length();
res = mConverter->FindNextWord(textChars, textLength, selOffset, &begin, &end);
if (NS_SUCCEEDED(res) && (begin != -1))
{
const nsAString &word = Substring(textblock, begin, end - begin);
res = mSpellCheck->CheckCurrentWordNoSuggest(PromiseFlatString(word).get(), &isMisspelled);
nsCOMPtr<nsIDOMRange> wordrange;
res = mTextServicesDocument->GetDOMRangeFor(begin, end - begin, getter_AddRefs(wordrange));
wordrange->GetStartContainer(getter_AddRefs(startNode));
wordrange->GetEndContainer(getter_AddRefs(endNode));
wordrange->GetStartOffset(&startOffset);
wordrange->GetEndOffset(&endOffset);
PRBool checkSpelling;
SkipSpellCheckForNode(startNode, &checkSpelling);
if (!checkSpelling)
break;
nsCOMPtr<nsIDOMRange> currentRange;
IsPointInSelection(aSpellCheckSelection, startNode, startOffset,
getter_AddRefs(currentRange));
if (!currentRange)
IsPointInSelection(aSpellCheckSelection, endNode, endOffset - 1, getter_AddRefs(currentRange));
if (currentRange) // remove the old range first
RemoveRange(aSpellCheckSelection, currentRange);
if (isMisspelled)
AddRange(aSpellCheckSelection, wordrange);
}
selOffset = end;
} while (end != -1 && !SpellCheckSelectionIsFull());
mTextServicesDocument->NextBlock();
selOffset = 0;
}
return NS_OK;
}
PRBool mozInlineSpellChecker::EndOfAWord(nsIDOMNode *aNode, PRInt32 aOffset)
{
PRBool endOfWord = PR_FALSE;
nsAutoString text;
PRUint16 nodeType;
if (aNode)
{
nsresult res = aNode->GetNodeType(&nodeType);
if (NS_SUCCEEDED(res))
{
if (nodeType == nsIDOMNode::TEXT_NODE) {
res = aNode->GetNodeValue(text);
if (NS_SUCCEEDED(res) && IsNonwordChar(text[aOffset]))
endOfWord = PR_TRUE;
}
}
}
return endOfWord;
}
nsresult
mozInlineSpellChecker::IsPointInSelection(nsISelection *aSelection,
nsIDOMNode *aNode,
PRInt32 aOffset,
nsIDOMRange **aRange)
{
*aRange = NULL;
NS_ENSURE_ARG_POINTER(aNode);
NS_ENSURE_ARG_POINTER(aSelection);
PRInt32 count;
aSelection->GetRangeCount(&count);
PRInt32 t;
for (t=0; t<count; t++)
{
nsCOMPtr<nsIDOMRange> checkRange;
aSelection->GetRangeAt(t,getter_AddRefs(checkRange));
nsCOMPtr<nsIDOMNSRange> nsCheckRange = do_QueryInterface(checkRange);
PRInt32 start, end;
checkRange->GetStartOffset(&start);
checkRange->GetEndOffset(&end);
PRBool isInRange;
nsCheckRange->IsPointInRange(aNode,aOffset,&isInRange);
if (isInRange)
{
*aRange = checkRange;
NS_ADDREF(*aRange);
break;
}
}
return NS_OK;
}
nsresult
mozInlineSpellChecker::CleanupRangesInSelection(nsISelection *aSelection)
{
// integrity check - remove ranges that have collapsed to nothing. This
// can happen if the node containing a highlighted word was removed.
NS_ENSURE_ARG_POINTER(aSelection);
PRInt32 count;
aSelection->GetRangeCount(&count);
for (PRInt32 index = 0; index < count; index++)
{
nsCOMPtr<nsIDOMRange> checkRange;
aSelection->GetRangeAt(index, getter_AddRefs(checkRange));
if (checkRange)
{
PRBool collapsed;
checkRange->GetCollapsed(&collapsed);
if (collapsed)
{
RemoveRange(aSelection, checkRange);
index--;
count--;
}
}
}
return NS_OK;
}
// For performance reasons, we have an upper bound on the number of word ranges
// in the spell check selection. When removing a range from the selection, we need to decrement
// mNumWordsInSpellSelection
nsresult mozInlineSpellChecker::RemoveRange(nsISelection *aSpellCheckSelection, nsIDOMRange * aRange)
{
NS_ENSURE_ARG_POINTER(aSpellCheckSelection);
NS_ENSURE_ARG_POINTER(aRange);
nsresult rv = aSpellCheckSelection->RemoveRange(aRange);
if (NS_SUCCEEDED(rv) && mNumWordsInSpellSelection)
mNumWordsInSpellSelection--;
return rv;
}
// For performance reasons, we have an upper bound on the number of word ranges we'll add
// to the spell check selection. Once we reach that upper bound, stop adding the ranges
nsresult mozInlineSpellChecker::AddRange(nsISelection *aSpellCheckSelection, nsIDOMRange * aRange)
{
NS_ENSURE_ARG_POINTER(aSpellCheckSelection);
NS_ENSURE_ARG_POINTER(aRange);
nsresult rv = NS_OK;
if (!SpellCheckSelectionIsFull())
{
rv = aSpellCheckSelection->AddRange(aRange);
if (NS_SUCCEEDED(rv))
mNumWordsInSpellSelection++;
}
return rv;
}
// a helper routine that expands the current position in the selection to the surrounding word
// and then removes it from the spell checker selection
nsresult mozInlineSpellChecker::RemoveCurrentWordFromSpellSelection(nsISelection *aSpellCheckSelection, nsIDOMRange * aWordRange)
{
NS_ENSURE_ARG_POINTER(aSpellCheckSelection);
NS_ENSURE_ARG_POINTER(aWordRange);
nsCOMPtr<nsIDOMNode> startNode;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 startOffset, endOffset;
aWordRange->GetStartContainer(getter_AddRefs(startNode));
aWordRange->GetEndContainer(getter_AddRefs(endNode));
aWordRange->GetStartOffset(&startOffset);
aWordRange->GetEndOffset(&endOffset);
nsCOMPtr<nsIDOMRange> currentRange;
IsPointInSelection(aSpellCheckSelection, startNode, startOffset, getter_AddRefs(currentRange));
if (currentRange)
RemoveRange(aSpellCheckSelection, currentRange);
IsPointInSelection(aSpellCheckSelection, endNode, endOffset - 1, getter_AddRefs(currentRange));
if (currentRange)
RemoveRange(aSpellCheckSelection, currentRange);
return NS_OK;
}
nsresult mozInlineSpellChecker::GetSpellCheckSelection(nsISelection ** aSpellCheckSelection)
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsISelectionController> selcon;
nsresult rv = editor->GetSelectionController(getter_AddRefs(selcon));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> spellCheckSelection;
return selcon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK, aSpellCheckSelection);
}
nsresult mozInlineSpellChecker::SaveCurrentSelectionPosition()
{
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ENSURE_TRUE(editor, NS_OK);
// figure out the old cursor position based on the current selection
nsCOMPtr<nsISelection> selection;
nsresult rv = editor->GetSelection(getter_AddRefs(selection));
NS_ENSURE_SUCCESS(rv, rv);
rv = selection->GetFocusNode(getter_AddRefs(mCurrentSelectionAnchorNode));
NS_ENSURE_SUCCESS(rv, rv);
selection->GetFocusOffset(&mCurrentSelectionOffset);
return NS_OK;
}
// HandleNavigationEvent: acts upon mouse clicks and keyboard navigation changes,
// spell checking the previous word if the new navigation location movees us to another word.
// This is complicated by the fact that our mouse events are happening after selection has been
// changed to account for the mouse click. But keyboard events are happening before the caret
// selection has changed. Working around this by letting keyboard events setting forceWordSpellCheck
// to true. aNewPositionOffset also trys to work around this for the DOM_VK_RIGHT and DOM_VK_LEFT cases.
nsresult mozInlineSpellChecker::HandleNavigationEvent(nsIDOMEvent * aEvent, PRBool aForceWordSpellCheck, PRInt32 aNewPositionOffset)
{
// get the current selection and compare it to the new selection.
nsresult rv;
nsCOMPtr<nsIDOMNode> currentAnchorNode = mCurrentSelectionAnchorNode;
PRInt32 currentAnchorOffset = mCurrentSelectionOffset;
// now remember the new focus position resulting from the event
SaveCurrentSelectionPosition();
NS_ENSURE_TRUE(currentAnchorNode, NS_OK);
// expand the old selection into a range for the nearest word boundary
nsCOMPtr<nsIDOMRange> currentWordRange;
GenerateRangeForSurroundingWord(currentAnchorNode, currentAnchorOffset, getter_AddRefs(currentWordRange));
if (!currentWordRange)
return NS_OK;
nsAutoString word;
currentWordRange->ToString(word);
nsCOMPtr<nsIDOMNSRange> currentWordNSRange = do_QueryInterface(currentWordRange, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isInRange;
rv = currentWordNSRange->IsPointInRange(mCurrentSelectionAnchorNode, mCurrentSelectionOffset + aNewPositionOffset, &isInRange);
NS_ENSURE_SUCCESS(rv, rv);
if (!isInRange || aForceWordSpellCheck) // selection is moving to a new word, spell check the current word
{
nsCOMPtr<nsISelection> spellCheckSelection;
GetSpellCheckSelection(getter_AddRefs(spellCheckSelection));
SpellCheckRange(currentWordRange, spellCheckSelection);
}
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::HandleEvent(nsIDOMEvent* aEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::MouseClick(nsIDOMEvent *aMouseEvent)
{
// ignore any errors from HandleNavigationEvent as we don't want to prevent
// anyone else from seeing this event.
HandleNavigationEvent(aMouseEvent, PR_FALSE);
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::MouseDown(nsIDOMEvent* aMouseEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::MouseUp(nsIDOMEvent* aMouseEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::MouseDblClick(nsIDOMEvent* aMouseEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::MouseOver(nsIDOMEvent* aMouseEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::MouseOut(nsIDOMEvent* aMouseEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::KeyDown(nsIDOMEvent* aKeyEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::KeyUp(nsIDOMEvent* aKeyEvent)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::KeyPress(nsIDOMEvent* aKeyEvent)
{
nsCOMPtr<nsIDOMKeyEvent>keyEvent = do_QueryInterface(aKeyEvent);
NS_ENSURE_TRUE(keyEvent, NS_OK);
PRUint32 keyCode;
keyEvent->GetKeyCode(&keyCode);
// we only care about navigation keys that moved selection
switch (keyCode)
{
case nsIDOMKeyEvent::DOM_VK_RIGHT:
case nsIDOMKeyEvent::DOM_VK_LEFT:
HandleNavigationEvent(aKeyEvent, PR_FALSE, keyCode == nsIDOMKeyEvent::DOM_VK_RIGHT ? 1 : -1);
break;
case nsIDOMKeyEvent::DOM_VK_UP:
case nsIDOMKeyEvent::DOM_VK_DOWN:
case nsIDOMKeyEvent::DOM_VK_HOME:
case nsIDOMKeyEvent::DOM_VK_END:
case nsIDOMKeyEvent::DOM_VK_PAGE_UP:
case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN:
HandleNavigationEvent(aKeyEvent, PR_TRUE /* force a spelling correction */);
break;
}
return NS_OK;
}