/* -*- 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/. */
// 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");
staticconst 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);
}
}
}
staticconst 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);
}
}
staticconst 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);
}
}
staticconst 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);
}
}
staticconst 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)");
}
}
// 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;
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;
}
}
switch (aInputEvent.mClass) { case eMouseEventClass: case ePointerEventClass: case eMouseScrollEventClass: case eWheelEventClass: case eDragEventClass: case eSimpleGestureEventClass:
InitMouseEvent(aInputEvent); break; default: break;
}
}
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()) { returntrue;
} // 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()) { returntrue;
} // 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...). returnfalse;
}
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;
}
}
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;
}
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;
}
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.
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();
} elseif (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));
}
} elseif (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));
}
} elseif (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);
}
}
// 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::IsFollowedByPrintableCharMessage() const { for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) { if (IsPrintableCharMessage(mFollowingCharMsgs[i])) { returntrue;
}
} returnfalse;
}
bool NativeKey::IsFollowedByPrintableCharOrSysCharMessage() const { for (size_t i = 0; i < mFollowingCharMsgs.Length(); ++i) { if (IsPrintableCharOrSysCharMessage(mFollowingCharMsgs[i])) { returntrue;
}
} returnfalse;
}
bool NativeKey::IsReservedBySystem() const { // Alt+Space key is handled by OS, we shouldn't touch it. if (mModKeyState.IsAlt() && !mModKeyState.IsControl() &&
mVirtualKeyCode == VK_SPACE) { returntrue;
}
// 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.
// 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
--> --------------------
¤ 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)
¤
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.