Bug 757688 part.5 Make KeyboardLayout stateless for non-dead keys r=jimm
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 15 Jun 2012 18:52:50 +0900
changeset 99534 fd9de8d2e181c3ff95b756ee9967d5e7808eb664
parent 99533 5d7af55c4536cc965a39a1de8f4dd8f0cb192839
child 99535 d3bed04319ab696f102d6ebe0399d5c7c55fdc3e
push idunknown
push userunknown
push dateunknown
reviewersjimm
bugs757688
milestone16.0a1
Bug 757688 part.5 Make KeyboardLayout stateless for non-dead keys r=jimm
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
widget/windows/nsWindow.cpp
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -9,16 +9,17 @@
 #include "nsWindow.h"
 #include "nsIMM32Handler.h"
 
 #include "nsMemory.h"
 #include "nsToolkit.h"
 #include "nsQuickSort.h"
 #include "nsAlgorithm.h"
 #include "nsGUIEvent.h"
+#include "nsUnicharUtils.h"
 #include "WidgetUtils.h"
 #include "WinUtils.h"
 
 #include "nsIDOMKeyEvent.h"
 
 #include <windows.h>
 #include <winuser.h>
 
@@ -147,16 +148,78 @@ ModifierKeyState::InitMouseEvent(nsInput
     mouseEvent.buttons |= nsMouseEvent::e4thButtonFlag;
   }
   if (::GetKeyState(VK_XBUTTON2) < 0) {
     mouseEvent.buttons |= nsMouseEvent::e5thButtonFlag;
   }
 }
 
 /*****************************************************************************
+ * mozilla::widget::UniCharsAndModifiers
+ *****************************************************************************/
+
+void
+UniCharsAndModifiers::Append(PRUnichar aUniChar, Modifiers aModifiers)
+{
+  MOZ_ASSERT(mLength < 5);
+  mChars[mLength] = aUniChar;
+  mModifiers[mLength] = aModifiers;
+  mLength++;
+}
+
+void
+UniCharsAndModifiers::FillModifiers(Modifiers aModifiers)
+{
+  for (PRUint32 i = 0; i < mLength; i++) {
+    mModifiers[i] = aModifiers;
+  }
+}
+
+bool
+UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers& aOther) const
+{
+  if (mLength != aOther.mLength) {
+    return false;
+  }
+  return !memcmp(mChars, aOther.mChars, mLength * sizeof(PRUnichar));
+}
+
+bool
+UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
+                        const UniCharsAndModifiers& aOther) const
+{
+  if (mLength != aOther.mLength) {
+    return false;
+  }
+
+  nsCaseInsensitiveStringComparator comp;
+  return !comp(mChars, aOther.mChars, mLength, aOther.mLength);
+}
+
+UniCharsAndModifiers&
+UniCharsAndModifiers::operator+=(const UniCharsAndModifiers& aOther)
+{
+  PRUint32 copyCount = NS_MIN(aOther.mLength, 5 - mLength);
+  NS_ENSURE_TRUE(copyCount > 0, *this);
+  memcpy(&mChars[mLength], aOther.mChars, copyCount * sizeof(PRUnichar));
+  memcpy(&mModifiers[mLength], aOther.mModifiers,
+         copyCount * sizeof(Modifiers));
+  mLength += copyCount;
+  return *this;
+}
+
+UniCharsAndModifiers
+UniCharsAndModifiers::operator+(const UniCharsAndModifiers& aOther) const
+{
+  UniCharsAndModifiers result(*this);
+  result += aOther;
+  return result;
+}
+
+/*****************************************************************************
  * mozilla::widget::VirtualKey
  *****************************************************************************/
 
 inline PRUnichar
 VirtualKey::GetCompositeChar(ShiftState aShiftState, PRUnichar aBaseChar) const
 {
   return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar);
 }
@@ -209,80 +272,74 @@ VirtualKey::SetDeadChar(ShiftState aShif
   NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index");
 
   SetDeadKey(aShiftState, true);
 
   mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar;
   mShiftStates[aShiftState].DeadKey.Table = nsnull;
 }
 
