widget/windows/KeyboardLayout.cpp
author Masayuki Nakano <masayuki@d-toybox.com>
Sat, 09 Apr 2016 15:45:06 +0900
changeset 332507 8ced614c9a1ff986eb1be374b75f96d545c5a8a1
parent 328959 69de5a5abae7d05b87dd4a36ee120349f6483683
child 332513 1bf2088320755b225f40a7b90ba0381460550421
permissions -rw-r--r--
Bug 1257759 part.3 ModifierKeyState should be available in plugin module r=jimm Before posting native key message from PluginInstanceChild, we need to check if the key combination could be a shortcut key, i.e., we need to check if Ctrl or Alt key is pressed. ModifierKeyState is a good class for checking it but it should be defined in an independent header file. This patch separates the definition of ModifierKeyState to mozilla/widget/WinModifierKeyState.h and implement MaybeMatchShortcutKey() for PluginInstanceChild. MozReview-Commit-ID: ZjLnJx02Ou

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/Logging.h"

#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/TextEvents.h"
#include "mozilla/WindowsVersion.h"

#include "nsAlgorithm.h"
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
#include "nsGkAtoms.h"
#include "nsIDOMKeyEvent.h"
#include "nsIIdleServiceInternal.h"
#include "nsMemory.h"
#include "nsPrintfCString.h"
#include "nsQuickSort.h"
#include "nsServiceManagerUtils.h"
#include "nsToolkit.h"
#include "nsUnicharUtils.h"
#include "nsWindowDbg.h"

#include "KeyboardLayout.h"
#include "WidgetUtils.h"
#include "WinUtils.h"

#include "npapi.h"

#include <windows.h>
#include <winuser.h>
#include <algorithm>

#ifndef WINABLEAPI
#include <winable.h>
#endif

// In WinUser.h, MAPVK_VK_TO_VSC_EX is defined only when WINVER >= 0x0600
#ifndef MAPVK_VK_TO_VSC_EX
#define MAPVK_VK_TO_VSC_EX (4)
#endif

namespace mozilla {
namespace widget {

static const char* kVirtualKeyName[] = {
  "NULL", "VK_LBUTTON", "VK_RBUTTON", "VK_CANCEL",
  "VK_MBUTTON", "VK_XBUTTON1", "VK_XBUTTON2", "0x07",
  "VK_BACK", "VK_TAB", "0x0A", "0x0B",
  "VK_CLEAR", "VK_RETURN", "0x0E", "0x0F",

  "VK_SHIFT", "VK_CONTROL", "VK_MENU", "VK_PAUSE",
  "VK_CAPITAL", "VK_KANA, VK_HANGUL", "0x16", "VK_JUNJA",
  "VK_FINAL", "VK_HANJA, VK_KANJI", "0x1A", "VK_ESCAPE",
  "VK_CONVERT", "VK_NONCONVERT", "VK_ACCEPT", "VK_MODECHANGE",

  "VK_SPACE", "VK_PRIOR", "VK_NEXT", "VK_END",
  "VK_HOME", "VK_LEFT", "VK_UP", "VK_RIGHT",
  "VK_DOWN", "VK_SELECT", "VK_PRINT", "VK_EXECUTE",
  "VK_SNAPSHOT", "VK_INSERT", "VK_DELETE", "VK_HELP",

  "VK_0", "VK_1", "VK_2", "VK_3",
  "VK_4", "VK_5", "VK_6", "VK_7",
  "VK_8", "VK_9", "0x3A", "0x3B",
  "0x3C", "0x3D", "0x3E", "0x3F",

  "0x40", "VK_A", "VK_B", "VK_C",
  "VK_D", "VK_E", "VK_F", "VK_G",
  "VK_H", "VK_I", "VK_J", "VK_K",
  "VK_L", "VK_M", "VK_N", "VK_O",

  "VK_P", "VK_Q", "VK_R", "VK_S",
  "VK_T", "VK_U", "VK_V", "VK_W",
  "VK_X", "VK_Y", "VK_Z", "VK_LWIN",
  "VK_RWIN", "VK_APPS", "0x5E", "VK_SLEEP",

  "VK_NUMPAD0", "VK_NUMPAD1", "VK_NUMPAD2", "VK_NUMPAD3",
  "VK_NUMPAD4", "VK_NUMPAD5", "VK_NUMPAD6", "VK_NUMPAD7",
  "VK_NUMPAD8", "VK_NUMPAD9", "VK_MULTIPLY", "VK_ADD",
  "VK_SEPARATOR", "VK_SUBTRACT", "VK_DECIMAL", "VK_DIVIDE",

  "VK_F1", "VK_F2", "VK_F3", "VK_F4",
  "VK_F5", "VK_F6", "VK_F7", "VK_F8",
  "VK_F9", "VK_F10", "VK_F11", "VK_F12",
  "VK_F13", "VK_F14", "VK_F15", "VK_F16",

  "VK_F17", "VK_F18", "VK_F19", "VK_F20",
  "VK_F21", "VK_F22", "VK_F23", "VK_F24",
  "0x88", "0x89", "0x8A", "0x8B",
  "0x8C", "0x8D", "0x8E", "0x8F",

  "VK_NUMLOCK", "VK_SCROLL", "VK_OEM_NEC_EQUAL, VK_OEM_FJ_JISHO",
    "VK_OEM_FJ_MASSHOU",
  "VK_OEM_FJ_TOUROKU", "VK_OEM_FJ_LOYA", "VK_OEM_FJ_ROYA", "0x97",
  "0x98", "0x99", "0x9A", "0x9B",
  "0x9C", "0x9D", "0x9E", "0x9F",

  "VK_LSHIFT", "VK_RSHIFT", "VK_LCONTROL", "VK_RCONTROL",
  "VK_LMENU", "VK_RMENU", "VK_BROWSER_BACK", "VK_BROWSER_FORWARD",
  "VK_BROWSER_REFRESH", "VK_BROWSER_STOP", "VK_BROWSER_SEARCH",
    "VK_BROWSER_FAVORITES",
  "VK_BROWSER_HOME", "VK_VOLUME_MUTE", "VK_VOLUME_DOWN", "VK_VOLUME_UP",

  "VK_MEDIA_NEXT_TRACK", "VK_MEDIA_PREV_TRACK", "VK_MEDIA_STOP",
    "VK_MEDIA_PLAY_PAUSE",
  "VK_LAUNCH_MAIL", "VK_LAUNCH_MEDIA_SELECT", "VK_LAUNCH_APP1",
    "VK_LAUNCH_APP2",
  "0xB8", "0xB9", "VK_OEM_1", "VK_OEM_PLUS",
  "VK_OEM_COMMA", "VK_OEM_MINUS", "VK_OEM_PERIOD", "VK_OEM_2",

  "VK_OEM_3", "VK_ABNT_C1", "VK_ABNT_C2", "0xC3",
  "0xC4", "0xC5", "0xC6", "0xC7",
  "0xC8", "0xC9", "0xCA", "0xCB",
  "0xCC", "0xCD", "0xCE", "0xCF",

  "0xD0", "0xD1", "0xD2", "0xD3",
  "0xD4", "0xD5", "0xD6", "0xD7",
  "0xD8", "0xD9", "0xDA", "VK_OEM_4",
  "VK_OEM_5", "VK_OEM_6", "VK_OEM_7", "VK_OEM_8",

  "0xE0", "VK_OEM_AX", "VK_OEM_102", "VK_ICO_HELP",
  "VK_ICO_00", "VK_PROCESSKEY", "VK_ICO_CLEAR", "VK_PACKET",
  "0xE8", "VK_OEM_RESET", "VK_OEM_JUMP", "VK_OEM_PA1",
  "VK_OEM_PA2", "VK_OEM_PA3", "VK_OEM_WSCTRL", "VK_OEM_CUSEL",

  "VK_OEM_ATTN", "VK_OEM_FINISH", "VK_OEM_COPY", "VK_OEM_AUTO",
  "VK_OEM_ENLW", "VK_OEM_BACKTAB", "VK_ATTN", "VK_CRSEL",
  "VK_EXSEL", "VK_EREOF", "VK_PLAY", "VK_ZOOM",
  "VK_NONAME", "VK_PA1", "VK_OEM_CLEAR", "0xFF"
};

static_assert(sizeof(kVirtualKeyName) / sizeof(const char*) == 0x100,
  "The virtual key name must be defined just 256 keys");

// Unique id counter associated with a keydown / keypress events. Used in
// identifing keypress events for removal from async event dispatch queue
// in metrofx after preventDefault is called on keydown events.
static uint32_t sUniqueKeyEventId = 0;

struct DeadKeyEntry
{
  char16_t BaseChar;
  char16_t CompositeChar;
};


class DeadKeyTable
{
  friend class KeyboardLayout;

  uint16_t mEntries;
  // KeyboardLayout::AddDeadKeyTable() will allocate as many entries as
  // required.  It is the only way to create new DeadKeyTable instances.
  DeadKeyEntry mTable[1];

  void Init(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries)
  {
    mEntries = aEntries;
    memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry));
  }

  static uint32_t SizeInBytes(uint32_t aEntries)
  {
    return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry);
  }

public:
  uint32_t Entries() const
  {
    return mEntries;
  }

  bool IsEqual(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const
  {
    return (mEntries == aEntries &&
            !memcmp(mTable, aDeadKeyArray,
                    aEntries * sizeof(DeadKeyEntry)));
  }

  char16_t GetCompositeChar(char16_t aBaseChar) const;
};


/*****************************************************************************
 * mozilla::widget::ModifierKeyState
 *****************************************************************************/

ModifierKeyState::ModifierKeyState()
{
  Update();
}

ModifierKeyState::ModifierKeyState(bool aIsShiftDown,
                                   bool aIsControlDown,
                                   bool aIsAltDown)
{
  Update();
  Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH);
  Modifiers modifiers = 0;
  if (aIsShiftDown) {
    modifiers |= MODIFIER_SHIFT;
  }
  if (aIsControlDown) {
    modifiers |= MODIFIER_CONTROL;
  }
  if (aIsAltDown) {
    modifiers |= MODIFIER_ALT;
  }
  if (modifiers) {
    Set(modifiers);
  }
}

ModifierKeyState::ModifierKeyState(Modifiers aModifiers) :
  mModifiers(aModifiers)
{
  EnsureAltGr();
}

void
ModifierKeyState::Update()
{
  mModifiers = 0;
  if (IS_VK_DOWN(VK_SHIFT)) {
    mModifiers |= MODIFIER_SHIFT;
  }
  if (IS_VK_DOWN(VK_CONTROL)) {
    mModifiers |= MODIFIER_CONTROL;
  }
  if (IS_VK_DOWN(VK_MENU)) {
    mModifiers |= MODIFIER_ALT;
  }
  if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) {
    mModifiers |= MODIFIER_OS;
  }
  if (::GetKeyState(VK_CAPITAL) & 1) {
    mModifiers |= MODIFIER_CAPSLOCK;
  }
  if (::GetKeyState(VK_NUMLOCK) & 1) {
    mModifiers |= MODIFIER_NUMLOCK;
  }
  if (::GetKeyState(VK_SCROLL) & 1) {
    mModifiers |= MODIFIER_SCROLLLOCK;
  }

  EnsureAltGr();
}

void
ModifierKeyState::Unset(Modifiers aRemovingModifiers)
{
  mModifiers &= ~aRemovingModifiers;
  // Note that we don't need to unset AltGr flag here automatically.
  // For nsEditor, we need to remove Alt and Control flags but AltGr isn't
  // checked in nsEditor, so, it can be kept.
}

void
ModifierKeyState::Set(Modifiers aAddingModifiers)
{
  mModifiers |= aAddingModifiers;
  EnsureAltGr();
}

void
ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const
{
  aInputEvent.mModifiers = mModifiers;

  switch(aInputEvent.mClass) {
    case eMouseEventClass:
    case eMouseScrollEventClass:
    case eWheelEventClass:
    case eDragEventClass:
    case eSimpleGestureEventClass:
      InitMouseEvent(aInputEvent);
      break;
    default:
      break;
  }
}

void
ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const
{
  NS_ASSERTION(aMouseEvent.mClass == eMouseEventClass ||
               aMouseEvent.mClass == eWheelEventClass ||
               aMouseEvent.mClass == eDragEventClass ||
               aMouseEvent.mClass == eSimpleGestureEventClass,
               "called with non-mouse event");

  WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
  mouseEvent.buttons = 0;
  if (::GetKeyState(VK_LBUTTON) < 0) {
    mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag;
  }
  if (::GetKeyState(VK_RBUTTON) < 0) {
    mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag;
  }
  if (::GetKeyState(VK_MBUTTON) < 0) {
    mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag;
  }
  if (::GetKeyState(VK_XBUTTON1) < 0) {
    mouseEvent.buttons |= WidgetMouseEvent::e4thButtonFlag;
  }
  if (::GetKeyState(VK_XBUTTON2) < 0) {
    mouseEvent.buttons |= WidgetMouseEvent::e5thButtonFlag;
  }
}

bool
ModifierKeyState::IsShift() const
{
  return (mModifiers & MODIFIER_SHIFT) != 0;
}

bool
ModifierKeyState::IsControl() const
{
  return (mModifiers & MODIFIER_CONTROL) != 0;
}

bool
ModifierKeyState::IsAlt() const
{
  return (mModifiers & MODIFIER_ALT) != 0;
}

bool
ModifierKeyState::IsAltGr() const
{
  return IsControl() && IsAlt();
}

bool
ModifierKeyState::IsWin() const
{
  return (mModifiers & MODIFIER_OS) != 0;
}

bool
ModifierKeyState::MaybeMatchShortcutKey() const
{
  // If Windows key is pressed, even if both Ctrl key and Alt key are pressed,
  // it's possible to match a shortcut key.
  if (IsWin()) {
    return true;
  }
  // Otherwise, when both Ctrl key and Alt key are pressed, it shouldn't be
  // a shortcut key for Windows since it means pressing AltGr key on
  // some keyboard layouts.
  if (IsControl() ^ IsAlt()) {
    return true;
  }
  // If no modifier key is active except a lockable modifier nor Shift key,
  // the key shouldn't match any shortcut keys (there are Space and
  // Shift+Space, though, let's ignore these special case...).
  return false;
}

bool
ModifierKeyState::IsCapsLocked() const
{
  return (mModifiers & MODIFIER_CAPSLOCK) != 0;
}

bool
ModifierKeyState::IsNumLocked() const
{
  return (mModifiers & MODIFIER_NUMLOCK) != 0;
}

bool
ModifierKeyState::IsScrollLocked() const
{
  return (mModifiers & MODIFIER_SCROLLLOCK) != 0;
}

void
ModifierKeyState::EnsureAltGr()
{
  // If both Control key and Alt key are pressed, it means AltGr is pressed.
  // Ideally, we should check whether the current keyboard layout has AltGr
  // or not.  However, setting AltGr flags for keyboard which doesn't have
  // AltGr must not be serious bug.  So, it should be OK for now.
  if (IsAltGr()) {
    mModifiers |= MODIFIER_ALTGRAPH;
  }
}

