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 #include "TextEventDispatcher.h"
9 #include "PuppetWidget.h"
10 #include "TextEvents.h"
12 #include "mozilla/Preferences.h"
13 #include "mozilla/StaticPrefs_dom.h"
14 #include "nsCharTraits.h"
16 #include "nsIWidget.h"
17 #include "nsPIDOMWindow.h"
23 /******************************************************************************
25 *****************************************************************************/
26 TextEventDispatcher::TextEventDispatcher(nsIWidget
* aWidget
)
29 mInputTransactionType(eNoInputTransaction
),
31 mIsHandlingComposition(false),
33 MOZ_RELEASE_ASSERT(mWidget
, "aWidget must not be nullptr");
35 ClearNotificationRequests();
38 nsresult
TextEventDispatcher::BeginInputTransaction(
39 TextEventDispatcherListener
* aListener
) {
40 return BeginInputTransactionInternal(aListener
,
41 eSameProcessSyncInputTransaction
);
44 nsresult
TextEventDispatcher::BeginTestInputTransaction(
45 TextEventDispatcherListener
* aListener
, bool aIsAPZAware
) {
46 return BeginInputTransactionInternal(
47 aListener
, aIsAPZAware
? eAsyncTestInputTransaction
48 : eSameProcessSyncTestInputTransaction
);
51 nsresult
TextEventDispatcher::BeginNativeInputTransaction() {
52 if (NS_WARN_IF(!mWidget
)) {
53 return NS_ERROR_FAILURE
;
55 RefPtr
<TextEventDispatcherListener
> listener
=
56 mWidget
->GetNativeTextEventDispatcherListener();
57 if (NS_WARN_IF(!listener
)) {
58 return NS_ERROR_FAILURE
;
60 return BeginInputTransactionInternal(listener
, eNativeInputTransaction
);
63 nsresult
TextEventDispatcher::BeginInputTransactionInternal(
64 TextEventDispatcherListener
* aListener
, InputTransactionType aType
) {
65 if (NS_WARN_IF(!aListener
)) {
66 return NS_ERROR_INVALID_ARG
;
68 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
70 if (listener
== aListener
&& mInputTransactionType
== aType
) {
71 UpdateNotificationRequests();
74 // If this has composition or is dispatching an event, any other listener
75 // can steal ownership. Especially, if the latter case is allowed,
76 // nobody cannot begin input transaction with this if a modal dialog is
77 // opened during dispatching an event.
78 if (IsComposing() || IsDispatchingEvent()) {
79 return NS_ERROR_ALREADY_INITIALIZED
;
82 mListener
= do_GetWeakReference(aListener
);
83 mInputTransactionType
= aType
;
84 if (listener
&& listener
!= aListener
) {
85 listener
->OnRemovedFrom(this);
87 UpdateNotificationRequests();
91 nsresult
TextEventDispatcher::BeginInputTransactionFor(
92 const WidgetGUIEvent
* aEvent
, PuppetWidget
* aPuppetWidget
) {
93 MOZ_ASSERT(XRE_IsContentProcess());
94 MOZ_ASSERT(!IsDispatchingEvent());
96 switch (aEvent
->mMessage
) {
100 MOZ_ASSERT(aEvent
->mClass
== eKeyboardEventClass
);
102 case eCompositionStart
:
103 case eCompositionChange
:
104 case eCompositionCommit
:
105 case eCompositionCommitAsIs
:
106 MOZ_ASSERT(aEvent
->mClass
== eCompositionEventClass
);
109 return NS_ERROR_INVALID_ARG
;
112 if (aEvent
->mFlags
.mIsSynthesizedForTests
) {
113 // If the event is for an automated test and this instance dispatched
114 // an event to the parent process, we can assume that this is already
115 // initialized properly.
116 if (mInputTransactionType
== eAsyncTestInputTransaction
) {
119 // Even if the event coming from the parent process is synthesized for
120 // tests, this process should treat it as "sync" test here because
121 // it won't be go back to the parent process.
122 nsresult rv
= BeginInputTransactionInternal(
123 static_cast<TextEventDispatcherListener
*>(aPuppetWidget
),
124 eSameProcessSyncTestInputTransaction
);
125 if (NS_WARN_IF(NS_FAILED(rv
))) {
129 nsresult rv
= BeginNativeInputTransaction();
130 if (NS_WARN_IF(NS_FAILED(rv
))) {
135 // Emulate modifying members which indicate the state of composition.
136 // If we need to manage more states and/or more complexly, we should create
137 // internal methods which are called by both here and each event dispatcher
138 // method of this class.
139 switch (aEvent
->mMessage
) {
144 case eCompositionStart
:
145 MOZ_ASSERT(!mIsComposing
);
146 mIsComposing
= mIsHandlingComposition
= true;
148 case eCompositionChange
:
149 MOZ_ASSERT(mIsComposing
);
150 MOZ_ASSERT(mIsHandlingComposition
);
151 mIsComposing
= mIsHandlingComposition
= true;
153 case eCompositionCommit
:
154 case eCompositionCommitAsIs
:
155 MOZ_ASSERT(mIsComposing
);
156 MOZ_ASSERT(mIsHandlingComposition
);
157 mIsComposing
= false;
158 mIsHandlingComposition
= true;
161 MOZ_ASSERT_UNREACHABLE("You forgot to handle the event");
162 return NS_ERROR_UNEXPECTED
;
165 void TextEventDispatcher::EndInputTransaction(
166 TextEventDispatcherListener
* aListener
) {
167 if (NS_WARN_IF(IsComposing()) || NS_WARN_IF(IsDispatchingEvent())) {
171 mInputTransactionType
= eNoInputTransaction
;
173 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
174 if (NS_WARN_IF(!listener
)) {
178 if (NS_WARN_IF(listener
!= aListener
)) {
183 listener
->OnRemovedFrom(this);
184 UpdateNotificationRequests();
187 void TextEventDispatcher::OnDestroyWidget() {
190 ClearNotificationRequests();
191 mPendingComposition
.Clear();
192 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
194 mWritingMode
.reset();
195 mInputTransactionType
= eNoInputTransaction
;
197 listener
->OnRemovedFrom(this);
201 nsresult
TextEventDispatcher::GetState() const {
202 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
204 return NS_ERROR_NOT_INITIALIZED
;
206 if (!mWidget
|| mWidget
->Destroyed()) {
207 return NS_ERROR_NOT_AVAILABLE
;
212 void TextEventDispatcher::InitEvent(WidgetGUIEvent
& aEvent
) const {
213 aEvent
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
214 aEvent
.mFlags
.mIsSynthesizedForTests
= IsForTests();
215 if (aEvent
.mClass
!= eCompositionEventClass
) {
218 void* pseudoIMEContext
= GetPseudoIMEContext();
219 if (pseudoIMEContext
) {
220 aEvent
.AsCompositionEvent()->mNativeIMEContext
.InitWithRawNativeIMEContext(
225 MOZ_ASSERT(!XRE_IsContentProcess(),
226 "Why did the content process start native event transaction?");
227 MOZ_ASSERT(aEvent
.AsCompositionEvent()->mNativeIMEContext
.IsValid(),
228 "Native IME context shouldn't be invalid");
230 #endif // #ifdef DEBUG
233 Maybe
<WritingMode
> TextEventDispatcher::MaybeQueryWritingModeAtSelection()
235 if (mHasFocus
|| mWritingMode
.isSome()) {
239 if (NS_WARN_IF(!mWidget
)) {
243 // If a remote content has focus and IME does not have focus, it's going to
244 // fail eQuerySelectedText in ContentCacheParent. For avoiding to waste
245 // unnecessary runtime cost and to prevent unnecessary warnings, we should
246 // not dispatch the event in the case.
247 const InputContext inputContext
= mWidget
->GetInputContext();
248 if (XRE_IsE10sParentProcess() && inputContext
.IsOriginContentProcess() &&
249 !inputContext
.mIMEState
.IsEditable()) {
253 WidgetQueryContentEvent
querySelectedTextEvent(true, eQuerySelectedText
,
255 nsEventStatus status
= nsEventStatus_eIgnore
;
256 const_cast<TextEventDispatcher
*>(this)->DispatchEvent(
257 mWidget
, querySelectedTextEvent
, status
);
258 if (!querySelectedTextEvent
.FoundSelection()) {
262 return Some(querySelectedTextEvent
.mReply
->mWritingMode
);
265 nsresult
TextEventDispatcher::DispatchEvent(nsIWidget
* aWidget
,
266 WidgetGUIEvent
& aEvent
,
267 nsEventStatus
& aStatus
) {
268 MOZ_ASSERT(!aEvent
.AsInputEvent(), "Use DispatchInputEvent()");
270 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(this);
271 nsCOMPtr
<nsIWidget
> widget(aWidget
);
273 nsresult rv
= widget
->DispatchEvent(&aEvent
, aStatus
);
278 nsresult
TextEventDispatcher::DispatchInputEvent(nsIWidget
* aWidget
,
279 WidgetInputEvent
& aEvent
,
280 nsEventStatus
& aStatus
) {
281 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(this);
282 nsCOMPtr
<nsIWidget
> widget(aWidget
);
285 // If the event is dispatched via nsIWidget::DispatchInputEvent(), it
286 // sends the event to the parent process first since APZ needs to handle it
287 // first. However, some callers (e.g., keyboard apps on B2G and tests
288 // expecting synchronous dispatch) don't want this to do that.
290 if (ShouldSendInputEventToAPZ()) {
291 aStatus
= widget
->DispatchInputEvent(&aEvent
).mContentStatus
;
293 rv
= widget
->DispatchEvent(&aEvent
, aStatus
);
300 nsresult
TextEventDispatcher::StartComposition(
301 nsEventStatus
& aStatus
, const WidgetEventTime
* aEventTime
) {
302 aStatus
= nsEventStatus_eIgnore
;
304 nsresult rv
= GetState();
305 if (NS_WARN_IF(NS_FAILED(rv
))) {
309 if (NS_WARN_IF(mIsComposing
)) {
310 return NS_ERROR_FAILURE
;
313 // When you change some members from here, you may need same change in
314 // BeginInputTransactionFor().
315 mIsComposing
= mIsHandlingComposition
= true;
316 WidgetCompositionEvent
compositionStartEvent(true, eCompositionStart
,
318 InitEvent(compositionStartEvent
);
320 compositionStartEvent
.AssignEventTime(*aEventTime
);
322 rv
= DispatchEvent(mWidget
, compositionStartEvent
, aStatus
);
323 if (NS_WARN_IF(NS_FAILED(rv
))) {
330 nsresult
TextEventDispatcher::StartCompositionAutomaticallyIfNecessary(
331 nsEventStatus
& aStatus
, const WidgetEventTime
* aEventTime
) {
336 nsresult rv
= StartComposition(aStatus
, aEventTime
);
337 if (NS_WARN_IF(NS_FAILED(rv
))) {
341 // If started composition has already been committed, we shouldn't dispatch
342 // the compositionchange event.
343 if (!IsComposing()) {
344 aStatus
= nsEventStatus_eConsumeNoDefault
;
348 // Note that the widget might be destroyed during a call of
349 // StartComposition(). In such case, we shouldn't keep dispatching next
353 MOZ_ASSERT(rv
!= NS_ERROR_NOT_INITIALIZED
,
354 "aDispatcher must still be initialized in this case");
355 aStatus
= nsEventStatus_eConsumeNoDefault
;
356 return NS_OK
; // Don't throw exception in this case
359 aStatus
= nsEventStatus_eIgnore
;
363 nsresult
TextEventDispatcher::CommitComposition(
364 nsEventStatus
& aStatus
, const nsAString
* aCommitString
,
365 const WidgetEventTime
* aEventTime
) {
366 aStatus
= nsEventStatus_eIgnore
;
368 nsresult rv
= GetState();
369 if (NS_WARN_IF(NS_FAILED(rv
))) {
373 // When there is no composition, caller shouldn't try to commit composition
374 // with non-existing composition string nor commit composition with empty
376 if (NS_WARN_IF(!IsComposing() &&
377 (!aCommitString
|| aCommitString
->IsEmpty()))) {
378 return NS_ERROR_FAILURE
;
381 nsCOMPtr
<nsIWidget
> widget(mWidget
);
382 rv
= StartCompositionAutomaticallyIfNecessary(aStatus
, aEventTime
);
383 if (NS_WARN_IF(NS_FAILED(rv
))) {
386 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
390 // When you change some members from here, you may need same change in
391 // BeginInputTransactionFor().
393 // End current composition and make this free for other IMEs.
394 mIsComposing
= false;
396 EventMessage message
=
397 aCommitString
? eCompositionCommit
: eCompositionCommitAsIs
;
398 WidgetCompositionEvent
compositionCommitEvent(true, message
, widget
);
399 InitEvent(compositionCommitEvent
);
401 compositionCommitEvent
.AssignEventTime(*aEventTime
);
403 if (message
== eCompositionCommit
) {
404 compositionCommitEvent
.mData
= *aCommitString
;
405 // If aCommitString comes from TextInputProcessor, it may be void, but
406 // editor requires non-void string even when it's empty.
407 compositionCommitEvent
.mData
.SetIsVoid(false);
408 // Don't send CRLF nor CR, replace it with LF here.
409 compositionCommitEvent
.mData
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
410 compositionCommitEvent
.mData
.ReplaceSubstring(u
"\r"_ns
, u
"\n"_ns
);
412 rv
= DispatchEvent(widget
, compositionCommitEvent
, aStatus
);
413 if (NS_WARN_IF(NS_FAILED(rv
))) {
420 nsresult
TextEventDispatcher::NotifyIME(
421 const IMENotification
& aIMENotification
) {
422 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
424 switch (aIMENotification
.mMessage
) {
425 case NOTIFY_IME_OF_FOCUS
: {
426 mWritingMode
= MaybeQueryWritingModeAtSelection();
429 case NOTIFY_IME_OF_BLUR
:
431 mWritingMode
.reset();
432 ClearNotificationRequests();
434 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
435 // If content handles composition events when native IME doesn't have
436 // composition, that means that we completely finished handling
437 // composition(s). Note that when focused content is in a remote
438 // process, this is sent when all dispatched composition events
439 // have been handled in the remote process.
440 if (!IsComposing()) {
441 mIsHandlingComposition
= false;
444 case NOTIFY_IME_OF_SELECTION_CHANGE
:
445 if (mHasFocus
&& aIMENotification
.mSelectionChangeData
.HasRange()) {
447 Some(aIMENotification
.mSelectionChangeData
.GetWritingMode());
454 // First, send the notification to current input transaction's listener.
455 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
457 rv
= listener
->NotifyIME(this, aIMENotification
);
464 // If current input transaction isn't for native event handler, we should
465 // send the notification to the native text event dispatcher listener
466 // since native event handler may need to do something from
467 // TextEventDispatcherListener::NotifyIME() even before there is no
468 // input transaction yet. For example, native IME handler may need to
469 // create new context at receiving NOTIFY_IME_OF_FOCUS. In this case,
470 // mListener may not be initialized since input transaction should be
471 // initialized immediately before dispatching every WidgetKeyboardEvent
472 // and WidgetCompositionEvent (dispatching events always occurs after
474 nsCOMPtr
<TextEventDispatcherListener
> nativeListener
=
475 mWidget
->GetNativeTextEventDispatcherListener();
476 if (listener
!= nativeListener
&& nativeListener
) {
477 switch (aIMENotification
.mMessage
) {
478 case REQUEST_TO_COMMIT_COMPOSITION
:
479 case REQUEST_TO_CANCEL_COMPOSITION
:
480 // It's not necessary to notify native IME of requests.
483 // Even if current input transaction's listener returns NS_OK or
484 // something, we need to notify native IME of notifications because
485 // when user typing after TIP does something, the changed information
486 // is necessary for them.
487 nsresult rv2
= nativeListener
->NotifyIME(this, aIMENotification
);
488 // But return the result from current listener except when the
489 // notification isn't handled.
490 if (rv
== NS_ERROR_NOT_IMPLEMENTED
) {
498 if (aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
) {
500 UpdateNotificationRequests();
506 void TextEventDispatcher::ClearNotificationRequests() {
507 mIMENotificationRequests
= IMENotificationRequests();
510 void TextEventDispatcher::UpdateNotificationRequests() {
511 ClearNotificationRequests();
513 // If it doesn't has focus, no notifications are available.
514 if (!mHasFocus
|| !mWidget
) {
518 // If there is a listener, its requests are necessary.
519 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
521 mIMENotificationRequests
= listener
->GetIMENotificationRequests();
524 // Even if this is in non-native input transaction, native IME needs
525 // requests. So, add native IME requests too.
526 if (!IsInNativeInputTransaction()) {
527 nsCOMPtr
<TextEventDispatcherListener
> nativeListener
=
528 mWidget
->GetNativeTextEventDispatcherListener();
529 if (nativeListener
) {
530 mIMENotificationRequests
|= nativeListener
->GetIMENotificationRequests();
535 bool TextEventDispatcher::DispatchKeyboardEvent(
536 EventMessage aMessage
, const WidgetKeyboardEvent
& aKeyboardEvent
,
537 nsEventStatus
& aStatus
, void* aData
) {
538 return DispatchKeyboardEventInternal(aMessage
, aKeyboardEvent
, aStatus
,
542 bool TextEventDispatcher::DispatchKeyboardEventInternal(
543 EventMessage aMessage
, const WidgetKeyboardEvent
& aKeyboardEvent
,
544 nsEventStatus
& aStatus
, void* aData
, uint32_t aIndexOfKeypress
,
545 bool aNeedsCallback
) {
546 // Note that this method is also used for dispatching key events on a plugin
547 // because key events on a plugin should be dispatched same as normal key
548 // events. Then, only some handlers which need to intercept key events
549 // before the focused plugin (e.g., reserved shortcut key handlers) can
550 // consume the events.
552 aMessage
== eKeyDown
|| aMessage
== eKeyUp
|| aMessage
== eKeyPress
,
553 "Invalid aMessage value");
554 nsresult rv
= GetState();
555 if (NS_WARN_IF(NS_FAILED(rv
))) {
559 // If the key shouldn't cause keypress events, don't this patch them.
560 if (aMessage
== eKeyPress
&& !aKeyboardEvent
.ShouldCauseKeypressEvents()) {
564 // Basically, key events shouldn't be dispatched during composition.
565 // Note that plugin process has different IME context. Therefore, we don't
566 // need to check our composition state when the key event is fired on a
569 // However, if we need to behave like other browsers, we need the keydown
570 // and keyup events. Note that this behavior is also allowed by D3E spec.
571 // FYI: keypress events must not be fired during composition.
572 if (!StaticPrefs::dom_keyboardevent_dispatch_during_composition() ||
573 aMessage
== eKeyPress
) {
576 // XXX If there was mOnlyContentDispatch for this case, it might be useful
577 // because our chrome doesn't assume that key events are fired during
581 WidgetKeyboardEvent
keyEvent(true, aMessage
, mWidget
);
583 keyEvent
.AssignKeyEventData(aKeyboardEvent
, false);
584 // Command arrays are not duplicated by AssignKeyEventData() due to
585 // both performance and footprint reasons. So, when TextInputProcessor
586 // emulates real text input or synthesizing keyboard events for tests,
587 // the arrays may be initialized all commands already. If so, we need to
588 // duplicate the arrays here, but we should do this only when we're
589 // dispatching eKeyPress events because BrowserParent::SendRealKeyEvent()
590 // does this only for eKeyPress event. Note that this is not required if
591 // we're in the main process because in the parent process, the edit commands
592 // will be initialized by `ExecuteEditCommands()` (when the event is handled
593 // by editor event listener) or `InitAllEditCommands()` (when the event is
594 // set to a content process). We should test whether these pathes work or
596 if (XRE_IsContentProcess() && keyEvent
.mIsSynthesizedByTIP
) {
597 if (aMessage
== eKeyPress
) {
598 keyEvent
.AssignCommands(aKeyboardEvent
);
600 // Prevent retriving native edit commands if we're in a content process
601 // because only `eKeyPress` events coming from the main process have
602 // edit commands (See `BrowserParent::SendRealKeyEvent`). And also
603 // retriving edit commands from a content process requires synchonous
604 // IPC and that makes running tests slower. Therefore, we should mark
605 // the `eKeyPress` event does not need to retrieve edit commands anymore.
606 keyEvent
.PreventNativeKeyBindings();
610 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
611 // If the key event should be dispatched as consumed event, marking it here.
612 // This is useful to prevent double action. This is intended to the system
613 // has already consumed the event but we need to dispatch the event for
614 // compatibility with older version and other browsers. So, we should not
615 // stop cross process forwarding of them.
616 keyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
619 // Corrects each member for the specific key event type.
620 if (keyEvent
.mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
) {
621 MOZ_ASSERT(!aIndexOfKeypress
,
622 "aIndexOfKeypress must be 0 for non-printable key");
623 // If the keyboard event isn't caused by printable key, its charCode should
625 keyEvent
.SetCharCode(0);
627 MOZ_DIAGNOSTIC_ASSERT_IF(aMessage
== eKeyDown
|| aMessage
== eKeyUp
,
629 MOZ_DIAGNOSTIC_ASSERT_IF(
630 aMessage
== eKeyPress
,
631 aIndexOfKeypress
< std::max
<size_t>(keyEvent
.mKeyValue
.Length(), 1));
633 keyEvent
.mKeyValue
.IsEmpty() ? 0 : keyEvent
.mKeyValue
[aIndexOfKeypress
];
634 keyEvent
.SetCharCode(static_cast<uint32_t>(ch
));
635 if (aMessage
== eKeyPress
) {
636 // keyCode of eKeyPress events of printable keys should be always 0.
637 keyEvent
.mKeyCode
= 0;
638 // eKeyPress events are dispatched for every character.
639 // So, each key value of eKeyPress events should be a character.
641 if (!IS_SURROGATE(ch
)) {
642 keyEvent
.mKeyValue
.Assign(ch
);
644 const bool isHighSurrogateFollowedByLowSurrogate
=
645 aIndexOfKeypress
+ 1 < keyEvent
.mKeyValue
.Length() &&
646 NS_IS_HIGH_SURROGATE(ch
) &&
647 NS_IS_LOW_SURROGATE(keyEvent
.mKeyValue
[aIndexOfKeypress
+ 1]);
648 const bool isLowSurrogateFollowingHighSurrogate
=
649 !isHighSurrogateFollowedByLowSurrogate
&& aIndexOfKeypress
> 0 &&
650 NS_IS_LOW_SURROGATE(ch
) &&
651 NS_IS_HIGH_SURROGATE(keyEvent
.mKeyValue
[aIndexOfKeypress
- 1]);
652 NS_WARNING_ASSERTION(isHighSurrogateFollowedByLowSurrogate
||
653 isLowSurrogateFollowingHighSurrogate
,
654 "Lone surrogate input should not happen");
656 dom_event_keypress_dispatch_once_per_surrogate_pair()) {
657 if (isHighSurrogateFollowedByLowSurrogate
) {
658 keyEvent
.mKeyValue
.Assign(
659 keyEvent
.mKeyValue
.BeginReading() + aIndexOfKeypress
, 2);
660 keyEvent
.SetCharCode(
661 SURROGATE_TO_UCS4(ch
, keyEvent
.mKeyValue
[1]));
662 } else if (isLowSurrogateFollowingHighSurrogate
) {
663 // Although not dispatching eKeyPress event (because it's already
664 // dispatched for the low surrogate above), the caller should
665 // treat that this dispatched eKeyPress event normally so that
669 // Do not expose ill-formed UTF-16 string because it's a
670 // problematic for Rust-running-as-wasm for example.
672 keyEvent
.mKeyValue
.Truncate();
674 } else if (!StaticPrefs::
675 dom_event_keypress_key_allow_lone_surrogate()) {
676 // If it's a high surrogate followed by a low surrogate, we should
677 // expose the surrogate pair with .key value.
678 if (isHighSurrogateFollowedByLowSurrogate
) {
679 keyEvent
.mKeyValue
.Assign(
680 keyEvent
.mKeyValue
.BeginReading() + aIndexOfKeypress
, 2);
682 // Do not expose low surrogate which should be handled by the
683 // preceding keypress event. And also do not expose ill-formed
684 // UTF-16 because it's a problematic for Rust-running-as-wasm for
687 keyEvent
.mKeyValue
.Truncate();
690 // Here is a path for traditional behavior. We set `.key` to
691 // high-surrogate and low-surrogate separately.
692 keyEvent
.mKeyValue
.Assign(ch
);
696 keyEvent
.mKeyValue
.Truncate();
700 if (aMessage
== eKeyUp
) {
701 // mIsRepeat of keyup event must be false.
702 keyEvent
.mIsRepeat
= false;
704 // mIsComposing should be initialized later.
705 keyEvent
.mIsComposing
= false;
706 if (mInputTransactionType
== eNativeInputTransaction
) {
707 // Copy mNativeKeyEvent here because for safety for other users of
708 // AssignKeyEventData(), it doesn't copy this.
709 keyEvent
.mNativeKeyEvent
= aKeyboardEvent
.mNativeKeyEvent
;
711 // If it's not a keyboard event for native key event, we should ensure that
712 // mNativeKeyEvent is null.
713 keyEvent
.mNativeKeyEvent
= nullptr;
715 // TODO: Manage mUniqueId here.
717 // Request the alternative char codes for the key event.
718 // eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
719 // needs to check if a following keypress event is reserved by chrome for
720 // stopping propagation of its preceding keydown event.
721 keyEvent
.mAlternativeCharCodes
.Clear();
722 if ((aMessage
== eKeyDown
|| aMessage
== eKeyPress
) &&
723 (aNeedsCallback
|| keyEvent
.IsControl() || keyEvent
.IsAlt() ||
724 keyEvent
.IsMeta())) {
725 nsCOMPtr
<TextEventDispatcherListener
> listener
=
726 do_QueryReferent(mListener
);
728 DebugOnly
<WidgetKeyboardEvent
> original(keyEvent
);
729 listener
->WillDispatchKeyboardEvent(this, keyEvent
, aIndexOfKeypress
,
731 MOZ_ASSERT(keyEvent
.mMessage
==
732 static_cast<WidgetKeyboardEvent
&>(original
).mMessage
);
733 MOZ_ASSERT(keyEvent
.mKeyCode
==
734 static_cast<WidgetKeyboardEvent
&>(original
).mKeyCode
);
735 MOZ_ASSERT(keyEvent
.mLocation
==
736 static_cast<WidgetKeyboardEvent
&>(original
).mLocation
);
737 MOZ_ASSERT(keyEvent
.mIsRepeat
==
738 static_cast<WidgetKeyboardEvent
&>(original
).mIsRepeat
);
739 MOZ_ASSERT(keyEvent
.mIsComposing
==
740 static_cast<WidgetKeyboardEvent
&>(original
).mIsComposing
);
741 MOZ_ASSERT(keyEvent
.mKeyNameIndex
==
742 static_cast<WidgetKeyboardEvent
&>(original
).mKeyNameIndex
);
743 MOZ_ASSERT(keyEvent
.mCodeNameIndex
==
744 static_cast<WidgetKeyboardEvent
&>(original
).mCodeNameIndex
);
745 MOZ_ASSERT(keyEvent
.mKeyValue
==
746 static_cast<WidgetKeyboardEvent
&>(original
).mKeyValue
);
747 MOZ_ASSERT(keyEvent
.mCodeValue
==
748 static_cast<WidgetKeyboardEvent
&>(original
).mCodeValue
);
753 dom_keyboardevent_keypress_dispatch_non_printable_keys_only_system_group_in_content() &&
754 keyEvent
.mMessage
== eKeyPress
&&
755 !keyEvent
.ShouldKeyPressEventBeFiredOnContent()) {
756 // Note that even if we set it to true, this may be overwritten by
757 // PresShell::DispatchEventToDOM().
758 keyEvent
.mFlags
.mOnlySystemGroupDispatchInContent
= true;
761 // If an editable element has focus and we're in the parent process, we should
762 // retrieve native key bindings right now because even if it matches with a
763 // reserved shortcut key, it should be handled by the editor.
764 if (XRE_IsParentProcess() && mHasFocus
&&
765 (aMessage
== eKeyDown
|| aMessage
== eKeyPress
)) {
766 keyEvent
.InitAllEditCommands(mWritingMode
);
769 DispatchInputEvent(mWidget
, keyEvent
, aStatus
);
773 bool TextEventDispatcher::MaybeDispatchKeypressEvents(
774 const WidgetKeyboardEvent
& aKeyboardEvent
, nsEventStatus
& aStatus
,
775 void* aData
, bool aNeedsCallback
) {
776 // If the key event was consumed, keypress event shouldn't be fired.
777 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
781 // If the key shouldn't cause keypress events, don't fire them.
782 if (!aKeyboardEvent
.ShouldCauseKeypressEvents()) {
786 // If the key isn't a printable key or just inputting one character or
787 // no character, we should dispatch only one keypress. Otherwise, i.e.,
788 // if the key is a printable key and inputs multiple characters, keypress
789 // event should be dispatched the count of inputting characters times.
790 size_t keypressCount
=
791 aKeyboardEvent
.mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
793 : std::max(static_cast<nsAString::size_type
>(1),
794 aKeyboardEvent
.mKeyValue
.Length());
795 bool isDispatched
= false;
796 bool consumed
= false;
797 for (size_t i
= 0; i
< keypressCount
; i
++) {
798 aStatus
= nsEventStatus_eIgnore
;
799 if (!DispatchKeyboardEventInternal(eKeyPress
, aKeyboardEvent
, aStatus
,
800 aData
, i
, aNeedsCallback
)) {
801 // The widget must have been gone.
806 consumed
= (aStatus
== nsEventStatus_eConsumeNoDefault
);
810 // If one of the keypress event was consumed, return ConsumeNoDefault.
812 aStatus
= nsEventStatus_eConsumeNoDefault
;
818 /******************************************************************************
819 * TextEventDispatcher::PendingComposition
820 *****************************************************************************/
822 TextEventDispatcher::PendingComposition::PendingComposition() { Clear(); }
824 void TextEventDispatcher::PendingComposition::Clear() {
827 mCaret
.mRangeType
= TextRangeType::eUninitialized
;
828 mReplacedNativeLineBreakers
= false;
831 void TextEventDispatcher::PendingComposition::EnsureClauseArray() {
835 mClauses
= new TextRangeArray();
838 nsresult
TextEventDispatcher::PendingComposition::SetString(
839 const nsAString
& aString
) {
840 MOZ_ASSERT(!mReplacedNativeLineBreakers
);
845 nsresult
TextEventDispatcher::PendingComposition::AppendClause(
846 uint32_t aLength
, TextRangeType aTextRangeType
) {
847 MOZ_ASSERT(!mReplacedNativeLineBreakers
);
849 if (NS_WARN_IF(!aLength
)) {
850 return NS_ERROR_INVALID_ARG
;
853 switch (aTextRangeType
) {
854 case TextRangeType::eRawClause
:
855 case TextRangeType::eSelectedRawClause
:
856 case TextRangeType::eConvertedClause
:
857 case TextRangeType::eSelectedClause
: {
860 textRange
.mStartOffset
=
861 mClauses
->IsEmpty() ? 0 : mClauses
->LastElement().mEndOffset
;
862 textRange
.mEndOffset
= textRange
.mStartOffset
+ aLength
;
863 textRange
.mRangeType
= aTextRangeType
;
864 mClauses
->AppendElement(textRange
);
868 return NS_ERROR_INVALID_ARG
;
872 nsresult
TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset
,
874 MOZ_ASSERT(!mReplacedNativeLineBreakers
);
876 mCaret
.mStartOffset
= aOffset
;
877 mCaret
.mEndOffset
= mCaret
.mStartOffset
+ aLength
;
878 mCaret
.mRangeType
= TextRangeType::eCaret
;
882 nsresult
TextEventDispatcher::PendingComposition::Set(
883 const nsAString
& aString
, const TextRangeArray
* aRanges
) {
886 nsresult rv
= SetString(aString
);
887 if (NS_WARN_IF(NS_FAILED(rv
))) {
891 if (!aRanges
|| aRanges
->IsEmpty()) {
892 // Create dummy range if mString isn't empty.
893 if (!mString
.IsEmpty()) {
894 rv
= AppendClause(mString
.Length(), TextRangeType::eRawClause
);
895 if (NS_WARN_IF(NS_FAILED(rv
))) {
898 ReplaceNativeLineBreakers();
903 // Adjust offsets in the ranges for XP linefeed character (only \n).
904 for (uint32_t i
= 0; i
< aRanges
->Length(); ++i
) {
905 TextRange range
= aRanges
->ElementAt(i
);
906 if (range
.mRangeType
== TextRangeType::eCaret
) {
910 mClauses
->AppendElement(range
);
913 ReplaceNativeLineBreakers();
917 void TextEventDispatcher::PendingComposition::ReplaceNativeLineBreakers() {
918 mReplacedNativeLineBreakers
= true;
920 // If the composition string is empty, we don't need to do anything.
921 if (mString
.IsEmpty()) {
925 nsAutoString
nativeString(mString
);
926 // Don't expose CRLF nor CR to web contents, instead, use LF.
927 mString
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
928 mString
.ReplaceSubstring(u
"\r"_ns
, u
"\n"_ns
);
930 // If the length isn't changed, we don't need to adjust any offset and length
931 // of mClauses nor mCaret.
932 if (nativeString
.Length() == mString
.Length()) {
937 for (TextRange
& clause
: *mClauses
) {
938 AdjustRange(clause
, nativeString
);
941 if (mCaret
.mRangeType
== TextRangeType::eCaret
) {
942 AdjustRange(mCaret
, nativeString
);
947 void TextEventDispatcher::PendingComposition::AdjustRange(
948 TextRange
& aRange
, const nsAString
& aNativeString
) {
949 TextRange nativeRange
= aRange
;
950 // XXX Following code wastes runtime cost because this causes computing
951 // mStartOffset for each clause from the start of composition string.
952 // If we'd make TextRange have only its length, we don't need to do
953 // this. However, this must not be so serious problem because
954 // composition string is usually short and separated as a few clauses.
955 if (nativeRange
.mStartOffset
> 0) {
956 nsAutoString
preText(Substring(aNativeString
, 0, nativeRange
.mStartOffset
));
957 preText
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
958 aRange
.mStartOffset
= preText
.Length();
960 if (nativeRange
.Length() == 0) {
961 aRange
.mEndOffset
= aRange
.mStartOffset
;
963 nsAutoString
clause(Substring(aNativeString
, nativeRange
.mStartOffset
,
964 nativeRange
.Length()));
965 clause
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
966 aRange
.mEndOffset
= aRange
.mStartOffset
+ clause
.Length();
970 nsresult
TextEventDispatcher::PendingComposition::Flush(
971 TextEventDispatcher
* aDispatcher
, nsEventStatus
& aStatus
,
972 const WidgetEventTime
* aEventTime
) {
973 aStatus
= nsEventStatus_eIgnore
;
975 nsresult rv
= aDispatcher
->GetState();
976 if (NS_WARN_IF(NS_FAILED(rv
))) {
980 if (mClauses
&& !mClauses
->IsEmpty() &&
981 mClauses
->LastElement().mEndOffset
!= mString
.Length()) {
983 "Sum of length of the all clauses must be same as the string "
986 return NS_ERROR_ILLEGAL_VALUE
;
988 if (mCaret
.mRangeType
== TextRangeType::eCaret
) {
989 if (mCaret
.mEndOffset
> mString
.Length()) {
990 NS_WARNING("Caret position is out of the composition string");
992 return NS_ERROR_ILLEGAL_VALUE
;
995 mClauses
->AppendElement(mCaret
);
998 // If the composition string is set without Set(), we need to replace native
999 // line breakers in the composition string with XP line breaker.
1000 if (!mReplacedNativeLineBreakers
) {
1001 ReplaceNativeLineBreakers();
1004 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(aDispatcher
);
1005 nsCOMPtr
<nsIWidget
> widget(aDispatcher
->mWidget
);
1006 WidgetCompositionEvent
compChangeEvent(true, eCompositionChange
, widget
);
1007 aDispatcher
->InitEvent(compChangeEvent
);
1009 compChangeEvent
.AssignEventTime(*aEventTime
);
1011 compChangeEvent
.mData
= mString
;
1012 // If mString comes from TextInputProcessor, it may be void, but editor
1013 // requires non-void string even when it's empty.
1014 compChangeEvent
.mData
.SetIsVoid(false);
1016 MOZ_ASSERT(!mClauses
->IsEmpty(),
1017 "mClauses must be non-empty array when it's not nullptr");
1018 compChangeEvent
.mRanges
= mClauses
;
1021 // While this method dispatches a composition event, some other event handler
1022 // cause more clauses to be added. So, we should clear pending composition
1023 // before dispatching the event.
1026 rv
= aDispatcher
->StartCompositionAutomaticallyIfNecessary(aStatus
,
1028 if (NS_WARN_IF(NS_FAILED(rv
))) {
1031 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
1034 rv
= aDispatcher
->DispatchEvent(widget
, compChangeEvent
, aStatus
);
1035 if (NS_WARN_IF(NS_FAILED(rv
))) {
1042 } // namespace widget
1043 } // namespace mozilla