-PRUint32
-VirtualKey::GetUniChars(ShiftState aShiftState,
-                        PRUnichar* aUniChars,
-                        Modifiers* aFinalModifiers) const
+UniCharsAndModifiers
+VirtualKey::GetUniChars(ShiftState aShiftState) const
 {
-  *aFinalModifiers = ShiftStateToModifiers(aShiftState);
-  PRUint32 numOfChars = GetNativeUniChars(aShiftState, aUniChars);
+  UniCharsAndModifiers result = GetNativeUniChars(aShiftState);
 
   const ShiftState STATE_ALT_CONTROL = (STATE_ALT | STATE_CONTROL);
   if (!(aShiftState & STATE_ALT_CONTROL)) {
-    return numOfChars;
+    return result;
   }
 
-  PRUnichar unshiftedChars[5];
-  PRUint32 numOfUnshiftedChars =
-    GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL, unshiftedChars);
-
-  if (!numOfChars) {
-    if (!numOfUnshiftedChars) {
-      return 0;
-    }
-    memcpy(aUniChars, unshiftedChars,
-           numOfUnshiftedChars * sizeof(PRUnichar));
-    return numOfUnshiftedChars;
+  if (!result.mLength) {
+    result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
+    result.FillModifiers(ShiftStateToModifiers(aShiftState));
+    return result;
   }
 
   if ((aShiftState & STATE_ALT_CONTROL) == STATE_ALT_CONTROL) {
     // Even if the shifted chars and the unshifted chars are same, we
     // should consume the Alt key state and the Ctrl key state when
     // AltGr key is pressed. Because if we don't consume them, the input
     // events are ignored on nsEditor. (I.e., Users cannot input the
     // characters with this key combination.)
-    *aFinalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
-  } else if (!(numOfChars == numOfUnshiftedChars &&
-               !memcmp(aUniChars, unshiftedChars,
-                       numOfChars * sizeof(PRUnichar)))) {
+    Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
+    finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
+    result.FillModifiers(finalModifiers);
+    return result;
+  }
+
+  UniCharsAndModifiers unmodifiedReslt =
+    GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
+  if (!result.UniCharsEqual(unmodifiedReslt)) {
     // Otherwise, we should consume the Alt key state and the Ctrl key state
     // only when the shifted chars and unshifted chars are different.
-    *aFinalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
+    Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
+    finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
+    result.FillModifiers(finalModifiers);
   }
-  return numOfChars;
+  return result;
 }
 
 
-PRUint32
-VirtualKey::GetNativeUniChars(ShiftState aShiftState,
-                              PRUnichar* aUniChars) const
+UniCharsAndModifiers
+VirtualKey::GetNativeUniChars(ShiftState aShiftState) const
 {
+  UniCharsAndModifiers result;
+  Modifiers modifiers = ShiftStateToModifiers(aShiftState);
   if (IsDeadKey(aShiftState)) {
-    if (aUniChars) {
-      aUniChars[0] = mShiftStates[aShiftState].DeadKey.DeadChar;
-    }
-    return 1;
+    result.Append(mShiftStates[aShiftState].DeadKey.DeadChar, modifiers);
+    return result;
   }
 
   PRUint32 index;
   PRUint32 len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
   for (index = 0;
        index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) {
-    if (aUniChars) {
-      aUniChars[index] = mShiftStates[aShiftState].Normal.Chars[index];
-    }
+    result.Append(mShiftStates[aShiftState].Normal.Chars[index], modifiers);
   }
-  return index;
+  return result;
 }
 
 // static
 void
 VirtualKey::FillKbdState(PBYTE aKbdState,
                          const ShiftState aShiftState)
 {
   NS_ASSERTION(aShiftState < 16, "aShiftState out of range");
@@ -490,130 +547,93 @@ KeyboardLayout::IsDeadKey(PRUint8 aVirtu
   if (virtualKeyIndex < 0) {
     return false;
   }
 
   return mVirtualKeys[virtualKeyIndex].IsDeadKey(
            VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
 }
 
-static void FillModifiers(Modifiers* aDest, const Modifiers aSrc,
-                          PRUint32 aCount)
-{
-  for (PRUint32 i = 0; i < aCount; i++) {
-    aDest[i] = aSrc;
-  }
-}
-
-void
+UniCharsAndModifiers
 KeyboardLayout::OnKeyDown(PRUint8 aVirtualKey,
                           const ModifierKeyState& aModKeyState)
 {
   PRInt32 virtualKeyIndex = GetKeyIndex(aVirtualKey);
 
   if (virtualKeyIndex < 0) {
     // Does not produce any printable characters, but still preserves the
     // dead-key state.
-    mNumOfChars = 0;
-    return;
+    return UniCharsAndModifiers();
   }
 
   PRUint8 shiftState =
     VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers());
 
   if (mVirtualKeys[virtualKeyIndex].IsDeadKey(shiftState)) {
     if (mActiveDeadKey < 0) {
       // Dead-key state activated. No characters generated.
       mActiveDeadKey = aVirtualKey;
       mDeadKeyShiftState = shiftState;
-      mNumOfChars = 0;
-      return;
+      return UniCharsAndModifiers();
     }
 
     // Dead-key followed by another dead-key. Reset dead-key state and
     // return both dead-key characters.
     PRInt32 activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey);
-    mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState,
-                                                 mChars, mModifiersOfChars);
-    mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState, &mChars[1],
-                                              &mModifiersOfChars[1]);
-    mNumOfChars = 2;
+    UniCharsAndModifiers result =
+      mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState);
+    result += mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
     DeactivateDeadKeyState();