/*****************************************************************************
 * mozilla::widget::UniCharsAndModifiers
 *****************************************************************************/

void
UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers)
{
  MOZ_ASSERT(mLength < 5);
  mChars[mLength] = aUniChar;
  mModifiers[mLength] = aModifiers;
  mLength++;
}

void
UniCharsAndModifiers::FillModifiers(Modifiers aModifiers)
{
  for (uint32_t i = 0; i < mLength; i++) {
    mModifiers[i] = aModifiers;
  }
}

bool
UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers& aOther) const
{
  if (mLength != aOther.mLength) {
    return false;
  }
  return !memcmp(mChars, aOther.mChars, mLength * sizeof(char16_t));
}

bool
UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
                        const UniCharsAndModifiers& aOther) const
{
  if (mLength != aOther.mLength) {
    return false;
  }

  nsCaseInsensitiveStringComparator comp;
  return !comp(mChars, aOther.mChars, mLength, aOther.mLength);
}

UniCharsAndModifiers&
UniCharsAndModifiers::operator+=(const UniCharsAndModifiers& aOther)
{
  uint32_t copyCount = std::min(aOther.mLength, 5 - mLength);
  NS_ENSURE_TRUE(copyCount > 0, *this);
  memcpy(&mChars[mLength], aOther.mChars, copyCount * sizeof(char16_t));
  memcpy(&mModifiers[mLength], aOther.mModifiers,
         copyCount * sizeof(Modifiers));
  mLength += copyCount;
  return *this;
}

UniCharsAndModifiers
UniCharsAndModifiers::operator+(const UniCharsAndModifiers& aOther) const
{
  UniCharsAndModifiers result(*this);
  result += aOther;
  return result;
}

/*****************************************************************************
 * mozilla::widget::VirtualKey
 *****************************************************************************/

// static
VirtualKey::ShiftState
VirtualKey::ModifiersToShiftState(Modifiers aModifiers)
{
  ShiftState state = 0;
  if (aModifiers & MODIFIER_SHIFT) {
    state |= STATE_SHIFT;
  }
  if (aModifiers & MODIFIER_CONTROL) {
    state |= STATE_CONTROL;
  }
  if (aModifiers & MODIFIER_ALT) {
    state |= STATE_ALT;
  }
  if (aModifiers & MODIFIER_CAPSLOCK) {
    state |= STATE_CAPSLOCK;
  }
  return state;
}

// static
Modifiers
VirtualKey::ShiftStateToModifiers(ShiftState aShiftState)
{
  Modifiers modifiers = 0;
  if (aShiftState & STATE_SHIFT) {
    modifiers |= MODIFIER_SHIFT;
  }
  if (aShiftState & STATE_CONTROL) {
    modifiers |= MODIFIER_CONTROL;
  }
  if (aShiftState & STATE_ALT) {
    modifiers |= MODIFIER_ALT;
  }
  if (aShiftState & STATE_CAPSLOCK) {
    modifiers |= MODIFIER_CAPSLOCK;
  }
  if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) ==
         (MODIFIER_ALT | MODIFIER_CONTROL)) {
    modifiers |= MODIFIER_ALTGRAPH;
  }
  return modifiers;
}

inline char16_t
VirtualKey::GetCompositeChar(ShiftState aShiftState, char16_t aBaseChar) const
{
  return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar);
}

const DeadKeyTable*
VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
                                 uint32_t aEntries) const
{
  if (!mIsDeadKey) {
    return nullptr;
  }

  for (ShiftState shiftState = 0; shiftState < 16; shiftState++) {
    if (!IsDeadKey(shiftState)) {
      continue;
    }
    const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table;
    if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) {
      return dkt;
    }
  }

  return nullptr;
}

void
VirtualKey::SetNormalChars(ShiftState aShiftState,
                           const char16_t* aChars,
                           uint32_t aNumOfChars)
{
  NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index");

  SetDeadKey(aShiftState, false);

  for (uint32_t index = 0; index < aNumOfChars; index++) {
    // Ignore legacy non-printable control characters
    mShiftStates[aShiftState].Normal.Chars[index] =
      (aChars[index] >= 0x20) ? aChars[index] : 0;
  }

  uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
  for (uint32_t index = aNumOfChars; index < len; index++) {
    mShiftStates[aShiftState].Normal.Chars[index] = 0;
  }
}

void
VirtualKey::SetDeadChar(ShiftState aShiftState, char16_t aDeadChar)
{
  NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index");

  SetDeadKey(aShiftState, true);

  mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar;
  mShiftStates[aShiftState].DeadKey.Table = nullptr;
}

UniCharsAndModifiers
VirtualKey::GetUniChars(ShiftState aShiftState) const
{
  UniCharsAndModifiers result = GetNativeUniChars(aShiftState);

  const ShiftState STATE_ALT_CONTROL = (STATE_ALT | STATE_CONTROL);
  if (!(aShiftState & STATE_ALT_CONTROL)) {
    return result;
  }

  if (!result.mLength) {
    result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
    result.FillModifiers(ShiftStateToModifiers(aShiftState));
    return result;
  }

  if ((aShiftState & STATE_ALT_CONTROL) == STATE_ALT_CONTROL) {
    // Even if the shifted chars and the unshifted chars are same, we
    // should consume the Alt key state and the Ctrl key state when
    // AltGr key is pressed. Because if we don't consume them, the input
    // events are ignored on nsEditor. (I.e., Users cannot input the
    // characters with this key combination.)
    Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
    finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
    result.FillModifiers(finalModifiers);
    return result;
  }

  UniCharsAndModifiers unmodifiedReslt =
    GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
  if (!result.UniCharsEqual(unmodifiedReslt)) {
    // Otherwise, we should consume the Alt key state and the Ctrl key state
    // only when the shifted chars and unshifted chars are different.
    Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
    finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
    result.FillModifiers(finalModifiers);
  }
  return result;
}


UniCharsAndModifiers
VirtualKey::GetNativeUniChars(ShiftState aShiftState) const
{
#ifdef DEBUG
  if (aShiftState >= ArrayLength(mShiftStates)) {
    nsPrintfCString warning("Shift state is out of range: "
                            "aShiftState=%d, ArrayLength(mShiftState)=%d",
                            aShiftState, ArrayLength(mShiftStates));
    NS_WARNING(warning.get());
  }
#endif

  UniCharsAndModifiers result;
  Modifiers modifiers = ShiftStateToModifiers(aShiftState);
  if (IsDeadKey(aShiftState)) {
    result.Append(mShiftStates[aShiftState].DeadKey.DeadChar, modifiers);
    return result;
  }

  uint32_t index;
  uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
  for (index = 0;
       index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) {
    result.Append(mShiftStates[aShiftState].Normal.Chars[index], modifiers);
  }
  return result;
}

// static
void
VirtualKey::FillKbdState(PBYTE aKbdState,
                         const ShiftState aShiftState)
{
  NS_ASSERTION(aShiftState < 16, "aShiftState out of range");

  if (aShiftState & STATE_SHIFT) {
    aKbdState[VK_SHIFT] |= 0x80;
  } else {
    aKbdState[VK_SHIFT]  &= ~0x80;
    aKbdState[VK_LSHIFT] &= ~0x80;
    aKbdState[VK_RSHIFT] &= ~0x80;
  }

  if (aShiftState & STATE_CONTROL) {
    aKbdState[VK_CONTROL] |= 0x80;
  } else {
    aKbdState[VK_CONTROL]  &= ~0x80;
    aKbdState[VK_LCONTROL] &= ~0x80;
    aKbdState[VK_RCONTROL] &= ~0x80;
  }

  if (aShiftState & STATE_ALT) {
    aKbdState[VK_MENU] |= 0x80;
  } else {
    aKbdState[VK_MENU]  &= ~0x80;
    aKbdState[VK_LMENU] &= ~0x80;
    aKbdState[VK_RMENU] &= ~0x80;
  }

  if (aShiftState & STATE_CAPSLOCK) {
    aKbdState[VK_CAPITAL] |= 0x01;
  } else {
    aKbdState[VK_CAPITAL] &= ~0x01;
  }
}

/*****************************************************************************
 * mozilla::widget::NativeKey
 *****************************************************************************/

uint8_t NativeKey::sDispatchedKeyOfAppCommand = 0;

NativeKey::NativeKey(nsWindowBase* aWidget,
                     const MSG& aMessage,
                     const ModifierKeyState& aModKeyState,
                     nsTArray<FakeCharMsg>* aFakeCharMsgs)
  : mWidget(aWidget)
  , mDispatcher(aWidget->GetTextEventDispatcher())
  , mMsg(aMessage)
  , mDOMKeyCode(0)
  , mKeyNameIndex(KEY_NAME_INDEX_Unidentified)
  , mCodeNameIndex(CODE_NAME_INDEX_UNKNOWN)
  , mModKeyState(aModKeyState)
  , mVirtualKeyCode(0)
  , mOriginalVirtualKeyCode(0)
  , mShiftedLatinChar(0)
  , mUnshiftedLatinChar(0)
  , mScanCode(0)
  , mIsExtended(false)
  , mIsDeadKey(false)
  , mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ?
                    aFakeCharMsgs : nullptr)
{
  MOZ_ASSERT(aWidget);
  MOZ_ASSERT(mDispatcher);
  KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
  mKeyboardLayout = keyboardLayout->GetLayout();

  if (mMsg.message == WM_APPCOMMAND) {
    InitWithAppCommand();
    return;
  }

  mScanCode = WinUtils::GetScanCode(mMsg.lParam);
  mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam);
  switch (mMsg.message) {
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
    case WM_KEYUP:
    case WM_SYSKEYUP: {
      // If the key message is sent from other application like a11y tools, the
      // scancode value might not be set proper value.  Then, probably the value
      // is 0.
      // NOTE: If the virtual keycode can be caused by both non-extended key
      //       and extended key, the API returns the non-extended key's
      //       scancode.  E.g., VK_LEFT causes "4" key on numpad.
      if (!mScanCode) {
        uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mMsg.wParam);
        if (scanCodeEx) {
          mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
          uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
          mIsExtended = (extended == 0xE0) || (extended == 0xE1);
        }
      }
      // First, resolve the IME converted virtual keycode to its original
      // keycode.
      if (mMsg.wParam == VK_PROCESSKEY) {
        mOriginalVirtualKeyCode =
          static_cast<uint8_t>(::ImmGetVirtualKey(mMsg.hwnd));
      } else {
        mOriginalVirtualKeyCode = static_cast<uint8_t>(mMsg.wParam);
      }

      // Most keys are not distinguished as left or right keys.
      bool isLeftRightDistinguishedKey = false;

      // mOriginalVirtualKeyCode must not distinguish left or right of
      // Shift, Control or Alt.
      switch (mOriginalVirtualKeyCode) {
        case VK_SHIFT:
        case VK_CONTROL:
        case VK_MENU:
          isLeftRightDistinguishedKey = true;
          break;
        case VK_LSHIFT:
        case VK_RSHIFT:
          mVirtualKeyCode = mOriginalVirtualKeyCode;
          mOriginalVirtualKeyCode = VK_SHIFT;
          isLeftRightDistinguishedKey = true;
          break;
        case VK_LCONTROL:
        case VK_RCONTROL:
          mVirtualKeyCode = mOriginalVirtualKeyCode;
          mOriginalVirtualKeyCode = VK_CONTROL;
          isLeftRightDistinguishedKey = true;
          break;
        case VK_LMENU:
        case VK_RMENU:
          mVirtualKeyCode = mOriginalVirtualKeyCode;
          mOriginalVirtualKeyCode = VK_MENU;
          isLeftRightDistinguishedKey = true;
          break;
      }

      // If virtual keycode (left-right distinguished keycode) is already
      // computed, we don't need to do anymore.
      if (mVirtualKeyCode) {
        break;
      }

      // If the keycode doesn't have LR distinguished keycode, we just set
      // mOriginalVirtualKeyCode to mVirtualKeyCode.  Note that don't compute
      // it from MapVirtualKeyEx() because the scan code might be wrong if
      // the message is sent/posted by other application.  Then, we will compute
      // unexpected keycode from the scan code.
      if (!isLeftRightDistinguishedKey) {
        break;
      }

      if (!CanComputeVirtualKeyCodeFromScanCode()) {
        // The right control key and the right alt key are extended keys.
        // Therefore, we never get VK_RCONTRL and VK_RMENU for the result of
        // MapVirtualKeyEx() on WinXP or WinServer2003.
        //
        // If VK_CONTROL or VK_MENU key message is caused by an extended key,
        // we should assume that the right key of them is pressed.
        switch (mOriginalVirtualKeyCode) {
          case VK_CONTROL:
            mVirtualKeyCode = VK_RCONTROL;
            break;
          case VK_MENU:
            mVirtualKeyCode = VK_RMENU;
            break;
          case VK_SHIFT:
            // Neither left shift nor right shift is not an extended key,
            // let's use VK_LSHIFT for invalid scan code.
            mVirtualKeyCode = VK_LSHIFT;
            break;
          default:
            MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
        }
        break;
      }

      NS_ASSERTION(!mVirtualKeyCode,
                   "mVirtualKeyCode has been computed already");

      // Otherwise, compute the virtual keycode with MapVirtualKeyEx().
      mVirtualKeyCode = ComputeVirtualKeyCodeFromScanCodeEx();

      // Following code shouldn't be used now because we compute scancode value
      // if we detect that the sender doesn't set proper scancode.
      // However, the detection might fail. Therefore, let's keep using this.
      switch (mOriginalVirtualKeyCode) {
        case VK_CONTROL:
          if (mVirtualKeyCode != VK_LCONTROL &&
              mVirtualKeyCode != VK_RCONTROL) {
            mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL;
          }
          break;
        case VK_MENU:
          if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) {
            mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU;
          }
          break;
        case VK_SHIFT:
          if (mVirtualKeyCode != VK_LSHIFT && mVirtualKeyCode != VK_RSHIFT) {
            // Neither left shift nor right shift is not an extended key,
            // let's use VK_LSHIFT for invalid scan code.
            mVirtualKeyCode = VK_LSHIFT;
          }
          break;
        default:
          MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
      }
      break;
    }
    case WM_CHAR:
    case WM_UNICHAR:
    case WM_SYSCHAR:
      // NOTE: If other applications like a11y tools sends WM_*CHAR without
      //       scancode, we cannot compute virtual keycode.  I.e., with such
      //       applications, we cannot generate proper KeyboardEvent.code value.

      // We cannot compute the virtual key code from WM_CHAR message on WinXP
      // if it's caused by an extended key.
      if (!CanComputeVirtualKeyCodeFromScanCode()) {
        break;
      }
      mVirtualKeyCode = mOriginalVirtualKeyCode =
        ComputeVirtualKeyCodeFromScanCodeEx();
      NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode");
      break;
    default:
      MOZ_CRASH("Unsupported message");
  }

  if (!mVirtualKeyCode) {
    mVirtualKeyCode = mOriginalVirtualKeyCode;
  }

  mDOMKeyCode =
    keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
  mKeyNameIndex =
    keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode);
  mCodeNameIndex =
    KeyboardLayout::ConvertScanCodeToCodeNameIndex(
      GetScanCodeWithExtendedFlag());

  keyboardLayout->InitNativeKey(*this, mModKeyState);

  mIsDeadKey =
    (IsFollowedByDeadCharMessage() ||
     keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
  mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);

  // Compute some strings which may be inputted by the key with various
  // modifier state if this key event won't cause text input actually.
  // They will be used for setting alternativeCharCodes in the callback
  // method which will be called by TextEventDispatcher.
  if (IsKeyDownMessage() && NeedsToHandleWithoutFollowingCharMessages()) {
    ComputeInputtingStringWithKeyboardLayout();
  }
}

