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/. */
6 #ifndef mozilla_mozInlineSpellChecker_h
7 #define mozilla_mozInlineSpellChecker_h
9 #include "nsCycleCollectionParticipant.h"
10 #include "nsIDOMEventListener.h"
11 #include "nsIEditorSpellCheck.h"
12 #include "nsIInlineSpellChecker.h"
13 #include "mozInlineSpellWordUtil.h"
14 #include "mozilla/Result.h"
16 #include "nsWeakReference.h"
18 class InitEditorSpellCheckCallback
;
19 class mozInlineSpellChecker
;
20 class mozInlineSpellResume
;
21 class UpdateCurrentDictionaryCallback
;
24 class EditorSpellCheck
;
26 enum class EditSubAction
: int32_t;
31 } // namespace mozilla
33 class mozInlineSpellStatus
{
35 static mozilla::Result
<mozilla::UniquePtr
<mozInlineSpellStatus
>, nsresult
>
36 CreateForEditorChange(mozInlineSpellChecker
& aSpellChecker
,
37 mozilla::EditSubAction aEditSubAction
,
38 nsINode
* aAnchorNode
, uint32_t aAnchorOffset
,
39 nsINode
* aPreviousNode
, uint32_t aPreviousOffset
,
40 nsINode
* aStartNode
, uint32_t aStartOffset
,
41 nsINode
* aEndNode
, uint32_t aEndOffset
);
43 static mozilla::Result
<mozilla::UniquePtr
<mozInlineSpellStatus
>, nsresult
>
44 CreateForNavigation(mozInlineSpellChecker
& aSpellChecker
, bool aForceCheck
,
45 int32_t aNewPositionOffset
, nsINode
* aOldAnchorNode
,
46 uint32_t aOldAnchorOffset
, nsINode
* aNewAnchorNode
,
47 uint32_t aNewAnchorOffset
, bool* aContinue
);
49 static mozilla::UniquePtr
<mozInlineSpellStatus
> CreateForSelection(
50 mozInlineSpellChecker
& aSpellChecker
);
52 static mozilla::UniquePtr
<mozInlineSpellStatus
> CreateForRange(
53 mozInlineSpellChecker
& aSpellChecker
, nsRange
* aRange
);
55 nsresult
FinishInitOnEvent(mozInlineSpellWordUtil
& aWordUtil
);
57 // Return true if we plan to spell-check everything
58 bool IsFullSpellCheck() const { return mOp
== eOpChange
&& !mRange
; }
60 const RefPtr
<mozInlineSpellChecker
> mSpellChecker
;
63 eOpChange
, // for SpellCheckAfterEditorChange except
65 eOpChangeDelete
, // for SpellCheckAfterEditorChange with
67 eOpNavigation
, // for HandleNavigationEvent
68 eOpSelection
, // re-check all misspelled words
73 Operation
GetOperation() const { return mOp
; }
75 // Used for events where we have already computed the range to use. It can
76 // also be nullptr in these cases where we need to check the entire range.
77 RefPtr
<nsRange
> mRange
;
79 // See `mCreatedRange`.
80 const nsRange
* GetCreatedRange() const { return mCreatedRange
; }
82 // See `mNoCheckRange`.
83 const nsRange
* GetNoCheckRange() const { return mNoCheckRange
; }
86 // @param aSpellChecker must be non-nullptr.
87 explicit mozInlineSpellStatus(mozInlineSpellChecker
* aSpellChecker
);
89 // For resuming a previously started check.
93 // If we happen to know something was inserted, this is that range.
94 // Can be nullptr (this only allows an optimization, so not setting doesn't
96 RefPtr
<nsRange
> mCreatedRange
;
98 // Contains the range computed for the current word. Can be nullptr.
99 RefPtr
<nsRange
> mNoCheckRange
;
101 // Indicates the position of the cursor for the event (so we can compute
102 // mNoCheckRange). It can be nullptr if we don't care about the cursor
103 // position (such as for the intial check of everything).
105 // For mOp == eOpNavigation, this is the NEW position of the cursor
106 RefPtr
<nsRange
> mAnchorRange
;
109 // The following members are only for navigation events and are only
110 // stored for FinishNavigationEvent to initialize the other members.
113 // this is the OLD position of the cursor
114 RefPtr
<nsRange
> mOldNavigationAnchorRange
;
116 // Set when we should force checking the current word. See
117 // mozInlineSpellChecker::HandleNavigationEvent for a description of why we
119 bool mForceNavigationWordCheck
;
121 // Contains the offset passed in to HandleNavigationEvent
122 int32_t mNewNavigationPositionOffset
;
124 nsresult
FinishNavigationEvent(mozInlineSpellWordUtil
& aWordUtil
);
126 nsresult
FillNoCheckRangeFromAnchor(mozInlineSpellWordUtil
& aWordUtil
);
128 mozilla::dom::Document
* GetDocument() const;
129 already_AddRefed
<nsRange
> PositionToCollapsedRange(nsINode
* aNode
,
133 class mozInlineSpellChecker final
: public nsIInlineSpellChecker
,
134 public nsIDOMEventListener
,
135 public nsSupportsWeakReference
{
137 friend class mozInlineSpellStatus
;
138 friend class InitEditorSpellCheckCallback
;
139 friend class UpdateCurrentDictionaryCallback
;
140 friend class AutoChangeNumPendingSpellChecks
;
142 // Access with CanEnableInlineSpellChecking
143 enum SpellCheckingState
{
144 SpellCheck_Uninitialized
= -1,
145 SpellCheck_NotAvailable
= 0,
146 SpellCheck_Available
= 1
148 static SpellCheckingState gCanEnableSpellChecking
;
150 RefPtr
<mozilla::TextEditor
> mTextEditor
;
151 RefPtr
<mozilla::EditorSpellCheck
> mSpellCheck
;
152 RefPtr
<mozilla::EditorSpellCheck
> mPendingSpellCheck
;
154 int32_t mNumWordsInSpellSelection
;
155 int32_t mMaxNumWordsInSpellSelection
;
157 // we need to keep track of the current text position in the document
158 // so we can spell check the old word when the user clicks around the
160 nsCOMPtr
<nsINode
> mCurrentSelectionAnchorNode
;
161 uint32_t mCurrentSelectionOffset
;
163 // Tracks the number of pending spell checks *and* async operations that may
164 // lead to spell checks, like updating the current dictionary. This is
165 // necessary so that observers can know when to wait for spell check to
167 int32_t mNumPendingSpellChecks
;
169 // The number of calls to UpdateCurrentDictionary that haven't finished yet.
170 int32_t mNumPendingUpdateCurrentDictionary
;
172 // This number is incremented each time the spell checker is disabled so that
173 // pending scheduled spell checks and UpdateCurrentDictionary calls can be
174 // ignored when they finish.
175 uint32_t mDisabledAsyncToken
;
177 // When mPendingSpellCheck is non-null, this is the callback passed when
178 // it was initialized.
179 RefPtr
<InitEditorSpellCheckCallback
> mPendingInitEditorSpellCheckCallback
;
181 // Set when we have spellchecked after the last edit operation. See the
182 // commment at the top of the .cpp file for more info.
183 bool mNeedsCheckAfterNavigation
;
185 // Set when we have a pending mozInlineSpellResume which will check
186 // the whole document.
187 bool mFullSpellCheckScheduled
;
189 // Set to true when this instance needs to listen to edit actions of
191 bool mIsListeningToEditSubActions
;
194 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
195 NS_DECL_NSIINLINESPELLCHECKER
196 NS_DECL_NSIDOMEVENTLISTENER
197 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker
,
200 mozilla::EditorSpellCheck
* GetEditorSpellCheck();
202 // See `mDisabledAsyncToken`.
203 uint32_t GetDisabledAsyncToken() const { return mDisabledAsyncToken
; }
205 // returns true if there are any spell checking dictionaries available
206 static bool CanEnableInlineSpellChecking();
207 // update the cached value whenever the list of available dictionaries changes
208 static void UpdateCanEnableInlineSpellChecking();
210 nsresult
OnBlur(mozilla::dom::Event
* aEvent
);
211 nsresult
OnMouseClick(mozilla::dom::Event
* aMouseEvent
);
212 nsresult
OnKeyPress(mozilla::dom::Event
* aKeyEvent
);
214 mozInlineSpellChecker();
216 // spell checks all of the words between two nodes
217 nsresult
SpellCheckBetweenNodes(nsINode
* aStartNode
, int32_t aStartOffset
,
218 nsINode
* aEndNode
, int32_t aEndOffset
);
220 // examines the dom node in question and returns true if the inline spell
221 // checker should skip the node (i.e. the text is inside of a block quote
222 // or an e-mail signature...)
223 bool ShouldSpellCheckNode(mozilla::TextEditor
* aTextEditor
,
224 nsINode
* aNode
) const;
226 // spell check the text contained within aRange, potentially scheduling
227 // another check in the future if the time threshold is reached
228 nsresult
ScheduleSpellCheck(
229 mozilla::UniquePtr
<mozInlineSpellStatus
>&& aStatus
);
231 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
232 DoSpellCheckSelection(mozInlineSpellWordUtil
& aWordUtil
,
233 mozilla::dom::Selection
* aSpellCheckSelection
);
234 nsresult
DoSpellCheck(mozInlineSpellWordUtil
& aWordUtil
,
235 mozilla::dom::Selection
* aSpellCheckSelection
,
236 const mozilla::UniquePtr
<mozInlineSpellStatus
>& aStatus
,
237 bool* aDoneChecking
);
239 // helper routine to determine if a point is inside of the passed in
241 static nsresult
IsPointInSelection(mozilla::dom::Selection
& aSelection
,
242 nsINode
* aNode
, int32_t aOffset
,
245 nsresult
CleanupRangesInSelection(mozilla::dom::Selection
* aSelection
);
248 * @param aRange needs to be kept alive by the caller.
250 // TODO: annotate with `MOZ_CAN_RUN_SCRIPT` instead
251 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1620540).
252 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
253 RemoveRange(mozilla::dom::Selection
* aSpellCheckSelection
, nsRange
* aRange
);
254 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
255 AddRange(mozilla::dom::Selection
* aSpellCheckSelection
, nsRange
* aRange
);
256 bool IsSpellCheckSelectionFull() const {
257 return mNumWordsInSpellSelection
>= mMaxNumWordsInSpellSelection
;
260 nsresult
MakeSpellCheckRange(nsINode
* aStartNode
, int32_t aStartOffset
,
261 nsINode
* aEndNode
, int32_t aEndOffset
,
262 nsRange
** aRange
) const;
264 // DOM and editor event registration helper routines
265 nsresult
RegisterEventListeners();
266 nsresult
UnregisterEventListeners();
267 nsresult
HandleNavigationEvent(bool aForceWordSpellCheck
,
268 int32_t aNewPositionOffset
= 0);
270 already_AddRefed
<mozilla::dom::Selection
> GetSpellCheckSelection();
271 nsresult
SaveCurrentSelectionPosition();
273 nsresult
ResumeCheck(mozilla::UniquePtr
<mozInlineSpellStatus
>&& aStatus
);
275 // Those methods are called when mTextEditor splits a node or joins the
277 void DidSplitNode(nsINode
* aExistingRightNode
, nsINode
* aNewLeftNode
);
278 void DidJoinNodes(nsINode
& aRightNode
, nsINode
& aLeftNode
);
280 nsresult
SpellCheckAfterEditorChange(mozilla::EditSubAction aEditSubAction
,
281 mozilla::dom::Selection
& aSelection
,
282 nsINode
* aPreviousSelectedNode
,
283 uint32_t aPreviousSelectedOffset
,
285 uint32_t aStartOffset
, nsINode
* aEndNode
,
286 uint32_t aEndOffset
);
289 virtual ~mozInlineSpellChecker();
291 // called when async nsIEditorSpellCheck methods complete
292 nsresult
EditorSpellCheckInited();
293 nsresult
CurrentDictionaryUpdated();
295 // track the number of pending spell checks and async operations that may lead
296 // to spell checks, notifying observers accordingly
297 void ChangeNumPendingSpellChecks(int32_t aDelta
,
298 mozilla::TextEditor
* aTextEditor
= nullptr);
299 void NotifyObservers(const char* aTopic
, mozilla::TextEditor
* aTextEditor
);
301 void StartToListenToEditSubActions() { mIsListeningToEditSubActions
= true; }
302 void EndListeningToEditSubActions() { mIsListeningToEditSubActions
= false; }
304 void CheckCurrentWordsNoSuggest(mozilla::dom::Selection
* aSpellCheckSelection
,
305 nsTArray
<nsString
>&& aWords
,
306 nsTArray
<NodeOffsetRange
>&& aRanges
);
309 #endif // #ifndef mozilla_mozInlineSpellChecker_h