-    return;
+    return result;
   }
 
-  Modifiers finalModifiers;
-  PRUnichar uniChars[5];
-  PRUint32 numOfBaseChars =
-    mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState, uniChars,
-                                              &finalModifiers);
+  UniCharsAndModifiers baseChars =
+    mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
   if (mActiveDeadKey < 0) {
     // No dead-keys are active. Just return the produced characters.
-    memcpy(mChars, uniChars, numOfBaseChars * sizeof(PRUnichar));
-    FillModifiers(mModifiersOfChars, finalModifiers, numOfBaseChars);
-    mNumOfChars = numOfBaseChars;
-    return;
+    return baseChars;
   }
 
   // Dead-key was active. See if pressed base character does produce
   // valid composite character.
   PRInt32 activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey);
-  PRUnichar compositeChar = (numOfBaseChars == 1 && uniChars[0]) ?
+  PRUnichar compositeChar = (baseChars.mLength == 1 && baseChars.mChars[0]) ?
     mVirtualKeys[activeDeadKeyIndex].GetCompositeChar(mDeadKeyShiftState,
-                                                      uniChars[0]) : 0;
+                                                      baseChars.mChars[0]) : 0;
   if (compositeChar) {
     // Active dead-key and base character does produce exactly one
     // composite character.
-    mChars[0] = compositeChar;
-    mModifiersOfChars[0] = finalModifiers;
-    mNumOfChars = 1;
-  } else {
-    // There is no valid dead-key and base character combination.
-    // Return dead-key character followed by base character.
-    mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState,
-                                                 mChars, mModifiersOfChars);
-    memcpy(&mChars[1], uniChars, numOfBaseChars * sizeof(PRUnichar));
-    FillModifiers(&mModifiersOfChars[1], finalModifiers, numOfBaseChars);
-    mNumOfChars = numOfBaseChars + 1;
+    UniCharsAndModifiers result;
+    result.Append(compositeChar, baseChars.mModifiers[0]);
+    DeactivateDeadKeyState();
+    return result;
   }
 
+  // There is no valid dead-key and base character combination.
+  // Return dead-key character followed by base character.
+  UniCharsAndModifiers result =
+    mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState);
+  result += baseChars;
   DeactivateDeadKeyState();
+
+  return result;
 }
 