void
NativeKey::InitWithAppCommand()
{
  if (GET_DEVICE_LPARAM(mMsg.lParam) != FAPPCOMMAND_KEY) {
    return;
  }

  uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
  switch (GET_APPCOMMAND_LPARAM(mMsg.lParam)) {

#undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX
#define NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX(aAppCommand, aKeyNameIndex) \
    case aAppCommand: \
      mKeyNameIndex = aKeyNameIndex; \
      break;

#include "NativeKeyToDOMKeyName.h"

#undef NS_APPCOMMAND_TO_DOM_KEY_NAME_INDEX

    default:
      mKeyNameIndex = KEY_NAME_INDEX_Unidentified;
  }

  // Guess the virtual keycode which caused this message.
  switch (appCommand) {
    case APPCOMMAND_BROWSER_BACKWARD:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_BACK;
      break;
    case APPCOMMAND_BROWSER_FORWARD:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FORWARD;
      break;
    case APPCOMMAND_BROWSER_REFRESH:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_REFRESH;
      break;
    case APPCOMMAND_BROWSER_STOP:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_STOP;
      break;
    case APPCOMMAND_BROWSER_SEARCH:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_SEARCH;
      break;
    case APPCOMMAND_BROWSER_FAVORITES:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_FAVORITES;
      break;
    case APPCOMMAND_BROWSER_HOME:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_BROWSER_HOME;
      break;
    case APPCOMMAND_VOLUME_MUTE:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_MUTE;
      break;
    case APPCOMMAND_VOLUME_DOWN:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_DOWN;
      break;
    case APPCOMMAND_VOLUME_UP:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_VOLUME_UP;
      break;
    case APPCOMMAND_MEDIA_NEXTTRACK:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_NEXT_TRACK;
      break;
    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PREV_TRACK;
      break;
    case APPCOMMAND_MEDIA_STOP:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_STOP;
      break;
    case APPCOMMAND_MEDIA_PLAY_PAUSE:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_MEDIA_PLAY_PAUSE;
      break;
    case APPCOMMAND_LAUNCH_MAIL:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MAIL;
      break;
    case APPCOMMAND_LAUNCH_MEDIA_SELECT:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_MEDIA_SELECT;
      break;
    case APPCOMMAND_LAUNCH_APP1:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP1;
      break;
    case APPCOMMAND_LAUNCH_APP2:
      mVirtualKeyCode = mOriginalVirtualKeyCode = VK_LAUNCH_APP2;
      break;
    default:
      return;
  }

  uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mVirtualKeyCode);
  mScanCode = static_cast<uint8_t>(scanCodeEx & 0xFF);
  uint8_t extended = static_cast<uint8_t>((scanCodeEx & 0xFF00) >> 8);
  mIsExtended = (extended == 0xE0) || (extended == 0xE1);
  mDOMKeyCode =
    KeyboardLayout::GetInstance()->
      ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
  mCodeNameIndex =
    KeyboardLayout::ConvertScanCodeToCodeNameIndex(
      GetScanCodeWithExtendedFlag());
}

bool
NativeKey::IsFollowedByDeadCharMessage() const
{
  MSG nextMsg;
  if (mFakeCharMsgs) {
    nextMsg = mFakeCharMsgs->ElementAt(0).GetCharMsg(mMsg.hwnd);
  } else {
    if (!WinUtils::PeekMessage(&nextMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
                               PM_NOREMOVE | PM_NOYIELD)) {
      return false;
    }
  }
  return IsDeadCharMessage(nextMsg);
}

bool
NativeKey::IsIMEDoingKakuteiUndo() const
{
  // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG:
  // ---------------------------------------------------------------------------
  // WM_KEYDOWN              * n (wParam = VK_BACK, lParam = 0x1)
  // WM_KEYUP                * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK
  // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0)
  // WM_IME_COMPOSITION      * 1 (wParam = 0x0, lParam = 0x1BF)
  // WM_CHAR                 * n (wParam = VK_BACK, lParam = 0x1)
  // WM_KEYUP                * 1 (wParam = VK_BACK, lParam = 0xC00E0001)
  // ---------------------------------------------------------------------------
  // This doesn't match usual key message pattern such as:
  //   WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP
  // See following bugs for the detail.
  // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
  // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
  MSG startCompositionMsg, compositionMsg, charMsg;
  return WinUtils::PeekMessage(&startCompositionMsg, mMsg.hwnd,
                               WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION,
                               PM_NOREMOVE | PM_NOYIELD) &&
         WinUtils::PeekMessage(&compositionMsg, mMsg.hwnd, WM_IME_COMPOSITION,
                               WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) &&
         WinUtils::PeekMessage(&charMsg, mMsg.hwnd, WM_CHAR, WM_CHAR,
                               PM_NOREMOVE | PM_NOYIELD) &&
         startCompositionMsg.wParam == 0x0 &&
         startCompositionMsg.lParam == 0x0 &&
         compositionMsg.wParam == 0x0 &&
         compositionMsg.lParam == 0x1BF &&
         charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 &&
         startCompositionMsg.time <= compositionMsg.time &&
         compositionMsg.time <= charMsg.time;
}

UINT
NativeKey::GetScanCodeWithExtendedFlag() const
{
  // MapVirtualKeyEx() has been improved for supporting extended keys since
  // Vista.  When we call it for mapping a scancode of an extended key and
  // a virtual keycode, we need to add 0xE000 to the scancode.
  // On Win XP and Win Server 2003, this doesn't support. On them, we have
  // no way to get virtual keycodes from scancode of extended keys.
  if (!mIsExtended || !IsVistaOrLater()) {
    return mScanCode;
  }
  return (0xE000 | mScanCode);
}

uint32_t
NativeKey::GetKeyLocation() const
{
  switch (mVirtualKeyCode) {
    case VK_LSHIFT:
    case VK_LCONTROL:
    case VK_LMENU:
    case VK_LWIN:
      return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;

    case VK_RSHIFT:
    case VK_RCONTROL:
    case VK_RMENU:
    case VK_RWIN:
      return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;

    case VK_RETURN:
      // XXX This code assumes that all keyboard drivers use same mapping.
      return !mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD :
                            nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;

    case VK_INSERT:
    case VK_DELETE:
    case VK_END:
    case VK_DOWN:
    case VK_NEXT:
    case VK_LEFT:
    case VK_CLEAR:
    case VK_RIGHT:
    case VK_HOME:
    case VK_UP:
    case VK_PRIOR:
      // XXX This code assumes that all keyboard drivers use same mapping.
      return mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD :
                           nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;

    // NumLock key isn't included due to IE9's behavior.
    case VK_NUMPAD0:
    case VK_NUMPAD1:
    case VK_NUMPAD2:
    case VK_NUMPAD3:
    case VK_NUMPAD4:
    case VK_NUMPAD5:
    case VK_NUMPAD6:
    case VK_NUMPAD7:
    case VK_NUMPAD8:
    case VK_NUMPAD9:
    case VK_DECIMAL:
    case VK_DIVIDE:
    case VK_MULTIPLY:
    case VK_SUBTRACT:
    case VK_ADD:
    // Separator key of Brazilian keyboard or JIS keyboard for Mac
    case VK_ABNT_C2:
      return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;

    case VK_SHIFT:
    case VK_CONTROL:
    case VK_MENU:
      NS_WARNING("Failed to decide the key location?");

    default:
      return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
  }
}

bool
NativeKey::CanComputeVirtualKeyCodeFromScanCode() const
{
  // Vista or later supports ScanCodeEx.
  if (IsVistaOrLater()) {
    return true;
  }
  // Otherwise, MapVirtualKeyEx() can compute virtual keycode only with
  // non-extended key.
  return !mIsExtended;
}

uint8_t
NativeKey::ComputeVirtualKeyCodeFromScanCode() const
{
  return static_cast<uint8_t>(
           ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout));
}

uint8_t
NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const
{
  if (NS_WARN_IF(!CanComputeVirtualKeyCodeFromScanCode())) {
    return 0;
  }
  return static_cast<uint8_t>(
           ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX,
                             mKeyboardLayout));
}

uint16_t
NativeKey::ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const
{
  return static_cast<uint16_t>(
           ::MapVirtualKeyEx(aVirtualKeyCode,
                             IsVistaOrLater() ? MAPVK_VK_TO_VSC_EX :
                                                MAPVK_VK_TO_VSC,
                             mKeyboardLayout));
}

char16_t
NativeKey::ComputeUnicharFromScanCode() const
{
  return static_cast<char16_t>(
           ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(),
                             MAPVK_VK_TO_CHAR, mKeyboardLayout));
}

nsEventStatus
NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                        const MSG* aMsgSentToPlugin) const
{
  return InitKeyEvent(aKeyEvent, mModKeyState, aMsgSentToPlugin);
}

nsEventStatus
NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
                        const ModifierKeyState& aModKeyState,
                        const MSG* aMsgSentToPlugin) const
{
  if (mWidget->Destroyed()) {
    MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget");
  }

  LayoutDeviceIntPoint point(0, 0);
  mWidget->InitEvent(aKeyEvent, &point);

  switch (aKeyEvent.mMessage) {
    case eKeyDown:
      aKeyEvent.keyCode = mDOMKeyCode;
      // Unique id for this keydown event and its associated keypress.
      sUniqueKeyEventId++;
      aKeyEvent.mUniqueId = sUniqueKeyEventId;
      break;
    case eKeyUp:
      aKeyEvent.keyCode = mDOMKeyCode;
      // Set defaultPrevented of the key event if the VK_MENU is not a system
      // key release, so that the menu bar does not trigger.  This helps avoid
      // triggering the menu bar for ALT key accelerators used in assistive
      // technologies such as Window-Eyes and ZoomText or for switching open
      // state of IME.
      if (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP) {
        aKeyEvent.PreventDefaultBeforeDispatch();
      }
      break;
    case eKeyPress:
      aKeyEvent.mUniqueId = sUniqueKeyEventId;
      break;
    default:
      MOZ_CRASH("Invalid event message");
  }

  aKeyEvent.mIsRepeat = IsRepeat();
  aKeyEvent.mKeyNameIndex = mKeyNameIndex;
  if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
    aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString();
  }
  aKeyEvent.mCodeNameIndex = mCodeNameIndex;
  MOZ_ASSERT(mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
  aKeyEvent.location = GetKeyLocation();
  aModKeyState.InitInputEvent(aKeyEvent);

  NPEvent pluginEvent;
  if (aMsgSentToPlugin &&
      mWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) {
    pluginEvent.event = aMsgSentToPlugin->message;
    pluginEvent.wParam = aMsgSentToPlugin->wParam;
    pluginEvent.lParam = aMsgSentToPlugin->lParam;
    aKeyEvent.mPluginEvent.Copy(pluginEvent);
  }

  KeyboardLayout::NotifyIdleServiceOfUserActivity();

  return aKeyEvent.DefaultPrevented() ? nsEventStatus_eConsumeNoDefault :
                                        nsEventStatus_eIgnore;
}

bool
NativeKey::DispatchCommandEvent(uint32_t aEventCommand) const
{
  nsCOMPtr<nsIAtom> command;
  switch (aEventCommand) {
    case APPCOMMAND_BROWSER_BACKWARD:
      command = nsGkAtoms::Back;
      break;
    case APPCOMMAND_BROWSER_FORWARD:
      command = nsGkAtoms::Forward;
      break;
    case APPCOMMAND_BROWSER_REFRESH:
      command = nsGkAtoms::Reload;
      break;
    case APPCOMMAND_BROWSER_STOP:
      command = nsGkAtoms::Stop;
      break;
    case APPCOMMAND_BROWSER_SEARCH:
      command = nsGkAtoms::Search;
      break;
    case APPCOMMAND_BROWSER_FAVORITES:
      command = nsGkAtoms::Bookmarks;
      break;
    case APPCOMMAND_BROWSER_HOME:
      command = nsGkAtoms::Home;
      break;
    case APPCOMMAND_CLOSE:
      command = nsGkAtoms::Close;
      break;
    case APPCOMMAND_FIND:
      command = nsGkAtoms::Find;
      break;
    case APPCOMMAND_HELP:
      command = nsGkAtoms::Help;
      break;
    case APPCOMMAND_NEW:
      command = nsGkAtoms::New;
      break;
    case APPCOMMAND_OPEN:
      command = nsGkAtoms::Open;
      break;
    case APPCOMMAND_PRINT:
      command = nsGkAtoms::Print;
      break;
    case APPCOMMAND_SAVE:
      command = nsGkAtoms::Save;
      break;
    case APPCOMMAND_FORWARD_MAIL:
      command = nsGkAtoms::ForwardMail;
      break;
    case APPCOMMAND_REPLY_TO_MAIL:
      command = nsGkAtoms::ReplyToMail;
      break;
    case APPCOMMAND_SEND_MAIL:
      command = nsGkAtoms::SendMail;
      break;
    case APPCOMMAND_MEDIA_NEXTTRACK:
      command = nsGkAtoms::NextTrack;
      break;
    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
      command = nsGkAtoms::PreviousTrack;
      break;
    case APPCOMMAND_MEDIA_STOP:
      command = nsGkAtoms::MediaStop;
      break;
    case APPCOMMAND_MEDIA_PLAY_PAUSE:
      command = nsGkAtoms::PlayPause;
      break;
    default:
      return false;
  }
  WidgetCommandEvent commandEvent(true, nsGkAtoms::onAppCommand,
                                  command, mWidget);

  mWidget->InitEvent(commandEvent);
  return (mWidget->DispatchWindowEvent(&commandEvent) || mWidget->Destroyed());
}

