Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/windows/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 199 kB image not shown  

Quelle  KeyboardLayout.cpp   Sprache: C

 
/* -*- 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/AutoRestore.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/TextEvents.h"
#include "mozilla/widget/WinRegistry.h"

#include "nsExceptionHandler.h"
#include "nsGkAtoms.h"
#include "nsIUserIdleServiceInternal.h"
#include "nsIWindowsRegKey.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsTArray.h"
#include "nsUnicharUtils.h"
#include "nsWindowDbg.h"

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

#include "npapi.h"

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

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

// For collecting other people's log, tell them `MOZ_LOG=KeyboardHandler:4,sync`
// rather than `MOZ_LOG=KeyboardHandler:5,sync` since using `5` may create too
// big file.
// Therefore you shouldn't use `LogLevel::Verbose` for logging usual behavior.
mozilla::LazyLogModule gKeyLog("KeyboardHandler");

namespace mozilla {
namespace widget {

static const charconst 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");

static const char* GetBoolName(bool aBool) { return aBool ? "true" : "false"; }

static const nsCString GetCharacterCodeName(WPARAM aCharCode) {
  switch (aCharCode) {
    case 0x0000:
      return "NULL (0x0000)"_ns;
    case 0x0008:
      return "BACKSPACE (0x0008)"_ns;
    case 0x0009:
      return "CHARACTER TABULATION (0x0009)"_ns;
    case 0x000A:
      return "LINE FEED (0x000A)"_ns;
    case 0x000B:
      return "LINE TABULATION (0x000B)"_ns;
    case 0x000C:
      return "FORM FEED (0x000C)"_ns;
    case 0x000D:
      return "CARRIAGE RETURN (0x000D)"_ns;
    case 0x0018:
      return "CANCEL (0x0018)"_ns;
    case 0x001B:
      return "ESCAPE (0x001B)"_ns;
    case 0x0020:
      return "SPACE (0x0020)"_ns;
    case 0x007F:
      return "DELETE (0x007F)"_ns;
    case 0x00A0:
      return "NO-BREAK SPACE (0x00A0)"_ns;
    case 0x00AD:
      return "SOFT HYPHEN (0x00AD)"_ns;
    case 0x2000:
      return "EN QUAD (0x2000)"_ns;
    case 0x2001:
      return "EM QUAD (0x2001)"_ns;
    case 0x2002:
      return "EN SPACE (0x2002)"_ns;
    case 0x2003:
      return "EM SPACE (0x2003)"_ns;
    case 0x2004:
      return "THREE-PER-EM SPACE (0x2004)"_ns;
    case 0x2005:
      return "FOUR-PER-EM SPACE (0x2005)"_ns;
    case 0x2006:
      return "SIX-PER-EM SPACE (0x2006)"_ns;
    case 0x2007:
      return "FIGURE SPACE (0x2007)"_ns;
    case 0x2008:
      return "PUNCTUATION SPACE (0x2008)"_ns;
    case 0x2009:
      return "THIN SPACE (0x2009)"_ns;
    case 0x200A:
      return "HAIR SPACE (0x200A)"_ns;
    case 0x200B:
      return "ZERO WIDTH SPACE (0x200B)"_ns;
    case 0x200C:
      return "ZERO WIDTH NON-JOINER (0x200C)"_ns;
    case 0x200D:
      return "ZERO WIDTH JOINER (0x200D)"_ns;
    case 0x200E:
      return "LEFT-TO-RIGHT MARK (0x200E)"_ns;
    case 0x200F:
      return "RIGHT-TO-LEFT MARK (0x200F)"_ns;
    case 0x2029:
      return "PARAGRAPH SEPARATOR (0x2029)"_ns;
    case 0x202A:
      return "LEFT-TO-RIGHT EMBEDDING (0x202A)"_ns;
    case 0x202B:
      return "RIGHT-TO-LEFT EMBEDDING (0x202B)"_ns;
    case 0x202D:
      return "LEFT-TO-RIGHT OVERRIDE (0x202D)"_ns;
    case 0x202E:
      return "RIGHT-TO-LEFT OVERRIDE (0x202E)"_ns;
    case 0x202F:
      return "NARROW NO-BREAK SPACE (0x202F)"_ns;
    case 0x205F:
      return "MEDIUM MATHEMATICAL SPACE (0x205F)"_ns;
    case 0x2060:
      return "WORD JOINER (0x2060)"_ns;
    case 0x2066:
      return "LEFT-TO-RIGHT ISOLATE (0x2066)"_ns;
    case 0x2067:
      return "RIGHT-TO-LEFT ISOLATE (0x2067)"_ns;
    case 0x3000:
      return "IDEOGRAPHIC SPACE (0x3000)"_ns;
    case 0xFEFF:
      return "ZERO WIDTH NO-BREAK SPACE (0xFEFF)"_ns;
    default: {
      if (aCharCode < ' ' || (aCharCode >= 0x80 && aCharCode < 0xA0)) {
        return nsPrintfCString("control (0x%04zX)", aCharCode);
      }
      if (NS_IS_HIGH_SURROGATE(aCharCode)) {
        return nsPrintfCString("high surrogate (0x%04zX)", aCharCode);
      }
      if (NS_IS_LOW_SURROGATE(aCharCode)) {
        return nsPrintfCString("low surrogate (0x%04zX)", aCharCode);
      }
      return IS_IN_BMP(aCharCode)
                 ? nsPrintfCString(
                       "'%s' (0x%04zX)",
                       NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
                       aCharCode)
                 : nsPrintfCString(
                       "'%s' (0x%08zX)",
                       NS_ConvertUTF16toUTF8(nsAutoString(aCharCode)).get(),
                       aCharCode);
    }
  }
}

static const nsCString GetKeyLocationName(uint32_t aLocation) {
  switch (aLocation) {
    case eKeyLocationLeft:
      return "KEY_LOCATION_LEFT"_ns;
    case eKeyLocationRight:
      return "KEY_LOCATION_RIGHT"_ns;
    case eKeyLocationStandard:
      return "KEY_LOCATION_STANDARD"_ns;
    case eKeyLocationNumpad:
      return "KEY_LOCATION_NUMPAD"_ns;
    default:
      return nsPrintfCString("Unknown (0x%04X)", aLocation);
  }
}

static const nsCString GetCharacterCodeNames(const char16_t* aChars,
                                             uint32_t aLength) {
  if (!aLength) {
    return ""_ns;
  }
  nsCString result;
  result.AssignLiteral("\"");
  StringJoinAppend(result, ", "_ns, Span{aChars, aLength},
                   [](nsACString& dest, const char16_t charValue) {
                     dest.Append(GetCharacterCodeName(charValue));
                   });
  result.AppendLiteral("\"");
  return result;
}

static const nsCString GetCharacterCodeNames(
    const UniCharsAndModifiers& aUniCharsAndModifiers) {
  if (aUniCharsAndModifiers.IsEmpty()) {
    return ""_ns;
  }
  nsCString result;
  result.AssignLiteral("\"");
  StringJoinAppend(result, ", "_ns, Span{aUniCharsAndModifiers.ToString()},
                   [](nsACString& dest, const char16_t charValue) {
                     dest.Append(GetCharacterCodeName(charValue));
                   });
  result.AppendLiteral("\"");
  return result;
}

class MOZ_STACK_CLASS GetShiftStateName final : public nsAutoCString {
 public:
  explicit GetShiftStateName(VirtualKey::ShiftState aShiftState) {
    if (!aShiftState) {
      AssignLiteral("none");
      return;
    }
    if (aShiftState & VirtualKey::STATE_SHIFT) {
      AssignLiteral("Shift");
      aShiftState &= ~VirtualKey::STATE_SHIFT;
    }
    if (aShiftState & VirtualKey::STATE_CONTROL) {
      MaybeAppendSeparator();
      AssignLiteral("Ctrl");
      aShiftState &= ~VirtualKey::STATE_CONTROL;
    }
    if (aShiftState & VirtualKey::STATE_ALT) {
      MaybeAppendSeparator();
      AssignLiteral("Alt");
      aShiftState &= ~VirtualKey::STATE_ALT;
    }
    if (aShiftState & VirtualKey::STATE_CAPSLOCK) {
      MaybeAppendSeparator();
      AssignLiteral("CapsLock");
      aShiftState &= ~VirtualKey::STATE_CAPSLOCK;
    }
    MOZ_ASSERT(!aShiftState);
  }

 private:
  void MaybeAppendSeparator() {
    if (!IsEmpty()) {
      AppendLiteral(" | ");
    }
  }
};

static const nsCString GetMessageName(UINT aMessage) {
  switch (aMessage) {
    case WM_NULL:
      return "WM_NULL"_ns;
    case WM_KEYDOWN:
      return "WM_KEYDOWN"_ns;
    case WM_KEYUP:
      return "WM_KEYUP"_ns;
    case WM_SYSKEYDOWN:
      return "WM_SYSKEYDOWN"_ns;
    case WM_SYSKEYUP:
      return "WM_SYSKEYUP"_ns;
    case WM_CHAR:
      return "WM_CHAR"_ns;
    case WM_UNICHAR:
      return "WM_UNICHAR"_ns;
    case WM_SYSCHAR:
      return "WM_SYSCHAR"_ns;
    case WM_DEADCHAR:
      return "WM_DEADCHAR"_ns;
    case WM_SYSDEADCHAR:
      return "WM_SYSDEADCHAR"_ns;
    case WM_APPCOMMAND:
      return "WM_APPCOMMAND"_ns;
    case WM_QUIT:
      return "WM_QUIT"_ns;
    default:
      return nsPrintfCString("Unknown Message (0x%04X)", aMessage);
  }
}

static const nsCString GetVirtualKeyCodeName(WPARAM aVK) {
  if (aVK >= std::size(kVirtualKeyName)) {
    return nsPrintfCString("Invalid (0x%08zX)", aVK);
  }
  return nsCString(kVirtualKeyName[aVK]);
}

static const nsCString GetAppCommandName(WPARAM aCommand) {
  switch (aCommand) {
    case APPCOMMAND_BASS_BOOST:
      return "APPCOMMAND_BASS_BOOST"_ns;
    case APPCOMMAND_BASS_DOWN:
      return "APPCOMMAND_BASS_DOWN"_ns;
    case APPCOMMAND_BASS_UP:
      return "APPCOMMAND_BASS_UP"_ns;
    case APPCOMMAND_BROWSER_BACKWARD:
      return "APPCOMMAND_BROWSER_BACKWARD"_ns;
    case APPCOMMAND_BROWSER_FAVORITES:
      return "APPCOMMAND_BROWSER_FAVORITES"_ns;
    case APPCOMMAND_BROWSER_FORWARD:
      return "APPCOMMAND_BROWSER_FORWARD"_ns;
    case APPCOMMAND_BROWSER_HOME:
      return "APPCOMMAND_BROWSER_HOME"_ns;
    case APPCOMMAND_BROWSER_REFRESH:
      return "APPCOMMAND_BROWSER_REFRESH"_ns;
    case APPCOMMAND_BROWSER_SEARCH:
      return "APPCOMMAND_BROWSER_SEARCH"_ns;
    case APPCOMMAND_BROWSER_STOP:
      return "APPCOMMAND_BROWSER_STOP"_ns;
    case APPCOMMAND_CLOSE:
      return "APPCOMMAND_CLOSE"_ns;
    case APPCOMMAND_COPY:
      return "APPCOMMAND_COPY"_ns;
    case APPCOMMAND_CORRECTION_LIST:
      return "APPCOMMAND_CORRECTION_LIST"_ns;
    case APPCOMMAND_CUT:
      return "APPCOMMAND_CUT"_ns;
    case APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE:
      return "APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE"_ns;
    case APPCOMMAND_FIND:
      return "APPCOMMAND_FIND"_ns;
    case APPCOMMAND_FORWARD_MAIL:
      return "APPCOMMAND_FORWARD_MAIL"_ns;
    case APPCOMMAND_HELP:
      return "APPCOMMAND_HELP"_ns;
    case APPCOMMAND_LAUNCH_APP1:
      return "APPCOMMAND_LAUNCH_APP1"_ns;
    case APPCOMMAND_LAUNCH_APP2:
      return "APPCOMMAND_LAUNCH_APP2"_ns;
    case APPCOMMAND_LAUNCH_MAIL:
      return "APPCOMMAND_LAUNCH_MAIL"_ns;
    case APPCOMMAND_LAUNCH_MEDIA_SELECT:
      return "APPCOMMAND_LAUNCH_MEDIA_SELECT"_ns;
    case APPCOMMAND_MEDIA_CHANNEL_DOWN:
      return "APPCOMMAND_MEDIA_CHANNEL_DOWN"_ns;
    case APPCOMMAND_MEDIA_CHANNEL_UP:
      return "APPCOMMAND_MEDIA_CHANNEL_UP"_ns;
    case APPCOMMAND_MEDIA_FAST_FORWARD:
      return "APPCOMMAND_MEDIA_FAST_FORWARD"_ns;
    case APPCOMMAND_MEDIA_NEXTTRACK:
      return "APPCOMMAND_MEDIA_NEXTTRACK"_ns;
    case APPCOMMAND_MEDIA_PAUSE:
      return "APPCOMMAND_MEDIA_PAUSE"_ns;
    case APPCOMMAND_MEDIA_PLAY:
      return "APPCOMMAND_MEDIA_PLAY"_ns;
    case APPCOMMAND_MEDIA_PLAY_PAUSE:
      return "APPCOMMAND_MEDIA_PLAY_PAUSE"_ns;
    case APPCOMMAND_MEDIA_PREVIOUSTRACK:
      return "APPCOMMAND_MEDIA_PREVIOUSTRACK"_ns;
    case APPCOMMAND_MEDIA_RECORD:
      return "APPCOMMAND_MEDIA_RECORD"_ns;
    case APPCOMMAND_MEDIA_REWIND:
      return "APPCOMMAND_MEDIA_REWIND"_ns;
    case APPCOMMAND_MEDIA_STOP:
      return "APPCOMMAND_MEDIA_STOP"_ns;
    case APPCOMMAND_MIC_ON_OFF_TOGGLE:
      return "APPCOMMAND_MIC_ON_OFF_TOGGLE"_ns;
    case APPCOMMAND_MICROPHONE_VOLUME_DOWN:
      return "APPCOMMAND_MICROPHONE_VOLUME_DOWN"_ns;
    case APPCOMMAND_MICROPHONE_VOLUME_MUTE:
      return "APPCOMMAND_MICROPHONE_VOLUME_MUTE"_ns;
    case APPCOMMAND_MICROPHONE_VOLUME_UP:
      return "APPCOMMAND_MICROPHONE_VOLUME_UP"_ns;
    case APPCOMMAND_NEW:
      return "APPCOMMAND_NEW"_ns;
    case APPCOMMAND_OPEN:
      return "APPCOMMAND_OPEN"_ns;
    case APPCOMMAND_PASTE:
      return "APPCOMMAND_PASTE"_ns;
    case APPCOMMAND_PRINT:
      return "APPCOMMAND_PRINT"_ns;
    case APPCOMMAND_REDO:
      return "APPCOMMAND_REDO"_ns;
    case APPCOMMAND_REPLY_TO_MAIL:
      return "APPCOMMAND_REPLY_TO_MAIL"_ns;
    case APPCOMMAND_SAVE:
      return "APPCOMMAND_SAVE"_ns;
    case APPCOMMAND_SEND_MAIL:
      return "APPCOMMAND_SEND_MAIL"_ns;
    case APPCOMMAND_SPELL_CHECK:
      return "APPCOMMAND_SPELL_CHECK"_ns;
    case APPCOMMAND_TREBLE_DOWN:
      return "APPCOMMAND_TREBLE_DOWN"_ns;
    case APPCOMMAND_TREBLE_UP:
      return "APPCOMMAND_TREBLE_UP"_ns;
    case APPCOMMAND_UNDO:
      return "APPCOMMAND_UNDO"_ns;
    case APPCOMMAND_VOLUME_DOWN:
      return "APPCOMMAND_VOLUME_DOWN"_ns;
    case APPCOMMAND_VOLUME_MUTE:
      return "APPCOMMAND_VOLUME_MUTE"_ns;
    case APPCOMMAND_VOLUME_UP:
      return "APPCOMMAND_VOLUME_UP"_ns;
    default:
      return nsPrintfCString("Unknown app command (0x%08zX)", aCommand);
  }
}

static const nsCString GetAppCommandDeviceName(LPARAM aDevice) {
  switch (aDevice) {
    case FAPPCOMMAND_KEY:
      return "FAPPCOMMAND_KEY"_ns;
    case FAPPCOMMAND_MOUSE:
      return "FAPPCOMMAND_MOUSE"_ns;
    case FAPPCOMMAND_OEM:
      return "FAPPCOMMAND_OEM"_ns;
    default:
      return nsPrintfCString("Unknown app command device (0x%04" PRIXLPTR ")",
                             aDevice);
  }
};

class MOZ_STACK_CLASS GetAppCommandKeysName final : public nsAutoCString {
 public:
  explicit GetAppCommandKeysName(WPARAM aKeys) {
    if (aKeys & MK_CONTROL) {
      AppendLiteral("MK_CONTROL");
      aKeys &= ~MK_CONTROL;
    }
    if (aKeys & MK_LBUTTON) {
      MaybeAppendSeparator();
      AppendLiteral("MK_LBUTTON");
      aKeys &= ~MK_LBUTTON;
    }
    if (aKeys & MK_MBUTTON) {
      MaybeAppendSeparator();
      AppendLiteral("MK_MBUTTON");
      aKeys &= ~MK_MBUTTON;
    }
    if (aKeys & MK_RBUTTON) {
      MaybeAppendSeparator();
      AppendLiteral("MK_RBUTTON");
      aKeys &= ~MK_RBUTTON;
    }
    if (aKeys & MK_SHIFT) {
      MaybeAppendSeparator();
      AppendLiteral("MK_SHIFT");
      aKeys &= ~MK_SHIFT;
    }
    if (aKeys & MK_XBUTTON1) {
      MaybeAppendSeparator();
      AppendLiteral("MK_XBUTTON1");
      aKeys &= ~MK_XBUTTON1;
    }
    if (aKeys & MK_XBUTTON2) {
      MaybeAppendSeparator();
      AppendLiteral("MK_XBUTTON2");
      aKeys &= ~MK_XBUTTON2;
    }
    if (aKeys) {
      MaybeAppendSeparator();
      AppendPrintf("Unknown Flags (0x%04zX)", aKeys);
    }
    if (IsEmpty()) {
      AssignLiteral("none (0x0000)");
    }
  }

 private:
  void MaybeAppendSeparator() {
    if (!IsEmpty()) {
      AppendLiteral(" | ");
    }
  }
};

static const nsCString ToString(const MSG& aMSG) {
  nsCString result;
  result.AssignLiteral("{ message=");
  result.Append(GetMessageName(aMSG.message).get());
  result.AppendLiteral(", ");
  switch (aMSG.message) {
    case WM_KEYDOWN:
    case WM_KEYUP:
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
      result.AppendPrintf(
          "virtual keycode=%s, repeat count=%" PRIdLPTR
          ", "
          "scancode=0x%02X, extended key=%s, "
          "context code=%s, previous key state=%s, "
          "transition state=%s",
          GetVirtualKeyCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
          WinUtils::GetScanCode(aMSG.lParam),
          GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
          GetBoolName((aMSG.lParam & (1 << 29)) != 0),
          GetBoolName((aMSG.lParam & (1 << 30)) != 0),
          GetBoolName((aMSG.lParam & (1 << 31)) != 0));
      break;
    case WM_CHAR:
    case WM_DEADCHAR:
    case WM_SYSCHAR:
    case WM_SYSDEADCHAR:
      result.AppendPrintf(
          "character code=%s, repeat count=%" PRIdLPTR
          ", "
          "scancode=0x%02X, extended key=%s, "
          "context code=%s, previous key state=%s, "
          "transition state=%s",
          GetCharacterCodeName(aMSG.wParam).get(), aMSG.lParam & 0xFFFF,
          WinUtils::GetScanCode(aMSG.lParam),
          GetBoolName(WinUtils::IsExtendedScanCode(aMSG.lParam)),
          GetBoolName((aMSG.lParam & (1 << 29)) != 0),
          GetBoolName((aMSG.lParam & (1 << 30)) != 0),
          GetBoolName((aMSG.lParam & (1 << 31)) != 0));
      break;
    case WM_APPCOMMAND:
      result.AppendPrintf(
          "window handle=0x%zx, app command=%s, device=%s, dwKeys=%s",
          aMSG.wParam,
          GetAppCommandName(GET_APPCOMMAND_LPARAM(aMSG.lParam)).get(),
          GetAppCommandDeviceName(GET_DEVICE_LPARAM(aMSG.lParam)).get(),
          GetAppCommandKeysName(GET_KEYSTATE_LPARAM(aMSG.lParam)).get());
      break;
    default:
      result.AppendPrintf("wParam=%zu, lParam=%" PRIdLPTR, aMSG.wParam,
                          aMSG.lParam);
      break;
  }
  result.AppendPrintf(", hwnd=0x%p", aMSG.hwnd);
  return result;
}

static const nsCString ToString(
    const UniCharsAndModifiers& aUniCharsAndModifiers) {
  if (aUniCharsAndModifiers.IsEmpty()) {
    return "{}"_ns;
  }
  nsCString result;
  result.AssignLiteral("{ ");
  result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(0)));
  for (size_t i = 1; i < aUniCharsAndModifiers.Length(); ++i) {
    if (aUniCharsAndModifiers.ModifiersAt(i - 1) !=
        aUniCharsAndModifiers.ModifiersAt(i)) {
      result.AppendLiteral(" [");
      result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(0)));
      result.AppendLiteral("]");
    }
    result.AppendLiteral(", ");
    result.Append(GetCharacterCodeName(aUniCharsAndModifiers.CharAt(i)));
  }
  result.AppendLiteral(" [");
  uint32_t lastIndex = aUniCharsAndModifiers.Length() - 1;
  result.Append(GetModifiersName(aUniCharsAndModifiers.ModifiersAt(lastIndex)));
  result.AppendLiteral("] }");
  return result;
}

const nsCString ToString(const ModifierKeyState& aModifierKeyState) {
  nsCString result;
  result.AssignLiteral("{ ");
  result.Append(GetModifiersName(aModifierKeyState.GetModifiers()).get());
  result.AppendLiteral(" }");
  return result;
}

// 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;

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


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

ModifierKeyState::ModifierKeyState(Modifiers aModifiers)
    : mModifiers(aModifiers) {
  MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
             "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
             "if MODIFIER_ALTGRAPH is set");
}

void ModifierKeyState::Update() {
  mModifiers = 0;
  if (IS_VK_DOWN(VK_SHIFT)) {
    mModifiers |= MODIFIER_SHIFT;
  }
  // If AltGr key (i.e., VK_RMENU on some keyboard layout) is pressed, only
  // MODIFIER_ALTGRAPH should be set.  Otherwise, i.e., if both Ctrl and Alt
  // keys are pressed to emulate AltGr key, MODIFIER_CONTROL and MODIFIER_ALT
  // keys should be set separately.
  if (IS_VK_DOWN(VK_RMENU) && KeyboardLayout::GetInstance()->HasAltGr()) {
    mModifiers |= MODIFIER_ALTGRAPH;
  } else {
    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_META;
  }
  if (::GetKeyState(VK_CAPITAL) & 1) {
    mModifiers |= MODIFIER_CAPSLOCK;
  }
  if (::GetKeyState(VK_NUMLOCK) & 1) {
    mModifiers |= MODIFIER_NUMLOCK;
  }
  if (::GetKeyState(VK_SCROLL) & 1) {
    mModifiers |= MODIFIER_SCROLLLOCK;
  }
}

void ModifierKeyState::Unset(Modifiers aRemovingModifiers) {
  mModifiers &= ~aRemovingModifiers;
}

void ModifierKeyState::Set(Modifiers aAddingModifiers) {
  mModifiers |= aAddingModifiers;
  MOZ_ASSERT(!(mModifiers & MODIFIER_ALTGRAPH) || (!IsControl() && !IsAlt()),
             "Neither MODIFIER_CONTROL nor MODIFIER_ALT should be set "
             "if MODIFIER_ALTGRAPH is set");
}

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

  switch (aInputEvent.mClass) {
    case eMouseEventClass:
    case ePointerEventClass:
    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 == ePointerEventClass ||
                   aMouseEvent.mClass == eWheelEventClass ||
                   aMouseEvent.mClass == eDragEventClass ||
                   aMouseEvent.mClass == eSimpleGestureEventClass,
               "called with non-mouse event");

  WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
  mouseEvent.mButtons = 0;
  if (::GetKeyState(VK_LBUTTON) < 0) {
    mouseEvent.mButtons |= MouseButtonsFlag::ePrimaryFlag;
  }
  if (::GetKeyState(VK_RBUTTON) < 0) {
    mouseEvent.mButtons |= MouseButtonsFlag::eSecondaryFlag;
  }
  if (::GetKeyState(VK_MBUTTON) < 0) {
    mouseEvent.mButtons |= MouseButtonsFlag::eMiddleFlag;
  }
  if (::GetKeyState(VK_XBUTTON1) < 0) {
    mouseEvent.mButtons |= MouseButtonsFlag::e4thFlag;
  }
  if (::GetKeyState(VK_XBUTTON2) < 0) {
    mouseEvent.mButtons |= MouseButtonsFlag::e5thFlag;
  }
}

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::IsWin() const {
  return (mModifiers & MODIFIER_META) != 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;
}

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


void UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers) {
  mChars.Append(aUniChar);
  mModifiers.AppendElement(aModifiers);
}

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

void UniCharsAndModifiers::OverwriteModifiersIfBeginsWith(
    const UniCharsAndModifiers& aOther) {
  if (!BeginsWith(aOther)) {
    return;
  }
  for (size_t i = 0; i < aOther.Length(); ++i) {
    mModifiers[i] = aOther.mModifiers[i];
  }
}

bool UniCharsAndModifiers::UniCharsEqual(
    const UniCharsAndModifiers& aOther) const {
  return mChars.Equals(aOther.mChars);
}

bool UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
    const UniCharsAndModifiers& aOther) const {
  return mChars.Equals(aOther.mChars, nsCaseInsensitiveStringComparator);
}

bool UniCharsAndModifiers::BeginsWith(
    const UniCharsAndModifiers& aOther) const {
  return StringBeginsWith(mChars, aOther.mChars);
}

UniCharsAndModifiers& UniCharsAndModifiers::operator+=(
    const UniCharsAndModifiers& aOther) {
  mChars.Append(aOther.mChars);
  mModifiers.AppendElements(aOther.mModifiers);
  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_ALTGRAPH) {
    state |= STATE_ALTGRAPH;
  } else {
    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_ALTGRAPH) {
    modifiers |= MODIFIER_ALTGRAPH;
  } else {
    if (aShiftState & STATE_CONTROL) {
      modifiers |= MODIFIER_CONTROL;
    }
    if (aShiftState & STATE_ALT) {
      modifiers |= MODIFIER_ALT;
    }
  }
  if (aShiftState & STATE_CAPSLOCK) {
    modifiers |= MODIFIER_CAPSLOCK;
  }
  return modifiers;
}

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) {
  MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));

  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 = std::size(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) {
  MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));

  SetDeadKey(aShiftState, true);

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

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

  const uint8_t kShiftStateIndex = ToShiftStateIndex(aShiftState);
  if (!(kShiftStateIndex & STATE_CONTROL_ALT)) {
    // If neither Alt nor Ctrl key is pressed, just return stored data
    // for the key.
    return result;
  }

  if (result.IsEmpty()) {
    // If Alt and/or Control are pressed and the key produces no
    // character, return characters which is produced by the key without
    // Alt and Control, and return given modifiers as is.
    result = GetNativeUniChars(kShiftStateIndex & ~STATE_CONTROL_ALT);
    result.FillModifiers(ShiftStateToModifiers(aShiftState));
    return result;
  }

  if (IsAltGrIndex(kShiftStateIndex)) {
    // If AltGr or both Ctrl and Alt are pressed and the key produces
    // character(s), we need to clear MODIFIER_ALT and MODIFIER_CONTROL
    // since TextEditor won't handle eKeyPress event whose mModifiers
    // has MODIFIER_ALT or MODIFIER_CONTROL.  Additionally, we need to
    // use MODIFIER_ALTGRAPH when a key produces character(s) with
    // AltGr or both Ctrl and Alt on Windows.  See following spec issue:
    // <https://github.com/w3c/uievents/issues/147>
    Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
    finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
    finalModifiers |= MODIFIER_ALTGRAPH;
    result.FillModifiers(finalModifiers);
    return result;
  }

  // Otherwise, i.e., Alt or Ctrl is pressed and it produces character(s),
  // check if different character(s) is produced by the key without Alt/Ctrl.
  // If it produces different character, we need to consume the Alt and
  // Ctrl modifier for TextEditor.  Otherwise, the key does not produces the
  // character actually.  So, keep setting Alt and Ctrl modifiers.
  UniCharsAndModifiers unmodifiedReslt =
      GetNativeUniChars(kShiftStateIndex & ~STATE_CONTROL_ALT);
  if (!result.UniCharsEqual(unmodifiedReslt)) {
    Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
    finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
    result.FillModifiers(finalModifiers);
  }
  return result;
}

UniCharsAndModifiers VirtualKey::GetNativeUniChars(
    ShiftState aShiftState) const {
  const uint8_t kShiftStateIndex = ToShiftStateIndex(aShiftState);
  UniCharsAndModifiers result;
  Modifiers modifiers = ShiftStateToModifiers(aShiftState);
  if (IsDeadKey(aShiftState)) {
    result.Append(mShiftStates[kShiftStateIndex].DeadKey.DeadChar, modifiers);
    return result;
  }

  uint32_t len = std::size(mShiftStates[kShiftStateIndex].Normal.Chars);
  for (uint32_t i = 0;
       i < len && mShiftStates[kShiftStateIndex].Normal.Chars[i]; i++) {
    result.Append(mShiftStates[kShiftStateIndex].Normal.Chars[i], modifiers);
  }
  return result;
}

// static
void VirtualKey::FillKbdState(PBYTE aKbdState, const ShiftState aShiftState) {
  if (aShiftState & STATE_SHIFT) {
    aKbdState[VK_SHIFT] |= 0x80;
  } else {
    aKbdState[VK_SHIFT] &= ~0x80;
    aKbdState[VK_LSHIFT] &= ~0x80;
    aKbdState[VK_RSHIFT] &= ~0x80;
  }

  if (aShiftState & STATE_ALTGRAPH) {
    aKbdState[VK_CONTROL] |= 0x80;
    aKbdState[VK_LCONTROL] |= 0x80;
    aKbdState[VK_RCONTROL] &= ~0x80;
    aKbdState[VK_MENU] |= 0x80;
    aKbdState[VK_LMENU] &= ~0x80;
    aKbdState[VK_RMENU] |= 0x80;
  } else {
    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::sLatestInstance = nullptr;
const MSG NativeKey::sEmptyMSG = {};
MSG NativeKey::sLastKeyOrCharMSG = {};
MSG NativeKey::sLastKeyMSG = {};
char16_t NativeKey::sPendingHighSurrogate = 0;

NativeKey::NativeKey(nsWindow* aWidget, const MSG& aMessage,
                     const ModifierKeyState& aModKeyState,
                     HKL aOverrideKeyboardLayout,
                     nsTArray<FakeCharMsg>* aFakeCharMsgs)
    : mLastInstance(sLatestInstance),
      mRemovingMsg(sEmptyMSG),
      mReceivedMsg(sEmptyMSG),
      mWidget(aWidget),
      mDispatcher(aWidget->GetTextEventDispatcher()),
      mMsg(aMessage),
      mFocusedWndBeforeDispatch(::GetFocus()),
      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),
      mIsRepeat(false),
      mIsDeadKey(false),
      mIsPrintableKey(false),
      mIsSkippableInRemoteProcess(false),
      mCharMessageHasGone(false),
      mCanIgnoreModifierStateAtKeyPress(true),
      mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ? aFakeCharMsgs
                                                             : nullptr) {
  MOZ_LOG(gKeyLog, LogLevel::Info,
          ("%p NativeKey::NativeKey(aWidget=0x%p { GetWindowHandle()=0x%p }, "
           "aMessage=%s, aModKeyState=%s), sLatestInstance=0x%p",
           this, aWidget, aWidget->GetWindowHandle(), ToString(aMessage).get(),
           ToString(aModKeyState).get(), sLatestInstance));

  MOZ_ASSERT(aWidget);
  MOZ_ASSERT(mDispatcher);
  sLatestInstance = this;
  KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
  mKeyboardLayout = KeyboardLayout::GetLayout();
  if (aOverrideKeyboardLayout && mKeyboardLayout != aOverrideKeyboardLayout) {
    keyboardLayout->OverrideLayout(aOverrideKeyboardLayout);
    mKeyboardLayout = keyboardLayout->GetLoadedLayout();
    MOZ_ASSERT(mKeyboardLayout == aOverrideKeyboardLayout);
    mIsOverridingKeyboardLayout = true;
  } else {
    mIsOverridingKeyboardLayout = false;
    sLastKeyOrCharMSG = aMessage;
  }

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

  MOZ_LOG(gKeyLog, LogLevel::Info,
          ("%p NativeKey::NativeKey(), mKeyboardLayout=0x%p, "
           "mFocusedWndBeforeDispatch=0x%p, mDOMKeyCode=%s, "
           "mKeyNameIndex=%s, mCodeNameIndex=%s, mModKeyState=%s, "
           "mVirtualKeyCode=%s, mOriginalVirtualKeyCode=%s, "
           "mCommittedCharsAndModifiers=%s, mInputtingStringAndModifiers=%s, "
           "mShiftedString=%s, mUnshiftedString=%s, mShiftedLatinChar=%s, "
           "mUnshiftedLatinChar=%s, mScanCode=0x%04X, mIsExtended=%s, "
           "mIsRepeat=%s, mIsDeadKey=%s, mIsPrintableKey=%s, "
           "mIsSkippableInRemoteProcess=%s, mCharMessageHasGone=%s, "
           "mIsOverridingKeyboardLayout=%s",
           this, mKeyboardLayout, mFocusedWndBeforeDispatch,
           GetDOMKeyCodeName(mDOMKeyCode).get(), ToString(mKeyNameIndex).get(),
           ToString(mCodeNameIndex).get(), ToString(mModKeyState).get(),
           GetVirtualKeyCodeName(mVirtualKeyCode).get(),
           GetVirtualKeyCodeName(mOriginalVirtualKeyCode).get(),
           ToString(mCommittedCharsAndModifiers).get(),
           ToString(mInputtingStringAndModifiers).get(),
           ToString(mShiftedString).get(), ToString(mUnshiftedString).get(),
           GetCharacterCodeName(mShiftedLatinChar).get(),
           GetCharacterCodeName(mUnshiftedLatinChar).get(), mScanCode,
           GetBoolName(mIsExtended), GetBoolName(mIsRepeat),
           GetBoolName(mIsDeadKey), GetBoolName(mIsPrintableKey),
           GetBoolName(mIsSkippableInRemoteProcess),
           GetBoolName(mCharMessageHasGone),
           GetBoolName(mIsOverridingKeyboardLayout)));
}

void NativeKey::InitIsSkippableForKeyOrChar(const MSG& aLastKeyMSG) {
  mIsSkippableInRemoteProcess = false;

  if (!mIsRepeat) {
    // If the message is not repeated key message, the event should be always
    // handled in remote process even if it's too old.
    return;
  }

  // Keyboard utilities may send us some generated messages and such messages
  // may be marked as "repeated", e.g., SendInput() calls with
  // KEYEVENTF_UNICODE but without KEYEVENTF_KEYUP.   However, key sequence
  // comes from such utilities may be really important.  For example, utilities
  // may send WM_KEYDOWN for VK_BACK to remove previous character and send
  // WM_KEYDOWN for VK_PACKET to insert a composite character.  Therefore, we
  // should check if current message and previous key message are caused by
  // same physical key.  If not, the message may be generated by such
  // utility.
  // XXX With this approach, if VK_BACK messages are generated with known
  //     scancode, we cannot distinguish whether coming VK_BACK message is
  //     actually repeated by the auto-repeat feature.  Currently, we need
  //     this hack only for "SinhalaTamil IME" and fortunately, it generates
  //     VK_BACK messages with odd scancode.  So, we don't need to handle
  //     VK_BACK specially at least for now.

  if (mCodeNameIndex == CODE_NAME_INDEX_UNKNOWN) {
    // If current event is not caused by physical key operation, it may be
    // caused by a keyboard utility.  If so, the event shouldn't be ignored by
    // BrowserChild since it want to insert the character, delete a character or
    // move caret.
    return;
  }

  if (mOriginalVirtualKeyCode == VK_PACKET) {
    // If the message is VK_PACKET, that means that a keyboard utility
    // tries to insert a character.
    return;
  }

  switch (mMsg.message) {
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
    case WM_CHAR:
    case WM_SYSCHAR:
    case WM_DEADCHAR:
    case WM_SYSDEADCHAR:
      // However, some keyboard layouts may send some keyboard messages with
      // activating the bit.  If we dispatch repeated keyboard events, they
      // may be ignored by BrowserChild due to performance reason.  So, we need
      // to check if actually a physical key is repeated by the auto-repeat
      // feature.
      switch (aLastKeyMSG.message) {
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
          if (aLastKeyMSG.wParam == VK_PACKET) {
            // If the last message was VK_PACKET, that means that a keyboard
            // utility tried to insert a character.  So, current message is
            // not repeated key event of the previous event.
            return;
          }
          // Let's check whether current message and previous message are
          // caused by same physical key.
          mIsSkippableInRemoteProcess =
              mScanCode == WinUtils::GetScanCode(aLastKeyMSG.lParam) &&
              mIsExtended == WinUtils::IsExtendedScanCode(aLastKeyMSG.lParam);
          return;
        default:
          // If previous message is not a keydown, this must not be generated
          // by the auto-repeat feature.
          return;
      }
    case WM_APPCOMMAND:
      MOZ_ASSERT_UNREACHABLE(
          "WM_APPCOMMAND should be handled in "
          "InitWithAppCommand()");
      return;
    default:
      // keyup message shouldn't be repeated by the auto-repeat feature.
      return;
  }
}

void NativeKey::InitWithKeyOrChar() {
  MSG lastKeyMSG = sLastKeyMSG;
  char16_t pendingHighSurrogate = sPendingHighSurrogate;
  mScanCode = WinUtils::GetScanCode(mMsg.lParam);
  mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam);
  switch (mMsg.message) {
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
      sPendingHighSurrogate = 0;
      [[fallthrough]];
    case WM_KEYUP:
    case WM_SYSKEYUP: {
      // Modify sLastKeyMSG now since retrieving following char messages may
      // cause sending another key message if odd tool hooks GetMessage(),
      // PeekMessage().
      sLastKeyMSG = mMsg;

      // Note that we don't need to compute raw virtual keycode here even when
      // it's VK_PROCESS (i.e., already handled by IME) because we need to
      // export it as DOM_VK_PROCESS and KEY_NAME_INDEX_Process.
      mOriginalVirtualKeyCode = static_cast<uint8_t>(mMsg.wParam);

      // 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 && mOriginalVirtualKeyCode != VK_PACKET) {
        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);
        }
      }

      // 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;
      }

      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 an extended key,
            // let's use VK_LSHIFT for unknown mapping.
            mVirtualKeyCode = VK_LSHIFT;
          }
          break;
        default:
          MOZ_CRASH("Unsupported mOriginalVirtualKeyCode");
      }
      break;
    }
    case WM_CHAR:
    case WM_UNICHAR:
    case WM_SYSCHAR:
      sPendingHighSurrogate = 0;
      // If there is another instance and it is trying to remove a char message
      // from the queue, this message should be handled in the old instance.
      if (IsAnotherInstanceRemovingCharMessage()) {
        // XXX Do we need to make mReceivedMsg an array?
        MOZ_ASSERT(IsEmptyMSG(mLastInstance->mReceivedMsg));
        MOZ_LOG(
            gKeyLog, LogLevel::Warning,
            ("%p NativeKey::InitWithKeyOrChar(), WARNING, detecting another "
             "instance is trying to remove a char message, so, this instance "
             "should do nothing, mLastInstance=0x%p, mRemovingMsg=%s, "
             "mReceivedMsg=%s",
             this, mLastInstance, ToString(mLastInstance->mRemovingMsg).get(),
             ToString(mLastInstance->mReceivedMsg).get()));
        mLastInstance->mReceivedMsg = mMsg;
        return;
      }

      // 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.

      mVirtualKeyCode = mOriginalVirtualKeyCode =
          ComputeVirtualKeyCodeFromScanCodeEx();
      NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode");
      break;
    default: {
      MOZ_CRASH_UNSAFE_PRINTF("Unsupported message: 0x%04X", mMsg.message);
      break;
    }
  }

  if (!mVirtualKeyCode) {
    mVirtualKeyCode = mOriginalVirtualKeyCode;
  }

  KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
  mDOMKeyCode =
      keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mVirtualKeyCode);
  // Be aware, keyboard utilities can change non-printable keys to printable
  // keys.  In such case, we should make the key value as a printable key.
  // FYI: IsFollowedByPrintableCharMessage() returns true only when it's
  //      handling a keydown message.
  mKeyNameIndex =
      IsFollowedByPrintableCharMessage()
          ? KEY_NAME_INDEX_USE_STRING
          : keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mVirtualKeyCode);
  mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex(
      GetScanCodeWithExtendedFlag());

  // If next message of WM_(SYS)KEYDOWN is WM_*CHAR message and the key
  // combination is not reserved by the system, let's consume it now.
  // TODO: We cannot initialize mCommittedCharsAndModifiers for VK_PACKET
  //       if the message is WM_KEYUP because we don't have preceding
  //       WM_CHAR message.
  // TODO: Like Edge, we shouldn't dispatch two sets of keyboard events
  //       for a Unicode character in non-BMP because its key value looks
  //       broken and not good thing for our editor if only one keydown or
  //       keypress event's default is prevented.  I guess, we should store
  //       key message information globally and we should wait following
  //       WM_KEYDOWN if following WM_CHAR is a part of a Unicode character.
  if ((mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN) &&
      !IsReservedBySystem()) {
    MSG charMsg;
    while (GetFollowingCharMessage(charMsg)) {
      // Although, got message shouldn't be WM_NULL in desktop apps,
      // we should keep checking this.  FYI: This was added for Metrofox.
      if (charMsg.message == WM_NULL) {
        continue;
      }
      MOZ_LOG(gKeyLog, LogLevel::Info,
              ("%p NativeKey::InitWithKeyOrChar(), removed char message, %s",
               this, ToString(charMsg).get()));
      NS_WARNING_ASSERTION(
          charMsg.hwnd == mMsg.hwnd,
          "The retrieved char message was targeted to differnet window");
      mFollowingCharMsgs.AppendElement(charMsg);
    }
    if (mFollowingCharMsgs.Length() == 1) {
      // If we receive a keydown message for a high-surrogate, a low-surrogate
      // keydown message **will** and should follow it.  We cannot translate the
      // following WM_KEYDOWN message for the low-surrogate right now since
      // it's not yet queued into the message queue yet.  Therefore, we need to
      // wait next one to dispatch keypress event with setting its `.key` value
      // to a surrogate pair rather than setting it to a lone surrogate.
      // FYI: This may happen with typing a non-BMP character on the touch
      // keyboard on Windows 10 or later except when an IME is installed. (If
      // IME is installed, composition is used instead.)
      if (IS_HIGH_SURROGATE(mFollowingCharMsgs[0].wParam)) {
        if (pendingHighSurrogate) {
          MOZ_LOG(gKeyLog, LogLevel::Warning,
                  ("%p NativeKey::InitWithKeyOrChar(), there is pending "
                   "high surrogate input, but received another high surrogate "
                   "input. The previous one is discarded",
                   this));
        }
        sPendingHighSurrogate = mFollowingCharMsgs[0].wParam;
        mFollowingCharMsgs.Clear();
      } else if (IS_LOW_SURROGATE(mFollowingCharMsgs[0].wParam)) {
        // If we stopped dispathing a keypress event for a preceding
        // high-surrogate, treat this keydown (for a low-surrogate) as
        // introducing both the high surrogate and the low surrogate.
        if (pendingHighSurrogate) {
          MSG charMsg = mFollowingCharMsgs[0];
          mFollowingCharMsgs[0].wParam = pendingHighSurrogate;
          mFollowingCharMsgs.AppendElement(std::move(charMsg));
        } else {
          MOZ_LOG(
              gKeyLog, LogLevel::Warning,
              ("%p NativeKey::InitWithKeyOrChar(), there is no pending high "
               "surrogate input, but received lone low surrogate input",
               this));
        }
      } else if (MOZ_UNLIKELY(pendingHighSurrogate)) {
        MOZ_LOG(gKeyLog, LogLevel::Warning,
                ("%p NativeKey::InitWithKeyOrChar(), there is pending "
                 "high surrogate input, but received non-surrogate input. "
                 "The high surrogate input is discarded",
                 this));
      }
    } else if (MOZ_UNLIKELY(pendingHighSurrogate &&
                            !mFollowingCharMsgs.IsEmpty())) {
      MOZ_LOG(gKeyLog, LogLevel::Warning,
              ("%p NativeKey::InitWithKeyOrChar(), there is pending "
               "high surrogate input, but received 2 or more character input. "
               "The high surrogate input is discarded",
               this));
    }
  }

  keyboardLayout->InitNativeKey(*this);

  // Now, we can know if the key produces character(s) or a dead key with
  // AltGraph modifier.  When user emulates AltGr key press with pressing
  // both Ctrl and Alt and the key produces character(s) or a dead key, we
  // need to replace Control and Alt state with AltGraph if the keyboard
  // layout has AltGr key.
  // Note that if Ctrl and/or Alt are pressed (not to emulate to press AltGr),
  // we need to set actual modifiers to eKeyDown and eKeyUp.
  if (MaybeEmulatingAltGraph() &&
      (mCommittedCharsAndModifiers.IsProducingCharsWithAltGr() ||
       mKeyNameIndex == KEY_NAME_INDEX_Dead)) {
    mModKeyState.Unset(MODIFIER_CONTROL | MODIFIER_ALT);
    mModKeyState.Set(MODIFIER_ALTGRAPH);
  }

  mIsDeadKey =
      (IsFollowedByDeadCharMessage() ||
       keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState));
  mIsPrintableKey = mKeyNameIndex == KEY_NAME_INDEX_USE_STRING ||
                    KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode);
  // The repeat count in mMsg.lParam isn't useful to check whether the event
  // is caused by the auto-repeat feature because it's not incremented even
  // if it's repeated twice or more (i.e., always 1).  Therefore, we need to
  // check previous key state (31th bit) instead.  If it's 1, the key was down
  // before the message was sent.
  mIsRepeat = (mMsg.lParam & (1 << 30)) != 0;
  InitIsSkippableForKeyOrChar(lastKeyMSG);

  if (IsKeyDownMessage()) {
    // 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 mAlternativeCharCodes in the callback
    // method which will be called by TextEventDispatcher.
    if (!IsFollowedByPrintableCharMessage()) {
      ComputeInputtingStringWithKeyboardLayout();
    }
    // Remove odd char messages if there are.
    RemoveFollowingOddCharMessages();
  }
}

void NativeKey::InitCommittedCharsAndModifiersWithFollowingCharMessages() {
  mCommittedCharsAndModifiers.Clear();
  // This should cause inputting text in focused editor.  However, it
  // ignores keypress events whose altKey or ctrlKey is true.
  // Therefore, we need to remove these modifier state here.
  Modifiers modifiers = mModKeyState.GetModifiers();
  if (IsFollowedByPrintableCharMessage()) {
    modifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
    if (MaybeEmulatingAltGraph()) {
      modifiers |= MODIFIER_ALTGRAPH;
    }
  }
  // NOTE: This method assumes that WM_CHAR and WM_SYSCHAR are never retrieved
  //       at same time.
  for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
    // Ignore non-printable char messages.
    if (!IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
      continue;
    }
    char16_t ch = static_cast<char16_t>(mFollowingCharMsgs[i].wParam);
    mCommittedCharsAndModifiers.Append(ch, modifiers);
  }
}

NativeKey::~NativeKey() {
  MOZ_LOG(gKeyLog, LogLevel::Debug,
          ("%p NativeKey::~NativeKey(), destroyed"this));
  if (mIsOverridingKeyboardLayout) {
    KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
    keyboardLayout->RestoreLayout();
  }
  sLatestInstance = mLastInstance;
}

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());
  // If we can map the WM_APPCOMMAND to a virtual keycode, we can trust
  // the result of GetKeyboardState().  Otherwise, we dispatch both
  // keydown and keyup events from WM_APPCOMMAND handler.  Therefore,
  // even if WM_APPCOMMAND is caused by auto key repeat, web apps receive
  // a pair of DOM keydown and keyup events.  I.e., KeyboardEvent.repeat
  // should be never true of such keys.
  // XXX Isn't the key state always true?  If the key press caused this
  //     WM_APPCOMMAND, that means it's pressed at that time.
  if (mVirtualKeyCode) {
    BYTE kbdState[256];
    memset(kbdState, 0, sizeof(kbdState));
    ::GetKeyboardState(kbdState);
    mIsSkippableInRemoteProcess = mIsRepeat = !!kbdState[mVirtualKeyCode];
  }
}

bool NativeKey::MaybeEmulatingAltGraph() const {
  return IsControl() && IsAlt() && KeyboardLayout::GetInstance()->HasAltGr();
}

// static
bool NativeKey::IsControlChar(char16_t aChar) {
  static const char16_t U_SPACE = 0x20;
  static const char16_t U_DELETE = 0x7F;
  return aChar < U_SPACE || aChar == U_DELETE;
}

bool NativeKey::IsFollowedByDeadCharMessage() const {
  if (mFollowingCharMsgs.IsEmpty()) {
    return false;
  }
  return IsDeadCharMessage(mFollowingCharMsgs[0]);
}

bool NativeKey::IsFollowedByPrintableCharMessage() const {
  for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
    if (IsPrintableCharMessage(mFollowingCharMsgs[i])) {
      return true;
    }
  }
  return false;
}

bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const {
  for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) {
    if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) {
      return true;
    }
  }
  return false;
}

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

  // XXX How about Alt+F4? We receive WM_SYSKEYDOWN for F4 before closing the
  //     window.  Although, we don't prevent to close the window but the key
  //     event shouldn't be exposed to the web.

  return false;
}

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;
}

void NativeKey::RemoveFollowingOddCharMessages() {
  MOZ_ASSERT(IsKeyDownMessage());

  // If the keydown message is synthesized for automated tests, there is
  // nothing to do here.
  if (mFakeCharMsgs) {
    return;
  }

  // If there are some following char messages before another key message,
  // there is nothing to do here.
  if (!mFollowingCharMsgs.IsEmpty()) {
    return;
  }

  // If the handling key isn't Backspace, there is nothing to do here.
  if (mOriginalVirtualKeyCode != VK_BACK) {
    return;
  }

  // If we don't see the odd message pattern, there is nothing to do here.
  if (!IsIMEDoingKakuteiUndo()) {
    return;
  }

  // Otherwise, we need to remove odd WM_CHAR messages for ATOK or WXG (both
  // of them are Japanese IME).
  MSG msg;
  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_LOG(
        gKeyLog, LogLevel::Info,
        ("%p NativeKey::RemoveFollowingOddCharMessages(), removed odd char "
         "message, %s",
         this, ToString(msg).get()));
    mRemovedOddCharMsgs.AppendElement(msg);
  }
}

UINT NativeKey::GetScanCodeWithExtendedFlag() const {
  if (!mIsExtended) {
--> --------------------

--> maximum size reached

--> --------------------

97%


¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.35Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.