-PRUint32
-KeyboardLayout::GetUniChars(PRUnichar* aUniChars,
-                            Modifiers* aModifiersOfUniChars,
-                            PRUint32 aMaxChars) const
+UniCharsAndModifiers
+KeyboardLayout::GetUniCharsAndModifiers(
+                  PRUint8 aVirtualKey,
+                  const ModifierKeyState& aModKeyState) const
 {
-  PRUint32 chars = NS_MIN<PRUint32>(mNumOfChars, aMaxChars);
-
-  memcpy(aUniChars, mChars, chars * sizeof(PRUnichar));
-  memcpy(aModifiersOfUniChars, mModifiersOfChars, chars * sizeof(Modifiers));
-
-  return chars;
-}
-
-PRUint32
-KeyboardLayout::GetUniCharsWithShiftState(PRUint8 aVirtualKey,
-                                          Modifiers aModifiers,
-                                          PRUnichar* aUniChars,
-                                          PRUint32 aMaxChars) const
-{
+  UniCharsAndModifiers result;
   PRInt32 key = GetKeyIndex(aVirtualKey);
   if (key < 0) {
-    return 0;
+    return result;
   }
-  Modifiers finalModifiers;
-  PRUnichar uniChars[5];
-  PRUint32 numOfBaseChars =
-    mVirtualKeys[key].GetUniChars(VirtualKey::ModifiersToShiftState(aModifiers),
-                                  uniChars, &finalModifiers);
-  PRUint32 chars = NS_MIN(numOfBaseChars, aMaxChars);
-  memcpy(aUniChars, uniChars, chars * sizeof(PRUnichar));
-  return chars;
+  return mVirtualKeys[key].
+    GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
 }
 
 void
 KeyboardLayout::LoadLayout(HKL aLayout)
 {
   if (mKeyboardLayout == aLayout) {
     return;
   }
@@ -626,17 +646,16 @@ KeyboardLayout::LoadLayout(HKL aLayout)
   BYTE originalKbdState[256];
   // Bitfield with all shift states that have at least one dead-key.
   PRUint16 shiftStatesWithDeadKeys = 0;
   // Bitfield with all shift states that produce any possible dead-key base
   // characters.
   PRUint16 shiftStatesWithBaseChars = 0;
 
   mActiveDeadKey = -1;
-  mNumOfChars = 0;
 
   ReleaseDeadKeyTables();
 
   ::GetKeyboardState(originalKbdState);
 
   // For each shift state gather all printable characters that are produced
   // for normal case when no any dead-key is active.
 
@@ -871,17 +890,18 @@ KeyboardLayout::GetDeadKeyCombinations(P
     }
 
     VirtualKey::FillKbdState(kbdState, shiftState);
 
     for (PRUint32 virtualKey = 0; virtualKey < 256; virtualKey++) {
       PRInt32 vki = GetKeyIndex(virtualKey);
       // Dead-key can pair only with such key that produces exactly one base
       // character.
-      if (vki >= 0 && mVirtualKeys[vki].GetNativeUniChars(shiftState) == 1) {
+      if (vki >= 0 &&
+          mVirtualKeys[vki].GetNativeUniChars(shiftState).mLength == 1) {
         // Ensure dead-key is in active state, when it swallows entered
         // character and waits for the next pressed key.
         if (!deadKeyActive) {
           deadKeyActive = EnsureDeadKeyActive(true, aDeadKey,
                                               aDeadKeyKbdState);
         }
 
         // Depending on the character the followed the dead-key, the keyboard
@@ -1055,29 +1075,29 @@ KeyboardLayout::ConvertNativeKeyCodeToDO
     case VK_OEM_8:
     case 0xE1: // OEM specific
     case VK_OEM_102:
     case 0xE3: // OEM specific
     case 0xE4: // OEM specific
     {
       NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode),
                    "The key must be printable");
-      PRUnichar uniChars[5];
-      PRUint32 numOfChars =
-        GetUniCharsWithShiftState(aNativeKeyCode, 0,
-                                  uniChars, ArrayLength(uniChars));
-      if (numOfChars != 1 || uniChars[0] < ' ' || uniChars[0] > 0x7F) {
-        numOfChars =
-          GetUniCharsWithShiftState(aNativeKeyCode, MODIFIER_SHIFT,
-                                    uniChars, ArrayLength(uniChars));
-        if (numOfChars != 1 || uniChars[0] < ' ' || uniChars[0] > 0x7F) {
+      ModifierKeyState modKeyState(0);
+      UniCharsAndModifiers uniChars =
+        GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
+      if (uniChars.mLength != 1 ||
+          uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) {
+        modKeyState.Set(MODIFIER_SHIFT);
+        uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
+        if (uniChars.mLength != 1 ||
+            uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) {
           return 0;
         }
       }
-      return WidgetUtils::ComputeKeyCodeFromChar(uniChars[0]);
+      return WidgetUtils::ComputeKeyCodeFromChar(uniChars.mChars[0]);
     }
 
     // VK_PROCESSKEY means IME already consumed the key event.
     case VK_PROCESSKEY:
       return 0;
     // VK_PACKET is generated by SendInput() API, we don't need to
     // care this message as key event.
     case VK_PACKET:
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -56,16 +56,22 @@ public:
     if (aIsAltDown) {
       modifiers |= MODIFIER_ALT;
     }
     if (modifiers) {
       Set(modifiers);
     }
   }
 
+  ModifierKeyState(Modifiers aModifiers) :
+    mModifiers(aModifiers)
+  {
+    EnsureAltGr();
+  }
+
   void Update();
 
   void Unset(Modifiers aRemovingModifiers)
   {
     mModifiers &= ~aRemovingModifiers;
     // Note that we don't need to unset AltGr flag here automatically.
     // For nsEditor, we need to remove Alt and Control flags but AltGr isn't
     // checked in nsEditor, so, it can be kept.
@@ -103,16 +109,39 @@ private:
     if (IsAltGr()) {
       mModifiers |= MODIFIER_ALTGRAPH;
     }
   }
 
   void InitMouseEvent(nsInputEvent& aMouseEvent) const;
 };
 
+struct UniCharsAndModifiers
+{
+  // Dead-key + up to 4 characters
+  PRUnichar mChars[5];
+  Modifiers mModifiers[5];
+  PRUint32  mLength;
+
+  UniCharsAndModifiers() : mLength(0) {}
+  UniCharsAndModifiers operator+(const UniCharsAndModifiers& aOther) const;
+  UniCharsAndModifiers& operator+=(const UniCharsAndModifiers& aOther);
+
+  /**
+   * Append a pair of unicode character and the final modifier.
+   */
+  void Append(PRUnichar aUniChar, Modifiers aModifiers);
+  void Clear() { mLength = 0; }
+
+  void FillModifiers(Modifiers aModifiers);
+
+  bool UniCharsEqual(const UniCharsAndModifiers& aOther) const;
+  bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers& aOther) const;
+};
+
 struct DeadKeyEntry;
 class DeadKeyTable;
 
 
 class VirtualKey
 {
 public:
   //  0 - Normal
@@ -224,20 +253,18 @@ public:
 
   void SetNormalChars(ShiftState aShiftState, const PRUnichar* aChars,
                       PRUint32 aNumOfChars);
   void SetDeadChar(ShiftState aShiftState, PRUnichar aDeadChar);
   const DeadKeyTable* MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
                                            PRUint32 aEntries) const;
   inline PRUnichar GetCompositeChar(ShiftState aShiftState,
                                     PRUnichar aBaseChar) const;
-  PRUint32 GetNativeUniChars(ShiftState aShiftState,
-                             PRUnichar* aUniChars = nsnull) const;
-  PRUint32 GetUniChars(ShiftState aShiftState, PRUnichar* aUniChars,
-                       Modifiers* aFinalModifiers) const;
+  UniCharsAndModifiers GetNativeUniChars(ShiftState aShiftState) const;
+  UniCharsAndModifiers GetUniChars(ShiftState aShiftState) const;
 };
 
 class NativeKey {
 public:
   NativeKey() :
     mDOMKeyCode(0), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0),
     mScanCode(0), mIsExtended(false)
   {
@@ -278,19 +305,16 @@ class KeyboardLayout
   };
 
   HKL mKeyboardLayout;
 
   VirtualKey mVirtualKeys[NS_NUM_OF_KEYS];
   DeadKeyTableListEntry* mDeadKeyTableListHead;
   PRInt32 mActiveDeadKey;                 // -1 = no active dead-key
   VirtualKey::ShiftState mDeadKeyShiftState;
