TextEventDispatcher.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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/. */
  5. #ifndef mozilla_textcompositionsynthesizer_h_
  6. #define mozilla_textcompositionsynthesizer_h_
  7. #include "mozilla/RefPtr.h"
  8. #include "nsString.h"
  9. #include "mozilla/Attributes.h"
  10. #include "mozilla/EventForwards.h"
  11. #include "mozilla/TextEventDispatcherListener.h"
  12. #include "mozilla/TextRange.h"
  13. class nsIWidget;
  14. namespace mozilla {
  15. namespace widget {
  16. struct IMENotification;
  17. /**
  18. * TextEventDispatcher is a helper class for dispatching widget events defined
  19. * in TextEvents.h. Currently, this is a helper for dispatching
  20. * WidgetCompositionEvent and WidgetKeyboardEvent. This manages the behavior
  21. * of them for conforming to DOM Level 3 Events.
  22. * An instance of this class is created by nsIWidget instance and owned by it.
  23. * This is typically created only by the top level widgets because only they
  24. * handle IME.
  25. */
  26. class TextEventDispatcher final
  27. {
  28. ~TextEventDispatcher()
  29. {
  30. }
  31. NS_INLINE_DECL_REFCOUNTING(TextEventDispatcher)
  32. public:
  33. explicit TextEventDispatcher(nsIWidget* aWidget);
  34. /**
  35. * Initializes the instance for IME or automated test. Either IME or tests
  36. * need to call one of them before starting composition. If they return
  37. * NS_ERROR_ALREADY_INITIALIZED, it means that the listener already listens
  38. * notifications from TextEventDispatcher for same purpose (for IME or tests).
  39. * If this returns another error, the caller shouldn't keep starting
  40. * composition.
  41. *
  42. * @param aListener Specify the listener to listen notifications and
  43. * requests. This must not be null.
  44. * NOTE: aListener is stored as weak reference in
  45. * TextEventDispatcher. See mListener
  46. * definition below.
  47. */
  48. nsresult BeginInputTransaction(TextEventDispatcherListener* aListener);
  49. nsresult BeginTestInputTransaction(TextEventDispatcherListener* aListener,
  50. bool aIsAPZAware);
  51. nsresult BeginNativeInputTransaction();
  52. /**
  53. * EndInputTransaction() should be called when the listener stops using
  54. * the TextEventDispatcher.
  55. *
  56. * @param aListener The listener using the TextEventDispatcher instance.
  57. */
  58. void EndInputTransaction(TextEventDispatcherListener* aListener);
  59. /**
  60. * OnDestroyWidget() is called when mWidget is being destroyed.
  61. */
  62. void OnDestroyWidget();
  63. nsIWidget* GetWidget() const { return mWidget; }
  64. /**
  65. * GetState() returns current state of this class.
  66. *
  67. * @return NS_OK: Fine to compose text.
  68. * NS_ERROR_NOT_INITIALIZED: BeginInputTransaction() or
  69. * BeginInputTransactionForTests()
  70. * should be called.
  71. * NS_ERROR_NOT_AVAILABLE: The widget isn't available for
  72. * composition.
  73. */
  74. nsresult GetState() const;
  75. /**
  76. * IsComposing() returns true after calling StartComposition() and before
  77. * calling CommitComposition().
  78. */
  79. bool IsComposing() const { return mIsComposing; }
  80. /**
  81. * IsInNativeInputTransaction() returns true if native IME handler began a
  82. * transaction and it's not finished yet.
  83. */
  84. bool IsInNativeInputTransaction() const
  85. {
  86. return mInputTransactionType == eNativeInputTransaction;
  87. }
  88. /**
  89. * IsDispatchingEvent() returns true while this instance dispatching an event.
  90. */
  91. bool IsDispatchingEvent() const { return mDispatchingEvent > 0; }
  92. /**
  93. * GetPseudoIMEContext() returns pseudo native IME context if there is an
  94. * input transaction whose type is not for native event handler.
  95. * Otherwise, returns nullptr.
  96. */
  97. void* GetPseudoIMEContext() const
  98. {
  99. if (mInputTransactionType == eNoInputTransaction ||
  100. mInputTransactionType == eNativeInputTransaction) {
  101. return nullptr;
  102. }
  103. return const_cast<TextEventDispatcher*>(this);
  104. }
  105. /**
  106. * StartComposition() starts composition explicitly.
  107. *
  108. * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
  109. * be initialized with this. Otherwise, initialized
  110. * with the time at initializing.
  111. */
  112. nsresult StartComposition(nsEventStatus& aStatus,
  113. const WidgetEventTime* aEventTime = nullptr);
  114. /**
  115. * CommitComposition() commits composition.
  116. *
  117. * @param aCommitString If this is null, commits with the last composition
  118. * string. Otherwise, commits the composition with
  119. * this value.
  120. * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
  121. * be initialized with this. Otherwise, initialized
  122. * with the time at initializing.
  123. */
  124. nsresult CommitComposition(nsEventStatus& aStatus,
  125. const nsAString* aCommitString = nullptr,
  126. const WidgetEventTime* aEventTime = nullptr);
  127. /**
  128. * SetPendingCompositionString() sets new composition string which will be
  129. * dispatched with eCompositionChange event by calling Flush().
  130. *
  131. * @param aString New composition string.
  132. */
  133. nsresult SetPendingCompositionString(const nsAString& aString)
  134. {
  135. return mPendingComposition.SetString(aString);
  136. }
  137. /**
  138. * AppendClauseToPendingComposition() appends a clause information to
  139. * the pending composition string.
  140. *
  141. * @param aLength Length of the clause.
  142. * @param aTextRangeType One of TextRangeType::eRawClause,
  143. * TextRangeType::eSelectedRawClause,
  144. * TextRangeType::eConvertedClause or
  145. * TextRangeType::eSelectedClause.
  146. */
  147. nsresult AppendClauseToPendingComposition(uint32_t aLength,
  148. TextRangeType aTextRangeType)
  149. {
  150. return mPendingComposition.AppendClause(aLength, aTextRangeType);
  151. }
  152. /**
  153. * SetCaretInPendingComposition() sets caret position in the pending
  154. * composition string and its length. This is optional. If IME doesn't
  155. * want to show caret, it shouldn't need to call this.
  156. *
  157. * @param aOffset Offset of the caret in the pending composition
  158. * string. This should not be larger than the length
  159. * of the pending composition string.
  160. * @param aLength Caret width. If this is 0, caret will be collapsed.
  161. * Note that Gecko doesn't supported wide caret yet,
  162. * therefore, this is ignored for now.
  163. */
  164. nsresult SetCaretInPendingComposition(uint32_t aOffset,
  165. uint32_t aLength)
  166. {
  167. return mPendingComposition.SetCaret(aOffset, aLength);
  168. }
  169. /**
  170. * SetPendingComposition() is useful if native IME handler already creates
  171. * array of clauses and/or caret information.
  172. *
  173. * @param aString Composition string. This may include native line
  174. * breakers since they will be replaced with XP line
  175. * breakers automatically.
  176. * @param aRanges This should include the ranges of clauses and/or
  177. * a range of caret. Note that this method allows
  178. * some ranges overlap each other and the range order
  179. * is not from start to end.
  180. */
  181. nsresult SetPendingComposition(const nsAString& aString,
  182. const TextRangeArray* aRanges)
  183. {
  184. return mPendingComposition.Set(aString, aRanges);
  185. }
  186. /**
  187. * FlushPendingComposition() sends the pending composition string
  188. * to the widget of the store DOM window. Before calling this, IME needs to
  189. * set pending composition string with SetPendingCompositionString(),
  190. * AppendClauseToPendingComposition() and/or
  191. * SetCaretInPendingComposition().
  192. *
  193. * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
  194. * be initialized with this. Otherwise, initialized
  195. * with the time at initializing.
  196. */
  197. nsresult FlushPendingComposition(nsEventStatus& aStatus,
  198. const WidgetEventTime* aEventTime = nullptr)
  199. {
  200. return mPendingComposition.Flush(this, aStatus, aEventTime);
  201. }
  202. /**
  203. * ClearPendingComposition() makes this instance forget pending composition.
  204. */
  205. void ClearPendingComposition()
  206. {
  207. mPendingComposition.Clear();
  208. }
  209. /**
  210. * GetPendingCompositionClauses() returns text ranges which was appended by
  211. * AppendClauseToPendingComposition() or SetPendingComposition().
  212. */
  213. const TextRangeArray* GetPendingCompositionClauses() const
  214. {
  215. return mPendingComposition.GetClauses();
  216. }
  217. /**
  218. * @see nsIWidget::NotifyIME()
  219. */
  220. nsresult NotifyIME(const IMENotification& aIMENotification);
  221. /**
  222. * DispatchKeyboardEvent() maybe dispatches aKeyboardEvent.
  223. *
  224. * @param aMessage Must be eKeyDown or eKeyUp.
  225. * Use MaybeDispatchKeypressEvents() for dispatching
  226. * eKeyPress.
  227. * @param aKeyboardEvent A keyboard event.
  228. * @param aStatus If dispatching event should be marked as consumed,
  229. * set nsEventStatus_eConsumeNoDefault. Otherwise,
  230. * set nsEventStatus_eIgnore. After dispatching
  231. * a event and it's consumed this returns
  232. * nsEventStatus_eConsumeNoDefault.
  233. * @param aData Calling this method may cause calling
  234. * WillDispatchKeyboardEvent() of the listener.
  235. * aData will be set to its argument.
  236. * @return true if an event is dispatched. Otherwise, false.
  237. */
  238. bool DispatchKeyboardEvent(EventMessage aMessage,
  239. const WidgetKeyboardEvent& aKeyboardEvent,
  240. nsEventStatus& aStatus,
  241. void* aData = nullptr);
  242. /**
  243. * MaybeDispatchKeypressEvents() maybe dispatches a keypress event which is
  244. * generated from aKeydownEvent.
  245. *
  246. * @param aKeyboardEvent A keyboard event.
  247. * @param aStatus Sets the result when the caller dispatches
  248. * aKeyboardEvent. Note that if the value is
  249. * nsEventStatus_eConsumeNoDefault, this does NOT
  250. * dispatch keypress events.
  251. * When this method dispatches one or more keypress
  252. * events and one of them is consumed, this returns
  253. * nsEventStatus_eConsumeNoDefault.
  254. * @param aData Calling this method may cause calling
  255. * WillDispatchKeyboardEvent() of the listener.
  256. * aData will be set to its argument.
  257. * @param aNeedsCallback Set true when caller needs to initialize each
  258. * eKeyPress event immediately before dispatch.
  259. * Then, WillDispatchKeyboardEvent() is always called.
  260. * @return true if one or more events are dispatched.
  261. * Otherwise, false.
  262. */
  263. bool MaybeDispatchKeypressEvents(const WidgetKeyboardEvent& aKeyboardEvent,
  264. nsEventStatus& aStatus,
  265. void* aData = nullptr,
  266. bool aNeedsCallback = false);
  267. private:
  268. // mWidget is owner of the instance. When this is created, this is set.
  269. // And when mWidget is released, this is cleared by OnDestroyWidget().
  270. // Note that mWidget may be destroyed already (i.e., mWidget->Destroyed() may
  271. // return true).
  272. nsIWidget* mWidget;
  273. // mListener is a weak reference to TextEventDispatcherListener. That might
  274. // be referred by JS. Therefore, the listener might be difficult to release
  275. // itself if this is a strong reference. Additionally, it's difficult to
  276. // check if a method to uninstall the listener is called by valid instance.
  277. // So, using weak reference is the best way in this case.
  278. nsWeakPtr mListener;
  279. // mPendingComposition stores new composition string temporarily.
  280. // These values will be used for dispatching eCompositionChange event
  281. // in Flush(). When Flush() is called, the members will be cleared
  282. // automatically.
  283. class PendingComposition
  284. {
  285. public:
  286. PendingComposition();
  287. nsresult SetString(const nsAString& aString);
  288. nsresult AppendClause(uint32_t aLength, TextRangeType aTextRangeType);
  289. nsresult SetCaret(uint32_t aOffset, uint32_t aLength);
  290. nsresult Set(const nsAString& aString, const TextRangeArray* aRanges);
  291. nsresult Flush(TextEventDispatcher* aDispatcher,
  292. nsEventStatus& aStatus,
  293. const WidgetEventTime* aEventTime);
  294. const TextRangeArray* GetClauses() const { return mClauses; }
  295. void Clear();
  296. private:
  297. nsString mString;
  298. RefPtr<TextRangeArray> mClauses;
  299. TextRange mCaret;
  300. void EnsureClauseArray();
  301. };
  302. PendingComposition mPendingComposition;
  303. // While dispatching an event, this is incremented.
  304. uint16_t mDispatchingEvent;
  305. enum InputTransactionType : uint8_t
  306. {
  307. // No input transaction has been started.
  308. eNoInputTransaction,
  309. // Input transaction for native IME or keyboard event handler. Note that
  310. // keyboard events may be dispatched via parent process if there is.
  311. eNativeInputTransaction,
  312. // Input transaction for automated tests which are APZ-aware. Note that
  313. // keyboard events may be dispatched via parent process if there is.
  314. eAsyncTestInputTransaction,
  315. // Input transaction for automated tests which assume events are fired
  316. // synchronously. I.e., keyboard events are always dispatched in the
  317. // current process.
  318. eSameProcessSyncTestInputTransaction,
  319. // Input transaction for Others (must be IME on B2G). Events are fired
  320. // synchronously because TextInputProcessor which is the only user of
  321. // this input transaction type supports only keyboard apps on B2G.
  322. // Keyboard apps on B2G doesn't want to dispatch keyboard events to
  323. // chrome process. Therefore, this should dispatch key events only in
  324. // the current process.
  325. eSameProcessSyncInputTransaction
  326. };
  327. InputTransactionType mInputTransactionType;
  328. bool IsForTests() const
  329. {
  330. return mInputTransactionType == eAsyncTestInputTransaction ||
  331. mInputTransactionType == eSameProcessSyncTestInputTransaction;
  332. }
  333. // ShouldSendInputEventToAPZ() returns true when WidgetInputEvent should
  334. // be dispatched via its parent process (if there is) for APZ. Otherwise,
  335. // when the input transaction is for IME of B2G or automated tests which
  336. // isn't APZ-aware, WidgetInputEvent should be dispatched form current
  337. // process directly.
  338. bool ShouldSendInputEventToAPZ() const
  339. {
  340. switch (mInputTransactionType) {
  341. case eNativeInputTransaction:
  342. case eAsyncTestInputTransaction:
  343. return true;
  344. case eSameProcessSyncTestInputTransaction:
  345. case eSameProcessSyncInputTransaction:
  346. return false;
  347. case eNoInputTransaction:
  348. NS_WARNING("Why does the caller need to dispatch an event when "
  349. "there is no input transaction?");
  350. return true;
  351. default:
  352. MOZ_CRASH("Define the behavior of new InputTransactionType");
  353. }
  354. }
  355. // See IsComposing().
  356. bool mIsComposing;
  357. // If this is true, keydown and keyup events are dispatched even when there
  358. // is a composition.
  359. static bool sDispatchKeyEventsDuringComposition;
  360. nsresult BeginInputTransactionInternal(
  361. TextEventDispatcherListener* aListener,
  362. InputTransactionType aType);
  363. /**
  364. * InitEvent() initializes aEvent. This must be called before dispatching
  365. * the event.
  366. */
  367. void InitEvent(WidgetGUIEvent& aEvent) const;
  368. /**
  369. * DispatchEvent() dispatches aEvent on aWidget.
  370. */
  371. nsresult DispatchEvent(nsIWidget* aWidget,
  372. WidgetGUIEvent& aEvent,
  373. nsEventStatus& aStatus);
  374. /**
  375. * DispatchInputEvent() dispatches aEvent on aWidget.
  376. */
  377. nsresult DispatchInputEvent(nsIWidget* aWidget,
  378. WidgetInputEvent& aEvent,
  379. nsEventStatus& aStatus);
  380. /**
  381. * StartCompositionAutomaticallyIfNecessary() starts composition if it hasn't
  382. * been started it yet.
  383. *
  384. * @param aStatus If it succeeded to start composition normally, this
  385. * returns nsEventStatus_eIgnore. Otherwise, e.g.,
  386. * the composition is canceled during dispatching
  387. * compositionstart event, this returns
  388. * nsEventStatus_eConsumeNoDefault. In this case,
  389. * the caller shouldn't keep doing its job.
  390. * @param aEventTime If this is not nullptr, WidgetCompositionEvent will
  391. * be initialized with this. Otherwise, initialized
  392. * with the time at initializing.
  393. * @return Only when something unexpected occurs, this returns
  394. * an error. Otherwise, returns NS_OK even if aStatus
  395. * is nsEventStatus_eConsumeNoDefault.
  396. */
  397. nsresult StartCompositionAutomaticallyIfNecessary(
  398. nsEventStatus& aStatus,
  399. const WidgetEventTime* aEventTime);
  400. /**
  401. * DispatchKeyboardEventInternal() maybe dispatches aKeyboardEvent.
  402. *
  403. * @param aMessage Must be eKeyDown, eKeyUp or eKeyPress.
  404. * @param aKeyboardEvent A keyboard event. If aMessage is eKeyPress and
  405. * the event is for second or later character, its
  406. * mKeyValue should be empty string.
  407. * @param aStatus If dispatching event should be marked as consumed,
  408. * set nsEventStatus_eConsumeNoDefault. Otherwise,
  409. * set nsEventStatus_eIgnore. After dispatching
  410. * a event and it's consumed this returns
  411. * nsEventStatus_eConsumeNoDefault.
  412. * @param aData Calling this method may cause calling
  413. * WillDispatchKeyboardEvent() of the listener.
  414. * aData will be set to its argument.
  415. * @param aIndexOfKeypress This must be 0 if aMessage isn't eKeyPress or
  416. * aKeyboard.mKeyNameIndex isn't
  417. * KEY_NAME_INDEX_USE_STRING. Otherwise, i.e.,
  418. * when an eKeyPress event causes inputting
  419. * text, this must be between 0 and
  420. * mKeyValue.Length() - 1 since keypress events
  421. * sending only one character per event.
  422. * @param aNeedsCallback Set true when caller needs to initialize each
  423. * eKeyPress event immediately before dispatch.
  424. * Then, WillDispatchKeyboardEvent() is always called.
  425. * @return true if an event is dispatched. Otherwise, false.
  426. */
  427. bool DispatchKeyboardEventInternal(EventMessage aMessage,
  428. const WidgetKeyboardEvent& aKeyboardEvent,
  429. nsEventStatus& aStatus,
  430. void* aData,
  431. uint32_t aIndexOfKeypress = 0,
  432. bool aNeedsCallback = false);
  433. };
  434. } // namespace widget
  435. } // namespace mozilla
  436. #endif // #ifndef mozilla_widget_textcompositionsynthesizer_h_