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 "mozilla/Preferences.h"
7 #include "mozilla/StaticPrefs_dom.h"
8 #include "mozilla/TextEvents.h"
9 #include "mozilla/TextEventDispatcher.h"
11 #include "nsIWidget.h"
12 #include "nsPIDOMWindow.h"
14 #include "PuppetWidget.h"
19 /******************************************************************************
21 *****************************************************************************/
22 TextEventDispatcher::TextEventDispatcher(nsIWidget
* aWidget
)
25 mInputTransactionType(eNoInputTransaction
),
27 mIsHandlingComposition(false),
29 MOZ_RELEASE_ASSERT(mWidget
, "aWidget must not be nullptr");
31 ClearNotificationRequests();
34 nsresult
TextEventDispatcher::BeginInputTransaction(
35 TextEventDispatcherListener
* aListener
) {
36 return BeginInputTransactionInternal(aListener
,
37 eSameProcessSyncInputTransaction
);
40 nsresult
TextEventDispatcher::BeginTestInputTransaction(
41 TextEventDispatcherListener
* aListener
, bool aIsAPZAware
) {
42 return BeginInputTransactionInternal(
43 aListener
, aIsAPZAware
? eAsyncTestInputTransaction
44 : eSameProcessSyncTestInputTransaction
);
47 nsresult
TextEventDispatcher::BeginNativeInputTransaction() {
48 if (NS_WARN_IF(!mWidget
)) {
49 return NS_ERROR_FAILURE
;
51 RefPtr
<TextEventDispatcherListener
> listener
=
52 mWidget
->GetNativeTextEventDispatcherListener();
53 if (NS_WARN_IF(!listener
)) {
54 return NS_ERROR_FAILURE
;
56 return BeginInputTransactionInternal(listener
, eNativeInputTransaction
);
59 nsresult
TextEventDispatcher::BeginInputTransactionInternal(
60 TextEventDispatcherListener
* aListener
, InputTransactionType aType
) {
61 if (NS_WARN_IF(!aListener
)) {
62 return NS_ERROR_INVALID_ARG
;
64 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
66 if (listener
== aListener
&& mInputTransactionType
== aType
) {
67 UpdateNotificationRequests();
70 // If this has composition or is dispatching an event, any other listener
71 // can steal ownership. Especially, if the latter case is allowed,
72 // nobody cannot begin input transaction with this if a modal dialog is
73 // opened during dispatching an event.
74 if (IsComposing() || IsDispatchingEvent()) {
75 return NS_ERROR_ALREADY_INITIALIZED
;
78 mListener
= do_GetWeakReference(aListener
);
79 mInputTransactionType
= aType
;
80 if (listener
&& listener
!= aListener
) {
81 listener
->OnRemovedFrom(this);
83 UpdateNotificationRequests();
87 nsresult
TextEventDispatcher::BeginInputTransactionFor(
88 const WidgetGUIEvent
* aEvent
, PuppetWidget
* aPuppetWidget
) {
89 MOZ_ASSERT(XRE_IsContentProcess());
90 MOZ_ASSERT(!IsDispatchingEvent());
92 switch (aEvent
->mMessage
) {
96 MOZ_ASSERT(aEvent
->mClass
== eKeyboardEventClass
);
98 case eCompositionStart
:
99 case eCompositionChange
:
100 case eCompositionCommit
:
101 case eCompositionCommitAsIs
:
102 MOZ_ASSERT(aEvent
->mClass
== eCompositionEventClass
);
105 return NS_ERROR_INVALID_ARG
;
108 if (aEvent
->mFlags
.mIsSynthesizedForTests
) {
109 // If the event is for an automated test and this instance dispatched
110 // an event to the parent process, we can assume that this is already
111 // initialized properly.
112 if (mInputTransactionType
== eAsyncTestInputTransaction
) {
115 // Even if the event coming from the parent process is synthesized for
116 // tests, this process should treat it as "sync" test here because
117 // it won't be go back to the parent process.
118 nsresult rv
= BeginInputTransactionInternal(
119 static_cast<TextEventDispatcherListener
*>(aPuppetWidget
),
120 eSameProcessSyncTestInputTransaction
);
121 if (NS_WARN_IF(NS_FAILED(rv
))) {
125 nsresult rv
= BeginNativeInputTransaction();
126 if (NS_WARN_IF(NS_FAILED(rv
))) {
131 // Emulate modifying members which indicate the state of composition.
132 // If we need to manage more states and/or more complexly, we should create
133 // internal methods which are called by both here and each event dispatcher
134 // method of this class.
135 switch (aEvent
->mMessage
) {
140 case eCompositionStart
:
141 MOZ_ASSERT(!mIsComposing
);
142 mIsComposing
= mIsHandlingComposition
= true;
144 case eCompositionChange
:
145 MOZ_ASSERT(mIsComposing
);
146 MOZ_ASSERT(mIsHandlingComposition
);
147 mIsComposing
= mIsHandlingComposition
= true;
149 case eCompositionCommit
:
150 case eCompositionCommitAsIs
:
151 MOZ_ASSERT(mIsComposing
);
152 MOZ_ASSERT(mIsHandlingComposition
);
153 mIsComposing
= false;
154 mIsHandlingComposition
= true;
157 MOZ_ASSERT_UNREACHABLE("You forgot to handle the event");
158 return NS_ERROR_UNEXPECTED
;
161 void TextEventDispatcher::EndInputTransaction(
162 TextEventDispatcherListener
* aListener
) {
163 if (NS_WARN_IF(IsComposing()) || NS_WARN_IF(IsDispatchingEvent())) {
167 mInputTransactionType
= eNoInputTransaction
;
169 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
170 if (NS_WARN_IF(!listener
)) {
174 if (NS_WARN_IF(listener
!= aListener
)) {
179 listener
->OnRemovedFrom(this);
180 UpdateNotificationRequests();
183 void TextEventDispatcher::OnDestroyWidget() {
186 ClearNotificationRequests();
187 mPendingComposition
.Clear();
188 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
190 mWritingMode
.reset();
191 mInputTransactionType
= eNoInputTransaction
;
193 listener
->OnRemovedFrom(this);
197 nsresult
TextEventDispatcher::GetState() const {
198 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
200 return NS_ERROR_NOT_INITIALIZED
;
202 if (!mWidget
|| mWidget
->Destroyed()) {
203 return NS_ERROR_NOT_AVAILABLE
;
208 void TextEventDispatcher::InitEvent(WidgetGUIEvent
& aEvent
) const {
209 aEvent
.mTime
= PR_IntervalNow();
210 aEvent
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
211 aEvent
.mFlags
.mIsSynthesizedForTests
= IsForTests();
212 if (aEvent
.mClass
!= eCompositionEventClass
) {
215 void* pseudoIMEContext
= GetPseudoIMEContext();
216 if (pseudoIMEContext
) {
217 aEvent
.AsCompositionEvent()->mNativeIMEContext
.InitWithRawNativeIMEContext(
222 MOZ_ASSERT(!XRE_IsContentProcess(),
223 "Why did the content process start native event transaction?");
224 MOZ_ASSERT(aEvent
.AsCompositionEvent()->mNativeIMEContext
.IsValid(),
225 "Native IME context shouldn't be invalid");
227 #endif // #ifdef DEBUG
230 Maybe
<WritingMode
> TextEventDispatcher::MaybeWritingModeAtSelection() const {
231 if (mWritingMode
.isSome()) {
235 if (NS_WARN_IF(!mWidget
)) {
239 WidgetQueryContentEvent
querySelectedTextEvent(true, eQuerySelectedText
,
241 nsEventStatus status
= nsEventStatus_eIgnore
;
242 const_cast<TextEventDispatcher
*>(this)->DispatchEvent(
243 mWidget
, querySelectedTextEvent
, status
);
244 if (!querySelectedTextEvent
.FoundSelection()) {
246 mWritingMode
.reset();
252 mWritingMode
= Some(querySelectedTextEvent
.mReply
->mWritingMode
);
254 return Some(querySelectedTextEvent
.mReply
->mWritingMode
);
257 nsresult
TextEventDispatcher::DispatchEvent(nsIWidget
* aWidget
,
258 WidgetGUIEvent
& aEvent
,
259 nsEventStatus
& aStatus
) {
260 MOZ_ASSERT(!aEvent
.AsInputEvent(), "Use DispatchInputEvent()");
262 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(this);
263 nsCOMPtr
<nsIWidget
> widget(aWidget
);
265 nsresult rv
= widget
->DispatchEvent(&aEvent
, aStatus
);
270 nsresult
TextEventDispatcher::DispatchInputEvent(nsIWidget
* aWidget
,
271 WidgetInputEvent
& aEvent
,
272 nsEventStatus
& aStatus
) {
273 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(this);
274 nsCOMPtr
<nsIWidget
> widget(aWidget
);
277 // If the event is dispatched via nsIWidget::DispatchInputEvent(), it
278 // sends the event to the parent process first since APZ needs to handle it
279 // first. However, some callers (e.g., keyboard apps on B2G and tests
280 // expecting synchronous dispatch) don't want this to do that.
282 if (ShouldSendInputEventToAPZ()) {
283 aStatus
= widget
->DispatchInputEvent(&aEvent
);
285 rv
= widget
->DispatchEvent(&aEvent
, aStatus
);
292 nsresult
TextEventDispatcher::StartComposition(
293 nsEventStatus
& aStatus
, const WidgetEventTime
* aEventTime
) {
294 aStatus
= nsEventStatus_eIgnore
;
296 nsresult rv
= GetState();
297 if (NS_WARN_IF(NS_FAILED(rv
))) {
301 if (NS_WARN_IF(mIsComposing
)) {
302 return NS_ERROR_FAILURE
;
305 // When you change some members from here, you may need same change in
306 // BeginInputTransactionFor().
307 mIsComposing
= mIsHandlingComposition
= true;
308 WidgetCompositionEvent
compositionStartEvent(true, eCompositionStart
,
310 InitEvent(compositionStartEvent
);
312 compositionStartEvent
.AssignEventTime(*aEventTime
);
314 rv
= DispatchEvent(mWidget
, compositionStartEvent
, aStatus
);
315 if (NS_WARN_IF(NS_FAILED(rv
))) {
322 nsresult
TextEventDispatcher::StartCompositionAutomaticallyIfNecessary(
323 nsEventStatus
& aStatus
, const WidgetEventTime
* aEventTime
) {
328 nsresult rv
= StartComposition(aStatus
, aEventTime
);
329 if (NS_WARN_IF(NS_FAILED(rv
))) {
333 // If started composition has already been committed, we shouldn't dispatch
334 // the compositionchange event.
335 if (!IsComposing()) {
336 aStatus
= nsEventStatus_eConsumeNoDefault
;
340 // Note that the widget might be destroyed during a call of
341 // StartComposition(). In such case, we shouldn't keep dispatching next
345 MOZ_ASSERT(rv
!= NS_ERROR_NOT_INITIALIZED
,
346 "aDispatcher must still be initialized in this case");
347 aStatus
= nsEventStatus_eConsumeNoDefault
;
348 return NS_OK
; // Don't throw exception in this case
351 aStatus
= nsEventStatus_eIgnore
;
355 nsresult
TextEventDispatcher::CommitComposition(
356 nsEventStatus
& aStatus
, const nsAString
* aCommitString
,
357 const WidgetEventTime
* aEventTime
) {
358 aStatus
= nsEventStatus_eIgnore
;
360 nsresult rv
= GetState();
361 if (NS_WARN_IF(NS_FAILED(rv
))) {
365 // When there is no composition, caller shouldn't try to commit composition
366 // with non-existing composition string nor commit composition with empty
368 if (NS_WARN_IF(!IsComposing() &&
369 (!aCommitString
|| aCommitString
->IsEmpty()))) {
370 return NS_ERROR_FAILURE
;
373 nsCOMPtr
<nsIWidget
> widget(mWidget
);
374 rv
= StartCompositionAutomaticallyIfNecessary(aStatus
, aEventTime
);
375 if (NS_WARN_IF(NS_FAILED(rv
))) {
378 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
382 // When you change some members from here, you may need same change in
383 // BeginInputTransactionFor().
385 // End current composition and make this free for other IMEs.
386 mIsComposing
= false;
388 EventMessage message
=
389 aCommitString
? eCompositionCommit
: eCompositionCommitAsIs
;
390 WidgetCompositionEvent
compositionCommitEvent(true, message
, widget
);
391 InitEvent(compositionCommitEvent
);
393 compositionCommitEvent
.AssignEventTime(*aEventTime
);
395 if (message
== eCompositionCommit
) {
396 compositionCommitEvent
.mData
= *aCommitString
;
397 // If aCommitString comes from TextInputProcessor, it may be void, but
398 // editor requires non-void string even when it's empty.
399 compositionCommitEvent
.mData
.SetIsVoid(false);
400 // Don't send CRLF nor CR, replace it with LF here.
401 compositionCommitEvent
.mData
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
402 compositionCommitEvent
.mData
.ReplaceSubstring(u
"\r"_ns
, u
"\n"_ns
);
404 rv
= DispatchEvent(widget
, compositionCommitEvent
, aStatus
);
405 if (NS_WARN_IF(NS_FAILED(rv
))) {
412 nsresult
TextEventDispatcher::NotifyIME(
413 const IMENotification
& aIMENotification
) {
414 nsresult rv
= NS_ERROR_NOT_IMPLEMENTED
;
416 switch (aIMENotification
.mMessage
) {
417 case NOTIFY_IME_OF_BLUR
:
419 mWritingMode
.reset();
420 ClearNotificationRequests();
422 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
423 // If content handles composition events when native IME doesn't have
424 // composition, that means that we completely finished handling
425 // composition(s). Note that when focused content is in a remote
426 // process, this is sent when all dispatched composition events
427 // have been handled in the remote process.
428 if (!IsComposing()) {
429 mIsHandlingComposition
= false;
432 case NOTIFY_IME_OF_SELECTION_CHANGE
:
435 Some(aIMENotification
.mSelectionChangeData
.GetWritingMode());
442 // First, send the notification to current input transaction's listener.
443 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
445 rv
= listener
->NotifyIME(this, aIMENotification
);
452 // If current input transaction isn't for native event handler, we should
453 // send the notification to the native text event dispatcher listener
454 // since native event handler may need to do something from
455 // TextEventDispatcherListener::NotifyIME() even before there is no
456 // input transaction yet. For example, native IME handler may need to
457 // create new context at receiving NOTIFY_IME_OF_FOCUS. In this case,
458 // mListener may not be initialized since input transaction should be
459 // initialized immediately before dispatching every WidgetKeyboardEvent
460 // and WidgetCompositionEvent (dispatching events always occurs after
462 nsCOMPtr
<TextEventDispatcherListener
> nativeListener
=
463 mWidget
->GetNativeTextEventDispatcherListener();
464 if (listener
!= nativeListener
&& nativeListener
) {
465 switch (aIMENotification
.mMessage
) {
466 case REQUEST_TO_COMMIT_COMPOSITION
:
467 case REQUEST_TO_CANCEL_COMPOSITION
:
468 // It's not necessary to notify native IME of requests.
471 // Even if current input transaction's listener returns NS_OK or
472 // something, we need to notify native IME of notifications because
473 // when user typing after TIP does something, the changed information
474 // is necessary for them.
475 nsresult rv2
= nativeListener
->NotifyIME(this, aIMENotification
);
476 // But return the result from current listener except when the
477 // notification isn't handled.
478 if (rv
== NS_ERROR_NOT_IMPLEMENTED
) {
486 if (aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
) {
488 UpdateNotificationRequests();
494 void TextEventDispatcher::ClearNotificationRequests() {
495 mIMENotificationRequests
= IMENotificationRequests();
498 void TextEventDispatcher::UpdateNotificationRequests() {
499 ClearNotificationRequests();
501 // If it doesn't has focus, no notifications are available.
502 if (!mHasFocus
|| !mWidget
) {
506 // If there is a listener, its requests are necessary.
507 nsCOMPtr
<TextEventDispatcherListener
> listener
= do_QueryReferent(mListener
);
509 mIMENotificationRequests
= listener
->GetIMENotificationRequests();
512 // Even if this is in non-native input transaction, native IME needs
513 // requests. So, add native IME requests too.
514 if (!IsInNativeInputTransaction()) {
515 nsCOMPtr
<TextEventDispatcherListener
> nativeListener
=
516 mWidget
->GetNativeTextEventDispatcherListener();
517 if (nativeListener
) {
518 mIMENotificationRequests
|= nativeListener
->GetIMENotificationRequests();
523 bool TextEventDispatcher::DispatchKeyboardEvent(
524 EventMessage aMessage
, const WidgetKeyboardEvent
& aKeyboardEvent
,
525 nsEventStatus
& aStatus
, void* aData
) {
526 return DispatchKeyboardEventInternal(aMessage
, aKeyboardEvent
, aStatus
,
530 bool TextEventDispatcher::DispatchKeyboardEventInternal(
531 EventMessage aMessage
, const WidgetKeyboardEvent
& aKeyboardEvent
,
532 nsEventStatus
& aStatus
, void* aData
, uint32_t aIndexOfKeypress
,
533 bool aNeedsCallback
) {
534 // Note that this method is also used for dispatching key events on a plugin
535 // because key events on a plugin should be dispatched same as normal key
536 // events. Then, only some handlers which need to intercept key events
537 // before the focused plugin (e.g., reserved shortcut key handlers) can
538 // consume the events.
540 aMessage
== eKeyDown
|| aMessage
== eKeyUp
|| aMessage
== eKeyPress
,
541 "Invalid aMessage value");
542 nsresult rv
= GetState();
543 if (NS_WARN_IF(NS_FAILED(rv
))) {
547 // If the key shouldn't cause keypress events, don't this patch them.
548 if (aMessage
== eKeyPress
&& !aKeyboardEvent
.ShouldCauseKeypressEvents()) {
552 // Basically, key events shouldn't be dispatched during composition.
553 // Note that plugin process has different IME context. Therefore, we don't
554 // need to check our composition state when the key event is fired on a
557 // However, if we need to behave like other browsers, we need the keydown
558 // and keyup events. Note that this behavior is also allowed by D3E spec.
559 // FYI: keypress events must not be fired during composition.
560 if (!StaticPrefs::dom_keyboardevent_dispatch_during_composition() ||
561 aMessage
== eKeyPress
) {
564 // XXX If there was mOnlyContentDispatch for this case, it might be useful
565 // because our chrome doesn't assume that key events are fired during
569 WidgetKeyboardEvent
keyEvent(true, aMessage
, mWidget
);
571 keyEvent
.AssignKeyEventData(aKeyboardEvent
, false);
572 // Command arrays are not duplicated by AssignKeyEventData() due to
573 // both performance and footprint reasons. So, when TextInputProcessor
574 // emulates real text input or synthesizing keyboard events for tests,
575 // the arrays may be initialized all commands already. If so, we need to
576 // duplicate the arrays here, but we should do this only when we're
577 // dispatching eKeyPress events because BrowserParent::SendRealKeyEvent()
578 // does this only for eKeyPress event. Note that this is not required if
579 // we're in the main process because in the parent process, the edit commands
580 // will be initialized by `ExecuteEditCommands()` (when the event is handled
581 // by editor event listener) or `InitAllEditCommands()` (when the event is
582 // set to a content process). We should test whether these pathes work or
584 if (XRE_IsContentProcess() && keyEvent
.mIsSynthesizedByTIP
) {
585 if (aMessage
== eKeyPress
) {
586 keyEvent
.AssignCommands(aKeyboardEvent
);
588 // Prevent retriving native edit commands if we're in a content process
589 // because only `eKeyPress` events coming from the main process have
590 // edit commands (See `BrowserParent::SendRealKeyEvent`). And also
591 // retriving edit commands from a content process requires synchonous
592 // IPC and that makes running tests slower. Therefore, we should mark
593 // the `eKeyPress` event does not need to retrieve edit commands anymore.
594 keyEvent
.PreventNativeKeyBindings();
598 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
599 // If the key event should be dispatched as consumed event, marking it here.
600 // This is useful to prevent double action. This is intended to the system
601 // has already consumed the event but we need to dispatch the event for
602 // compatibility with older version and other browsers. So, we should not
603 // stop cross process forwarding of them.
604 keyEvent
.PreventDefaultBeforeDispatch(CrossProcessForwarding::eAllow
);
607 // Corrects each member for the specific key event type.
608 if (keyEvent
.mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
) {
609 MOZ_ASSERT(!aIndexOfKeypress
,
610 "aIndexOfKeypress must be 0 for non-printable key");
611 // If the keyboard event isn't caused by printable key, its charCode should
613 keyEvent
.SetCharCode(0);
615 MOZ_DIAGNOSTIC_ASSERT_IF(aMessage
== eKeyDown
|| aMessage
== eKeyUp
,
617 MOZ_DIAGNOSTIC_ASSERT_IF(
618 aMessage
== eKeyPress
,
619 aIndexOfKeypress
< std::max(keyEvent
.mKeyValue
.Length(), 1u));
621 keyEvent
.mKeyValue
.IsEmpty() ? 0 : keyEvent
.mKeyValue
[aIndexOfKeypress
];
622 keyEvent
.SetCharCode(static_cast<uint32_t>(ch
));
623 if (aMessage
== eKeyPress
) {
624 // keyCode of eKeyPress events of printable keys should be always 0.
625 keyEvent
.mKeyCode
= 0;
626 // eKeyPress events are dispatched for every character.
627 // So, each key value of eKeyPress events should be a character.
629 keyEvent
.mKeyValue
.Assign(ch
);
631 keyEvent
.mKeyValue
.Truncate();
635 if (aMessage
== eKeyUp
) {
636 // mIsRepeat of keyup event must be false.
637 keyEvent
.mIsRepeat
= false;
639 // mIsComposing should be initialized later.
640 keyEvent
.mIsComposing
= false;
641 if (mInputTransactionType
== eNativeInputTransaction
) {
642 // Copy mNativeKeyEvent here because for safety for other users of
643 // AssignKeyEventData(), it doesn't copy this.
644 keyEvent
.mNativeKeyEvent
= aKeyboardEvent
.mNativeKeyEvent
;
646 // If it's not a keyboard event for native key event, we should ensure that
647 // mNativeKeyEvent and mPluginEvent are null/empty.
648 keyEvent
.mNativeKeyEvent
= nullptr;
649 keyEvent
.mPluginEvent
.Clear();
651 // TODO: Manage mUniqueId here.
653 // Request the alternative char codes for the key event.
654 // eKeyDown also needs alternative char codes because nsXBLWindowKeyHandler
655 // needs to check if a following keypress event is reserved by chrome for
656 // stopping propagation of its preceding keydown event.
657 keyEvent
.mAlternativeCharCodes
.Clear();
658 if ((aMessage
== eKeyDown
|| aMessage
== eKeyPress
) &&
659 (aNeedsCallback
|| keyEvent
.IsControl() || keyEvent
.IsAlt() ||
660 keyEvent
.IsMeta() || keyEvent
.IsOS())) {
661 nsCOMPtr
<TextEventDispatcherListener
> listener
=
662 do_QueryReferent(mListener
);
664 DebugOnly
<WidgetKeyboardEvent
> original(keyEvent
);
665 listener
->WillDispatchKeyboardEvent(this, keyEvent
, aIndexOfKeypress
,
667 MOZ_ASSERT(keyEvent
.mMessage
==
668 static_cast<WidgetKeyboardEvent
&>(original
).mMessage
);
669 MOZ_ASSERT(keyEvent
.mKeyCode
==
670 static_cast<WidgetKeyboardEvent
&>(original
).mKeyCode
);
671 MOZ_ASSERT(keyEvent
.mLocation
==
672 static_cast<WidgetKeyboardEvent
&>(original
).mLocation
);
673 MOZ_ASSERT(keyEvent
.mIsRepeat
==
674 static_cast<WidgetKeyboardEvent
&>(original
).mIsRepeat
);
675 MOZ_ASSERT(keyEvent
.mIsComposing
==
676 static_cast<WidgetKeyboardEvent
&>(original
).mIsComposing
);
677 MOZ_ASSERT(keyEvent
.mKeyNameIndex
==
678 static_cast<WidgetKeyboardEvent
&>(original
).mKeyNameIndex
);
679 MOZ_ASSERT(keyEvent
.mCodeNameIndex
==
680 static_cast<WidgetKeyboardEvent
&>(original
).mCodeNameIndex
);
681 MOZ_ASSERT(keyEvent
.mKeyValue
==
682 static_cast<WidgetKeyboardEvent
&>(original
).mKeyValue
);
683 MOZ_ASSERT(keyEvent
.mCodeValue
==
684 static_cast<WidgetKeyboardEvent
&>(original
).mCodeValue
);
689 dom_keyboardevent_keypress_dispatch_non_printable_keys_only_system_group_in_content() &&
690 keyEvent
.mMessage
== eKeyPress
&&
691 !keyEvent
.ShouldKeyPressEventBeFiredOnContent()) {
692 // Note that even if we set it to true, this may be overwritten by
693 // PresShell::DispatchEventToDOM().
694 keyEvent
.mFlags
.mOnlySystemGroupDispatchInContent
= true;
697 DispatchInputEvent(mWidget
, keyEvent
, aStatus
);
701 bool TextEventDispatcher::MaybeDispatchKeypressEvents(
702 const WidgetKeyboardEvent
& aKeyboardEvent
, nsEventStatus
& aStatus
,
703 void* aData
, bool aNeedsCallback
) {
704 // If the key event was consumed, keypress event shouldn't be fired.
705 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
709 // If the key shouldn't cause keypress events, don't fire them.
710 if (!aKeyboardEvent
.ShouldCauseKeypressEvents()) {
714 // If the key isn't a printable key or just inputting one character or
715 // no character, we should dispatch only one keypress. Otherwise, i.e.,
716 // if the key is a printable key and inputs multiple characters, keypress
717 // event should be dispatched the count of inputting characters times.
718 size_t keypressCount
=
719 aKeyboardEvent
.mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
721 : std::max(static_cast<nsAString::size_type
>(1),
722 aKeyboardEvent
.mKeyValue
.Length());
723 bool isDispatched
= false;
724 bool consumed
= false;
725 for (size_t i
= 0; i
< keypressCount
; i
++) {
726 aStatus
= nsEventStatus_eIgnore
;
727 if (!DispatchKeyboardEventInternal(eKeyPress
, aKeyboardEvent
, aStatus
,
728 aData
, i
, aNeedsCallback
)) {
729 // The widget must have been gone.
734 consumed
= (aStatus
== nsEventStatus_eConsumeNoDefault
);
738 // If one of the keypress event was consumed, return ConsumeNoDefault.
740 aStatus
= nsEventStatus_eConsumeNoDefault
;
746 /******************************************************************************
747 * TextEventDispatcher::PendingComposition
748 *****************************************************************************/
750 TextEventDispatcher::PendingComposition::PendingComposition() { Clear(); }
752 void TextEventDispatcher::PendingComposition::Clear() {
755 mCaret
.mRangeType
= TextRangeType::eUninitialized
;
756 mReplacedNativeLineBreakers
= false;
759 void TextEventDispatcher::PendingComposition::EnsureClauseArray() {
763 mClauses
= new TextRangeArray();
766 nsresult
TextEventDispatcher::PendingComposition::SetString(
767 const nsAString
& aString
) {
768 MOZ_ASSERT(!mReplacedNativeLineBreakers
);
773 nsresult
TextEventDispatcher::PendingComposition::AppendClause(
774 uint32_t aLength
, TextRangeType aTextRangeType
) {
775 MOZ_ASSERT(!mReplacedNativeLineBreakers
);
777 if (NS_WARN_IF(!aLength
)) {
778 return NS_ERROR_INVALID_ARG
;
781 switch (aTextRangeType
) {
782 case TextRangeType::eRawClause
:
783 case TextRangeType::eSelectedRawClause
:
784 case TextRangeType::eConvertedClause
:
785 case TextRangeType::eSelectedClause
: {
788 textRange
.mStartOffset
=
789 mClauses
->IsEmpty() ? 0 : mClauses
->LastElement().mEndOffset
;
790 textRange
.mEndOffset
= textRange
.mStartOffset
+ aLength
;
791 textRange
.mRangeType
= aTextRangeType
;
792 mClauses
->AppendElement(textRange
);
796 return NS_ERROR_INVALID_ARG
;
800 nsresult
TextEventDispatcher::PendingComposition::SetCaret(uint32_t aOffset
,
802 MOZ_ASSERT(!mReplacedNativeLineBreakers
);
804 mCaret
.mStartOffset
= aOffset
;
805 mCaret
.mEndOffset
= mCaret
.mStartOffset
+ aLength
;
806 mCaret
.mRangeType
= TextRangeType::eCaret
;
810 nsresult
TextEventDispatcher::PendingComposition::Set(
811 const nsAString
& aString
, const TextRangeArray
* aRanges
) {
814 nsresult rv
= SetString(aString
);
815 if (NS_WARN_IF(NS_FAILED(rv
))) {
819 if (!aRanges
|| aRanges
->IsEmpty()) {
820 // Create dummy range if mString isn't empty.
821 if (!mString
.IsEmpty()) {
822 rv
= AppendClause(mString
.Length(), TextRangeType::eRawClause
);
823 if (NS_WARN_IF(NS_FAILED(rv
))) {
826 ReplaceNativeLineBreakers();
831 // Adjust offsets in the ranges for XP linefeed character (only \n).
832 for (uint32_t i
= 0; i
< aRanges
->Length(); ++i
) {
833 TextRange range
= aRanges
->ElementAt(i
);
834 if (range
.mRangeType
== TextRangeType::eCaret
) {
838 mClauses
->AppendElement(range
);
841 ReplaceNativeLineBreakers();
845 void TextEventDispatcher::PendingComposition::ReplaceNativeLineBreakers() {
846 mReplacedNativeLineBreakers
= true;
848 // If the composition string is empty, we don't need to do anything.
849 if (mString
.IsEmpty()) {
853 nsAutoString
nativeString(mString
);
854 // Don't expose CRLF nor CR to web contents, instead, use LF.
855 mString
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
856 mString
.ReplaceSubstring(u
"\r"_ns
, u
"\n"_ns
);
858 // If the length isn't changed, we don't need to adjust any offset and length
859 // of mClauses nor mCaret.
860 if (nativeString
.Length() == mString
.Length()) {
865 for (TextRange
& clause
: *mClauses
) {
866 AdjustRange(clause
, nativeString
);
869 if (mCaret
.mRangeType
== TextRangeType::eCaret
) {
870 AdjustRange(mCaret
, nativeString
);
875 void TextEventDispatcher::PendingComposition::AdjustRange(
876 TextRange
& aRange
, const nsAString
& aNativeString
) {
877 TextRange nativeRange
= aRange
;
878 // XXX Following code wastes runtime cost because this causes computing
879 // mStartOffset for each clause from the start of composition string.
880 // If we'd make TextRange have only its length, we don't need to do
881 // this. However, this must not be so serious problem because
882 // composition string is usually short and separated as a few clauses.
883 if (nativeRange
.mStartOffset
> 0) {
884 nsAutoString
preText(Substring(aNativeString
, 0, nativeRange
.mStartOffset
));
885 preText
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
886 aRange
.mStartOffset
= preText
.Length();
888 if (nativeRange
.Length() == 0) {
889 aRange
.mEndOffset
= aRange
.mStartOffset
;
891 nsAutoString
clause(Substring(aNativeString
, nativeRange
.mStartOffset
,
892 nativeRange
.Length()));
893 clause
.ReplaceSubstring(u
"\r\n"_ns
, u
"\n"_ns
);
894 aRange
.mEndOffset
= aRange
.mStartOffset
+ clause
.Length();
898 nsresult
TextEventDispatcher::PendingComposition::Flush(
899 TextEventDispatcher
* aDispatcher
, nsEventStatus
& aStatus
,
900 const WidgetEventTime
* aEventTime
) {
901 aStatus
= nsEventStatus_eIgnore
;
903 nsresult rv
= aDispatcher
->GetState();
904 if (NS_WARN_IF(NS_FAILED(rv
))) {
908 if (mClauses
&& !mClauses
->IsEmpty() &&
909 mClauses
->LastElement().mEndOffset
!= mString
.Length()) {
911 "Sum of length of the all clauses must be same as the string "
914 return NS_ERROR_ILLEGAL_VALUE
;
916 if (mCaret
.mRangeType
== TextRangeType::eCaret
) {
917 if (mCaret
.mEndOffset
> mString
.Length()) {
918 NS_WARNING("Caret position is out of the composition string");
920 return NS_ERROR_ILLEGAL_VALUE
;
923 mClauses
->AppendElement(mCaret
);
926 // If the composition string is set without Set(), we need to replace native
927 // line breakers in the composition string with XP line breaker.
928 if (!mReplacedNativeLineBreakers
) {
929 ReplaceNativeLineBreakers();
932 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(aDispatcher
);
933 nsCOMPtr
<nsIWidget
> widget(aDispatcher
->mWidget
);
934 WidgetCompositionEvent
compChangeEvent(true, eCompositionChange
, widget
);
935 aDispatcher
->InitEvent(compChangeEvent
);
937 compChangeEvent
.AssignEventTime(*aEventTime
);
939 compChangeEvent
.mData
= mString
;
940 // If mString comes from TextInputProcessor, it may be void, but editor
941 // requires non-void string even when it's empty.
942 compChangeEvent
.mData
.SetIsVoid(false);
944 MOZ_ASSERT(!mClauses
->IsEmpty(),
945 "mClauses must be non-empty array when it's not nullptr");
946 compChangeEvent
.mRanges
= mClauses
;
949 // While this method dispatches a composition event, some other event handler
950 // cause more clauses to be added. So, we should clear pending composition
951 // before dispatching the event.
954 rv
= aDispatcher
->StartCompositionAutomaticallyIfNecessary(aStatus
,
956 if (NS_WARN_IF(NS_FAILED(rv
))) {
959 if (aStatus
== nsEventStatus_eConsumeNoDefault
) {
962 rv
= aDispatcher
->DispatchEvent(widget
, compChangeEvent
, aStatus
);
963 if (NS_WARN_IF(NS_FAILED(rv
))) {
970 } // namespace widget
971 } // namespace mozilla