-  PRUnichar mChars[5];                    // Dead-key + up to 4 characters
-  Modifiers mModifiersOfChars[5];
-  PRUint8 mNumOfChars;
 
   static inline PRInt32 GetKeyIndex(PRUint8 aVirtualKey);
   static int CompareDeadKeyEntries(const void* aArg1, const void* aArg2,
                                    void* aData);
   static bool AddDeadKeyEntry(PRUnichar aBaseChar, PRUnichar aCompositeChar,
                                 DeadKeyEntry* aDeadKeyArray, PRUint32 aEntries);
   bool EnsureDeadKeyActive(bool aIsActive, PRUint8 aDeadKey,
                              const PBYTE aDeadKeyKbdState);
@@ -308,35 +332,39 @@ public:
   KeyboardLayout();
   ~KeyboardLayout();
 
   static bool IsPrintableCharKey(PRUint8 aVirtualKey);
   static bool IsNumpadKey(PRUint8 aVirtualKey);
 
   /**
    * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
+   * This method isn't stateful.
    */
   bool IsDeadKey(PRUint8 aVirtualKey,
                  const ModifierKeyState& aModKeyState) const;
 
   /**
+   * GetUniCharsAndModifiers() returns characters which is inputted by the
+   * aVirtualKey with aModKeyState.  This method isn't stateful.
+   */
+  UniCharsAndModifiers GetUniCharsAndModifiers(
+                         PRUint8 aVirtualKey,
+                         const ModifierKeyState& aModKeyState) const;
+
+  /**
    * OnKeyDown() must be called when actually widget receives WM_KEYDOWN
    * message.  This method is stateful.  This saves current dead key state
    * and computes current inputted character(s).
    */
-  void OnKeyDown(PRUint8 aVirtualKey,
-                 const ModifierKeyState& aModKeyState);
+  UniCharsAndModifiers OnKeyDown(PRUint8 aVirtualKey,
+                                 const ModifierKeyState& aModKeyState);
 
   void LoadLayout(HKL aLayout);
 
-  PRUint32 GetUniChars(PRUnichar* aUniChars, Modifiers* aModifiersOfUniChars,
-                       PRUint32 aMaxChars) const;
-  PRUint32 GetUniCharsWithShiftState(PRUint8 aVirtualKey, Modifiers aModifiers,
-                                     PRUnichar* aUniChars,
-                                     PRUint32 aMaxChars) const;
   PRUint32 ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const;
 
   HKL GetLayout() const { return mKeyboardLayout; }
 };
 
 } // namespace widget
 } // namespace mozilla
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -89,17 +89,16 @@
 #include "nsFontMetrics.h"
 #include "nsIFontEnumerator.h"
 #include "nsGUIEvent.h"
 #include "nsFont.h"
 #include "nsRect.h"
 #include "nsThreadUtils.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsGkAtoms.h"
