Bug 757688 part.7 Make nsWindow for Windows possible to test dead keys r=jimm
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 15 Jun 2012 18:52:51 +0900
changeset 96821 f5b8c6a545cf633d50b390b07bbf3da3b79023d3
parent 96820 d3bed04319ab696f102d6ebe0399d5c7c55fdc3e
child 96822 c4b46347c04bd25be43d2cb4e71d11e574e0524e
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjimm
bugs757688
milestone16.0a1
Bug 757688 part.7 Make nsWindow for Windows possible to test dead keys r=jimm
widget/tests/test_keycodes.xul
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
widget/windows/nsWindow.cpp
widget/windows/nsWindowDefs.h
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -86,16 +86,17 @@ if (IS_MAC) {
   };
 } else if (IS_WIN) {
   // These constants can be found by inspecting registry keys under
   // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts
   keyboardLayouts = {
     "US":0x409,
     "German":0x407,
     "Greek":0x408,
+    "Spanish":0x40a,
     "French":0x40c,
     "Swedish":0x41d,
     "Arabic":0x401,
     "Hebrew":0x40d,
     "Japanese":0x411,
     "Norwegian":0x414,
     "Lithuanian":0x10427,
     "Thai": 0x2041e
@@ -243,40 +244,45 @@ function runKeyEventTests()
 
     synthesizeKey(aEvent, "button");
 
     var name = eventToString(aEvent);
 
     var expectEventTypeList = [];
     if (aShouldDelivedEvent & SHOULD_DELIVER_KEYDOWN)
       expectEventTypeList.push("keydown");
-    if (aShouldDelivedEvent & SHOULD_DELIVER_KEYPRESS)
+    if (aShouldDelivedEvent & SHOULD_DELIVER_KEYPRESS) {
       expectEventTypeList.push("keypress");
+      for (var i = 1; i < aExpectGeckoChar.length; i++) {
+        expectEventTypeList.push("keypress");
+      }
+    }
     if (aShouldDelivedEvent & SHOULD_DELIVER_KEYUP)
       expectEventTypeList.push("keyup");
     is(eventList.length, expectEventTypeList.length, name + ", wrong number of key events");
 
     var longerLength = Math.max(eventList.length, expectEventTypeList.length);
+    var keypressCount = 0;
     for (var i = 0; i < longerLength; i++) {
       var firedEventType = i < eventList.length ? eventList[i].type : "";
       var expectEventType = i < expectEventTypeList.length ? expectEventTypeList[i] : "";
       if (firedEventType != "")
         is(firedEventType, expectEventType, name + ", wrong type event fired");
       else
         is(firedEventType, expectEventType, name + ", a needed event is not fired");
 
       if (firedEventType != "") {
         var e = eventList[i];
         is(e.ctrlKey, aEvent.ctrl || aEvent.ctrlRight || 0, name + ", Ctrl mismatch");
         is(e.metaKey, aEvent.command || aEvent.commandRight || 0, name + ", Command mismatch");
         is(e.altKey, aEvent.alt || aEvent.altRight || 0, name + ", Alt mismatch");
         is(e.shiftKey, aEvent.shift || aEvent.shiftRight || 0, name + ", Shift mismatch");
 
         if (aExpectGeckoChar.length > 0 && e.type == "keypress") {
-          is(e.charCode, aExpectGeckoChar.charCodeAt(0), name + ", charcode");
+          is(e.charCode, aExpectGeckoChar.charCodeAt(keypressCount++), name + ", charcode");
           if (aExpectedGeckoKeyCode >= 0) {
             if (aExpectGeckoChar) {
               is(e.keyCode, 0, name + ", wrong keycode");
             } else {
               is(e.keyCode, aExpectedGeckoKeyCode, name + ", wrong keycode");
             }
           }
         } else {
@@ -1756,19 +1762,19 @@ function runKeyEventTests()
             nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, ")", SHOULD_DELIVER_ALL);
     testKey({layout:"French", keyCode:219, shift:1, chars:"\u00B0"},
             nsIDOMKeyEvent.DOM_VK_CLOSE_PAREN, "\u00B0", SHOULD_DELIVER_ALL);
     testKey({layout:"French", keyCode:187, chars:"="},
             nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
     testKey({layout:"French", keyCode:187, shift:1, chars:"+"},
             nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL);
     //testKey({layout:"French", keyCode:221, chars:""},
-    //        0, "", SHOULD_DELIVER_KEYDOWN_KEYUP); // Dead-key
+    //        nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP); // Dead-key
     //testKey({layout:"French", keyCode:221, shift:1, chars:""},
-    //        0, "", SHOULD_DELIVER_KEYDOWN_KEYUP); // Dead-key
+    //        nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_ALL); // Dead-key
     testKey({layout:"French", keyCode:186, chars:"$"},
             nsIDOMKeyEvent.DOM_VK_DOLLAR, "$", SHOULD_DELIVER_ALL);
     testKey({layout:"French", keyCode:186, shift:1, chars:"\u00A3"},
             nsIDOMKeyEvent.DOM_VK_DOLLAR, "\u00A3", SHOULD_DELIVER_ALL);
     testKey({layout:"French", keyCode:192, chars:"\u00F9"},
             nsIDOMKeyEvent.DOM_VK_PERCENT, "\u00F9", SHOULD_DELIVER_ALL);
     testKey({layout:"French", keyCode:192, shift:1, chars:"%"},
             nsIDOMKeyEvent.DOM_VK_PERCENT, "%", SHOULD_DELIVER_ALL);
@@ -1879,16 +1885,137 @@ function runKeyEventTests()
     testKey({layout:"German", keyCode:191, shift:1, chars:"'"},
             nsIDOMKeyEvent.DOM_VK_HASH, "'", SHOULD_DELIVER_ALL);
 
     // Norwegian
     testKey({layout:"Norwegian", keyCode:220, chars:"|"},
             nsIDOMKeyEvent.DOM_VK_PIPE, "|", SHOULD_DELIVER_ALL);
     testKey({layout:"Norwegian", keyCode:220, shift:1, chars:"\u00A7"},
             nsIDOMKeyEvent.DOM_VK_PIPE, "\u00A7", SHOULD_DELIVER_ALL);
+
+    // Dead keys on any layouts
+    testKey({layout:"French", keyCode:221, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:221, chars:"^^"},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "^^", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:65, chars:"\u00E2"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E2", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:65, shift:1, chars:"\u00C2"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00C2", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:81, chars:"^q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "^q", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:221, shift:1, chars:"\u00A8\u00A8"},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "\u00A8\u00A8", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:65, shift:1, chars:"\u00C4"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00C4", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:65, chars:"\u00E4"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E4", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"French", keyCode:221, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_CIRCUMFLEX, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"French", keyCode:81, shift:1, chars:"\u00A8Q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "\u00A8Q", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:186, chars:"``"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "``", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, chars:"\u00E0"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E0", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, shift:1, chars:"\u00C0"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00C0", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:81, chars:"`q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "`q", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:186, shift:1, chars:"^^"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "^^", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, shift:1, chars:"\u00C2"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00C2", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, chars:"\u00E2"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E2", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:186, shift:1, chars:""},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:81, shift:1, chars:"^Q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "^Q", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:222, chars:"\u00B4\u00B4"},
+            0, "\u00B4\u00B4", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, chars:"\u00E1"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E1", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, shift:1, chars:"\u00C1"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00C1", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:81, chars:"\u00B4q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "\u00B4q", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, shift:1, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:222, shift:1, chars:"\u00A8\u00A8"},
+            0, "\u00A8\u00A8", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, shift:1, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, shift:1, chars:"\u00C4"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00C4", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, shift:1, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:65, chars:"\u00E4"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E4", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"Spanish", keyCode:222, shift:1, chars:""},
+            0, "", SHOULD_DELIVER_KEYDOWN_KEYUP);
+    testKey({layout:"Spanish", keyCode:81, shift:1, chars:"\u00A8Q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "\u00A8Q", SHOULD_DELIVER_ALL);
   }
 
   document.removeEventListener("keydown",  onKeyEvent, false);
   document.removeEventListener("keypress", onKeyEvent, false);
   document.removeEventListener("keyup",    onKeyEvent, false);
 }
 
 // Test the activation (or not) of an HTML accesskey
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -508,17 +508,17 @@ NativeKey::GetKeyLocation() const
   }
 }
 
 /*****************************************************************************
  * mozilla::widget::KeyboardLayout
  *****************************************************************************/
 
 KeyboardLayout::KeyboardLayout() :
-  mKeyboardLayout(0)
+  mKeyboardLayout(0), mPendingKeyboardLayout(0)
 {
   mDeadKeyTableListHead = nsnull;
 
   // Note: Don't call LoadLayout from here. Because an instance of this class
   // can be static. In that case, we cannot use any services in LoadLayout,
   // e.g., pref service.
 }
 
@@ -545,16 +545,20 @@ KeyboardLayout::IsDeadKey(PRUint8 aVirtu
   return mVirtualKeys[virtualKeyIndex].IsDeadKey(
            VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
 }
 
 UniCharsAndModifiers
 KeyboardLayout::OnKeyDown(PRUint8 aVirtualKey,
                           const ModifierKeyState& aModKeyState)
 {
+  if (mPendingKeyboardLayout) {
+    LoadLayout(mPendingKeyboardLayout);
+  }
+
   PRInt32 virtualKeyIndex = GetKeyIndex(aVirtualKey);
 
   if (virtualKeyIndex < 0) {
     // Does not produce any printable characters, but still preserves the
     // dead-key state.
     return UniCharsAndModifiers();
   }
 
@@ -621,18 +625,25 @@ KeyboardLayout::GetUniCharsAndModifiers(
   if (key < 0) {
     return result;
   }
   return mVirtualKeys[key].
     GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
 }
 
 void
-KeyboardLayout::LoadLayout(HKL aLayout)
+KeyboardLayout::LoadLayout(HKL aLayout, bool aLoadLater)
 {
+  if (aLoadLater) {
+    mPendingKeyboardLayout = aLayout;
+    return;
+  }
+
+  mPendingKeyboardLayout = 0;
+
   if (mKeyboardLayout == aLayout) {
     return;
   }
 
   mKeyboardLayout = aLayout;
 
   BYTE kbdState[256];
   memset(kbdState, 0, sizeof(kbdState));
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -3,16 +3,17 @@
  * 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/. */
 
 #ifndef KeyboardLayout_h__
 #define KeyboardLayout_h__
 
 #include "nscore.h"
 #include "nsEvent.h"
+#include "nsString.h"
 #include <windows.h>
 
 #define NS_NUM_OF_KEYS          68
 
 #define VK_OEM_1                0xBA   // ';:' for US
 #define VK_OEM_PLUS             0xBB   // '+' any country
 #define VK_OEM_COMMA            0xBC
 #define VK_OEM_MINUS            0xBD   // '-' any country
@@ -130,16 +131,18 @@ struct UniCharsAndModifiers
    */
   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;
+
+  nsString ToString() const { return nsString(mChars, mLength); }
 };
 
 struct DeadKeyEntry;
 class DeadKeyTable;
 
 
 class VirtualKey
 {
@@ -300,16 +303,17 @@ class KeyboardLayout
 {
   struct DeadKeyTableListEntry
   {
     DeadKeyTableListEntry* next;
     PRUint8 data[1];
   };
 
   HKL mKeyboardLayout;
+  HKL mPendingKeyboardLayout;
 
   VirtualKey mVirtualKeys[NS_NUM_OF_KEYS];
   DeadKeyTableListEntry* mDeadKeyTableListHead;
   PRInt32 mActiveDeadKey;                 // -1 = no active dead-key
   VirtualKey::ShiftState mDeadKeyShiftState;
 
   static inline PRInt32 GetKeyIndex(PRUint8 aVirtualKey);
   static int CompareDeadKeyEntries(const void* aArg1, const void* aArg2,
@@ -352,19 +356,26 @@ public:
   /**
    * 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).
    */
   UniCharsAndModifiers OnKeyDown(PRUint8 aVirtualKey,
                                  const ModifierKeyState& aModKeyState);
 
-  void LoadLayout(HKL aLayout);
+  /**
+   * LoadLayout() loads the keyboard layout.  If aLoadLater is true,
+   * it will be done when OnKeyDown() is called.
+   */
+  void LoadLayout(HKL aLayout, bool aLoadLater = false);
 
   PRUint32 ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const;
 
-  HKL GetLayout() const { return mKeyboardLayout; }
+  HKL GetLayout() const
+  {
+    return mPendingKeyboardLayout ? mPendingKeyboardLayout : mKeyboardLayout;
+  }
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -5648,20 +5648,36 @@ LRESULT nsWindow::ProcessKeyDownMessage(
 
 nsresult
 nsWindow::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
                                    PRInt32 aNativeKeyCode,
                                    PRUint32 aModifierFlags,
                                    const nsAString& aCharacters,
                                    const nsAString& aUnmodifiedCharacters)
 {
+  UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, NULL);
+  NS_ASSERTION(keyboardLayoutListCount > 0,
+               "One keyboard layout must be installed at least");
+  HKL keyboardLayoutListBuff[50];
+  HKL* keyboardLayoutList =
+    keyboardLayoutListCount < 50 ? keyboardLayoutListBuff :
+                                   new HKL[keyboardLayoutListCount];
+  keyboardLayoutListCount =
+    ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList);
+  NS_ASSERTION(keyboardLayoutListCount > 0,
+               "Failed to get all keyboard layouts installed on the system");
+
   nsPrintfCString layoutName("%08x", aNativeKeyboardLayout);
   HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL);
-  if (loadedLayout == NULL)
+  if (loadedLayout == NULL) {
+    if (keyboardLayoutListBuff != keyboardLayoutList) {
+      delete [] keyboardLayoutList;
+    }
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
   // Setup clean key state and load desired layout
   BYTE originalKbdState[256];
   ::GetKeyboardState(originalKbdState);
   BYTE kbdState[256];
   memset(kbdState, 0, sizeof(kbdState));
   // This changes the state of the keyboard for the current thread only,
   // and we'll restore it soon, so this should be OK.
@@ -5689,19 +5705,40 @@ nsWindow::SynthesizeNativeKeyEvent(PRInt
                                       gKbdLayout.GetLayout());
     LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
     // Add extended key flag to the lParam for right control key and right alt
     // key.
     if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
       lParam |= 0x1000000;
     }
     MSG msg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam);
-    if (i == keySequence.Length() - 1 && aCharacters.Length() > 0) {
-      nsFakeCharMessage fakeMsg = { aCharacters.CharAt(0), scanCode };
-      OnKeyDown(msg, modKeyState, nsnull, &fakeMsg);
+    if (i == keySequence.Length() - 1) {
+      bool makeDeadCharMessage =
+        gKbdLayout.IsDeadKey(key, modKeyState) && aCharacters.IsEmpty();
+      nsAutoString chars(aCharacters);
+      if (makeDeadCharMessage) {
+        UniCharsAndModifiers deadChars =
+          gKbdLayout.GetUniCharsAndModifiers(key, modKeyState);
+        chars = deadChars.ToString();
+        NS_ASSERTION(chars.Length() == 1,
+                     "Dead char must be only one character");
+      }
+      if (chars.IsEmpty()) {
+        OnKeyDown(msg, modKeyState, nsnull, nsnull);
+      } else {
+        nsFakeCharMessage fakeMsg = { chars.CharAt(0), scanCode,
+                                      makeDeadCharMessage };
+        OnKeyDown(msg, modKeyState, nsnull, &fakeMsg);
+        for (PRUint32 j = 1; j < chars.Length(); j++) {
+          nsFakeCharMessage fakeMsg = { chars.CharAt(j), scanCode, false };
+          MSG msg = fakeMsg.GetCharMessage(mWnd);
+          NativeKey nativeKey(gKbdLayout, this, msg);
+          OnChar(msg, nativeKey, modKeyState, nsnull);
+        }
+      }
     } else {
       OnKeyDown(msg, modKeyState, nsnull, nsnull);
     }
   }
   for (PRUint32 i = keySequence.Length(); i > 0; --i) {
     PRUint8 key = keySequence[i - 1].mGeneral;
     PRUint8 keySpecific = keySequence[i - 1].mSpecific;
     kbdState[key] = 0; // key is up and toggled off if appropriate
@@ -5719,19 +5756,31 @@ nsWindow::SynthesizeNativeKeyEvent(PRInt
       lParam |= 0x1000000;
     }
     MSG msg = WinUtils::InitMSG(WM_KEYUP, key, lParam);
     OnKeyUp(msg, modKeyState, nsnull);
   }
 
   // Restore old key state and layout
   ::SetKeyboardState(originalKbdState);
-  gKbdLayout.LoadLayout(oldLayout);
-
-  UnloadKeyboardLayout(loadedLayout);
+  gKbdLayout.LoadLayout(oldLayout, true);
+
+  // Don't unload the layout if it's installed actually.
+  for (PRUint32 i = 0; i < keyboardLayoutListCount; i++) {
+    if (keyboardLayoutList[i] == loadedLayout) {
+      loadedLayout = 0;
+      break;
+    }
+  }
+  if (keyboardLayoutListBuff != keyboardLayoutList) {
+    delete [] keyboardLayoutList;
+  }
+  if (loadedLayout) {
+    ::UnloadKeyboardLayout(loadedLayout);
+  }
   return NS_OK;
 }
 
 nsresult
 nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
                                      PRUint32 aNativeMessage,
                                      PRUint32 aModifierFlags)
 {
@@ -6353,16 +6402,19 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
       RemoveMessageAndDispatchPluginEvent(WM_CHAR, WM_CHAR);
     }
   }
   else if (gotMsg &&
            (aFakeCharMessage ||
             msg.message == WM_CHAR || msg.message == WM_SYSCHAR || msg.message == WM_DEADCHAR)) {
     if (aFakeCharMessage) {
       MSG msg = aFakeCharMessage->GetCharMessage(mWnd);
+      if (msg.message == WM_DEADCHAR) {
+        return false;
+      }
       return OnChar(msg, nativeKey, aModKeyState, nsnull, extraFlags);
     }
 
     // If prevent default set for keydown, do same for keypress
     ::GetMessageW(&msg, mWnd, msg.message, msg.message);
 
     if (msg.message == WM_DEADCHAR) {
       if (!PluginHasFocus())
--- a/widget/windows/nsWindowDefs.h
+++ b/widget/windows/nsWindowDefs.h
@@ -206,22 +206,23 @@ static const PRUint32 sModifierKeyMap[][
  * 
  **************************************************************/
 
 // Used in OnKeyDown
 struct nsAlternativeCharCode; // defined in nsGUIEvent.h
 struct nsFakeCharMessage {
   UINT mCharCode;
   UINT mScanCode;
+  bool mIsDeadKey;
 
   MSG GetCharMessage(HWND aWnd)
   {
     MSG msg;
     msg.hwnd = aWnd;
-    msg.message = WM_CHAR;
+    msg.message = mIsDeadKey ? WM_DEADCHAR : WM_CHAR;
     msg.wParam = static_cast<WPARAM>(mCharCode);
     msg.lParam = static_cast<LPARAM>(mScanCode);
     msg.time = 0;
     msg.pt.x = msg.pt.y = 0;
     return msg;
   }
 };