bool
NativeKey::HandleAppCommandMessage() const
{
  // NOTE: Typical behavior of WM_APPCOMMAND caused by key is, WM_APPCOMMAND
  //       message is _sent_ first.  Then, the DefaultWndProc will _post_
  //       WM_KEYDOWN message and WM_KEYUP message if the keycode for the
  //       command is available (i.e., mVirtualKeyCode is not 0).

  // NOTE: IntelliType (Microsoft's keyboard utility software) always consumes
  //       WM_KEYDOWN and WM_KEYUP.

  // Let's dispatch keydown message before our chrome handles the command
  // when the message is caused by a keypress.  This behavior makes handling
  // WM_APPCOMMAND be a default action of the keydown event.  This means that
  // web applications can handle multimedia keys and prevent our default action.
  // This allow web applications to provide better UX for multimedia keyboard
  // users.
  bool dispatchKeyEvent = (GET_DEVICE_LPARAM(mMsg.lParam) == FAPPCOMMAND_KEY);
  if (dispatchKeyEvent) {
    // If a plug-in window has focus but it didn't consume the message, our
    // window receive WM_APPCOMMAND message.  In this case, we shouldn't
    // dispatch KeyboardEvents because an event handler may access the
    // plug-in process synchronously.
    dispatchKeyEvent =
      WinUtils::IsOurProcessWindow(reinterpret_cast<HWND>(mMsg.wParam));
  }

  bool consumed = false;

  if (dispatchKeyEvent) {
    nsresult rv = mDispatcher->BeginNativeInputTransaction();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return true;
    }
    WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
    nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState, &mMsg);
    // NOTE: If the keydown event is consumed by web contents, we shouldn't
    //       continue to handle the command.
    if (!mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, status,
                                            const_cast<NativeKey*>(this))) {
      // If keyboard event wasn't fired, there must be composition.
      // So, we don't need to dispatch a command event.
      return true;
    }
    consumed = status == nsEventStatus_eConsumeNoDefault;
    sDispatchedKeyOfAppCommand = mVirtualKeyCode;
    if (mWidget->Destroyed()) {
      return true;
    }
  }

  // Dispatch a command event or a content command event if the command is
  // supported.
  if (!consumed) {
    uint32_t appCommand = GET_APPCOMMAND_LPARAM(mMsg.lParam);
    EventMessage contentCommandMessage = eVoidEvent;
    switch (appCommand) {
      case APPCOMMAND_BROWSER_BACKWARD:
      case APPCOMMAND_BROWSER_FORWARD:
      case APPCOMMAND_BROWSER_REFRESH:
      case APPCOMMAND_BROWSER_STOP:
      case APPCOMMAND_BROWSER_SEARCH:
      case APPCOMMAND_BROWSER_FAVORITES:
      case APPCOMMAND_BROWSER_HOME:
      case APPCOMMAND_CLOSE:
      case APPCOMMAND_FIND:
      case APPCOMMAND_HELP:
      case APPCOMMAND_NEW:
      case APPCOMMAND_OPEN:
      case APPCOMMAND_PRINT:
      case APPCOMMAND_SAVE:
      case APPCOMMAND_FORWARD_MAIL:
      case APPCOMMAND_REPLY_TO_MAIL:
      case APPCOMMAND_SEND_MAIL:
      case APPCOMMAND_MEDIA_NEXTTRACK:
      case APPCOMMAND_MEDIA_PREVIOUSTRACK:
      case APPCOMMAND_MEDIA_STOP:
      case APPCOMMAND_MEDIA_PLAY_PAUSE:
        // We shouldn't consume the message always because if we don't handle
        // the message, the sender (typically, utility of keyboard or mouse)
        // may send other key messages which indicate well known shortcut key.
        consumed = DispatchCommandEvent(appCommand);
        break;

      // Use content command for following commands:
      case APPCOMMAND_COPY:
        contentCommandMessage = eContentCommandCopy;
        break;
      case APPCOMMAND_CUT:
        contentCommandMessage = eContentCommandCut;
        break;
      case APPCOMMAND_PASTE:
        contentCommandMessage = eContentCommandPaste;
        break;
      case APPCOMMAND_REDO:
        contentCommandMessage = eContentCommandRedo;
        break;
      case APPCOMMAND_UNDO:
        contentCommandMessage = eContentCommandUndo;
        break;
    }

    if (contentCommandMessage) {
      WidgetContentCommandEvent contentCommandEvent(true, contentCommandMessage,
                                                    mWidget);
      mWidget->DispatchWindowEvent(&contentCommandEvent);
      consumed = true;
    }

    if (mWidget->Destroyed()) {
      return true;
    }
  }

  // Dispatch a keyup event if the command is caused by pressing a key and
  // the key isn't mapped to a virtual keycode.
  if (dispatchKeyEvent && !mVirtualKeyCode) {
    nsresult rv = mDispatcher->BeginNativeInputTransaction();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return true;
    }
    WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
    nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState, &mMsg);
    // NOTE: Ignore if the keyup event is consumed because keyup event
    //       represents just a physical key event state change.
    mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status,
                                       const_cast<NativeKey*>(this));
    if (mWidget->Destroyed()) {
      return true;
    }
  }

  return consumed;
}

bool
NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const
{
  MOZ_ASSERT(IsKeyDownMessage());

  if (aEventDispatched) {
    *aEventDispatched = false;
  }

  if (sDispatchedKeyOfAppCommand &&
      sDispatchedKeyOfAppCommand == mOriginalVirtualKeyCode) {
    // The multimedia key event has already been dispatch from
    // HandleAppCommandMessage().
    sDispatchedKeyOfAppCommand = 0;
    return true;
  }

  bool defaultPrevented = false;
  if (mFakeCharMsgs ||
      !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) {
    // Ignore [shift+]alt+space so the OS can handle it.
    if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
        mVirtualKeyCode == VK_SPACE) {
      return false;
    }

    nsresult rv = mDispatcher->BeginNativeInputTransaction();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return true;
    }

    bool isIMEEnabled = WinUtils::IsIMEEnabled(mWidget->GetInputContext());
    WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget);
    nsEventStatus status = InitKeyEvent(keydownEvent, mModKeyState, &mMsg);
    bool dispatched =
      mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, status,
                                         const_cast<NativeKey*>(this));
    if (aEventDispatched) {
      *aEventDispatched = dispatched;
    }
    if (!dispatched) {
      // If the keydown event wasn't fired, there must be composition.
      // we don't need to do anything anymore.
      return false;
    }
    defaultPrevented = status == nsEventStatus_eConsumeNoDefault;

    if (mWidget->Destroyed()) {
      return true;
    }

    // If IMC wasn't associated to the window but is associated it now (i.e.,
    // focus is moved from a non-editable editor to an editor by keydown
    // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
    // inputting if IME is opened.  But then, we should redirect the native
    // keydown message to IME.
    // However, note that if focus has been already moved to another
    // application, we shouldn't redirect the message to it because the keydown
    // message is processed by us, so, nobody shouldn't process it.
    HWND focusedWnd = ::GetFocus();
    if (!defaultPrevented && !mFakeCharMsgs && focusedWnd &&
        !mWidget->PluginHasFocus() && !isIMEEnabled &&
        WinUtils::IsIMEEnabled(mWidget->GetInputContext())) {
      RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd);

      INPUT keyinput;
      keyinput.type = INPUT_KEYBOARD;
      keyinput.ki.wVk = mOriginalVirtualKeyCode;
      keyinput.ki.wScan = mScanCode;
      keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
      if (mIsExtended) {
        keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
      }
      keyinput.ki.time = 0;
      keyinput.ki.dwExtraInfo = 0;

      RedirectedKeyDownMessageManager::WillRedirect(mMsg, defaultPrevented);

      ::SendInput(1, &keyinput, sizeof(keyinput));

      // Return here.  We shouldn't dispatch keypress event for this WM_KEYDOWN.
      // If it's needed, it will be dispatched after next (redirected)
      // WM_KEYDOWN.
      return true;
    }
  } else {
    defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented();
    // If this is redirected keydown message, we have dispatched the keydown
    // event already.
    if (aEventDispatched) {
      *aEventDispatched = true;
    }
  }

  RedirectedKeyDownMessageManager::Forget();

  // If the key was processed by IME, we shouldn't dispatch keypress event.
  if (mOriginalVirtualKeyCode == VK_PROCESSKEY) {
    return defaultPrevented;
  }

  if (defaultPrevented) {
    DispatchPluginEventsAndDiscardsCharMessages();
    return true;
  }

  // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a
  // keypress for almost all keys
  if (NeedsToHandleWithoutFollowingCharMessages()) {
    return (DispatchPluginEventsAndDiscardsCharMessages() ||
            DispatchKeyPressEventsWithoutCharMessage());
  }

  MSG followingCharMsg;
  if (GetFollowingCharMessage(followingCharMsg)) {
    // Even if there was char message, it might be redirected by different
    // window (perhaps, focus move?).  Then, we shouldn't continue to handle
    // the message since no input should occur on the window.
    if (followingCharMsg.message == WM_NULL ||
        followingCharMsg.hwnd != mMsg.hwnd) {
      return false;
    }
    return DispatchKeyPressEventForFollowingCharMessage(followingCharMsg);
  }

  if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
      !mModKeyState.IsWin() && mIsPrintableKey) {
    // If this is simple KeyDown event but next message is not WM_CHAR,
    // this event may not input text, so we should ignore this event.
    // See bug 314130.
    return false;
  }

  if (mIsDeadKey) {
    return false;
  }

  return DispatchKeyPressEventsWithoutCharMessage();
}

bool
NativeKey::HandleCharMessage(const MSG& aCharMsg,
                             bool* aEventDispatched) const
{
  MOZ_ASSERT(IsKeyDownMessage() || IsPrintableCharMessage(mMsg));
  MOZ_ASSERT(IsPrintableCharMessage(aCharMsg.message));

  if (aEventDispatched) {
    *aEventDispatched = false;
  }

  // Alt+Space key is handled by OS, we shouldn't touch it.
  if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
      mVirtualKeyCode == VK_SPACE) {
    return false;
  }

  // Bug 818235: Ignore Ctrl+Enter.
  if (!mModKeyState.IsAlt() && mModKeyState.IsControl() &&
      mVirtualKeyCode == VK_RETURN) {
    return false;
  }

  // XXXmnakao I think that if aNativeKeyDown is null, such lonely WM_CHAR
  //           should cause composition events because they are not caused
  //           by actual keyboard operation.

  static const char16_t U_SPACE = 0x20;
  static const char16_t U_EQUAL = 0x3D;

  // First, handle normal text input or non-printable key case here.
  if ((!mModKeyState.IsAlt() && !mModKeyState.IsControl()) ||
      mModKeyState.IsAltGr() ||
      (mOriginalVirtualKeyCode &&
       !KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode))) {
    WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
    if (aCharMsg.wParam >= U_SPACE) {
      keypressEvent.charCode = static_cast<uint32_t>(aCharMsg.wParam);
    } else {
      keypressEvent.keyCode = mDOMKeyCode;
    }
    nsresult rv = mDispatcher->BeginNativeInputTransaction();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return true;
    }

    // When AltGr (Alt+Ctrl) is pressed, that causes normal text input.
    // At this time, if either alt or ctrl flag is set, nsEditor ignores the
    // keypress event.  For avoiding this issue, we should remove ctrl and alt
    // flags.
    ModifierKeyState modKeyState(mModKeyState);
    modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
    nsEventStatus status = InitKeyEvent(keypressEvent, modKeyState, &aCharMsg);
    bool dispatched =
      mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status,
                                               const_cast<NativeKey*>(this));
    if (aEventDispatched) {
      *aEventDispatched = dispatched;
    }
    return status == nsEventStatus_eConsumeNoDefault;
  }

  // XXX It seems that following code was implemented for shortcut key
  //     handling.  However, it's now handled in WM_KEYDOWN message handler.
  //     So, this actually runs only when WM_CHAR is sent/posted without
  //     WM_KEYDOWN.  I think that we don't need to keypress event in such
  //     case especially for shortcut keys.

  char16_t uniChar;
  // Ctrl+A Ctrl+Z, see Programming Windows 3.1 page 110 for details
  if (mModKeyState.IsControl() && aCharMsg.wParam <= 0x1A) {
    // Bug 16486: Need to account for shift here.
    uniChar = aCharMsg.wParam - 1 + (mModKeyState.IsShift() ? 'A' : 'a');
  } else if (mModKeyState.IsControl() && aCharMsg.wParam <= 0x1F) {
    // Bug 50255: <ctrl><[> and <ctrl><]> are not being processed.
    // also fixes ctrl+\ (x1c), ctrl+^ (x1e) and ctrl+_ (x1f)
    // for some reason the keypress handler need to have the uniChar code set
    // with the addition of a upper case A not the lower case.
    uniChar = aCharMsg.wParam - 1 + 'A';
  } else if (aCharMsg.wParam < U_SPACE ||
             (aCharMsg.wParam == U_EQUAL && mModKeyState.IsControl())) {
    uniChar = 0;
  } else {
    uniChar = aCharMsg.wParam;
  }

  // Bug 50255 and Bug 351310: Keep the characters unshifted for shortcuts and
  // accesskeys and make sure that numbers are always passed as such.
  if (uniChar && (mModKeyState.IsControl() || mModKeyState.IsAlt())) {
    char16_t unshiftedCharCode =
      (mVirtualKeyCode >= '0' && mVirtualKeyCode <= '9') ?
        mVirtualKeyCode :  mModKeyState.IsShift() ?
                             ComputeUnicharFromScanCode() : 0;
    // Ignore diacritics (top bit set) and key mapping errors (char code 0)
    if (static_cast<int32_t>(unshiftedCharCode) > 0) {
      uniChar = unshiftedCharCode;
    }
  }

  // Bug 285161 and Bug 295095: They were caused by the initial fix for
  // bug 178110.  When pressing (alt|ctrl)+char, the char must be lowercase
  // unless shift is pressed too.
  if (!mModKeyState.IsShift() &&
      (mModKeyState.IsAlt() || mModKeyState.IsControl())) {
    uniChar = towlower(uniChar);
  }

  nsresult rv = mDispatcher->BeginNativeInputTransaction();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return true;
  }

  WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
  keypressEvent.charCode = uniChar;
  if (!keypressEvent.charCode) {
    keypressEvent.keyCode = mDOMKeyCode;
  }
  nsEventStatus status = InitKeyEvent(keypressEvent, mModKeyState, &aCharMsg);
  bool dispatched =
    mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status,
                                             const_cast<NativeKey*>(this));
  if (aEventDispatched) {
    *aEventDispatched = dispatched;
  }
  return status == nsEventStatus_eConsumeNoDefault;
}

bool
NativeKey::HandleKeyUpMessage(bool* aEventDispatched) const
{
  MOZ_ASSERT(IsKeyUpMessage());

  if (aEventDispatched) {
    *aEventDispatched = false;
  }

  // Ignore [shift+]alt+space so the OS can handle it.
  if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
      mVirtualKeyCode == VK_SPACE) {
    return false;
  }

  nsresult rv = mDispatcher->BeginNativeInputTransaction();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return true;
  }

  WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget);
  nsEventStatus status = InitKeyEvent(keyupEvent, mModKeyState, &mMsg);
  bool dispatched =
    mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, status,
                                       const_cast<NativeKey*>(this));
  if (aEventDispatched) {
    *aEventDispatched = dispatched;
  }
  return status == nsEventStatus_eConsumeNoDefault;
}

