--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -3492,21 +3492,28 @@ KeyboardLayout::Shutdown()
// static
void
KeyboardLayout::NotifyIdleServiceOfUserActivity()
{
sIdleService->ResetIdleTimeOut(0);
}
-KeyboardLayout::KeyboardLayout() :
- mKeyboardLayout(0), mIsOverridden(false),
- mIsPendingToRestoreKeyboardLayout(false)
+KeyboardLayout::KeyboardLayout()
+ : mKeyboardLayout(0)
+ , mIsOverridden(false)
+ , mIsPendingToRestoreKeyboardLayout(false)
{
mDeadKeyTableListHead = nullptr;
+ // A dead key sequence should be made from up to 5 keys. Therefore, 4 is
+ // enough and makes sense because the item is uint8_t.
+ // (Although, even if it's possible to be 6 keys or more in a sequence,
+ // this array will be re-allocated).
+ mActiveDeadKeys.SetCapacity(4);
+ mDeadKeyShiftStates.SetCapacity(4);
// NOTE: LoadLayout() should be called via OnLayoutChange().
}
KeyboardLayout::~KeyboardLayout()
{
ReleaseDeadKeyTables();
}
@@ -3608,45 +3615,44 @@ KeyboardLayout::InitNativeKey(NativeKey&
// If it's not in dead key sequence, we don't need to do anymore here.
if (!IsInDeadKeySequence()) {
return;
}
// If it's in dead key sequence and dead char is inputted as is, we need to
// set the previous modifier state which is stored when preceding dead key
// is pressed.
- UniCharsAndModifiers deadChars =
- GetUniCharsAndModifiers(mActiveDeadKey, mDeadKeyShiftState);
+ UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
aNativeKey.mCommittedCharsAndModifiers.
OverwriteModifiersIfBeginsWith(deadChars);
// Finish the dead key sequence.
DeactivateDeadKeyState();
return;
}
+ // If it's a dead key, aNativeKey will be initialized by
+ // MaybeInitNativeKeyAsDeadKey().
+ if (MaybeInitNativeKeyAsDeadKey(aNativeKey, aModKeyState)) {
+ return;
+ }
+
// If the key is not a usual printable key, KeyboardLayout class assume that
// it's not cause dead char nor printable char. Therefore, there are nothing
// to do here fore such keys (e.g., function keys).
// However, this should keep dead key state even if non-printable key is
// pressed during a dead key sequence.
if (!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode)) {
return;
}
MOZ_ASSERT(aNativeKey.mOriginalVirtualKeyCode != VK_PACKET,
"At handling VK_PACKET, we shouldn't refer keyboard layout");
MOZ_ASSERT(aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING,
"Printable key's key name index must be KEY_NAME_INDEX_USE_STRING");
- // If it's a dead key, aNativeKey will be initialized by
- // MaybeInitNativeKeyAsDeadKey().
- if (MaybeInitNativeKeyAsDeadKey(aNativeKey, aModKeyState)) {
- return;
- }
-
// If it's in dead key handling and the pressed key causes a composite
// character, aNativeKey will be initialized by
// MaybeInitNativeKeyWithCompositeChar().
if (MaybeInitNativeKeyWithCompositeChar(aNativeKey, aModKeyState)) {
return;
}
UniCharsAndModifiers baseChars =
@@ -3654,59 +3660,53 @@ KeyboardLayout::InitNativeKey(NativeKey&
// If the key press isn't related to any dead keys, initialize aNativeKey
// with the characters which should be caused by the key.
if (!IsInDeadKeySequence()) {
aNativeKey.mCommittedCharsAndModifiers = baseChars;
return;
}
- // Although, this shouldn't occur, if active dead key isn't a printable
- // key, we cannot handle it because KeyboardLayout assumes that dead key
- // is never mapped to non-printable keys (e.g., F4, etc). Please be aware,
- // it's possible, but we've not known such special keyboard layout yet.
- if (NS_WARN_IF(!IsPrintableCharKey(mActiveDeadKey))) {
- return;
- }
-
// If the key doesn't cause a composite character with preceding dead key,
// initialize aNativeKey with the dead-key character followed by current
// key's character.
- UniCharsAndModifiers deadChars =
- GetUniCharsAndModifiers(mActiveDeadKey, mDeadKeyShiftState);
+ UniCharsAndModifiers deadChars = GetDeadUniCharsAndModifiers();
aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars;
if (aNativeKey.IsKeyDownMessage()) {
DeactivateDeadKeyState();
}
}
bool
KeyboardLayout::MaybeInitNativeKeyAsDeadKey(
NativeKey& aNativeKey,
const ModifierKeyState& aModKeyState)
{
- if (!IsDeadKey(aNativeKey.mOriginalVirtualKeyCode, aModKeyState)) {
+ // Only when it's not in dead key sequence, we can trust IsDeadKey() result.
+ if (!IsInDeadKeySequence() &&
+ !IsDeadKey(aNativeKey.mOriginalVirtualKeyCode, aModKeyState)) {
return false;
}
- // If it's a keydown event but not in dead key sequence or it's a keyup
- // event of a dead key which activated current dead key sequence,
- // initialize aNativeKey as a dead key event.
- if ((aNativeKey.IsKeyDownMessage() && !IsInDeadKeySequence()) ||
- (!aNativeKey.IsKeyDownMessage() &&
- mActiveDeadKey == aNativeKey.mOriginalVirtualKeyCode)) {
+ // When keydown message is followed by a dead char message, it should be
+ // initialized as dead key.
+ bool isDeadKeyDownEvent =
+ aNativeKey.IsKeyDownMessage() &&
+ aNativeKey.IsFollowedByDeadCharMessage();
+
+ // When keyup message is received, let's check if it's one of preceding
+ // dead keys because keydown message order and keyup message order may be
+ // different.
+ bool isDeadKeyUpEvent =
+ !aNativeKey.IsKeyDownMessage() &&
+ mActiveDeadKeys.Contains(aNativeKey.mOriginalVirtualKeyCode);
+
+ if (isDeadKeyDownEvent || isDeadKeyUpEvent) {
ActivateDeadKeyState(aNativeKey, aModKeyState);
-#ifdef DEBUG
- UniCharsAndModifiers deadChars =
- GetNativeUniCharsAndModifiers(aNativeKey.mOriginalVirtualKeyCode,
- aModKeyState);
- MOZ_ASSERT(deadChars.Length() == 1,
- "dead key must generate only one character");
-#endif
- // First dead key event doesn't generate characters. Dead key should
+ // Any dead key events don't generate characters. So, a dead key should
// cause only keydown event and keyup event whose KeyboardEvent.key
// values are "Dead".
aNativeKey.mCommittedCharsAndModifiers.Clear();
aNativeKey.mKeyNameIndex = KEY_NAME_INDEX_Dead;
return true;
}
// At keydown message handling, we need to forget the first dead key
@@ -3715,47 +3715,37 @@ KeyboardLayout::MaybeInitNativeKeyAsDead
// another dead key before releasing current key. Therefore, we can
// set only a character for current key for keyup event.
if (!IsInDeadKeySequence()) {
aNativeKey.mCommittedCharsAndModifiers =
GetUniCharsAndModifiers(aNativeKey.mOriginalVirtualKeyCode, aModKeyState);
return true;
}
+ // When non-printable key event comes during a dead key sequence, that must
+ // be a modifier key event. So, such events shouldn't be handled as a part
+ // of the dead key sequence.
+ if (!IsDeadKey(aNativeKey.mOriginalVirtualKeyCode, aModKeyState)) {
+ return false;
+ }
+
// FYI: Following code may run when the user doesn't input text actually
// but the key sequence is a dead key sequence. For example,
// ` -> Ctrl+` with Spanish keyboard layout. Let's keep using this
// complicated code for now because this runs really rarely.
- if (NS_WARN_IF(!IsPrintableCharKey(mActiveDeadKey))) {
-#if defined(DEBUG) || defined(MOZ_CRASHREPORTER)
- nsPrintfCString warning("The virtual key index (%d) of mActiveDeadKey "
- "(0x%02X) is not a printable key "
- "(aNativeKey.mOriginalVirtualKeyCode=0x%02X)",
- GetKeyIndex(mActiveDeadKey), mActiveDeadKey,
- aNativeKey.mOriginalVirtualKeyCode);
- NS_WARNING(warning.get());
-#ifdef MOZ_CRASHREPORTER
- CrashReporter::AppendAppNotesToCrashReport(
- NS_LITERAL_CSTRING("\n") + warning);
-#endif // #ifdef MOZ_CRASHREPORTER
-#endif // #if defined(DEBUG) || defined(MOZ_CRASHREPORTER)
- MOZ_CRASH("Trying to reference out of range of mVirtualKeys");
- }
-
// Dead key followed by another dead key may cause a composed character
// (e.g., "Russian - Mnemonic" keyboard layout's 's' -> 'c').
if (MaybeInitNativeKeyWithCompositeChar(aNativeKey, aModKeyState)) {
return true;
}
// Otherwise, dead key followed by another dead key causes inputting both
// character.
- UniCharsAndModifiers prevDeadChars =
- GetUniCharsAndModifiers(mActiveDeadKey, mDeadKeyShiftState);
+ UniCharsAndModifiers prevDeadChars = GetDeadUniCharsAndModifiers();
UniCharsAndModifiers newChars =
GetUniCharsAndModifiers(aNativeKey.mOriginalVirtualKeyCode, aModKeyState);
// But keypress events should be fired for each committed character.
aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars;
if (aNativeKey.IsKeyDownMessage()) {
DeactivateDeadKeyState();
}
return true;
@@ -3765,29 +3755,27 @@ bool
KeyboardLayout::MaybeInitNativeKeyWithCompositeChar(
NativeKey& aNativeKey,
const ModifierKeyState& aModKeyState)
{
if (!IsInDeadKeySequence()) {
return false;
}
- if (NS_WARN_IF(!IsPrintableCharKey(mActiveDeadKey)) ||
- NS_WARN_IF(!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode))) {
+ if (NS_WARN_IF(!IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode))) {
return false;
}
UniCharsAndModifiers baseChars =
GetUniCharsAndModifiers(aNativeKey.mOriginalVirtualKeyCode, aModKeyState);
if (baseChars.IsEmpty() || !baseChars.CharAt(0)) {
return false;
}
- char16_t compositeChar =
- GetCompositeChar(mActiveDeadKey, mDeadKeyShiftState, baseChars.CharAt(0));
+ char16_t compositeChar = GetCompositeChar(baseChars.CharAt(0));
if (!compositeChar) {
return false;
}
// Active dead-key and base character does produce exactly one composite
// character.
aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar,
baseChars.ModifiersAt(0));
@@ -3819,26 +3807,53 @@ KeyboardLayout::GetNativeUniCharsAndModi
if (key < 0) {
return UniCharsAndModifiers();
}
VirtualKey::ShiftState shiftState =
VirtualKey::ModifierKeyStateToShiftState(aModKeyState);
return mVirtualKeys[key].GetNativeUniChars(shiftState);
}
+UniCharsAndModifiers
+KeyboardLayout::GetDeadUniCharsAndModifiers() const
+{
+ MOZ_RELEASE_ASSERT(mActiveDeadKeys.Length() == mDeadKeyShiftStates.Length());
+
+ if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
+ return UniCharsAndModifiers();
+ }
+
+ UniCharsAndModifiers result;
+ for (size_t i = 0; i < mActiveDeadKeys.Length(); ++i) {
+ result +=
+ GetUniCharsAndModifiers(mActiveDeadKeys[i], mDeadKeyShiftStates[i]);
+ }
+ return result;
+}
+
char16_t
-KeyboardLayout::GetCompositeChar(uint8_t aVirtualKeyOfDeadKey,
- VirtualKey::ShiftState aShiftStateOfDeadKey,
- char16_t aBaseChar) const
+KeyboardLayout::GetCompositeChar(char16_t aBaseChar) const
{
- int32_t key = GetKeyIndex(aVirtualKeyOfDeadKey);
+ if (NS_WARN_IF(mActiveDeadKeys.IsEmpty())) {
+ return 0;
+ }
+ // XXX Currently, we don't support computing a composite character with
+ // two or more dead keys since it needs big table for supporting
+ // long chained dead keys. However, this should be a minor bug
+ // because this runs only when the latest keydown event does not cause
+ // WM_(SYS)CHAR messages. So, when user wants to input a character,
+ // this path never runs.
+ if (mActiveDeadKeys.Length() > 1) {
+ return 0;
+ }
+ int32_t key = GetKeyIndex(mActiveDeadKeys[0]);
if (key < 0) {
return 0;
}
- return mVirtualKeys[key].GetCompositeChar(aShiftStateOfDeadKey, aBaseChar);
+ return mVirtualKeys[key].GetCompositeChar(mDeadKeyShiftStates[0], aBaseChar);
}
void
KeyboardLayout::LoadLayout(HKL aLayout)
{
mIsPendingToRestoreKeyboardLayout = false;
if (mKeyboardLayout == aLayout) {
@@ -3855,17 +3870,18 @@ KeyboardLayout::LoadLayout(HKL aLayout)
BYTE originalKbdState[256];
// Bitfield with all shift states that have at least one dead-key.
uint16_t shiftStatesWithDeadKeys = 0;
// Bitfield with all shift states that produce any possible dead-key base
// characters.
uint16_t shiftStatesWithBaseChars = 0;
- mActiveDeadKey = -1;
+ mActiveDeadKeys.Clear();
+ mDeadKeyShiftStates.Clear();
ReleaseDeadKeyTables();
::GetKeyboardState(originalKbdState);
// For each shift state gather all printable characters that are produced
// for normal case when no any dead-key is active.
@@ -4094,36 +4110,36 @@ void
KeyboardLayout::ActivateDeadKeyState(const NativeKey& aNativeKey,
const ModifierKeyState& aModKeyState)
{
// Dead-key state should be activated at keydown.
if (!aNativeKey.IsKeyDownMessage()) {
return;
}
- MOZ_RELEASE_ASSERT(IsPrintableCharKey(aNativeKey.mOriginalVirtualKeyCode));
-
- mActiveDeadKey = aNativeKey.mOriginalVirtualKeyCode;
- mDeadKeyShiftState = VirtualKey::ModifierKeyStateToShiftState(aModKeyState);
+ mActiveDeadKeys.AppendElement(aNativeKey.mOriginalVirtualKeyCode);
+ mDeadKeyShiftStates.AppendElement(
+ VirtualKey::ModifierKeyStateToShiftState(aModKeyState));
}
void
KeyboardLayout::DeactivateDeadKeyState()
{
- if (mActiveDeadKey < 0) {
+ if (mActiveDeadKeys.IsEmpty()) {
return;
}
BYTE kbdState[256];
memset(kbdState, 0, sizeof(kbdState));
- VirtualKey::FillKbdState(kbdState, mDeadKeyShiftState);
-
- EnsureDeadKeyActive(false, mActiveDeadKey, kbdState);
- mActiveDeadKey = -1;
+ // Assume that the last dead key can finish dead key sequence.
+ VirtualKey::FillKbdState(kbdState, mDeadKeyShiftStates.LastElement());
+ EnsureDeadKeyActive(false, mActiveDeadKeys.LastElement(), kbdState);
+ mActiveDeadKeys.Clear();
+ mDeadKeyShiftStates.Clear();
}
bool
KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar,
char16_t aCompositeChar,
DeadKeyEntry* aDeadKeyArray,
uint32_t aEntries)
{
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -689,17 +689,17 @@ public:
bool IsDeadKey(uint8_t aVirtualKey,
const ModifierKeyState& aModKeyState) const;
/**
* IsInDeadKeySequence() returns true when it's in a dead key sequence.
* It starts when a dead key is down and ends when another key down causes
* inactivating the dead key state.
*/
- bool IsInDeadKeySequence() const { return mActiveDeadKey >= 0; }
+ bool IsInDeadKeySequence() const { return !mActiveDeadKeys.IsEmpty(); }
/**
* IsSysKey() returns true if aVirtualKey with aModKeyState causes WM_SYSKEY*
* or WM_SYS*CHAR messages.
*/
bool IsSysKey(uint8_t aVirtualKey,
const ModifierKeyState& aModKeyState) const;
@@ -806,18 +806,24 @@ private:
DeadKeyTableListEntry* next;
uint8_t data[1];
};
HKL mKeyboardLayout;
VirtualKey mVirtualKeys[NS_NUM_OF_KEYS];
DeadKeyTableListEntry* mDeadKeyTableListHead;
- int32_t mActiveDeadKey; // -1 = no active dead-key
- VirtualKey::ShiftState mDeadKeyShiftState;
+ // When mActiveDeadKeys is empty, it's not in dead key sequence.
+ // Otherwise, it contains virtual keycodes which are pressed in current
+ // dead key sequence.
+ nsTArray<uint8_t> mActiveDeadKeys;
+ // mDeadKeyShiftStates is always same length as mActiveDeadKeys.
+ // This stores shift states at pressing each dead key stored in
+ // mActiveDeadKeys.
+ nsTArray<VirtualKey::ShiftState> mDeadKeyShiftStates;
bool mIsOverridden;
bool mIsPendingToRestoreKeyboardLayout;
static inline int32_t GetKeyIndex(uint8_t aVirtualKey);
static int CompareDeadKeyEntries(const void* aArg1, const void* aArg2,
void* aData);
static bool AddDeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar,
@@ -879,25 +885,29 @@ private:
/**
* See the comment of GetUniCharsAndModifiers() below.
*/
UniCharsAndModifiers GetUniCharsAndModifiers(
uint8_t aVirtualKey,
VirtualKey::ShiftState aShiftState) const;
/**
+ * GetDeadUniCharsAndModifiers() returns dead chars which are stored in
+ * current dead key sequence. So, this is stateful.
+ */
+ UniCharsAndModifiers GetDeadUniCharsAndModifiers() const;
+
+ /**
* GetCompositeChar() returns a composite character with dead character
- * caused by aVirtualKeyOfDeadKey and aShiftStateOfDeadKey and a base
- * character (aBaseChar).
+ * caused by mActiveDeadKeys, mDeadKeyShiftStates and a base character
+ * (aBaseChar).
* If the combination of the dead character and the base character doesn't
* cause a composite character, this returns 0.
*/
- char16_t GetCompositeChar(uint8_t aVirtualKeyOfDeadKey,
- VirtualKey::ShiftState aShiftStateOfDeadKey,
- char16_t aBaseChar) const;
+ char16_t GetCompositeChar(char16_t aBaseChar) const;
// NativeKey class should access InitNativeKey() directly, but it shouldn't
// be available outside of NativeKey. So, let's make NativeKey a friend
// class of this.
friend class NativeKey;
};
class RedirectedKeyDownMessageManager