Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #ifndef __mozinlinespellchecker_h__
7 : #define __mozinlinespellchecker_h__
8 :
9 : #include "mozilla/EditorBase.h"
10 : #include "nsRange.h"
11 : #include "nsIEditorSpellCheck.h"
12 : #include "nsIEditActionListener.h"
13 : #include "nsIInlineSpellChecker.h"
14 : #include "nsIDOMTreeWalker.h"
15 : #include "nsWeakReference.h"
16 : #include "nsIDOMEventListener.h"
17 : #include "nsWeakReference.h"
18 : #include "mozISpellI18NUtil.h"
19 : #include "nsCycleCollectionParticipant.h"
20 :
21 : // X.h defines KeyPress
22 : #ifdef KeyPress
23 : #undef KeyPress
24 : #endif
25 :
26 : class mozInlineSpellWordUtil;
27 : class mozInlineSpellChecker;
28 : class mozInlineSpellResume;
29 : class InitEditorSpellCheckCallback;
30 : class UpdateCurrentDictionaryCallback;
31 : class mozInlineSpellResume;
32 :
33 0 : class mozInlineSpellStatus
34 : {
35 : public:
36 : explicit mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker);
37 :
38 : nsresult InitForEditorChange(EditAction aAction,
39 : nsIDOMNode* aAnchorNode, int32_t aAnchorOffset,
40 : nsIDOMNode* aPreviousNode, int32_t aPreviousOffset,
41 : nsIDOMNode* aStartNode, int32_t aStartOffset,
42 : nsIDOMNode* aEndNode, int32_t aEndOffset);
43 : nsresult InitForNavigation(bool aForceCheck, int32_t aNewPositionOffset,
44 : nsIDOMNode* aOldAnchorNode, int32_t aOldAnchorOffset,
45 : nsIDOMNode* aNewAnchorNode, int32_t aNewAnchorOffset,
46 : bool* aContinue);
47 : nsresult InitForSelection();
48 : nsresult InitForRange(nsRange* aRange);
49 :
50 : nsresult FinishInitOnEvent(mozInlineSpellWordUtil& aWordUtil);
51 :
52 : // Return true if we plan to spell-check everything
53 0 : bool IsFullSpellCheck() const {
54 0 : return mOp == eOpChange && !mRange;
55 : }
56 :
57 : RefPtr<mozInlineSpellChecker> mSpellChecker;
58 :
59 : // The total number of words checked in this sequence, using this tally tells
60 : // us when to stop. This count is preserved as we continue checking in new
61 : // messages.
62 : int32_t mWordCount;
63 :
64 : // what happened?
65 : enum Operation { eOpChange, // for SpellCheckAfterChange except deleteSelection
66 : eOpChangeDelete, // for SpellCheckAfterChange deleteSelection
67 : eOpNavigation, // for HandleNavigationEvent
68 : eOpSelection, // re-check all misspelled words
69 : eOpResume }; // for resuming a previously started check
70 : Operation mOp;
71 :
72 : // Used for events where we have already computed the range to use. It can
73 : // also be nullptr in these cases where we need to check the entire range.
74 : RefPtr<nsRange> mRange;
75 :
76 : // If we happen to know something was inserted, this is that range.
77 : // Can be nullptr (this only allows an optimization, so not setting doesn't hurt)
78 : RefPtr<nsRange> mCreatedRange;
79 :
80 : // Contains the range computed for the current word. Can be nullptr.
81 : RefPtr<nsRange> mNoCheckRange;
82 :
83 : // Indicates the position of the cursor for the event (so we can compute
84 : // mNoCheckRange). It can be nullptr if we don't care about the cursor position
85 : // (such as for the intial check of everything).
86 : //
87 : // For mOp == eOpNavigation, this is the NEW position of the cursor
88 : RefPtr<nsRange> mAnchorRange;
89 :
90 : // -----
91 : // The following members are only for navigation events and are only
92 : // stored for FinishNavigationEvent to initialize the other members.
93 : // -----
94 :
95 : // this is the OLD position of the cursor
96 : RefPtr<nsRange> mOldNavigationAnchorRange;
97 :
98 : // Set when we should force checking the current word. See
99 : // mozInlineSpellChecker::HandleNavigationEvent for a description of why we
100 : // have this.
101 : bool mForceNavigationWordCheck;
102 :
103 : // Contains the offset passed in to HandleNavigationEvent
104 : int32_t mNewNavigationPositionOffset;
105 :
106 : protected:
107 : nsresult FinishNavigationEvent(mozInlineSpellWordUtil& aWordUtil);
108 :
109 : nsresult FillNoCheckRangeFromAnchor(mozInlineSpellWordUtil& aWordUtil);
110 :
111 : nsresult GetDocument(nsIDOMDocument** aDocument);
112 : nsresult PositionToCollapsedRange(nsIDOMDocument* aDocument,
113 : nsIDOMNode* aNode, int32_t aOffset,
114 : nsRange** aRange);
115 : };
116 :
117 : class mozInlineSpellChecker final : public nsIInlineSpellChecker,
118 : public nsIEditActionListener,
119 : public nsIDOMEventListener,
120 : public nsSupportsWeakReference
121 : {
122 : private:
123 : friend class mozInlineSpellStatus;
124 : friend class InitEditorSpellCheckCallback;
125 : friend class UpdateCurrentDictionaryCallback;
126 : friend class AutoChangeNumPendingSpellChecks;
127 : friend class mozInlineSpellResume;
128 :
129 : // Access with CanEnableInlineSpellChecking
130 : enum SpellCheckingState { SpellCheck_Uninitialized = -1,
131 : SpellCheck_NotAvailable = 0,
132 : SpellCheck_Available = 1};
133 : static SpellCheckingState gCanEnableSpellChecking;
134 :
135 : nsWeakPtr mEditor;
136 : nsCOMPtr<nsIEditorSpellCheck> mSpellCheck;
137 : nsCOMPtr<nsIEditorSpellCheck> mPendingSpellCheck;
138 : nsCOMPtr<nsIDOMTreeWalker> mTreeWalker;
139 : nsCOMPtr<mozISpellI18NUtil> mConverter;
140 :
141 : int32_t mNumWordsInSpellSelection;
142 : int32_t mMaxNumWordsInSpellSelection;
143 :
144 : // How many misspellings we can add at once. This is often less than the max
145 : // total number of misspellings. When you have a large textarea prepopulated
146 : // with text with many misspellings, we can hit this limit. By making it
147 : // lower than the total number of misspelled words, new text typed by the
148 : // user can also have spellchecking in it.
149 : int32_t mMaxMisspellingsPerCheck;
150 :
151 : // we need to keep track of the current text position in the document
152 : // so we can spell check the old word when the user clicks around the document.
153 : nsCOMPtr<nsIDOMNode> mCurrentSelectionAnchorNode;
154 : int32_t mCurrentSelectionOffset;
155 :
156 : // Tracks the number of pending spell checks *and* async operations that may
157 : // lead to spell checks, like updating the current dictionary. This is
158 : // necessary so that observers can know when to wait for spell check to
159 : // complete.
160 : int32_t mNumPendingSpellChecks;
161 :
162 : // The number of calls to UpdateCurrentDictionary that haven't finished yet.
163 : int32_t mNumPendingUpdateCurrentDictionary;
164 :
165 : // This number is incremented each time the spell checker is disabled so that
166 : // pending scheduled spell checks and UpdateCurrentDictionary calls can be
167 : // ignored when they finish.
168 : uint32_t mDisabledAsyncToken;
169 :
170 : // When mPendingSpellCheck is non-null, this is the callback passed when
171 : // it was initialized.
172 : RefPtr<InitEditorSpellCheckCallback> mPendingInitEditorSpellCheckCallback;
173 :
174 : // Set when we have spellchecked after the last edit operation. See the
175 : // commment at the top of the .cpp file for more info.
176 : bool mNeedsCheckAfterNavigation;
177 :
178 : // Set when we have a pending mozInlineSpellResume which will check
179 : // the whole document.
180 : bool mFullSpellCheckScheduled;
181 :
182 : // Maintains state during the asynchronous UpdateCurrentDictionary call.
183 : nsString mPreviousDictionary;
184 :
185 : public:
186 :
187 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
188 : NS_DECL_NSIEDITACTIONLISTENER
189 : NS_DECL_NSIINLINESPELLCHECKER
190 : NS_DECL_NSIDOMEVENTLISTENER
191 0 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener)
192 :
193 : // returns true if there are any spell checking dictionaries available
194 : static bool CanEnableInlineSpellChecking();
195 : // update the cached value whenever the list of available dictionaries changes
196 : static void UpdateCanEnableInlineSpellChecking();
197 :
198 : nsresult Blur(nsIDOMEvent* aEvent);
199 : nsresult MouseClick(nsIDOMEvent* aMouseEvent);
200 : nsresult KeyPress(nsIDOMEvent* aKeyEvent);
201 :
202 : mozInlineSpellChecker();
203 :
204 : // spell checks all of the words between two nodes
205 : nsresult SpellCheckBetweenNodes(nsIDOMNode *aStartNode,
206 : int32_t aStartOffset,
207 : nsIDOMNode *aEndNode,
208 : int32_t aEndOffset);
209 :
210 : // examines the dom node in question and returns true if the inline spell
211 : // checker should skip the node (i.e. the text is inside of a block quote
212 : // or an e-mail signature...)
213 : bool ShouldSpellCheckNode(nsIEditor* aEditor, nsINode *aNode);
214 :
215 : nsresult SpellCheckAfterChange(nsIDOMNode* aCursorNode, int32_t aCursorOffset,
216 : nsIDOMNode* aPreviousNode, int32_t aPreviousOffset,
217 : nsISelection* aSpellCheckSelection);
218 :
219 : // spell check the text contained within aRange, potentially scheduling
220 : // another check in the future if the time threshold is reached
221 : nsresult ScheduleSpellCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
222 :
223 : nsresult DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
224 : mozilla::dom::Selection* aSpellCheckSelection);
225 : nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
226 : mozilla::dom::Selection *aSpellCheckSelection,
227 : const mozilla::UniquePtr<mozInlineSpellStatus>& aStatus,
228 : bool* aDoneChecking);
229 :
230 : // helper routine to determine if a point is inside of the passed in selection.
231 : nsresult IsPointInSelection(nsISelection *aSelection,
232 : nsIDOMNode *aNode,
233 : int32_t aOffset,
234 : nsIDOMRange **aRange);
235 :
236 : nsresult CleanupRangesInSelection(mozilla::dom::Selection *aSelection);
237 :
238 : nsresult RemoveRange(mozilla::dom::Selection *aSpellCheckSelection,
239 : nsRange *aRange);
240 : nsresult AddRange(nsISelection *aSpellCheckSelection, nsIDOMRange * aRange);
241 0 : bool SpellCheckSelectionIsFull() { return mNumWordsInSpellSelection >= mMaxNumWordsInSpellSelection; }
242 :
243 : nsresult MakeSpellCheckRange(nsIDOMNode* aStartNode, int32_t aStartOffset,
244 : nsIDOMNode* aEndNode, int32_t aEndOffset,
245 : nsRange** aRange);
246 :
247 : // DOM and editor event registration helper routines
248 : nsresult RegisterEventListeners();
249 : nsresult UnregisterEventListeners();
250 : nsresult HandleNavigationEvent(bool aForceWordSpellCheck, int32_t aNewPositionOffset = 0);
251 :
252 : nsresult GetSpellCheckSelection(nsISelection ** aSpellCheckSelection);
253 : nsresult SaveCurrentSelectionPosition();
254 :
255 : nsresult ResumeCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
256 :
257 : protected:
258 : virtual ~mozInlineSpellChecker();
259 :
260 : // called when async nsIEditorSpellCheck methods complete
261 : nsresult EditorSpellCheckInited();
262 : nsresult CurrentDictionaryUpdated();
263 :
264 : // track the number of pending spell checks and async operations that may lead
265 : // to spell checks, notifying observers accordingly
266 : void ChangeNumPendingSpellChecks(int32_t aDelta,
267 : nsIEditor* aEditor = nullptr);
268 : void NotifyObservers(const char* aTopic, nsIEditor* aEditor);
269 : };
270 :
271 : #endif /* __mozinlinespellchecker_h__ */
|