1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/Event.h"
8 #include "mozilla/EventForwards.h"
9 #include "mozilla/Maybe.h"
10 #include "mozilla/NativeKeyBindingsType.h"
11 #include "mozilla/StaticPrefs_test.h"
12 #include "mozilla/TextEventDispatcher.h"
13 #include "mozilla/TextEvents.h"
14 #include "mozilla/TextInputProcessor.h"
15 #include "mozilla/WritingModes.h"
16 #include "mozilla/widget/IMEData.h"
17 #include "mozilla/dom/KeyboardEvent.h"
18 #include "nsContentUtils.h"
19 #include "nsIDocShell.h"
20 #include "nsIWidget.h"
21 #include "nsPIDOMWindow.h"
22 #include "nsPresContext.h"
24 using mozilla::dom::Event
;
25 using mozilla::dom::KeyboardEvent
;
26 using namespace mozilla::widget
;
30 /******************************************************************************
31 * TextInputProcessorNotification
32 ******************************************************************************/
34 class TextInputProcessorNotification final
35 : public nsITextInputProcessorNotification
{
36 using SelectionChangeData
= IMENotification::SelectionChangeData
;
37 using SelectionChangeDataBase
= IMENotification::SelectionChangeDataBase
;
38 using TextChangeData
= IMENotification::TextChangeData
;
39 using TextChangeDataBase
= IMENotification::TextChangeDataBase
;
42 explicit TextInputProcessorNotification(const char* aType
)
43 : mType(aType
), mTextChangeData() {}
45 explicit TextInputProcessorNotification(
46 const TextChangeDataBase
& aTextChangeData
)
47 : mType("notify-text-change"), mTextChangeData(aTextChangeData
) {}
49 explicit TextInputProcessorNotification(
50 const SelectionChangeDataBase
& aSelectionChangeData
)
51 : mType("notify-selection-change"),
52 mSelectionChangeData(aSelectionChangeData
) {
53 // SelectionChangeDataBase::mString still refers nsString instance owned
54 // by aSelectionChangeData. So, this needs to copy the instance.
55 if (aSelectionChangeData
.HasRange()) {
56 mSelectionChangeData
.mString
=
57 new nsString(aSelectionChangeData
.String());
59 mSelectionChangeData
.mString
= nullptr;
65 NS_IMETHOD
GetType(nsACString
& aType
) final
{
70 // "notify-text-change" and "notify-selection-change"
71 NS_IMETHOD
GetOffset(uint32_t* aOffset
) final
{
72 if (NS_WARN_IF(!aOffset
)) {
73 return NS_ERROR_INVALID_ARG
;
75 if (IsSelectionChange()) {
76 if (!mSelectionChangeData
.HasRange()) {
77 return NS_ERROR_NOT_AVAILABLE
;
79 *aOffset
= mSelectionChangeData
.mOffset
;
83 *aOffset
= mTextChangeData
.mStartOffset
;
86 return NS_ERROR_NOT_AVAILABLE
;
89 // "notify-selection-change"
90 NS_IMETHOD
GetHasRange(bool* aHasRange
) final
{
91 if (IsSelectionChange()) {
92 *aHasRange
= mSelectionChangeData
.HasRange();
95 return NS_ERROR_NOT_AVAILABLE
;
97 NS_IMETHOD
GetText(nsAString
& aText
) final
{
98 if (IsSelectionChange()) {
99 if (!mSelectionChangeData
.HasRange()) {
100 return NS_ERROR_NOT_AVAILABLE
;
102 aText
= mSelectionChangeData
.String();
105 return NS_ERROR_NOT_AVAILABLE
;
108 NS_IMETHOD
GetCollapsed(bool* aCollapsed
) final
{
109 if (NS_WARN_IF(!aCollapsed
)) {
110 return NS_ERROR_INVALID_ARG
;
112 if (IsSelectionChange()) {
113 *aCollapsed
= mSelectionChangeData
.IsCollapsed();
116 return NS_ERROR_NOT_AVAILABLE
;
119 NS_IMETHOD
GetLength(uint32_t* aLength
) final
{
120 if (NS_WARN_IF(!aLength
)) {
121 return NS_ERROR_INVALID_ARG
;
123 if (IsSelectionChange()) {
124 if (!mSelectionChangeData
.HasRange()) {
125 return NS_ERROR_NOT_AVAILABLE
;
127 *aLength
= mSelectionChangeData
.Length();
130 return NS_ERROR_NOT_AVAILABLE
;
133 NS_IMETHOD
GetReversed(bool* aReversed
) final
{
134 if (NS_WARN_IF(!aReversed
)) {
135 return NS_ERROR_INVALID_ARG
;
137 if (IsSelectionChange()) {
138 if (!mSelectionChangeData
.HasRange()) {
139 return NS_ERROR_NOT_AVAILABLE
;
141 *aReversed
= mSelectionChangeData
.mReversed
;
144 return NS_ERROR_NOT_AVAILABLE
;
147 NS_IMETHOD
GetWritingMode(nsACString
& aWritingMode
) final
{
148 if (IsSelectionChange()) {
149 WritingMode writingMode
= mSelectionChangeData
.GetWritingMode();
150 if (!writingMode
.IsVertical()) {
151 aWritingMode
.AssignLiteral("horizontal-tb");
152 } else if (writingMode
.IsVerticalLR()) {
153 aWritingMode
.AssignLiteral("vertical-lr");
155 aWritingMode
.AssignLiteral("vertical-rl");
159 return NS_ERROR_NOT_AVAILABLE
;
162 NS_IMETHOD
GetCausedByComposition(bool* aCausedByComposition
) final
{
163 if (NS_WARN_IF(!aCausedByComposition
)) {
164 return NS_ERROR_INVALID_ARG
;
166 if (IsSelectionChange()) {
167 *aCausedByComposition
= mSelectionChangeData
.mCausedByComposition
;
170 return NS_ERROR_NOT_AVAILABLE
;
173 NS_IMETHOD
GetCausedBySelectionEvent(bool* aCausedBySelectionEvent
) final
{
174 if (NS_WARN_IF(!aCausedBySelectionEvent
)) {
175 return NS_ERROR_INVALID_ARG
;
177 if (IsSelectionChange()) {
178 *aCausedBySelectionEvent
= mSelectionChangeData
.mCausedBySelectionEvent
;
181 return NS_ERROR_NOT_AVAILABLE
;
184 NS_IMETHOD
GetOccurredDuringComposition(
185 bool* aOccurredDuringComposition
) final
{
186 if (NS_WARN_IF(!aOccurredDuringComposition
)) {
187 return NS_ERROR_INVALID_ARG
;
189 if (IsSelectionChange()) {
190 *aOccurredDuringComposition
=
191 mSelectionChangeData
.mOccurredDuringComposition
;
194 return NS_ERROR_NOT_AVAILABLE
;
197 // "notify-text-change"
198 NS_IMETHOD
GetRemovedLength(uint32_t* aLength
) final
{
199 if (NS_WARN_IF(!aLength
)) {
200 return NS_ERROR_INVALID_ARG
;
202 if (IsTextChange()) {
203 *aLength
= mTextChangeData
.OldLength();
206 return NS_ERROR_NOT_AVAILABLE
;
209 NS_IMETHOD
GetAddedLength(uint32_t* aLength
) final
{
210 if (NS_WARN_IF(!aLength
)) {
211 return NS_ERROR_INVALID_ARG
;
213 if (IsTextChange()) {
214 *aLength
= mTextChangeData
.NewLength();
217 return NS_ERROR_NOT_AVAILABLE
;
220 NS_IMETHOD
GetCausedOnlyByComposition(bool* aCausedOnlyByComposition
) final
{
221 if (NS_WARN_IF(!aCausedOnlyByComposition
)) {
222 return NS_ERROR_INVALID_ARG
;
224 if (IsTextChange()) {
225 *aCausedOnlyByComposition
= mTextChangeData
.mCausedOnlyByComposition
;
228 return NS_ERROR_NOT_AVAILABLE
;
231 NS_IMETHOD
GetIncludingChangesDuringComposition(
232 bool* aIncludingChangesDuringComposition
) final
{
233 if (NS_WARN_IF(!aIncludingChangesDuringComposition
)) {
234 return NS_ERROR_INVALID_ARG
;
236 if (IsTextChange()) {
237 *aIncludingChangesDuringComposition
=
238 mTextChangeData
.mIncludingChangesDuringComposition
;
241 return NS_ERROR_NOT_AVAILABLE
;
244 NS_IMETHOD
GetIncludingChangesWithoutComposition(
245 bool* aIncludingChangesWithoutComposition
) final
{
246 if (NS_WARN_IF(!aIncludingChangesWithoutComposition
)) {
247 return NS_ERROR_INVALID_ARG
;
249 if (IsTextChange()) {
250 *aIncludingChangesWithoutComposition
=
251 mTextChangeData
.mIncludingChangesWithoutComposition
;
254 return NS_ERROR_NOT_AVAILABLE
;
258 virtual ~TextInputProcessorNotification() {
259 if (IsSelectionChange() && mSelectionChangeData
.mString
) {
260 delete mSelectionChangeData
.mString
;
261 mSelectionChangeData
.mString
= nullptr;
265 bool IsTextChange() const {
266 return mType
.EqualsLiteral("notify-text-change");
269 bool IsSelectionChange() const {
270 return mType
.EqualsLiteral("notify-selection-change");
276 TextChangeDataBase mTextChangeData
;
277 SelectionChangeDataBase mSelectionChangeData
;
280 TextInputProcessorNotification() : mTextChangeData() {}
283 NS_IMPL_ISUPPORTS(TextInputProcessorNotification
,
284 nsITextInputProcessorNotification
)
286 /******************************************************************************
288 ******************************************************************************/
290 NS_IMPL_ISUPPORTS(TextInputProcessor
, nsITextInputProcessor
,
291 TextEventDispatcherListener
, nsISupportsWeakReference
)
293 TextInputProcessor::TextInputProcessor()
294 : mDispatcher(nullptr), mForTests(false) {}
296 TextInputProcessor::~TextInputProcessor() {
297 if (mDispatcher
&& mDispatcher
->IsComposing()) {
298 // If this is composing and not canceling the composition, nobody can steal
299 // the rights of TextEventDispatcher from this instance. Therefore, this
300 // needs to cancel the composition here.
301 if (NS_SUCCEEDED(IsValidStateForComposition())) {
302 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
303 nsEventStatus status
= nsEventStatus_eIgnore
;
304 kungFuDeathGrip
->CommitComposition(status
, &EmptyString());
309 bool TextInputProcessor::IsComposing() const {
310 return mDispatcher
&& mDispatcher
->IsComposing();
314 TextInputProcessor::GetHasComposition(bool* aHasComposition
) {
315 MOZ_RELEASE_ASSERT(aHasComposition
, "aHasComposition must not be nullptr");
316 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
317 *aHasComposition
= IsComposing();
322 TextInputProcessor::BeginInputTransaction(
323 mozIDOMWindow
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
325 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
326 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
327 if (NS_WARN_IF(!aCallback
)) {
329 return NS_ERROR_INVALID_ARG
;
331 return BeginInputTransactionInternal(aWindow
, aCallback
, false, *aSucceeded
);
335 TextInputProcessor::BeginInputTransactionForTests(
336 mozIDOMWindow
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
337 uint8_t aOptionalArgc
, bool* aSucceeded
) {
338 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
339 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
340 nsITextInputProcessorCallback
* callback
=
341 aOptionalArgc
>= 1 ? aCallback
: nullptr;
342 return BeginInputTransactionInternal(aWindow
, callback
, true, *aSucceeded
);
345 nsresult
TextInputProcessor::BeginInputTransactionForFuzzing(
346 nsPIDOMWindowInner
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
348 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
349 return BeginInputTransactionInternal(aWindow
, aCallback
, false, *aSucceeded
);
352 nsresult
TextInputProcessor::BeginInputTransactionInternal(
353 mozIDOMWindow
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
354 bool aForTests
, bool& aSucceeded
) {
356 if (NS_WARN_IF(!aWindow
)) {
357 return NS_ERROR_INVALID_ARG
;
359 nsCOMPtr
<nsPIDOMWindowInner
> pWindow
= nsPIDOMWindowInner::From(aWindow
);
360 if (NS_WARN_IF(!pWindow
)) {
361 return NS_ERROR_INVALID_ARG
;
363 nsCOMPtr
<nsIDocShell
> docShell(pWindow
->GetDocShell());
364 if (NS_WARN_IF(!docShell
)) {
365 return NS_ERROR_FAILURE
;
367 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
368 if (NS_WARN_IF(!presContext
)) {
369 return NS_ERROR_FAILURE
;
371 nsCOMPtr
<nsIWidget
> widget
= presContext
->GetRootWidget();
372 if (NS_WARN_IF(!widget
)) {
373 return NS_ERROR_FAILURE
;
376 RefPtr
<TextEventDispatcher
> dispatcher
= widget
->GetTextEventDispatcher();
377 MOZ_RELEASE_ASSERT(dispatcher
, "TextEventDispatcher must not be null");
379 // If the instance was initialized and is being initialized for same
380 // dispatcher and same purpose, we don't need to initialize the dispatcher
382 if (mDispatcher
&& dispatcher
== mDispatcher
&& aCallback
== mCallback
&&
383 aForTests
== mForTests
) {
388 // If this instance is composing or dispatching an event, don't allow to
389 // initialize again. Especially, if we allow to begin input transaction with
390 // another TextEventDispatcher during dispatching an event, it may cause that
391 // nobody cannot begin input transaction with it if the last event causes
392 // opening modal dialog.
394 (mDispatcher
->IsComposing() || mDispatcher
->IsDispatchingEvent())) {
395 return NS_ERROR_ALREADY_INITIALIZED
;
398 // And also if another instance is composing with the new dispatcher or
399 // dispatching an event, it'll fail to steal its ownership. Then, we should
400 // not throw an exception, just return false.
401 if (dispatcher
->IsComposing() || dispatcher
->IsDispatchingEvent()) {
405 // This instance has finished preparing to link to the dispatcher. Therefore,
406 // let's forget the old dispatcher and purpose.
408 mDispatcher
->EndInputTransaction(this);
409 if (NS_WARN_IF(mDispatcher
)) {
410 // Forcibly initialize the members if we failed to end the input
412 UnlinkFromTextEventDispatcher();
418 bool isAPZAware
= StaticPrefs::test_events_async_enabled();
419 rv
= dispatcher
->BeginTestInputTransaction(this, isAPZAware
);
421 rv
= dispatcher
->BeginInputTransaction(this);
424 if (NS_WARN_IF(NS_FAILED(rv
))) {
428 mDispatcher
= dispatcher
;
429 mCallback
= aCallback
;
430 mForTests
= aForTests
;
435 void TextInputProcessor::UnlinkFromTextEventDispatcher() {
436 mDispatcher
= nullptr;
439 nsCOMPtr
<nsITextInputProcessorCallback
> callback(mCallback
);
442 RefPtr
<TextInputProcessorNotification
> notification
=
443 new TextInputProcessorNotification("notify-end-input-transaction");
445 callback
->OnNotify(this, notification
, &result
);
449 nsresult
TextInputProcessor::IsValidStateForComposition() {
450 if (NS_WARN_IF(!mDispatcher
)) {
451 return NS_ERROR_NOT_INITIALIZED
;
454 nsresult rv
= mDispatcher
->GetState();
455 if (NS_WARN_IF(NS_FAILED(rv
))) {
462 bool TextInputProcessor::IsValidEventTypeForComposition(
463 const WidgetKeyboardEvent
& aKeyboardEvent
) const {
464 // The key event type of composition methods must be "", "keydown" or "keyup".
465 if (aKeyboardEvent
.mMessage
== eKeyDown
||
466 aKeyboardEvent
.mMessage
== eKeyUp
) {
469 if (aKeyboardEvent
.mMessage
== eUnidentifiedEvent
&&
470 aKeyboardEvent
.mSpecifiedEventType
&&
471 nsDependentAtomString(aKeyboardEvent
.mSpecifiedEventType
)
472 .EqualsLiteral("on")) {
478 TextInputProcessor::EventDispatcherResult
479 TextInputProcessor::MaybeDispatchKeydownForComposition(
480 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
) {
481 EventDispatcherResult result
;
483 result
.mResult
= IsValidStateForComposition();
484 if (NS_WARN_IF(NS_FAILED(result
.mResult
))) {
485 result
.mCanContinue
= false;
489 if (!aKeyboardEvent
) {
493 // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
495 if (aKeyboardEvent
->mMessage
== eKeyUp
) {
499 // Modifier keys are not allowed because managing modifier state in this
500 // method makes this messy.
501 if (NS_WARN_IF(aKeyboardEvent
->IsModifierKeyEvent())) {
502 result
.mResult
= NS_ERROR_INVALID_ARG
;
503 result
.mCanContinue
= false;
507 uint32_t consumedFlags
= 0;
510 KeydownInternal(*aKeyboardEvent
, aKeyFlags
, false, consumedFlags
);
511 result
.mDoDefault
= !consumedFlags
;
512 if (NS_WARN_IF(NS_FAILED(result
.mResult
))) {
513 result
.mCanContinue
= false;
517 result
.mCanContinue
= NS_SUCCEEDED(IsValidStateForComposition());
521 TextInputProcessor::EventDispatcherResult
522 TextInputProcessor::MaybeDispatchKeyupForComposition(
523 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
) {
524 EventDispatcherResult result
;
526 if (!aKeyboardEvent
) {
530 // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
532 if (aKeyboardEvent
->mMessage
== eKeyDown
) {
536 // If the widget has been destroyed, we can do nothing here.
537 result
.mResult
= IsValidStateForComposition();
538 if (NS_FAILED(result
.mResult
)) {
539 result
.mCanContinue
= false;
543 result
.mResult
= KeyupInternal(*aKeyboardEvent
, aKeyFlags
, result
.mDoDefault
);
544 if (NS_WARN_IF(NS_FAILED(result
.mResult
))) {
545 result
.mCanContinue
= false;
549 result
.mCanContinue
= NS_SUCCEEDED(IsValidStateForComposition());
553 nsresult
TextInputProcessor::PrepareKeyboardEventForComposition(
554 KeyboardEvent
* aDOMKeyEvent
, uint32_t& aKeyFlags
, uint8_t aOptionalArgc
,
555 WidgetKeyboardEvent
*& aKeyboardEvent
) {
556 aKeyboardEvent
= nullptr;
558 aKeyboardEvent
= aOptionalArgc
&& aDOMKeyEvent
559 ? aDOMKeyEvent
->WidgetEventPtr()->AsKeyboardEvent()
561 if (!aKeyboardEvent
|| aOptionalArgc
< 2) {
565 if (!aKeyboardEvent
) {
569 if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent
))) {
570 return NS_ERROR_INVALID_ARG
;
577 TextInputProcessor::StartComposition(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
578 uint8_t aOptionalArgc
, bool* aSucceeded
) {
579 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
580 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
583 RefPtr
<KeyboardEvent
> keyEvent
;
585 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
586 if (NS_WARN_IF(!keyEvent
)) {
587 return NS_ERROR_INVALID_ARG
;
591 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
593 WidgetKeyboardEvent
* keyboardEvent
;
594 nsresult rv
= PrepareKeyboardEventForComposition(
595 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
596 if (NS_WARN_IF(NS_FAILED(rv
))) {
600 EventDispatcherResult dispatcherResult
=
601 MaybeDispatchKeydownForComposition(keyboardEvent
, aKeyFlags
);
602 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
603 !dispatcherResult
.mCanContinue
) {
604 return dispatcherResult
.mResult
;
607 if (dispatcherResult
.mDoDefault
) {
608 nsEventStatus status
= nsEventStatus_eIgnore
;
609 rv
= kungFuDeathGrip
->StartComposition(status
);
610 *aSucceeded
= status
!= nsEventStatus_eConsumeNoDefault
&&
611 kungFuDeathGrip
&& kungFuDeathGrip
->IsComposing();
614 MaybeDispatchKeyupForComposition(keyboardEvent
, aKeyFlags
);
616 if (NS_WARN_IF(NS_FAILED(rv
))) {
623 TextInputProcessor::SetPendingCompositionString(const nsAString
& aString
) {
624 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
625 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
626 nsresult rv
= IsValidStateForComposition();
627 if (NS_WARN_IF(NS_FAILED(rv
))) {
630 return kungFuDeathGrip
->SetPendingCompositionString(aString
);
634 TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength
,
635 uint32_t aAttribute
) {
636 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
637 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
638 TextRangeType textRangeType
;
639 switch (aAttribute
) {
640 case ATTR_RAW_CLAUSE
:
641 case ATTR_SELECTED_RAW_CLAUSE
:
642 case ATTR_CONVERTED_CLAUSE
:
643 case ATTR_SELECTED_CLAUSE
:
644 textRangeType
= ToTextRangeType(aAttribute
);
647 return NS_ERROR_INVALID_ARG
;
649 nsresult rv
= IsValidStateForComposition();
650 if (NS_WARN_IF(NS_FAILED(rv
))) {
653 return kungFuDeathGrip
->AppendClauseToPendingComposition(aLength
,
658 TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset
) {
659 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
660 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
661 nsresult rv
= IsValidStateForComposition();
662 if (NS_WARN_IF(NS_FAILED(rv
))) {
665 return kungFuDeathGrip
->SetCaretInPendingComposition(aOffset
, 0);
669 TextInputProcessor::FlushPendingComposition(Event
* aDOMKeyEvent
,
671 uint8_t aOptionalArgc
,
673 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
674 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
676 // Even if this doesn't flush pending composition actually, we need to reset
677 // pending composition for starting next composition with new user input.
678 AutoPendingCompositionResetter
resetter(this);
681 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
682 bool wasComposing
= IsComposing();
684 RefPtr
<KeyboardEvent
> keyEvent
;
686 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
687 if (NS_WARN_IF(!keyEvent
)) {
688 return NS_ERROR_INVALID_ARG
;
692 WidgetKeyboardEvent
* keyboardEvent
;
693 nsresult rv
= PrepareKeyboardEventForComposition(
694 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
695 if (NS_WARN_IF(NS_FAILED(rv
))) {
699 EventDispatcherResult dispatcherResult
=
700 MaybeDispatchKeydownForComposition(keyboardEvent
, aKeyFlags
);
701 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
702 !dispatcherResult
.mCanContinue
) {
703 return dispatcherResult
.mResult
;
706 // Even if the preceding keydown event was consumed, if the composition
707 // was already started, we shouldn't prevent the change of composition.
708 if (dispatcherResult
.mDoDefault
|| wasComposing
) {
709 // Preceding keydown event may cause destroying the widget.
710 if (NS_FAILED(IsValidStateForComposition())) {
713 nsEventStatus status
= nsEventStatus_eIgnore
;
714 rv
= kungFuDeathGrip
->FlushPendingComposition(status
);
715 *aSucceeded
= status
!= nsEventStatus_eConsumeNoDefault
;
718 MaybeDispatchKeyupForComposition(keyboardEvent
, aKeyFlags
);
720 if (NS_WARN_IF(NS_FAILED(rv
))) {
727 TextInputProcessor::CommitComposition(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
728 uint8_t aOptionalArgc
) {
729 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
731 RefPtr
<KeyboardEvent
> keyEvent
;
733 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
734 if (NS_WARN_IF(!keyEvent
)) {
735 return NS_ERROR_INVALID_ARG
;
739 WidgetKeyboardEvent
* keyboardEvent
;
740 nsresult rv
= PrepareKeyboardEventForComposition(
741 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
742 if (NS_WARN_IF(NS_FAILED(rv
))) {
746 return CommitCompositionInternal(keyboardEvent
, aKeyFlags
);
750 TextInputProcessor::CommitCompositionWith(const nsAString
& aCommitString
,
753 uint8_t aOptionalArgc
,
755 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
756 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
758 RefPtr
<KeyboardEvent
> keyEvent
;
760 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
761 if (NS_WARN_IF(!keyEvent
)) {
762 return NS_ERROR_INVALID_ARG
;
766 WidgetKeyboardEvent
* keyboardEvent
;
767 nsresult rv
= PrepareKeyboardEventForComposition(
768 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
769 if (NS_WARN_IF(NS_FAILED(rv
))) {
773 return CommitCompositionInternal(keyboardEvent
, aKeyFlags
, &aCommitString
,
777 nsresult
TextInputProcessor::CommitCompositionInternal(
778 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
,
779 const nsAString
* aCommitString
, bool* aSucceeded
) {
783 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
784 bool wasComposing
= IsComposing();
786 EventDispatcherResult dispatcherResult
=
787 MaybeDispatchKeydownForComposition(aKeyboardEvent
, aKeyFlags
);
788 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
789 !dispatcherResult
.mCanContinue
) {
790 return dispatcherResult
.mResult
;
793 // Even if the preceding keydown event was consumed, if the composition
794 // was already started, we shouldn't prevent the commit of composition.
796 if (dispatcherResult
.mDoDefault
|| wasComposing
) {
797 // Preceding keydown event may cause destroying the widget.
798 if (NS_FAILED(IsValidStateForComposition())) {
801 nsEventStatus status
= nsEventStatus_eIgnore
;
802 rv
= kungFuDeathGrip
->CommitComposition(status
, aCommitString
);
804 *aSucceeded
= status
!= nsEventStatus_eConsumeNoDefault
;
808 MaybeDispatchKeyupForComposition(aKeyboardEvent
, aKeyFlags
);
810 if (NS_WARN_IF(NS_FAILED(rv
))) {
817 TextInputProcessor::CancelComposition(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
818 uint8_t aOptionalArgc
) {
819 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
821 RefPtr
<KeyboardEvent
> keyEvent
;
823 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
824 if (NS_WARN_IF(!keyEvent
)) {
825 return NS_ERROR_INVALID_ARG
;
829 WidgetKeyboardEvent
* keyboardEvent
;
830 nsresult rv
= PrepareKeyboardEventForComposition(
831 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
832 if (NS_WARN_IF(NS_FAILED(rv
))) {
836 return CancelCompositionInternal(keyboardEvent
, aKeyFlags
);
839 nsresult
TextInputProcessor::CancelCompositionInternal(
840 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
) {
841 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
843 EventDispatcherResult dispatcherResult
=
844 MaybeDispatchKeydownForComposition(aKeyboardEvent
, aKeyFlags
);
845 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
846 !dispatcherResult
.mCanContinue
) {
847 return dispatcherResult
.mResult
;
850 nsEventStatus status
= nsEventStatus_eIgnore
;
851 nsresult rv
= kungFuDeathGrip
->CommitComposition(status
, &EmptyString());
853 MaybeDispatchKeyupForComposition(aKeyboardEvent
, aKeyFlags
);
855 if (NS_WARN_IF(NS_FAILED(rv
))) {
862 TextInputProcessor::NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
863 const IMENotification
& aNotification
) {
864 // If This is called while this is being initialized, ignore the call.
865 // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
866 // we can say, TextInputProcessor doesn't implement any handlers of the
867 // requests and notifications.
869 return NS_ERROR_NOT_IMPLEMENTED
;
871 MOZ_ASSERT(aTextEventDispatcher
== mDispatcher
,
872 "Wrong TextEventDispatcher notifies this");
873 NS_ASSERTION(mForTests
|| mCallback
,
874 "mCallback can be null only when IME is initialized for tests");
876 RefPtr
<TextInputProcessorNotification
> notification
;
877 switch (aNotification
.mMessage
) {
878 case REQUEST_TO_COMMIT_COMPOSITION
: {
879 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
880 "Why is this requested without composition?");
881 notification
= new TextInputProcessorNotification("request-to-commit");
884 case REQUEST_TO_CANCEL_COMPOSITION
: {
885 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
886 "Why is this requested without composition?");
887 notification
= new TextInputProcessorNotification("request-to-cancel");
890 case NOTIFY_IME_OF_FOCUS
:
891 notification
= new TextInputProcessorNotification("notify-focus");
893 case NOTIFY_IME_OF_BLUR
:
894 notification
= new TextInputProcessorNotification("notify-blur");
896 case NOTIFY_IME_OF_TEXT_CHANGE
:
898 new TextInputProcessorNotification(aNotification
.mTextChangeData
);
900 case NOTIFY_IME_OF_SELECTION_CHANGE
:
901 notification
= new TextInputProcessorNotification(
902 aNotification
.mSelectionChangeData
);
904 case NOTIFY_IME_OF_POSITION_CHANGE
:
906 new TextInputProcessorNotification("notify-position-change");
909 return NS_ERROR_NOT_IMPLEMENTED
;
911 MOZ_RELEASE_ASSERT(notification
);
913 nsresult rv
= mCallback
->OnNotify(this, notification
, &result
);
914 if (NS_WARN_IF(NS_FAILED(rv
))) {
917 return result
? NS_OK
: NS_ERROR_FAILURE
;
920 switch (aNotification
.mMessage
) {
921 case REQUEST_TO_COMMIT_COMPOSITION
: {
922 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
923 "Why is this requested without composition?");
924 CommitCompositionInternal();
927 case REQUEST_TO_CANCEL_COMPOSITION
: {
928 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
929 "Why is this requested without composition?");
930 CancelCompositionInternal();
934 return NS_ERROR_NOT_IMPLEMENTED
;
938 NS_IMETHODIMP_(IMENotificationRequests
)
939 TextInputProcessor::GetIMENotificationRequests() {
940 // TextInputProcessor should support all change notifications.
941 return IMENotificationRequests(
942 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
943 IMENotificationRequests::NOTIFY_POSITION_CHANGE
);
947 TextInputProcessor::OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) {
948 // If This is called while this is being initialized, ignore the call.
952 MOZ_ASSERT(aTextEventDispatcher
== mDispatcher
,
953 "Wrong TextEventDispatcher notifies this");
954 UnlinkFromTextEventDispatcher();
958 TextInputProcessor::WillDispatchKeyboardEvent(
959 TextEventDispatcher
* aTextEventDispatcher
,
960 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aIndexOfKeypress
,
962 // TextInputProcessor doesn't set alternative char code nor modify charCode
963 // even when Ctrl key is pressed.
966 nsresult
TextInputProcessor::PrepareKeyboardEventToDispatch(
967 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aKeyFlags
) {
968 if (NS_WARN_IF(aKeyboardEvent
.mCodeNameIndex
== CODE_NAME_INDEX_USE_STRING
)) {
969 return NS_ERROR_INVALID_ARG
;
971 if ((aKeyFlags
& KEY_NON_PRINTABLE_KEY
) &&
972 NS_WARN_IF(aKeyboardEvent
.mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
)) {
973 return NS_ERROR_INVALID_ARG
;
975 if ((aKeyFlags
& KEY_FORCE_PRINTABLE_KEY
) &&
976 aKeyboardEvent
.mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
) {
977 aKeyboardEvent
.GetDOMKeyName(aKeyboardEvent
.mKeyValue
);
978 aKeyboardEvent
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
980 if (aKeyFlags
& KEY_KEEP_KEY_LOCATION_STANDARD
) {
981 // If .location is initialized with specific value, using
982 // KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller.
983 // Let's throw an exception for notifying the developer of this bug.
984 if (NS_WARN_IF(aKeyboardEvent
.mLocation
)) {
985 return NS_ERROR_INVALID_ARG
;
987 } else if (!aKeyboardEvent
.mLocation
) {
988 // If KeyboardEvent.mLocation is 0, it may be uninitialized. If so, we
989 // should compute proper mLocation value from its .code value.
990 aKeyboardEvent
.mLocation
=
991 WidgetKeyboardEvent::ComputeLocationFromCodeValue(
992 aKeyboardEvent
.mCodeNameIndex
);
995 if (aKeyFlags
& KEY_KEEP_KEYCODE_ZERO
) {
996 // If .keyCode is initialized with specific value, using
997 // KEY_KEEP_KEYCODE_ZERO must be a bug of the caller. Let's throw an
998 // exception for notifying the developer of such bug.
999 if (NS_WARN_IF(aKeyboardEvent
.mKeyCode
)) {
1000 return NS_ERROR_INVALID_ARG
;
1002 } else if (!aKeyboardEvent
.mKeyCode
&&
1003 aKeyboardEvent
.mKeyNameIndex
> KEY_NAME_INDEX_Unidentified
&&
1004 aKeyboardEvent
.mKeyNameIndex
< KEY_NAME_INDEX_USE_STRING
) {
1005 // If KeyboardEvent.keyCode is 0, it may be uninitialized. If so, we may
1006 // be able to decide a good .keyCode value if the .key value is a
1007 // non-printable key.
1008 aKeyboardEvent
.mKeyCode
=
1009 WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
1010 aKeyboardEvent
.mKeyNameIndex
);
1013 aKeyboardEvent
.mIsSynthesizedByTIP
= true;
1014 aKeyboardEvent
.mFlags
.mIsSynthesizedForTests
= mForTests
;
1019 nsresult
TextInputProcessor::InitEditCommands(
1020 WidgetKeyboardEvent
& aKeyboardEvent
) const {
1021 MOZ_ASSERT(XRE_IsContentProcess());
1022 MOZ_ASSERT(aKeyboardEvent
.mMessage
== eKeyPress
);
1024 // When this emulates real input only in content process, we need to
1025 // initialize edit commands with the main process's widget via PuppetWidget
1026 // because they are initialized by BrowserParent before content process treats
1028 // And also when this synthesizes keyboard events for tests, we need default
1029 // shortcut keys on the platform for making any developers get constant
1030 // results in any environments.
1032 // Note that retrieving edit commands via PuppetWidget is expensive.
1033 // Let's skip it when the keyboard event is inputting text.
1034 if (aKeyboardEvent
.IsInputtingText()) {
1035 aKeyboardEvent
.PreventNativeKeyBindings();
1039 Maybe
<WritingMode
> writingMode
;
1040 if (RefPtr
<TextEventDispatcher
> dispatcher
= mDispatcher
) {
1041 writingMode
= dispatcher
->MaybeQueryWritingModeAtSelection();
1044 // FYI: WidgetKeyboardEvent::InitAllEditCommands() isn't available here
1045 // since it checks whether it's called in the main process to
1046 // avoid performance issues so that we need to initialize each
1047 // command manually here.
1048 if (NS_WARN_IF(!aKeyboardEvent
.InitEditCommandsFor(
1049 NativeKeyBindingsType::SingleLineEditor
, writingMode
))) {
1050 return NS_ERROR_NOT_AVAILABLE
;
1052 if (NS_WARN_IF(!aKeyboardEvent
.InitEditCommandsFor(
1053 NativeKeyBindingsType::MultiLineEditor
, writingMode
))) {
1054 return NS_ERROR_NOT_AVAILABLE
;
1056 if (NS_WARN_IF(!aKeyboardEvent
.InitEditCommandsFor(
1057 NativeKeyBindingsType::RichTextEditor
, writingMode
))) {
1058 return NS_ERROR_NOT_AVAILABLE
;
1065 TextInputProcessor::Keydown(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
1066 uint8_t aOptionalArgc
, uint32_t* aConsumedFlags
) {
1067 MOZ_RELEASE_ASSERT(aConsumedFlags
, "aConsumedFlags must not be nullptr");
1068 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1069 if (!aOptionalArgc
) {
1072 if (NS_WARN_IF(!aDOMKeyEvent
)) {
1073 return NS_ERROR_INVALID_ARG
;
1075 WidgetKeyboardEvent
* originalKeyEvent
=
1076 aDOMKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
1077 if (NS_WARN_IF(!originalKeyEvent
)) {
1078 return NS_ERROR_INVALID_ARG
;
1080 return KeydownInternal(*originalKeyEvent
, aKeyFlags
, true, *aConsumedFlags
);
1083 nsresult
TextInputProcessor::Keydown(const WidgetKeyboardEvent
& aKeyboardEvent
,
1085 uint32_t* aConsumedFlags
) {
1086 uint32_t consumedFlags
= 0;
1087 return KeydownInternal(aKeyboardEvent
, aKeyFlags
, true,
1088 aConsumedFlags
? *aConsumedFlags
: consumedFlags
);
1091 nsresult
TextInputProcessor::KeydownInternal(
1092 const WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aKeyFlags
,
1093 bool aAllowToDispatchKeypress
, uint32_t& aConsumedFlags
) {
1094 aConsumedFlags
= KEYEVENT_NOT_CONSUMED
;
1096 // We shouldn't modify the internal WidgetKeyboardEvent.
1097 WidgetKeyboardEvent
keyEvent(aKeyboardEvent
);
1098 keyEvent
.mFlags
.mIsTrusted
= true;
1099 keyEvent
.mMessage
= eKeyDown
;
1100 nsresult rv
= PrepareKeyboardEventToDispatch(keyEvent
, aKeyFlags
);
1101 if (NS_WARN_IF(NS_FAILED(rv
))) {
1105 aConsumedFlags
= (aKeyFlags
& KEY_DEFAULT_PREVENTED
) ? KEYDOWN_IS_CONSUMED
1106 : KEYEVENT_NOT_CONSUMED
;
1108 if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent
.mKeyNameIndex
)) {
1109 ModifierKeyData
modifierKeyData(keyEvent
);
1110 if (WidgetKeyboardEvent::IsLockableModifier(keyEvent
.mKeyNameIndex
)) {
1111 // If the modifier key is lockable modifier key such as CapsLock,
1112 // let's toggle modifier key state at keydown.
1113 ToggleModifierKey(modifierKeyData
);
1115 // Activate modifier flag before dispatching keydown event (i.e., keydown
1116 // event should indicate the releasing modifier is active.
1117 ActivateModifierKey(modifierKeyData
);
1119 if (aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
) {
1122 } else if (NS_WARN_IF(aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
)) {
1123 return NS_ERROR_INVALID_ARG
;
1125 keyEvent
.mModifiers
= GetActiveModifiers();
1127 if (!aAllowToDispatchKeypress
&&
1128 !(aKeyFlags
& KEY_DONT_MARK_KEYDOWN_AS_PROCESSED
)) {
1129 keyEvent
.mKeyCode
= NS_VK_PROCESSKEY
;
1130 keyEvent
.mKeyNameIndex
= KEY_NAME_INDEX_Process
;
1133 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
1134 rv
= IsValidStateForComposition();
1135 if (NS_WARN_IF(NS_FAILED(rv
))) {
1139 nsEventStatus status
=
1140 aConsumedFlags
? nsEventStatus_eConsumeNoDefault
: nsEventStatus_eIgnore
;
1141 if (!kungFuDeathGrip
->DispatchKeyboardEvent(eKeyDown
, keyEvent
, status
)) {
1142 // If keydown event isn't dispatched, we don't need to dispatch keypress
1147 aConsumedFlags
|= (status
== nsEventStatus_eConsumeNoDefault
)
1148 ? KEYDOWN_IS_CONSUMED
1149 : KEYEVENT_NOT_CONSUMED
;
1151 if (!aAllowToDispatchKeypress
) {
1155 keyEvent
.mMessage
= eKeyPress
;
1157 // Only `eKeyPress` events, editor wants to execute system default edit
1158 // commands mapped to the key combination. In e10s world, edit commands can
1159 // be retrieved only in the parent process due to the performance reason.
1160 // Therefore, BrowserParent initializes edit commands for all cases before
1161 // sending the event to focused content process. For emulating this, we
1162 // need to do it now for synthesizing `eKeyPress` events if and only if
1163 // we're dispatching the events in a content process.
1164 if (XRE_IsContentProcess()) {
1165 nsresult rv
= InitEditCommands(keyEvent
);
1166 if (NS_WARN_IF(NS_FAILED(rv
))) {
1170 if (kungFuDeathGrip
->MaybeDispatchKeypressEvents(keyEvent
, status
)) {
1171 aConsumedFlags
|= (status
== nsEventStatus_eConsumeNoDefault
)
1172 ? KEYPRESS_IS_CONSUMED
1173 : KEYEVENT_NOT_CONSUMED
;
1180 TextInputProcessor::Keyup(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
1181 uint8_t aOptionalArgc
, bool* aDoDefault
) {
1182 MOZ_RELEASE_ASSERT(aDoDefault
, "aDoDefault must not be nullptr");
1183 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1184 if (!aOptionalArgc
) {
1187 if (NS_WARN_IF(!aDOMKeyEvent
)) {
1188 return NS_ERROR_INVALID_ARG
;
1190 WidgetKeyboardEvent
* originalKeyEvent
=
1191 aDOMKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
1192 if (NS_WARN_IF(!originalKeyEvent
)) {
1193 return NS_ERROR_INVALID_ARG
;
1195 return KeyupInternal(*originalKeyEvent
, aKeyFlags
, *aDoDefault
);
1198 nsresult
TextInputProcessor::Keyup(const WidgetKeyboardEvent
& aKeyboardEvent
,
1199 uint32_t aKeyFlags
, bool* aDoDefault
) {
1200 bool doDefault
= false;
1201 return KeyupInternal(aKeyboardEvent
, aKeyFlags
,
1202 aDoDefault
? *aDoDefault
: doDefault
);
1205 nsresult
TextInputProcessor::KeyupInternal(
1206 const WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aKeyFlags
,
1210 // We shouldn't modify the internal WidgetKeyboardEvent.
1211 WidgetKeyboardEvent
keyEvent(aKeyboardEvent
);
1212 keyEvent
.mFlags
.mIsTrusted
= true;
1213 keyEvent
.mMessage
= eKeyUp
;
1214 nsresult rv
= PrepareKeyboardEventToDispatch(keyEvent
, aKeyFlags
);
1215 if (NS_WARN_IF(NS_FAILED(rv
))) {
1219 aDoDefault
= !(aKeyFlags
& KEY_DEFAULT_PREVENTED
);
1221 if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent
.mKeyNameIndex
)) {
1222 if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent
.mKeyNameIndex
)) {
1223 // Inactivate modifier flag before dispatching keyup event (i.e., keyup
1224 // event shouldn't indicate the releasing modifier is active.
1225 InactivateModifierKey(ModifierKeyData(keyEvent
));
1227 if (aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
) {
1230 } else if (NS_WARN_IF(aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
)) {
1231 return NS_ERROR_INVALID_ARG
;
1233 keyEvent
.mModifiers
= GetActiveModifiers();
1235 if (aKeyFlags
& KEY_MARK_KEYUP_AS_PROCESSED
) {
1236 keyEvent
.mKeyCode
= NS_VK_PROCESSKEY
;
1237 keyEvent
.mKeyNameIndex
= KEY_NAME_INDEX_Process
;
1240 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
1241 rv
= IsValidStateForComposition();
1242 if (NS_WARN_IF(NS_FAILED(rv
))) {
1246 nsEventStatus status
=
1247 aDoDefault
? nsEventStatus_eIgnore
: nsEventStatus_eConsumeNoDefault
;
1248 kungFuDeathGrip
->DispatchKeyboardEvent(eKeyUp
, keyEvent
, status
);
1249 aDoDefault
= (status
!= nsEventStatus_eConsumeNoDefault
);
1254 TextInputProcessor::GetModifierState(const nsAString
& aModifierKeyName
,
1256 MOZ_RELEASE_ASSERT(aActive
, "aActive must not be null");
1257 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1258 Modifiers modifier
= WidgetInputEvent::GetModifier(aModifierKeyName
);
1259 *aActive
= ((GetActiveModifiers() & modifier
) != 0);
1264 TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor
* aOther
) {
1265 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1267 mModifierKeyDataArray
= nullptr;
1270 TextInputProcessor
* other
= static_cast<TextInputProcessor
*>(aOther
);
1271 if (!other
->mModifierKeyDataArray
) {
1272 other
->mModifierKeyDataArray
= new ModifierKeyDataArray();
1274 mModifierKeyDataArray
= other
->mModifierKeyDataArray
;
1279 TextInputProcessor::ComputeCodeValueOfNonPrintableKey(
1280 const nsAString
& aKeyValue
, JS::Handle
<JS::Value
> aLocation
,
1281 uint8_t aOptionalArgc
, nsAString
& aCodeValue
) {
1282 aCodeValue
.Truncate();
1284 Maybe
<uint32_t> location
;
1285 if (aOptionalArgc
) {
1286 if (aLocation
.isNullOrUndefined()) {
1287 // location should be nothing.
1288 } else if (aLocation
.isInt32()) {
1289 location
= mozilla::Some(static_cast<uint32_t>(aLocation
.toInt32()));
1291 NS_WARNING_ASSERTION(aLocation
.isNullOrUndefined() || aLocation
.isInt32(),
1292 "aLocation must be undefined, null or int");
1293 return NS_ERROR_INVALID_ARG
;
1297 KeyNameIndex keyNameIndex
= WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue
);
1298 if (keyNameIndex
== KEY_NAME_INDEX_Unidentified
||
1299 keyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
1303 CodeNameIndex codeNameIndex
=
1304 WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex
,
1306 if (codeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1309 MOZ_ASSERT(codeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
1310 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex
, aCodeValue
);
1315 TextInputProcessor::GuessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
1316 const nsAString
& aKeyValue
, JS::Handle
<JS::Value
> aLocation
,
1317 uint8_t aOptionalArgc
, nsAString
& aCodeValue
) {
1318 aCodeValue
.Truncate();
1320 Maybe
<uint32_t> location
;
1321 if (aOptionalArgc
) {
1322 if (aLocation
.isNullOrUndefined()) {
1323 // location should be nothing.
1324 } else if (aLocation
.isInt32()) {
1325 location
= mozilla::Some(static_cast<uint32_t>(aLocation
.toInt32()));
1327 NS_WARNING_ASSERTION(aLocation
.isNullOrUndefined() || aLocation
.isInt32(),
1328 "aLocation must be undefined, null or int");
1329 return NS_ERROR_INVALID_ARG
;
1332 CodeNameIndex codeNameIndex
=
1333 GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(aKeyValue
, location
);
1334 if (codeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1337 MOZ_ASSERT(codeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
1338 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex
, aCodeValue
);
1344 TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
1345 const nsAString
& aKeyValue
, const Maybe
<uint32_t>& aLocation
) {
1346 if (aKeyValue
.IsEmpty()) {
1347 return CODE_NAME_INDEX_UNKNOWN
;
1349 // US keyboard layout can input only one character per key. So, we can
1350 // assume that if the key value is 2 or more characters, it's a known
1351 // key name or not a usual key emulation.
1352 if (aKeyValue
.Length() > 1) {
1353 return CODE_NAME_INDEX_UNKNOWN
;
1355 if (aLocation
.isSome() &&
1356 aLocation
.value() ==
1357 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD
) {
1358 switch (aKeyValue
[0]) {
1360 return CODE_NAME_INDEX_NumpadAdd
;
1362 return CODE_NAME_INDEX_NumpadSubtract
;
1364 return CODE_NAME_INDEX_NumpadMultiply
;
1366 return CODE_NAME_INDEX_NumpadDivide
;
1368 return CODE_NAME_INDEX_NumpadDecimal
;
1370 return CODE_NAME_INDEX_Numpad0
;
1372 return CODE_NAME_INDEX_Numpad1
;
1374 return CODE_NAME_INDEX_Numpad2
;
1376 return CODE_NAME_INDEX_Numpad3
;
1378 return CODE_NAME_INDEX_Numpad4
;
1380 return CODE_NAME_INDEX_Numpad5
;
1382 return CODE_NAME_INDEX_Numpad6
;
1384 return CODE_NAME_INDEX_Numpad7
;
1386 return CODE_NAME_INDEX_Numpad8
;
1388 return CODE_NAME_INDEX_Numpad9
;
1390 return CODE_NAME_INDEX_UNKNOWN
;
1394 if (aLocation
.isSome() &&
1395 aLocation
.value() !=
1396 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD
) {
1397 return CODE_NAME_INDEX_UNKNOWN
;
1400 // TODO: Support characters inputted with option key on macOS.
1401 switch (aKeyValue
[0]) {
1404 return CODE_NAME_INDEX_KeyA
;
1407 return CODE_NAME_INDEX_KeyB
;
1410 return CODE_NAME_INDEX_KeyC
;
1413 return CODE_NAME_INDEX_KeyD
;
1416 return CODE_NAME_INDEX_KeyE
;
1419 return CODE_NAME_INDEX_KeyF
;
1422 return CODE_NAME_INDEX_KeyG
;
1425 return CODE_NAME_INDEX_KeyH
;
1428 return CODE_NAME_INDEX_KeyI
;
1431 return CODE_NAME_INDEX_KeyJ
;
1434 return CODE_NAME_INDEX_KeyK
;
1437 return CODE_NAME_INDEX_KeyL
;
1440 return CODE_NAME_INDEX_KeyM
;
1443 return CODE_NAME_INDEX_KeyN
;
1446 return CODE_NAME_INDEX_KeyO
;
1449 return CODE_NAME_INDEX_KeyP
;
1452 return CODE_NAME_INDEX_KeyQ
;
1455 return CODE_NAME_INDEX_KeyR
;
1458 return CODE_NAME_INDEX_KeyS
;
1461 return CODE_NAME_INDEX_KeyT
;
1464 return CODE_NAME_INDEX_KeyU
;
1467 return CODE_NAME_INDEX_KeyV
;
1470 return CODE_NAME_INDEX_KeyW
;
1473 return CODE_NAME_INDEX_KeyX
;
1476 return CODE_NAME_INDEX_KeyY
;
1479 return CODE_NAME_INDEX_KeyZ
;
1483 return CODE_NAME_INDEX_Backquote
;
1486 return CODE_NAME_INDEX_Digit1
;
1489 return CODE_NAME_INDEX_Digit2
;
1492 return CODE_NAME_INDEX_Digit3
;
1495 return CODE_NAME_INDEX_Digit4
;
1498 return CODE_NAME_INDEX_Digit5
;
1501 return CODE_NAME_INDEX_Digit6
;
1504 return CODE_NAME_INDEX_Digit7
;
1507 return CODE_NAME_INDEX_Digit8
;
1510 return CODE_NAME_INDEX_Digit9
;
1513 return CODE_NAME_INDEX_Digit0
;
1516 return CODE_NAME_INDEX_Minus
;
1519 return CODE_NAME_INDEX_Equal
;
1523 return CODE_NAME_INDEX_BracketLeft
;
1526 return CODE_NAME_INDEX_BracketRight
;
1529 return CODE_NAME_INDEX_Backslash
;
1533 return CODE_NAME_INDEX_Semicolon
;
1536 return CODE_NAME_INDEX_Quote
;
1540 return CODE_NAME_INDEX_Comma
;
1543 return CODE_NAME_INDEX_Period
;
1546 return CODE_NAME_INDEX_Slash
;
1549 return CODE_NAME_INDEX_Space
;
1552 return CODE_NAME_INDEX_UNKNOWN
;
1557 TextInputProcessor::GuessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
1558 const nsAString
& aKeyValue
, JS::Handle
<JS::Value
> aLocation
,
1559 uint8_t aOptionalArgc
, uint32_t* aKeyCodeValue
) {
1560 if (NS_WARN_IF(!aKeyCodeValue
)) {
1561 return NS_ERROR_INVALID_ARG
;
1564 Maybe
<uint32_t> location
;
1565 if (aOptionalArgc
) {
1566 if (aLocation
.isNullOrUndefined()) {
1567 // location should be nothing.
1568 } else if (aLocation
.isInt32()) {
1569 location
= mozilla::Some(static_cast<uint32_t>(aLocation
.toInt32()));
1571 NS_WARNING_ASSERTION(aLocation
.isNullOrUndefined() || aLocation
.isInt32(),
1572 "aLocation must be undefined, null or int");
1573 return NS_ERROR_INVALID_ARG
;
1578 GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue
, location
);
1583 uint32_t TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
1584 const nsAString
& aKeyValue
, const Maybe
<uint32_t>& aLocation
) {
1585 if (aKeyValue
.IsEmpty()) {
1588 // US keyboard layout can input only one character per key. So, we can
1589 // assume that if the key value is 2 or more characters, it's a known
1590 // key name of a non-printable key or not a usual key emulation.
1591 if (aKeyValue
.Length() > 1) {
1595 if (aLocation
.isSome() &&
1596 aLocation
.value() ==
1597 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD
) {
1598 switch (aKeyValue
[0]) {
1600 return dom::KeyboardEvent_Binding::DOM_VK_ADD
;
1602 return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT
;
1604 return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY
;
1606 return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE
;
1608 return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL
;
1610 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0
;
1612 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1
;
1614 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2
;
1616 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3
;
1618 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4
;
1620 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5
;
1622 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6
;
1624 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7
;
1626 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8
;
1628 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9
;
1634 if (aLocation
.isSome() &&
1635 aLocation
.value() !=
1636 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD
) {
1640 // TODO: Support characters inputted with option key on macOS.
1641 switch (aKeyValue
[0]) {
1644 return dom::KeyboardEvent_Binding::DOM_VK_A
;
1647 return dom::KeyboardEvent_Binding::DOM_VK_B
;
1650 return dom::KeyboardEvent_Binding::DOM_VK_C
;
1653 return dom::KeyboardEvent_Binding::DOM_VK_D
;
1656 return dom::KeyboardEvent_Binding::DOM_VK_E
;
1659 return dom::KeyboardEvent_Binding::DOM_VK_F
;
1662 return dom::KeyboardEvent_Binding::DOM_VK_G
;
1665 return dom::KeyboardEvent_Binding::DOM_VK_H
;
1668 return dom::KeyboardEvent_Binding::DOM_VK_I
;
1671 return dom::KeyboardEvent_Binding::DOM_VK_J
;
1674 return dom::KeyboardEvent_Binding::DOM_VK_K
;
1677 return dom::KeyboardEvent_Binding::DOM_VK_L
;
1680 return dom::KeyboardEvent_Binding::DOM_VK_M
;
1683 return dom::KeyboardEvent_Binding::DOM_VK_N
;
1686 return dom::KeyboardEvent_Binding::DOM_VK_O
;
1689 return dom::KeyboardEvent_Binding::DOM_VK_P
;
1692 return dom::KeyboardEvent_Binding::DOM_VK_Q
;
1695 return dom::KeyboardEvent_Binding::DOM_VK_R
;
1698 return dom::KeyboardEvent_Binding::DOM_VK_S
;
1701 return dom::KeyboardEvent_Binding::DOM_VK_T
;
1704 return dom::KeyboardEvent_Binding::DOM_VK_U
;
1707 return dom::KeyboardEvent_Binding::DOM_VK_V
;
1710 return dom::KeyboardEvent_Binding::DOM_VK_W
;
1713 return dom::KeyboardEvent_Binding::DOM_VK_X
;
1716 return dom::KeyboardEvent_Binding::DOM_VK_Y
;
1719 return dom::KeyboardEvent_Binding::DOM_VK_Z
;
1723 return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE
;
1726 return dom::KeyboardEvent_Binding::DOM_VK_1
;
1729 return dom::KeyboardEvent_Binding::DOM_VK_2
;
1732 return dom::KeyboardEvent_Binding::DOM_VK_3
;
1735 return dom::KeyboardEvent_Binding::DOM_VK_4
;
1738 return dom::KeyboardEvent_Binding::DOM_VK_5
;
1741 return dom::KeyboardEvent_Binding::DOM_VK_6
;
1744 return dom::KeyboardEvent_Binding::DOM_VK_7
;
1747 return dom::KeyboardEvent_Binding::DOM_VK_8
;
1750 return dom::KeyboardEvent_Binding::DOM_VK_9
;
1753 return dom::KeyboardEvent_Binding::DOM_VK_0
;
1756 return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS
;
1759 return dom::KeyboardEvent_Binding::DOM_VK_EQUALS
;
1763 return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET
;
1766 return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET
;
1769 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH
;
1773 return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON
;
1776 return dom::KeyboardEvent_Binding::DOM_VK_QUOTE
;
1780 return dom::KeyboardEvent_Binding::DOM_VK_COMMA
;
1783 return dom::KeyboardEvent_Binding::DOM_VK_PERIOD
;
1786 return dom::KeyboardEvent_Binding::DOM_VK_SLASH
;
1789 return dom::KeyboardEvent_Binding::DOM_VK_SPACE
;
1796 /******************************************************************************
1797 * TextInputProcessor::AutoPendingCompositionResetter
1798 ******************************************************************************/
1799 TextInputProcessor::AutoPendingCompositionResetter::
1800 AutoPendingCompositionResetter(TextInputProcessor
* aTIP
)
1802 MOZ_RELEASE_ASSERT(mTIP
.get(), "mTIP must not be null");
1805 TextInputProcessor::AutoPendingCompositionResetter::
1806 ~AutoPendingCompositionResetter() {
1807 if (mTIP
->mDispatcher
) {
1808 mTIP
->mDispatcher
->ClearPendingComposition();
1812 /******************************************************************************
1813 * TextInputProcessor::ModifierKeyData
1814 ******************************************************************************/
1815 TextInputProcessor::ModifierKeyData::ModifierKeyData(
1816 const WidgetKeyboardEvent
& aKeyboardEvent
)
1817 : mKeyNameIndex(aKeyboardEvent
.mKeyNameIndex
),
1818 mCodeNameIndex(aKeyboardEvent
.mCodeNameIndex
) {
1819 mModifier
= WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex
);
1820 MOZ_ASSERT(mModifier
, "mKeyNameIndex must be a modifier key name");
1823 /******************************************************************************
1824 * TextInputProcessor::ModifierKeyDataArray
1825 ******************************************************************************/
1826 Modifiers
TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const {
1827 Modifiers result
= MODIFIER_NONE
;
1828 for (uint32_t i
= 0; i
< Length(); i
++) {
1829 result
|= ElementAt(i
).mModifier
;
1834 void TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
1835 const TextInputProcessor::ModifierKeyData
& aModifierKeyData
) {
1836 if (Contains(aModifierKeyData
)) {
1839 AppendElement(aModifierKeyData
);
1842 void TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey(
1843 const TextInputProcessor::ModifierKeyData
& aModifierKeyData
) {
1844 RemoveElement(aModifierKeyData
);
1847 void TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey(
1848 const TextInputProcessor::ModifierKeyData
& aModifierKeyData
) {
1849 auto index
= IndexOf(aModifierKeyData
);
1850 if (index
== NoIndex
) {
1851 AppendElement(aModifierKeyData
);
1854 RemoveElementAt(index
);
1857 } // namespace mozilla