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 99536 f5b8c6a545cf633d50b390b07bbf3da3b79023d3
parent 99535 d3bed04319ab696f102d6ebe0399d5c7c55fdc3e
child 99537 c4b46347c04bd25be43d2cb4e71d11e574e0524e
push idunknown
push userunknown
push dateunknown
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;
   }
 };