-#include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsXPIDLString.h"
 #include "nsWidgetsCID.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsString.h"
 #include "mozilla/Services.h"
@@ -6190,27 +6189,16 @@ bool nsWindow::OnGesture(WPARAM wParam, 
   }
 
   // Only close this if we process and return true.
   mGesture.CloseGestureInfoHandle((HGESTUREINFO)lParam);
 
   return true; // Handled
 }
 
-static bool
-StringCaseInsensitiveEquals(const PRUnichar* aChars1, const PRUint32 aNumChars1,
-                            const PRUnichar* aChars2, const PRUint32 aNumChars2)
-{
-  if (aNumChars1 != aNumChars2)
-    return false;
-
-  nsCaseInsensitiveStringComparator comp;
-  return comp(aChars1, aChars2, aNumChars1, aNumChars2) == 0;
-}
-
 /* static */
 bool nsWindow::IsRedirectedKeyDownMessage(const MSG &aMsg)
 {
   return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
          (sRedirectedKeyDown.message == aMsg.message &&
           WinUtils::GetScanCode(sRedirectedKeyDown.lParam) ==
             WinUtils::GetScanCode(aMsg.lParam));
 }
@@ -6225,17 +6213,18 @@ bool nsWindow::IsRedirectedKeyDownMessag
  */
 LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
                             const ModifierKeyState &aModKeyState,
                             bool *aEventDispatched,
                             nsFakeCharMessage* aFakeCharMessage)
 {
   NativeKey nativeKey(gKbdLayout, this, aMsg);
   UINT virtualKeyCode = nativeKey.GetOriginalVirtualKeyCode();
-  gKbdLayout.OnKeyDown(virtualKeyCode, aModKeyState);
+  UniCharsAndModifiers inputtingChars =
+    gKbdLayout.OnKeyDown(virtualKeyCode, aModKeyState);
 
   // Use only DOMKeyCode for XP processing.
   // Use virtualKeyCode for gKbdLayout and native processing.
   PRUint32 DOMKeyCode = nativeKey.GetDOMKeyCode();
 
 #ifdef DEBUG
   //PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("In OnKeyDown virt: %d\n", DOMKeyCode));
 #endif
@@ -6405,145 +6394,148 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
     // See bug 314130.
     return PluginHasFocus() && noDefault;
   }
 
   if (isDeadKey) {
     return PluginHasFocus() && noDefault;
   }
 
-  widget::Modifiers modifiersOfChars[5];
-  PRUnichar uniChars[5];
-  PRUnichar shiftedChars[5] = {0, 0, 0, 0, 0};
-  PRUnichar unshiftedChars[5] = {0, 0, 0, 0, 0};
+  UniCharsAndModifiers shiftedChars;
+  UniCharsAndModifiers unshiftedChars;
   PRUint32 shiftedLatinChar = 0;
   PRUint32 unshiftedLatinChar = 0;
