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/StaticPrefs_test.h"
11 #include "mozilla/TextEventDispatcher.h"
12 #include "mozilla/TextEvents.h"
13 #include "mozilla/TextInputProcessor.h"
14 #include "mozilla/WritingModes.h"
15 #include "mozilla/widget/IMEData.h"
16 #include "mozilla/dom/KeyboardEvent.h"
17 #include "nsContentUtils.h"
18 #include "nsIDocShell.h"
19 #include "nsIWidget.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsPresContext.h"
23 using mozilla::dom::Event
;
24 using mozilla::dom::KeyboardEvent
;
25 using namespace mozilla::widget
;
29 /******************************************************************************
30 * TextInputProcessorNotification
31 ******************************************************************************/
33 class TextInputProcessorNotification final
34 : public nsITextInputProcessorNotification
{
35 using SelectionChangeData
= IMENotification::SelectionChangeData
;
36 using SelectionChangeDataBase
= IMENotification::SelectionChangeDataBase
;
37 using TextChangeData
= IMENotification::TextChangeData
;
38 using TextChangeDataBase
= IMENotification::TextChangeDataBase
;
41 explicit TextInputProcessorNotification(const char* aType
)
42 : mType(aType
), mTextChangeData() {}
44 explicit TextInputProcessorNotification(
45 const TextChangeDataBase
& aTextChangeData
)
46 : mType("notify-text-change"), mTextChangeData(aTextChangeData
) {}
48 explicit TextInputProcessorNotification(
49 const SelectionChangeDataBase
& aSelectionChangeData
)
50 : mType("notify-selection-change"),
51 mSelectionChangeData(aSelectionChangeData
) {
52 // SelectionChangeDataBase::mString still refers nsString instance owned
53 // by aSelectionChangeData. So, this needs to copy the instance.
54 nsString
* string
= new nsString(aSelectionChangeData
.String());
55 mSelectionChangeData
.mString
= string
;
60 NS_IMETHOD
GetType(nsACString
& aType
) final
{
65 // "notify-text-change" and "notify-selection-change"
66 NS_IMETHOD
GetOffset(uint32_t* aOffset
) final
{
67 if (NS_WARN_IF(!aOffset
)) {
68 return NS_ERROR_INVALID_ARG
;
70 if (IsSelectionChange()) {
71 *aOffset
= mSelectionChangeData
.mOffset
;
75 *aOffset
= mTextChangeData
.mStartOffset
;
78 return NS_ERROR_NOT_AVAILABLE
;
81 // "notify-selection-change"
82 NS_IMETHOD
GetText(nsAString
& aText
) final
{
83 if (IsSelectionChange()) {
84 aText
= mSelectionChangeData
.String();
87 return NS_ERROR_NOT_AVAILABLE
;
90 NS_IMETHOD
GetCollapsed(bool* aCollapsed
) final
{
91 if (NS_WARN_IF(!aCollapsed
)) {
92 return NS_ERROR_INVALID_ARG
;
94 if (IsSelectionChange()) {
95 *aCollapsed
= mSelectionChangeData
.IsCollapsed();
98 return NS_ERROR_NOT_AVAILABLE
;
101 NS_IMETHOD
GetLength(uint32_t* aLength
) final
{
102 if (NS_WARN_IF(!aLength
)) {
103 return NS_ERROR_INVALID_ARG
;
105 if (IsSelectionChange()) {
106 *aLength
= mSelectionChangeData
.Length();
109 return NS_ERROR_NOT_AVAILABLE
;
112 NS_IMETHOD
GetReversed(bool* aReversed
) final
{
113 if (NS_WARN_IF(!aReversed
)) {
114 return NS_ERROR_INVALID_ARG
;
116 if (IsSelectionChange()) {
117 *aReversed
= mSelectionChangeData
.mReversed
;
120 return NS_ERROR_NOT_AVAILABLE
;
123 NS_IMETHOD
GetWritingMode(nsACString
& aWritingMode
) final
{
124 if (IsSelectionChange()) {
125 WritingMode writingMode
= mSelectionChangeData
.GetWritingMode();
126 if (!writingMode
.IsVertical()) {
127 aWritingMode
.AssignLiteral("horizontal-tb");
128 } else if (writingMode
.IsVerticalLR()) {
129 aWritingMode
.AssignLiteral("vertical-lr");
131 aWritingMode
.AssignLiteral("vertical-rl");
135 return NS_ERROR_NOT_AVAILABLE
;
138 NS_IMETHOD
GetCausedByComposition(bool* aCausedByComposition
) final
{
139 if (NS_WARN_IF(!aCausedByComposition
)) {
140 return NS_ERROR_INVALID_ARG
;
142 if (IsSelectionChange()) {
143 *aCausedByComposition
= mSelectionChangeData
.mCausedByComposition
;
146 return NS_ERROR_NOT_AVAILABLE
;
149 NS_IMETHOD
GetCausedBySelectionEvent(bool* aCausedBySelectionEvent
) final
{
150 if (NS_WARN_IF(!aCausedBySelectionEvent
)) {
151 return NS_ERROR_INVALID_ARG
;
153 if (IsSelectionChange()) {
154 *aCausedBySelectionEvent
= mSelectionChangeData
.mCausedBySelectionEvent
;
157 return NS_ERROR_NOT_AVAILABLE
;
160 NS_IMETHOD
GetOccurredDuringComposition(
161 bool* aOccurredDuringComposition
) final
{
162 if (NS_WARN_IF(!aOccurredDuringComposition
)) {
163 return NS_ERROR_INVALID_ARG
;
165 if (IsSelectionChange()) {
166 *aOccurredDuringComposition
=
167 mSelectionChangeData
.mOccurredDuringComposition
;
170 return NS_ERROR_NOT_AVAILABLE
;
173 // "notify-text-change"
174 NS_IMETHOD
GetRemovedLength(uint32_t* aLength
) final
{
175 if (NS_WARN_IF(!aLength
)) {
176 return NS_ERROR_INVALID_ARG
;
178 if (IsTextChange()) {
179 *aLength
= mTextChangeData
.OldLength();
182 return NS_ERROR_NOT_AVAILABLE
;
185 NS_IMETHOD
GetAddedLength(uint32_t* aLength
) final
{
186 if (NS_WARN_IF(!aLength
)) {
187 return NS_ERROR_INVALID_ARG
;
189 if (IsTextChange()) {
190 *aLength
= mTextChangeData
.NewLength();
193 return NS_ERROR_NOT_AVAILABLE
;
196 NS_IMETHOD
GetCausedOnlyByComposition(bool* aCausedOnlyByComposition
) final
{
197 if (NS_WARN_IF(!aCausedOnlyByComposition
)) {
198 return NS_ERROR_INVALID_ARG
;
200 if (IsTextChange()) {
201 *aCausedOnlyByComposition
= mTextChangeData
.mCausedOnlyByComposition
;
204 return NS_ERROR_NOT_AVAILABLE
;
207 NS_IMETHOD
GetIncludingChangesDuringComposition(
208 bool* aIncludingChangesDuringComposition
) final
{
209 if (NS_WARN_IF(!aIncludingChangesDuringComposition
)) {
210 return NS_ERROR_INVALID_ARG
;
212 if (IsTextChange()) {
213 *aIncludingChangesDuringComposition
=
214 mTextChangeData
.mIncludingChangesDuringComposition
;
217 return NS_ERROR_NOT_AVAILABLE
;
220 NS_IMETHOD
GetIncludingChangesWithoutComposition(
221 bool* aIncludingChangesWithoutComposition
) final
{
222 if (NS_WARN_IF(!aIncludingChangesWithoutComposition
)) {
223 return NS_ERROR_INVALID_ARG
;
225 if (IsTextChange()) {
226 *aIncludingChangesWithoutComposition
=
227 mTextChangeData
.mIncludingChangesWithoutComposition
;
230 return NS_ERROR_NOT_AVAILABLE
;
234 virtual ~TextInputProcessorNotification() {
235 if (IsSelectionChange()) {
236 delete mSelectionChangeData
.mString
;
237 mSelectionChangeData
.mString
= nullptr;
241 bool IsTextChange() const {
242 return mType
.EqualsLiteral("notify-text-change");
245 bool IsSelectionChange() const {
246 return mType
.EqualsLiteral("notify-selection-change");
252 TextChangeDataBase mTextChangeData
;
253 SelectionChangeDataBase mSelectionChangeData
;
256 TextInputProcessorNotification() : mTextChangeData() {}
259 NS_IMPL_ISUPPORTS(TextInputProcessorNotification
,
260 nsITextInputProcessorNotification
)
262 /******************************************************************************
264 ******************************************************************************/
266 NS_IMPL_ISUPPORTS(TextInputProcessor
, nsITextInputProcessor
,
267 TextEventDispatcherListener
, nsISupportsWeakReference
)
269 TextInputProcessor::TextInputProcessor()
270 : mDispatcher(nullptr), mForTests(false) {}
272 TextInputProcessor::~TextInputProcessor() {
273 if (mDispatcher
&& mDispatcher
->IsComposing()) {
274 // If this is composing and not canceling the composition, nobody can steal
275 // the rights of TextEventDispatcher from this instance. Therefore, this
276 // needs to cancel the composition here.
277 if (NS_SUCCEEDED(IsValidStateForComposition())) {
278 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
279 nsEventStatus status
= nsEventStatus_eIgnore
;
280 kungFuDeathGrip
->CommitComposition(status
, &EmptyString());
285 bool TextInputProcessor::IsComposing() const {
286 return mDispatcher
&& mDispatcher
->IsComposing();
290 TextInputProcessor::GetHasComposition(bool* aHasComposition
) {
291 MOZ_RELEASE_ASSERT(aHasComposition
, "aHasComposition must not be nullptr");
292 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
293 *aHasComposition
= IsComposing();
298 TextInputProcessor::BeginInputTransaction(
299 mozIDOMWindow
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
301 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
302 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
303 if (NS_WARN_IF(!aCallback
)) {
305 return NS_ERROR_INVALID_ARG
;
307 return BeginInputTransactionInternal(aWindow
, aCallback
, false, *aSucceeded
);
311 TextInputProcessor::BeginInputTransactionForTests(
312 mozIDOMWindow
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
313 uint8_t aOptionalArgc
, bool* aSucceeded
) {
314 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
315 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
316 nsITextInputProcessorCallback
* callback
=
317 aOptionalArgc
>= 1 ? aCallback
: nullptr;
318 return BeginInputTransactionInternal(aWindow
, callback
, true, *aSucceeded
);
321 nsresult
TextInputProcessor::BeginInputTransactionForFuzzing(
322 nsPIDOMWindowInner
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
324 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
325 return BeginInputTransactionInternal(aWindow
, aCallback
, false, *aSucceeded
);
328 nsresult
TextInputProcessor::BeginInputTransactionInternal(
329 mozIDOMWindow
* aWindow
, nsITextInputProcessorCallback
* aCallback
,
330 bool aForTests
, bool& aSucceeded
) {
332 if (NS_WARN_IF(!aWindow
)) {
333 return NS_ERROR_INVALID_ARG
;
335 nsCOMPtr
<nsPIDOMWindowInner
> pWindow
= nsPIDOMWindowInner::From(aWindow
);
336 if (NS_WARN_IF(!pWindow
)) {
337 return NS_ERROR_INVALID_ARG
;
339 nsCOMPtr
<nsIDocShell
> docShell(pWindow
->GetDocShell());
340 if (NS_WARN_IF(!docShell
)) {
341 return NS_ERROR_FAILURE
;
343 RefPtr
<nsPresContext
> presContext
= docShell
->GetPresContext();
344 if (NS_WARN_IF(!presContext
)) {
345 return NS_ERROR_FAILURE
;
347 nsCOMPtr
<nsIWidget
> widget
= presContext
->GetRootWidget();
348 if (NS_WARN_IF(!widget
)) {
349 return NS_ERROR_FAILURE
;
352 RefPtr
<TextEventDispatcher
> dispatcher
= widget
->GetTextEventDispatcher();
353 MOZ_RELEASE_ASSERT(dispatcher
, "TextEventDispatcher must not be null");
355 // If the instance was initialized and is being initialized for same
356 // dispatcher and same purpose, we don't need to initialize the dispatcher
358 if (mDispatcher
&& dispatcher
== mDispatcher
&& aCallback
== mCallback
&&
359 aForTests
== mForTests
) {
364 // If this instance is composing or dispatching an event, don't allow to
365 // initialize again. Especially, if we allow to begin input transaction with
366 // another TextEventDispatcher during dispatching an event, it may cause that
367 // nobody cannot begin input transaction with it if the last event causes
368 // opening modal dialog.
370 (mDispatcher
->IsComposing() || mDispatcher
->IsDispatchingEvent())) {
371 return NS_ERROR_ALREADY_INITIALIZED
;
374 // And also if another instance is composing with the new dispatcher or
375 // dispatching an event, it'll fail to steal its ownership. Then, we should
376 // not throw an exception, just return false.
377 if (dispatcher
->IsComposing() || dispatcher
->IsDispatchingEvent()) {
381 // This instance has finished preparing to link to the dispatcher. Therefore,
382 // let's forget the old dispatcher and purpose.
384 mDispatcher
->EndInputTransaction(this);
385 if (NS_WARN_IF(mDispatcher
)) {
386 // Forcibly initialize the members if we failed to end the input
388 UnlinkFromTextEventDispatcher();
394 bool isAPZAware
= StaticPrefs::test_events_async_enabled();
395 rv
= dispatcher
->BeginTestInputTransaction(this, isAPZAware
);
397 rv
= dispatcher
->BeginInputTransaction(this);
400 if (NS_WARN_IF(NS_FAILED(rv
))) {
404 mDispatcher
= dispatcher
;
405 mCallback
= aCallback
;
406 mForTests
= aForTests
;
411 void TextInputProcessor::UnlinkFromTextEventDispatcher() {
412 mDispatcher
= nullptr;
415 nsCOMPtr
<nsITextInputProcessorCallback
> callback(mCallback
);
418 RefPtr
<TextInputProcessorNotification
> notification
=
419 new TextInputProcessorNotification("notify-end-input-transaction");
421 callback
->OnNotify(this, notification
, &result
);
425 nsresult
TextInputProcessor::IsValidStateForComposition() {
426 if (NS_WARN_IF(!mDispatcher
)) {
427 return NS_ERROR_NOT_INITIALIZED
;
430 nsresult rv
= mDispatcher
->GetState();
431 if (NS_WARN_IF(NS_FAILED(rv
))) {
438 bool TextInputProcessor::IsValidEventTypeForComposition(
439 const WidgetKeyboardEvent
& aKeyboardEvent
) const {
440 // The key event type of composition methods must be "", "keydown" or "keyup".
441 if (aKeyboardEvent
.mMessage
== eKeyDown
||
442 aKeyboardEvent
.mMessage
== eKeyUp
) {
445 if (aKeyboardEvent
.mMessage
== eUnidentifiedEvent
&&
446 aKeyboardEvent
.mSpecifiedEventType
&&
447 nsDependentAtomString(aKeyboardEvent
.mSpecifiedEventType
)
448 .EqualsLiteral("on")) {
454 TextInputProcessor::EventDispatcherResult
455 TextInputProcessor::MaybeDispatchKeydownForComposition(
456 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
) {
457 EventDispatcherResult result
;
459 result
.mResult
= IsValidStateForComposition();
460 if (NS_WARN_IF(NS_FAILED(result
.mResult
))) {
461 result
.mCanContinue
= false;
465 if (!aKeyboardEvent
) {
469 // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
471 if (aKeyboardEvent
->mMessage
== eKeyUp
) {
475 // Modifier keys are not allowed because managing modifier state in this
476 // method makes this messy.
477 if (NS_WARN_IF(aKeyboardEvent
->IsModifierKeyEvent())) {
478 result
.mResult
= NS_ERROR_INVALID_ARG
;
479 result
.mCanContinue
= false;
483 uint32_t consumedFlags
= 0;
486 KeydownInternal(*aKeyboardEvent
, aKeyFlags
, false, consumedFlags
);
487 result
.mDoDefault
= !consumedFlags
;
488 if (NS_WARN_IF(NS_FAILED(result
.mResult
))) {
489 result
.mCanContinue
= false;
493 result
.mCanContinue
= NS_SUCCEEDED(IsValidStateForComposition());
497 TextInputProcessor::EventDispatcherResult
498 TextInputProcessor::MaybeDispatchKeyupForComposition(
499 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
) {
500 EventDispatcherResult result
;
502 if (!aKeyboardEvent
) {
506 // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
508 if (aKeyboardEvent
->mMessage
== eKeyDown
) {
512 // If the widget has been destroyed, we can do nothing here.
513 result
.mResult
= IsValidStateForComposition();
514 if (NS_FAILED(result
.mResult
)) {
515 result
.mCanContinue
= false;
519 result
.mResult
= KeyupInternal(*aKeyboardEvent
, aKeyFlags
, result
.mDoDefault
);
520 if (NS_WARN_IF(NS_FAILED(result
.mResult
))) {
521 result
.mCanContinue
= false;
525 result
.mCanContinue
= NS_SUCCEEDED(IsValidStateForComposition());
529 nsresult
TextInputProcessor::PrepareKeyboardEventForComposition(
530 KeyboardEvent
* aDOMKeyEvent
, uint32_t& aKeyFlags
, uint8_t aOptionalArgc
,
531 WidgetKeyboardEvent
*& aKeyboardEvent
) {
532 aKeyboardEvent
= nullptr;
534 aKeyboardEvent
= aOptionalArgc
&& aDOMKeyEvent
535 ? aDOMKeyEvent
->WidgetEventPtr()->AsKeyboardEvent()
537 if (!aKeyboardEvent
|| aOptionalArgc
< 2) {
541 if (!aKeyboardEvent
) {
545 if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent
))) {
546 return NS_ERROR_INVALID_ARG
;
553 TextInputProcessor::StartComposition(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
554 uint8_t aOptionalArgc
, bool* aSucceeded
) {
555 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
556 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
559 RefPtr
<KeyboardEvent
> keyEvent
;
561 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
562 if (NS_WARN_IF(!keyEvent
)) {
563 return NS_ERROR_INVALID_ARG
;
567 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
569 WidgetKeyboardEvent
* keyboardEvent
;
570 nsresult rv
= PrepareKeyboardEventForComposition(
571 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
572 if (NS_WARN_IF(NS_FAILED(rv
))) {
576 EventDispatcherResult dispatcherResult
=
577 MaybeDispatchKeydownForComposition(keyboardEvent
, aKeyFlags
);
578 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
579 !dispatcherResult
.mCanContinue
) {
580 return dispatcherResult
.mResult
;
583 if (dispatcherResult
.mDoDefault
) {
584 nsEventStatus status
= nsEventStatus_eIgnore
;
585 rv
= kungFuDeathGrip
->StartComposition(status
);
586 *aSucceeded
= status
!= nsEventStatus_eConsumeNoDefault
&&
587 kungFuDeathGrip
&& kungFuDeathGrip
->IsComposing();
590 MaybeDispatchKeyupForComposition(keyboardEvent
, aKeyFlags
);
592 if (NS_WARN_IF(NS_FAILED(rv
))) {
599 TextInputProcessor::SetPendingCompositionString(const nsAString
& aString
) {
600 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
601 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
602 nsresult rv
= IsValidStateForComposition();
603 if (NS_WARN_IF(NS_FAILED(rv
))) {
606 return kungFuDeathGrip
->SetPendingCompositionString(aString
);
610 TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength
,
611 uint32_t aAttribute
) {
612 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
613 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
614 TextRangeType textRangeType
;
615 switch (aAttribute
) {
616 case ATTR_RAW_CLAUSE
:
617 case ATTR_SELECTED_RAW_CLAUSE
:
618 case ATTR_CONVERTED_CLAUSE
:
619 case ATTR_SELECTED_CLAUSE
:
620 textRangeType
= ToTextRangeType(aAttribute
);
623 return NS_ERROR_INVALID_ARG
;
625 nsresult rv
= IsValidStateForComposition();
626 if (NS_WARN_IF(NS_FAILED(rv
))) {
629 return kungFuDeathGrip
->AppendClauseToPendingComposition(aLength
,
634 TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset
) {
635 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
636 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
637 nsresult rv
= IsValidStateForComposition();
638 if (NS_WARN_IF(NS_FAILED(rv
))) {
641 return kungFuDeathGrip
->SetCaretInPendingComposition(aOffset
, 0);
645 TextInputProcessor::FlushPendingComposition(Event
* aDOMKeyEvent
,
647 uint8_t aOptionalArgc
,
649 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
650 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
652 // Even if this doesn't flush pending composition actually, we need to reset
653 // pending composition for starting next composition with new user input.
654 AutoPendingCompositionResetter
resetter(this);
657 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
658 bool wasComposing
= IsComposing();
660 RefPtr
<KeyboardEvent
> keyEvent
;
662 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
663 if (NS_WARN_IF(!keyEvent
)) {
664 return NS_ERROR_INVALID_ARG
;
668 WidgetKeyboardEvent
* keyboardEvent
;
669 nsresult rv
= PrepareKeyboardEventForComposition(
670 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
671 if (NS_WARN_IF(NS_FAILED(rv
))) {
675 EventDispatcherResult dispatcherResult
=
676 MaybeDispatchKeydownForComposition(keyboardEvent
, aKeyFlags
);
677 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
678 !dispatcherResult
.mCanContinue
) {
679 return dispatcherResult
.mResult
;
682 // Even if the preceding keydown event was consumed, if the composition
683 // was already started, we shouldn't prevent the change of composition.
684 if (dispatcherResult
.mDoDefault
|| wasComposing
) {
685 // Preceding keydown event may cause destroying the widget.
686 if (NS_FAILED(IsValidStateForComposition())) {
689 nsEventStatus status
= nsEventStatus_eIgnore
;
690 rv
= kungFuDeathGrip
->FlushPendingComposition(status
);
691 *aSucceeded
= status
!= nsEventStatus_eConsumeNoDefault
;
694 MaybeDispatchKeyupForComposition(keyboardEvent
, aKeyFlags
);
696 if (NS_WARN_IF(NS_FAILED(rv
))) {
703 TextInputProcessor::CommitComposition(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
704 uint8_t aOptionalArgc
) {
705 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
707 RefPtr
<KeyboardEvent
> keyEvent
;
709 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
710 if (NS_WARN_IF(!keyEvent
)) {
711 return NS_ERROR_INVALID_ARG
;
715 WidgetKeyboardEvent
* keyboardEvent
;
716 nsresult rv
= PrepareKeyboardEventForComposition(
717 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
718 if (NS_WARN_IF(NS_FAILED(rv
))) {
722 return CommitCompositionInternal(keyboardEvent
, aKeyFlags
);
726 TextInputProcessor::CommitCompositionWith(const nsAString
& aCommitString
,
729 uint8_t aOptionalArgc
,
731 MOZ_RELEASE_ASSERT(aSucceeded
, "aSucceeded must not be nullptr");
732 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
734 RefPtr
<KeyboardEvent
> keyEvent
;
736 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
737 if (NS_WARN_IF(!keyEvent
)) {
738 return NS_ERROR_INVALID_ARG
;
742 WidgetKeyboardEvent
* keyboardEvent
;
743 nsresult rv
= PrepareKeyboardEventForComposition(
744 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
745 if (NS_WARN_IF(NS_FAILED(rv
))) {
749 return CommitCompositionInternal(keyboardEvent
, aKeyFlags
, &aCommitString
,
753 nsresult
TextInputProcessor::CommitCompositionInternal(
754 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
,
755 const nsAString
* aCommitString
, bool* aSucceeded
) {
759 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
760 bool wasComposing
= IsComposing();
762 EventDispatcherResult dispatcherResult
=
763 MaybeDispatchKeydownForComposition(aKeyboardEvent
, aKeyFlags
);
764 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
765 !dispatcherResult
.mCanContinue
) {
766 return dispatcherResult
.mResult
;
769 // Even if the preceding keydown event was consumed, if the composition
770 // was already started, we shouldn't prevent the commit of composition.
772 if (dispatcherResult
.mDoDefault
|| wasComposing
) {
773 // Preceding keydown event may cause destroying the widget.
774 if (NS_FAILED(IsValidStateForComposition())) {
777 nsEventStatus status
= nsEventStatus_eIgnore
;
778 rv
= kungFuDeathGrip
->CommitComposition(status
, aCommitString
);
780 *aSucceeded
= status
!= nsEventStatus_eConsumeNoDefault
;
784 MaybeDispatchKeyupForComposition(aKeyboardEvent
, aKeyFlags
);
786 if (NS_WARN_IF(NS_FAILED(rv
))) {
793 TextInputProcessor::CancelComposition(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
794 uint8_t aOptionalArgc
) {
795 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
797 RefPtr
<KeyboardEvent
> keyEvent
;
799 keyEvent
= aDOMKeyEvent
->AsKeyboardEvent();
800 if (NS_WARN_IF(!keyEvent
)) {
801 return NS_ERROR_INVALID_ARG
;
805 WidgetKeyboardEvent
* keyboardEvent
;
806 nsresult rv
= PrepareKeyboardEventForComposition(
807 keyEvent
, aKeyFlags
, aOptionalArgc
, keyboardEvent
);
808 if (NS_WARN_IF(NS_FAILED(rv
))) {
812 return CancelCompositionInternal(keyboardEvent
, aKeyFlags
);
815 nsresult
TextInputProcessor::CancelCompositionInternal(
816 const WidgetKeyboardEvent
* aKeyboardEvent
, uint32_t aKeyFlags
) {
817 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
819 EventDispatcherResult dispatcherResult
=
820 MaybeDispatchKeydownForComposition(aKeyboardEvent
, aKeyFlags
);
821 if (NS_WARN_IF(NS_FAILED(dispatcherResult
.mResult
)) ||
822 !dispatcherResult
.mCanContinue
) {
823 return dispatcherResult
.mResult
;
826 nsEventStatus status
= nsEventStatus_eIgnore
;
827 nsresult rv
= kungFuDeathGrip
->CommitComposition(status
, &EmptyString());
829 MaybeDispatchKeyupForComposition(aKeyboardEvent
, aKeyFlags
);
831 if (NS_WARN_IF(NS_FAILED(rv
))) {
838 TextInputProcessor::NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
839 const IMENotification
& aNotification
) {
840 // If This is called while this is being initialized, ignore the call.
841 // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
842 // we can say, TextInputProcessor doesn't implement any handlers of the
843 // requests and notifications.
845 return NS_ERROR_NOT_IMPLEMENTED
;
847 MOZ_ASSERT(aTextEventDispatcher
== mDispatcher
,
848 "Wrong TextEventDispatcher notifies this");
849 NS_ASSERTION(mForTests
|| mCallback
,
850 "mCallback can be null only when IME is initialized for tests");
852 RefPtr
<TextInputProcessorNotification
> notification
;
853 switch (aNotification
.mMessage
) {
854 case REQUEST_TO_COMMIT_COMPOSITION
: {
855 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
856 "Why is this requested without composition?");
857 notification
= new TextInputProcessorNotification("request-to-commit");
860 case REQUEST_TO_CANCEL_COMPOSITION
: {
861 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
862 "Why is this requested without composition?");
863 notification
= new TextInputProcessorNotification("request-to-cancel");
866 case NOTIFY_IME_OF_FOCUS
:
867 notification
= new TextInputProcessorNotification("notify-focus");
869 case NOTIFY_IME_OF_BLUR
:
870 notification
= new TextInputProcessorNotification("notify-blur");
872 case NOTIFY_IME_OF_TEXT_CHANGE
:
874 new TextInputProcessorNotification(aNotification
.mTextChangeData
);
876 case NOTIFY_IME_OF_SELECTION_CHANGE
:
877 notification
= new TextInputProcessorNotification(
878 aNotification
.mSelectionChangeData
);
880 case NOTIFY_IME_OF_POSITION_CHANGE
:
882 new TextInputProcessorNotification("notify-position-change");
885 return NS_ERROR_NOT_IMPLEMENTED
;
887 MOZ_RELEASE_ASSERT(notification
);
889 nsresult rv
= mCallback
->OnNotify(this, notification
, &result
);
890 if (NS_WARN_IF(NS_FAILED(rv
))) {
893 return result
? NS_OK
: NS_ERROR_FAILURE
;
896 switch (aNotification
.mMessage
) {
897 case REQUEST_TO_COMMIT_COMPOSITION
: {
898 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
899 "Why is this requested without composition?");
900 CommitCompositionInternal();
903 case REQUEST_TO_CANCEL_COMPOSITION
: {
904 NS_ASSERTION(aTextEventDispatcher
->IsComposing(),
905 "Why is this requested without composition?");
906 CancelCompositionInternal();
910 return NS_ERROR_NOT_IMPLEMENTED
;
914 NS_IMETHODIMP_(IMENotificationRequests
)
915 TextInputProcessor::GetIMENotificationRequests() {
916 // TextInputProcessor should support all change notifications.
917 return IMENotificationRequests(
918 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
919 IMENotificationRequests::NOTIFY_POSITION_CHANGE
);
923 TextInputProcessor::OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) {
924 // If This is called while this is being initialized, ignore the call.
928 MOZ_ASSERT(aTextEventDispatcher
== mDispatcher
,
929 "Wrong TextEventDispatcher notifies this");
930 UnlinkFromTextEventDispatcher();
934 TextInputProcessor::WillDispatchKeyboardEvent(
935 TextEventDispatcher
* aTextEventDispatcher
,
936 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aIndexOfKeypress
,
938 // TextInputProcessor doesn't set alternative char code nor modify charCode
939 // even when Ctrl key is pressed.
942 nsresult
TextInputProcessor::PrepareKeyboardEventToDispatch(
943 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aKeyFlags
) {
944 if (NS_WARN_IF(aKeyboardEvent
.mCodeNameIndex
== CODE_NAME_INDEX_USE_STRING
)) {
945 return NS_ERROR_INVALID_ARG
;
947 if ((aKeyFlags
& KEY_NON_PRINTABLE_KEY
) &&
948 NS_WARN_IF(aKeyboardEvent
.mKeyNameIndex
== KEY_NAME_INDEX_USE_STRING
)) {
949 return NS_ERROR_INVALID_ARG
;
951 if ((aKeyFlags
& KEY_FORCE_PRINTABLE_KEY
) &&
952 aKeyboardEvent
.mKeyNameIndex
!= KEY_NAME_INDEX_USE_STRING
) {
953 aKeyboardEvent
.GetDOMKeyName(aKeyboardEvent
.mKeyValue
);
954 aKeyboardEvent
.mKeyNameIndex
= KEY_NAME_INDEX_USE_STRING
;
956 if (aKeyFlags
& KEY_KEEP_KEY_LOCATION_STANDARD
) {
957 // If .location is initialized with specific value, using
958 // KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller.
959 // Let's throw an exception for notifying the developer of this bug.
960 if (NS_WARN_IF(aKeyboardEvent
.mLocation
)) {
961 return NS_ERROR_INVALID_ARG
;
963 } else if (!aKeyboardEvent
.mLocation
) {
964 // If KeyboardEvent.mLocation is 0, it may be uninitialized. If so, we
965 // should compute proper mLocation value from its .code value.
966 aKeyboardEvent
.mLocation
=
967 WidgetKeyboardEvent::ComputeLocationFromCodeValue(
968 aKeyboardEvent
.mCodeNameIndex
);
971 if (aKeyFlags
& KEY_KEEP_KEYCODE_ZERO
) {
972 // If .keyCode is initialized with specific value, using
973 // KEY_KEEP_KEYCODE_ZERO must be a bug of the caller. Let's throw an
974 // exception for notifying the developer of such bug.
975 if (NS_WARN_IF(aKeyboardEvent
.mKeyCode
)) {
976 return NS_ERROR_INVALID_ARG
;
978 } else if (!aKeyboardEvent
.mKeyCode
&&
979 aKeyboardEvent
.mKeyNameIndex
> KEY_NAME_INDEX_Unidentified
&&
980 aKeyboardEvent
.mKeyNameIndex
< KEY_NAME_INDEX_USE_STRING
) {
981 // If KeyboardEvent.keyCode is 0, it may be uninitialized. If so, we may
982 // be able to decide a good .keyCode value if the .key value is a
983 // non-printable key.
984 aKeyboardEvent
.mKeyCode
=
985 WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
986 aKeyboardEvent
.mKeyNameIndex
);
989 aKeyboardEvent
.mIsSynthesizedByTIP
= true;
990 aKeyboardEvent
.mFlags
.mIsSynthesizedForTests
= mForTests
;
995 nsresult
TextInputProcessor::InitEditCommands(
996 WidgetKeyboardEvent
& aKeyboardEvent
) const {
997 MOZ_ASSERT(XRE_IsContentProcess());
998 MOZ_ASSERT(aKeyboardEvent
.mMessage
== eKeyPress
);
1000 // When this emulates real input only in content process, we need to
1001 // initialize edit commands with the main process's widget via PuppetWidget
1002 // because they are initialized by BrowserParent before content process treats
1004 // And also when this synthesizes keyboard events for tests, we need default
1005 // shortcut keys on the platform for making any developers get constant
1006 // results in any environments.
1008 // Note that retrieving edit commands via PuppetWidget is expensive.
1009 // Let's skip it when the keyboard event is inputting text.
1010 if (aKeyboardEvent
.IsInputtingText()) {
1011 aKeyboardEvent
.PreventNativeKeyBindings();
1015 Maybe
<WritingMode
> writingMode
;
1016 if (RefPtr
<TextEventDispatcher
> dispatcher
= mDispatcher
) {
1017 writingMode
= dispatcher
->MaybeWritingModeAtSelection();
1020 // FYI: WidgetKeyboardEvent::InitAllEditCommands() isn't available here
1021 // since it checks whether it's called in the main process to
1022 // avoid performance issues so that we need to initialize each
1023 // command manually here.
1024 if (NS_WARN_IF(!aKeyboardEvent
.InitEditCommandsFor(
1025 nsIWidget::NativeKeyBindingsForSingleLineEditor
, writingMode
))) {
1026 return NS_ERROR_NOT_AVAILABLE
;
1028 if (NS_WARN_IF(!aKeyboardEvent
.InitEditCommandsFor(
1029 nsIWidget::NativeKeyBindingsForMultiLineEditor
, writingMode
))) {
1030 return NS_ERROR_NOT_AVAILABLE
;
1032 if (NS_WARN_IF(!aKeyboardEvent
.InitEditCommandsFor(
1033 nsIWidget::NativeKeyBindingsForRichTextEditor
, writingMode
))) {
1034 return NS_ERROR_NOT_AVAILABLE
;
1041 TextInputProcessor::Keydown(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
1042 uint8_t aOptionalArgc
, uint32_t* aConsumedFlags
) {
1043 MOZ_RELEASE_ASSERT(aConsumedFlags
, "aConsumedFlags must not be nullptr");
1044 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1045 if (!aOptionalArgc
) {
1048 if (NS_WARN_IF(!aDOMKeyEvent
)) {
1049 return NS_ERROR_INVALID_ARG
;
1051 WidgetKeyboardEvent
* originalKeyEvent
=
1052 aDOMKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
1053 if (NS_WARN_IF(!originalKeyEvent
)) {
1054 return NS_ERROR_INVALID_ARG
;
1056 return KeydownInternal(*originalKeyEvent
, aKeyFlags
, true, *aConsumedFlags
);
1059 nsresult
TextInputProcessor::Keydown(const WidgetKeyboardEvent
& aKeyboardEvent
,
1061 uint32_t* aConsumedFlags
) {
1062 uint32_t consumedFlags
= 0;
1063 return KeydownInternal(aKeyboardEvent
, aKeyFlags
, true,
1064 aConsumedFlags
? *aConsumedFlags
: consumedFlags
);
1067 nsresult
TextInputProcessor::KeydownInternal(
1068 const WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aKeyFlags
,
1069 bool aAllowToDispatchKeypress
, uint32_t& aConsumedFlags
) {
1070 aConsumedFlags
= KEYEVENT_NOT_CONSUMED
;
1072 // We shouldn't modify the internal WidgetKeyboardEvent.
1073 WidgetKeyboardEvent
keyEvent(aKeyboardEvent
);
1074 keyEvent
.mFlags
.mIsTrusted
= true;
1075 keyEvent
.mMessage
= eKeyDown
;
1076 nsresult rv
= PrepareKeyboardEventToDispatch(keyEvent
, aKeyFlags
);
1077 if (NS_WARN_IF(NS_FAILED(rv
))) {
1081 aConsumedFlags
= (aKeyFlags
& KEY_DEFAULT_PREVENTED
) ? KEYDOWN_IS_CONSUMED
1082 : KEYEVENT_NOT_CONSUMED
;
1084 if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent
.mKeyNameIndex
)) {
1085 ModifierKeyData
modifierKeyData(keyEvent
);
1086 if (WidgetKeyboardEvent::IsLockableModifier(keyEvent
.mKeyNameIndex
)) {
1087 // If the modifier key is lockable modifier key such as CapsLock,
1088 // let's toggle modifier key state at keydown.
1089 ToggleModifierKey(modifierKeyData
);
1091 // Activate modifier flag before dispatching keydown event (i.e., keydown
1092 // event should indicate the releasing modifier is active.
1093 ActivateModifierKey(modifierKeyData
);
1095 if (aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
) {
1098 } else if (NS_WARN_IF(aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
)) {
1099 return NS_ERROR_INVALID_ARG
;
1101 keyEvent
.mModifiers
= GetActiveModifiers();
1103 if (!aAllowToDispatchKeypress
&&
1104 !(aKeyFlags
& KEY_DONT_MARK_KEYDOWN_AS_PROCESSED
)) {
1105 keyEvent
.mKeyCode
= NS_VK_PROCESSKEY
;
1106 keyEvent
.mKeyNameIndex
= KEY_NAME_INDEX_Process
;
1109 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
1110 rv
= IsValidStateForComposition();
1111 if (NS_WARN_IF(NS_FAILED(rv
))) {
1115 nsEventStatus status
=
1116 aConsumedFlags
? nsEventStatus_eConsumeNoDefault
: nsEventStatus_eIgnore
;
1117 if (!kungFuDeathGrip
->DispatchKeyboardEvent(eKeyDown
, keyEvent
, status
)) {
1118 // If keydown event isn't dispatched, we don't need to dispatch keypress
1123 aConsumedFlags
|= (status
== nsEventStatus_eConsumeNoDefault
)
1124 ? KEYDOWN_IS_CONSUMED
1125 : KEYEVENT_NOT_CONSUMED
;
1127 if (!aAllowToDispatchKeypress
) {
1131 keyEvent
.mMessage
= eKeyPress
;
1133 // Only `eKeyPress` events, editor wants to execute system default edit
1134 // commands mapped to the key combination. In e10s world, edit commands can
1135 // be retrieved only in the parent process due to the performance reason.
1136 // Therefore, BrowserParent initializes edit commands for all cases before
1137 // sending the event to focused content process. For emulating this, we
1138 // need to do it now for synthesizing `eKeyPress` events if and only if
1139 // we're dispatching the events in a content process.
1140 if (XRE_IsContentProcess()) {
1141 nsresult rv
= InitEditCommands(keyEvent
);
1142 if (NS_WARN_IF(NS_FAILED(rv
))) {
1146 if (kungFuDeathGrip
->MaybeDispatchKeypressEvents(keyEvent
, status
)) {
1147 aConsumedFlags
|= (status
== nsEventStatus_eConsumeNoDefault
)
1148 ? KEYPRESS_IS_CONSUMED
1149 : KEYEVENT_NOT_CONSUMED
;
1156 TextInputProcessor::Keyup(Event
* aDOMKeyEvent
, uint32_t aKeyFlags
,
1157 uint8_t aOptionalArgc
, bool* aDoDefault
) {
1158 MOZ_RELEASE_ASSERT(aDoDefault
, "aDoDefault must not be nullptr");
1159 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1160 if (!aOptionalArgc
) {
1163 if (NS_WARN_IF(!aDOMKeyEvent
)) {
1164 return NS_ERROR_INVALID_ARG
;
1166 WidgetKeyboardEvent
* originalKeyEvent
=
1167 aDOMKeyEvent
->WidgetEventPtr()->AsKeyboardEvent();
1168 if (NS_WARN_IF(!originalKeyEvent
)) {
1169 return NS_ERROR_INVALID_ARG
;
1171 return KeyupInternal(*originalKeyEvent
, aKeyFlags
, *aDoDefault
);
1174 nsresult
TextInputProcessor::Keyup(const WidgetKeyboardEvent
& aKeyboardEvent
,
1175 uint32_t aKeyFlags
, bool* aDoDefault
) {
1176 bool doDefault
= false;
1177 return KeyupInternal(aKeyboardEvent
, aKeyFlags
,
1178 aDoDefault
? *aDoDefault
: doDefault
);
1181 nsresult
TextInputProcessor::KeyupInternal(
1182 const WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aKeyFlags
,
1186 // We shouldn't modify the internal WidgetKeyboardEvent.
1187 WidgetKeyboardEvent
keyEvent(aKeyboardEvent
);
1188 keyEvent
.mFlags
.mIsTrusted
= true;
1189 keyEvent
.mMessage
= eKeyUp
;
1190 nsresult rv
= PrepareKeyboardEventToDispatch(keyEvent
, aKeyFlags
);
1191 if (NS_WARN_IF(NS_FAILED(rv
))) {
1195 aDoDefault
= !(aKeyFlags
& KEY_DEFAULT_PREVENTED
);
1197 if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent
.mKeyNameIndex
)) {
1198 if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent
.mKeyNameIndex
)) {
1199 // Inactivate modifier flag before dispatching keyup event (i.e., keyup
1200 // event shouldn't indicate the releasing modifier is active.
1201 InactivateModifierKey(ModifierKeyData(keyEvent
));
1203 if (aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
) {
1206 } else if (NS_WARN_IF(aKeyFlags
& KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT
)) {
1207 return NS_ERROR_INVALID_ARG
;
1209 keyEvent
.mModifiers
= GetActiveModifiers();
1211 if (aKeyFlags
& KEY_MARK_KEYUP_AS_PROCESSED
) {
1212 keyEvent
.mKeyCode
= NS_VK_PROCESSKEY
;
1213 keyEvent
.mKeyNameIndex
= KEY_NAME_INDEX_Process
;
1216 RefPtr
<TextEventDispatcher
> kungFuDeathGrip(mDispatcher
);
1217 rv
= IsValidStateForComposition();
1218 if (NS_WARN_IF(NS_FAILED(rv
))) {
1222 nsEventStatus status
=
1223 aDoDefault
? nsEventStatus_eIgnore
: nsEventStatus_eConsumeNoDefault
;
1224 kungFuDeathGrip
->DispatchKeyboardEvent(eKeyUp
, keyEvent
, status
);
1225 aDoDefault
= (status
!= nsEventStatus_eConsumeNoDefault
);
1230 TextInputProcessor::GetModifierState(const nsAString
& aModifierKeyName
,
1232 MOZ_RELEASE_ASSERT(aActive
, "aActive must not be null");
1233 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1234 Modifiers modifier
= WidgetInputEvent::GetModifier(aModifierKeyName
);
1235 *aActive
= ((GetActiveModifiers() & modifier
) != 0);
1240 TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor
* aOther
) {
1241 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1243 mModifierKeyDataArray
= nullptr;
1246 TextInputProcessor
* other
= static_cast<TextInputProcessor
*>(aOther
);
1247 if (!other
->mModifierKeyDataArray
) {
1248 other
->mModifierKeyDataArray
= new ModifierKeyDataArray();
1250 mModifierKeyDataArray
= other
->mModifierKeyDataArray
;
1255 TextInputProcessor::ComputeCodeValueOfNonPrintableKey(
1256 const nsAString
& aKeyValue
, JS::Handle
<JS::Value
> aLocation
,
1257 uint8_t aOptionalArgc
, nsAString
& aCodeValue
) {
1258 aCodeValue
.Truncate();
1260 Maybe
<uint32_t> location
;
1261 if (aOptionalArgc
) {
1262 if (aLocation
.isNullOrUndefined()) {
1263 // location should be nothing.
1264 } else if (aLocation
.isInt32()) {
1265 location
= mozilla::Some(static_cast<uint32_t>(aLocation
.toInt32()));
1267 NS_WARNING_ASSERTION(aLocation
.isNullOrUndefined() || aLocation
.isInt32(),
1268 "aLocation must be undefined, null or int");
1269 return NS_ERROR_INVALID_ARG
;
1273 KeyNameIndex keyNameIndex
= WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue
);
1274 if (keyNameIndex
== KEY_NAME_INDEX_Unidentified
||
1275 keyNameIndex
== KEY_NAME_INDEX_USE_STRING
) {
1279 CodeNameIndex codeNameIndex
=
1280 WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex
,
1282 if (codeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1285 MOZ_ASSERT(codeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
1286 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex
, aCodeValue
);
1291 TextInputProcessor::GuessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
1292 const nsAString
& aKeyValue
, JS::Handle
<JS::Value
> aLocation
,
1293 uint8_t aOptionalArgc
, nsAString
& aCodeValue
) {
1294 aCodeValue
.Truncate();
1296 Maybe
<uint32_t> location
;
1297 if (aOptionalArgc
) {
1298 if (aLocation
.isNullOrUndefined()) {
1299 // location should be nothing.
1300 } else if (aLocation
.isInt32()) {
1301 location
= mozilla::Some(static_cast<uint32_t>(aLocation
.toInt32()));
1303 NS_WARNING_ASSERTION(aLocation
.isNullOrUndefined() || aLocation
.isInt32(),
1304 "aLocation must be undefined, null or int");
1305 return NS_ERROR_INVALID_ARG
;
1308 CodeNameIndex codeNameIndex
=
1309 GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(aKeyValue
, location
);
1310 if (codeNameIndex
== CODE_NAME_INDEX_UNKNOWN
) {
1313 MOZ_ASSERT(codeNameIndex
!= CODE_NAME_INDEX_USE_STRING
);
1314 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex
, aCodeValue
);
1320 TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
1321 const nsAString
& aKeyValue
, const Maybe
<uint32_t>& aLocation
) {
1322 if (aKeyValue
.IsEmpty()) {
1323 return CODE_NAME_INDEX_UNKNOWN
;
1325 // US keyboard layout can input only one character per key. So, we can
1326 // assume that if the key value is 2 or more characters, it's a known
1327 // key name or not a usual key emulation.
1328 if (aKeyValue
.Length() > 1) {
1329 return CODE_NAME_INDEX_UNKNOWN
;
1331 if (aLocation
.isSome() &&
1332 aLocation
.value() ==
1333 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD
) {
1334 switch (aKeyValue
[0]) {
1336 return CODE_NAME_INDEX_NumpadAdd
;
1338 return CODE_NAME_INDEX_NumpadSubtract
;
1340 return CODE_NAME_INDEX_NumpadMultiply
;
1342 return CODE_NAME_INDEX_NumpadDivide
;
1344 return CODE_NAME_INDEX_NumpadDecimal
;
1346 return CODE_NAME_INDEX_Numpad0
;
1348 return CODE_NAME_INDEX_Numpad1
;
1350 return CODE_NAME_INDEX_Numpad2
;
1352 return CODE_NAME_INDEX_Numpad3
;
1354 return CODE_NAME_INDEX_Numpad4
;
1356 return CODE_NAME_INDEX_Numpad5
;
1358 return CODE_NAME_INDEX_Numpad6
;
1360 return CODE_NAME_INDEX_Numpad7
;
1362 return CODE_NAME_INDEX_Numpad8
;
1364 return CODE_NAME_INDEX_Numpad9
;
1366 return CODE_NAME_INDEX_UNKNOWN
;
1370 if (aLocation
.isSome() &&
1371 aLocation
.value() !=
1372 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD
) {
1373 return CODE_NAME_INDEX_UNKNOWN
;
1376 // TODO: Support characters inputted with option key on macOS.
1377 switch (aKeyValue
[0]) {
1380 return CODE_NAME_INDEX_KeyA
;
1383 return CODE_NAME_INDEX_KeyB
;
1386 return CODE_NAME_INDEX_KeyC
;
1389 return CODE_NAME_INDEX_KeyD
;
1392 return CODE_NAME_INDEX_KeyE
;
1395 return CODE_NAME_INDEX_KeyF
;
1398 return CODE_NAME_INDEX_KeyG
;
1401 return CODE_NAME_INDEX_KeyH
;
1404 return CODE_NAME_INDEX_KeyI
;
1407 return CODE_NAME_INDEX_KeyJ
;
1410 return CODE_NAME_INDEX_KeyK
;
1413 return CODE_NAME_INDEX_KeyL
;
1416 return CODE_NAME_INDEX_KeyM
;
1419 return CODE_NAME_INDEX_KeyN
;
1422 return CODE_NAME_INDEX_KeyO
;
1425 return CODE_NAME_INDEX_KeyP
;
1428 return CODE_NAME_INDEX_KeyQ
;
1431 return CODE_NAME_INDEX_KeyR
;
1434 return CODE_NAME_INDEX_KeyS
;
1437 return CODE_NAME_INDEX_KeyT
;
1440 return CODE_NAME_INDEX_KeyU
;
1443 return CODE_NAME_INDEX_KeyV
;
1446 return CODE_NAME_INDEX_KeyW
;
1449 return CODE_NAME_INDEX_KeyX
;
1452 return CODE_NAME_INDEX_KeyY
;
1455 return CODE_NAME_INDEX_KeyZ
;
1459 return CODE_NAME_INDEX_Backquote
;
1462 return CODE_NAME_INDEX_Digit1
;
1465 return CODE_NAME_INDEX_Digit2
;
1468 return CODE_NAME_INDEX_Digit3
;
1471 return CODE_NAME_INDEX_Digit4
;
1474 return CODE_NAME_INDEX_Digit5
;
1477 return CODE_NAME_INDEX_Digit6
;
1480 return CODE_NAME_INDEX_Digit7
;
1483 return CODE_NAME_INDEX_Digit8
;
1486 return CODE_NAME_INDEX_Digit9
;
1489 return CODE_NAME_INDEX_Digit0
;
1492 return CODE_NAME_INDEX_Minus
;
1495 return CODE_NAME_INDEX_Equal
;
1499 return CODE_NAME_INDEX_BracketLeft
;
1502 return CODE_NAME_INDEX_BracketRight
;
1505 return CODE_NAME_INDEX_Backslash
;
1509 return CODE_NAME_INDEX_Semicolon
;
1512 return CODE_NAME_INDEX_Quote
;
1516 return CODE_NAME_INDEX_Comma
;
1519 return CODE_NAME_INDEX_Period
;
1522 return CODE_NAME_INDEX_Slash
;
1525 return CODE_NAME_INDEX_Space
;
1528 return CODE_NAME_INDEX_UNKNOWN
;
1533 TextInputProcessor::GuessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
1534 const nsAString
& aKeyValue
, JS::Handle
<JS::Value
> aLocation
,
1535 uint8_t aOptionalArgc
, uint32_t* aKeyCodeValue
) {
1536 if (NS_WARN_IF(!aKeyCodeValue
)) {
1537 return NS_ERROR_INVALID_ARG
;
1540 Maybe
<uint32_t> location
;
1541 if (aOptionalArgc
) {
1542 if (aLocation
.isNullOrUndefined()) {
1543 // location should be nothing.
1544 } else if (aLocation
.isInt32()) {
1545 location
= mozilla::Some(static_cast<uint32_t>(aLocation
.toInt32()));
1547 NS_WARNING_ASSERTION(aLocation
.isNullOrUndefined() || aLocation
.isInt32(),
1548 "aLocation must be undefined, null or int");
1549 return NS_ERROR_INVALID_ARG
;
1554 GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue
, location
);
1559 uint32_t TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
1560 const nsAString
& aKeyValue
, const Maybe
<uint32_t>& aLocation
) {
1561 if (aKeyValue
.IsEmpty()) {
1564 // US keyboard layout can input only one character per key. So, we can
1565 // assume that if the key value is 2 or more characters, it's a known
1566 // key name of a non-printable key or not a usual key emulation.
1567 if (aKeyValue
.Length() > 1) {
1571 if (aLocation
.isSome() &&
1572 aLocation
.value() ==
1573 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD
) {
1574 switch (aKeyValue
[0]) {
1576 return dom::KeyboardEvent_Binding::DOM_VK_ADD
;
1578 return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT
;
1580 return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY
;
1582 return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE
;
1584 return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL
;
1586 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0
;
1588 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1
;
1590 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2
;
1592 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3
;
1594 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4
;
1596 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5
;
1598 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6
;
1600 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7
;
1602 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8
;
1604 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9
;
1610 if (aLocation
.isSome() &&
1611 aLocation
.value() !=
1612 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD
) {
1616 // TODO: Support characters inputted with option key on macOS.
1617 switch (aKeyValue
[0]) {
1620 return dom::KeyboardEvent_Binding::DOM_VK_A
;
1623 return dom::KeyboardEvent_Binding::DOM_VK_B
;
1626 return dom::KeyboardEvent_Binding::DOM_VK_C
;
1629 return dom::KeyboardEvent_Binding::DOM_VK_D
;
1632 return dom::KeyboardEvent_Binding::DOM_VK_E
;
1635 return dom::KeyboardEvent_Binding::DOM_VK_F
;
1638 return dom::KeyboardEvent_Binding::DOM_VK_G
;
1641 return dom::KeyboardEvent_Binding::DOM_VK_H
;
1644 return dom::KeyboardEvent_Binding::DOM_VK_I
;
1647 return dom::KeyboardEvent_Binding::DOM_VK_J
;
1650 return dom::KeyboardEvent_Binding::DOM_VK_K
;
1653 return dom::KeyboardEvent_Binding::DOM_VK_L
;
1656 return dom::KeyboardEvent_Binding::DOM_VK_M
;
1659 return dom::KeyboardEvent_Binding::DOM_VK_N
;
1662 return dom::KeyboardEvent_Binding::DOM_VK_O
;
1665 return dom::KeyboardEvent_Binding::DOM_VK_P
;
1668 return dom::KeyboardEvent_Binding::DOM_VK_Q
;
1671 return dom::KeyboardEvent_Binding::DOM_VK_R
;
1674 return dom::KeyboardEvent_Binding::DOM_VK_S
;
1677 return dom::KeyboardEvent_Binding::DOM_VK_T
;
1680 return dom::KeyboardEvent_Binding::DOM_VK_U
;
1683 return dom::KeyboardEvent_Binding::DOM_VK_V
;
1686 return dom::KeyboardEvent_Binding::DOM_VK_W
;
1689 return dom::KeyboardEvent_Binding::DOM_VK_X
;
1692 return dom::KeyboardEvent_Binding::DOM_VK_Y
;
1695 return dom::KeyboardEvent_Binding::DOM_VK_Z
;
1699 return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE
;
1702 return dom::KeyboardEvent_Binding::DOM_VK_1
;
1705 return dom::KeyboardEvent_Binding::DOM_VK_2
;
1708 return dom::KeyboardEvent_Binding::DOM_VK_3
;
1711 return dom::KeyboardEvent_Binding::DOM_VK_4
;
1714 return dom::KeyboardEvent_Binding::DOM_VK_5
;
1717 return dom::KeyboardEvent_Binding::DOM_VK_6
;
1720 return dom::KeyboardEvent_Binding::DOM_VK_7
;
1723 return dom::KeyboardEvent_Binding::DOM_VK_8
;
1726 return dom::KeyboardEvent_Binding::DOM_VK_9
;
1729 return dom::KeyboardEvent_Binding::DOM_VK_0
;
1732 return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS
;
1735 return dom::KeyboardEvent_Binding::DOM_VK_EQUALS
;
1739 return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET
;
1742 return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET
;
1745 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH
;
1749 return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON
;
1752 return dom::KeyboardEvent_Binding::DOM_VK_QUOTE
;
1756 return dom::KeyboardEvent_Binding::DOM_VK_COMMA
;
1759 return dom::KeyboardEvent_Binding::DOM_VK_PERIOD
;
1762 return dom::KeyboardEvent_Binding::DOM_VK_SLASH
;
1765 return dom::KeyboardEvent_Binding::DOM_VK_SPACE
;
1772 /******************************************************************************
1773 * TextInputProcessor::AutoPendingCompositionResetter
1774 ******************************************************************************/
1775 TextInputProcessor::AutoPendingCompositionResetter::
1776 AutoPendingCompositionResetter(TextInputProcessor
* aTIP
)
1778 MOZ_RELEASE_ASSERT(mTIP
.get(), "mTIP must not be null");
1781 TextInputProcessor::AutoPendingCompositionResetter::
1782 ~AutoPendingCompositionResetter() {
1783 if (mTIP
->mDispatcher
) {
1784 mTIP
->mDispatcher
->ClearPendingComposition();
1788 /******************************************************************************
1789 * TextInputProcessor::ModifierKeyData
1790 ******************************************************************************/
1791 TextInputProcessor::ModifierKeyData::ModifierKeyData(
1792 const WidgetKeyboardEvent
& aKeyboardEvent
)
1793 : mKeyNameIndex(aKeyboardEvent
.mKeyNameIndex
),
1794 mCodeNameIndex(aKeyboardEvent
.mCodeNameIndex
) {
1795 mModifier
= WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex
);
1796 MOZ_ASSERT(mModifier
, "mKeyNameIndex must be a modifier key name");
1799 /******************************************************************************
1800 * TextInputProcessor::ModifierKeyDataArray
1801 ******************************************************************************/
1802 Modifiers
TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const {
1803 Modifiers result
= MODIFIER_NONE
;
1804 for (uint32_t i
= 0; i
< Length(); i
++) {
1805 result
|= ElementAt(i
).mModifier
;
1810 void TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
1811 const TextInputProcessor::ModifierKeyData
& aModifierKeyData
) {
1812 if (Contains(aModifierKeyData
)) {
1815 AppendElement(aModifierKeyData
);
1818 void TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey(
1819 const TextInputProcessor::ModifierKeyData
& aModifierKeyData
) {
1820 RemoveElement(aModifierKeyData
);
1823 void TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey(
1824 const TextInputProcessor::ModifierKeyData
& aModifierKeyData
) {
1825 auto index
= IndexOf(aModifierKeyData
);
1826 if (index
== NoIndex
) {
1827 AppendElement(aModifierKeyData
);
1830 RemoveElementAt(index
);
1833 } // namespace mozilla