bool
NativeKey::NeedsToHandleWithoutFollowingCharMessages() const
{
  MOZ_ASSERT(IsKeyDownMessage());

  // Enter and backspace are always handled here to avoid for example the
  // confusion between ctrl-enter and ctrl-J.
  if (mDOMKeyCode == NS_VK_RETURN || mDOMKeyCode == NS_VK_BACK) {
    return true;
  }

  // If inputting two or more characters, should be dispatched after removing
  // whole following char messages.
  if (mCommittedCharsAndModifiers.mLength > 1) {
    return true;
  }

  // If any modifier keys which may cause printable keys becoming non-printable
  // are not pressed, we don't need special handling for the key.
  if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() &&
      !mModKeyState.IsWin()) {
    return false;
  }

  // If the key event causes dead key event, we don't need to dispatch keypress
  // event.
  if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) {
    return false;
  }

  // Even if the key is a printable key, it might cause non-printable character
  // input with modifier key(s).
  return mIsPrintableKey;
}

#ifdef MOZ_CRASHREPORTER

static nsCString
GetResultOfInSendMessageEx()
{
  DWORD ret = ::InSendMessageEx(nullptr);
  if (!ret) {
    return NS_LITERAL_CSTRING("ISMEX_NOSEND");
  }
  nsAutoCString result;
  if (ret & ISMEX_CALLBACK) {
    result = "ISMEX_CALLBACK";
  }
  if (ret & ISMEX_NOTIFY) {
    if (!result.IsEmpty()) {
      result += " | ";
    }
    result += "ISMEX_NOTIFY";
  }
  if (ret & ISMEX_REPLIED) {
    if (!result.IsEmpty()) {
      result += " | ";
    }
    result += "ISMEX_REPLIED";
  }
  if (ret & ISMEX_SEND) {
    if (!result.IsEmpty()) {
      result += " | ";
    }
    result += "ISMEX_SEND";
  }
  return result;
}

static const char*
GetMessageName(UINT aMessage)
{
  switch (aMessage) {
    case WM_KEYDOWN:     return "WM_KEYDOWN";
    case WM_SYSKEYDOWN:  return "WM_SYSKEYDOWN";
    case WM_KEYUP:       return "WM_KEYUP";
    case WM_SYSKEYUP:    return "WM_SYSKEYUP";
    case WM_CHAR:        return "WM_CHAR";
    case WM_DEADCHAR:    return "WM_DEADCHAR";
    case WM_SYSCHAR:     return "WM_SYSCHAR";
    case WM_SYSDEADCHAR: return "WM_SYSDEADCHAR";
    case WM_UNICHAR:     return "WM_UNICHAR";
    case WM_QUIT:        return "WM_QUIT";
    case WM_NULL:        return "WM_NULL";
    default:             return "Unknown";
  }
}

#endif // #ifdef MOZ_CRASHREPORTER

bool
NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1,
                                const MSG& aCharMsg2) const
{
  // NOTE: Although, we don't know when this case occurs, the scan code value
  //       in lParam may be changed from 0 to something.  The changed value
  //       is different from the scan code of handling keydown message.
  static const LPARAM kScanCodeMask = 0x00FF0000;
  return
    aCharMsg1.message == aCharMsg2.message &&
    aCharMsg1.wParam == aCharMsg2.wParam &&
    (aCharMsg1.lParam & ~kScanCodeMask) == (aCharMsg2.lParam & ~kScanCodeMask);
}

bool
NativeKey::GetFollowingCharMessage(MSG& aCharMsg) const
{
  MOZ_ASSERT(IsKeyDownMessage());

  aCharMsg.message = WM_NULL;

  if (mFakeCharMsgs) {
    for (size_t i = 0; i < mFakeCharMsgs->Length(); i++) {
      FakeCharMsg& fakeCharMsg = mFakeCharMsgs->ElementAt(i);
      if (fakeCharMsg.mConsumed) {
        continue;
      }
      MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd);
      fakeCharMsg.mConsumed = true;
      if (!IsCharMessage(charMsg)) {
        return false;
      }
      aCharMsg = charMsg;
      return true;
    }
    return false;
  }

  // If next key message is not char message, we should give up to find a
  // related char message for the handling keydown event for now.
  // Note that it's possible other applications may send other key message
  // after we call TranslateMessage(). That may cause PeekMessage() failing
  // to get char message for the handling keydown message.
  MSG nextKeyMsg;
  if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST,
                             PM_NOREMOVE | PM_NOYIELD) ||
      !IsCharMessage(nextKeyMsg)) {
    return false;
  }

  // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify
  // the message range.  So, if it returns WM_NULL, we should retry to get
  // the following char message it was found above.
  for (uint32_t i = 0; i < 5; i++) {
    MSG removedMsg, nextKeyMsgInAllWindows;
    bool doCrash = false;
    if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd,
                               nextKeyMsg.message, nextKeyMsg.message,
                               PM_REMOVE | PM_NOYIELD)) {
      // We meets unexpected case.  We should collect the message queue state
      // and crash for reporting the bug.
      doCrash = true;
      // The char message is redirected to different thread's window by focus
      // move or something or just cancelled by external application.
      if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0,
                                 WM_KEYFIRST, WM_KEYLAST,
                                 PM_NOREMOVE | PM_NOYIELD)) {
        return true;
      }
      if (MayBeSameCharMessage(nextKeyMsgInAllWindows, nextKeyMsg)) {
        // The char message is redirected to different window created by our
        // thread.
        if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) {
          aCharMsg = nextKeyMsgInAllWindows;
          return true;
        }
        // The found char message still in the queue, but PeekMessage() failed
        // to remove it only with PM_REMOVE.  Although, we don't know why this
        // occurs.  However, this occurs acctually.
        // Try to remove the char message with GetMessage() again.
        if (WinUtils::GetMessage(&removedMsg, mMsg.hwnd,
                                 nextKeyMsg.message, nextKeyMsg.message)) {
          // Cancel to crash, but we need to check the removed message value.
          doCrash = false;
        }
      }
    }

    if (doCrash) {
#ifdef MOZ_CRASHREPORTER
      nsPrintfCString info("\nPeekMessage() failed to remove char message! "
                           "\nHandling message: %s (0x%08X), wParam: 0x%08X, "
                           "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, \n"
                           "Found message: %s (0x%08X), wParam: 0x%08X, "
                           "lParam: 0x%08X, hwnd=0x%p, "
                           "\nWM_NULL has been removed: %d, "
                           "\nNext key message in all windows: %s (0x%08X), "
                           "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, "
                           "time=%d, ",
                           GetMessageName(mMsg.message),
                           mMsg.message, mMsg.wParam, mMsg.lParam,
                           nextKeyMsg.hwnd,
                           GetResultOfInSendMessageEx().get(),
                           GetMessageName(nextKeyMsg.message),
                           nextKeyMsg.message, nextKeyMsg.wParam,
                           nextKeyMsg.lParam, nextKeyMsg.hwnd, i,
                           GetMessageName(nextKeyMsgInAllWindows.message),
                           nextKeyMsgInAllWindows.message,
                           nextKeyMsgInAllWindows.wParam,
                           nextKeyMsgInAllWindows.lParam,
                           nextKeyMsgInAllWindows.hwnd,
                           nextKeyMsgInAllWindows.time);
      CrashReporter::AppendAppNotesToCrashReport(info);
      MSG nextMsg;
      if (WinUtils::PeekMessage(&nextMsg, 0, 0, 0,
                                PM_NOREMOVE | PM_NOYIELD)) {
        nsPrintfCString info("\nNext message in all windows: %s (0x%08X), "
                             "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, "
                             "time=%d",
                             GetMessageName(nextMsg.message),
                             nextMsg.message, nextMsg.wParam, nextMsg.lParam,
                             nextMsg.hwnd, nextMsg.time);
        CrashReporter::AppendAppNotesToCrashReport(info);
      } else {
        CrashReporter::AppendAppNotesToCrashReport(
          NS_LITERAL_CSTRING("\nThere is no message in any window"));
      }
#endif // #ifdef MOZ_CRASHREPORTER
      MOZ_CRASH("We lost the following char message");
    }

    // Retry for the strange case.
    if (removedMsg.message == WM_NULL) {
      continue;
    }

    // Typically, this case occurs with WM_DEADCHAR.  If the removed message's
    // wParam becomes 0, that means that the key event shouldn't cause text
    // input.  So, let's ignore the strange char message.
    if (removedMsg.message == nextKeyMsg.message && !removedMsg.wParam) {
      return false;
    }

    // NOTE: Although, we don't know when this case occurs, the scan code value
    //       in lParam may be changed from 0 to something.  The changed value
    //       is different from the scan code of handling keydown message.
    if (!MayBeSameCharMessage(removedMsg, nextKeyMsg)) {
#ifdef MOZ_CRASHREPORTER
      nsPrintfCString info("\nPeekMessage() removed unexpcted char message! "
                           "\nHandling message: %s (0x%08X), wParam: 0x%08X, "
                           "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, "
                           "\nFound message: %s (0x%08X), wParam: 0x%08X, "
                           "lParam: 0x%08X, hwnd=0x%p, "
                           "\nRemoved message: %s (0x%08X), wParam: 0x%08X, "
                           "lParam: 0x%08X, hwnd=0x%p, ",
                           GetMessageName(mMsg.message),
                           mMsg.message, mMsg.wParam, mMsg.lParam, mMsg.hwnd,
                           GetResultOfInSendMessageEx().get(),
                           GetMessageName(nextKeyMsg.message),
                           nextKeyMsg.message, nextKeyMsg.wParam,
                           nextKeyMsg.lParam, nextKeyMsg.hwnd,
                           GetMessageName(removedMsg.message),
                           removedMsg.message, removedMsg.wParam,
                           removedMsg.lParam, removedMsg.hwnd);
      CrashReporter::AppendAppNotesToCrashReport(info);
      // What's the next key message?
      MSG nextKeyMsgAfter;
      if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd,
                                WM_KEYFIRST, WM_KEYLAST,
                                PM_NOREMOVE | PM_NOYIELD)) {
        nsPrintfCString info("\nNext key message after unexpected char message "
                             "removed: %s (0x%08X), wParam: 0x%08X, "
                             "lParam: 0x%08X, hwnd=0x%p, ",
                             GetMessageName(nextKeyMsgAfter.message),
                             nextKeyMsgAfter.message, nextKeyMsgAfter.wParam,
                             nextKeyMsgAfter.lParam, nextKeyMsgAfter.hwnd);
        CrashReporter::AppendAppNotesToCrashReport(info);
      } else {
        CrashReporter::AppendAppNotesToCrashReport(
          NS_LITERAL_CSTRING("\nThere is no key message after unexpected char "
                             "message removed, "));
      }
      // Another window has a key message?
      MSG nextKeyMsgInAllWindows;
      if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0,
                                WM_KEYFIRST, WM_KEYLAST,
                                PM_NOREMOVE | PM_NOYIELD)) {
        nsPrintfCString info("\nNext key message in all windows: %s (0x%08X), "
                             "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p.",
                             GetMessageName(nextKeyMsgInAllWindows.message),
                             nextKeyMsgInAllWindows.message,
                             nextKeyMsgInAllWindows.wParam,
                             nextKeyMsgInAllWindows.lParam,
                             nextKeyMsgInAllWindows.hwnd);
        CrashReporter::AppendAppNotesToCrashReport(info);
      } else {
        CrashReporter::AppendAppNotesToCrashReport(
          NS_LITERAL_CSTRING("\nThere is no key message in any windows."));
      }
#endif // #ifdef MOZ_CRASHREPORTER
      MOZ_CRASH("PeekMessage() removed unexpected message");
    }

    aCharMsg = removedMsg;
    return true;
  }
#ifdef MOZ_CRASHREPORTER
  nsPrintfCString info("\nWe lost following char message! "
                       "\nHandling message: %s (0x%08X), wParam: 0x%08X, "
                       "lParam: 0x%08X, InSendMessageEx()=%s, \n"
                       "Found message: %s (0x%08X), wParam: 0x%08X, "
                       "lParam: 0x%08X, removed a lot of WM_NULL",
                       GetMessageName(mMsg.message),
                       mMsg.message, mMsg.wParam, mMsg.lParam,
                       GetResultOfInSendMessageEx().get(),
                       GetMessageName(nextKeyMsg.message),
                       nextKeyMsg.message, nextKeyMsg.wParam,
                       nextKeyMsg.lParam);
  CrashReporter::AppendAppNotesToCrashReport(info);
#endif // #ifdef MOZ_CRASHREPORTER
  MOZ_CRASH("We lost the following char message");
  return false;
}

bool
NativeKey::DispatchPluginEventsAndDiscardsCharMessages() const
{
  MOZ_ASSERT(IsKeyDownMessage());

  // Remove a possible WM_CHAR or WM_SYSCHAR messages from the message queue.
  // They can be more than one because of:
  //  * Dead-keys not pairing with base character
  //  * Some keyboard layouts may map up to 4 characters to the single key
  bool anyCharMessagesRemoved = false;
  MSG msg;
  while (GetFollowingCharMessage(msg)) {
    if (msg.message == WM_NULL) {
      continue;
    }
    anyCharMessagesRemoved = true;
    // If the window handle is changed, focused window must be changed.
    // So, plugin shouldn't handle it anymore.
    if (msg.hwnd != mMsg.hwnd) {
      break;
    }
    MOZ_RELEASE_ASSERT(!mWidget->Destroyed(),
      "NativeKey tries to dispatch a plugin event on destroyed widget");
    mWidget->DispatchPluginEvent(msg);
    if (mWidget->Destroyed()) {
      return true;
    }
  }

  if (!mFakeCharMsgs && !anyCharMessagesRemoved &&
      mDOMKeyCode == NS_VK_BACK && IsIMEDoingKakuteiUndo()) {
    // This is for a hack for ATOK and WXG.  So, PeekMessage() must scceed!
    while (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_CHAR, WM_CHAR,
                                 PM_REMOVE | PM_NOYIELD)) {
      if (msg.message != WM_CHAR) {
        MOZ_RELEASE_ASSERT(msg.message == WM_NULL,
                           "Unexpected message was removed");
        continue;
      }
      MOZ_RELEASE_ASSERT(!mWidget->Destroyed(),
        "NativeKey tries to dispatch a plugin event on destroyed widget");
      mWidget->DispatchPluginEvent(msg);
      return mWidget->Destroyed();
    }
    MOZ_CRASH("NativeKey failed to get WM_CHAR for ATOK or WXG");
  }

  return false;
}

