Bug 1700051: part 43) Const-qualify `mozInlineSpellStatus::PositionToCollapsedRange...
[gecko.git] / extensions / spellcheck / src / mozInlineSpellChecker.h
blob341e9577e8895a82ed27e4cbcf22e758cc92db28
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 // @param aOp see mOp.
88 // @param aForceNavigationWordCheck see mForceNavigationWordCheck.
89 // @param aNewNavigationPositionOffset see mNewNavigationPositionOffset.
90 explicit mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker,
91 Operation aOp, bool aForceNavigationWordCheck,
92 int32_t aNewNavigationPositionOffset);
94 // For resuming a previously started check.
95 const Operation mOp;
98 // If we happen to know something was inserted, this is that range.
99 // Can be nullptr (this only allows an optimization, so not setting doesn't
100 // hurt)
101 RefPtr<nsRange> mCreatedRange;
103 // Contains the range computed for the current word. Can be nullptr.
104 RefPtr<nsRange> mNoCheckRange;
106 // Indicates the position of the cursor for the event (so we can compute
107 // mNoCheckRange). It can be nullptr if we don't care about the cursor
108 // position (such as for the intial check of everything).
110 // For mOp == eOpNavigation, this is the NEW position of the cursor
111 RefPtr<nsRange> mAnchorRange;
113 // -----
114 // The following members are only for navigation events and are only
115 // stored for FinishNavigationEvent to initialize the other members.
116 // -----
118 // this is the OLD position of the cursor
119 RefPtr<nsRange> mOldNavigationAnchorRange;
121 // Set when we should force checking the current word. See
122 // mozInlineSpellChecker::HandleNavigationEvent for a description of why we
123 // have this.
124 const bool mForceNavigationWordCheck;
126 // Contains the offset passed in to HandleNavigationEvent
127 const int32_t mNewNavigationPositionOffset;
129 nsresult FinishNavigationEvent(mozInlineSpellWordUtil& aWordUtil);
131 nsresult FillNoCheckRangeFromAnchor(mozInlineSpellWordUtil& aWordUtil);
133 mozilla::dom::Document* GetDocument() const;
134 already_AddRefed<nsRange> PositionToCollapsedRange(nsINode* aNode,
135 uint32_t aOffset) const;
138 class mozInlineSpellChecker final : public nsIInlineSpellChecker,
139 public nsIDOMEventListener,
140 public nsSupportsWeakReference {
141 private:
142 friend class mozInlineSpellStatus;
143 friend class InitEditorSpellCheckCallback;
144 friend class UpdateCurrentDictionaryCallback;
145 friend class AutoChangeNumPendingSpellChecks;
147 // Access with CanEnableInlineSpellChecking
148 enum SpellCheckingState {
149 SpellCheck_Uninitialized = -1,
150 SpellCheck_NotAvailable = 0,
151 SpellCheck_Available = 1
153 static SpellCheckingState gCanEnableSpellChecking;
155 RefPtr<mozilla::TextEditor> mTextEditor;
156 RefPtr<mozilla::EditorSpellCheck> mSpellCheck;
157 RefPtr<mozilla::EditorSpellCheck> mPendingSpellCheck;
159 int32_t mNumWordsInSpellSelection;
160 int32_t mMaxNumWordsInSpellSelection;
162 // we need to keep track of the current text position in the document
163 // so we can spell check the old word when the user clicks around the
164 // document.
165 nsCOMPtr<nsINode> mCurrentSelectionAnchorNode;
166 uint32_t mCurrentSelectionOffset;
168 // Tracks the number of pending spell checks *and* async operations that may
169 // lead to spell checks, like updating the current dictionary. This is
170 // necessary so that observers can know when to wait for spell check to
171 // complete.
172 int32_t mNumPendingSpellChecks;
174 // The number of calls to UpdateCurrentDictionary that haven't finished yet.
175 int32_t mNumPendingUpdateCurrentDictionary;
177 // This number is incremented each time the spell checker is disabled so that
178 // pending scheduled spell checks and UpdateCurrentDictionary calls can be
179 // ignored when they finish.
180 uint32_t mDisabledAsyncToken;
182 // When mPendingSpellCheck is non-null, this is the callback passed when
183 // it was initialized.
184 RefPtr<InitEditorSpellCheckCallback> mPendingInitEditorSpellCheckCallback;
186 // Set when we have spellchecked after the last edit operation. See the
187 // commment at the top of the .cpp file for more info.
188 bool mNeedsCheckAfterNavigation;
190 // Set when we have a pending mozInlineSpellResume which will check
191 // the whole document.
192 bool mFullSpellCheckScheduled;
194 // Set to true when this instance needs to listen to edit actions of
195 // the editor.
196 bool mIsListeningToEditSubActions;
198 public:
199 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
200 NS_DECL_NSIINLINESPELLCHECKER
201 NS_DECL_NSIDOMEVENTLISTENER
202 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker,
203 nsIDOMEventListener)
205 mozilla::EditorSpellCheck* GetEditorSpellCheck();
207 // See `mDisabledAsyncToken`.
208 uint32_t GetDisabledAsyncToken() const { return mDisabledAsyncToken; }
210 // returns true if there are any spell checking dictionaries available
211 static bool CanEnableInlineSpellChecking();
212 // update the cached value whenever the list of available dictionaries changes
213 static void UpdateCanEnableInlineSpellChecking();
215 nsresult OnBlur(mozilla::dom::Event* aEvent);
216 nsresult OnMouseClick(mozilla::dom::Event* aMouseEvent);
217 nsresult OnKeyPress(mozilla::dom::Event* aKeyEvent);
219 mozInlineSpellChecker();
221 // spell checks all of the words between two nodes
222 nsresult SpellCheckBetweenNodes(nsINode* aStartNode, int32_t aStartOffset,
223 nsINode* aEndNode, int32_t aEndOffset);
225 // examines the dom node in question and returns true if the inline spell
226 // checker should skip the node (i.e. the text is inside of a block quote
227 // or an e-mail signature...)
228 bool ShouldSpellCheckNode(mozilla::TextEditor* aTextEditor,
229 nsINode* aNode) const;
231 // spell check the text contained within aRange, potentially scheduling
232 // another check in the future if the time threshold is reached
233 nsresult ScheduleSpellCheck(
234 mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
236 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
237 DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
238 mozilla::dom::Selection* aSpellCheckSelection);
239 nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
240 mozilla::dom::Selection* aSpellCheckSelection,
241 const mozilla::UniquePtr<mozInlineSpellStatus>& aStatus,
242 bool* aDoneChecking);
244 // helper routine to determine if a point is inside of the passed in
245 // selection.
246 static nsresult IsPointInSelection(mozilla::dom::Selection& aSelection,
247 nsINode* aNode, int32_t aOffset,
248 nsRange** aRange);
250 nsresult CleanupRangesInSelection(mozilla::dom::Selection* aSelection);
253 * @param aRange needs to be kept alive by the caller.
255 // TODO: annotate with `MOZ_CAN_RUN_SCRIPT` instead
256 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1620540).
257 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
258 RemoveRange(mozilla::dom::Selection* aSpellCheckSelection, nsRange* aRange);
259 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
260 AddRange(mozilla::dom::Selection* aSpellCheckSelection, nsRange* aRange);
261 bool IsSpellCheckSelectionFull() const {
262 return mNumWordsInSpellSelection >= mMaxNumWordsInSpellSelection;
265 nsresult MakeSpellCheckRange(nsINode* aStartNode, int32_t aStartOffset,
266 nsINode* aEndNode, int32_t aEndOffset,
267 nsRange** aRange) const;
269 // DOM and editor event registration helper routines
270 nsresult RegisterEventListeners();
271 nsresult UnregisterEventListeners();
272 nsresult HandleNavigationEvent(bool aForceWordSpellCheck,
273 int32_t aNewPositionOffset = 0);
275 already_AddRefed<mozilla::dom::Selection> GetSpellCheckSelection();
276 nsresult SaveCurrentSelectionPosition();
278 nsresult ResumeCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
280 // Those methods are called when mTextEditor splits a node or joins the
281 // given nodes.
282 void DidSplitNode(nsINode* aExistingRightNode, nsINode* aNewLeftNode);
283 void DidJoinNodes(nsINode& aRightNode, nsINode& aLeftNode);
285 nsresult SpellCheckAfterEditorChange(mozilla::EditSubAction aEditSubAction,
286 mozilla::dom::Selection& aSelection,
287 nsINode* aPreviousSelectedNode,
288 uint32_t aPreviousSelectedOffset,
289 nsINode* aStartNode,
290 uint32_t aStartOffset, nsINode* aEndNode,
291 uint32_t aEndOffset);
293 protected:
294 virtual ~mozInlineSpellChecker();
296 // called when async nsIEditorSpellCheck methods complete
297 nsresult EditorSpellCheckInited();
298 nsresult CurrentDictionaryUpdated();
300 // track the number of pending spell checks and async operations that may lead
301 // to spell checks, notifying observers accordingly
302 void ChangeNumPendingSpellChecks(int32_t aDelta,
303 mozilla::TextEditor* aTextEditor = nullptr);
304 void NotifyObservers(const char* aTopic, mozilla::TextEditor* aTextEditor);
306 void StartToListenToEditSubActions() { mIsListeningToEditSubActions = true; }
307 void EndListeningToEditSubActions() { mIsListeningToEditSubActions = false; }
309 void CheckCurrentWordsNoSuggest(mozilla::dom::Selection* aSpellCheckSelection,
310 nsTArray<nsString>&& aWords,
311 nsTArray<NodeOffsetRange>&& aRanges);
314 #endif // #ifndef mozilla_mozInlineSpellChecker_h