Bug 1700051: part 11) Change `mozInlineSpellStatus::InitForRange` to static factory...
[gecko.git] / extensions / spellcheck / src / mozInlineSpellChecker.h
blob774dee53322a15dbc91d8d5f7641ef0399f9d9f8
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 // @param aSpellChecker must be non-nullptr.
36 explicit mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker);
38 static mozilla::Result<mozilla::UniquePtr<mozInlineSpellStatus>, nsresult>
39 CreateForEditorChange(mozInlineSpellChecker& aSpellChecker,
40 mozilla::EditSubAction aEditSubAction,
41 nsINode* aAnchorNode, uint32_t aAnchorOffset,
42 nsINode* aPreviousNode, uint32_t aPreviousOffset,
43 nsINode* aStartNode, uint32_t aStartOffset,
44 nsINode* aEndNode, uint32_t aEndOffset);
46 static mozilla::Result<mozilla::UniquePtr<mozInlineSpellStatus>, nsresult>
47 CreateForNavigation(mozInlineSpellChecker& aSpellChecker, bool aForceCheck,
48 int32_t aNewPositionOffset, nsINode* aOldAnchorNode,
49 uint32_t aOldAnchorOffset, nsINode* aNewAnchorNode,
50 uint32_t aNewAnchorOffset, bool* aContinue);
52 static mozilla::UniquePtr<mozInlineSpellStatus> CreateForSelection(
53 mozInlineSpellChecker& aSpellChecker);
55 static mozilla::UniquePtr<mozInlineSpellStatus> CreateForRange(
56 mozInlineSpellChecker& aSpellChecker, nsRange* aRange);
58 nsresult FinishInitOnEvent(mozInlineSpellWordUtil& aWordUtil);
60 // Return true if we plan to spell-check everything
61 bool IsFullSpellCheck() const { return mOp == eOpChange && !mRange; }
63 RefPtr<mozInlineSpellChecker> mSpellChecker;
65 enum Operation {
66 eOpChange, // for SpellCheckAfterEditorChange except
67 // deleteSelection
68 eOpChangeDelete, // for SpellCheckAfterEditorChange with
69 // deleteSelection
70 eOpNavigation, // for HandleNavigationEvent
71 eOpSelection, // re-check all misspelled words
72 eOpResume
75 // See `mOp`.
76 Operation GetOperation() const { return mOp; }
78 // Used for events where we have already computed the range to use. It can
79 // also be nullptr in these cases where we need to check the entire range.
80 RefPtr<nsRange> mRange;
82 // See `mCreatedRange`.
83 const nsRange* GetCreatedRange() const { return mCreatedRange; }
85 // See `mNoCheckRange`.
86 const nsRange* GetNoCheckRange() const { return mNoCheckRange; }
88 private:
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;
141 friend class mozInlineSpellResume;
143 // Access with CanEnableInlineSpellChecking
144 enum SpellCheckingState {
145 SpellCheck_Uninitialized = -1,
146 SpellCheck_NotAvailable = 0,
147 SpellCheck_Available = 1
149 static SpellCheckingState gCanEnableSpellChecking;
151 RefPtr<mozilla::TextEditor> mTextEditor;
152 RefPtr<mozilla::EditorSpellCheck> mSpellCheck;
153 RefPtr<mozilla::EditorSpellCheck> mPendingSpellCheck;
155 int32_t mNumWordsInSpellSelection;
156 int32_t mMaxNumWordsInSpellSelection;
158 // we need to keep track of the current text position in the document
159 // so we can spell check the old word when the user clicks around the
160 // document.
161 nsCOMPtr<nsINode> mCurrentSelectionAnchorNode;
162 uint32_t mCurrentSelectionOffset;
164 // Tracks the number of pending spell checks *and* async operations that may
165 // lead to spell checks, like updating the current dictionary. This is
166 // necessary so that observers can know when to wait for spell check to
167 // complete.
168 int32_t mNumPendingSpellChecks;
170 // The number of calls to UpdateCurrentDictionary that haven't finished yet.
171 int32_t mNumPendingUpdateCurrentDictionary;
173 // This number is incremented each time the spell checker is disabled so that
174 // pending scheduled spell checks and UpdateCurrentDictionary calls can be
175 // ignored when they finish.
176 uint32_t mDisabledAsyncToken;
178 // When mPendingSpellCheck is non-null, this is the callback passed when
179 // it was initialized.
180 RefPtr<InitEditorSpellCheckCallback> mPendingInitEditorSpellCheckCallback;
182 // Set when we have spellchecked after the last edit operation. See the
183 // commment at the top of the .cpp file for more info.
184 bool mNeedsCheckAfterNavigation;
186 // Set when we have a pending mozInlineSpellResume which will check
187 // the whole document.
188 bool mFullSpellCheckScheduled;
190 // Set to true when this instance needs to listen to edit actions of
191 // the editor.
192 bool mIsListeningToEditSubActions;
194 public:
195 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
196 NS_DECL_NSIINLINESPELLCHECKER
197 NS_DECL_NSIDOMEVENTLISTENER
198 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker,
199 nsIDOMEventListener)
201 mozilla::EditorSpellCheck* GetEditorSpellCheck();
203 // returns true if there are any spell checking dictionaries available
204 static bool CanEnableInlineSpellChecking();
205 // update the cached value whenever the list of available dictionaries changes
206 static void UpdateCanEnableInlineSpellChecking();
208 nsresult OnBlur(mozilla::dom::Event* aEvent);
209 nsresult OnMouseClick(mozilla::dom::Event* aMouseEvent);
210 nsresult OnKeyPress(mozilla::dom::Event* aKeyEvent);
212 mozInlineSpellChecker();
214 // spell checks all of the words between two nodes
215 nsresult SpellCheckBetweenNodes(nsINode* aStartNode, int32_t aStartOffset,
216 nsINode* aEndNode, int32_t aEndOffset);
218 // examines the dom node in question and returns true if the inline spell
219 // checker should skip the node (i.e. the text is inside of a block quote
220 // or an e-mail signature...)
221 bool ShouldSpellCheckNode(mozilla::TextEditor* aTextEditor, nsINode* aNode);
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(
226 mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
228 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
229 DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
230 mozilla::dom::Selection* aSpellCheckSelection);
231 nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
232 mozilla::dom::Selection* aSpellCheckSelection,
233 const mozilla::UniquePtr<mozInlineSpellStatus>& aStatus,
234 bool* aDoneChecking);
236 // helper routine to determine if a point is inside of the passed in
237 // selection.
238 nsresult IsPointInSelection(mozilla::dom::Selection& aSelection,
239 nsINode* aNode, int32_t aOffset,
240 nsRange** aRange);
242 nsresult CleanupRangesInSelection(mozilla::dom::Selection* aSelection);
245 * @param aRange needs to be kept alive by the caller.
247 // TODO: annotate with `MOZ_CAN_RUN_SCRIPT` instead
248 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1620540).
249 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
250 RemoveRange(mozilla::dom::Selection* aSpellCheckSelection, nsRange* aRange);
251 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
252 AddRange(mozilla::dom::Selection* aSpellCheckSelection, nsRange* aRange);
253 bool SpellCheckSelectionIsFull() {
254 return mNumWordsInSpellSelection >= mMaxNumWordsInSpellSelection;
257 nsresult MakeSpellCheckRange(nsINode* aStartNode, int32_t aStartOffset,
258 nsINode* aEndNode, int32_t aEndOffset,
259 nsRange** aRange);
261 // DOM and editor event registration helper routines
262 nsresult RegisterEventListeners();
263 nsresult UnregisterEventListeners();
264 nsresult HandleNavigationEvent(bool aForceWordSpellCheck,
265 int32_t aNewPositionOffset = 0);
267 already_AddRefed<mozilla::dom::Selection> GetSpellCheckSelection();
268 nsresult SaveCurrentSelectionPosition();
270 nsresult ResumeCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
272 // Those methods are called when mTextEditor splits a node or joins the
273 // given nodes.
274 void DidSplitNode(nsINode* aExistingRightNode, nsINode* aNewLeftNode);
275 void DidJoinNodes(nsINode& aRightNode, nsINode& aLeftNode);
277 nsresult SpellCheckAfterEditorChange(mozilla::EditSubAction aEditSubAction,
278 mozilla::dom::Selection& aSelection,
279 nsINode* aPreviousSelectedNode,
280 uint32_t aPreviousSelectedOffset,
281 nsINode* aStartNode,
282 uint32_t aStartOffset, nsINode* aEndNode,
283 uint32_t aEndOffset);
285 protected:
286 virtual ~mozInlineSpellChecker();
288 // called when async nsIEditorSpellCheck methods complete
289 nsresult EditorSpellCheckInited();
290 nsresult CurrentDictionaryUpdated();
292 // track the number of pending spell checks and async operations that may lead
293 // to spell checks, notifying observers accordingly
294 void ChangeNumPendingSpellChecks(int32_t aDelta,
295 mozilla::TextEditor* aTextEditor = nullptr);
296 void NotifyObservers(const char* aTopic, mozilla::TextEditor* aTextEditor);
298 void StartToListenToEditSubActions() { mIsListeningToEditSubActions = true; }
299 void EndListeningToEditSubActions() { mIsListeningToEditSubActions = false; }
301 void CheckCurrentWordsNoSuggest(mozilla::dom::Selection* aSpellCheckSelection,
302 nsTArray<nsString>&& aWords,
303 nsTArray<NodeOffsetRange>&& aRanges);
306 #endif // #ifndef mozilla_mozInlineSpellChecker_h