void
NativeKey::ComputeInputtingStringWithKeyboardLayout()
{
  KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();

  if (KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode)) {
    mInputtingStringAndModifiers = mCommittedCharsAndModifiers;
  } else {
    mInputtingStringAndModifiers.Clear();
  }
  mShiftedString.Clear();
  mUnshiftedString.Clear();
  mShiftedLatinChar = mUnshiftedLatinChar = 0;

  // XXX How about when Win key is pressed?
  if (mModKeyState.IsControl() == mModKeyState.IsAlt()) {
    return;
  }

  ModifierKeyState capsLockState(
                     mModKeyState.GetModifiers() & MODIFIER_CAPSLOCK);

  mUnshiftedString =
    keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);
  capsLockState.Set(MODIFIER_SHIFT);
  mShiftedString =
    keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState);

  // The current keyboard cannot input alphabets or numerics,
  // we should append them for Shortcut/Access keys.
  // E.g., for Cyrillic keyboard layout.
  capsLockState.Unset(MODIFIER_SHIFT);
  WidgetUtils::GetLatinCharCodeForKeyCode(mDOMKeyCode,
                                          capsLockState.GetModifiers(),
                                          &mUnshiftedLatinChar,
                                          &mShiftedLatinChar);

  // If the mShiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
  if (mShiftedLatinChar) {
    // If the produced characters of the key on current keyboard layout
    // are same as computed Latin characters, we shouldn't append the
    // Latin characters to alternativeCharCode.
    if (mUnshiftedLatinChar == mUnshiftedString.mChars[0] &&
        mShiftedLatinChar == mShiftedString.mChars[0]) {
      mShiftedLatinChar = mUnshiftedLatinChar = 0;
    }
  } else if (mUnshiftedLatinChar) {
    // If the mShiftedLatinChar is 0, the keyCode doesn't produce
    // alphabet character.  At that time, the character may be produced
    // with Shift key.  E.g., on French keyboard layout, NS_VK_PERCENT
    // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
    // Shift key but with Shift key, it produces '%'.
    // If the mUnshiftedLatinChar is produced by the key on current
    // keyboard layout, we shouldn't append it to alternativeCharCode.
    if (mUnshiftedLatinChar == mUnshiftedString.mChars[0] ||
        mUnshiftedLatinChar == mShiftedString.mChars[0]) {
      mUnshiftedLatinChar = 0;
    }
  }

  if (!mModKeyState.IsControl()) {
    return;
  }

  // If the charCode is not ASCII character, we should replace the
  // charCode with ASCII character only when Ctrl is pressed.
  // But don't replace the charCode when the charCode is not same as
  // unmodified characters. In such case, Ctrl is sometimes used for a
  // part of character inputting key combination like Shift.
  uint32_t ch =
    mModKeyState.IsShift() ? mShiftedLatinChar : mUnshiftedLatinChar;
  if (!ch) {
    return;
  }
  if (mInputtingStringAndModifiers.IsEmpty() ||
      mInputtingStringAndModifiers.UniCharsCaseInsensitiveEqual(
        mModKeyState.IsShift() ? mShiftedString : mUnshiftedString)) {
    mInputtingStringAndModifiers.Clear();
    mInputtingStringAndModifiers.Append(ch, mModKeyState.GetModifiers());
  }
}

bool
NativeKey::DispatchKeyPressEventsWithoutCharMessage() const
{
  MOZ_ASSERT(IsKeyDownMessage());
  MOZ_ASSERT(!mIsDeadKey || !mCommittedCharsAndModifiers.IsEmpty());

  nsresult rv = mDispatcher->BeginNativeInputTransaction();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return true;
  }

  WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget);
  if (mInputtingStringAndModifiers.IsEmpty() &&
      mShiftedString.IsEmpty() && mUnshiftedString.IsEmpty()) {
    keypressEvent.keyCode = mDOMKeyCode;
  }
  nsEventStatus status = InitKeyEvent(keypressEvent, mModKeyState);
  mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, status,
                                           const_cast<NativeKey*>(this));
  return status == nsEventStatus_eConsumeNoDefault;
}

void
NativeKey::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
                                     uint32_t aIndex)
{
  uint32_t longestLength =
    std::max(mInputtingStringAndModifiers.mLength,
             std::max(mShiftedString.mLength, mUnshiftedString.mLength));
  uint32_t skipUniChars = longestLength - mInputtingStringAndModifiers.mLength;
  uint32_t skipShiftedChars = longestLength - mShiftedString.mLength;
  uint32_t skipUnshiftedChars = longestLength - mUnshiftedString.mLength;
  if (aIndex >= longestLength) {
    return;
  }

  nsTArray<AlternativeCharCode>& altArray = aKeyboardEvent.alternativeCharCodes;

  uint16_t shiftedChar = 0, unshiftedChar = 0;
  if (skipUniChars <= aIndex) {
    // XXX Modifying modifier state of aKeyboardEvent is illegal, but no way
    //     to set different modifier state per keypress event except this
    //     hack.  Note that ideally, dead key should cause composition events
    //     instead of keypress events, though.
    if (aIndex - skipUniChars  < mInputtingStringAndModifiers.mLength) {
      ModifierKeyState modKeyState(mModKeyState);
      // If key in combination with Alt and/or Ctrl produces a different
      // character than without them then do not report these flags
      // because it is separate keyboard layout shift state. If dead-key
      // and base character does not produce a valid composite character
      // then both produced dead-key character and following base
      // character may have different modifier flags, too.
      modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
                        MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
      modKeyState.Set(
        mInputtingStringAndModifiers.mModifiers[aIndex - skipUniChars]);
      modKeyState.InitInputEvent(aKeyboardEvent);
    }
    uint16_t uniChar =
      mInputtingStringAndModifiers.mChars[aIndex - skipUniChars];

    // The charCode was set from mKeyValue. However, for example, when Ctrl key
    // is pressed, its value should indicate an ASCII character for backward
    // compatibility rather than inputting character without the modifiers.
    // Therefore, we need to modify charCode value here.
    aKeyboardEvent.SetCharCode(uniChar);
  }

  if (skipShiftedChars <= aIndex) {
    shiftedChar = mShiftedString.mChars[aIndex - skipShiftedChars];
  }
  if (skipUnshiftedChars <= aIndex) {
    unshiftedChar = mUnshiftedString.mChars[aIndex - skipUnshiftedChars];
  }

  if (shiftedChar || unshiftedChar) {
    AlternativeCharCode chars(unshiftedChar, shiftedChar);
    altArray.AppendElement(chars);
  }

  if (aIndex == longestLength - 1) {
    if (mUnshiftedLatinChar || mShiftedLatinChar) {
      AlternativeCharCode chars(mUnshiftedLatinChar, mShiftedLatinChar);
      altArray.AppendElement(chars);
    }

    // Typically, following virtual keycodes are used for a key which can
    // input the character.  However, these keycodes are also used for
    // other keys on some keyboard layout.  E.g., in spite of Shift+'1'
    // inputs '+' on Thai keyboard layout, a key which is at '=/+'
    // key on ANSI keyboard layout is VK_OEM_PLUS.  Native applications
    // handle it as '+' key if Ctrl key is pressed.
    char16_t charForOEMKeyCode = 0;
    switch (mVirtualKeyCode) {
      case VK_OEM_PLUS:   charForOEMKeyCode = '+'; break;
      case VK_OEM_COMMA:  charForOEMKeyCode = ','; break;
      case VK_OEM_MINUS:  charForOEMKeyCode = '-'; break;
      case VK_OEM_PERIOD: charForOEMKeyCode = '.'; break;
    }
    if (charForOEMKeyCode &&
        charForOEMKeyCode != mUnshiftedString.mChars[0] &&
        charForOEMKeyCode != mShiftedString.mChars[0] &&
        charForOEMKeyCode != mUnshiftedLatinChar &&
        charForOEMKeyCode != mShiftedLatinChar) {
      AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
      altArray.AppendElement(OEMChars);
    }
  }
}

bool
NativeKey::DispatchKeyPressEventForFollowingCharMessage(
             const MSG& aCharMsg) const
{
  MOZ_ASSERT(IsKeyDownMessage());

  if (mFakeCharMsgs) {
    if (IsDeadCharMessage(aCharMsg)) {
      return false;
    }
#ifdef DEBUG
    if (mIsPrintableKey) {
      nsPrintfCString log(
        "mOriginalVirtualKeyCode=0x%02X, mCommittedCharsAndModifiers={ "
        "mChars=[ 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, "
        "wParam=0x%04X",
        mOriginalVirtualKeyCode, mCommittedCharsAndModifiers.mChars[0],
        mCommittedCharsAndModifiers.mChars[1],
        mCommittedCharsAndModifiers.mChars[2],
        mCommittedCharsAndModifiers.mChars[3],
        mCommittedCharsAndModifiers.mChars[4],
        mCommittedCharsAndModifiers.mLength, aCharMsg.wParam);
      if (mCommittedCharsAndModifiers.IsEmpty()) {
        log.Insert("length is zero: ", 0);
        NS_ERROR(log.get());
        NS_ABORT();
      } else if (mCommittedCharsAndModifiers.mChars[0] != aCharMsg.wParam) {
        log.Insert("character mismatch: ", 0);
        NS_ERROR(log.get());
        NS_ABORT();
      }
    }
#endif // #ifdef DEBUG
    return HandleCharMessage(aCharMsg);
  }

  if (IsDeadCharMessage(aCharMsg)) {
    if (!mWidget->PluginHasFocus()) {
      return false;
    }
    return (mWidget->DispatchPluginEvent(aCharMsg) || mWidget->Destroyed());
  }

  bool defaultPrevented = HandleCharMessage(aCharMsg);
  // If a syschar keypress wasn't processed, Windows may want to
  // handle it to activate a native menu.
  if (!defaultPrevented && IsSysCharMessage(aCharMsg)) {
    ::DefWindowProcW(aCharMsg.hwnd, aCharMsg.message,
                     aCharMsg.wParam, aCharMsg.lParam);
  }
  return defaultPrevented;
}

/*****************************************************************************
 * mozilla::widget::KeyboardLayout
 *****************************************************************************/

KeyboardLayout* KeyboardLayout::sInstance = nullptr;
nsIIdleServiceInternal* KeyboardLayout::sIdleService = nullptr;

PRLogModuleInfo* sKeyboardLayoutLogger = nullptr;

// static
KeyboardLayout*
KeyboardLayout::GetInstance()
{
  if (!sInstance) {
    if (!sKeyboardLayoutLogger) {
      sKeyboardLayoutLogger = PR_NewLogModule("KeyboardLayoutWidgets");
    }
    sInstance = new KeyboardLayout();
    nsCOMPtr<nsIIdleServiceInternal> idleService =
      do_GetService("@mozilla.org/widget/idleservice;1");
    // The refcount will be decreased at shut down.
    sIdleService = idleService.forget().take();
  }
  return sInstance;
}

// static
void
KeyboardLayout::Shutdown()
{
  delete sInstance;
  sInstance = nullptr;
  NS_IF_RELEASE(sIdleService);
}

// static
void
KeyboardLayout::NotifyIdleServiceOfUserActivity()
{
  sIdleService->ResetIdleTimeOut(0);
}

KeyboardLayout::KeyboardLayout() :
  mKeyboardLayout(0), mIsOverridden(false),
  mIsPendingToRestoreKeyboardLayout(false)
{
  mDeadKeyTableListHead = nullptr;

  // NOTE: LoadLayout() should be called via OnLayoutChange().
}

KeyboardLayout::~KeyboardLayout()
{
  ReleaseDeadKeyTables();
}

bool
KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey)
{
  return GetKeyIndex(aVirtualKey) >= 0;
}

WORD
KeyboardLayout::ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const
{
  return static_cast<WORD>(
           ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout()));
}

bool
KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
                          const ModifierKeyState& aModKeyState) const
{
  int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
  if (virtualKeyIndex < 0) {
    return false;
  }

  return mVirtualKeys[virtualKeyIndex].IsDeadKey(
           VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
}

void
KeyboardLayout::InitNativeKey(NativeKey& aNativeKey,
                              const ModifierKeyState& aModKeyState)
{
  if (mIsPendingToRestoreKeyboardLayout) {
    LoadLayout(::GetKeyboardLayout(0));
  }

  // If the aNativeKey is initialized with WM_CHAR, the key information
  // should be discarded because mKeyValue should have the string to be
  // inputted.
  if (aNativeKey.mMsg.message == WM_CHAR) {
    aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
    if (aNativeKey.mMsg.wParam) {
      aNativeKey.mCommittedCharsAndModifiers.Append(
        static_cast<char16_t>(aNativeKey.mMsg.wParam),
        aModKeyState.GetModifiers());
    } else {
      aNativeKey.mCommittedCharsAndModifiers.Clear();
    }
    return;
  }

  uint8_t virtualKey = aNativeKey.mOriginalVirtualKeyCode;
  int32_t virtualKeyIndex = GetKeyIndex(virtualKey);

  if (virtualKeyIndex < 0) {
    // Does not produce any printable characters, but still preserves the
    // dead-key state.
    return;
  }

  MOZ_ASSERT(aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING,
    "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");

  bool isKeyDown = aNativeKey.IsKeyDownMessage();
  uint8_t shiftState =
    VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers());

  if (mVirtualKeys[virtualKeyIndex].IsDeadKey(shiftState)) {
    if ((isKeyDown && mActiveDeadKey < 0) ||
        (!isKeyDown && mActiveDeadKey == virtualKey)) {
      //  First dead key event doesn't generate characters.
      if (isKeyDown) {
        // Dead-key state activated at keydown.
        mActiveDeadKey = virtualKey;
        mDeadKeyShiftState = shiftState;
      }
      UniCharsAndModifiers deadChars =
        mVirtualKeys[virtualKeyIndex].GetNativeUniChars(shiftState);
      NS_ASSERTION(deadChars.mLength == 1,
                   "dead key must generate only one character");
      aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_Dead;
      return;
    }

    // Dead key followed by another dead key causes inputting both character.
    // However, at keydown message handling, we need to forget the first
    // dead key because there is no guarantee coming WM_KEYUP for the second
    // dead key before next WM_KEYDOWN.  E.g., due to auto key repeat or
    // pressing another dead key before releasing current key.  Therefore,
    // we can set only a character for current key for keyup event.
    if (mActiveDeadKey < 0) {
      aNativeKey.mCommittedCharsAndModifiers =
        mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
      return;
    }

    int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey);
    if (activeDeadKeyIndex < 0 || activeDeadKeyIndex >= NS_NUM_OF_KEYS) {
#if defined(DEBUG) || defined(MOZ_CRASHREPORTER)
      nsPrintfCString warning("The virtual key index (%d) of mActiveDeadKey "
                              "(0x%02X) is not a printable key (virtualKey="
                              "0x%02X)",
                              activeDeadKeyIndex, mActiveDeadKey, virtualKey);
      NS_WARNING(warning.get());
#ifdef MOZ_CRASHREPORTER
      CrashReporter::AppendAppNotesToCrashReport(
                       NS_LITERAL_CSTRING("\n") + warning);
#endif // #ifdef MOZ_CRASHREPORTER
#endif // #if defined(DEBUG) || defined(MOZ_CRASHREPORTER)
      MOZ_CRASH("Trying to reference out of range of mVirtualKeys");
    }
    UniCharsAndModifiers prevDeadChars =
      mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState);
    UniCharsAndModifiers newChars =
      mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
    // But keypress events should be fired for each committed character.
    aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars;
    if (isKeyDown) {
      DeactivateDeadKeyState();
    }
    return;
  }

  UniCharsAndModifiers baseChars =
    mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
  if (mActiveDeadKey < 0) {
    // No dead-keys are active. Just return the produced characters.
    aNativeKey.mCommittedCharsAndModifiers = baseChars;
    return;
  }

  // Dead-key was active. See if pressed base character does produce
  // valid composite character.
  int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey);
  char16_t compositeChar = (baseChars.mLength == 1 && baseChars.mChars[0]) ?
    mVirtualKeys[activeDeadKeyIndex].GetCompositeChar(mDeadKeyShiftState,
                                                      baseChars.mChars[0]) : 0;
  if (compositeChar) {
    // Active dead-key and base character does produce exactly one
    // composite character.
    aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar,
                                                  baseChars.mModifiers[0]);
    if (isKeyDown) {
      DeactivateDeadKeyState();
    }
    return;
  }

  // There is no valid dead-key and base character combination.
  // Return dead-key character followed by base character.
  UniCharsAndModifiers deadChars =
    mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState);
  // But keypress events should be fired for each committed character.
  aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars;
  if (isKeyDown) {
    DeactivateDeadKeyState();
  }

  return;
}

