Bug 1750871 - run mochitest-remote on fission everywhere. r=releng-reviewers,aki
[gecko.git] / dom / base / TextInputProcessor.cpp
blob483049d35bc42e98d46e8b840e9c432bc426e8a5
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;
27 namespace mozilla {
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;
40 public:
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;
58 NS_DECL_ISUPPORTS
60 NS_IMETHOD GetType(nsACString& aType) final {
61 aType = mType;
62 return NS_OK;
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;
72 return NS_OK;
74 if (IsTextChange()) {
75 *aOffset = mTextChangeData.mStartOffset;
76 return NS_OK;
78 return NS_ERROR_NOT_AVAILABLE;
81 // "notify-selection-change"
82 NS_IMETHOD GetText(nsAString& aText) final {
83 if (IsSelectionChange()) {
84 aText = mSelectionChangeData.String();
85 return NS_OK;
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();
96 return NS_OK;
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();
107 return NS_OK;
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;
118 return NS_OK;
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");
130 } else {
131 aWritingMode.AssignLiteral("vertical-rl");
133 return NS_OK;
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;
144 return NS_OK;
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;
155 return NS_OK;
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;
168 return NS_OK;
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();
180 return NS_OK;
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();
191 return NS_OK;
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;
202 return NS_OK;
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;
215 return NS_OK;
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;
228 return NS_OK;
230 return NS_ERROR_NOT_AVAILABLE;
233 protected:
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");
249 private:
250 nsAutoCString mType;
251 union {
252 TextChangeDataBase mTextChangeData;
253 SelectionChangeDataBase mSelectionChangeData;
256 TextInputProcessorNotification() : mTextChangeData() {}
259 NS_IMPL_ISUPPORTS(TextInputProcessorNotification,
260 nsITextInputProcessorNotification)
262 /******************************************************************************
263 * TextInputProcessor
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();
289 NS_IMETHODIMP
290 TextInputProcessor::GetHasComposition(bool* aHasComposition) {
291 MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr");
292 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
293 *aHasComposition = IsComposing();
294 return NS_OK;
297 NS_IMETHODIMP
298 TextInputProcessor::BeginInputTransaction(
299 mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback,
300 bool* aSucceeded) {
301 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
302 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
303 if (NS_WARN_IF(!aCallback)) {
304 *aSucceeded = false;
305 return NS_ERROR_INVALID_ARG;
307 return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
310 NS_IMETHODIMP
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,
323 bool* aSucceeded) {
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) {
331 aSucceeded = false;
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
357 // again.
358 if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback &&
359 aForTests == mForTests) {
360 aSucceeded = true;
361 return NS_OK;
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.
369 if (mDispatcher &&
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()) {
378 return NS_OK;
381 // This instance has finished preparing to link to the dispatcher. Therefore,
382 // let's forget the old dispatcher and purpose.
383 if (mDispatcher) {
384 mDispatcher->EndInputTransaction(this);
385 if (NS_WARN_IF(mDispatcher)) {
386 // Forcibly initialize the members if we failed to end the input
387 // transaction.
388 UnlinkFromTextEventDispatcher();
392 nsresult rv = NS_OK;
393 if (aForTests) {
394 bool isAPZAware = StaticPrefs::test_events_async_enabled();
395 rv = dispatcher->BeginTestInputTransaction(this, isAPZAware);
396 } else {
397 rv = dispatcher->BeginInputTransaction(this);
400 if (NS_WARN_IF(NS_FAILED(rv))) {
401 return rv;
404 mDispatcher = dispatcher;
405 mCallback = aCallback;
406 mForTests = aForTests;
407 aSucceeded = true;
408 return NS_OK;
411 void TextInputProcessor::UnlinkFromTextEventDispatcher() {
412 mDispatcher = nullptr;
413 mForTests = false;
414 if (mCallback) {
415 nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback);
416 mCallback = nullptr;
418 RefPtr<TextInputProcessorNotification> notification =
419 new TextInputProcessorNotification("notify-end-input-transaction");
420 bool result = false;
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))) {
432 return rv;
435 return NS_OK;
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) {
443 return true;
445 if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
446 aKeyboardEvent.mSpecifiedEventType &&
447 nsDependentAtomString(aKeyboardEvent.mSpecifiedEventType)
448 .EqualsLiteral("on")) {
449 return true;
451 return false;
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;
462 return result;
465 if (!aKeyboardEvent) {
466 return result;
469 // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
470 // eKeyDown event.
471 if (aKeyboardEvent->mMessage == eKeyUp) {
472 return result;
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;
480 return result;
483 uint32_t consumedFlags = 0;
485 result.mResult =
486 KeydownInternal(*aKeyboardEvent, aKeyFlags, false, consumedFlags);
487 result.mDoDefault = !consumedFlags;
488 if (NS_WARN_IF(NS_FAILED(result.mResult))) {
489 result.mCanContinue = false;
490 return result;
493 result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
494 return result;
497 TextInputProcessor::EventDispatcherResult
498 TextInputProcessor::MaybeDispatchKeyupForComposition(
499 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) {
500 EventDispatcherResult result;
502 if (!aKeyboardEvent) {
503 return result;
506 // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
507 // eKeyUp event.
508 if (aKeyboardEvent->mMessage == eKeyDown) {
509 return result;
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;
516 return result;
519 result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault);
520 if (NS_WARN_IF(NS_FAILED(result.mResult))) {
521 result.mCanContinue = false;
522 return result;
525 result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
526 return result;
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()
536 : nullptr;
537 if (!aKeyboardEvent || aOptionalArgc < 2) {
538 aKeyFlags = 0;
541 if (!aKeyboardEvent) {
542 return NS_OK;
545 if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) {
546 return NS_ERROR_INVALID_ARG;
549 return NS_OK;
552 NS_IMETHODIMP
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());
557 *aSucceeded = false;
559 RefPtr<KeyboardEvent> keyEvent;
560 if (aDOMKeyEvent) {
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))) {
573 return 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))) {
593 return rv;
595 return NS_OK;
598 NS_IMETHODIMP
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))) {
604 return rv;
606 return kungFuDeathGrip->SetPendingCompositionString(aString);
609 NS_IMETHODIMP
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);
621 break;
622 default:
623 return NS_ERROR_INVALID_ARG;
625 nsresult rv = IsValidStateForComposition();
626 if (NS_WARN_IF(NS_FAILED(rv))) {
627 return rv;
629 return kungFuDeathGrip->AppendClauseToPendingComposition(aLength,
630 textRangeType);
633 NS_IMETHODIMP
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))) {
639 return rv;
641 return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0);
644 NS_IMETHODIMP
645 TextInputProcessor::FlushPendingComposition(Event* aDOMKeyEvent,
646 uint32_t aKeyFlags,
647 uint8_t aOptionalArgc,
648 bool* aSucceeded) {
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);
656 *aSucceeded = false;
657 RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
658 bool wasComposing = IsComposing();
660 RefPtr<KeyboardEvent> keyEvent;
661 if (aDOMKeyEvent) {
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))) {
672 return 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())) {
687 return NS_OK;
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))) {
697 return rv;
699 return NS_OK;
702 NS_IMETHODIMP
703 TextInputProcessor::CommitComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
704 uint8_t aOptionalArgc) {
705 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
707 RefPtr<KeyboardEvent> keyEvent;
708 if (aDOMKeyEvent) {
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))) {
719 return rv;
722 return CommitCompositionInternal(keyboardEvent, aKeyFlags);
725 NS_IMETHODIMP
726 TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString,
727 Event* aDOMKeyEvent,
728 uint32_t aKeyFlags,
729 uint8_t aOptionalArgc,
730 bool* aSucceeded) {
731 MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
732 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
734 RefPtr<KeyboardEvent> keyEvent;
735 if (aDOMKeyEvent) {
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))) {
746 return rv;
749 return CommitCompositionInternal(keyboardEvent, aKeyFlags, &aCommitString,
750 aSucceeded);
753 nsresult TextInputProcessor::CommitCompositionInternal(
754 const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags,
755 const nsAString* aCommitString, bool* aSucceeded) {
756 if (aSucceeded) {
757 *aSucceeded = false;
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.
771 nsresult rv = NS_OK;
772 if (dispatcherResult.mDoDefault || wasComposing) {
773 // Preceding keydown event may cause destroying the widget.
774 if (NS_FAILED(IsValidStateForComposition())) {
775 return NS_OK;
777 nsEventStatus status = nsEventStatus_eIgnore;
778 rv = kungFuDeathGrip->CommitComposition(status, aCommitString);
779 if (aSucceeded) {
780 *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
784 MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
786 if (NS_WARN_IF(NS_FAILED(rv))) {
787 return rv;
789 return NS_OK;
792 NS_IMETHODIMP
793 TextInputProcessor::CancelComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
794 uint8_t aOptionalArgc) {
795 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
797 RefPtr<KeyboardEvent> keyEvent;
798 if (aDOMKeyEvent) {
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))) {
809 return 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))) {
832 return rv;
834 return NS_OK;
837 NS_IMETHODIMP
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.
844 if (!mDispatcher) {
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");
851 if (mCallback) {
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");
858 break;
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");
864 break;
866 case NOTIFY_IME_OF_FOCUS:
867 notification = new TextInputProcessorNotification("notify-focus");
868 break;
869 case NOTIFY_IME_OF_BLUR:
870 notification = new TextInputProcessorNotification("notify-blur");
871 break;
872 case NOTIFY_IME_OF_TEXT_CHANGE:
873 notification =
874 new TextInputProcessorNotification(aNotification.mTextChangeData);
875 break;
876 case NOTIFY_IME_OF_SELECTION_CHANGE:
877 notification = new TextInputProcessorNotification(
878 aNotification.mSelectionChangeData);
879 break;
880 case NOTIFY_IME_OF_POSITION_CHANGE:
881 notification =
882 new TextInputProcessorNotification("notify-position-change");
883 break;
884 default:
885 return NS_ERROR_NOT_IMPLEMENTED;
887 MOZ_RELEASE_ASSERT(notification);
888 bool result = false;
889 nsresult rv = mCallback->OnNotify(this, notification, &result);
890 if (NS_WARN_IF(NS_FAILED(rv))) {
891 return 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();
901 return NS_OK;
903 case REQUEST_TO_CANCEL_COMPOSITION: {
904 NS_ASSERTION(aTextEventDispatcher->IsComposing(),
905 "Why is this requested without composition?");
906 CancelCompositionInternal();
907 return NS_OK;
909 default:
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);
922 NS_IMETHODIMP_(void)
923 TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
924 // If This is called while this is being initialized, ignore the call.
925 if (!mDispatcher) {
926 return;
928 MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
929 "Wrong TextEventDispatcher notifies this");
930 UnlinkFromTextEventDispatcher();
933 NS_IMETHODIMP_(void)
934 TextInputProcessor::WillDispatchKeyboardEvent(
935 TextEventDispatcher* aTextEventDispatcher,
936 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
937 void* aData) {
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;
992 return NS_OK;
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
1003 // them.
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();
1012 return NS_OK;
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;
1037 return NS_OK;
1040 NS_IMETHODIMP
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) {
1046 aKeyFlags = 0;
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,
1060 uint32_t aKeyFlags,
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))) {
1078 return 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);
1090 } else {
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) {
1096 return NS_OK;
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))) {
1112 return 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
1119 // events.
1120 return NS_OK;
1123 aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault)
1124 ? KEYDOWN_IS_CONSUMED
1125 : KEYEVENT_NOT_CONSUMED;
1127 if (!aAllowToDispatchKeypress) {
1128 return NS_OK;
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))) {
1143 return rv;
1146 if (kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) {
1147 aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault)
1148 ? KEYPRESS_IS_CONSUMED
1149 : KEYEVENT_NOT_CONSUMED;
1152 return NS_OK;
1155 NS_IMETHODIMP
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) {
1161 aKeyFlags = 0;
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,
1183 bool& aDoDefault) {
1184 aDoDefault = false;
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))) {
1192 return 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) {
1204 return NS_OK;
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))) {
1219 return rv;
1222 nsEventStatus status =
1223 aDoDefault ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault;
1224 kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status);
1225 aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
1226 return NS_OK;
1229 NS_IMETHODIMP
1230 TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
1231 bool* aActive) {
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);
1236 return NS_OK;
1239 NS_IMETHODIMP
1240 TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther) {
1241 MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
1242 if (!aOther) {
1243 mModifierKeyDataArray = nullptr;
1244 return NS_OK;
1246 TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
1247 if (!other->mModifierKeyDataArray) {
1248 other->mModifierKeyDataArray = new ModifierKeyDataArray();
1250 mModifierKeyDataArray = other->mModifierKeyDataArray;
1251 return NS_OK;
1254 NS_IMETHODIMP
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()));
1266 } else {
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) {
1276 return NS_OK;
1279 CodeNameIndex codeNameIndex =
1280 WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex,
1281 location);
1282 if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
1283 return NS_OK;
1285 MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
1286 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
1287 return NS_OK;
1290 NS_IMETHODIMP
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()));
1302 } else {
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) {
1311 return NS_OK;
1313 MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
1314 WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
1315 return NS_OK;
1318 // static
1319 CodeNameIndex
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]) {
1335 case '+':
1336 return CODE_NAME_INDEX_NumpadAdd;
1337 case '-':
1338 return CODE_NAME_INDEX_NumpadSubtract;
1339 case '*':
1340 return CODE_NAME_INDEX_NumpadMultiply;
1341 case '/':
1342 return CODE_NAME_INDEX_NumpadDivide;
1343 case '.':
1344 return CODE_NAME_INDEX_NumpadDecimal;
1345 case '0':
1346 return CODE_NAME_INDEX_Numpad0;
1347 case '1':
1348 return CODE_NAME_INDEX_Numpad1;
1349 case '2':
1350 return CODE_NAME_INDEX_Numpad2;
1351 case '3':
1352 return CODE_NAME_INDEX_Numpad3;
1353 case '4':
1354 return CODE_NAME_INDEX_Numpad4;
1355 case '5':
1356 return CODE_NAME_INDEX_Numpad5;
1357 case '6':
1358 return CODE_NAME_INDEX_Numpad6;
1359 case '7':
1360 return CODE_NAME_INDEX_Numpad7;
1361 case '8':
1362 return CODE_NAME_INDEX_Numpad8;
1363 case '9':
1364 return CODE_NAME_INDEX_Numpad9;
1365 default:
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]) {
1378 case 'a':
1379 case 'A':
1380 return CODE_NAME_INDEX_KeyA;
1381 case 'b':
1382 case 'B':
1383 return CODE_NAME_INDEX_KeyB;
1384 case 'c':
1385 case 'C':
1386 return CODE_NAME_INDEX_KeyC;
1387 case 'd':
1388 case 'D':
1389 return CODE_NAME_INDEX_KeyD;
1390 case 'e':
1391 case 'E':
1392 return CODE_NAME_INDEX_KeyE;
1393 case 'f':
1394 case 'F':
1395 return CODE_NAME_INDEX_KeyF;
1396 case 'g':
1397 case 'G':
1398 return CODE_NAME_INDEX_KeyG;
1399 case 'h':
1400 case 'H':
1401 return CODE_NAME_INDEX_KeyH;
1402 case 'i':
1403 case 'I':
1404 return CODE_NAME_INDEX_KeyI;
1405 case 'j':
1406 case 'J':
1407 return CODE_NAME_INDEX_KeyJ;
1408 case 'k':
1409 case 'K':
1410 return CODE_NAME_INDEX_KeyK;
1411 case 'l':
1412 case 'L':
1413 return CODE_NAME_INDEX_KeyL;
1414 case 'm':
1415 case 'M':
1416 return CODE_NAME_INDEX_KeyM;
1417 case 'n':
1418 case 'N':
1419 return CODE_NAME_INDEX_KeyN;
1420 case 'o':
1421 case 'O':
1422 return CODE_NAME_INDEX_KeyO;
1423 case 'p':
1424 case 'P':
1425 return CODE_NAME_INDEX_KeyP;
1426 case 'q':
1427 case 'Q':
1428 return CODE_NAME_INDEX_KeyQ;
1429 case 'r':
1430 case 'R':
1431 return CODE_NAME_INDEX_KeyR;
1432 case 's':
1433 case 'S':
1434 return CODE_NAME_INDEX_KeyS;
1435 case 't':
1436 case 'T':
1437 return CODE_NAME_INDEX_KeyT;
1438 case 'u':
1439 case 'U':
1440 return CODE_NAME_INDEX_KeyU;
1441 case 'v':
1442 case 'V':
1443 return CODE_NAME_INDEX_KeyV;
1444 case 'w':
1445 case 'W':
1446 return CODE_NAME_INDEX_KeyW;
1447 case 'x':
1448 case 'X':
1449 return CODE_NAME_INDEX_KeyX;
1450 case 'y':
1451 case 'Y':
1452 return CODE_NAME_INDEX_KeyY;
1453 case 'z':
1454 case 'Z':
1455 return CODE_NAME_INDEX_KeyZ;
1457 case '`':
1458 case '~':
1459 return CODE_NAME_INDEX_Backquote;
1460 case '1':
1461 case '!':
1462 return CODE_NAME_INDEX_Digit1;
1463 case '2':
1464 case '@':
1465 return CODE_NAME_INDEX_Digit2;
1466 case '3':
1467 case '#':
1468 return CODE_NAME_INDEX_Digit3;
1469 case '4':
1470 case '$':
1471 return CODE_NAME_INDEX_Digit4;
1472 case '5':
1473 case '%':
1474 return CODE_NAME_INDEX_Digit5;
1475 case '6':
1476 case '^':
1477 return CODE_NAME_INDEX_Digit6;
1478 case '7':
1479 case '&':
1480 return CODE_NAME_INDEX_Digit7;
1481 case '8':
1482 case '*':
1483 return CODE_NAME_INDEX_Digit8;
1484 case '9':
1485 case '(':
1486 return CODE_NAME_INDEX_Digit9;
1487 case '0':
1488 case ')':
1489 return CODE_NAME_INDEX_Digit0;
1490 case '-':
1491 case '_':
1492 return CODE_NAME_INDEX_Minus;
1493 case '=':
1494 case '+':
1495 return CODE_NAME_INDEX_Equal;
1497 case '[':
1498 case '{':
1499 return CODE_NAME_INDEX_BracketLeft;
1500 case ']':
1501 case '}':
1502 return CODE_NAME_INDEX_BracketRight;
1503 case '\\':
1504 case '|':
1505 return CODE_NAME_INDEX_Backslash;
1507 case ';':
1508 case ':':
1509 return CODE_NAME_INDEX_Semicolon;
1510 case '\'':
1511 case '"':
1512 return CODE_NAME_INDEX_Quote;
1514 case ',':
1515 case '<':
1516 return CODE_NAME_INDEX_Comma;
1517 case '.':
1518 case '>':
1519 return CODE_NAME_INDEX_Period;
1520 case '/':
1521 case '?':
1522 return CODE_NAME_INDEX_Slash;
1524 case ' ':
1525 return CODE_NAME_INDEX_Space;
1527 default:
1528 return CODE_NAME_INDEX_UNKNOWN;
1532 NS_IMETHODIMP
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()));
1546 } else {
1547 NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
1548 "aLocation must be undefined, null or int");
1549 return NS_ERROR_INVALID_ARG;
1553 *aKeyCodeValue =
1554 GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue, location);
1555 return NS_OK;
1558 // static
1559 uint32_t TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
1560 const nsAString& aKeyValue, const Maybe<uint32_t>& aLocation) {
1561 if (aKeyValue.IsEmpty()) {
1562 return 0;
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) {
1568 return 0;
1571 if (aLocation.isSome() &&
1572 aLocation.value() ==
1573 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
1574 switch (aKeyValue[0]) {
1575 case '+':
1576 return dom::KeyboardEvent_Binding::DOM_VK_ADD;
1577 case '-':
1578 return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT;
1579 case '*':
1580 return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY;
1581 case '/':
1582 return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE;
1583 case '.':
1584 return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL;
1585 case '0':
1586 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0;
1587 case '1':
1588 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1;
1589 case '2':
1590 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2;
1591 case '3':
1592 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3;
1593 case '4':
1594 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4;
1595 case '5':
1596 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5;
1597 case '6':
1598 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6;
1599 case '7':
1600 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7;
1601 case '8':
1602 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8;
1603 case '9':
1604 return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9;
1605 default:
1606 return 0;
1610 if (aLocation.isSome() &&
1611 aLocation.value() !=
1612 dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
1613 return 0;
1616 // TODO: Support characters inputted with option key on macOS.
1617 switch (aKeyValue[0]) {
1618 case 'a':
1619 case 'A':
1620 return dom::KeyboardEvent_Binding::DOM_VK_A;
1621 case 'b':
1622 case 'B':
1623 return dom::KeyboardEvent_Binding::DOM_VK_B;
1624 case 'c':
1625 case 'C':
1626 return dom::KeyboardEvent_Binding::DOM_VK_C;
1627 case 'd':
1628 case 'D':
1629 return dom::KeyboardEvent_Binding::DOM_VK_D;
1630 case 'e':
1631 case 'E':
1632 return dom::KeyboardEvent_Binding::DOM_VK_E;
1633 case 'f':
1634 case 'F':
1635 return dom::KeyboardEvent_Binding::DOM_VK_F;
1636 case 'g':
1637 case 'G':
1638 return dom::KeyboardEvent_Binding::DOM_VK_G;
1639 case 'h':
1640 case 'H':
1641 return dom::KeyboardEvent_Binding::DOM_VK_H;
1642 case 'i':
1643 case 'I':
1644 return dom::KeyboardEvent_Binding::DOM_VK_I;
1645 case 'j':
1646 case 'J':
1647 return dom::KeyboardEvent_Binding::DOM_VK_J;
1648 case 'k':
1649 case 'K':
1650 return dom::KeyboardEvent_Binding::DOM_VK_K;
1651 case 'l':
1652 case 'L':
1653 return dom::KeyboardEvent_Binding::DOM_VK_L;
1654 case 'm':
1655 case 'M':
1656 return dom::KeyboardEvent_Binding::DOM_VK_M;
1657 case 'n':
1658 case 'N':
1659 return dom::KeyboardEvent_Binding::DOM_VK_N;
1660 case 'o':
1661 case 'O':
1662 return dom::KeyboardEvent_Binding::DOM_VK_O;
1663 case 'p':
1664 case 'P':
1665 return dom::KeyboardEvent_Binding::DOM_VK_P;
1666 case 'q':
1667 case 'Q':
1668 return dom::KeyboardEvent_Binding::DOM_VK_Q;
1669 case 'r':
1670 case 'R':
1671 return dom::KeyboardEvent_Binding::DOM_VK_R;
1672 case 's':
1673 case 'S':
1674 return dom::KeyboardEvent_Binding::DOM_VK_S;
1675 case 't':
1676 case 'T':
1677 return dom::KeyboardEvent_Binding::DOM_VK_T;
1678 case 'u':
1679 case 'U':
1680 return dom::KeyboardEvent_Binding::DOM_VK_U;
1681 case 'v':
1682 case 'V':
1683 return dom::KeyboardEvent_Binding::DOM_VK_V;
1684 case 'w':
1685 case 'W':
1686 return dom::KeyboardEvent_Binding::DOM_VK_W;
1687 case 'x':
1688 case 'X':
1689 return dom::KeyboardEvent_Binding::DOM_VK_X;
1690 case 'y':
1691 case 'Y':
1692 return dom::KeyboardEvent_Binding::DOM_VK_Y;
1693 case 'z':
1694 case 'Z':
1695 return dom::KeyboardEvent_Binding::DOM_VK_Z;
1697 case '`':
1698 case '~':
1699 return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE;
1700 case '1':
1701 case '!':
1702 return dom::KeyboardEvent_Binding::DOM_VK_1;
1703 case '2':
1704 case '@':
1705 return dom::KeyboardEvent_Binding::DOM_VK_2;
1706 case '3':
1707 case '#':
1708 return dom::KeyboardEvent_Binding::DOM_VK_3;
1709 case '4':
1710 case '$':
1711 return dom::KeyboardEvent_Binding::DOM_VK_4;
1712 case '5':
1713 case '%':
1714 return dom::KeyboardEvent_Binding::DOM_VK_5;
1715 case '6':
1716 case '^':
1717 return dom::KeyboardEvent_Binding::DOM_VK_6;
1718 case '7':
1719 case '&':
1720 return dom::KeyboardEvent_Binding::DOM_VK_7;
1721 case '8':
1722 case '*':
1723 return dom::KeyboardEvent_Binding::DOM_VK_8;
1724 case '9':
1725 case '(':
1726 return dom::KeyboardEvent_Binding::DOM_VK_9;
1727 case '0':
1728 case ')':
1729 return dom::KeyboardEvent_Binding::DOM_VK_0;
1730 case '-':
1731 case '_':
1732 return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS;
1733 case '=':
1734 case '+':
1735 return dom::KeyboardEvent_Binding::DOM_VK_EQUALS;
1737 case '[':
1738 case '{':
1739 return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET;
1740 case ']':
1741 case '}':
1742 return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET;
1743 case '\\':
1744 case '|':
1745 return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH;
1747 case ';':
1748 case ':':
1749 return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON;
1750 case '\'':
1751 case '"':
1752 return dom::KeyboardEvent_Binding::DOM_VK_QUOTE;
1754 case ',':
1755 case '<':
1756 return dom::KeyboardEvent_Binding::DOM_VK_COMMA;
1757 case '.':
1758 case '>':
1759 return dom::KeyboardEvent_Binding::DOM_VK_PERIOD;
1760 case '/':
1761 case '?':
1762 return dom::KeyboardEvent_Binding::DOM_VK_SLASH;
1764 case ' ':
1765 return dom::KeyboardEvent_Binding::DOM_VK_SPACE;
1767 default:
1768 return 0;
1772 /******************************************************************************
1773 * TextInputProcessor::AutoPendingCompositionResetter
1774 ******************************************************************************/
1775 TextInputProcessor::AutoPendingCompositionResetter::
1776 AutoPendingCompositionResetter(TextInputProcessor* aTIP)
1777 : mTIP(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;
1807 return result;
1810 void TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
1811 const TextInputProcessor::ModifierKeyData& aModifierKeyData) {
1812 if (Contains(aModifierKeyData)) {
1813 return;
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);
1828 return;
1830 RemoveElementAt(index);
1833 } // namespace mozilla