-  PRUint32 numOfUniChars = 0;
-  PRUint32 numOfShiftedChars = 0;
-  PRUint32 numOfUnshiftedChars = 0;
-  PRUint32 numOfShiftStates = 0;
 
   switch (virtualKeyCode) {
     // keys to be sent as characters
-    case VK_ADD:       uniChars [0] = '+';  numOfUniChars = 1;  break;
-    case VK_SUBTRACT:  uniChars [0] = '-';  numOfUniChars = 1;  break;
-    case VK_DIVIDE:    uniChars [0] = '/';  numOfUniChars = 1;  break;
-    case VK_MULTIPLY:  uniChars [0] = '*';  numOfUniChars = 1;  break;
+    case VK_ADD:
+      inputtingChars.Clear();
+      inputtingChars.Append('+', aModKeyState.GetModifiers());
+      break;
+    case VK_SUBTRACT:
+      inputtingChars.Clear();
+      inputtingChars.Append('-', aModKeyState.GetModifiers());
+      break;
+    case VK_DIVIDE:
+      inputtingChars.Clear();
+      inputtingChars.Append('/', aModKeyState.GetModifiers());
+      break;
+    case VK_MULTIPLY:
+      inputtingChars.Clear();
+      inputtingChars.Append('*', aModKeyState.GetModifiers());
+      break;
     case VK_NUMPAD0:
     case VK_NUMPAD1:
     case VK_NUMPAD2:
     case VK_NUMPAD3:
     case VK_NUMPAD4:
     case VK_NUMPAD5:
     case VK_NUMPAD6:
     case VK_NUMPAD7:
     case VK_NUMPAD8:
     case VK_NUMPAD9:
-      uniChars [0] = virtualKeyCode - VK_NUMPAD0 + '0';
-      numOfUniChars = 1;
+      inputtingChars.Clear();
+      inputtingChars.Append(virtualKeyCode - VK_NUMPAD0 + '0',
+                            aModKeyState.GetModifiers());
       break;
     default:
-      if (KeyboardLayout::IsPrintableCharKey(virtualKeyCode)) {
-        numOfUniChars = numOfShiftStates =
-          gKbdLayout.GetUniChars(uniChars, modifiersOfChars,
-                                 ArrayLength(uniChars));
+      if (!KeyboardLayout::IsPrintableCharKey(virtualKeyCode)) {
+        inputtingChars.Clear();
       }
 
       if (aModKeyState.IsControl() ^ aModKeyState.IsAlt()) {
-        widget::Modifiers capsLockState =
-          aModKeyState.IsCapsLocked() ? MODIFIER_CAPSLOCK : 0;
-        numOfUnshiftedChars =
-          gKbdLayout.GetUniCharsWithShiftState(virtualKeyCode, capsLockState,
-                       unshiftedChars, ArrayLength(unshiftedChars));
-        numOfShiftedChars =
-          gKbdLayout.GetUniCharsWithShiftState(virtualKeyCode,
-                       capsLockState | MODIFIER_SHIFT,
-                       shiftedChars, ArrayLength(shiftedChars));
+        widget::ModifierKeyState capsLockState(
+          aModKeyState.GetModifiers() & MODIFIER_CAPSLOCK);
+        unshiftedChars =
+          gKbdLayout.GetUniCharsAndModifiers(virtualKeyCode, capsLockState);
+        capsLockState.Set(MODIFIER_SHIFT);
+        shiftedChars =
+          gKbdLayout.GetUniCharsAndModifiers(virtualKeyCode, capsLockState);
 
         // The current keyboard cannot input alphabets or numerics,
         // we should append them for Shortcut/Access keys.
         // E.g., for Cyrillic keyboard layout.
-        WidgetUtils::GetLatinCharCodeForKeyCode(DOMKeyCode, capsLockState,
+        capsLockState.Unset(MODIFIER_SHIFT);
+        WidgetUtils::GetLatinCharCodeForKeyCode(DOMKeyCode,
+                                                capsLockState.GetModifiers(),
                                                 &unshiftedLatinChar,
                                                 &shiftedLatinChar);
 
         // If the shiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
         if (shiftedLatinChar) {
           // If the produced characters of the key on current keyboard layout
           // are same as computed Latin characters, we shouldn't append the
           // Latin characters to alternativeCharCode.
-          if (unshiftedLatinChar == unshiftedChars[0] &&
-              shiftedLatinChar == shiftedChars[0]) {
+          if (unshiftedLatinChar == unshiftedChars.mChars[0] &&
+              shiftedLatinChar == shiftedChars.mChars[0]) {
             shiftedLatinChar = unshiftedLatinChar = 0;
           }
         } else if (unshiftedLatinChar) {
           // If the shiftedLatinChar is 0, the keyCode doesn't produce
           // alphabet character.  At that time, the character may be produced
           // with Shift key.  E.g., on French keyboard layout, NS_VK_PERCENT
           // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
           // Shift key but with Shift key, it produces '%'.
           // If the unshiftedLatinChar is produced by the key on current
           // keyboard layout, we shouldn't append it to alternativeCharCode.
-          if (unshiftedLatinChar == unshiftedChars[0] ||
-              unshiftedLatinChar == shiftedChars[0]) {
+          if (unshiftedLatinChar == unshiftedChars.mChars[0] ||
+              unshiftedLatinChar == shiftedChars.mChars[0]) {
             unshiftedLatinChar = 0;
           }
         }
 
         // If the charCode is not ASCII character, we should replace the
         // charCode with ASCII character only when Ctrl is pressed.
         // But don't replace the charCode when the charCode is not same as
         // unmodified characters. In such case, Ctrl is sometimes used for a
         // part of character inputting key combination like Shift.
         if (aModKeyState.IsControl()) {
           PRUint32 ch =
             aModKeyState.IsShift() ? shiftedLatinChar : unshiftedLatinChar;
           if (ch &&
-              (numOfUniChars == 0 ||
-               StringCaseInsensitiveEquals(uniChars, numOfUniChars,
-                 aModKeyState.IsShift() ? shiftedChars : unshiftedChars,
-                 aModKeyState.IsShift() ? numOfShiftedChars :
-                                          numOfUnshiftedChars))) {
-            numOfUniChars = numOfShiftStates = 1;
-            uniChars[0] = ch;
-            modifiersOfChars[0] = aModKeyState.GetModifiers();
+              (!inputtingChars.mLength ||
+               inputtingChars.UniCharsCaseInsensitiveEqual(
+                 aModKeyState.IsShift() ? shiftedChars : unshiftedChars))) {
+            inputtingChars.Clear();
+            inputtingChars.Append(ch, aModKeyState.GetModifiers());
           }
         }
       }
   }
 
-  if (numOfUniChars > 0 || numOfShiftedChars > 0 || numOfUnshiftedChars > 0) {
-    PRUint32 num = NS_MAX(numOfUniChars,
-                          NS_MAX(numOfShiftedChars, numOfUnshiftedChars));
-    PRUint32 skipUniChars = num - numOfUniChars;
-    PRUint32 skipShiftedChars = num - numOfShiftedChars;
-    PRUint32 skipUnshiftedChars = num - numOfUnshiftedChars;
-    UINT keyCode = numOfUniChars == 0 ? DOMKeyCode : 0;
+  if (inputtingChars.mLength ||
+      shiftedChars.mLength || unshiftedChars.mLength) {
+    PRUint32 num = NS_MAX(inputtingChars.mLength,
+                          NS_MAX(shiftedChars.mLength, unshiftedChars.mLength));
+    PRUint32 skipUniChars = num - inputtingChars.mLength;
+    PRUint32 skipShiftedChars = num - shiftedChars.mLength;
+    PRUint32 skipUnshiftedChars = num - unshiftedChars.mLength;
+    UINT keyCode = !inputtingChars.mLength ? DOMKeyCode : 0;
     for (PRUint32 cnt = 0; cnt < num; cnt++) {
       PRUint16 uniChar, shiftedChar, unshiftedChar;
       uniChar = shiftedChar = unshiftedChar = 0;
       ModifierKeyState modKeyState(aModKeyState);
       if (skipUniChars <= cnt) {
-        if (cnt - skipUniChars  < numOfShiftStates) {
+        if (cnt - skipUniChars  < inputtingChars.mLength) {
           // If key in combination with Alt and/or Ctrl produces a different
           // character than without them then do not report these flags
           // because it is separate keyboard layout shift state. If dead-key
           // and base character does not produce a valid composite character
           // then both produced dead-key character and following base
           // character may have different modifier flags, too.
           modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
                             MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
-          modKeyState.Set(modifiersOfChars[cnt - skipUniChars]);
+          modKeyState.Set(inputtingChars.mModifiers[cnt - skipUniChars]);
         }
-        uniChar = uniChars[cnt - skipUniChars];
+        uniChar = inputtingChars.mChars[cnt - skipUniChars];
       }
       if (skipShiftedChars <= cnt)
-        shiftedChar = shiftedChars[cnt - skipShiftedChars];
+        shiftedChar = shiftedChars.mChars[cnt - skipShiftedChars];
       if (skipUnshiftedChars <= cnt)
-        unshiftedChar = unshiftedChars[cnt - skipUnshiftedChars];
+        unshiftedChar = unshiftedChars.mChars[cnt - skipUnshiftedChars];
       nsAutoTArray<nsAlternativeCharCode, 5> altArray;
 
       if (shiftedChar || unshiftedChar) {
         nsAlternativeCharCode chars(unshiftedChar, shiftedChar);
         altArray.AppendElement(chars);
       }
       if (cnt == num - 1) {
         if (unshiftedLatinChar || shiftedLatinChar) {
@@ -6560,18 +6552,18 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
         PRUnichar charForOEMKeyCode = 0;
         switch (virtualKeyCode) {
           case VK_OEM_PLUS:   charForOEMKeyCode = '+'; break;
           case VK_OEM_COMMA:  charForOEMKeyCode = ','; break;
           case VK_OEM_MINUS:  charForOEMKeyCode = '-'; break;
           case VK_OEM_PERIOD: charForOEMKeyCode = '.'; break;
         }
         if (charForOEMKeyCode &&
-            charForOEMKeyCode != unshiftedChars[0] &&
-            charForOEMKeyCode != shiftedChars[0] &&
+            charForOEMKeyCode != unshiftedChars.mChars[0] &&
+            charForOEMKeyCode != shiftedChars.mChars[0] &&
             charForOEMKeyCode != unshiftedLatinChar &&
             charForOEMKeyCode != shiftedLatinChar) {
           nsAlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
           altArray.AppendElement(OEMChars);
         }
       }
 
       nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);