UniCharsAndModifiers
KeyboardLayout::GetUniCharsAndModifiers(
                  uint8_t aVirtualKey,
                  const ModifierKeyState& aModKeyState) const
{
  UniCharsAndModifiers result;
  int32_t key = GetKeyIndex(aVirtualKey);
  if (key < 0) {
    return result;
  }
  return mVirtualKeys[key].
    GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
}

void
KeyboardLayout::LoadLayout(HKL aLayout)
{
  mIsPendingToRestoreKeyboardLayout = false;

  if (mKeyboardLayout == aLayout) {
    return;
  }

  mKeyboardLayout = aLayout;

  BYTE kbdState[256];
  memset(kbdState, 0, sizeof(kbdState));

  BYTE originalKbdState[256];
  // Bitfield with all shift states that have at least one dead-key.
  uint16_t shiftStatesWithDeadKeys = 0;
  // Bitfield with all shift states that produce any possible dead-key base
  // characters.
  uint16_t shiftStatesWithBaseChars = 0;

  mActiveDeadKey = -1;

  ReleaseDeadKeyTables();

  ::GetKeyboardState(originalKbdState);

  // For each shift state gather all printable characters that are produced
  // for normal case when no any dead-key is active.

  for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
    VirtualKey::FillKbdState(kbdState, shiftState);
    for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
      int32_t vki = GetKeyIndex(virtualKey);
      if (vki < 0) {
        continue;
      }
      NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index");
      char16_t uniChars[5];
      int32_t ret =
        ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
                      ArrayLength(uniChars), 0, mKeyboardLayout);
      // dead-key
      if (ret < 0) {
        shiftStatesWithDeadKeys |= (1 << shiftState);
        // Repeat dead-key to deactivate it and get its character
        // representation.
        char16_t deadChar[2];
        ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar,
                            ArrayLength(deadChar), 0, mKeyboardLayout);
        NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character");
        mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]);
      } else {
        if (ret == 1) {
          // dead-key can pair only with exactly one base character.
          shiftStatesWithBaseChars |= (1 << shiftState);
        }
        mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret);
      }
    }
  }

  // Now process each dead-key to find all its base characters and resulting
  // composite characters.
  for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
    if (!(shiftStatesWithDeadKeys & (1 << shiftState))) {
      continue;
    }

    VirtualKey::FillKbdState(kbdState, shiftState);

    for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
      int32_t vki = GetKeyIndex(virtualKey);
      if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) {
        DeadKeyEntry deadKeyArray[256];
        int32_t n = GetDeadKeyCombinations(virtualKey, kbdState,
                                           shiftStatesWithBaseChars,
                                           deadKeyArray,
                                           ArrayLength(deadKeyArray));
        const DeadKeyTable* dkt =
          mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n);
        if (!dkt) {
          dkt = AddDeadKeyTable(deadKeyArray, n);
        }
        mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt);
      }
    }
  }

  ::SetKeyboardState(originalKbdState);

  if (MOZ_LOG_TEST(sKeyboardLayoutLogger, LogLevel::Debug)) {
    static const UINT kExtendedScanCode[] = { 0x0000, 0xE000 };
    static const UINT kMapType =
      IsVistaOrLater() ? MAPVK_VSC_TO_VK_EX : MAPVK_VSC_TO_VK;
    MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Debug,
           ("Logging virtual keycode values for scancode (0x%p)...",
            mKeyboardLayout));
    for (uint32_t i = 0; i < ArrayLength(kExtendedScanCode); i++) {
      for (uint32_t j = 1; j <= 0xFF; j++) {
        UINT scanCode = kExtendedScanCode[i] + j;
        UINT virtualKeyCode =
          ::MapVirtualKeyEx(scanCode, kMapType, mKeyboardLayout);
        MOZ_LOG(sKeyboardLayoutLogger, LogLevel::Debug,
               ("0x%04X, %s", scanCode, kVirtualKeyName[virtualKeyCode]));
      }
      // XP and Server 2003 don't support 0xE0 prefix of the scancode.
      // Therefore, we don't need to continue on them.
      if (!IsVistaOrLater()) {
        break;
      }
    }
  }
}

inline int32_t
KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey)
{
// Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
// to produce visible representation:
// 0x20 - VK_SPACE          ' '
// 0x30..0x39               '0'..'9'
// 0x41..0x5A               'A'..'Z'
// 0x60..0x69               '0'..'9' on numpad
// 0x6A - VK_MULTIPLY       '*' on numpad
// 0x6B - VK_ADD            '+' on numpad
// 0x6D - VK_SUBTRACT       '-' on numpad
// 0x6E - VK_DECIMAL        '.' on numpad
// 0x6F - VK_DIVIDE         '/' on numpad
// 0x6E - VK_DECIMAL        '.'
// 0xBA - VK_OEM_1          ';:' for US
// 0xBB - VK_OEM_PLUS       '+' any country
// 0xBC - VK_OEM_COMMA      ',' any country
// 0xBD - VK_OEM_MINUS      '-' any country
// 0xBE - VK_OEM_PERIOD     '.' any country
// 0xBF - VK_OEM_2          '/?' for US
// 0xC0 - VK_OEM_3          '`~' for US
// 0xC1 - VK_ABNT_C1        '/?' for Brazilian
// 0xC2 - VK_ABNT_C2        separator key on numpad (Brazilian or JIS for Mac)
// 0xDB - VK_OEM_4          '[{' for US
// 0xDC - VK_OEM_5          '\|' for US
// 0xDD - VK_OEM_6          ']}' for US
// 0xDE - VK_OEM_7          ''"' for US
// 0xDF - VK_OEM_8
// 0xE1 - no name
// 0xE2 - VK_OEM_102        '\_' for JIS
// 0xE3 - no name
// 0xE4 - no name

  static const int8_t xlat[256] =
  {
  // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
  //-----------------------------------------------------------------------
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 00
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 10
     0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 20
     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, -1, -1, -1, -1, -1, -1,   // 30
    -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,   // 40
    26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1,   // 50
    37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51,   // 60
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 70
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 80
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // 90
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // A0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57,   // B0
    58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // C0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65,   // D0
    -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,   // E0
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1    // F0
  };

  return xlat[aVirtualKey];
}

int
KeyboardLayout::CompareDeadKeyEntries(const void* aArg1,
                                      const void* aArg2,
                                      void*)
{
  const DeadKeyEntry* arg1 = static_cast<const DeadKeyEntry*>(aArg1);
  const DeadKeyEntry* arg2 = static_cast<const DeadKeyEntry*>(aArg2);

  return arg1->BaseChar - arg2->BaseChar;
}

const DeadKeyTable*
KeyboardLayout::AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
                                uint32_t aEntries)
{
  DeadKeyTableListEntry* next = mDeadKeyTableListHead;

  const size_t bytes = offsetof(DeadKeyTableListEntry, data) +
    DeadKeyTable::SizeInBytes(aEntries);
  uint8_t* p = new uint8_t[bytes];

  mDeadKeyTableListHead = reinterpret_cast<DeadKeyTableListEntry*>(p);
  mDeadKeyTableListHead->next = next;

  DeadKeyTable* dkt =
    reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data);

  dkt->Init(aDeadKeyArray, aEntries);

  return dkt;
}

void
KeyboardLayout::ReleaseDeadKeyTables()
{
  while (mDeadKeyTableListHead) {
    uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead);
    mDeadKeyTableListHead = mDeadKeyTableListHead->next;

    delete [] p;
  }
}

bool
KeyboardLayout::EnsureDeadKeyActive(bool aIsActive,
                                    uint8_t aDeadKey,
                                    const PBYTE aDeadKeyKbdState)
{
  int32_t ret;
  do {
    char16_t dummyChars[5];
    ret = ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState,
                        (LPWSTR)dummyChars, ArrayLength(dummyChars), 0,
                        mKeyboardLayout);
    // returned values:
    // <0 - Dead key state is active. The keyboard driver will wait for next
    //      character.
    //  1 - Previous pressed key was a valid base character that produced
    //      exactly one composite character.
    // >1 - Previous pressed key does not produce any composite characters.
    //      Return dead-key character followed by base character(s).
  } while ((ret < 0) != aIsActive);

  return (ret < 0);
}

void
KeyboardLayout::DeactivateDeadKeyState()
{
  if (mActiveDeadKey < 0) {
    return;
  }

  BYTE kbdState[256];
  memset(kbdState, 0, sizeof(kbdState));

  VirtualKey::FillKbdState(kbdState, mDeadKeyShiftState);

  EnsureDeadKeyActive(false, mActiveDeadKey, kbdState);
  mActiveDeadKey = -1;
}

bool
KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar,
                                char16_t aCompositeChar,
                                DeadKeyEntry* aDeadKeyArray,
                                uint32_t aEntries)
{
  for (uint32_t index = 0; index < aEntries; index++) {
    if (aDeadKeyArray[index].BaseChar == aBaseChar) {
      return false;
    }
  }

  aDeadKeyArray[aEntries].BaseChar = aBaseChar;
  aDeadKeyArray[aEntries].CompositeChar = aCompositeChar;

  return true;
}

uint32_t
KeyboardLayout::GetDeadKeyCombinations(uint8_t aDeadKey,
                                       const PBYTE aDeadKeyKbdState,
                                       uint16_t aShiftStatesWithBaseChars,
                                       DeadKeyEntry* aDeadKeyArray,
                                       uint32_t aMaxEntries)
{
  bool deadKeyActive = false;
  uint32_t entries = 0;
  BYTE kbdState[256];
  memset(kbdState, 0, sizeof(kbdState));

  for (uint32_t shiftState = 0; shiftState < 16; shiftState++) {
    if (!(aShiftStatesWithBaseChars & (1 << shiftState))) {
      continue;
    }

    VirtualKey::FillKbdState(kbdState, shiftState);

    for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
      int32_t vki = GetKeyIndex(virtualKey);
      // Dead-key can pair only with such key that produces exactly one base
      // character.
      if (vki >= 0 &&
          mVirtualKeys[vki].GetNativeUniChars(shiftState).mLength == 1) {
        // Ensure dead-key is in active state, when it swallows entered
        // character and waits for the next pressed key.
        if (!deadKeyActive) {
          deadKeyActive = EnsureDeadKeyActive(true, aDeadKey,
                                              aDeadKeyKbdState);
        }

        // Depending on the character the followed the dead-key, the keyboard
        // driver can produce one composite character, or a dead-key character
        // followed by a second character.
        char16_t compositeChars[5];
        int32_t ret =
          ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars,
                        ArrayLength(compositeChars), 0, mKeyboardLayout);
        switch (ret) {
          case 0:
            // This key combination does not produce any characters. The
            // dead-key is still in active state.
            break;
          case 1: {
            // Exactly one composite character produced. Now, when dead-key
            // is not active, repeat the last character one more time to
            // determine the base character.
            char16_t baseChars[5];
            ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars,
                                ArrayLength(baseChars), 0, mKeyboardLayout);
            NS_ASSERTION(ret == 1, "One base character expected");
            if (ret == 1 && entries < aMaxEntries &&
                AddDeadKeyEntry(baseChars[0], compositeChars[0],
                                aDeadKeyArray, entries)) {
              entries++;
            }
            deadKeyActive = false;
            break;
          }
          default:
            // 1. Unexpected dead-key. Dead-key chaining is not supported.
            // 2. More than one character generated. This is not a valid
            //    dead-key and base character combination.
            deadKeyActive = false;
            break;
        }
      }
    }
  }

  if (deadKeyActive) {
    deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
  }

  NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry),
               CompareDeadKeyEntries, nullptr);
  return entries;
}

