tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TextInputProcessor.cpp (61295B)


      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/. */
      6 
      7 #include "mozilla/TextInputProcessor.h"
      8 
      9 #include "mozilla/EventForwards.h"
     10 #include "mozilla/Maybe.h"
     11 #include "mozilla/NativeKeyBindingsType.h"
     12 #include "mozilla/StaticPrefs_test.h"
     13 #include "mozilla/TextEventDispatcher.h"
     14 #include "mozilla/TextEvents.h"
     15 #include "mozilla/WritingModes.h"
     16 #include "mozilla/dom/Event.h"
     17 #include "mozilla/dom/KeyboardEvent.h"
     18 #include "mozilla/widget/IMEData.h"
     19 #include "nsContentUtils.h"
     20 #include "nsIDocShell.h"
     21 #include "nsIWidget.h"
     22 #include "nsPIDOMWindow.h"
     23 #include "nsPresContext.h"
     24 
     25 using mozilla::dom::Event;
     26 using mozilla::dom::KeyboardEvent;
     27 using namespace mozilla::widget;
     28 
     29 namespace mozilla {
     30 
     31 /******************************************************************************
     32 * TextInputProcessorNotification
     33 ******************************************************************************/
     34 
     35 class TextInputProcessorNotification final
     36    : public nsITextInputProcessorNotification {
     37  using SelectionChangeData = IMENotification::SelectionChangeData;
     38  using SelectionChangeDataBase = IMENotification::SelectionChangeDataBase;
     39  using TextChangeData = IMENotification::TextChangeData;
     40  using TextChangeDataBase = IMENotification::TextChangeDataBase;
     41 
     42 public:
     43  explicit TextInputProcessorNotification(const char* aType)
     44      : mType(aType), mTextChangeData() {}
     45 
     46  explicit TextInputProcessorNotification(
     47      const TextChangeDataBase& aTextChangeData)
     48      : mType("notify-text-change"), mTextChangeData(aTextChangeData) {}
     49 
     50  explicit TextInputProcessorNotification(
     51      const SelectionChangeDataBase& aSelectionChangeData)
     52      : mType("notify-selection-change"),
     53        mSelectionChangeData(aSelectionChangeData) {
     54    // SelectionChangeDataBase::mString still refers nsString instance owned
     55    // by aSelectionChangeData.  So, this needs to copy the instance.
     56    if (aSelectionChangeData.HasRange()) {
     57      mSelectionChangeData.mString =
     58          new nsString(aSelectionChangeData.String());
     59    } else {
     60      mSelectionChangeData.mString = nullptr;
     61    }
     62  }
     63 
     64  NS_DECL_ISUPPORTS
     65 
     66  NS_IMETHOD GetType(nsACString& aType) final {
     67    aType = mType;
     68    return NS_OK;
     69  }
     70 
     71  // "notify-text-change" and "notify-selection-change"
     72  NS_IMETHOD GetOffset(uint32_t* aOffset) final {
     73    if (NS_WARN_IF(!aOffset)) {
     74      return NS_ERROR_INVALID_ARG;
     75    }
     76    if (IsSelectionChange()) {
     77      if (!mSelectionChangeData.HasRange()) {
     78        return NS_ERROR_NOT_AVAILABLE;
     79      }
     80      *aOffset = mSelectionChangeData.mOffset;
     81      return NS_OK;
     82    }
     83    if (IsTextChange()) {
     84      *aOffset = mTextChangeData.mStartOffset;
     85      return NS_OK;
     86    }
     87    return NS_ERROR_NOT_AVAILABLE;
     88  }
     89 
     90  // "notify-selection-change"
     91  NS_IMETHOD GetHasRange(bool* aHasRange) final {
     92    if (IsSelectionChange()) {
     93      *aHasRange = mSelectionChangeData.HasRange();
     94      return NS_OK;
     95    }
     96    return NS_ERROR_NOT_AVAILABLE;
     97  }
     98  NS_IMETHOD GetText(nsAString& aText) final {
     99    if (IsSelectionChange()) {
    100      if (!mSelectionChangeData.HasRange()) {
    101        return NS_ERROR_NOT_AVAILABLE;
    102      }
    103      aText = mSelectionChangeData.String();
    104      return NS_OK;
    105    }
    106    return NS_ERROR_NOT_AVAILABLE;
    107  }
    108 
    109  NS_IMETHOD GetCollapsed(bool* aCollapsed) final {
    110    if (NS_WARN_IF(!aCollapsed)) {
    111      return NS_ERROR_INVALID_ARG;
    112    }
    113    if (IsSelectionChange()) {
    114      *aCollapsed = mSelectionChangeData.IsCollapsed();
    115      return NS_OK;
    116    }
    117    return NS_ERROR_NOT_AVAILABLE;
    118  }
    119 
    120  NS_IMETHOD GetLength(uint32_t* aLength) final {
    121    if (NS_WARN_IF(!aLength)) {
    122      return NS_ERROR_INVALID_ARG;
    123    }
    124    if (IsSelectionChange()) {
    125      if (!mSelectionChangeData.HasRange()) {
    126        return NS_ERROR_NOT_AVAILABLE;
    127      }
    128      *aLength = mSelectionChangeData.Length();
    129      return NS_OK;
    130    }
    131    return NS_ERROR_NOT_AVAILABLE;
    132  }
    133 
    134  NS_IMETHOD GetReversed(bool* aReversed) final {
    135    if (NS_WARN_IF(!aReversed)) {
    136      return NS_ERROR_INVALID_ARG;
    137    }
    138    if (IsSelectionChange()) {
    139      if (!mSelectionChangeData.HasRange()) {
    140        return NS_ERROR_NOT_AVAILABLE;
    141      }
    142      *aReversed = mSelectionChangeData.mReversed;
    143      return NS_OK;
    144    }
    145    return NS_ERROR_NOT_AVAILABLE;
    146  }
    147 
    148  NS_IMETHOD GetWritingMode(nsACString& aWritingMode) final {
    149    if (IsSelectionChange()) {
    150      WritingMode writingMode = mSelectionChangeData.GetWritingMode();
    151      if (!writingMode.IsVertical()) {
    152        aWritingMode.AssignLiteral("horizontal-tb");
    153      } else if (writingMode.IsVerticalLR()) {
    154        aWritingMode.AssignLiteral("vertical-lr");
    155      } else {
    156        aWritingMode.AssignLiteral("vertical-rl");
    157      }
    158      return NS_OK;
    159    }
    160    return NS_ERROR_NOT_AVAILABLE;
    161  }
    162 
    163  NS_IMETHOD GetCausedByComposition(bool* aCausedByComposition) final {
    164    if (NS_WARN_IF(!aCausedByComposition)) {
    165      return NS_ERROR_INVALID_ARG;
    166    }
    167    if (IsSelectionChange()) {
    168      *aCausedByComposition = mSelectionChangeData.mCausedByComposition;
    169      return NS_OK;
    170    }
    171    return NS_ERROR_NOT_AVAILABLE;
    172  }
    173 
    174  NS_IMETHOD GetCausedBySelectionEvent(bool* aCausedBySelectionEvent) final {
    175    if (NS_WARN_IF(!aCausedBySelectionEvent)) {
    176      return NS_ERROR_INVALID_ARG;
    177    }
    178    if (IsSelectionChange()) {
    179      *aCausedBySelectionEvent = mSelectionChangeData.mCausedBySelectionEvent;
    180      return NS_OK;
    181    }
    182    return NS_ERROR_NOT_AVAILABLE;
    183  }
    184 
    185  NS_IMETHOD GetOccurredDuringComposition(
    186      bool* aOccurredDuringComposition) final {
    187    if (NS_WARN_IF(!aOccurredDuringComposition)) {
    188      return NS_ERROR_INVALID_ARG;
    189    }
    190    if (IsSelectionChange()) {
    191      *aOccurredDuringComposition =
    192          mSelectionChangeData.mOccurredDuringComposition;
    193      return NS_OK;
    194    }
    195    return NS_ERROR_NOT_AVAILABLE;
    196  }
    197 
    198  // "notify-text-change"
    199  NS_IMETHOD GetRemovedLength(uint32_t* aLength) final {
    200    if (NS_WARN_IF(!aLength)) {
    201      return NS_ERROR_INVALID_ARG;
    202    }
    203    if (IsTextChange()) {
    204      *aLength = mTextChangeData.OldLength();
    205      return NS_OK;
    206    }
    207    return NS_ERROR_NOT_AVAILABLE;
    208  }
    209 
    210  NS_IMETHOD GetAddedLength(uint32_t* aLength) final {
    211    if (NS_WARN_IF(!aLength)) {
    212      return NS_ERROR_INVALID_ARG;
    213    }
    214    if (IsTextChange()) {
    215      *aLength = mTextChangeData.NewLength();
    216      return NS_OK;
    217    }
    218    return NS_ERROR_NOT_AVAILABLE;
    219  }
    220 
    221  NS_IMETHOD GetCausedOnlyByComposition(bool* aCausedOnlyByComposition) final {
    222    if (NS_WARN_IF(!aCausedOnlyByComposition)) {
    223      return NS_ERROR_INVALID_ARG;
    224    }
    225    if (IsTextChange()) {
    226      *aCausedOnlyByComposition = mTextChangeData.mCausedOnlyByComposition;
    227      return NS_OK;
    228    }
    229    return NS_ERROR_NOT_AVAILABLE;
    230  }
    231 
    232  NS_IMETHOD GetIncludingChangesDuringComposition(
    233      bool* aIncludingChangesDuringComposition) final {
    234    if (NS_WARN_IF(!aIncludingChangesDuringComposition)) {
    235      return NS_ERROR_INVALID_ARG;
    236    }
    237    if (IsTextChange()) {
    238      *aIncludingChangesDuringComposition =
    239          mTextChangeData.mIncludingChangesDuringComposition;
    240      return NS_OK;
    241    }
    242    return NS_ERROR_NOT_AVAILABLE;
    243  }
    244 
    245  NS_IMETHOD GetIncludingChangesWithoutComposition(
    246      bool* aIncludingChangesWithoutComposition) final {
    247    if (NS_WARN_IF(!aIncludingChangesWithoutComposition)) {
    248      return NS_ERROR_INVALID_ARG;
    249    }
    250    if (IsTextChange()) {
    251      *aIncludingChangesWithoutComposition =
    252          mTextChangeData.mIncludingChangesWithoutComposition;
    253      return NS_OK;
    254    }
    255    return NS_ERROR_NOT_AVAILABLE;
    256  }
    257 
    258 protected:
    259  virtual ~TextInputProcessorNotification() {
    260    if (IsSelectionChange() && mSelectionChangeData.mString) {
    261      delete mSelectionChangeData.mString;
    262      mSelectionChangeData.mString = nullptr;
    263    }
    264  }
    265 
    266  bool IsTextChange() const {
    267    return mType.EqualsLiteral("notify-text-change");
    268  }
    269 
    270  bool IsSelectionChange() const {
    271    return mType.EqualsLiteral("notify-selection-change");
    272  }
    273 
    274 private:
    275  nsAutoCString mType;
    276  union {
    277    TextChangeDataBase mTextChangeData;
    278    SelectionChangeDataBase mSelectionChangeData;
    279  };
    280 
    281  TextInputProcessorNotification() : mTextChangeData() {}
    282 };
    283 
    284 NS_IMPL_ISUPPORTS(TextInputProcessorNotification,
    285                  nsITextInputProcessorNotification)
    286 
    287 /******************************************************************************
    288 * TextInputProcessor
    289 ******************************************************************************/
    290 
    291 NS_IMPL_ISUPPORTS(TextInputProcessor, nsITextInputProcessor,
    292                  TextEventDispatcherListener, nsISupportsWeakReference)
    293 
    294 TextInputProcessor::TextInputProcessor()
    295    : mDispatcher(nullptr), mForTests(false) {}
    296 
    297 TextInputProcessor::~TextInputProcessor() {
    298  if (mDispatcher && mDispatcher->IsComposing()) {
    299    // If this is composing and not canceling the composition, nobody can steal
    300    // the rights of TextEventDispatcher from this instance.  Therefore, this
    301    // needs to cancel the composition here.
    302    if (NS_SUCCEEDED(IsValidStateForComposition())) {
    303      RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    304      nsEventStatus status = nsEventStatus_eIgnore;
    305      kungFuDeathGrip->CommitComposition(status, &EmptyString());
    306    }
    307  }
    308 }
    309 
    310 bool TextInputProcessor::IsComposing() const {
    311  return mDispatcher && mDispatcher->IsComposing();
    312 }
    313 
    314 NS_IMETHODIMP
    315 TextInputProcessor::GetHasComposition(bool* aHasComposition) {
    316  MOZ_RELEASE_ASSERT(aHasComposition, "aHasComposition must not be nullptr");
    317  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    318  *aHasComposition = IsComposing();
    319  return NS_OK;
    320 }
    321 
    322 NS_IMETHODIMP
    323 TextInputProcessor::BeginInputTransaction(
    324    mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback,
    325    bool* aSucceeded) {
    326  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
    327  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    328  if (NS_WARN_IF(!aCallback)) {
    329    *aSucceeded = false;
    330    return NS_ERROR_INVALID_ARG;
    331  }
    332  return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
    333 }
    334 
    335 NS_IMETHODIMP
    336 TextInputProcessor::BeginInputTransactionForTests(
    337    mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback,
    338    uint8_t aOptionalArgc, bool* aSucceeded) {
    339  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
    340  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    341  nsITextInputProcessorCallback* callback =
    342      aOptionalArgc >= 1 ? aCallback : nullptr;
    343  return BeginInputTransactionInternal(aWindow, callback, true, *aSucceeded);
    344 }
    345 
    346 nsresult TextInputProcessor::BeginInputTransactionForFuzzing(
    347    nsPIDOMWindowInner* aWindow, nsITextInputProcessorCallback* aCallback,
    348    bool* aSucceeded) {
    349  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
    350  return BeginInputTransactionInternal(aWindow, aCallback, false, *aSucceeded);
    351 }
    352 
    353 nsresult TextInputProcessor::BeginInputTransactionInternal(
    354    mozIDOMWindow* aWindow, nsITextInputProcessorCallback* aCallback,
    355    bool aForTests, bool& aSucceeded) {
    356  aSucceeded = false;
    357  if (NS_WARN_IF(!aWindow)) {
    358    return NS_ERROR_INVALID_ARG;
    359  }
    360  nsCOMPtr<nsPIDOMWindowInner> pWindow = nsPIDOMWindowInner::From(aWindow);
    361  if (NS_WARN_IF(!pWindow)) {
    362    return NS_ERROR_INVALID_ARG;
    363  }
    364  nsCOMPtr<nsIDocShell> docShell(pWindow->GetDocShell());
    365  if (NS_WARN_IF(!docShell)) {
    366    return NS_ERROR_FAILURE;
    367  }
    368  RefPtr<nsPresContext> presContext = docShell->GetPresContext();
    369  if (NS_WARN_IF(!presContext)) {
    370    return NS_ERROR_FAILURE;
    371  }
    372  nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
    373  if (NS_WARN_IF(!widget)) {
    374    return NS_ERROR_FAILURE;
    375  }
    376 
    377  RefPtr<TextEventDispatcher> dispatcher = widget->GetTextEventDispatcher();
    378  MOZ_RELEASE_ASSERT(dispatcher, "TextEventDispatcher must not be null");
    379 
    380  // If the instance was initialized and is being initialized for same
    381  // dispatcher and same purpose, we don't need to initialize the dispatcher
    382  // again.
    383  if (mDispatcher && dispatcher == mDispatcher && aCallback == mCallback &&
    384      aForTests == mForTests) {
    385    aSucceeded = true;
    386    return NS_OK;
    387  }
    388 
    389  // If this instance is composing or dispatching an event, don't allow to
    390  // initialize again.  Especially, if we allow to begin input transaction with
    391  // another TextEventDispatcher during dispatching an event, it may cause that
    392  // nobody cannot begin input transaction with it if the last event causes
    393  // opening modal dialog.
    394  if (mDispatcher &&
    395      (mDispatcher->IsComposing() || mDispatcher->IsDispatchingEvent())) {
    396    return NS_ERROR_ALREADY_INITIALIZED;
    397  }
    398 
    399  // And also if another instance is composing with the new dispatcher or
    400  // dispatching an event, it'll fail to steal its ownership.  Then, we should
    401  // not throw an exception, just return false.
    402  if (dispatcher->IsComposing() || dispatcher->IsDispatchingEvent()) {
    403    return NS_OK;
    404  }
    405 
    406  // This instance has finished preparing to link to the dispatcher.  Therefore,
    407  // let's forget the old dispatcher and purpose.
    408  if (mDispatcher) {
    409    mDispatcher->EndInputTransaction(this);
    410    if (NS_WARN_IF(mDispatcher)) {
    411      // Forcibly initialize the members if we failed to end the input
    412      // transaction.
    413      UnlinkFromTextEventDispatcher();
    414    }
    415  }
    416 
    417  nsresult rv = NS_OK;
    418  if (aForTests) {
    419    bool isAPZAware = StaticPrefs::test_events_async_enabled();
    420    rv = dispatcher->BeginTestInputTransaction(this, isAPZAware);
    421  } else {
    422    rv = dispatcher->BeginInputTransaction(this);
    423  }
    424 
    425  if (NS_WARN_IF(NS_FAILED(rv))) {
    426    return rv;
    427  }
    428 
    429  mDispatcher = dispatcher;
    430  mCallback = aCallback;
    431  mForTests = aForTests;
    432  aSucceeded = true;
    433  return NS_OK;
    434 }
    435 
    436 void TextInputProcessor::UnlinkFromTextEventDispatcher() {
    437  mDispatcher = nullptr;
    438  mForTests = false;
    439  if (mCallback) {
    440    nsCOMPtr<nsITextInputProcessorCallback> callback(mCallback);
    441    mCallback = nullptr;
    442 
    443    RefPtr<TextInputProcessorNotification> notification =
    444        new TextInputProcessorNotification("notify-end-input-transaction");
    445    bool result = false;
    446    callback->OnNotify(this, notification, &result);
    447  }
    448 }
    449 
    450 nsresult TextInputProcessor::IsValidStateForComposition() {
    451  if (NS_WARN_IF(!mDispatcher)) {
    452    return NS_ERROR_NOT_INITIALIZED;
    453  }
    454 
    455  nsresult rv = mDispatcher->GetState();
    456  if (NS_WARN_IF(NS_FAILED(rv))) {
    457    return rv;
    458  }
    459 
    460  return NS_OK;
    461 }
    462 
    463 bool TextInputProcessor::IsValidEventTypeForComposition(
    464    const WidgetKeyboardEvent& aKeyboardEvent) const {
    465  // The key event type of composition methods must be "", "keydown" or "keyup".
    466  if (aKeyboardEvent.mMessage == eKeyDown ||
    467      aKeyboardEvent.mMessage == eKeyUp) {
    468    return true;
    469  }
    470  if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
    471      aKeyboardEvent.mSpecifiedEventType &&
    472      nsDependentAtomString(aKeyboardEvent.mSpecifiedEventType)
    473          .EqualsLiteral("on")) {
    474    return true;
    475  }
    476  return false;
    477 }
    478 
    479 TextInputProcessor::EventDispatcherResult
    480 TextInputProcessor::MaybeDispatchKeydownForComposition(
    481    const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) {
    482  EventDispatcherResult result;
    483 
    484  result.mResult = IsValidStateForComposition();
    485  if (NS_WARN_IF(NS_FAILED(result.mResult))) {
    486    result.mCanContinue = false;
    487    return result;
    488  }
    489 
    490  if (!aKeyboardEvent) {
    491    return result;
    492  }
    493 
    494  // If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
    495  // eKeyDown event.
    496  if (aKeyboardEvent->mMessage == eKeyUp) {
    497    return result;
    498  }
    499 
    500  // Modifier keys are not allowed because managing modifier state in this
    501  // method makes this messy.
    502  if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) {
    503    result.mResult = NS_ERROR_INVALID_ARG;
    504    result.mCanContinue = false;
    505    return result;
    506  }
    507 
    508  uint32_t consumedFlags = 0;
    509 
    510  result.mResult =
    511      KeydownInternal(*aKeyboardEvent, aKeyFlags, false, consumedFlags);
    512  result.mDoDefault = !consumedFlags;
    513  if (NS_WARN_IF(NS_FAILED(result.mResult))) {
    514    result.mCanContinue = false;
    515    return result;
    516  }
    517 
    518  result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
    519  return result;
    520 }
    521 
    522 TextInputProcessor::EventDispatcherResult
    523 TextInputProcessor::MaybeDispatchKeyupForComposition(
    524    const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) {
    525  EventDispatcherResult result;
    526 
    527  if (!aKeyboardEvent) {
    528    return result;
    529  }
    530 
    531  // If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
    532  // eKeyUp event.
    533  if (aKeyboardEvent->mMessage == eKeyDown) {
    534    return result;
    535  }
    536 
    537  // If the widget has been destroyed, we can do nothing here.
    538  result.mResult = IsValidStateForComposition();
    539  if (NS_FAILED(result.mResult)) {
    540    result.mCanContinue = false;
    541    return result;
    542  }
    543 
    544  result.mResult = KeyupInternal(*aKeyboardEvent, aKeyFlags, result.mDoDefault);
    545  if (NS_WARN_IF(NS_FAILED(result.mResult))) {
    546    result.mCanContinue = false;
    547    return result;
    548  }
    549 
    550  result.mCanContinue = NS_SUCCEEDED(IsValidStateForComposition());
    551  return result;
    552 }
    553 
    554 nsresult TextInputProcessor::PrepareKeyboardEventForComposition(
    555    KeyboardEvent* aDOMKeyEvent, uint32_t& aKeyFlags, uint8_t aOptionalArgc,
    556    WidgetKeyboardEvent*& aKeyboardEvent) {
    557  aKeyboardEvent = nullptr;
    558 
    559  aKeyboardEvent = aOptionalArgc && aDOMKeyEvent
    560                       ? aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent()
    561                       : nullptr;
    562  if (!aKeyboardEvent || aOptionalArgc < 2) {
    563    aKeyFlags = 0;
    564  }
    565 
    566  if (!aKeyboardEvent) {
    567    return NS_OK;
    568  }
    569 
    570  if (NS_WARN_IF(!IsValidEventTypeForComposition(*aKeyboardEvent))) {
    571    return NS_ERROR_INVALID_ARG;
    572  }
    573 
    574  return NS_OK;
    575 }
    576 
    577 NS_IMETHODIMP
    578 TextInputProcessor::StartComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
    579                                     uint8_t aOptionalArgc, bool* aSucceeded) {
    580  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
    581  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    582  *aSucceeded = false;
    583 
    584  RefPtr<KeyboardEvent> keyEvent;
    585  if (aDOMKeyEvent) {
    586    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
    587    if (NS_WARN_IF(!keyEvent)) {
    588      return NS_ERROR_INVALID_ARG;
    589    }
    590  }
    591 
    592  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    593 
    594  WidgetKeyboardEvent* keyboardEvent;
    595  nsresult rv = PrepareKeyboardEventForComposition(
    596      keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent);
    597  if (NS_WARN_IF(NS_FAILED(rv))) {
    598    return rv;
    599  }
    600 
    601  EventDispatcherResult dispatcherResult =
    602      MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
    603  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
    604      !dispatcherResult.mCanContinue) {
    605    return dispatcherResult.mResult;
    606  }
    607 
    608  if (dispatcherResult.mDoDefault) {
    609    nsEventStatus status = nsEventStatus_eIgnore;
    610    rv = kungFuDeathGrip->StartComposition(status);
    611    *aSucceeded = status != nsEventStatus_eConsumeNoDefault &&
    612                  kungFuDeathGrip && kungFuDeathGrip->IsComposing();
    613  }
    614 
    615  MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
    616 
    617  if (NS_WARN_IF(NS_FAILED(rv))) {
    618    return rv;
    619  }
    620  return NS_OK;
    621 }
    622 
    623 NS_IMETHODIMP
    624 TextInputProcessor::SetPendingCompositionString(const nsAString& aString) {
    625  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    626  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    627  nsresult rv = IsValidStateForComposition();
    628  if (NS_WARN_IF(NS_FAILED(rv))) {
    629    return rv;
    630  }
    631  return kungFuDeathGrip->SetPendingCompositionString(aString);
    632 }
    633 
    634 NS_IMETHODIMP
    635 TextInputProcessor::AppendClauseToPendingComposition(uint32_t aLength,
    636                                                     uint32_t aAttribute) {
    637  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    638  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    639  TextRangeType textRangeType;
    640  switch (aAttribute) {
    641    case ATTR_RAW_CLAUSE:
    642    case ATTR_SELECTED_RAW_CLAUSE:
    643    case ATTR_CONVERTED_CLAUSE:
    644    case ATTR_SELECTED_CLAUSE:
    645      textRangeType = ToTextRangeType(aAttribute);
    646      break;
    647    default:
    648      return NS_ERROR_INVALID_ARG;
    649  }
    650  nsresult rv = IsValidStateForComposition();
    651  if (NS_WARN_IF(NS_FAILED(rv))) {
    652    return rv;
    653  }
    654  return kungFuDeathGrip->AppendClauseToPendingComposition(aLength,
    655                                                           textRangeType);
    656 }
    657 
    658 NS_IMETHODIMP
    659 TextInputProcessor::SetCaretInPendingComposition(uint32_t aOffset) {
    660  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    661  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    662  nsresult rv = IsValidStateForComposition();
    663  if (NS_WARN_IF(NS_FAILED(rv))) {
    664    return rv;
    665  }
    666  return kungFuDeathGrip->SetCaretInPendingComposition(aOffset, 0);
    667 }
    668 
    669 NS_IMETHODIMP
    670 TextInputProcessor::FlushPendingComposition(Event* aDOMKeyEvent,
    671                                            uint32_t aKeyFlags,
    672                                            uint8_t aOptionalArgc,
    673                                            bool* aSucceeded) {
    674  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
    675  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    676 
    677  // Even if this doesn't flush pending composition actually, we need to reset
    678  // pending composition for starting next composition with new user input.
    679  AutoPendingCompositionResetter resetter(this);
    680 
    681  *aSucceeded = false;
    682  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    683  bool wasComposing = IsComposing();
    684 
    685  RefPtr<KeyboardEvent> keyEvent;
    686  if (aDOMKeyEvent) {
    687    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
    688    if (NS_WARN_IF(!keyEvent)) {
    689      return NS_ERROR_INVALID_ARG;
    690    }
    691  }
    692 
    693  WidgetKeyboardEvent* keyboardEvent;
    694  nsresult rv = PrepareKeyboardEventForComposition(
    695      keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent);
    696  if (NS_WARN_IF(NS_FAILED(rv))) {
    697    return rv;
    698  }
    699 
    700  EventDispatcherResult dispatcherResult =
    701      MaybeDispatchKeydownForComposition(keyboardEvent, aKeyFlags);
    702  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
    703      !dispatcherResult.mCanContinue) {
    704    return dispatcherResult.mResult;
    705  }
    706 
    707  // Even if the preceding keydown event was consumed, if the composition
    708  // was already started, we shouldn't prevent the change of composition.
    709  if (dispatcherResult.mDoDefault || wasComposing) {
    710    // Preceding keydown event may cause destroying the widget.
    711    if (NS_FAILED(IsValidStateForComposition())) {
    712      return NS_OK;
    713    }
    714    nsEventStatus status = nsEventStatus_eIgnore;
    715    rv = kungFuDeathGrip->FlushPendingComposition(status);
    716    *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
    717  }
    718 
    719  MaybeDispatchKeyupForComposition(keyboardEvent, aKeyFlags);
    720 
    721  if (NS_WARN_IF(NS_FAILED(rv))) {
    722    return rv;
    723  }
    724  return NS_OK;
    725 }
    726 
    727 NS_IMETHODIMP
    728 TextInputProcessor::CommitComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
    729                                      uint8_t aOptionalArgc) {
    730  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    731 
    732  RefPtr<KeyboardEvent> keyEvent;
    733  if (aDOMKeyEvent) {
    734    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
    735    if (NS_WARN_IF(!keyEvent)) {
    736      return NS_ERROR_INVALID_ARG;
    737    }
    738  }
    739 
    740  WidgetKeyboardEvent* keyboardEvent;
    741  nsresult rv = PrepareKeyboardEventForComposition(
    742      keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent);
    743  if (NS_WARN_IF(NS_FAILED(rv))) {
    744    return rv;
    745  }
    746 
    747  return CommitCompositionInternal(keyboardEvent, aKeyFlags);
    748 }
    749 
    750 NS_IMETHODIMP
    751 TextInputProcessor::CommitCompositionWith(const nsAString& aCommitString,
    752                                          Event* aDOMKeyEvent,
    753                                          uint32_t aKeyFlags,
    754                                          uint8_t aOptionalArgc,
    755                                          bool* aSucceeded) {
    756  MOZ_RELEASE_ASSERT(aSucceeded, "aSucceeded must not be nullptr");
    757  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    758 
    759  RefPtr<KeyboardEvent> keyEvent;
    760  if (aDOMKeyEvent) {
    761    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
    762    if (NS_WARN_IF(!keyEvent)) {
    763      return NS_ERROR_INVALID_ARG;
    764    }
    765  }
    766 
    767  WidgetKeyboardEvent* keyboardEvent;
    768  nsresult rv = PrepareKeyboardEventForComposition(
    769      keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent);
    770  if (NS_WARN_IF(NS_FAILED(rv))) {
    771    return rv;
    772  }
    773 
    774  return CommitCompositionInternal(keyboardEvent, aKeyFlags, &aCommitString,
    775                                   aSucceeded);
    776 }
    777 
    778 nsresult TextInputProcessor::CommitCompositionInternal(
    779    const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags,
    780    const nsAString* aCommitString, bool* aSucceeded) {
    781  if (aSucceeded) {
    782    *aSucceeded = false;
    783  }
    784  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    785  bool wasComposing = IsComposing();
    786 
    787  EventDispatcherResult dispatcherResult =
    788      MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
    789  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
    790      !dispatcherResult.mCanContinue) {
    791    return dispatcherResult.mResult;
    792  }
    793 
    794  // Even if the preceding keydown event was consumed, if the composition
    795  // was already started, we shouldn't prevent the commit of composition.
    796  nsresult rv = NS_OK;
    797  if (dispatcherResult.mDoDefault || wasComposing) {
    798    // Preceding keydown event may cause destroying the widget.
    799    if (NS_FAILED(IsValidStateForComposition())) {
    800      return NS_OK;
    801    }
    802    nsEventStatus status = nsEventStatus_eIgnore;
    803    rv = kungFuDeathGrip->CommitComposition(status, aCommitString);
    804    if (aSucceeded) {
    805      *aSucceeded = status != nsEventStatus_eConsumeNoDefault;
    806    }
    807  }
    808 
    809  MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
    810 
    811  if (NS_WARN_IF(NS_FAILED(rv))) {
    812    return rv;
    813  }
    814  return NS_OK;
    815 }
    816 
    817 NS_IMETHODIMP
    818 TextInputProcessor::CancelComposition(Event* aDOMKeyEvent, uint32_t aKeyFlags,
    819                                      uint8_t aOptionalArgc) {
    820  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
    821 
    822  RefPtr<KeyboardEvent> keyEvent;
    823  if (aDOMKeyEvent) {
    824    keyEvent = aDOMKeyEvent->AsKeyboardEvent();
    825    if (NS_WARN_IF(!keyEvent)) {
    826      return NS_ERROR_INVALID_ARG;
    827    }
    828  }
    829 
    830  WidgetKeyboardEvent* keyboardEvent;
    831  nsresult rv = PrepareKeyboardEventForComposition(
    832      keyEvent, aKeyFlags, aOptionalArgc, keyboardEvent);
    833  if (NS_WARN_IF(NS_FAILED(rv))) {
    834    return rv;
    835  }
    836 
    837  return CancelCompositionInternal(keyboardEvent, aKeyFlags);
    838 }
    839 
    840 nsresult TextInputProcessor::CancelCompositionInternal(
    841    const WidgetKeyboardEvent* aKeyboardEvent, uint32_t aKeyFlags) {
    842  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
    843 
    844  EventDispatcherResult dispatcherResult =
    845      MaybeDispatchKeydownForComposition(aKeyboardEvent, aKeyFlags);
    846  if (NS_WARN_IF(NS_FAILED(dispatcherResult.mResult)) ||
    847      !dispatcherResult.mCanContinue) {
    848    return dispatcherResult.mResult;
    849  }
    850 
    851  nsEventStatus status = nsEventStatus_eIgnore;
    852  nsresult rv = kungFuDeathGrip->CommitComposition(status, &EmptyString());
    853 
    854  MaybeDispatchKeyupForComposition(aKeyboardEvent, aKeyFlags);
    855 
    856  if (NS_WARN_IF(NS_FAILED(rv))) {
    857    return rv;
    858  }
    859  return NS_OK;
    860 }
    861 
    862 NS_IMETHODIMP
    863 TextInputProcessor::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
    864                              const IMENotification& aNotification) {
    865  // If This is called while this is being initialized, ignore the call.
    866  // In such case, this method should return NS_ERROR_NOT_IMPLEMENTED because
    867  // we can say, TextInputProcessor doesn't implement any handlers of the
    868  // requests and notifications.
    869  if (!mDispatcher) {
    870    return NS_ERROR_NOT_IMPLEMENTED;
    871  }
    872  MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
    873             "Wrong TextEventDispatcher notifies this");
    874  NS_ASSERTION(mForTests || mCallback,
    875               "mCallback can be null only when IME is initialized for tests");
    876  if (mCallback) {
    877    RefPtr<TextInputProcessorNotification> notification;
    878    switch (aNotification.mMessage) {
    879      case REQUEST_TO_COMMIT_COMPOSITION: {
    880        NS_ASSERTION(aTextEventDispatcher->IsComposing(),
    881                     "Why is this requested without composition?");
    882        notification = new TextInputProcessorNotification("request-to-commit");
    883        break;
    884      }
    885      case REQUEST_TO_CANCEL_COMPOSITION: {
    886        NS_ASSERTION(aTextEventDispatcher->IsComposing(),
    887                     "Why is this requested without composition?");
    888        notification = new TextInputProcessorNotification("request-to-cancel");
    889        break;
    890      }
    891      case NOTIFY_IME_OF_FOCUS:
    892        notification = new TextInputProcessorNotification("notify-focus");
    893        break;
    894      case NOTIFY_IME_OF_BLUR:
    895        notification = new TextInputProcessorNotification("notify-blur");
    896        break;
    897      case NOTIFY_IME_OF_TEXT_CHANGE:
    898        notification =
    899            new TextInputProcessorNotification(aNotification.mTextChangeData);
    900        break;
    901      case NOTIFY_IME_OF_SELECTION_CHANGE:
    902        notification = new TextInputProcessorNotification(
    903            aNotification.mSelectionChangeData);
    904        break;
    905      case NOTIFY_IME_OF_POSITION_CHANGE:
    906        notification =
    907            new TextInputProcessorNotification("notify-position-change");
    908        break;
    909      default:
    910        return NS_ERROR_NOT_IMPLEMENTED;
    911    }
    912    MOZ_RELEASE_ASSERT(notification);
    913    bool result = false;
    914    nsresult rv = mCallback->OnNotify(this, notification, &result);
    915    if (NS_WARN_IF(NS_FAILED(rv))) {
    916      return rv;
    917    }
    918    return result ? NS_OK : NS_ERROR_FAILURE;
    919  }
    920 
    921  switch (aNotification.mMessage) {
    922    case REQUEST_TO_COMMIT_COMPOSITION: {
    923      NS_ASSERTION(aTextEventDispatcher->IsComposing(),
    924                   "Why is this requested without composition?");
    925      CommitCompositionInternal();
    926      return NS_OK;
    927    }
    928    case REQUEST_TO_CANCEL_COMPOSITION: {
    929      NS_ASSERTION(aTextEventDispatcher->IsComposing(),
    930                   "Why is this requested without composition?");
    931      CancelCompositionInternal();
    932      return NS_OK;
    933    }
    934    default:
    935      return NS_ERROR_NOT_IMPLEMENTED;
    936  }
    937 }
    938 
    939 NS_IMETHODIMP_(IMENotificationRequests)
    940 TextInputProcessor::GetIMENotificationRequests() {
    941  // TextInputProcessor should support all change notifications.
    942  return IMENotificationRequests(
    943      IMENotificationRequests::NOTIFY_TEXT_CHANGE |
    944      IMENotificationRequests::NOTIFY_POSITION_CHANGE);
    945 }
    946 
    947 NS_IMETHODIMP_(void)
    948 TextInputProcessor::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
    949  // If This is called while this is being initialized, ignore the call.
    950  if (!mDispatcher) {
    951    return;
    952  }
    953  MOZ_ASSERT(aTextEventDispatcher == mDispatcher,
    954             "Wrong TextEventDispatcher notifies this");
    955  UnlinkFromTextEventDispatcher();
    956 }
    957 
    958 NS_IMETHODIMP_(void)
    959 TextInputProcessor::WillDispatchKeyboardEvent(
    960    TextEventDispatcher* aTextEventDispatcher,
    961    WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
    962    void* aData) {
    963  // TextInputProcessor doesn't set alternative char code nor modify charCode
    964  // even when Ctrl key is pressed.
    965 }
    966 
    967 nsresult TextInputProcessor::PrepareKeyboardEventToDispatch(
    968    WidgetKeyboardEvent& aKeyboardEvent, uint32_t aKeyFlags) {
    969  if (NS_WARN_IF(aKeyboardEvent.mCodeNameIndex == CODE_NAME_INDEX_USE_STRING)) {
    970    return NS_ERROR_INVALID_ARG;
    971  }
    972  if ((aKeyFlags & KEY_NON_PRINTABLE_KEY) &&
    973      NS_WARN_IF(aKeyboardEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING)) {
    974    return NS_ERROR_INVALID_ARG;
    975  }
    976  if ((aKeyFlags & KEY_FORCE_PRINTABLE_KEY) &&
    977      aKeyboardEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING) {
    978    aKeyboardEvent.GetDOMKeyName(aKeyboardEvent.mKeyValue);
    979    aKeyboardEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
    980  }
    981  if (aKeyFlags & KEY_KEEP_KEY_LOCATION_STANDARD) {
    982    // If .location is initialized with specific value, using
    983    // KEY_KEEP_KEY_LOCATION_STANDARD must be a bug of the caller.
    984    // Let's throw an exception for notifying the developer of this bug.
    985    if (NS_WARN_IF(aKeyboardEvent.mLocation)) {
    986      return NS_ERROR_INVALID_ARG;
    987    }
    988  } else if (!aKeyboardEvent.mLocation) {
    989    // If KeyboardEvent.mLocation is 0, it may be uninitialized.  If so, we
    990    // should compute proper mLocation value from its .code value.
    991    aKeyboardEvent.mLocation =
    992        WidgetKeyboardEvent::ComputeLocationFromCodeValue(
    993            aKeyboardEvent.mCodeNameIndex);
    994  }
    995 
    996  if (aKeyFlags & KEY_KEEP_KEYCODE_ZERO) {
    997    // If .keyCode is initialized with specific value, using
    998    // KEY_KEEP_KEYCODE_ZERO must be a bug of the caller.  Let's throw an
    999    // exception for notifying the developer of such bug.
   1000    if (NS_WARN_IF(aKeyboardEvent.mKeyCode)) {
   1001      return NS_ERROR_INVALID_ARG;
   1002    }
   1003  } else if (!aKeyboardEvent.mKeyCode &&
   1004             aKeyboardEvent.mKeyNameIndex > KEY_NAME_INDEX_Unidentified &&
   1005             aKeyboardEvent.mKeyNameIndex < KEY_NAME_INDEX_USE_STRING) {
   1006    // If KeyboardEvent.keyCode is 0, it may be uninitialized.  If so, we may
   1007    // be able to decide a good .keyCode value if the .key value is a
   1008    // non-printable key.
   1009    aKeyboardEvent.mKeyCode =
   1010        WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(
   1011            aKeyboardEvent.mKeyNameIndex);
   1012  }
   1013 
   1014  aKeyboardEvent.mIsSynthesizedByTIP = true;
   1015  aKeyboardEvent.mFlags.mIsSynthesizedForTests = mForTests;
   1016 
   1017  return NS_OK;
   1018 }
   1019 
   1020 nsresult TextInputProcessor::InitEditCommands(
   1021    WidgetKeyboardEvent& aKeyboardEvent) const {
   1022  MOZ_ASSERT(XRE_IsContentProcess());
   1023  MOZ_ASSERT(aKeyboardEvent.mMessage == eKeyPress);
   1024 
   1025  // When this emulates real input only in content process, we need to
   1026  // initialize edit commands with the main process's widget via PuppetWidget
   1027  // because they are initialized by BrowserParent before content process treats
   1028  // them.
   1029  // And also when this synthesizes keyboard events for tests, we need default
   1030  // shortcut keys on the platform for making any developers get constant
   1031  // results in any environments.
   1032 
   1033  // Note that retrieving edit commands via PuppetWidget is expensive.
   1034  // Let's skip it when the keyboard event is inputting text.
   1035  if (aKeyboardEvent.IsInputtingText()) {
   1036    aKeyboardEvent.PreventNativeKeyBindings();
   1037    return NS_OK;
   1038  }
   1039 
   1040  Maybe<WritingMode> writingMode;
   1041  if (RefPtr<TextEventDispatcher> dispatcher = mDispatcher) {
   1042    writingMode = dispatcher->MaybeQueryWritingModeAtSelection();
   1043  }
   1044 
   1045  // FYI: WidgetKeyboardEvent::InitAllEditCommands() isn't available here
   1046  //      since it checks whether it's called in the main process to
   1047  //      avoid performance issues so that we need to initialize each
   1048  //      command manually here.
   1049  if (NS_WARN_IF(!aKeyboardEvent.InitEditCommandsFor(
   1050          NativeKeyBindingsType::SingleLineEditor, writingMode))) {
   1051    return NS_ERROR_NOT_AVAILABLE;
   1052  }
   1053  if (NS_WARN_IF(!aKeyboardEvent.InitEditCommandsFor(
   1054          NativeKeyBindingsType::MultiLineEditor, writingMode))) {
   1055    return NS_ERROR_NOT_AVAILABLE;
   1056  }
   1057  if (NS_WARN_IF(!aKeyboardEvent.InitEditCommandsFor(
   1058          NativeKeyBindingsType::RichTextEditor, writingMode))) {
   1059    return NS_ERROR_NOT_AVAILABLE;
   1060  }
   1061 
   1062  return NS_OK;
   1063 }
   1064 
   1065 NS_IMETHODIMP
   1066 TextInputProcessor::Keydown(Event* aDOMKeyEvent, uint32_t aKeyFlags,
   1067                            uint8_t aOptionalArgc, uint32_t* aConsumedFlags) {
   1068  MOZ_RELEASE_ASSERT(aConsumedFlags, "aConsumedFlags must not be nullptr");
   1069  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
   1070  if (!aOptionalArgc) {
   1071    aKeyFlags = 0;
   1072  }
   1073  if (NS_WARN_IF(!aDOMKeyEvent)) {
   1074    return NS_ERROR_INVALID_ARG;
   1075  }
   1076  WidgetKeyboardEvent* originalKeyEvent =
   1077      aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
   1078  if (NS_WARN_IF(!originalKeyEvent)) {
   1079    return NS_ERROR_INVALID_ARG;
   1080  }
   1081  return KeydownInternal(*originalKeyEvent, aKeyFlags, true, *aConsumedFlags);
   1082 }
   1083 
   1084 nsresult TextInputProcessor::Keydown(const WidgetKeyboardEvent& aKeyboardEvent,
   1085                                     uint32_t aKeyFlags,
   1086                                     uint32_t* aConsumedFlags) {
   1087  uint32_t consumedFlags = 0;
   1088  return KeydownInternal(aKeyboardEvent, aKeyFlags, true,
   1089                         aConsumedFlags ? *aConsumedFlags : consumedFlags);
   1090 }
   1091 
   1092 nsresult TextInputProcessor::KeydownInternal(
   1093    const WidgetKeyboardEvent& aKeyboardEvent, uint32_t aKeyFlags,
   1094    bool aAllowToDispatchKeypress, uint32_t& aConsumedFlags) {
   1095  aConsumedFlags = KEYEVENT_NOT_CONSUMED;
   1096 
   1097  // We shouldn't modify the internal WidgetKeyboardEvent.
   1098  WidgetKeyboardEvent keyEvent(aKeyboardEvent);
   1099  keyEvent.mFlags.mIsTrusted = true;
   1100  keyEvent.mMessage = eKeyDown;
   1101  nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
   1102  if (NS_WARN_IF(NS_FAILED(rv))) {
   1103    return rv;
   1104  }
   1105 
   1106  aConsumedFlags = (aKeyFlags & KEY_DEFAULT_PREVENTED) ? KEYDOWN_IS_CONSUMED
   1107                                                       : KEYEVENT_NOT_CONSUMED;
   1108 
   1109  if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
   1110    ModifierKeyData modifierKeyData(keyEvent);
   1111    if (WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
   1112      // If the modifier key is lockable modifier key such as CapsLock,
   1113      // let's toggle modifier key state at keydown.
   1114      ToggleModifierKey(modifierKeyData);
   1115    } else {
   1116      // Activate modifier flag before dispatching keydown event (i.e., keydown
   1117      // event should indicate the releasing modifier is active.
   1118      ActivateModifierKey(modifierKeyData);
   1119    }
   1120    if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
   1121      return NS_OK;
   1122    }
   1123  } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
   1124    return NS_ERROR_INVALID_ARG;
   1125  }
   1126  keyEvent.mModifiers = GetActiveModifiers();
   1127 
   1128  if (!aAllowToDispatchKeypress &&
   1129      !(aKeyFlags & KEY_DONT_MARK_KEYDOWN_AS_PROCESSED)) {
   1130    keyEvent.mKeyCode = NS_VK_PROCESSKEY;
   1131    keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
   1132  }
   1133 
   1134  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
   1135  rv = IsValidStateForComposition();
   1136  if (NS_WARN_IF(NS_FAILED(rv))) {
   1137    return rv;
   1138  }
   1139 
   1140  nsEventStatus status =
   1141      aConsumedFlags ? nsEventStatus_eConsumeNoDefault : nsEventStatus_eIgnore;
   1142  if (!kungFuDeathGrip->DispatchKeyboardEvent(eKeyDown, keyEvent, status)) {
   1143    // If keydown event isn't dispatched, we don't need to dispatch keypress
   1144    // events.
   1145    return NS_OK;
   1146  }
   1147 
   1148  aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault)
   1149                        ? KEYDOWN_IS_CONSUMED
   1150                        : KEYEVENT_NOT_CONSUMED;
   1151 
   1152  if (!aAllowToDispatchKeypress) {
   1153    return NS_OK;
   1154  }
   1155 
   1156  keyEvent.mMessage = eKeyPress;
   1157 
   1158  // Only `eKeyPress` events, editor wants to execute system default edit
   1159  // commands mapped to the key combination.  In e10s world, edit commands can
   1160  // be retrieved only in the parent process due to the performance reason.
   1161  // Therefore, BrowserParent initializes edit commands for all cases before
   1162  // sending the event to focused content process.  For emulating this, we
   1163  // need to do it now for synthesizing `eKeyPress` events if and only if
   1164  // we're dispatching the events in a content process.
   1165  if (XRE_IsContentProcess()) {
   1166    nsresult rv = InitEditCommands(keyEvent);
   1167    if (NS_WARN_IF(NS_FAILED(rv))) {
   1168      return rv;
   1169    }
   1170  }
   1171  if (kungFuDeathGrip->MaybeDispatchKeypressEvents(keyEvent, status)) {
   1172    aConsumedFlags |= (status == nsEventStatus_eConsumeNoDefault)
   1173                          ? KEYPRESS_IS_CONSUMED
   1174                          : KEYEVENT_NOT_CONSUMED;
   1175  }
   1176 
   1177  return NS_OK;
   1178 }
   1179 
   1180 NS_IMETHODIMP
   1181 TextInputProcessor::Keyup(Event* aDOMKeyEvent, uint32_t aKeyFlags,
   1182                          uint8_t aOptionalArgc, bool* aDoDefault) {
   1183  MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
   1184  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
   1185  if (!aOptionalArgc) {
   1186    aKeyFlags = 0;
   1187  }
   1188  if (NS_WARN_IF(!aDOMKeyEvent)) {
   1189    return NS_ERROR_INVALID_ARG;
   1190  }
   1191  WidgetKeyboardEvent* originalKeyEvent =
   1192      aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent();
   1193  if (NS_WARN_IF(!originalKeyEvent)) {
   1194    return NS_ERROR_INVALID_ARG;
   1195  }
   1196  return KeyupInternal(*originalKeyEvent, aKeyFlags, *aDoDefault);
   1197 }
   1198 
   1199 nsresult TextInputProcessor::Keyup(const WidgetKeyboardEvent& aKeyboardEvent,
   1200                                   uint32_t aKeyFlags, bool* aDoDefault) {
   1201  bool doDefault = false;
   1202  return KeyupInternal(aKeyboardEvent, aKeyFlags,
   1203                       aDoDefault ? *aDoDefault : doDefault);
   1204 }
   1205 
   1206 nsresult TextInputProcessor::KeyupInternal(
   1207    const WidgetKeyboardEvent& aKeyboardEvent, uint32_t aKeyFlags,
   1208    bool& aDoDefault) {
   1209  aDoDefault = false;
   1210 
   1211  // We shouldn't modify the internal WidgetKeyboardEvent.
   1212  WidgetKeyboardEvent keyEvent(aKeyboardEvent);
   1213  keyEvent.mFlags.mIsTrusted = true;
   1214  keyEvent.mMessage = eKeyUp;
   1215  nsresult rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
   1216  if (NS_WARN_IF(NS_FAILED(rv))) {
   1217    return rv;
   1218  }
   1219 
   1220  aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
   1221 
   1222  if (WidgetKeyboardEvent::GetModifierForKeyName(keyEvent.mKeyNameIndex)) {
   1223    if (!WidgetKeyboardEvent::IsLockableModifier(keyEvent.mKeyNameIndex)) {
   1224      // Inactivate modifier flag before dispatching keyup event (i.e., keyup
   1225      // event shouldn't indicate the releasing modifier is active.
   1226      InactivateModifierKey(ModifierKeyData(keyEvent));
   1227    }
   1228    if (aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT) {
   1229      return NS_OK;
   1230    }
   1231  } else if (NS_WARN_IF(aKeyFlags & KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT)) {
   1232    return NS_ERROR_INVALID_ARG;
   1233  }
   1234  keyEvent.mModifiers = GetActiveModifiers();
   1235 
   1236  if (aKeyFlags & KEY_MARK_KEYUP_AS_PROCESSED) {
   1237    keyEvent.mKeyCode = NS_VK_PROCESSKEY;
   1238    keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
   1239  }
   1240 
   1241  RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
   1242  rv = IsValidStateForComposition();
   1243  if (NS_WARN_IF(NS_FAILED(rv))) {
   1244    return rv;
   1245  }
   1246 
   1247  nsEventStatus status =
   1248      aDoDefault ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault;
   1249  kungFuDeathGrip->DispatchKeyboardEvent(eKeyUp, keyEvent, status);
   1250  aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   1251  return NS_OK;
   1252 }
   1253 
   1254 NS_IMETHODIMP
   1255 TextInputProcessor::InsertTextWithKeyPress(const nsAString& aString,
   1256                                           Event* aDOMKeyEvent,
   1257                                           uint32_t aKeyFlags,
   1258                                           uint8_t aOptionalArgc,
   1259                                           bool* aDoDefault) {
   1260  MOZ_RELEASE_ASSERT(aDoDefault, "aDoDefault must not be nullptr");
   1261  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
   1262 
   1263  nsresult rv = IsValidStateForComposition();
   1264  if (NS_WARN_IF(NS_FAILED(rv))) {
   1265    return rv;
   1266  }
   1267 
   1268  if (aOptionalArgc < 1) {
   1269    aDOMKeyEvent = nullptr;
   1270  }
   1271  if (aOptionalArgc < 2) {
   1272    aKeyFlags = 0;
   1273  }
   1274  *aDoDefault = !(aKeyFlags & KEY_DEFAULT_PREVENTED);
   1275 
   1276  WidgetKeyboardEvent* const originalKeyEvent =
   1277      aDOMKeyEvent ? aDOMKeyEvent->WidgetEventPtr()->AsKeyboardEvent()
   1278                   : nullptr;
   1279  if (NS_WARN_IF(aDOMKeyEvent && !originalKeyEvent)) {
   1280    return NS_ERROR_INVALID_ARG;
   1281  }
   1282 
   1283  WidgetKeyboardEvent keyEvent(true, eKeyPress, nullptr);
   1284  if (originalKeyEvent) {
   1285    keyEvent = WidgetKeyboardEvent(*originalKeyEvent);
   1286    keyEvent.mFlags.mIsTrusted = true;
   1287    keyEvent.mMessage = eKeyPress;
   1288  }
   1289  keyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   1290  keyEvent.mKeyValue = aString;
   1291  rv = PrepareKeyboardEventToDispatch(keyEvent, aKeyFlags);
   1292  if (NS_WARN_IF(NS_FAILED(rv))) {
   1293    return rv;
   1294  }
   1295  // Do not dispatch modifier key events even if the source event is a modifier
   1296  // key event because modifier state should be changed before this.
   1297  // TODO: In some test scenarios, we may need a new flag to use the given
   1298  // modifier state as-is.
   1299  keyEvent.mModifiers = GetActiveModifiers();
   1300 
   1301  // See KeyDownInternal() for the detail of this.
   1302  if (XRE_IsContentProcess()) {
   1303    nsresult rv = InitEditCommands(keyEvent);
   1304    if (NS_WARN_IF(NS_FAILED(rv))) {
   1305      return rv;
   1306    }
   1307  }
   1308 
   1309  nsEventStatus status =
   1310      *aDoDefault ? nsEventStatus_eIgnore : nsEventStatus_eConsumeNoDefault;
   1311  RefPtr<TextEventDispatcher> dispatcher(mDispatcher);
   1312  if (dispatcher->MaybeDispatchKeypressEvents(keyEvent, status)) {
   1313    *aDoDefault = (status != nsEventStatus_eConsumeNoDefault);
   1314  }
   1315 
   1316  return NS_OK;
   1317 }
   1318 
   1319 NS_IMETHODIMP
   1320 TextInputProcessor::GetModifierState(const nsAString& aModifierKeyName,
   1321                                     bool* aActive) {
   1322  MOZ_RELEASE_ASSERT(aActive, "aActive must not be null");
   1323  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
   1324  Modifiers modifier = WidgetInputEvent::GetModifier(aModifierKeyName);
   1325  *aActive = ((GetActiveModifiers() & modifier) != 0);
   1326  return NS_OK;
   1327 }
   1328 
   1329 NS_IMETHODIMP
   1330 TextInputProcessor::ShareModifierStateOf(nsITextInputProcessor* aOther) {
   1331  MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
   1332  if (!aOther) {
   1333    mModifierKeyDataArray = nullptr;
   1334    return NS_OK;
   1335  }
   1336  TextInputProcessor* other = static_cast<TextInputProcessor*>(aOther);
   1337  if (!other->mModifierKeyDataArray) {
   1338    other->mModifierKeyDataArray = new ModifierKeyDataArray();
   1339  }
   1340  mModifierKeyDataArray = other->mModifierKeyDataArray;
   1341  return NS_OK;
   1342 }
   1343 
   1344 NS_IMETHODIMP
   1345 TextInputProcessor::ComputeCodeValueOfNonPrintableKey(
   1346    const nsAString& aKeyValue, JS::Handle<JS::Value> aLocation,
   1347    uint8_t aOptionalArgc, nsAString& aCodeValue) {
   1348  aCodeValue.Truncate();
   1349 
   1350  Maybe<uint32_t> location;
   1351  if (aOptionalArgc) {
   1352    if (aLocation.isNullOrUndefined()) {
   1353      // location should be nothing.
   1354    } else if (aLocation.isInt32()) {
   1355      location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32()));
   1356    } else {
   1357      NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
   1358                           "aLocation must be undefined, null or int");
   1359      return NS_ERROR_INVALID_ARG;
   1360    }
   1361  }
   1362 
   1363  KeyNameIndex keyNameIndex = WidgetKeyboardEvent::GetKeyNameIndex(aKeyValue);
   1364  if (keyNameIndex == KEY_NAME_INDEX_Unidentified ||
   1365      keyNameIndex == KEY_NAME_INDEX_USE_STRING) {
   1366    return NS_OK;
   1367  }
   1368 
   1369  CodeNameIndex codeNameIndex =
   1370      WidgetKeyboardEvent::ComputeCodeNameIndexFromKeyNameIndex(keyNameIndex,
   1371                                                                location);
   1372  if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
   1373    return NS_OK;
   1374  }
   1375  MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
   1376  WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
   1377  return NS_OK;
   1378 }
   1379 
   1380 NS_IMETHODIMP
   1381 TextInputProcessor::GuessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
   1382    const nsAString& aKeyValue, JS::Handle<JS::Value> aLocation,
   1383    uint8_t aOptionalArgc, nsAString& aCodeValue) {
   1384  aCodeValue.Truncate();
   1385 
   1386  Maybe<uint32_t> location;
   1387  if (aOptionalArgc) {
   1388    if (aLocation.isNullOrUndefined()) {
   1389      // location should be nothing.
   1390    } else if (aLocation.isInt32()) {
   1391      location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32()));
   1392    } else {
   1393      NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
   1394                           "aLocation must be undefined, null or int");
   1395      return NS_ERROR_INVALID_ARG;
   1396    }
   1397  }
   1398  CodeNameIndex codeNameIndex =
   1399      GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(aKeyValue, location);
   1400  if (codeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
   1401    return NS_OK;
   1402  }
   1403  MOZ_ASSERT(codeNameIndex != CODE_NAME_INDEX_USE_STRING);
   1404  WidgetKeyboardEvent::GetDOMCodeName(codeNameIndex, aCodeValue);
   1405  return NS_OK;
   1406 }
   1407 
   1408 // static
   1409 CodeNameIndex
   1410 TextInputProcessor::GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
   1411    const nsAString& aKeyValue, const Maybe<uint32_t>& aLocation) {
   1412  if (aKeyValue.IsEmpty()) {
   1413    return CODE_NAME_INDEX_UNKNOWN;
   1414  }
   1415  // US keyboard layout can input only one character per key.  So, we can
   1416  // assume that if the key value is 2 or more characters, it's a known
   1417  // key name or not a usual key emulation.
   1418  if (aKeyValue.Length() > 1) {
   1419    return CODE_NAME_INDEX_UNKNOWN;
   1420  }
   1421  if (aLocation.isSome() &&
   1422      aLocation.value() ==
   1423          dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
   1424    switch (aKeyValue[0]) {
   1425      case '+':
   1426        return CODE_NAME_INDEX_NumpadAdd;
   1427      case '-':
   1428        return CODE_NAME_INDEX_NumpadSubtract;
   1429      case '*':
   1430        return CODE_NAME_INDEX_NumpadMultiply;
   1431      case '/':
   1432        return CODE_NAME_INDEX_NumpadDivide;
   1433      case '.':
   1434        return CODE_NAME_INDEX_NumpadDecimal;
   1435      case '0':
   1436        return CODE_NAME_INDEX_Numpad0;
   1437      case '1':
   1438        return CODE_NAME_INDEX_Numpad1;
   1439      case '2':
   1440        return CODE_NAME_INDEX_Numpad2;
   1441      case '3':
   1442        return CODE_NAME_INDEX_Numpad3;
   1443      case '4':
   1444        return CODE_NAME_INDEX_Numpad4;
   1445      case '5':
   1446        return CODE_NAME_INDEX_Numpad5;
   1447      case '6':
   1448        return CODE_NAME_INDEX_Numpad6;
   1449      case '7':
   1450        return CODE_NAME_INDEX_Numpad7;
   1451      case '8':
   1452        return CODE_NAME_INDEX_Numpad8;
   1453      case '9':
   1454        return CODE_NAME_INDEX_Numpad9;
   1455      default:
   1456        return CODE_NAME_INDEX_UNKNOWN;
   1457    }
   1458  }
   1459 
   1460  if (aLocation.isSome() &&
   1461      aLocation.value() !=
   1462          dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
   1463    return CODE_NAME_INDEX_UNKNOWN;
   1464  }
   1465 
   1466  // TODO: Support characters inputted with option key on macOS.
   1467  switch (aKeyValue[0]) {
   1468    case 'a':
   1469    case 'A':
   1470      return CODE_NAME_INDEX_KeyA;
   1471    case 'b':
   1472    case 'B':
   1473      return CODE_NAME_INDEX_KeyB;
   1474    case 'c':
   1475    case 'C':
   1476      return CODE_NAME_INDEX_KeyC;
   1477    case 'd':
   1478    case 'D':
   1479      return CODE_NAME_INDEX_KeyD;
   1480    case 'e':
   1481    case 'E':
   1482      return CODE_NAME_INDEX_KeyE;
   1483    case 'f':
   1484    case 'F':
   1485      return CODE_NAME_INDEX_KeyF;
   1486    case 'g':
   1487    case 'G':
   1488      return CODE_NAME_INDEX_KeyG;
   1489    case 'h':
   1490    case 'H':
   1491      return CODE_NAME_INDEX_KeyH;
   1492    case 'i':
   1493    case 'I':
   1494      return CODE_NAME_INDEX_KeyI;
   1495    case 'j':
   1496    case 'J':
   1497      return CODE_NAME_INDEX_KeyJ;
   1498    case 'k':
   1499    case 'K':
   1500      return CODE_NAME_INDEX_KeyK;
   1501    case 'l':
   1502    case 'L':
   1503      return CODE_NAME_INDEX_KeyL;
   1504    case 'm':
   1505    case 'M':
   1506      return CODE_NAME_INDEX_KeyM;
   1507    case 'n':
   1508    case 'N':
   1509      return CODE_NAME_INDEX_KeyN;
   1510    case 'o':
   1511    case 'O':
   1512      return CODE_NAME_INDEX_KeyO;
   1513    case 'p':
   1514    case 'P':
   1515      return CODE_NAME_INDEX_KeyP;
   1516    case 'q':
   1517    case 'Q':
   1518      return CODE_NAME_INDEX_KeyQ;
   1519    case 'r':
   1520    case 'R':
   1521      return CODE_NAME_INDEX_KeyR;
   1522    case 's':
   1523    case 'S':
   1524      return CODE_NAME_INDEX_KeyS;
   1525    case 't':
   1526    case 'T':
   1527      return CODE_NAME_INDEX_KeyT;
   1528    case 'u':
   1529    case 'U':
   1530      return CODE_NAME_INDEX_KeyU;
   1531    case 'v':
   1532    case 'V':
   1533      return CODE_NAME_INDEX_KeyV;
   1534    case 'w':
   1535    case 'W':
   1536      return CODE_NAME_INDEX_KeyW;
   1537    case 'x':
   1538    case 'X':
   1539      return CODE_NAME_INDEX_KeyX;
   1540    case 'y':
   1541    case 'Y':
   1542      return CODE_NAME_INDEX_KeyY;
   1543    case 'z':
   1544    case 'Z':
   1545      return CODE_NAME_INDEX_KeyZ;
   1546 
   1547    case '`':
   1548    case '~':
   1549      return CODE_NAME_INDEX_Backquote;
   1550    case '1':
   1551    case '!':
   1552      return CODE_NAME_INDEX_Digit1;
   1553    case '2':
   1554    case '@':
   1555      return CODE_NAME_INDEX_Digit2;
   1556    case '3':
   1557    case '#':
   1558      return CODE_NAME_INDEX_Digit3;
   1559    case '4':
   1560    case '$':
   1561      return CODE_NAME_INDEX_Digit4;
   1562    case '5':
   1563    case '%':
   1564      return CODE_NAME_INDEX_Digit5;
   1565    case '6':
   1566    case '^':
   1567      return CODE_NAME_INDEX_Digit6;
   1568    case '7':
   1569    case '&':
   1570      return CODE_NAME_INDEX_Digit7;
   1571    case '8':
   1572    case '*':
   1573      return CODE_NAME_INDEX_Digit8;
   1574    case '9':
   1575    case '(':
   1576      return CODE_NAME_INDEX_Digit9;
   1577    case '0':
   1578    case ')':
   1579      return CODE_NAME_INDEX_Digit0;
   1580    case '-':
   1581    case '_':
   1582      return CODE_NAME_INDEX_Minus;
   1583    case '=':
   1584    case '+':
   1585      return CODE_NAME_INDEX_Equal;
   1586 
   1587    case '[':
   1588    case '{':
   1589      return CODE_NAME_INDEX_BracketLeft;
   1590    case ']':
   1591    case '}':
   1592      return CODE_NAME_INDEX_BracketRight;
   1593    case '\\':
   1594    case '|':
   1595      return CODE_NAME_INDEX_Backslash;
   1596 
   1597    case ';':
   1598    case ':':
   1599      return CODE_NAME_INDEX_Semicolon;
   1600    case '\'':
   1601    case '"':
   1602      return CODE_NAME_INDEX_Quote;
   1603 
   1604    case ',':
   1605    case '<':
   1606      return CODE_NAME_INDEX_Comma;
   1607    case '.':
   1608    case '>':
   1609      return CODE_NAME_INDEX_Period;
   1610    case '/':
   1611    case '?':
   1612      return CODE_NAME_INDEX_Slash;
   1613 
   1614    case ' ':
   1615      return CODE_NAME_INDEX_Space;
   1616 
   1617    default:
   1618      return CODE_NAME_INDEX_UNKNOWN;
   1619  }
   1620 }
   1621 
   1622 NS_IMETHODIMP
   1623 TextInputProcessor::GuessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
   1624    const nsAString& aKeyValue, JS::Handle<JS::Value> aLocation,
   1625    uint8_t aOptionalArgc, uint32_t* aKeyCodeValue) {
   1626  if (NS_WARN_IF(!aKeyCodeValue)) {
   1627    return NS_ERROR_INVALID_ARG;
   1628  }
   1629 
   1630  Maybe<uint32_t> location;
   1631  if (aOptionalArgc) {
   1632    if (aLocation.isNullOrUndefined()) {
   1633      // location should be nothing.
   1634    } else if (aLocation.isInt32()) {
   1635      location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32()));
   1636    } else {
   1637      NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
   1638                           "aLocation must be undefined, null or int");
   1639      return NS_ERROR_INVALID_ARG;
   1640    }
   1641  }
   1642 
   1643  *aKeyCodeValue =
   1644      GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue, location);
   1645  return NS_OK;
   1646 }
   1647 
   1648 // static
   1649 uint32_t TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
   1650    const nsAString& aKeyValue, const Maybe<uint32_t>& aLocation) {
   1651  if (aKeyValue.IsEmpty()) {
   1652    return 0;
   1653  }
   1654  // US keyboard layout can input only one character per key.  So, we can
   1655  // assume that if the key value is 2 or more characters, it's a known
   1656  // key name of a non-printable key or not a usual key emulation.
   1657  if (aKeyValue.Length() > 1) {
   1658    return 0;
   1659  }
   1660 
   1661  if (aLocation.isSome() &&
   1662      aLocation.value() ==
   1663          dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
   1664    switch (aKeyValue[0]) {
   1665      case '+':
   1666        return dom::KeyboardEvent_Binding::DOM_VK_ADD;
   1667      case '-':
   1668        return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT;
   1669      case '*':
   1670        return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY;
   1671      case '/':
   1672        return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE;
   1673      case '.':
   1674        return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL;
   1675      case '0':
   1676        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0;
   1677      case '1':
   1678        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1;
   1679      case '2':
   1680        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2;
   1681      case '3':
   1682        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3;
   1683      case '4':
   1684        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4;
   1685      case '5':
   1686        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5;
   1687      case '6':
   1688        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6;
   1689      case '7':
   1690        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7;
   1691      case '8':
   1692        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8;
   1693      case '9':
   1694        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9;
   1695      default:
   1696        return 0;
   1697    }
   1698  }
   1699 
   1700  if (aLocation.isSome() &&
   1701      aLocation.value() !=
   1702          dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
   1703    return 0;
   1704  }
   1705 
   1706  // TODO: Support characters inputted with option key on macOS.
   1707  switch (aKeyValue[0]) {
   1708    case 'a':
   1709    case 'A':
   1710      return dom::KeyboardEvent_Binding::DOM_VK_A;
   1711    case 'b':
   1712    case 'B':
   1713      return dom::KeyboardEvent_Binding::DOM_VK_B;
   1714    case 'c':
   1715    case 'C':
   1716      return dom::KeyboardEvent_Binding::DOM_VK_C;
   1717    case 'd':
   1718    case 'D':
   1719      return dom::KeyboardEvent_Binding::DOM_VK_D;
   1720    case 'e':
   1721    case 'E':
   1722      return dom::KeyboardEvent_Binding::DOM_VK_E;
   1723    case 'f':
   1724    case 'F':
   1725      return dom::KeyboardEvent_Binding::DOM_VK_F;
   1726    case 'g':
   1727    case 'G':
   1728      return dom::KeyboardEvent_Binding::DOM_VK_G;
   1729    case 'h':
   1730    case 'H':
   1731      return dom::KeyboardEvent_Binding::DOM_VK_H;
   1732    case 'i':
   1733    case 'I':
   1734      return dom::KeyboardEvent_Binding::DOM_VK_I;
   1735    case 'j':
   1736    case 'J':
   1737      return dom::KeyboardEvent_Binding::DOM_VK_J;
   1738    case 'k':
   1739    case 'K':
   1740      return dom::KeyboardEvent_Binding::DOM_VK_K;
   1741    case 'l':
   1742    case 'L':
   1743      return dom::KeyboardEvent_Binding::DOM_VK_L;
   1744    case 'm':
   1745    case 'M':
   1746      return dom::KeyboardEvent_Binding::DOM_VK_M;
   1747    case 'n':
   1748    case 'N':
   1749      return dom::KeyboardEvent_Binding::DOM_VK_N;
   1750    case 'o':
   1751    case 'O':
   1752      return dom::KeyboardEvent_Binding::DOM_VK_O;
   1753    case 'p':
   1754    case 'P':
   1755      return dom::KeyboardEvent_Binding::DOM_VK_P;
   1756    case 'q':
   1757    case 'Q':
   1758      return dom::KeyboardEvent_Binding::DOM_VK_Q;
   1759    case 'r':
   1760    case 'R':
   1761      return dom::KeyboardEvent_Binding::DOM_VK_R;
   1762    case 's':
   1763    case 'S':
   1764      return dom::KeyboardEvent_Binding::DOM_VK_S;
   1765    case 't':
   1766    case 'T':
   1767      return dom::KeyboardEvent_Binding::DOM_VK_T;
   1768    case 'u':
   1769    case 'U':
   1770      return dom::KeyboardEvent_Binding::DOM_VK_U;
   1771    case 'v':
   1772    case 'V':
   1773      return dom::KeyboardEvent_Binding::DOM_VK_V;
   1774    case 'w':
   1775    case 'W':
   1776      return dom::KeyboardEvent_Binding::DOM_VK_W;
   1777    case 'x':
   1778    case 'X':
   1779      return dom::KeyboardEvent_Binding::DOM_VK_X;
   1780    case 'y':
   1781    case 'Y':
   1782      return dom::KeyboardEvent_Binding::DOM_VK_Y;
   1783    case 'z':
   1784    case 'Z':
   1785      return dom::KeyboardEvent_Binding::DOM_VK_Z;
   1786 
   1787    case '`':
   1788    case '~':
   1789      return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE;
   1790    case '1':
   1791    case '!':
   1792      return dom::KeyboardEvent_Binding::DOM_VK_1;
   1793    case '2':
   1794    case '@':
   1795      return dom::KeyboardEvent_Binding::DOM_VK_2;
   1796    case '3':
   1797    case '#':
   1798      return dom::KeyboardEvent_Binding::DOM_VK_3;
   1799    case '4':
   1800    case '$':
   1801      return dom::KeyboardEvent_Binding::DOM_VK_4;
   1802    case '5':
   1803    case '%':
   1804      return dom::KeyboardEvent_Binding::DOM_VK_5;
   1805    case '6':
   1806    case '^':
   1807      return dom::KeyboardEvent_Binding::DOM_VK_6;
   1808    case '7':
   1809    case '&':
   1810      return dom::KeyboardEvent_Binding::DOM_VK_7;
   1811    case '8':
   1812    case '*':
   1813      return dom::KeyboardEvent_Binding::DOM_VK_8;
   1814    case '9':
   1815    case '(':
   1816      return dom::KeyboardEvent_Binding::DOM_VK_9;
   1817    case '0':
   1818    case ')':
   1819      return dom::KeyboardEvent_Binding::DOM_VK_0;
   1820    case '-':
   1821    case '_':
   1822      return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS;
   1823    case '=':
   1824    case '+':
   1825      return dom::KeyboardEvent_Binding::DOM_VK_EQUALS;
   1826 
   1827    case '[':
   1828    case '{':
   1829      return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET;
   1830    case ']':
   1831    case '}':
   1832      return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET;
   1833    case '\\':
   1834    case '|':
   1835      return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH;
   1836 
   1837    case ';':
   1838    case ':':
   1839      return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON;
   1840    case '\'':
   1841    case '"':
   1842      return dom::KeyboardEvent_Binding::DOM_VK_QUOTE;
   1843 
   1844    case ',':
   1845    case '<':
   1846      return dom::KeyboardEvent_Binding::DOM_VK_COMMA;
   1847    case '.':
   1848    case '>':
   1849      return dom::KeyboardEvent_Binding::DOM_VK_PERIOD;
   1850    case '/':
   1851    case '?':
   1852      return dom::KeyboardEvent_Binding::DOM_VK_SLASH;
   1853 
   1854    case ' ':
   1855      return dom::KeyboardEvent_Binding::DOM_VK_SPACE;
   1856 
   1857    default:
   1858      return 0;
   1859  }
   1860 }
   1861 
   1862 /******************************************************************************
   1863 * TextInputProcessor::AutoPendingCompositionResetter
   1864 ******************************************************************************/
   1865 TextInputProcessor::AutoPendingCompositionResetter::
   1866    AutoPendingCompositionResetter(TextInputProcessor* aTIP)
   1867    : mTIP(aTIP) {
   1868  MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null");
   1869 }
   1870 
   1871 TextInputProcessor::AutoPendingCompositionResetter::
   1872    ~AutoPendingCompositionResetter() {
   1873  if (mTIP->mDispatcher) {
   1874    mTIP->mDispatcher->ClearPendingComposition();
   1875  }
   1876 }
   1877 
   1878 /******************************************************************************
   1879 * TextInputProcessor::ModifierKeyData
   1880 ******************************************************************************/
   1881 TextInputProcessor::ModifierKeyData::ModifierKeyData(
   1882    const WidgetKeyboardEvent& aKeyboardEvent)
   1883    : mKeyNameIndex(aKeyboardEvent.mKeyNameIndex),
   1884      mCodeNameIndex(aKeyboardEvent.mCodeNameIndex) {
   1885  mModifier = WidgetKeyboardEvent::GetModifierForKeyName(mKeyNameIndex);
   1886  MOZ_ASSERT(mModifier, "mKeyNameIndex must be a modifier key name");
   1887 }
   1888 
   1889 /******************************************************************************
   1890 * TextInputProcessor::ModifierKeyDataArray
   1891 ******************************************************************************/
   1892 Modifiers TextInputProcessor::ModifierKeyDataArray::GetActiveModifiers() const {
   1893  Modifiers result = MODIFIER_NONE;
   1894  for (uint32_t i = 0; i < Length(); i++) {
   1895    result |= ElementAt(i).mModifier;
   1896  }
   1897  return result;
   1898 }
   1899 
   1900 void TextInputProcessor::ModifierKeyDataArray::ActivateModifierKey(
   1901    const TextInputProcessor::ModifierKeyData& aModifierKeyData) {
   1902  if (Contains(aModifierKeyData)) {
   1903    return;
   1904  }
   1905  AppendElement(aModifierKeyData);
   1906 }
   1907 
   1908 void TextInputProcessor::ModifierKeyDataArray::InactivateModifierKey(
   1909    const TextInputProcessor::ModifierKeyData& aModifierKeyData) {
   1910  RemoveElement(aModifierKeyData);
   1911 }
   1912 
   1913 void TextInputProcessor::ModifierKeyDataArray::ToggleModifierKey(
   1914    const TextInputProcessor::ModifierKeyData& aModifierKeyData) {
   1915  auto index = IndexOf(aModifierKeyData);
   1916  if (index == NoIndex) {
   1917    AppendElement(aModifierKeyData);
   1918    return;
   1919  }
   1920  RemoveElementAt(index);
   1921 }
   1922 
   1923 }  // namespace mozilla