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"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/TextEventDispatcherListener.h"
14 #include "mozilla/TextRange.h"
15 #include "mozilla/widget/IMEData.h"
25 * TextEventDispatcher is a helper class for dispatching widget events defined
26 * in TextEvents.h. Currently, this is a helper for dispatching
27 * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior
28 * of them for conforming to DOM Level 3 Events.
29 * An instance of this class is created by nsIWidget instance and owned by it.
30 * This is typically created only by the top level widgets because only they
34 class TextEventDispatcher final
36 ~TextEventDispatcher()
40 NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher
)
43 explicit TextEventDispatcher(nsIWidget
* aWidget
);
46 * Initializes the instance for IME or automated test. Either IME or tests
47 * need to call one of them before starting composition. If they return
48 * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
49 * notifications from TextEventDispatcher for same purpose (for IME or tests).
50 * If this returns another error, the caller shouldn't keep starting
53 * @param aListener Specify the listener to listen notifications and
54 * requests. This must not be null.
55 * NOTE: aListener is stored as weak reference in
56 * TextEventDispatcher. See mListener
59 nsresult
BeginInputTransaction(TextEventDispatcherListener
* aListener
);
60 nsresult
BeginTestInputTransaction(TextEventDispatcherListener
* aListener
,
62 nsresult
BeginNativeInputTransaction();
65 * BeginInputTransactionFor() should be used when aPuppetWidget dispatches
66 * a composition or keyboard event coming from its parent process.
68 nsresult
BeginInputTransactionFor(const WidgetGUIEvent
* aEvent
,
69 PuppetWidget
* aPuppetWidget
);
72 * EndInputTransaction() should be called when the listener stops using
73 * the TextEventDispatcher.
75 * @param aListener The listener using the TextEventDispatcher instance.
77 void EndInputTransaction(TextEventDispatcherListener
* aListener
);
80 * OnDestroyWidget() is called when mWidget is being destroyed.
82 void OnDestroyWidget();
84 nsIWidget
* GetWidget() const { return mWidget
; }
86 const IMENotificationRequests
& IMENotificationRequestsRef() const
88 return mIMENotificationRequests
;
92 * GetState() returns current state of this class.
94 * @return NS_OK: Fine to compose text.
95 * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
96 * BeginInputTransactionForTests()
98 * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
101 nsresult
GetState() const;
104 * IsComposing() returns true after calling StartComposition() and before
105 * calling CommitComposition().
107 bool IsComposing() const { return mIsComposing
; }
110 * IsInNativeInputTransaction() returns true if native IME handler began a
111 * transaction and it's not finished yet.
113 bool IsInNativeInputTransaction() const
115 return mInputTransactionType
== eNativeInputTransaction
;
119 * IsDispatchingEvent() returns true while this instance dispatching an event.
121 bool IsDispatchingEvent() const { return mDispatchingEvent
> 0; }
124 * GetPseudoIMEContext() returns pseudo native IME context if there is an
125 * input transaction whose type is not for native event handler.
126 * Otherwise, returns nullptr.
128 void* GetPseudoIMEContext() const
130 if (mInputTransactionType
== eNoInputTransaction
||
131 mInputTransactionType
== eNativeInputTransaction
) {
134 return const_cast<TextEventDispatcher
*>(this);
138 * StartComposition() starts composition explicitly.
140 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
141 * be initialized with this. Otherwise, initialized
142 * with the time at initializing.
144 nsresult
StartComposition(nsEventStatus
& aStatus
,
145 const WidgetEventTime
* aEventTime
= nullptr);
148 * CommitComposition() commits composition.
150 * @param aCommitString If this is null, commits with the last composition
151 * string. Otherwise, commits the composition with
153 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
154 * be initialized with this. Otherwise, initialized
155 * with the time at initializing.
157 nsresult
CommitComposition(nsEventStatus
& aStatus
,
158 const nsAString
* aCommitString
= nullptr,
159 const WidgetEventTime
* aEventTime
= nullptr);
162 * SetPendingCompositionString() sets new composition string which will be
163 * dispatched with eCompositionChange event by calling Flush().
165 * @param aString New composition string.
167 nsresult
SetPendingCompositionString(const nsAString
& aString
)
169 return mPendingComposition
.SetString(aString
);
173 * AppendClauseToPendingComposition() appends a clause information to
174 * the pending composition string.
176 * @param aLength Length of the clause.
177 * @param aTextRangeType One of TextRangeType::eRawClause,
178 * TextRangeType::eSelectedRawClause,
179 * TextRangeType::eConvertedClause or
180 * TextRangeType::eSelectedClause.
182 nsresult
AppendClauseToPendingComposition(uint32_t aLength
,
183 TextRangeType aTextRangeType
)
185 return mPendingComposition
.AppendClause(aLength
, aTextRangeType
);
189 * SetCaretInPendingComposition() sets caret position in the pending
190 * composition string and its length. This is optional. If IME doesn't
191 * want to show caret, it shouldn't need to call this.
193 * @param aOffset Offset of the caret in the pending composition
194 * string. This should not be larger than the length
195 * of the pending composition string.
196 * @param aLength Caret width. If this is 0, caret will be collapsed.
197 * Note that Gecko doesn't supported wide caret yet,
198 * therefore, this is ignored for now.
200 nsresult
SetCaretInPendingComposition(uint32_t aOffset
,
203 return mPendingComposition
.SetCaret(aOffset
, aLength
);
207 * SetPendingComposition() is useful if native IME handler already creates
208 * array of clauses and/or caret information.
210 * @param aString Composition string. This may include native line
211 * breakers since they will be replaced with XP line
212 * breakers automatically.
213 * @param aRanges This should include the ranges of clauses and/or
214 * a range of caret. Note that this method allows
215 * some ranges overlap each other and the range order
216 * is not from start to end.
218 nsresult
SetPendingComposition(const nsAString
& aString
,
219 const TextRangeArray
* aRanges
)
221 return mPendingComposition
.Set(aString
, aRanges
);
225 * FlushPendingComposition() sends the pending composition string
226 * to the widget of the store DOM window. Before calling this, IME needs to
227 * set pending composition string with SetPendingCompositionString(),
228 * AppendClauseToPendingComposition() and/or
229 * SetCaretInPendingComposition().
231 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
232 * be initialized with this. Otherwise, initialized
233 * with the time at initializing.
235 nsresult
FlushPendingComposition(nsEventStatus
& aStatus
,
236 const WidgetEventTime
* aEventTime
= nullptr)
238 return mPendingComposition
.Flush(this, aStatus
, aEventTime
);
242 * ClearPendingComposition() makes this instance forget pending composition.
244 void ClearPendingComposition()
246 mPendingComposition
.Clear();
250 * GetPendingCompositionClauses() returns text ranges which was appended by
251 * AppendClauseToPendingComposition() or SetPendingComposition().
253 const TextRangeArray
* GetPendingCompositionClauses() const
255 return mPendingComposition
.GetClauses();
259 * @see nsIWidget::NotifyIME()
261 nsresult
NotifyIME(const IMENotification
& aIMENotification
);
264 * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
266 * @param aMessage Must be eKeyDown or eKeyUp.
267 * Use MaybeDispatchKeypressEvents() for dispatching
269 * @param aKeyboardEvent A keyboard event.
270 * @param aStatus If dispatching event should be marked as consumed,
271 * set nsEventStatus_eConsumeNoDefault. Otherwise,
272 * set nsEventStatus_eIgnore. After dispatching
273 * a event and it's consumed this returns
274 * nsEventStatus_eConsumeNoDefault.
275 * @param aData Calling this method may cause calling
276 * WillDispatchKeyboardEvent() of the listener.
277 * aData will be set to its argument.
278 * @return true if an event is dispatched. Otherwise, false.
280 bool DispatchKeyboardEvent(EventMessage aMessage
,
281 const WidgetKeyboardEvent
& aKeyboardEvent
,
282 nsEventStatus
& aStatus
,
283 void* aData
= nullptr);
286 * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
287 * generated from aKeydownEvent.
289 * @param aKeyboardEvent A keyboard event.
290 * @param aStatus Sets the result when the caller dispatches
291 * aKeyboardEvent. Note that if the value is
292 * nsEventStatus_eConsumeNoDefault, this does NOT
293 * dispatch keypress events.
294 * When this method dispatches one or more keypress
295 * events and one of them is consumed, this returns
296 * nsEventStatus_eConsumeNoDefault.
297 * @param aData Calling this method may cause calling
298 * WillDispatchKeyboardEvent() of the listener.
299 * aData will be set to its argument.
300 * @param aNeedsCallback Set true when caller needs to initialize each
301 * eKeyPress event immediately before dispatch.
302 * Then, WillDispatchKeyboardEvent() is always called.
303 * @return true if one or more events are dispatched.
306 bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent
& aKeyboardEvent
,
307 nsEventStatus
& aStatus
,
308 void* aData
= nullptr,
309 bool aNeedsCallback
= false);
312 // mWidget is owner of the instance. When this is created, this is set.
313 // And when mWidget is released, this is cleared by OnDestroyWidget().
314 // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
317 // mListener is a weak reference to TextEventDispatcherListener. That might
318 // be referred by JS. Therefore, the listener might be difficult to release
319 // itself if this is a strong reference. Additionally, it's difficult to
320 // check if a method to uninstall the listener is called by valid instance.
321 // So, using weak reference is the best way in this case.
323 // mIMENotificationRequests should store current IME's notification requests.
324 // So, this may be invalid when IME doesn't have focus.
325 IMENotificationRequests mIMENotificationRequests
;
327 // mPendingComposition stores new composition string temporarily.
328 // These values will be used for dispatching eCompositionChange event
329 // in Flush(). When Flush() is called, the members will be cleared
331 class PendingComposition
334 PendingComposition();
335 nsresult
SetString(const nsAString
& aString
);
336 nsresult
AppendClause(uint32_t aLength
, TextRangeType aTextRangeType
);
337 nsresult
SetCaret(uint32_t aOffset
, uint32_t aLength
);
338 nsresult
Set(const nsAString
& aString
, const TextRangeArray
* aRanges
);
339 nsresult
Flush(TextEventDispatcher
* aDispatcher
,
340 nsEventStatus
& aStatus
,
341 const WidgetEventTime
* aEventTime
);
342 const TextRangeArray
* GetClauses() const { return mClauses
; }
347 RefPtr
<TextRangeArray
> mClauses
;
349 bool mReplacedNativeLineBreakers
;
351 void EnsureClauseArray();
354 * ReplaceNativeLineBreakers() replaces "\r\n" and "\r" to "\n" and adjust
355 * each clause information and the caret information.
357 void ReplaceNativeLineBreakers();
360 * AdjustRange() adjusts aRange as in the string with XP line breakers.
362 * @param aRange The reference to a range in aNativeString.
363 * This will be modified.
364 * @param aNativeString The string with native line breakers.
365 * This may include "\r\n" and/or "\r".
367 static void AdjustRange(TextRange
& aRange
, const nsAString
& aNativeString
);
369 PendingComposition mPendingComposition
;
371 // While dispatching an event, this is incremented.
372 uint16_t mDispatchingEvent
;
374 enum InputTransactionType
: uint8_t
376 // No input transaction has been started.
378 // Input transaction for native IME or keyboard event handler. Note that
379 // keyboard events may be dispatched via parent process if there is.
380 // In remote processes, this is also used when events come from the parent
381 // process and are not for tests because we cannot distinguish if
382 // TextEventDispatcher has which type of transaction when it dispatches
383 // (eNativeInputTransaction or eSameProcessSyncInputTransaction).
384 eNativeInputTransaction
,
385 // Input transaction for automated tests which are APZ-aware. Note that
386 // keyboard events may be dispatched via parent process if there is.
387 eAsyncTestInputTransaction
,
388 // Input transaction for automated tests which assume events are fired
389 // synchronously. I.e., keyboard events are always dispatched in the
391 // In remote processes, this is also used when events come from the parent
392 // process and are not dispatched by the instance itself for APZ-aware
393 // tests because this instance won't dispatch the events via the parent
395 eSameProcessSyncTestInputTransaction
,
396 // Input transaction for Others (must be IME on B2G). Events are fired
397 // synchronously because TextInputProcessor which is the only user of
398 // this input transaction type supports only keyboard apps on B2G.
399 // Keyboard apps on B2G doesn't want to dispatch keyboard events to
400 // chrome process. Therefore, this should dispatch key events only in
401 // the current process.
402 eSameProcessSyncInputTransaction
405 InputTransactionType mInputTransactionType
;
407 bool IsForTests() const
409 return mInputTransactionType
== eAsyncTestInputTransaction
||
410 mInputTransactionType
== eSameProcessSyncTestInputTransaction
;
413 // ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should
414 // be dispatched via its parent process (if there is) for APZ. Otherwise,
415 // when the input transaction is for IME of B2G or automated tests which
416 // isn't APZ-aware, WidgetInputEvent should be dispatched form current
418 bool ShouldSendInputEventToAPZ() const
420 switch (mInputTransactionType
) {
421 case eNativeInputTransaction
:
422 case eAsyncTestInputTransaction
:
424 case eSameProcessSyncTestInputTransaction
:
425 case eSameProcessSyncInputTransaction
:
427 case eNoInputTransaction
:
428 NS_WARNING("Why does the caller need to dispatch an event when "
429 "there is no input transaction?");
432 MOZ_CRASH("Define the behavior of new InputTransactionType");
436 // See IsComposing().
439 // true while NOTIFY_IME_OF_FOCUS is received but NOTIFY_IME_OF_BLUR has not
440 // received yet. Otherwise, false.
443 // If this is true, keydown and keyup events are dispatched even when there
445 static bool sDispatchKeyEventsDuringComposition
;
447 nsresult
BeginInputTransactionInternal(
448 TextEventDispatcherListener
* aListener
,
449 InputTransactionType aType
);
452 * InitEvent() initializes aEvent. This must be called before dispatching
455 void InitEvent(WidgetGUIEvent
& aEvent
) const;
459 * DispatchEvent() dispatches aEvent on aWidget.
461 nsresult
DispatchEvent(nsIWidget
* aWidget
,
462 WidgetGUIEvent
& aEvent
,
463 nsEventStatus
& aStatus
);
466 * DispatchInputEvent() dispatches aEvent on aWidget.
468 nsresult
DispatchInputEvent(nsIWidget
* aWidget
,
469 WidgetInputEvent
& aEvent
,
470 nsEventStatus
& aStatus
);
473 * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
474 * been started it yet.
476 * @param aStatus If it succeeded to start composition normally, this
477 * returns nsEventStatus_eIgnore. Otherwise, e.g.,
478 * the composition is canceled during dispatching
479 * compositionstart event, this returns
480 * nsEventStatus_eConsumeNoDefault. In this case,
481 * the caller shouldn't keep doing its job.
482 * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
483 * be initialized with this. Otherwise, initialized
484 * with the time at initializing.
485 * @return Only when something unexpected occurs, this returns
486 * an error. Otherwise, returns NS_OK even if aStatus
487 * is nsEventStatus_eConsumeNoDefault.
489 nsresult
StartCompositionAutomaticallyIfNecessary(
490 nsEventStatus
& aStatus
,
491 const WidgetEventTime
* aEventTime
);
494 * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
496 * @param aMessage Must be eKeyDown, eKeyUp or eKeyPress.
497 * @param aKeyboardEvent A keyboard event. If aMessage is eKeyPress and
498 * the event is for second or later character, its
499 * mKeyValue should be empty string.
500 * @param aStatus If dispatching event should be marked as consumed,
501 * set nsEventStatus_eConsumeNoDefault. Otherwise,
502 * set nsEventStatus_eIgnore. After dispatching
503 * a event and it's consumed this returns
504 * nsEventStatus_eConsumeNoDefault.
505 * @param aData Calling this method may cause calling
506 * WillDispatchKeyboardEvent() of the listener.
507 * aData will be set to its argument.
508 * @param aIndexOfKeypress This must be 0 if aMessage isn't eKeyPress or
509 * aKeyboard.mKeyNameIndex isn't
510 * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
511 * when an eKeyPress event causes inputting
512 * text, this must be between 0 and
513 * mKeyValue.Length() - 1 since keypress events
514 * sending only one character per event.
515 * @param aNeedsCallback Set true when caller needs to initialize each
516 * eKeyPress event immediately before dispatch.
517 * Then, WillDispatchKeyboardEvent() is always called.
518 * @return true if an event is dispatched. Otherwise, false.
520 bool DispatchKeyboardEventInternal(EventMessage aMessage
,
521 const WidgetKeyboardEvent
& aKeyboardEvent
,
522 nsEventStatus
& aStatus
,
524 uint32_t aIndexOfKeypress
= 0,
525 bool aNeedsCallback
= false);
528 * ClearNotificationRequests() clears mIMENotificationRequests.
530 void ClearNotificationRequests();
533 * UpdateNotificationRequests() updates mIMENotificationRequests with
534 * current state. If the instance doesn't have focus, this clears
535 * mIMENotificationRequests. Otherwise, updates it with both requests of
536 * current listener and native listener.
538 void UpdateNotificationRequests();
541 } // namespace widget
542 } // namespace mozilla
544 #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_