Bug 1888590 - Mark some subtests on trusted-types-event-handlers.html as failing...
[gecko.git] / extensions / spellcheck / src / mozInlineSpellChecker.h
blob0e304c93a2c79f28352266daac7fa38ade26eabe
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/EditorDOMPoint.h"
15 #include "mozilla/Result.h"
16 #include "nsRange.h"
17 #include "nsWeakReference.h"
19 class InitEditorSpellCheckCallback;
20 class mozInlineSpellChecker;
21 class mozInlineSpellResume;
22 class UpdateCurrentDictionaryCallback;
24 namespace mozilla {
25 class EditorBase;
26 class EditorSpellCheck;
27 enum class EditSubAction : int32_t;
28 enum class JoinNodesDirection;
30 namespace dom {
31 class Event;
32 } // namespace dom
33 } // namespace mozilla
35 class mozInlineSpellStatus {
36 public:
37 static mozilla::Result<mozilla::UniquePtr<mozInlineSpellStatus>, nsresult>
38 CreateForEditorChange(mozInlineSpellChecker& aSpellChecker,
39 mozilla::EditSubAction aEditSubAction,
40 nsINode* aAnchorNode, uint32_t aAnchorOffset,
41 nsINode* aPreviousNode, uint32_t aPreviousOffset,
42 nsINode* aStartNode, uint32_t aStartOffset,
43 nsINode* aEndNode, uint32_t aEndOffset);
45 static mozilla::Result<mozilla::UniquePtr<mozInlineSpellStatus>, nsresult>
46 CreateForNavigation(mozInlineSpellChecker& aSpellChecker, bool aForceCheck,
47 int32_t aNewPositionOffset, nsINode* aOldAnchorNode,
48 uint32_t aOldAnchorOffset, nsINode* aNewAnchorNode,
49 uint32_t aNewAnchorOffset, bool* aContinue);
51 static mozilla::UniquePtr<mozInlineSpellStatus> CreateForSelection(
52 mozInlineSpellChecker& aSpellChecker);
54 static mozilla::UniquePtr<mozInlineSpellStatus> CreateForRange(
55 mozInlineSpellChecker& aSpellChecker, nsRange* aRange);
57 nsresult FinishInitOnEvent(mozInlineSpellWordUtil& aWordUtil);
59 // Return true if we plan to spell-check everything
60 bool IsFullSpellCheck() const { return mOp == eOpChange && !mRange; }
62 const RefPtr<mozInlineSpellChecker> mSpellChecker;
64 enum Operation {
65 eOpChange, // for SpellCheckAfterEditorChange except
66 // deleteSelection
67 eOpChangeDelete, // for SpellCheckAfterEditorChange with
68 // deleteSelection
69 eOpNavigation, // for HandleNavigationEvent
70 eOpSelection, // re-check all misspelled words
71 eOpResume
74 // See `mOp`.
75 Operation GetOperation() const { return mOp; }
77 // Used for events where we have already computed the range to use. It can
78 // also be nullptr in these cases where we need to check the entire range.
79 RefPtr<nsRange> mRange;
81 // See `mCreatedRange`.
82 const nsRange* GetCreatedRange() const { return mCreatedRange; }
84 // See `mNoCheckRange`.
85 const nsRange* GetNoCheckRange() const { return mNoCheckRange; }
87 private:
88 // @param aSpellChecker must be non-nullptr.
89 // @param aOp see mOp.
90 // @param aRange see mRange.
91 // @param aCreatedRange see mCreatedRange.
92 // @param aAnchorRange see mAnchorRange.
93 // @param aForceNavigationWordCheck see mForceNavigationWordCheck.
94 // @param aNewNavigationPositionOffset see mNewNavigationPositionOffset.
95 explicit mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker,
96 Operation aOp, RefPtr<nsRange>&& aRange,
97 RefPtr<nsRange>&& aCreatedRange,
98 RefPtr<nsRange>&& aAnchorRange,
99 bool aForceNavigationWordCheck,
100 int32_t aNewNavigationPositionOffset);
102 // For resuming a previously started check.
103 const Operation mOp;
106 // If we happen to know something was inserted, this is that range.
107 // Can be nullptr (this only allows an optimization, so not setting doesn't
108 // hurt)
109 const RefPtr<const nsRange> mCreatedRange;
111 // Contains the range computed for the current word. Can be nullptr.
112 RefPtr<nsRange> mNoCheckRange;
114 // Indicates the position of the cursor for the event (so we can compute
115 // mNoCheckRange). It can be nullptr if we don't care about the cursor
116 // position (such as for the intial check of everything).
118 // For mOp == eOpNavigation, this is the NEW position of the cursor
119 const RefPtr<const nsRange> mAnchorRange;
121 // -----
122 // The following members are only for navigation events and are only
123 // stored for FinishNavigationEvent to initialize the other members.
124 // -----
126 // this is the OLD position of the cursor
127 RefPtr<nsRange> mOldNavigationAnchorRange;
129 // Set when we should force checking the current word. See
130 // mozInlineSpellChecker::HandleNavigationEvent for a description of why we
131 // have this.
132 const bool mForceNavigationWordCheck;
134 // Contains the offset passed in to HandleNavigationEvent
135 const int32_t mNewNavigationPositionOffset;
137 nsresult FinishNavigationEvent(mozInlineSpellWordUtil& aWordUtil);
139 nsresult FillNoCheckRangeFromAnchor(mozInlineSpellWordUtil& aWordUtil);
141 mozilla::dom::Document* GetDocument() const;
142 static already_AddRefed<nsRange> PositionToCollapsedRange(nsINode* aNode,
143 uint32_t aOffset);
146 class mozInlineSpellChecker final : public nsIInlineSpellChecker,
147 public nsIDOMEventListener,
148 public nsSupportsWeakReference {
149 private:
150 friend class mozInlineSpellStatus;
151 friend class InitEditorSpellCheckCallback;
152 friend class UpdateCurrentDictionaryCallback;
153 friend class AutoChangeNumPendingSpellChecks;
155 // Access with CanEnableInlineSpellChecking
156 enum SpellCheckingState {
157 SpellCheck_Uninitialized = -1,
158 SpellCheck_NotAvailable = 0,
159 SpellCheck_Available = 1
161 static SpellCheckingState gCanEnableSpellChecking;
163 RefPtr<mozilla::EditorBase> mEditorBase;
164 RefPtr<mozilla::EditorSpellCheck> mSpellCheck;
165 RefPtr<mozilla::EditorSpellCheck> mPendingSpellCheck;
167 int32_t mNumWordsInSpellSelection;
168 const int32_t mMaxNumWordsInSpellSelection;
170 // we need to keep track of the current text position in the document
171 // so we can spell check the old word when the user clicks around the
172 // document.
173 nsCOMPtr<nsINode> mCurrentSelectionAnchorNode;
174 uint32_t mCurrentSelectionOffset;
176 // Tracks the number of pending spell checks *and* async operations that may
177 // lead to spell checks, like updating the current dictionary. This is
178 // necessary so that observers can know when to wait for spell check to
179 // complete.
180 int32_t mNumPendingSpellChecks;
182 // The number of calls to UpdateCurrentDictionary that haven't finished yet.
183 int32_t mNumPendingUpdateCurrentDictionary;
185 // This number is incremented each time the spell checker is disabled so that
186 // pending scheduled spell checks and UpdateCurrentDictionary calls can be
187 // ignored when they finish.
188 uint32_t mDisabledAsyncToken;
190 // When mPendingSpellCheck is non-null, this is the callback passed when
191 // it was initialized.
192 RefPtr<InitEditorSpellCheckCallback> mPendingInitEditorSpellCheckCallback;
194 // Set when we have spellchecked after the last edit operation. See the
195 // commment at the top of the .cpp file for more info.
196 bool mNeedsCheckAfterNavigation;
198 // Set when we have a pending mozInlineSpellResume which will check
199 // the whole document.
200 bool mFullSpellCheckScheduled;
202 // Set to true when this instance needs to listen to edit actions of
203 // the editor.
204 bool mIsListeningToEditSubActions;
206 class SpellCheckerSlice;
208 public:
209 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
210 NS_DECL_NSIINLINESPELLCHECKER
211 NS_DECL_NSIDOMEVENTLISTENER
212 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker,
213 nsIDOMEventListener)
215 mozilla::EditorSpellCheck* GetEditorSpellCheck();
217 // See `mDisabledAsyncToken`.
218 uint32_t GetDisabledAsyncToken() const { return mDisabledAsyncToken; }
220 // returns true if there are any spell checking dictionaries available
221 static bool CanEnableInlineSpellChecking();
222 // update the cached value whenever the list of available dictionaries changes
223 static void UpdateCanEnableInlineSpellChecking();
225 mozInlineSpellChecker();
227 // spell checks all of the words between two nodes
228 nsresult SpellCheckBetweenNodes(nsINode* aStartNode, int32_t aStartOffset,
229 nsINode* aEndNode, int32_t aEndOffset);
231 // examines the dom node in question and returns true if the inline spell
232 // checker should skip the node (i.e. the text is inside of a block quote
233 // or an e-mail signature...)
234 static bool ShouldSpellCheckNode(mozilla::EditorBase* aEditorBase,
235 nsINode* aNode);
237 // spell check the text contained within aRange, potentially scheduling
238 // another check in the future if the time threshold is reached
239 nsresult ScheduleSpellCheck(
240 mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
242 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
243 DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil,
244 mozilla::dom::Selection* aSpellCheckSelection);
246 nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil,
247 mozilla::dom::Selection* aSpellCheckSelection,
248 const mozilla::UniquePtr<mozInlineSpellStatus>& aStatus,
249 bool* aDoneChecking);
251 // helper routine to determine if a point is inside of the passed in
252 // selection.
253 static nsresult IsPointInSelection(mozilla::dom::Selection& aSelection,
254 nsINode* aNode, uint32_t aOffset,
255 nsRange** aRange);
257 nsresult CleanupRangesInSelection(mozilla::dom::Selection* aSelection);
260 * @param aRange needs to be kept alive by the caller.
262 // TODO: annotate with `MOZ_CAN_RUN_SCRIPT` instead
263 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1620540).
264 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
265 RemoveRange(mozilla::dom::Selection* aSpellCheckSelection, nsRange* aRange);
267 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
268 AddRange(mozilla::dom::Selection* aSpellCheckSelection, nsRange* aRange);
269 bool IsSpellCheckSelectionFull() const {
270 return mNumWordsInSpellSelection >= mMaxNumWordsInSpellSelection;
273 nsresult MakeSpellCheckRange(nsINode* aStartNode, int32_t aStartOffset,
274 nsINode* aEndNode, int32_t aEndOffset,
275 nsRange** aRange) const;
277 // DOM and editor event registration helper routines
278 nsresult RegisterEventListeners();
279 nsresult UnregisterEventListeners();
280 nsresult HandleNavigationEvent(bool aForceWordSpellCheck,
281 int32_t aNewPositionOffset = 0);
283 already_AddRefed<mozilla::dom::Selection> GetSpellCheckSelection();
284 nsresult SaveCurrentSelectionPosition();
286 nsresult ResumeCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
288 nsresult SpellCheckAfterEditorChange(mozilla::EditSubAction aEditSubAction,
289 mozilla::dom::Selection& aSelection,
290 nsINode* aPreviousSelectedNode,
291 uint32_t aPreviousSelectedOffset,
292 nsINode* aStartNode,
293 uint32_t aStartOffset, nsINode* aEndNode,
294 uint32_t aEndOffset);
296 protected:
297 virtual ~mozInlineSpellChecker();
299 struct CompareRangeAndNodeOffsetRange;
301 // Ensures that all misspelled words have corresponding ranges in
302 // aSpellCheckerSelection. Reuses those of the old ranges, which still
303 // correspond to misspelled words and adds new ranges for those misspelled
304 // words for which no corresponding old range exists.
305 // Removes the old ranges which aren't reused from aSpellCheckerSelection.
307 // @param aNodeOffsetRangesForWords corresponds to aIsMisspelled.
308 // `aNodeOffsetRangesForWords.Length() ==
309 // aIsMisspelled.Length()`.
310 // @param aOldRangesForSomeWords ranges belonging to aSpellCheckerSelection.
311 // Its length may differ from
312 // `aNodeOffsetRangesForWords.Length()`.
313 // @param aIsMisspelled indicates which words are misspelled.
314 MOZ_CAN_RUN_SCRIPT_BOUNDARY void UpdateRangesForMisspelledWords(
315 const nsTArray<NodeOffsetRange>& aNodeOffsetRangesForWords,
316 const nsTArray<RefPtr<nsRange>>& aOldRangesForSomeWords,
317 const nsTArray<bool>& aIsMisspelled,
318 mozilla::dom::Selection& aSpellCheckerSelection);
320 // called when async nsIEditorSpellCheck methods complete
321 nsresult EditorSpellCheckInited();
322 nsresult CurrentDictionaryUpdated();
324 // track the number of pending spell checks and async operations that may lead
325 // to spell checks, notifying observers accordingly
326 void ChangeNumPendingSpellChecks(int32_t aDelta,
327 mozilla::EditorBase* aEditorBase = nullptr);
328 void NotifyObservers(const char* aTopic, mozilla::EditorBase* aEditorBase);
330 void StartToListenToEditSubActions() { mIsListeningToEditSubActions = true; }
331 void EndListeningToEditSubActions() { mIsListeningToEditSubActions = false; }
333 void OnBlur(mozilla::dom::Event& aEvent);
334 void OnMouseClick(mozilla::dom::Event& aMouseEvent);
335 void OnKeyDown(mozilla::dom::Event& aKeyEvent);
338 #endif // #ifndef mozilla_mozInlineSpellChecker_h