TextEventDispatcher.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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. #include "mozilla/Preferences.h"
  6. #include "mozilla/TextEvents.h"
  7. #include "mozilla/TextEventDispatcher.h"
  8. #include "nsIDocShell.h"
  9. #include "nsIFrame.h"
  10. #include "nsIPresShell.h"
  11. #include "nsIWidget.h"
  12. #include "nsPIDOMWindow.h"
  13. #include "nsView.h"
  14. namespace mozilla {
  15. namespace widget {
  16. /******************************************************************************
  17. * TextEventDispatcher
  18. *****************************************************************************/
  19. bool TextEventDispatcher::sDispatchKeyEventsDuringComposition = false;
  20. TextEventDispatcher::TextEventDispatcher(nsIWidget* aWidget)
  21. : mWidget(aWidget)
  22. , mDispatchingEvent(0)
  23. , mInputTransactionType(eNoInputTransaction)
  24. , mIsComposing(false)
  25. {
  26. MOZ_RELEASE_ASSERT(mWidget, "aWidget must not be nullptr");
  27. static bool sInitialized = false;
  28. if (!sInitialized) {
  29. Preferences::AddBoolVarCache(
  30. &sDispatchKeyEventsDuringComposition,
  31. "dom.keyboardevent.dispatch_during_composition",
  32. false);
  33. sInitialized = true;
  34. }
  35. }
  36. nsresult
  37. TextEventDispatcher::BeginInputTransaction(
  38. TextEventDispatcherListener* aListener)
  39. {
  40. return BeginInputTransactionInternal(aListener,
  41. eSameProcessSyncInputTransaction);
  42. }
  43. nsresult
  44. TextEventDispatcher::BeginTestInputTransaction(
  45. TextEventDispatcherListener* aListener,
  46. bool aIsAPZAware)
  47. {
  48. return BeginInputTransactionInternal(aListener,
  49. aIsAPZAware ? eAsyncTestInputTransaction :
  50. eSameProcessSyncTestInputTransaction);
  51. }
  52. nsresult
  53. TextEventDispatcher::BeginNativeInputTransaction()
  54. {
  55. if (NS_WARN_IF(!mWidget)) {
  56. return NS_ERROR_FAILURE;
  57. }
  58. RefPtr<TextEventDispatcherListener> listener =
  59. mWidget->GetNativeTextEventDispatcherListener();
  60. if (NS_WARN_IF(!listener)) {
  61. return NS_ERROR_FAILURE;
  62. }
  63. return BeginInputTransactionInternal(listener, eNativeInputTransaction);
  64. }
  65. nsresult
  66. TextEventDispatcher::BeginInputTransactionInternal(
  67. TextEventDispatcherListener* aListener,
  68. InputTransactionType aType)
  69. {
  70. if (NS_WARN_IF(!aListener)) {
  71. return NS_ERROR_INVALID_ARG;
  72. }
  73. nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
  74. if (listener) {
  75. if (listener == aListener && mInputTransactionType == aType) {
  76. return NS_OK;
  77. }
  78. // If this has composition or is dispatching an event, any other listener
  79. // can steal ownership. Especially, if the latter case is allowed,
  80. // nobody cannot begin input transaction with this if a modal dialog is
  81. // opened during dispatching an event.
  82. if (IsComposing() || IsDispatchingEvent()) {
  83. return NS_ERROR_ALREADY_INITIALIZED;
  84. }
  85. }
  86. mListener = do_GetWeakReference(aListener);
  87. mInputTransactionType = aType;
  88. if (listener && listener != aListener) {
  89. listener->OnRemovedFrom(this);
  90. }
  91. return NS_OK;
  92. }
  93. void
  94. TextEventDispatcher::EndInputTransaction(TextEventDispatcherListener* aListener)
  95. {
  96. if (NS_WARN_IF(IsComposing()) || NS_WARN_IF(IsDispatchingEvent())) {
  97. return;
  98. }
  99. mInputTransactionType = eNoInputTransaction;
  100. nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
  101. if (NS_WARN_IF(!listener)) {
  102. return;
  103. }
  104. if (NS_WARN_IF(listener != aListener)) {
  105. return;
  106. }
  107. mListener = nullptr;
  108. listener->OnRemovedFrom(this);
  109. }
  110. void
  111. TextEventDispatcher::OnDestroyWidget()
  112. {
  113. mWidget = nullptr;
  114. mPendingComposition.Clear();
  115. nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
  116. mListener = nullptr;
  117. mInputTransactionType = eNoInputTransaction;
  118. if (listener) {
  119. listener->OnRemovedFrom(this);
  120. }
  121. }
  122. nsresult
  123. TextEventDispatcher::GetState() const
  124. {
  125. nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
  126. if (!listener) {
  127. return NS_ERROR_NOT_INITIALIZED;
  128. }
  129. if (!mWidget || mWidget->Destroyed()) {
  130. return NS_ERROR_NOT_AVAILABLE;
  131. }
  132. return NS_OK;
  133. }
  134. void
  135. TextEventDispatcher::InitEvent(WidgetGUIEvent& aEvent) const
  136. {
  137. aEvent.mTime = PR_IntervalNow();
  138. aEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
  139. aEvent.mFlags.mIsSynthesizedForTests = IsForTests();
  140. if (aEvent.mClass != eCompositionEventClass) {
  141. return;
  142. }
  143. void* pseudoIMEContext = GetPseudoIMEContext();
  144. if (pseudoIMEContext) {
  145. aEvent.AsCompositionEvent()->mNativeIMEContext.
  146. InitWithRawNativeIMEContext(pseudoIMEContext);
  147. }
  148. #ifdef DEBUG
  149. else {
  150. MOZ_ASSERT(!XRE_IsContentProcess(),
  151. "Why did the content process start native event transaction?");
  152. MOZ_ASSERT(aEvent.AsCompositionEvent()->mNativeIMEContext.IsValid(),
  153. "Native IME context shouldn't be invalid");
  154. }
  155. #endif // #ifdef DEBUG
  156. }
  157. nsresult
  158. TextEventDispatcher::DispatchEvent(nsIWidget* aWidget,
  159. WidgetGUIEvent& aEvent,
  160. nsEventStatus& aStatus)
  161. {
  162. MOZ_ASSERT(!aEvent.AsInputEvent(), "Use DispatchInputEvent()");
  163. RefPtr<TextEventDispatcher> kungFuDeathGrip(this);
  164. nsCOMPtr<nsIWidget> widget(aWidget);
  165. mDispatchingEvent++;
  166. nsresult rv = widget->DispatchEvent(&aEvent, aStatus);
  167. mDispatchingEvent--;
  168. return rv;
  169. }
  170. nsresult
  171. TextEventDispatcher::DispatchInputEvent(nsIWidget* aWidget,
  172. WidgetInputEvent& aEvent,
  173. nsEventStatus& aStatus)
  174. {
  175. RefPtr<TextEventDispatcher> kungFuDeathGrip(this);
  176. nsCOMPtr<nsIWidget> widget(aWidget);
  177. mDispatchingEvent++;
  178. // If the event is dispatched via nsIWidget::DispatchInputEvent(), it
  179. // sends the event to the parent process first since APZ needs to handle it
  180. // first. However, some callers (e.g., keyboard apps on B2G and tests
  181. // expecting synchronous dispatch) don't want this to do that.
  182. nsresult rv = NS_OK;
  183. if (ShouldSendInputEventToAPZ()) {
  184. aStatus = widget->DispatchInputEvent(&aEvent);
  185. } else {
  186. rv = widget->DispatchEvent(&aEvent, aStatus);
  187. }
  188. mDispatchingEvent--;
  189. return rv;
  190. }
  191. nsresult
  192. TextEventDispatcher::StartComposition(nsEventStatus& aStatus,
  193. const WidgetEventTime* aEventTime)
  194. {
  195. aStatus = nsEventStatus_eIgnore;
  196. nsresult rv = GetState();
  197. if (NS_WARN_IF(NS_FAILED(rv))) {
  198. return rv;
  199. }
  200. if (NS_WARN_IF(mIsComposing)) {
  201. return NS_ERROR_FAILURE;
  202. }
  203. mIsComposing = true;
  204. WidgetCompositionEvent compositionStartEvent(true, eCompositionStart,
  205. mWidget);
  206. InitEvent(compositionStartEvent);
  207. if (aEventTime) {
  208. compositionStartEvent.AssignEventTime(*aEventTime);
  209. }
  210. rv = DispatchEvent(mWidget, compositionStartEvent, aStatus);
  211. if (NS_WARN_IF(NS_FAILED(rv))) {
  212. return rv;
  213. }
  214. return NS_OK;
  215. }
  216. nsresult
  217. TextEventDispatcher::StartCompositionAutomaticallyIfNecessary(
  218. nsEventStatus& aStatus,
  219. const WidgetEventTime* aEventTime)
  220. {
  221. if (IsComposing()) {
  222. return NS_OK;
  223. }
  224. nsresult rv = StartComposition(aStatus, aEventTime);
  225. if (NS_WARN_IF(NS_FAILED(rv))) {
  226. return rv;
  227. }
  228. // If started composition has already been committed, we shouldn't dispatch
  229. // the compositionchange event.
  230. if (!IsComposing()) {
  231. aStatus = nsEventStatus_eConsumeNoDefault;
  232. return NS_OK;
  233. }
  234. // Note that the widget might be destroyed during a call of
  235. // StartComposition(). In such case, we shouldn't keep dispatching next
  236. // event.
  237. rv = GetState();
  238. if (NS_FAILED(rv)) {
  239. MOZ_ASSERT(rv != NS_ERROR_NOT_INITIALIZED,
  240. "aDispatcher must still be initialized in this case");
  241. aStatus = nsEventStatus_eConsumeNoDefault;
  242. return NS_OK; // Don't throw exception in this case
  243. }
  244. aStatus = nsEventStatus_eIgnore;
  245. return NS_OK;
  246. }
  247. nsresult
  248. TextEventDispatcher::CommitComposition(nsEventStatus& aStatus,
  249. const nsAString* aCommitString,
  250. const WidgetEventTime* aEventTime)
  251. {
  252. aStatus = nsEventStatus_eIgnore;
  253. nsresult rv = GetState();
  254. if (NS_WARN_IF(NS_FAILED(rv))) {
  255. return rv;
  256. }
  257. // When there is no composition, caller shouldn't try to commit composition
  258. // with non-existing composition string nor commit composition with empty
  259. // string.
  260. if (NS_WARN_IF(!IsComposing() &&
  261. (!aCommitString || aCommitString->IsEmpty()))) {
  262. return NS_ERROR_FAILURE;
  263. }
  264. nsCOMPtr<nsIWidget> widget(mWidget);
  265. rv = StartCompositionAutomaticallyIfNecessary(aStatus, aEventTime);
  266. if (NS_WARN_IF(NS_FAILED(rv))) {
  267. return rv;
  268. }
  269. if (aStatus == nsEventStatus_eConsumeNoDefault) {
  270. return NS_OK;
  271. }
  272. // End current composition and make this free for other IMEs.
  273. mIsComposing = false;
  274. EventMessage message = aCommitString ? eCompositionCommit :
  275. eCompositionCommitAsIs;
  276. WidgetCompositionEvent compositionCommitEvent(true, message, widget);
  277. InitEvent(compositionCommitEvent);
  278. if (aEventTime) {
  279. compositionCommitEvent.AssignEventTime(*aEventTime);
  280. }
  281. if (message == eCompositionCommit) {
  282. compositionCommitEvent.mData = *aCommitString;
  283. // Don't send CRLF, replace it with LF here.
  284. compositionCommitEvent.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  285. NS_LITERAL_STRING("\n"));
  286. }
  287. rv = DispatchEvent(widget, compositionCommitEvent, aStatus);
  288. if (NS_WARN_IF(NS_FAILED(rv))) {
  289. return rv;
  290. }
  291. return NS_OK;
  292. }
  293. nsresult
  294. TextEventDispatcher::NotifyIME(const IMENotification& aIMENotification)
  295. {
  296. nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
  297. // First, send the notification to current input transaction's listener.
  298. nsCOMPtr<TextEventDispatcherListener> listener = do_QueryReferent(mListener);
  299. if (listener) {
  300. rv = listener->NotifyIME(this, aIMENotification);
  301. }
  302. if (mInputTransactionType == eNativeInputTransaction || !mWidget) {
  303. return rv;
  304. }
  305. // If current input transaction isn't for native event handler, we should
  306. // send the notification to the native text event dispatcher listener
  307. // since native event handler may need to do something from
  308. // TextEventDispatcherListener::NotifyIME() even before there is no
  309. // input transaction yet. For example, native IME handler may need to
  310. // create new context at receiving NOTIFY_IME_OF_FOCUS. In this case,
  311. // mListener may not be initialized since input transaction should be
  312. // initialized immediately before dispatching every WidgetKeyboardEvent
  313. // and WidgetCompositionEvent (dispatching events always occurs after
  314. // focus move).
  315. nsCOMPtr<TextEventDispatcherListener> nativeListener =
  316. mWidget->GetNativeTextEventDispatcherListener();
  317. if (!nativeListener) {
  318. return rv;
  319. }
  320. switch (aIMENotification.mMessage) {
  321. case REQUEST_TO_COMMIT_COMPOSITION:
  322. case REQUEST_TO_CANCEL_COMPOSITION:
  323. // It's not necessary to notify native IME of requests.
  324. return rv;
  325. default: {
  326. // Even if current input transaction's listener returns NS_OK or
  327. // something, we need to notify native IME of notifications because
  328. // when user typing after TIP does something, the changed information
  329. // is necessary for them.
  330. nsresult rv2 =
  331. nativeListener->NotifyIME(this, aIMENotification);
  332. // But return the result from current listener except when the
  333. // notification isn't handled.
  334. return rv == NS_ERROR_NOT_IMPLEMENTED ? rv2 : rv;
  335. }
  336. }
  337. }
  338. bool
  339. TextEventDispatcher::DispatchKeyboardEvent(
  340. EventMessage aMessage,
  341. const WidgetKeyboardEvent& aKeyboardEvent,
  342. nsEventStatus& aStatus,
  343. void* aData)
  344. {
  345. return DispatchKeyboardEventInternal(aMessage, aKeyboardEvent, aStatus,
  346. aData);
  347. }
  348. bool
  349. TextEventDispatcher::DispatchKeyboardEventInternal(
  350. EventMessage aMessage,
  351. const WidgetKeyboardEvent& aKeyboardEvent,
  352. nsEventStatus& aStatus,
  353. void* aData,
  354. uint32_t aIndexOfKeypress,
  355. bool aNeedsCallback)
  356. {
  357. // Note that this method is also used for dispatching key events on a plugin
  358. // because key events on a plugin should be dispatched same as normal key
  359. // events. Then, only some handlers which need to intercept key events
  360. // before the focused plugin (e.g., reserved shortcut key handlers) can
  361. // consume the events.
  362. MOZ_ASSERT(WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
  363. WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage) ||
  364. aMessage == eKeyPress, "Invalid aMessage value");
  365. nsresult rv = GetState();
  366. if (NS_WARN_IF(NS_FAILED(rv))) {
  367. return false;
  368. }
  369. // If the key shouldn't cause keypress events, don't this patch them.
  370. if (aMessage == eKeyPress && !aKeyboardEvent.ShouldCauseKeypressEvents()) {
  371. return false;
  372. }
  373. // Basically, key events shouldn't be dispatched during composition.
  374. // Note that plugin process has different IME context. Therefore, we don't
  375. // need to check our composition state when the key event is fired on a
  376. // plugin.
  377. if (IsComposing() && !WidgetKeyboardEvent::IsKeyEventOnPlugin(aMessage)) {
  378. // However, if we need to behave like other browsers, we need the keydown
  379. // and keyup events. Note that this behavior is also allowed by D3E spec.
  380. // FYI: keypress events must not be fired during composition.
  381. if (!sDispatchKeyEventsDuringComposition || aMessage == eKeyPress) {
  382. return false;
  383. }
  384. // XXX If there was mOnlyContentDispatch for this case, it might be useful
  385. // because our chrome doesn't assume that key events are fired during
  386. // composition.
  387. }
  388. WidgetKeyboardEvent keyEvent(true, aMessage, mWidget);
  389. InitEvent(keyEvent);
  390. keyEvent.AssignKeyEventData(aKeyboardEvent, false);
  391. if (aStatus == nsEventStatus_eConsumeNoDefault) {
  392. // If the key event should be dispatched as consumed event, marking it here.
  393. // This is useful to prevent double action. E.g., when the key was already
  394. // handled by system, our chrome shouldn't handle it.
  395. keyEvent.PreventDefaultBeforeDispatch();
  396. }
  397. // Corrects each member for the specific key event type.
  398. if (keyEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
  399. MOZ_ASSERT(!aIndexOfKeypress,
  400. "aIndexOfKeypress must be 0 for non-printable key");
  401. // If the keyboard event isn't caused by printable key, its charCode should
  402. // be 0.
  403. keyEvent.SetCharCode(0);
  404. } else {
  405. if (WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
  406. WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage)) {
  407. MOZ_RELEASE_ASSERT(!aIndexOfKeypress,
  408. "aIndexOfKeypress must be 0 for either eKeyDown or eKeyUp");
  409. } else {
  410. MOZ_RELEASE_ASSERT(
  411. !aIndexOfKeypress || aIndexOfKeypress < keyEvent.mKeyValue.Length(),
  412. "aIndexOfKeypress must be 0 - mKeyValue.Length() - 1");
  413. }
  414. wchar_t ch =
  415. keyEvent.mKeyValue.IsEmpty() ? 0 : keyEvent.mKeyValue[aIndexOfKeypress];
  416. keyEvent.SetCharCode(static_cast<uint32_t>(ch));
  417. if (aMessage == eKeyPress) {
  418. // keyCode of eKeyPress events of printable keys should be always 0.
  419. keyEvent.mKeyCode = 0;
  420. // eKeyPress events are dispatched for every character.
  421. // So, each key value of eKeyPress events should be a character.
  422. if (ch) {
  423. keyEvent.mKeyValue.Assign(ch);
  424. } else {
  425. keyEvent.mKeyValue.Truncate();
  426. }
  427. }
  428. }
  429. if (WidgetKeyboardEvent::IsKeyUpOrKeyUpOnPlugin(aMessage)) {
  430. // mIsRepeat of keyup event must be false.
  431. keyEvent.mIsRepeat = false;
  432. }
  433. // mIsComposing should be initialized later.
  434. keyEvent.mIsComposing = false;
  435. if (mInputTransactionType == eNativeInputTransaction) {
  436. // Copy mNativeKeyEvent here because for safety for other users of
  437. // AssignKeyEventData(), it doesn't copy this.
  438. keyEvent.mNativeKeyEvent = aKeyboardEvent.mNativeKeyEvent;
  439. } else {
  440. // If it's not a keyboard event for native key event, we should ensure that
  441. // mNativeKeyEvent and mPluginEvent are null/empty.
  442. keyEvent.mNativeKeyEvent = nullptr;
  443. keyEvent.mPluginEvent.Clear();
  444. }
  445. // TODO: Manage mUniqueId here.
  446. // Request the alternative char codes for the key event.
  447. // eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
  448. // needs to check if a following keypress event is reserved by chrome for
  449. // stopping propagation of its preceding keydown event.
  450. keyEvent.mAlternativeCharCodes.Clear();
  451. if ((WidgetKeyboardEvent::IsKeyDownOrKeyDownOnPlugin(aMessage) ||
  452. aMessage == eKeyPress) &&
  453. (aNeedsCallback || keyEvent.IsControl() || keyEvent.IsAlt() ||
  454. keyEvent.IsMeta() || keyEvent.IsOS())) {
  455. nsCOMPtr<TextEventDispatcherListener> listener =
  456. do_QueryReferent(mListener);
  457. if (listener) {
  458. DebugOnly<WidgetKeyboardEvent> original(keyEvent);
  459. listener->WillDispatchKeyboardEvent(this, keyEvent, aIndexOfKeypress,
  460. aData);
  461. MOZ_ASSERT(keyEvent.mMessage ==
  462. static_cast<WidgetKeyboardEvent&>(original).mMessage);
  463. MOZ_ASSERT(keyEvent.mKeyCode ==
  464. static_cast<WidgetKeyboardEvent&>(original).mKeyCode);
  465. MOZ_ASSERT(keyEvent.mLocation ==
  466. static_cast<WidgetKeyboardEvent&>(original).mLocation);
  467. MOZ_ASSERT(keyEvent.mIsRepeat ==
  468. static_cast<WidgetKeyboardEvent&>(original).mIsRepeat);
  469. MOZ_ASSERT(keyEvent.mIsComposing ==
  470. static_cast<WidgetKeyboardEvent&>(original).mIsComposing);
  471. MOZ_ASSERT(keyEvent.mKeyNameIndex ==
  472. static_cast<WidgetKeyboardEvent&>(original).mKeyNameIndex);
  473. MOZ_ASSERT(keyEvent.mCodeNameIndex ==
  474. static_cast<WidgetKeyboardEvent&>(original).mCodeNameIndex);
  475. MOZ_ASSERT(keyEvent.mKeyValue ==
  476. static_cast<WidgetKeyboardEvent&>(original).mKeyValue);
  477. MOZ_ASSERT(keyEvent.mCodeValue ==
  478. static_cast<WidgetKeyboardEvent&>(original).mCodeValue);
  479. }
  480. }
  481. DispatchInputEvent(mWidget, keyEvent, aStatus);
  482. return true;
  483. }
  484. bool
  485. TextEventDispatcher::MaybeDispatchKeypressEvents(
  486. const WidgetKeyboardEvent& aKeyboardEvent,
  487. nsEventStatus& aStatus,
  488. void* aData,
  489. bool aNeedsCallback)
  490. {
  491. // If the key event was consumed, keypress event shouldn't be fired.
  492. if (aStatus == nsEventStatus_eConsumeNoDefault) {
  493. return false;
  494. }
  495. // If the key shouldn't cause keypress events, don't fire them.
  496. if (!aKeyboardEvent.ShouldCauseKeypressEvents()) {
  497. return false;
  498. }
  499. // If the key isn't a printable key or just inputting one character or
  500. // no character, we should dispatch only one keypress. Otherwise, i.e.,
  501. // if the key is a printable key and inputs multiple characters, keypress
  502. // event should be dispatched the count of inputting characters times.
  503. size_t keypressCount =
  504. aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING ?
  505. 1 : std::max(static_cast<nsAString::size_type>(1),
  506. aKeyboardEvent.mKeyValue.Length());
  507. bool isDispatched = false;
  508. bool consumed = false;
  509. for (size_t i = 0; i < keypressCount; i++) {
  510. aStatus = nsEventStatus_eIgnore;
  511. if (!DispatchKeyboardEventInternal(eKeyPress, aKeyboardEvent,
  512. aStatus, aData, i, aNeedsCallback)) {
  513. // The widget must have been gone.
  514. break;
  515. }
  516. isDispatched = true;
  517. if (!consumed) {
  518. consumed = (aStatus == nsEventStatus_eConsumeNoDefault);
  519. }
  520. }
  521. // If one of the keypress event was consumed, return ConsumeNoDefault.
  522. if (consumed) {
  523. aStatus = nsEventStatus_eConsumeNoDefault;
  524. }
  525. return isDispatched;
  526. }
  527. /******************************************************************************
  528. * TextEventDispatcher::PendingComposition
  529. *****************************************************************************/
  530. TextEventDispatcher::PendingComposition::PendingComposition()
  531. {
  532. Clear();
  533. }
  534. void
  535. TextEventDispatcher::PendingComposition::Clear()
  536. {
  537. mString.Truncate();
  538. mClauses = nullptr;
  539. mCaret.mRangeType = TextRangeType::eUninitialized;
  540. }
  541. void
  542. TextEventDispatcher::PendingComposition::EnsureClauseArray()
  543. {
  544. if (mClauses) {
  545. return;
  546. }
  547. mClauses = new TextRangeArray();
  548. }
  549. nsresult
  550. TextEventDispatcher::PendingComposition::SetString(const nsAString& aString)
  551. {
  552. mString = aString;
  553. return NS_OK;
  554. }
  555. nsresult
  556. TextEventDispatcher::PendingComposition::AppendClause(
  557. uint32_t aLength,
  558. TextRangeType aTextRangeType)
  559. {
  560. if (NS_WARN_IF(!aLength)) {
  561. return NS_ERROR_INVALID_ARG;
  562. }
  563. switch (aTextRangeType) {
  564. case TextRangeType::eRawClause:
  565. case TextRangeType::eSelectedRawClause:
  566. case TextRangeType::eConvertedClause:
  567. case TextRangeType::eSelectedClause: {
  568. EnsureClauseArray();
  569. TextRange textRange;
  570. textRange.mStartOffset =
  571. mClauses->IsEmpty() ? 0 : mClauses->LastElement().mEndOffset;
  572. textRange.mEndOffset = textRange.mStartOffset + aLength;
  573. textRange.mRangeType = aTextRangeType;
  574. mClauses->AppendElement(textRange);
  575. return NS_OK;
  576. }
  577. default:
  578. return NS_ERROR_INVALID_ARG;
  579. }
  580. }
  581. nsresult
  582. TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset,
  583. uint32_t aLength)
  584. {
  585. mCaret.mStartOffset = aOffset;
  586. mCaret.mEndOffset = mCaret.mStartOffset + aLength;
  587. mCaret.mRangeType = TextRangeType::eCaret;
  588. return NS_OK;
  589. }
  590. nsresult
  591. TextEventDispatcher::PendingComposition::Set(const nsAString& aString,
  592. const TextRangeArray* aRanges)
  593. {
  594. Clear();
  595. nsAutoString str(aString);
  596. // Don't expose CRLF to web contents, instead, use LF.
  597. str.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n"));
  598. nsresult rv = SetString(str);
  599. if (NS_WARN_IF(NS_FAILED(rv))) {
  600. return rv;
  601. }
  602. if (!aRanges || aRanges->IsEmpty()) {
  603. // Create dummy range if aString isn't empty.
  604. if (!aString.IsEmpty()) {
  605. rv = AppendClause(str.Length(), TextRangeType::eRawClause);
  606. if (NS_WARN_IF(NS_FAILED(rv))) {
  607. return rv;
  608. }
  609. }
  610. return NS_OK;
  611. }
  612. // Adjust offsets in the ranges for XP linefeed character (only \n).
  613. // XXX Following code is the safest approach. However, it wastes performance.
  614. // For ensuring the clauses do not overlap each other, we should redesign
  615. // TextRange later.
  616. for (uint32_t i = 0; i < aRanges->Length(); ++i) {
  617. TextRange range = aRanges->ElementAt(i);
  618. TextRange nativeRange = range;
  619. if (nativeRange.mStartOffset > 0) {
  620. nsAutoString preText(Substring(aString, 0, nativeRange.mStartOffset));
  621. preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  622. NS_LITERAL_STRING("\n"));
  623. range.mStartOffset = preText.Length();
  624. }
  625. if (nativeRange.Length() == 0) {
  626. range.mEndOffset = range.mStartOffset;
  627. } else {
  628. nsAutoString clause(
  629. Substring(aString, nativeRange.mStartOffset, nativeRange.Length()));
  630. clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"),
  631. NS_LITERAL_STRING("\n"));
  632. range.mEndOffset = range.mStartOffset + clause.Length();
  633. }
  634. if (range.mRangeType == TextRangeType::eCaret) {
  635. mCaret = range;
  636. } else {
  637. EnsureClauseArray();
  638. mClauses->AppendElement(range);
  639. }
  640. }
  641. return NS_OK;
  642. }
  643. nsresult
  644. TextEventDispatcher::PendingComposition::Flush(
  645. TextEventDispatcher* aDispatcher,
  646. nsEventStatus& aStatus,
  647. const WidgetEventTime* aEventTime)
  648. {
  649. aStatus = nsEventStatus_eIgnore;
  650. nsresult rv = aDispatcher->GetState();
  651. if (NS_WARN_IF(NS_FAILED(rv))) {
  652. return rv;
  653. }
  654. if (mClauses && !mClauses->IsEmpty() &&
  655. mClauses->LastElement().mEndOffset != mString.Length()) {
  656. NS_WARNING("Sum of length of the all clauses must be same as the string "
  657. "length");
  658. Clear();
  659. return NS_ERROR_ILLEGAL_VALUE;
  660. }
  661. if (mCaret.mRangeType == TextRangeType::eCaret) {
  662. if (mCaret.mEndOffset > mString.Length()) {
  663. NS_WARNING("Caret position is out of the composition string");
  664. Clear();
  665. return NS_ERROR_ILLEGAL_VALUE;
  666. }
  667. EnsureClauseArray();
  668. mClauses->AppendElement(mCaret);
  669. }
  670. RefPtr<TextEventDispatcher> kungFuDeathGrip(aDispatcher);
  671. nsCOMPtr<nsIWidget> widget(aDispatcher->mWidget);
  672. WidgetCompositionEvent compChangeEvent(true, eCompositionChange, widget);
  673. aDispatcher->InitEvent(compChangeEvent);
  674. if (aEventTime) {
  675. compChangeEvent.AssignEventTime(*aEventTime);
  676. }
  677. compChangeEvent.mData = mString;
  678. if (mClauses) {
  679. MOZ_ASSERT(!mClauses->IsEmpty(),
  680. "mClauses must be non-empty array when it's not nullptr");
  681. compChangeEvent.mRanges = mClauses;
  682. }
  683. // While this method dispatches a composition event, some other event handler
  684. // cause more clauses to be added. So, we should clear pending composition
  685. // before dispatching the event.
  686. Clear();
  687. rv = aDispatcher->StartCompositionAutomaticallyIfNecessary(aStatus,
  688. aEventTime);
  689. if (NS_WARN_IF(NS_FAILED(rv))) {
  690. return rv;
  691. }
  692. if (aStatus == nsEventStatus_eConsumeNoDefault) {
  693. return NS_OK;
  694. }
  695. rv = aDispatcher->DispatchEvent(widget, compChangeEvent, aStatus);
  696. if (NS_WARN_IF(NS_FAILED(rv))) {
  697. return rv;
  698. }
  699. return NS_OK;
  700. }
  701. } // namespace widget
  702. } // namespace mozilla