uint32_t
KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const
{
  // Alphabet or Numeric or Numpad or Function keys
  if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) ||
      (aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) ||
      (aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) {
    return static_cast<uint32_t>(aNativeKeyCode);
  }
  switch (aNativeKeyCode) {
    // Following keycodes are same as our DOM keycodes
    case VK_CANCEL:
    case VK_BACK:
    case VK_TAB:
    case VK_CLEAR:
    case VK_RETURN:
    case VK_SHIFT:
    case VK_CONTROL:
    case VK_MENU: // Alt
    case VK_PAUSE:
    case VK_CAPITAL: // CAPS LOCK
    case VK_KANA: // same as VK_HANGUL
    case VK_JUNJA:
    case VK_FINAL:
    case VK_HANJA: // same as VK_KANJI
    case VK_ESCAPE:
    case VK_CONVERT:
    case VK_NONCONVERT:
    case VK_ACCEPT:
    case VK_MODECHANGE:
    case VK_SPACE:
    case VK_PRIOR: // PAGE UP
    case VK_NEXT: // PAGE DOWN
    case VK_END:
    case VK_HOME:
    case VK_LEFT:
    case VK_UP:
    case VK_RIGHT:
    case VK_DOWN:
    case VK_SELECT:
    case VK_PRINT:
    case VK_EXECUTE:
    case VK_SNAPSHOT:
    case VK_INSERT:
    case VK_DELETE:
    case VK_APPS: // Context Menu
    case VK_SLEEP:
    case VK_NUMLOCK:
    case VK_SCROLL: // SCROLL LOCK
    case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400
    case VK_CRSEL: // Cursor Selection
    case VK_EXSEL: // Extend Selection
    case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout
    case VK_PLAY:
    case VK_ZOOM:
    case VK_PA1: // PA1 key of IBM 3270 keyboard layout
      return uint32_t(aNativeKeyCode);

    case VK_HELP:
      return NS_VK_HELP;

    // Windows key should be mapped to a Win keycode
    // They should be able to be distinguished by DOM3 KeyboardEvent.location
    case VK_LWIN:
    case VK_RWIN:
      return NS_VK_WIN;

    case VK_VOLUME_MUTE:
      return NS_VK_VOLUME_MUTE;
    case VK_VOLUME_DOWN:
      return NS_VK_VOLUME_DOWN;
    case VK_VOLUME_UP:
      return NS_VK_VOLUME_UP;

    // Following keycodes are not defined in our DOM keycodes.
    case VK_BROWSER_BACK:
    case VK_BROWSER_FORWARD:
    case VK_BROWSER_REFRESH:
    case VK_BROWSER_STOP:
    case VK_BROWSER_SEARCH:
    case VK_BROWSER_FAVORITES:
    case VK_BROWSER_HOME:
    case VK_MEDIA_NEXT_TRACK:
    case VK_MEDIA_PREV_TRACK:
    case VK_MEDIA_STOP:
    case VK_MEDIA_PLAY_PAUSE:
    case VK_LAUNCH_MAIL:
    case VK_LAUNCH_MEDIA_SELECT:
    case VK_LAUNCH_APP1:
    case VK_LAUNCH_APP2:
      return 0;

    // Following OEM specific virtual keycodes should pass through DOM keyCode
    // for compatibility with the other browsers on Windows.

    // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
    case VK_OEM_FJ_JISHO:
    case VK_OEM_FJ_MASSHOU:
    case VK_OEM_FJ_TOUROKU:
    case VK_OEM_FJ_LOYA:
    case VK_OEM_FJ_ROYA:
    // Not sure what means "ICO".
    case VK_ICO_HELP:
    case VK_ICO_00:
    case VK_ICO_CLEAR:
    // Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
    case VK_OEM_RESET:
    case VK_OEM_JUMP:
    case VK_OEM_PA1:
    case VK_OEM_PA2:
    case VK_OEM_PA3:
    case VK_OEM_WSCTRL:
    case VK_OEM_CUSEL:
    case VK_OEM_ATTN:
    case VK_OEM_FINISH:
    case VK_OEM_COPY:
    case VK_OEM_AUTO:
    case VK_OEM_ENLW:
    case VK_OEM_BACKTAB:
    // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
    // DOM keyCode like other OEM specific virtual keycodes.
    case VK_OEM_CLEAR:
      return uint32_t(aNativeKeyCode);

    // 0xE1 is an OEM specific virtual keycode. However, the value is already
    // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
    // cannot pass through DOM keyCode.
    case 0xE1:
      return 0;

    // Following keycodes are OEM keys which are keycodes for non-alphabet and
    // non-numeric keys, we should compute each keycode of them from unshifted
    // character which is inputted by each key.  But if the unshifted character
    // is not an ASCII character but shifted character is an ASCII character,
    // we should refer it.
    case VK_OEM_1:
    case VK_OEM_PLUS:
    case VK_OEM_COMMA:
    case VK_OEM_MINUS:
    case VK_OEM_PERIOD:
    case VK_OEM_2:
    case VK_OEM_3:
    case VK_OEM_4:
    case VK_OEM_5:
    case VK_OEM_6:
    case VK_OEM_7:
    case VK_OEM_8:
    case VK_OEM_102:
    case VK_ABNT_C1:
    {
      NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode),
                   "The key must be printable");
      ModifierKeyState modKeyState(0);
      UniCharsAndModifiers uniChars =
        GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
      if (uniChars.mLength != 1 ||
          uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) {
        modKeyState.Set(MODIFIER_SHIFT);
        uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
        if (uniChars.mLength != 1 ||
            uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) {
          return 0;
        }
      }
      return WidgetUtils::ComputeKeyCodeFromChar(uniChars.mChars[0]);
    }

    // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2.  However, we're already
    // using NS_VK_SEPARATOR for the separator key on Mac and Linux.  Therefore,
    // We should keep consistency between Gecko on all platforms rather than
    // with other browsers since a lot of keyCode values are already different
    // between browsers.
    case VK_ABNT_C2:
      return NS_VK_SEPARATOR;

    // VK_PROCESSKEY means IME already consumed the key event.
    case VK_PROCESSKEY:
      return 0;
    // VK_PACKET is generated by SendInput() API, we don't need to
    // care this message as key event.
    case VK_PACKET:
      return 0;
    // If a key is not mapped to a virtual keycode, 0xFF is used.
    case 0xFF:
      NS_WARNING("The key is failed to be converted to a virtual keycode");
      return 0;
  }
#ifdef DEBUG
  nsPrintfCString warning("Unknown virtual keycode (0x%08X), please check the "
                          "latest MSDN document, there may be some new "
                          "keycodes we've never known.",
                          aNativeKeyCode);
  NS_WARNING(warning.get());
#endif
  return 0;
}

KeyNameIndex
KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const
{
  if (IsPrintableCharKey(aVirtualKey)) {
    return KEY_NAME_INDEX_USE_STRING;
  }

  switch (aVirtualKey) {

#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
    case aNativeKey: return aKeyNameIndex;

#include "NativeKeyToDOMKeyName.h"

#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX

    default:
      break;
  }

  HKL layout = GetLayout();
  WORD langID = LOWORD(static_cast<HKL>(layout));
  WORD primaryLangID = PRIMARYLANGID(langID);

  if (primaryLangID == LANG_JAPANESE) {
    switch (aVirtualKey) {

#undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
#define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
      case aNativeKey: return aKeyNameIndex;

#include "NativeKeyToDOMKeyName.h"

#undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX

      default:
        break;
    }
  } else if (primaryLangID == LANG_KOREAN) {
    switch (aVirtualKey) {

#undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
#define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
      case aNativeKey: return aKeyNameIndex;

#include "NativeKeyToDOMKeyName.h"

#undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX

      default:
        return KEY_NAME_INDEX_Unidentified;
    }
  }

  switch (aVirtualKey) {

#undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
#define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\
    case aNativeKey: return aKeyNameIndex;

#include "NativeKeyToDOMKeyName.h"

#undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX

    default:
      return KEY_NAME_INDEX_Unidentified;
  }
}

// static
CodeNameIndex
KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode)
{
  switch (aScanCode) {

#define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \
    case aNativeKey: return aCodeNameIndex;

#include "NativeKeyToDOMCodeName.h"

#undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX

    default:
      return CODE_NAME_INDEX_UNKNOWN;
  }
}

nsresult
KeyboardLayout::SynthesizeNativeKeyEvent(nsWindowBase* aWidget,
                                         int32_t aNativeKeyboardLayout,
                                         int32_t aNativeKeyCode,
                                         uint32_t aModifierFlags,
                                         const nsAString& aCharacters,
                                         const nsAString& aUnmodifiedCharacters)
{
  UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, nullptr);
  NS_ASSERTION(keyboardLayoutListCount > 0,
               "One keyboard layout must be installed at least");
  HKL keyboardLayoutListBuff[50];
  HKL* keyboardLayoutList =
    keyboardLayoutListCount < 50 ? keyboardLayoutListBuff :
                                   new HKL[keyboardLayoutListCount];
  keyboardLayoutListCount =
    ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList);
  NS_ASSERTION(keyboardLayoutListCount > 0,
               "Failed to get all keyboard layouts installed on the system");

  nsPrintfCString layoutName("%08x", aNativeKeyboardLayout);
  HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL);
  if (loadedLayout == nullptr) {
    if (keyboardLayoutListBuff != keyboardLayoutList) {
      delete [] keyboardLayoutList;
    }
    return NS_ERROR_NOT_AVAILABLE;
  }

  // Setup clean key state and load desired layout
  BYTE originalKbdState[256];
  ::GetKeyboardState(originalKbdState);
  BYTE kbdState[256];
  memset(kbdState, 0, sizeof(kbdState));
  // This changes the state of the keyboard for the current thread only,
  // and we'll restore it soon, so this should be OK.
  ::SetKeyboardState(kbdState);

  OverrideLayout(loadedLayout);

  uint8_t argumentKeySpecific = 0;
  switch (aNativeKeyCode) {
    case VK_SHIFT:
      aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R);
      argumentKeySpecific = VK_LSHIFT;
      break;
    case VK_LSHIFT:
      aModifierFlags &= ~nsIWidget::SHIFT_L;
      argumentKeySpecific = aNativeKeyCode;
      aNativeKeyCode = VK_SHIFT;
      break;
    case VK_RSHIFT:
      aModifierFlags &= ~nsIWidget::SHIFT_R;
      argumentKeySpecific = aNativeKeyCode;
      aNativeKeyCode = VK_SHIFT;
      break;
    case VK_CONTROL:
      aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R);
      argumentKeySpecific = VK_LCONTROL;
      break;
    case VK_LCONTROL:
      aModifierFlags &= ~nsIWidget::CTRL_L;
      argumentKeySpecific = aNativeKeyCode;
      aNativeKeyCode = VK_CONTROL;
      break;
    case VK_RCONTROL:
      aModifierFlags &= ~nsIWidget::CTRL_R;
      argumentKeySpecific = aNativeKeyCode;
      aNativeKeyCode = VK_CONTROL;
      break;
    case VK_MENU:
      aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R);
      argumentKeySpecific = VK_LMENU;
      break;
    case VK_LMENU:
      aModifierFlags &= ~nsIWidget::ALT_L;
      argumentKeySpecific = aNativeKeyCode;
      aNativeKeyCode = VK_MENU;
      break;
    case VK_RMENU:
      aModifierFlags &= ~nsIWidget::ALT_R;
      argumentKeySpecific = aNativeKeyCode;
      aNativeKeyCode = VK_MENU;
      break;
    case VK_CAPITAL:
      aModifierFlags &= ~nsIWidget::CAPS_LOCK;
      argumentKeySpecific = VK_CAPITAL;
      break;
    case VK_NUMLOCK:
      aModifierFlags &= ~nsIWidget::NUM_LOCK;
      argumentKeySpecific = VK_NUMLOCK;
      break;
  }

  AutoTArray<KeyPair,10> keySequence;
  WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags);
  NS_ASSERTION(aNativeKeyCode >= 0 && aNativeKeyCode < 256,
               "Native VK key code out of range");
  keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));

  // Simulate the pressing of each modifier key and then the real key
  for (uint32_t i = 0; i < keySequence.Length(); ++i) {
    uint8_t key = keySequence[i].mGeneral;
    uint8_t keySpecific = keySequence[i].mSpecific;
    kbdState[key] = 0x81; // key is down and toggled on if appropriate
    if (keySpecific) {
      kbdState[keySpecific] = 0x81;
    }
    ::SetKeyboardState(kbdState);
    ModifierKeyState modKeyState;
    UINT scanCode =
      ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
    LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
    // Add extended key flag to the lParam for right control key and right alt
    // key.
    if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
      lParam |= 0x1000000;
    }
    MSG keyDownMsg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam,
                                       aWidget->GetWindowHandle());
    if (i == keySequence.Length() - 1) {
      bool makeDeadCharMsg =
        (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty());
      nsAutoString chars(aCharacters);
      if (makeDeadCharMsg) {
        UniCharsAndModifiers deadChars =
          GetUniCharsAndModifiers(key, modKeyState);
        chars = deadChars.ToString();
        NS_ASSERTION(chars.Length() == 1,
                     "Dead char must be only one character");
      }
      if (chars.IsEmpty()) {
        NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
        nativeKey.HandleKeyDownMessage();
      } else {
        AutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs;
        for (uint32_t j = 0; j < chars.Length(); j++) {
          NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement();
          fakeCharMsg->mCharCode = chars.CharAt(j);
          fakeCharMsg->mScanCode = scanCode;
          fakeCharMsg->mIsDeadKey = makeDeadCharMsg;
        }
        NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, &fakeCharMsgs);
        bool dispatched;
        nativeKey.HandleKeyDownMessage(&dispatched);
        // If some char messages are not consumed, let's emulate the widget
        // receiving the message directly.
        for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) {
          if (fakeCharMsgs[j].mConsumed) {
            continue;
          }
          MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle());
          NativeKey nativeKey(aWidget, charMsg, modKeyState);
          nativeKey.HandleCharMessage(charMsg);
        }
      }
    } else {
      NativeKey nativeKey(aWidget, keyDownMsg, modKeyState);
      nativeKey.HandleKeyDownMessage();
    }
  }
  for (uint32_t i = keySequence.Length(); i > 0; --i) {
    uint8_t key = keySequence[i - 1].mGeneral;
    uint8_t keySpecific = keySequence[i - 1].mSpecific;
    kbdState[key] = 0; // key is up and toggled off if appropriate
    if (keySpecific) {
      kbdState[keySpecific] = 0;
    }
    ::SetKeyboardState(kbdState);
    ModifierKeyState modKeyState;
    UINT scanCode =
      ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key);
    LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
    // Add extended key flag to the lParam for right control key and right alt
    // key.
    if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
      lParam |= 0x1000000;
    }
    MSG keyUpMsg = WinUtils::InitMSG(WM_KEYUP, key, lParam,
                                     aWidget->GetWindowHandle());
    NativeKey nativeKey(aWidget, keyUpMsg, modKeyState);
    nativeKey.HandleKeyUpMessage();
  }

  // Restore old key state and layout
  ::SetKeyboardState(originalKbdState);
  RestoreLayout();

  // Don't unload the layout if it's installed actually.
  for (uint32_t i = 0; i < keyboardLayoutListCount; i++) {
    if (keyboardLayoutList[i] == loadedLayout) {
      loadedLayout = 0;
      break;
    }
  }
  if (keyboardLayoutListBuff != keyboardLayoutList) {
    delete [] keyboardLayoutList;
  }
  if (loadedLayout) {
    ::UnloadKeyboardLayout(loadedLayout);
  }
  return NS_OK;
}

/*****************************************************************************
 * mozilla::widget::DeadKeyTable
 *****************************************************************************/

char16_t
DeadKeyTable::GetCompositeChar(char16_t aBaseChar) const
{
  // Dead-key table is sorted by BaseChar in ascending order.
  // Usually they are too small to use binary search.

  for (uint32_t index = 0; index < mEntries; index++) {
    if (mTable[index].BaseChar == aBaseChar) {
      return mTable[index].CompositeChar;
    }
    if (mTable[index].BaseChar > aBaseChar) {
      break;
    }
  }

  return 0;
}

/*****************************************************************************
 * mozilla::widget::RedirectedKeyDownMessage
 *****************************************************************************/

MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg;
bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false;

// static
bool
RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG& aMsg)
{
  return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
         (sRedirectedKeyDownMsg.message == aMsg.message &&
          WinUtils::GetScanCode(sRedirectedKeyDownMsg.lParam) ==
            WinUtils::GetScanCode(aMsg.lParam));
}

// static
void
RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd)
{
  MSG msg;
  if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST,
                            PM_NOREMOVE | PM_NOYIELD) &&
      (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
    WinUtils::PeekMessage(&msg, aWnd, msg.message, msg.message,
                          PM_REMOVE | PM_NOYIELD);
  }
}

} // namespace widget
} // namespace mozilla