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