Bug 1700051: part 19) `const`-qualify and rename `mozInlineSpellChecker::SpellCheckSe...
[gecko.git] / extensions / spellcheck / src / mozInlineSpellChecker.h
blob5a6f252db6152110b876cb22e2fa2c0df7ed081b
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"
15 #include "nsRange.h"
16 #include "nsWeakReference.h"
18 class InitEditorSpellCheckCallback;
19 class mozInlineSpellChecker;
20 class mozInlineSpellResume;
21 class UpdateCurrentDictionaryCallback;
23 namespace mozilla {
24 class EditorSpellCheck;
25 class TextEditor;
26 enum class EditSubAction : int32_t;
28 namespace dom {
29 class Event;
30 } // namespace dom
31 } // namespace mozilla
33 class mozInlineSpellStatus {
34 public:
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;
62 enum Operation {
63 eOpChange, // for SpellCheckAfterEditorChange except
64 // deleteSelection
65 eOpChangeDelete, // for SpellCheckAfterEditorChange with
66 // deleteSelection
67 eOpNavigation, // for HandleNavigationEvent
68 eOpSelection, // re-check all misspelled words
69 eOpResume
72 // See `mOp`.
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; }
85 private:
86 // @param aSpellChecker must be non-nullptr.
87 explicit mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker);
89 // For resuming a previously started check.
90 Operation mOp;
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
95 // hurt)
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;
108 // -----
109 // The following members are only for navigation events and are only
110 // stored for FinishNavigationEvent to initialize the other members.
111 // -----
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
118 // have this.
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,
130 uint32_t aOffset);
133 class mozInlineSpellChecker final : public nsIInlineSpellChecker,
134 public nsIDOMEventListener,
135 public nsSupportsWeakReference {
136 private:
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
159 // document.
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
166 // complete.
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
190 // the editor.
191 bool mIsListeningToEditSubActions;
193 public:
194 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
195 NS_DECL_NSIINLINESPELLCHECKER
196 NS_DECL_NSIDOMEVENTLISTENER
197 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker,
198 nsIDOMEventListener)
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
240 // selection.
241 static nsresult IsPointInSelection(mozilla::dom::Selection& aSelection,
242 nsINode* aNode, int32_t aOffset,
243 nsRange** aRange);
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
276 // given nodes.
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,
284 nsINode* aStartNode,
285 uint32_t aStartOffset, nsINode* aEndNode,
286 uint32_t aEndOffset);
288 protected:
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