Bug 1892041 - Part 3: Update test exclusions. r=spidermonkey-reviewers,dminor
[gecko.git] / widget / TextEventDispatcher.h
blob6ab131f0792c9136dc4c17dfd0fa8ed6bf32d9ad
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_textcompositionsynthesizer_h_
7 #define mozilla_textcompositionsynthesizer_h_
9 #include "mozilla/RefPtr.h"
10 #include "nsString.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/TextEventDispatcherListener.h"
15 #include "mozilla/TextRange.h"
16 #include "mozilla/widget/IMEData.h"
17 #include "WritingModes.h"
19 class nsIWidget;
21 namespace mozilla {
22 namespace widget {
24 class PuppetWidget;
26 /**
27 * TextEventDispatcher is a helper class for dispatching widget events defined
28 * in TextEvents.h. Currently, this is a helper for dispatching
29 * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior
30 * of them for conforming to DOM Level 3 Events.
31 * An instance of this class is created by nsIWidget instance and owned by it.
32 * This is typically created only by the top level widgets because only they
33 * handle IME.
36 class TextEventDispatcher final {
37 ~TextEventDispatcher() = default;
39 NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
41 public:
42 explicit TextEventDispatcher(nsIWidget* aWidget);
44 /**
45 * Initializes the instance for IME or automated test. Either IME or tests
46 * need to call one of them before starting composition. If they return
47 * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
48 * notifications from TextEventDispatcher for same purpose (for IME or tests).
49 * If this returns another error, the caller shouldn't keep starting
50 * composition.
52 * @param aListener Specify the listener to listen notifications and
53 * requests. This must not be null.
54 * NOTE: aListener is stored as weak reference in
55 * TextEventDispatcher. See mListener
56 * definition below.
58 nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
59 nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener,
60 bool aIsAPZAware);
61 nsresult BeginNativeInputTransaction();
63 /**
64 * BeginInputTransactionFor() should be used when aPuppetWidget dispatches
65 * a composition or keyboard event coming from its parent process.
67 nsresult BeginInputTransactionFor(const WidgetGUIEvent* aEvent,
68 PuppetWidget* aPuppetWidget);
70 /**
71 * EndInputTransaction() should be called when the listener stops using
72 * the TextEventDispatcher.
74 * @param aListener The listener using the TextEventDispatcher instance.
76 void EndInputTransaction(TextEventDispatcherListener* aListener);
78 /**
79 * OnDestroyWidget() is called when mWidget is being destroyed.
81 void OnDestroyWidget();
83 nsIWidget* GetWidget() const { return mWidget; }
85 /**
86 * Return true starting from ending handling focus notification and until
87 * receiving blur notification.
89 bool HasFocus() const { return mHasFocus; }
91 const IMENotificationRequests& IMENotificationRequestsRef() const {
92 return mIMENotificationRequests;
95 /**
96 * OnWidgetChangeIMENotificationRequests() is called when aWidget's
97 * IMENotificationRequest is maybe modified by unusual path. E.g.,
98 * modified in an async path.
100 void OnWidgetChangeIMENotificationRequests(nsIWidget* aWidget) {
101 MOZ_ASSERT(aWidget);
102 if (mWidget == aWidget) {
103 UpdateNotificationRequests();
108 * GetState() returns current state of this class.
110 * @return NS_OK: Fine to compose text.
111 * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
112 * BeginInputTransactionForTests()
113 * should be called.
114 * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
115 * composition.
117 nsresult GetState() const;
120 * IsComposing() returns true after calling StartComposition() and before
121 * calling CommitComposition(). In other words, native IME has composition
122 * when this returns true.
124 bool IsComposing() const { return mIsComposing; }
127 * IsHandlingComposition() returns true after calling StartComposition() and
128 * content has not handled eCompositionCommit(AsIs) event. In other words,
129 * our content has composition when this returns true.
131 bool IsHandlingComposition() const { return mIsHandlingComposition; }
134 * IsInNativeInputTransaction() returns true if native IME handler began a
135 * transaction and it's not finished yet.
137 bool IsInNativeInputTransaction() const {
138 return mInputTransactionType == eNativeInputTransaction;
142 * IsDispatchingEvent() returns true while this instance dispatching an event.
144 bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
147 * GetPseudoIMEContext() returns pseudo native IME context if there is an
148 * input transaction whose type is not for native event handler.
149 * Otherwise, returns nullptr.
151 void* GetPseudoIMEContext() const {
152 if (mInputTransactionType == eNoInputTransaction ||
153 mInputTransactionType == eNativeInputTransaction) {
154 return nullptr;
156 return const_cast<TextEventDispatcher*>(this);
160 * Return writing mode at selection while this has focus. Otherwise, or
161 * never exists selection ranges, this returns Nothing.
163 const Maybe<WritingMode>& MaybeWritingModeRefAtSelection() const {
164 return mWritingMode;
168 * MaybeQueryWritingModeAtSelection() returns writing mode at current
169 * selection even if this does not have focus. If this is not focused, this
170 * queries selection. Then, chrome script can run due to flushing the layout
171 * if an element in chrome has focus (but it should not cause any problem
172 * hopefully).
174 MOZ_CAN_RUN_SCRIPT Maybe<WritingMode> MaybeQueryWritingModeAtSelection()
175 const;
178 * StartComposition() starts composition explicitly.
180 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
181 * be initialized with this. Otherwise, initialized
182 * with the time at initializing.
184 nsresult StartComposition(nsEventStatus& aStatus,
185 const WidgetEventTime* aEventTime = nullptr);
188 * CommitComposition() commits composition.
190 * @param aCommitString If this is null, commits with the last composition
191 * string. Otherwise, commits the composition with
192 * this value.
193 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
194 * be initialized with this. Otherwise, initialized
195 * with the time at initializing.
197 nsresult CommitComposition(nsEventStatus& aStatus,
198 const nsAString* aCommitString = nullptr,
199 const WidgetEventTime* aEventTime = nullptr);
202 * SetPendingCompositionString() sets new composition string which will be
203 * dispatched with eCompositionChange event by calling Flush().
205 * @param aString New composition string.
207 nsresult SetPendingCompositionString(const nsAString& aString) {
208 return mPendingComposition.SetString(aString);
212 * AppendClauseToPendingComposition() appends a clause information to
213 * the pending composition string.
215 * @param aLength Length of the clause.
216 * @param aTextRangeType One of TextRangeType::eRawClause,
217 * TextRangeType::eSelectedRawClause,
218 * TextRangeType::eConvertedClause or
219 * TextRangeType::eSelectedClause.
221 nsresult AppendClauseToPendingComposition(uint32_t aLength,
222 TextRangeType aTextRangeType) {
223 return mPendingComposition.AppendClause(aLength, aTextRangeType);
227 * SetCaretInPendingComposition() sets caret position in the pending
228 * composition string and its length. This is optional. If IME doesn't
229 * want to show caret, it shouldn't need to call this.
231 * @param aOffset Offset of the caret in the pending composition
232 * string. This should not be larger than the length
233 * of the pending composition string.
234 * @param aLength Caret width. If this is 0, caret will be collapsed.
235 * Note that Gecko doesn't supported wide caret yet,
236 * therefore, this is ignored for now.
238 nsresult SetCaretInPendingComposition(uint32_t aOffset, uint32_t aLength) {
239 return mPendingComposition.SetCaret(aOffset, aLength);
243 * SetPendingComposition() is useful if native IME handler already creates
244 * array of clauses and/or caret information.
246 * @param aString Composition string. This may include native line
247 * breakers since they will be replaced with XP line
248 * breakers automatically.
249 * @param aRanges This should include the ranges of clauses and/or
250 * a range of caret. Note that this method allows
251 * some ranges overlap each other and the range order
252 * is not from start to end.
254 nsresult SetPendingComposition(const nsAString& aString,
255 const TextRangeArray* aRanges) {
256 return mPendingComposition.Set(aString, aRanges);
260 * FlushPendingComposition() sends the pending composition string
261 * to the widget of the store DOM window. Before calling this, IME needs to
262 * set pending composition string with SetPendingCompositionString(),
263 * AppendClauseToPendingComposition() and/or
264 * SetCaretInPendingComposition().
266 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
267 * be initialized with this. Otherwise, initialized
268 * with the time at initializing.
270 nsresult FlushPendingComposition(
271 nsEventStatus& aStatus, const WidgetEventTime* aEventTime = nullptr) {
272 return mPendingComposition.Flush(this, aStatus, aEventTime);
276 * ClearPendingComposition() makes this instance forget pending composition.
278 void ClearPendingComposition() { mPendingComposition.Clear(); }
281 * GetPendingCompositionClauses() returns text ranges which was appended by
282 * AppendClauseToPendingComposition() or SetPendingComposition().
284 const TextRangeArray* GetPendingCompositionClauses() const {
285 return mPendingComposition.GetClauses();
289 * @see nsIWidget::NotifyIME()
291 // Calling NotifyIME may call OS's API so that everything could happen.
292 // We should mark it MOZ_CAN_RUN_SCRIPT later.
293 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
294 NotifyIME(const IMENotification& aIMENotification);
297 * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
299 * @param aMessage Must be eKeyDown or eKeyUp.
300 * Use MaybeDispatchKeypressEvents() for dispatching
301 * eKeyPress.
302 * @param aKeyboardEvent A keyboard event.
303 * @param aStatus If dispatching event should be marked as consumed,
304 * set nsEventStatus_eConsumeNoDefault. Otherwise,
305 * set nsEventStatus_eIgnore. After dispatching
306 * a event and it's consumed this returns
307 * nsEventStatus_eConsumeNoDefault.
308 * @param aData Calling this method may cause calling
309 * WillDispatchKeyboardEvent() of the listener.
310 * aData will be set to its argument.
311 * @return true if an event is dispatched. Otherwise, false.
313 bool DispatchKeyboardEvent(EventMessage aMessage,
314 const WidgetKeyboardEvent& aKeyboardEvent,
315 nsEventStatus& aStatus, void* aData = nullptr);
318 * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
319 * generated from aKeydownEvent.
321 * @param aKeyboardEvent A keyboard event.
322 * @param aStatus Sets the result when the caller dispatches
323 * aKeyboardEvent. Note that if the value is
324 * nsEventStatus_eConsumeNoDefault, this does NOT
325 * dispatch keypress events.
326 * When this method dispatches one or more keypress
327 * events and one of them is consumed, this returns
328 * nsEventStatus_eConsumeNoDefault.
329 * @param aData Calling this method may cause calling
330 * WillDispatchKeyboardEvent() of the listener.
331 * aData will be set to its argument.
332 * @param aNeedsCallback Set true when caller needs to initialize each
333 * eKeyPress event immediately before dispatch.
334 * Then, WillDispatchKeyboardEvent() is always called.
335 * @return true if one or more events are dispatched.
336 * Otherwise, false.
338 bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
339 nsEventStatus& aStatus,
340 void* aData = nullptr,
341 bool aNeedsCallback = false);
343 private:
344 // mWidget is owner of the instance. When this is created, this is set.
345 // And when mWidget is released, this is cleared by OnDestroyWidget().
346 // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
347 // return true).
348 nsIWidget* mWidget;
349 // mListener is a weak reference to TextEventDispatcherListener. That might
350 // be referred by JS. Therefore, the listener might be difficult to release
351 // itself if this is a strong reference. Additionally, it's difficult to
352 // check if a method to uninstall the listener is called by valid instance.
353 // So, using weak reference is the best way in this case.
354 nsWeakPtr mListener;
355 // mIMENotificationRequests should store current IME's notification requests.
356 // So, this may be invalid when IME doesn't have focus.
357 IMENotificationRequests mIMENotificationRequests;
358 // mWritingMode stores writing mode at current selection starting from
359 // receiving focus notification and until receiving blur notification. When
360 // selection is changed, this is updated by every selection change
361 // notification.
362 Maybe<WritingMode> mWritingMode;
364 // mPendingComposition stores new composition string temporarily.
365 // These values will be used for dispatching eCompositionChange event
366 // in Flush(). When Flush() is called, the members will be cleared
367 // automatically.
368 class PendingComposition {
369 public:
370 PendingComposition();
371 nsresult SetString(const nsAString& aString);
372 nsresult AppendClause(uint32_t aLength, TextRangeType aTextRangeType);
373 nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
374 nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
375 nsresult Flush(TextEventDispatcher* aDispatcher, nsEventStatus& aStatus,
376 const WidgetEventTime* aEventTime);
377 const TextRangeArray* GetClauses() const { return mClauses; }
378 void Clear();
380 private:
381 nsString mString;
382 RefPtr<TextRangeArray> mClauses;
383 TextRange mCaret;
384 bool mReplacedNativeLineBreakers;
386 void EnsureClauseArray();
389 * ReplaceNativeLineBreakers() replaces "\r\n" and "\r" to "\n" and adjust
390 * each clause information and the caret information.
392 void ReplaceNativeLineBreakers();
395 * AdjustRange() adjusts aRange as in the string with XP line breakers.
397 * @param aRange The reference to a range in aNativeString.
398 * This will be modified.
399 * @param aNativeString The string with native line breakers.
400 * This may include "\r\n" and/or "\r".
402 static void AdjustRange(TextRange& aRange, const nsAString& aNativeString);
404 PendingComposition mPendingComposition;
406 // While dispatching an event, this is incremented.
407 uint16_t mDispatchingEvent;
409 enum InputTransactionType : uint8_t {
410 // No input transaction has been started.
411 eNoInputTransaction,
412 // Input transaction for native IME or keyboard event handler. Note that
413 // keyboard events may be dispatched via parent process if there is.
414 // In remote processes, this is also used when events come from the parent
415 // process and are not for tests because we cannot distinguish if
416 // TextEventDispatcher has which type of transaction when it dispatches
417 // (eNativeInputTransaction or eSameProcessSyncInputTransaction).
418 eNativeInputTransaction,
419 // Input transaction for automated tests which are APZ-aware. Note that
420 // keyboard events may be dispatched via parent process if there is.
421 eAsyncTestInputTransaction,
422 // Input transaction for automated tests which assume events are fired
423 // synchronously. I.e., keyboard events are always dispatched in the
424 // current process.
425 // In remote processes, this is also used when events come from the parent
426 // process and are not dispatched by the instance itself for APZ-aware
427 // tests because this instance won't dispatch the events via the parent
428 // process again.
429 eSameProcessSyncTestInputTransaction,
430 // Input transaction for others (currently, only FuzzingFunctions).
431 // Events are fired synchronously in the process.
432 // XXX Should we make this async for testing default action handlers in
433 // the main process?
434 eSameProcessSyncInputTransaction
437 InputTransactionType mInputTransactionType;
439 bool IsForTests() const {
440 return mInputTransactionType == eAsyncTestInputTransaction ||
441 mInputTransactionType == eSameProcessSyncTestInputTransaction;
444 // ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should
445 // be dispatched via its parent process (if there is) for APZ. Otherwise,
446 // when the input transaction is for IME of B2G or automated tests which
447 // isn't APZ-aware, WidgetInputEvent should be dispatched form current
448 // process directly.
449 bool ShouldSendInputEventToAPZ() const {
450 switch (mInputTransactionType) {
451 case eNativeInputTransaction:
452 case eAsyncTestInputTransaction:
453 return true;
454 case eSameProcessSyncTestInputTransaction:
455 case eSameProcessSyncInputTransaction:
456 return false;
457 case eNoInputTransaction:
458 NS_WARNING(
459 "Why does the caller need to dispatch an event when "
460 "there is no input transaction?");
461 return true;
462 default:
463 MOZ_CRASH("Define the behavior of new InputTransactionType");
467 // See IsComposing().
468 bool mIsComposing;
470 // See IsHandlingComposition().
471 bool mIsHandlingComposition;
473 // true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not
474 // received yet. Otherwise, false.
475 bool mHasFocus;
477 nsresult BeginInputTransactionInternal(TextEventDispatcherListener* aListener,
478 InputTransactionType aType);
481 * InitEvent() initializes aEvent. This must be called before dispatching
482 * the event.
484 void InitEvent(WidgetGUIEvent& aEvent) const;
487 * DispatchEvent() dispatches aEvent on aWidget.
489 nsresult DispatchEvent(nsIWidget* aWidget, WidgetGUIEvent& aEvent,
490 nsEventStatus& aStatus);
493 * DispatchInputEvent() dispatches aEvent on aWidget.
495 nsresult DispatchInputEvent(nsIWidget* aWidget, WidgetInputEvent& aEvent,
496 nsEventStatus& aStatus);
499 * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
500 * been started it yet.
502 * @param aStatus If it succeeded to start composition normally, this
503 * returns nsEventStatus_eIgnore. Otherwise, e.g.,
504 * the composition is canceled during dispatching
505 * compositionstart event, this returns
506 * nsEventStatus_eConsumeNoDefault. In this case,
507 * the caller shouldn't keep doing its job.
508 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
509 * be initialized with this. Otherwise, initialized
510 * with the time at initializing.
511 * @return Only when something unexpected occurs, this returns
512 * an error. Otherwise, returns NS_OK even if aStatus
513 * is nsEventStatus_eConsumeNoDefault.
515 nsresult StartCompositionAutomaticallyIfNecessary(
516 nsEventStatus& aStatus, const WidgetEventTime* aEventTime);
519 * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
521 * @param aMessage Must be eKeyDown, eKeyUp or eKeyPress.
522 * @param aKeyboardEvent A keyboard event. If aMessage is eKeyPress and
523 * the event is for second or later character, its
524 * mKeyValue should be empty string.
525 * @param aStatus If dispatching event should be marked as consumed,
526 * set nsEventStatus_eConsumeNoDefault. Otherwise,
527 * set nsEventStatus_eIgnore. After dispatching
528 * a event and it's consumed this returns
529 * nsEventStatus_eConsumeNoDefault.
530 * @param aData Calling this method may cause calling
531 * WillDispatchKeyboardEvent() of the listener.
532 * aData will be set to its argument.
533 * @param aIndexOfKeypress This must be 0 if aMessage isn't eKeyPress or
534 * aKeyboard.mKeyNameIndex isn't
535 * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
536 * when an eKeyPress event causes inputting
537 * text, this must be between 0 and
538 * mKeyValue.Length() - 1 since keypress events
539 * sending only one character per event.
540 * @param aNeedsCallback Set true when caller needs to initialize each
541 * eKeyPress event immediately before dispatch.
542 * Then, WillDispatchKeyboardEvent() is always called.
543 * @return true if an event is dispatched. Otherwise, false.
545 // TODO: Mark this as MOZ_CAN_RUN_SCRIPT instead.
546 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool DispatchKeyboardEventInternal(
547 EventMessage aMessage, const WidgetKeyboardEvent& aKeyboardEvent,
548 nsEventStatus& aStatus, void* aData, uint32_t aIndexOfKeypress = 0,
549 bool aNeedsCallback = false);
552 * ClearNotificationRequests() clears mIMENotificationRequests.
554 void ClearNotificationRequests();
557 * UpdateNotificationRequests() updates mIMENotificationRequests with
558 * current state. If the instance doesn't have focus, this clears
559 * mIMENotificationRequests. Otherwise, updates it with both requests of
560 * current listener and native listener.
562 void UpdateNotificationRequests();
565 } // namespace widget
566 } // namespace mozilla
568 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_