Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / base / TextInputProcessor.cpp
blobc263bdcf9b3a305aa0e2d187814fbd5c087a7c4b
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;
28 namespace mozilla {
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;
41 public:
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());
58 } else {
59 mSelectionChangeData.mString = nullptr;
63 NS_DECL_ISUPPORTS
65 NS_IMETHOD GetType(nsACString& aType) final {
66 aType = mType;
67 return NS_OK;
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;
80 return NS_OK;
82 if (IsTextChange()) {
83 *aOffset = mTextChangeData.mStartOffset;
84 return NS_OK;
86 return NS_ERROR_NOT_AVAILABLE;
89 // "notify-selection-change"
90 NS_IMETHOD GetHasRange(bool* aHasRange) final {
91 if (IsSelectionChange()) {
92 *aHasRange = mSelectionChangeData.HasRange();
93 return NS_OK;
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();
103 return NS_OK;
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();
114 return NS_OK;
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();
128 return NS_OK;
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;
142 return NS_OK;
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");
154 } else {
155 aWritingMode.AssignLiteral("vertical-rl");
157 return NS_OK;
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;
168 return NS_OK;
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;
179 return NS_OK;
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;
192 return NS_OK;
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();
204 return NS_OK;
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();
215 return NS_OK;
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;
226 return NS_OK;
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;
239 return NS_OK;
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;
252 return NS_OK;
254 return NS_ERROR_NOT_AVAILABLE;
257 protected:
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");
273 private:
274 nsAutoCString mType;
275 union {
276 TextChangeDataBase mTextChangeData;
277 SelectionChangeDataBase mSelectionChangeData;
280 TextInputProcessorNotification() : mTextChangeData() {}
283 NS_IMPL_ISUPPORTS(TextInputProcessorNotification,
284 nsITextInputProcessorNotification)
286 /******************************************************************************
287 * TextInputProcessor
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();
313 NS_IMETHODIMP
314 TextInputProcessor::GetHasComposition(bool* aHasComposition) {
315 MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr");
316 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
317 *aHasComposition = IsComposing();
318 return NS_OK;
321 NS_IMETHODIMP
322 TextInputProcessor::BeginInputTransaction(
323 mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback,
324 bool* aSucceeded) {
325 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
326 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
327 if (NS_WARN_IF(!aCallback)) {
328 *aSucceeded = false;
329 return NS_ERROR_INVALID_ARG;
331 return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
334 NS_IMETHODIMP
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,
347 bool* aSucceeded) {
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) {
355 aSucceeded = false;
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
381 // again.
382 if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback &&
383 aForTests == mForTests) {
384 aSucceeded = true;
385 return NS_OK;
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.
393 if (mDispatcher &&
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()) {
402 return NS_OK;
405 // This instance has finished preparing to link to the dispatcher. Therefore,
406 // let's forget the old dispatcher and purpose.
407 if (mDispatcher) {
408 mDispatcher->EndInputTransaction(this);
409 if (NS_WARN_IF(mDispatcher)) {
410 // Forcibly initialize the members if we failed to end the input
411 // transaction.
412 UnlinkFromTextEventDispatcher();
416 nsresult rv = NS_OK;
417 if (aForTests) {
418 bool isAPZAware = StaticPrefs::test_events_async_enabled();
419 rv = dispatcher->BeginTestInputTransaction(this, isAPZAware);
420 } else {
421 rv = dispatcher->BeginInputTransaction(this);
424 if (NS_WARN_IF(NS_FAILED(rv))) {
425 return rv;
428 mDispatcher = dispatcher;
429 mCallback = aCallback;
430 mForTests = aForTests;
431 aSucceeded = true;
432 return NS_OK;
435 void TextInputProcessor::UnlinkFromTextEventDispatcher() {
436 mDispatcher = nullptr;
437 mForTests = false;
438 if (mCallback) {
439 nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback);
440 mCallback = nullptr;
442 RefPtr<TextInputProcessorNotification> notification =
443 new TextInputProcessorNotification("notify-end-input-transaction");
444 bool result = false;
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))) {
456 return rv;
459 return NS_OK;
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) {
467 return true;
469 if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
470 aKeyboardEvent.mSpecifiedEventType &&
471 nsDependentAtomString(aKeyboardEvent.mSpecifiedEventType)
472 .EqualsLiteral("on")) {
473 return true;
475 return false;
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;
486 return result;
489 if (!aKeyboardEvent) {
490 return result;
493 // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
494 // eKeyDown event.
495 if (aKeyboardEvent->mMessage == eKeyUp) {
496 return result;
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;
504 return result;
507 uint32_t consumedFlags = 0;
509 result.mResult =
510 KeydownInternal(*aKeyboardEvent, aKeyFlags, false, consumedFlags);
511 result.mDoDefault = !consumedFlags;
512 if (NS_WARN_IF(NS_FAILED(result.mResult))) {
513 result.mCanContinue = false;
514 return result;
517 result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
518 return result;
521 TextInputProcessor::EventDispatcherResult
522 TextInputProcessor::MaybeDispatchKeyupForComposition(
523 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) {
524 EventDispatcherResult result;
526 if (!aKeyboardEvent) {
527 return result;
530 // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
531 // eKeyUp event.
532 if (aKeyboardEvent->mMessage == eKeyDown) {
533 return result;
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;
540 return result;
543 result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault);
544 if (NS_WARN_IF(NS_FAILED(result.mResult))) {
545 result.mCanContinue = false;
546 return result;
549 result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
550 return result;
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()
560 : nullptr;
561 if (!aKeyboardEvent || aOptionalArgc < 2) {
562 aKeyFlags = 0;
565 if (!aKeyboardEvent) {
566 return NS_OK;
569 if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) {
570 return NS_ERROR_INVALID_ARG;
573 return NS_OK;
576 NS_IMETHODIMP
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());
581 *aSucceeded = false;
583 RefPtr<KeyboardEvent> keyEvent;
584 if (aDOMKeyEvent) {
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))) {
597 return 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))) {
617 return rv;
619 return NS_OK;
622 NS_IMETHODIMP
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))) {
628 return rv;
630 return kungFuDeathGrip->SetPendingCompositionString(aString);
633 NS_IMETHODIMP
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);
645 break;
646 default:
647 return NS_ERROR_INVALID_ARG;
649 nsresult rv = IsValidStateForComposition();
650 if (NS_WARN_IF(NS_FAILED(rv))) {
651 return rv;
653 return kungFuDeathGrip->AppendClauseToPendingComposition(aLength,
654 textRangeType);
657 NS_IMETHODIMP
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))) {
663 return rv;
665 return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0);
668 NS_IMETHODIMP
669 TextInputProcessor::FlushPendingComposition(Event* aDOMKeyEvent,
670 uint32_t aKeyFlags,
671 uint8_t aOptionalArgc,
672 bool* aSucceeded) {
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);
680 *aSucceeded = false;
681 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
682 bool wasComposing = IsComposing();
684 RefPtr<KeyboardEvent> keyEvent;
685 if (aDOMKeyEvent) {
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))) {
696 return 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())) {
711 return NS_OK;
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))) {
721 return rv;
723 return NS_OK;
726 NS_IMETHODIMP
727 TextInputProcessor::CommitComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
728 uint8_t aOptionalArgc) {
729 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
731 RefPtr<KeyboardEvent> keyEvent;
732 if (aDOMKeyEvent) {
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))) {
743 return rv;
746 return CommitCompositionInternal(keyboardEvent, aKeyFlags);
749 NS_IMETHODIMP
750 TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString,
751 Event* aDOMKeyEvent,
752 uint32_t aKeyFlags,
753 uint8_t aOptionalArgc,
754 bool* aSucceeded) {
755 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
756 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
758 RefPtr<KeyboardEvent> keyEvent;
759 if (aDOMKeyEvent) {
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))) {
770 return rv;
773 return CommitCompositionInternal(keyboardEvent, aKeyFlags, &aCommitString,
774 aSucceeded);
777 nsresult TextInputProcessor::CommitCompositionInternal(
778 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags,
779 const nsAString* aCommitString, bool* aSucceeded) {
780 if (aSucceeded) {
781 *aSucceeded = false;
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.
795 nsresult rv = NS_OK;
796 if (dispatcherResult.mDoDefault || wasComposing) {
797 // Preceding keydown event may cause destroying the widget.
798 if (NS_FAILED(IsValidStateForComposition())) {
799 return NS_OK;
801 nsEventStatus status = nsEventStatus_eIgnore;
802 rv = kungFuDeathGrip->CommitComposition(status, aCommitString);
803 if (aSucceeded) {
804 *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
808 MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
810 if (NS_WARN_IF(NS_FAILED(rv))) {
811 return rv;
813 return NS_OK;
816 NS_IMETHODIMP
817 TextInputProcessor::CancelComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
818 uint8_t aOptionalArgc) {
819 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
821 RefPtr<KeyboardEvent> keyEvent;
822 if (aDOMKeyEvent) {
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))) {
833 return 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))) {
856 return rv;
858 return NS_OK;
861 NS_IMETHODIMP
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.
868 if (!mDispatcher) {
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");
875 if (mCallback) {
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");
882 break;
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");
888 break;
890 case NOTIFY_IME_OF_FOCUS:
891 notification = new TextInputProcessorNotification("notify-focus");
892 break;
893 case NOTIFY_IME_OF_BLUR:
894 notification = new TextInputProcessorNotification("notify-blur");
895 break;
896 case NOTIFY_IME_OF_TEXT_CHANGE:
897 notification =
898 new TextInputProcessorNotification(aNotification.mTextChangeData);
899 break;
900 case NOTIFY_IME_OF_SELECTION_CHANGE:
901 notification = new TextInputProcessorNotification(
902 aNotification.mSelectionChangeData);
903 break;
904 case NOTIFY_IME_OF_POSITION_CHANGE:
905 notification =
906 new TextInputProcessorNotification("notify-position-change");
907 break;
908 default:
909 return NS_ERROR_NOT_IMPLEMENTED;
911 MOZ_RELEASE_ASSERT(notification);
912 bool result = false;
913 nsresult rv = mCallback->OnNotify(this, notification, &result);
914 if (NS_WARN_IF(NS_FAILED(rv))) {
915 return 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();
925 return NS_OK;
927 case REQUEST_TO_CANCEL_COMPOSITION: {
928 NS_ASSERTION(aTextEventDispatcher->IsComposing(),
929 "Why is this requested without composition?");
930 CancelCompositionInternal();
931 return NS_OK;
933 default:
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);
946 NS_IMETHODIMP_(void)
947 TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
948 // If This is called while this is being initialized, ignore the call.
949 if (!mDispatcher) {
950 return;
952 MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
953 "Wrong TextEventDispatcher notifies this");
954 UnlinkFromTextEventDispatcher();
957 NS_IMETHODIMP_(void)
958 TextInputProcessor::WillDispatchKeyboardEvent(
959 TextEventDispatcher* aTextEventDispatcher,
960 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
961 void* aData) {
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;
1016 return NS_OK;
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
1027 // them.
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();
1036 return NS_OK;
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;
1061 return NS_OK;
1064 NS_IMETHODIMP
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) {
1070 aKeyFlags = 0;
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,
1084 uint32_t aKeyFlags,
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))) {
1102 return 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);
1114 } else {
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) {
1120 return NS_OK;
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))) {
1136 return 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
1143 // events.
1144 return NS_OK;
1147 aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault)
1148 ? KEYDOWN_IS_CONSUMED
1149 : KEYEVENT_NOT_CONSUMED;
1151 if (!aAllowToDispatchKeypress) {
1152 return NS_OK;
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))) {
1167 return rv;
1170 if (kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) {
1171 aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault)
1172 ? KEYPRESS_IS_CONSUMED
1173 : KEYEVENT_NOT_CONSUMED;
1176 return NS_OK;
1179 NS_IMETHODIMP
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) {
1185 aKeyFlags = 0;
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,
1207 bool& aDoDefault) {
1208 aDoDefault = false;
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))) {
1216 return 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) {
1228 return NS_OK;
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))) {
1243 return rv;
1246 nsEventStatus status =
1247 aDoDefault ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault;
1248 kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status);
1249 aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
1250 return NS_OK;
1253 NS_IMETHODIMP
1254 TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
1255 bool* aActive) {
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);
1260 return NS_OK;
1263 NS_IMETHODIMP
1264 TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther) {
1265 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1266 if (!aOther) {
1267 mModifierKeyDataArray = nullptr;
1268 return NS_OK;
1270 TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
1271 if (!other->mModifierKeyDataArray) {
1272 other->mModifierKeyDataArray = new ModifierKeyDataArray();
1274 mModifierKeyDataArray = other->mModifierKeyDataArray;
1275 return NS_OK;
1278 NS_IMETHODIMP
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()));
1290 } else {
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) {
1300 return NS_OK;
1303 CodeNameIndex codeNameIndex =
1304 WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex,
1305 location);
1306 if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
1307 return NS_OK;
1309 MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
1310 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
1311 return NS_OK;
1314 NS_IMETHODIMP
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()));
1326 } else {
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) {
1335 return NS_OK;
1337 MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
1338 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
1339 return NS_OK;
1342 // static
1343 CodeNameIndex
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]) {
1359 case '+':
1360 return CODE_NAME_INDEX_NumpadAdd;
1361 case '-':
1362 return CODE_NAME_INDEX_NumpadSubtract;
1363 case '*':
1364 return CODE_NAME_INDEX_NumpadMultiply;
1365 case '/':
1366 return CODE_NAME_INDEX_NumpadDivide;
1367 case '.':
1368 return CODE_NAME_INDEX_NumpadDecimal;
1369 case '0':
1370 return CODE_NAME_INDEX_Numpad0;
1371 case '1':
1372 return CODE_NAME_INDEX_Numpad1;
1373 case '2':
1374 return CODE_NAME_INDEX_Numpad2;
1375 case '3':
1376 return CODE_NAME_INDEX_Numpad3;
1377 case '4':
1378 return CODE_NAME_INDEX_Numpad4;
1379 case '5':
1380 return CODE_NAME_INDEX_Numpad5;
1381 case '6':
1382 return CODE_NAME_INDEX_Numpad6;
1383 case '7':
1384 return CODE_NAME_INDEX_Numpad7;
1385 case '8':
1386 return CODE_NAME_INDEX_Numpad8;
1387 case '9':
1388 return CODE_NAME_INDEX_Numpad9;
1389 default:
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]) {
1402 case 'a':
1403 case 'A':
1404 return CODE_NAME_INDEX_KeyA;
1405 case 'b':
1406 case 'B':
1407 return CODE_NAME_INDEX_KeyB;
1408 case 'c':
1409 case 'C':
1410 return CODE_NAME_INDEX_KeyC;
1411 case 'd':
1412 case 'D':
1413 return CODE_NAME_INDEX_KeyD;
1414 case 'e':
1415 case 'E':
1416 return CODE_NAME_INDEX_KeyE;
1417 case 'f':
1418 case 'F':
1419 return CODE_NAME_INDEX_KeyF;
1420 case 'g':
1421 case 'G':
1422 return CODE_NAME_INDEX_KeyG;
1423 case 'h':
1424 case 'H':
1425 return CODE_NAME_INDEX_KeyH;
1426 case 'i':
1427 case 'I':
1428 return CODE_NAME_INDEX_KeyI;
1429 case 'j':
1430 case 'J':
1431 return CODE_NAME_INDEX_KeyJ;
1432 case 'k':
1433 case 'K':
1434 return CODE_NAME_INDEX_KeyK;
1435 case 'l':
1436 case 'L':
1437 return CODE_NAME_INDEX_KeyL;
1438 case 'm':
1439 case 'M':
1440 return CODE_NAME_INDEX_KeyM;
1441 case 'n':
1442 case 'N':
1443 return CODE_NAME_INDEX_KeyN;
1444 case 'o':
1445 case 'O':
1446 return CODE_NAME_INDEX_KeyO;
1447 case 'p':
1448 case 'P':
1449 return CODE_NAME_INDEX_KeyP;
1450 case 'q':
1451 case 'Q':
1452 return CODE_NAME_INDEX_KeyQ;
1453 case 'r':
1454 case 'R':
1455 return CODE_NAME_INDEX_KeyR;
1456 case 's':
1457 case 'S':
1458 return CODE_NAME_INDEX_KeyS;
1459 case 't':
1460 case 'T':
1461 return CODE_NAME_INDEX_KeyT;
1462 case 'u':
1463 case 'U':
1464 return CODE_NAME_INDEX_KeyU;
1465 case 'v':
1466 case 'V':
1467 return CODE_NAME_INDEX_KeyV;
1468 case 'w':
1469 case 'W':
1470 return CODE_NAME_INDEX_KeyW;
1471 case 'x':
1472 case 'X':
1473 return CODE_NAME_INDEX_KeyX;
1474 case 'y':
1475 case 'Y':
1476 return CODE_NAME_INDEX_KeyY;
1477 case 'z':
1478 case 'Z':
1479 return CODE_NAME_INDEX_KeyZ;
1481 case '`':
1482 case '~':
1483 return CODE_NAME_INDEX_Backquote;
1484 case '1':
1485 case '!':
1486 return CODE_NAME_INDEX_Digit1;
1487 case '2':
1488 case '@':
1489 return CODE_NAME_INDEX_Digit2;
1490 case '3':
1491 case '#':
1492 return CODE_NAME_INDEX_Digit3;
1493 case '4':
1494 case '$':
1495 return CODE_NAME_INDEX_Digit4;
1496 case '5':
1497 case '%':
1498 return CODE_NAME_INDEX_Digit5;
1499 case '6':
1500 case '^':
1501 return CODE_NAME_INDEX_Digit6;
1502 case '7':
1503 case '&':
1504 return CODE_NAME_INDEX_Digit7;
1505 case '8':
1506 case '*':
1507 return CODE_NAME_INDEX_Digit8;
1508 case '9':
1509 case '(':
1510 return CODE_NAME_INDEX_Digit9;
1511 case '0':
1512 case ')':
1513 return CODE_NAME_INDEX_Digit0;
1514 case '-':
1515 case '_':
1516 return CODE_NAME_INDEX_Minus;
1517 case '=':
1518 case '+':
1519 return CODE_NAME_INDEX_Equal;
1521 case '[':
1522 case '{':
1523 return CODE_NAME_INDEX_BracketLeft;
1524 case ']':
1525 case '}':
1526 return CODE_NAME_INDEX_BracketRight;
1527 case '\\':
1528 case '|':
1529 return CODE_NAME_INDEX_Backslash;
1531 case ';':
1532 case ':':
1533 return CODE_NAME_INDEX_Semicolon;
1534 case '\'':
1535 case '"':
1536 return CODE_NAME_INDEX_Quote;
1538 case ',':
1539 case '<':
1540 return CODE_NAME_INDEX_Comma;
1541 case '.':
1542 case '>':
1543 return CODE_NAME_INDEX_Period;
1544 case '/':
1545 case '?':
1546 return CODE_NAME_INDEX_Slash;
1548 case ' ':
1549 return CODE_NAME_INDEX_Space;
1551 default:
1552 return CODE_NAME_INDEX_UNKNOWN;
1556 NS_IMETHODIMP
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()));
1570 } else {
1571 NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
1572 "aLocation must be undefined, null or int");
1573 return NS_ERROR_INVALID_ARG;
1577 *aKeyCodeValue =
1578 GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue, location);
1579 return NS_OK;
1582 // static
1583 uint32_t TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
1584 const nsAString& aKeyValue, const Maybe<uint32_t>& aLocation) {
1585 if (aKeyValue.IsEmpty()) {
1586 return 0;
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) {
1592 return 0;
1595 if (aLocation.isSome() &&
1596 aLocation.value() ==
1597 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
1598 switch (aKeyValue[0]) {
1599 case '+':
1600 return dom::KeyboardEvent_Binding::DOM_VK_ADD;
1601 case '-':
1602 return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT;
1603 case '*':
1604 return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY;
1605 case '/':
1606 return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE;
1607 case '.':
1608 return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL;
1609 case '0':
1610 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0;
1611 case '1':
1612 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1;
1613 case '2':
1614 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2;
1615 case '3':
1616 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3;
1617 case '4':
1618 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4;
1619 case '5':
1620 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5;
1621 case '6':
1622 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6;
1623 case '7':
1624 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7;
1625 case '8':
1626 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8;
1627 case '9':
1628 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9;
1629 default:
1630 return 0;
1634 if (aLocation.isSome() &&
1635 aLocation.value() !=
1636 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
1637 return 0;
1640 // TODO: Support characters inputted with option key on macOS.
1641 switch (aKeyValue[0]) {
1642 case 'a':
1643 case 'A':
1644 return dom::KeyboardEvent_Binding::DOM_VK_A;
1645 case 'b':
1646 case 'B':
1647 return dom::KeyboardEvent_Binding::DOM_VK_B;
1648 case 'c':
1649 case 'C':
1650 return dom::KeyboardEvent_Binding::DOM_VK_C;
1651 case 'd':
1652 case 'D':
1653 return dom::KeyboardEvent_Binding::DOM_VK_D;
1654 case 'e':
1655 case 'E':
1656 return dom::KeyboardEvent_Binding::DOM_VK_E;
1657 case 'f':
1658 case 'F':
1659 return dom::KeyboardEvent_Binding::DOM_VK_F;
1660 case 'g':
1661 case 'G':
1662 return dom::KeyboardEvent_Binding::DOM_VK_G;
1663 case 'h':
1664 case 'H':
1665 return dom::KeyboardEvent_Binding::DOM_VK_H;
1666 case 'i':
1667 case 'I':
1668 return dom::KeyboardEvent_Binding::DOM_VK_I;
1669 case 'j':
1670 case 'J':
1671 return dom::KeyboardEvent_Binding::DOM_VK_J;
1672 case 'k':
1673 case 'K':
1674 return dom::KeyboardEvent_Binding::DOM_VK_K;
1675 case 'l':
1676 case 'L':
1677 return dom::KeyboardEvent_Binding::DOM_VK_L;
1678 case 'm':
1679 case 'M':
1680 return dom::KeyboardEvent_Binding::DOM_VK_M;
1681 case 'n':
1682 case 'N':
1683 return dom::KeyboardEvent_Binding::DOM_VK_N;
1684 case 'o':
1685 case 'O':
1686 return dom::KeyboardEvent_Binding::DOM_VK_O;
1687 case 'p':
1688 case 'P':
1689 return dom::KeyboardEvent_Binding::DOM_VK_P;
1690 case 'q':
1691 case 'Q':
1692 return dom::KeyboardEvent_Binding::DOM_VK_Q;
1693 case 'r':
1694 case 'R':
1695 return dom::KeyboardEvent_Binding::DOM_VK_R;
1696 case 's':
1697 case 'S':
1698 return dom::KeyboardEvent_Binding::DOM_VK_S;
1699 case 't':
1700 case 'T':
1701 return dom::KeyboardEvent_Binding::DOM_VK_T;
1702 case 'u':
1703 case 'U':
1704 return dom::KeyboardEvent_Binding::DOM_VK_U;
1705 case 'v':
1706 case 'V':
1707 return dom::KeyboardEvent_Binding::DOM_VK_V;
1708 case 'w':
1709 case 'W':
1710 return dom::KeyboardEvent_Binding::DOM_VK_W;
1711 case 'x':
1712 case 'X':
1713 return dom::KeyboardEvent_Binding::DOM_VK_X;
1714 case 'y':
1715 case 'Y':
1716 return dom::KeyboardEvent_Binding::DOM_VK_Y;
1717 case 'z':
1718 case 'Z':
1719 return dom::KeyboardEvent_Binding::DOM_VK_Z;
1721 case '`':
1722 case '~':
1723 return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE;
1724 case '1':
1725 case '!':
1726 return dom::KeyboardEvent_Binding::DOM_VK_1;
1727 case '2':
1728 case '@':
1729 return dom::KeyboardEvent_Binding::DOM_VK_2;
1730 case '3':
1731 case '#':
1732 return dom::KeyboardEvent_Binding::DOM_VK_3;
1733 case '4':
1734 case '$':
1735 return dom::KeyboardEvent_Binding::DOM_VK_4;
1736 case '5':
1737 case '%':
1738 return dom::KeyboardEvent_Binding::DOM_VK_5;
1739 case '6':
1740 case '^':
1741 return dom::KeyboardEvent_Binding::DOM_VK_6;
1742 case '7':
1743 case '&':
1744 return dom::KeyboardEvent_Binding::DOM_VK_7;
1745 case '8':
1746 case '*':
1747 return dom::KeyboardEvent_Binding::DOM_VK_8;
1748 case '9':
1749 case '(':
1750 return dom::KeyboardEvent_Binding::DOM_VK_9;
1751 case '0':
1752 case ')':
1753 return dom::KeyboardEvent_Binding::DOM_VK_0;
1754 case '-':
1755 case '_':
1756 return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS;
1757 case '=':
1758 case '+':
1759 return dom::KeyboardEvent_Binding::DOM_VK_EQUALS;
1761 case '[':
1762 case '{':
1763 return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET;
1764 case ']':
1765 case '}':
1766 return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET;
1767 case '\\':
1768 case '|':
1769 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH;
1771 case ';':
1772 case ':':
1773 return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON;
1774 case '\'':
1775 case '"':
1776 return dom::KeyboardEvent_Binding::DOM_VK_QUOTE;
1778 case ',':
1779 case '<':
1780 return dom::KeyboardEvent_Binding::DOM_VK_COMMA;
1781 case '.':
1782 case '>':
1783 return dom::KeyboardEvent_Binding::DOM_VK_PERIOD;
1784 case '/':
1785 case '?':
1786 return dom::KeyboardEvent_Binding::DOM_VK_SLASH;
1788 case ' ':
1789 return dom::KeyboardEvent_Binding::DOM_VK_SPACE;
1791 default:
1792 return 0;
1796 /******************************************************************************
1797 * TextInputProcessor::AutoPendingCompositionResetter
1798 ******************************************************************************/
1799 TextInputProcessor::AutoPendingCompositionResetter::
1800 AutoPendingCompositionResetter(TextInputProcessor* aTIP)
1801 : mTIP(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;
1831 return result;
1834 void TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
1835 const TextInputProcessor::ModifierKeyData& aModifierKeyData) {
1836 if (Contains(aModifierKeyData)) {
1837 return;
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);
1852 return;
1854 RemoveElementAt(index);
1857 } // namespace mozilla