Bug 166240 part.2 Add support KeyboardEvent.location on Windows r=jimm
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 03 May 2012 17:35:02 +0900
changeset 92956 1b790392d57048a4bbed1967d4d67077864c76bc
parent 92955 03c2b4944d971619601929aa3f8308c6d79552e7
child 92957 3472062aae7fc89d1bcf6a2dff59eecae39fe0c0
push id8914
push usermasayuki@d-toybox.com
push dateThu, 03 May 2012 08:35:15 +0000
treeherdermozilla-inbound@de5745bce8bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs166240
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 166240 part.2 Add support KeyboardEvent.location on Windows r=jimm
widget/windows/KeyboardLayout.cpp
widget/windows/KeyboardLayout.h
widget/windows/nsIMM32Handler.cpp
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowDefs.h
--- a/widget/windows/KeyboardLayout.cpp
+++ b/widget/windows/KeyboardLayout.cpp
@@ -38,17 +38,21 @@
 #include "mozilla/Util.h"
 
 #include "KeyboardLayout.h"
 
 #include "nsMemory.h"
 #include "nsToolkit.h"
 #include "nsQuickSort.h"
 #include "nsAlgorithm.h"
+#include "WinUtils.h"
 
+#include "nsIDOMKeyEvent.h"
+
+#include <windows.h>
 #include <winuser.h>
 
 #ifndef WINABLEAPI
 #include <winable.h>
 #endif
 
 namespace mozilla {
 namespace widget {
@@ -217,16 +221,141 @@ VirtualKey::GetNativeUniChars(PRUint8 aS
        index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) {
     if (aUniChars) {
       aUniChars[index] = mShiftStates[aShiftState].Normal.Chars[index];
     }
   }
   return index;
 }
 
+NativeKey::NativeKey(HKL aKeyboardLayout,
+                     HWND aWnd,
+                     const MSG& aKeyOrCharMessage) :
+  mVirtualKeyCode(0), mOriginalVirtualKeyCode(0)
+{
+  mScanCode = WinUtils::GetScanCode(aKeyOrCharMessage.lParam);
+  mIsExtended = WinUtils::IsExtendedScanCode(aKeyOrCharMessage.lParam);
+  switch (aKeyOrCharMessage.message) {
+    case WM_KEYDOWN:
+    case WM_KEYUP:
+    case WM_SYSKEYDOWN:
+    case WM_SYSKEYUP:
+      mOriginalVirtualKeyCode = static_cast<PRUint8>(aKeyOrCharMessage.wParam);
+      switch (aKeyOrCharMessage.wParam) {
+        case VK_CONTROL:
+        case VK_MENU:
+        case VK_SHIFT:
+          mVirtualKeyCode = static_cast<PRUint8>(
+            ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(),
+                              MAPVK_VSC_TO_VK_EX, aKeyboardLayout));
+          break;
+        case VK_PROCESSKEY:
+          mVirtualKeyCode = mOriginalVirtualKeyCode =
+            static_cast<PRUint8>(::ImmGetVirtualKey(aWnd));
+          break;
+        default:
+          mVirtualKeyCode = mOriginalVirtualKeyCode;
+          break;
+      }
+      break;
+    case WM_CHAR:
+    case WM_UNICHAR:
+    case WM_SYSCHAR:
+      // We cannot compute the virtual key code from WM_CHAR message on WinXP
+      // and 
+      if (mIsExtended &&
+          WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
+        break;
+      }
+      mVirtualKeyCode = mOriginalVirtualKeyCode = static_cast<PRUint8>(
+        ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(),
+                          MAPVK_VSC_TO_VK_EX, aKeyboardLayout));
+    default:
+      MOZ_NOT_REACHED("Unsupported message");
+      break;
+  }
+
+  if (!mVirtualKeyCode) {
+    mVirtualKeyCode = mOriginalVirtualKeyCode;
+  }
+}
+
+UINT
+NativeKey::GetScanCodeWithExtendedFlag() const
+{
+  // MapVirtualKeyEx() has been improved for supporting extended keys since
+  // Vista.  When we call it for mapping a scancode of an extended key and
+  // a virtual keycode, we need to add 0xE000 to the scancode.
+  // On Win XP and Win Server 2003, this doesn't support. On them, we have
+  // no way to get virtual keycodes from scancode of extended keys.
+  if (!mIsExtended ||
+      WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
+    return mScanCode;
+  }
+  return (0xE000 | mScanCode);
+}
+
+PRUint32
+NativeKey::GetKeyLocation() const
+{
+  switch (mVirtualKeyCode) {
+    case VK_LSHIFT:
+    case VK_LCONTROL:
+    case VK_LMENU:
+    case VK_LWIN:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
+
+    case VK_RSHIFT:
+    case VK_RCONTROL:
+    case VK_RMENU:
+    case VK_RWIN:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
+
+    case VK_RETURN:
+      return !mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD :
+                            nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+
+    case VK_INSERT:
+    case VK_DELETE:
+    case VK_END:
+    case VK_DOWN:
+    case VK_NEXT:
+    case VK_LEFT:
+    case VK_CLEAR:
+    case VK_RIGHT:
+    case VK_HOME:
+    case VK_UP:
+    case VK_PRIOR:
+      return mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD :
+                           nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+
+    // NumLock key isn't included due to IE9's behavior.
+    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:
+    case VK_DECIMAL:
+    case VK_DIVIDE:
+    case VK_MULTIPLY:
+    case VK_SUBTRACT:
+    case VK_ADD:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
+
+    default:
+      return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
+  }
+}
+
+
 KeyboardLayout::KeyboardLayout() :
   mKeyboardLayout(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.
--- a/widget/windows/KeyboardLayout.h
+++ b/widget/windows/KeyboardLayout.h
@@ -126,16 +126,45 @@ public:
   inline PRUnichar GetCompositeChar(PRUint8 aShiftState,
                                     PRUnichar aBaseChar) const;
   PRUint32 GetNativeUniChars(PRUint8 aShiftState,
                              PRUnichar* aUniChars = nsnull) const;
   PRUint32 GetUniChars(PRUint8 aShiftState, PRUnichar* aUniChars,
                        PRUint8* aFinalShiftState) const;
 };
 
+class NativeKey {
+public:
+  NativeKey() :
+    mVirtualKeyCode(0), mOriginalVirtualKeyCode(0),
+    mScanCode(0), mIsExtended(false)
+  {
+  }
+
+  NativeKey(HKL aKeyboardLayout,
+            HWND aWnd,
+            const MSG& aKeyOrCharMessage);
+
+  // The result is one of nsIDOMKeyEvent::DOM_KEY_LOCATION_*.
+  PRUint32 GetKeyLocation() const;
+  PRUint8 GetVirtualKeyCode() const { return mVirtualKeyCode; }
+  PRUint8 GetOriginalVirtualKeyCode() const { return mOriginalVirtualKeyCode; }
+
+private:
+  // mVirtualKeyCode distinguishes left key or right key of modifier key.
+  PRUint8 mVirtualKeyCode;
+  // mOriginalVirtualKeyCode doesn't distinguish left key or right key of
+  // modifier key.  However, if the given keycode is VK_PROCESS, it's resolved
+  // to a keycode before it's handled by IME.
+  PRUint8 mOriginalVirtualKeyCode;
+  WORD    mScanCode;
+  bool    mIsExtended;
+
+  UINT GetScanCodeWithExtendedFlag() const;
+};
 
 class KeyboardLayout
 {
   struct DeadKeyTableListEntry
   {
     DeadKeyTableListEntry* next;
     PRUint8 data[1];
   };
--- a/widget/windows/nsIMM32Handler.cpp
+++ b/widget/windows/nsIMM32Handler.cpp
@@ -56,16 +56,17 @@
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG /* Allow logging in the release build */
 #endif // MOZ_LOGGING
 #include "prlog.h"
 
 #include "nsIMM32Handler.h"
 #include "nsWindow.h"
 #include "WinUtils.h"
+#include "KeyboardLayout.h"
 
 using namespace mozilla::widget;
 
 static nsIMM32Handler* gIMM32Handler = nsnull;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gIMM32Log = nsnull;
 #endif
@@ -759,17 +760,21 @@ nsIMM32Handler::OnIMENotify(nsWindow* aW
   // Japanese IME incorrectly activates "File" menu).  If one or more keypress
   // events come between Alt keydown event and Alt keyup event, XUL menubar
   // isn't activated by the Alt keyup event.  Therefore, this code sends dummy
   // keypress event to Gecko.  But this is ugly, and this fires incorrect DOM
   // keypress event.  So, we should find another way for the bug.
 
   // add hacky code here
   nsModifierKeyState modKeyState(false, false, true);
-  aWindow->DispatchKeyEvent(NS_KEY_PRESS, 0, nsnull, 192, nsnull, modKeyState);
+  mozilla::widget::NativeKey nativeKey; // Dummy is okay for this usage.
+  nsKeyEvent keyEvent(true, NS_KEY_PRESS, aWindow);
+  keyEvent.keyCode = 192;
+  aWindow->InitKeyEvent(keyEvent, nativeKey, modKeyState);
+  aWindow->DispatchKeyEvent(keyEvent, nsnull);
   sIsStatusChanged = sIsStatusChanged || (wParam == IMN_SETOPENSTATUS);
   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
     ("IMM32: OnIMENotify, sIsStatusChanged=%s\n",
      sIsStatusChanged ? "TRUE" : "FALSE"));
 
   // not implement yet
   return false;
 }
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -3546,73 +3546,40 @@ bool nsWindow::DispatchWindowEvent(nsGUI
   return ConvertStatus(status);
 }
 
 bool nsWindow::DispatchWindowEvent(nsGUIEvent* event, nsEventStatus &aStatus) {
   DispatchEvent(event, aStatus);
   return ConvertStatus(aStatus);
 }
 
-bool nsWindow::DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode,
-                   const nsTArray<nsAlternativeCharCode>* aAlternativeCharCodes,
-                   UINT aVirtualCharCode, const MSG *aMsg,
-                   const nsModifierKeyState &aModKeyState,
-                   PRUint32 aFlags)
+void nsWindow::InitKeyEvent(nsKeyEvent& aKeyEvent,
+                            const NativeKey& aNativeKey,
+                            const nsModifierKeyState &aModKeyState)
+{
+  nsIntPoint point(0, 0);
+  InitEvent(aKeyEvent, &point);
+  aKeyEvent.location = aNativeKey.GetKeyLocation();
+  aModKeyState.InitInputEvent(aKeyEvent);
+}
+
+bool nsWindow::DispatchKeyEvent(nsKeyEvent& aKeyEvent,
+                                const MSG *aMsgSentToPlugin)
 {
   UserActivity();
 
-  nsKeyEvent event(true, aEventType, this);
-  nsIntPoint point(0, 0);
-
-  InitEvent(event, &point); // this add ref's event.widget
-
-  event.flags |= aFlags;
-  event.charCode = aCharCode;
-  if (aAlternativeCharCodes)
-    event.alternativeCharCodes.AppendElements(*aAlternativeCharCodes);
-  event.keyCode  = aVirtualCharCode;
-
-#ifdef KE_DEBUG
-  static cnt=0;
-  PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
-         ("%d DispatchKE Type: %s charCode %d  keyCode %d ", cnt++, 
-          NS_KEY_PRESS == aEventType ? "PRESS : 
-                                       (aEventType == NS_KEY_UP ? "Up" : "Down"),
-          event.charCode, event.keyCode));
-
-  PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
-         ("Shift: %s Control %s Alt: %s \n", (mIsShiftDown ? "D" : "U"), 
-         (mIsControlDown ? "D" : "U"), (mIsAltDown ? "D" : "U")));
-
-  PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
-         ("[%c][%c][%c] <==   [%c][%c][%c][ space bar ][%c][%c][%c]\n",
-          IS_VK_DOWN(NS_VK_SHIFT) ? 'S' : ' ',
-          IS_VK_DOWN(NS_VK_CONTROL) ? 'C' : ' ',
-          IS_VK_DOWN(NS_VK_ALT) ? 'A' : ' ',
-          IS_VK_DOWN(VK_LSHIFT) ? 'S' : ' ',
-          IS_VK_DOWN(VK_LCONTROL) ? 'C' : ' ',
-          IS_VK_DOWN(VK_LMENU) ? 'A' : ' ',
-          IS_VK_DOWN(VK_RMENU) ? 'A' : ' ',
-          IS_VK_DOWN(VK_RCONTROL) ? 'C' : ' ',
-          IS_VK_DOWN(VK_RSHIFT) ? 'S' : ' '));
-#endif
-
-  aModKeyState.InitInputEvent(event);
-
   NPEvent pluginEvent;
-  if (aMsg && PluginHasFocus()) {
-    pluginEvent.event = aMsg->message;
-    pluginEvent.wParam = aMsg->wParam;
-    pluginEvent.lParam = aMsg->lParam;
-    event.pluginEvent = (void *)&pluginEvent;
-  }
-
-  bool result = DispatchWindowEvent(&event);
-
-  return result;
+  if (aMsgSentToPlugin && PluginHasFocus()) {
+    pluginEvent.event = aMsgSentToPlugin->message;
+    pluginEvent.wParam = aMsgSentToPlugin->wParam;
+    pluginEvent.lParam = aMsgSentToPlugin->lParam;
+    aKeyEvent.pluginEvent = (void *)&pluginEvent;
+  }
+
+  return DispatchWindowEvent(&aKeyEvent);
 }
 
 bool nsWindow::DispatchCommandEvent(PRUint32 aEventCommand)
 {
   nsCOMPtr<nsIAtom> command;
   switch (aEventCommand) {
     case APPCOMMAND_BROWSER_BACKWARD:
       command = nsGkAtoms::Back;
@@ -5597,17 +5564,18 @@ LRESULT nsWindow::ProcessCharMessage(con
   PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
          ("%s charCode=%d scanCode=%d\n",
           aMsg.message == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
           aMsg.wParam, HIWORD(aMsg.lParam) & 0xFF));
 
   // These must be checked here too as a lone WM_CHAR could be received
   // if a child window didn't handle it (for example Alt+Space in a content window)
   nsModifierKeyState modKeyState;
-  return OnChar(aMsg, modKeyState, aEventDispatched);
+  NativeKey nativeKey(gKbdLayout.GetLayout(), mWnd, aMsg);
+  return OnChar(aMsg, nativeKey, modKeyState, aEventDispatched);
 }
 
 LRESULT nsWindow::ProcessKeyUpMessage(const MSG &aMsg, bool *aEventDispatched)
 {
   NS_PRECONDITION(aMsg.message == WM_KEYUP || aMsg.message == WM_SYSKEYUP,
                   "message is not keydown event");
   PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
          ("%s VK=%d\n", aMsg.message == WM_SYSKEYDOWN ?
@@ -5739,36 +5707,50 @@ nsWindow::SynthesizeNativeKeyEvent(PRInt
     PRUint8 key = keySequence[i].mGeneral;
     PRUint8 keySpecific = keySequence[i].mSpecific;
     kbdState[key] = 0x81; // key is down and toggled on if appropriate
     if (keySpecific) {
       kbdState[keySpecific] = 0x81;
     }
     ::SetKeyboardState(kbdState);
     nsModifierKeyState modKeyState;
-    MSG msg = WinUtils::InitMSG(WM_KEYDOWN, key, 0);
+    UINT scanCode = ::MapVirtualKeyEx(aNativeKeyCode, MAPVK_VK_TO_VSC,
+                                      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) {
-      UINT scanCode = ::MapVirtualKeyEx(aNativeKeyCode, MAPVK_VK_TO_VSC,
-                                        gKbdLayout.GetLayout());
       nsFakeCharMessage fakeMsg = { aCharacters.CharAt(0), scanCode };
       OnKeyDown(msg, modKeyState, nsnull, &fakeMsg);
     } 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
     if (keySpecific) {
       kbdState[keySpecific] = 0;
     }
     ::SetKeyboardState(kbdState);
     nsModifierKeyState modKeyState;
-    MSG msg = WinUtils::InitMSG(WM_KEYUP, key, 0);
+    UINT scanCode = ::MapVirtualKeyEx(aNativeKeyCode, MAPVK_VK_TO_VSC,
+                                      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_KEYUP, key, lParam);
     OnKeyUp(msg, modKeyState, nsnull);
   }
 
   // Restore old key state and layout
   ::SetKeyboardState(originalKbdState);
   gKbdLayout.LoadLayout(oldLayout);
 
   UnloadKeyboardLayout(loadedLayout);
@@ -6271,35 +6253,37 @@ bool nsWindow::IsRedirectedKeyDownMessag
  * that as if it was in the message queue, and refrain from actually
  * looking at or touching the message queue.
  */
 LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
                             nsModifierKeyState &aModKeyState,
                             bool *aEventDispatched,
                             nsFakeCharMessage* aFakeCharMessage)
 {
-  UINT virtualKeyCode =
-    aMsg.wParam != VK_PROCESSKEY ? aMsg.wParam : ::ImmGetVirtualKey(mWnd);
+  NativeKey nativeKey(gKbdLayout.GetLayout(), mWnd, aMsg);
+  UINT virtualKeyCode = nativeKey.GetOriginalVirtualKeyCode();
   gKbdLayout.OnKeyDown(virtualKeyCode);
 
   // Use only DOMKeyCode for XP processing.
   // Use virtualKeyCode for gKbdLayout and native processing.
   UINT DOMKeyCode = nsIMM32Handler::IsComposingOn(this) ?
                       virtualKeyCode : MapFromNativeToDOM(virtualKeyCode);
 
 #ifdef DEBUG
   //PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("In OnKeyDown virt: %d\n", DOMKeyCode));
 #endif
 
   static bool sRedirectedKeyDownEventPreventedDefault = false;
   bool noDefault;
   if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
     nsIMEContext IMEContext(mWnd);
-    noDefault =
-      DispatchKeyEvent(NS_KEY_DOWN, 0, nsnull, DOMKeyCode, &aMsg, aModKeyState);
+    nsKeyEvent keydownEvent(true, NS_KEY_DOWN, this);
+    keydownEvent.keyCode = DOMKeyCode;
+    InitKeyEvent(keydownEvent, nativeKey, aModKeyState);
+    noDefault = DispatchKeyEvent(keydownEvent, &aMsg);
     if (aEventDispatched) {
       *aEventDispatched = true;
     }
 
     // If IMC wasn't associated to the window but is associated it now (i.e.,
     // focus is moved from a non-editable editor to an editor by keydown
     // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
     // inputting if IME is opened.  But then, we should redirect the native
@@ -6408,19 +6392,17 @@ 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);
-      return OnCharRaw(aFakeCharMessage->mCharCode,
-                       aFakeCharMessage->mScanCode,
-                       aModKeyState, extraFlags, &msg);
+      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())
         return false;
@@ -6430,17 +6412,17 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
       return noDefault;
     }
 
     PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
            ("%s charCode=%d scanCode=%d\n",
             msg.message == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
             msg.wParam, HIWORD(msg.lParam) & 0xFF));
 
-    BOOL result = OnChar(msg, aModKeyState, nsnull, extraFlags);
+    BOOL result = OnChar(msg, nativeKey, aModKeyState, nsnull, extraFlags);
     // If a syschar keypress wasn't processed, Windows may want to
     // handle it to activate a native menu.
     if (!result && msg.message == WM_SYSCHAR)
       ::DefWindowProcW(mWnd, msg.message, msg.wParam, msg.lParam);
     return result;
   }
   else if (!aModKeyState.mIsControlDown && !aModKeyState.mIsAltDown &&
              (KeyboardLayout::IsPrintableCharKey(virtualKeyCode) ||
@@ -6598,22 +6580,29 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
         nsAlternativeCharCode chars(unshiftedChar, shiftedChar);
         altArray.AppendElement(chars);
       }
       if (cnt == num - 1 && (unshiftedLatinChar || shiftedLatinChar)) {
         nsAlternativeCharCode chars(unshiftedLatinChar, shiftedLatinChar);
         altArray.AppendElement(chars);
       }
 
-      DispatchKeyEvent(NS_KEY_PRESS, uniChar, &altArray,
-                       keyCode, nsnull, aModKeyState, extraFlags);
+      nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);
+      keypressEvent.flags |= extraFlags;
+      keypressEvent.charCode = uniChar;
+      keypressEvent.alternativeCharCodes.AppendElements(altArray);
+      InitKeyEvent(keypressEvent, nativeKey, aModKeyState);
+      DispatchKeyEvent(keypressEvent, nsnull);
     }
   } else {
-    DispatchKeyEvent(NS_KEY_PRESS, 0, nsnull, DOMKeyCode, nsnull, aModKeyState,
-                     extraFlags);
+    nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);
+    keypressEvent.flags |= extraFlags;
+    keypressEvent.keyCode = DOMKeyCode;
+    InitKeyEvent(keypressEvent, nativeKey, aModKeyState);
+    DispatchKeyEvent(keypressEvent, nsnull);
   }
 
   return noDefault;
 }
 
 // OnKeyUp
 LRESULT nsWindow::OnKeyUp(const MSG &aMsg,
                           nsModifierKeyState &aModKeyState,
@@ -6625,39 +6614,36 @@ LRESULT nsWindow::OnKeyUp(const MSG &aMs
          ("nsWindow::OnKeyUp VK=%d\n", virtualKeyCode));
 
   if (!nsIMM32Handler::IsComposingOn(this)) {
     virtualKeyCode = MapFromNativeToDOM(virtualKeyCode);
   }
 
   if (aEventDispatched)
     *aEventDispatched = true;
-  return DispatchKeyEvent(NS_KEY_UP, 0, nsnull, virtualKeyCode, &aMsg,
-                          aModKeyState);
+  nsKeyEvent keyupEvent(true, NS_KEY_UP, this);
+  keyupEvent.keyCode = virtualKeyCode;
+  NativeKey nativeKey(gKbdLayout.GetLayout(), mWnd, aMsg);
+  InitKeyEvent(keyupEvent, nativeKey, aModKeyState);
+  return DispatchKeyEvent(keyupEvent, &aMsg);
 }
 
 // OnChar
-LRESULT nsWindow::OnChar(const MSG &aMsg, nsModifierKeyState &aModKeyState,
+LRESULT nsWindow::OnChar(const MSG &aMsg,
+                         const NativeKey& aNativeKey,
+                         nsModifierKeyState &aModKeyState,
                          bool *aEventDispatched, PRUint32 aFlags)
 {
-  return OnCharRaw(aMsg.wParam, HIWORD(aMsg.lParam) & 0xFF, aModKeyState,
-                   aFlags, &aMsg, aEventDispatched);
-}
-
-// OnCharRaw
-LRESULT nsWindow::OnCharRaw(UINT charCode, UINT aScanCode,
-                            nsModifierKeyState &aModKeyState, PRUint32 aFlags,
-                            const MSG *aMsg, bool *aEventDispatched)
-{
   // ignore [shift+]alt+space so the OS can handle it
   if (aModKeyState.mIsAltDown && !aModKeyState.mIsControlDown &&
       IS_VK_DOWN(NS_VK_SPACE)) {
     return FALSE;
   }
-  
+
+  PRUint32 charCode = aMsg.wParam;
   // Ignore Ctrl+Enter (bug 318235)
   if (aModKeyState.mIsControlDown && charCode == 0xA) {
     return FALSE;
   }
 
   // WM_CHAR with Control and Alt (== AltGr) down really means a normal character
   bool saveIsAltDown = aModKeyState.mIsAltDown;
   bool saveIsControlDown = aModKeyState.mIsControlDown;
@@ -6692,17 +6678,18 @@ LRESULT nsWindow::OnCharRaw(UINT charCod
       uniChar = charCode;
       charCode = 0;
     }
   }
 
   // Keep the characters unshifted for shortcuts and accesskeys and make sure
   // that numbers are always passed as such (among others: bugs 50255 and 351310)
   if (uniChar && (aModKeyState.mIsControlDown || aModKeyState.mIsAltDown)) {
-    UINT virtualKeyCode = ::MapVirtualKeyEx(aScanCode, MAPVK_VSC_TO_VK,
+    UINT virtualKeyCode = ::MapVirtualKeyEx(WinUtils::GetScanCode(aMsg.lParam),
+                                            MAPVK_VSC_TO_VK,
                                             gKbdLayout.GetLayout());
     UINT unshiftedCharCode =
       virtualKeyCode >= '0' && virtualKeyCode <= '9' ? virtualKeyCode :
         aModKeyState.mIsShiftDown ? ::MapVirtualKeyEx(virtualKeyCode,
                                         MAPVK_VK_TO_CHAR,
                                         gKbdLayout.GetLayout()) : 0;
     // ignore diacritics (top bit set) and key mapping errors (char code 0)
     if ((INT)unshiftedCharCode > 0)
@@ -6711,18 +6698,21 @@ LRESULT nsWindow::OnCharRaw(UINT charCod
 
   // Fix for bug 285161 (and 295095) which was caused by the initial fix for bug 178110.
   // When pressing (alt|ctrl)+char, the char must be lowercase unless shift is
   // pressed too.
   if (!aModKeyState.mIsShiftDown && (saveIsAltDown || saveIsControlDown)) {
     uniChar = towlower(uniChar);
   }
 
-  bool result = DispatchKeyEvent(NS_KEY_PRESS, uniChar, nsnull,
-                                   charCode, aMsg, aModKeyState, aFlags);
+  nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);
+  keypressEvent.flags |= aFlags;
+  keypressEvent.charCode = uniChar;
+  InitKeyEvent(keypressEvent, aNativeKey, aModKeyState);
+  bool result = DispatchKeyEvent(keypressEvent, &aMsg);
   if (aEventDispatched)
     *aEventDispatched = true;
   aModKeyState.mIsAltDown = saveIsAltDown;
   aModKeyState.mIsControlDown = saveIsControlDown;
   return result;
 }
 
 void
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -85,26 +85,33 @@
  * Forward class definitions
  */
 
 class nsNativeDragTarget;
 class nsIRollupListener;
 class nsIFile;
 class imgIContainer;
 
+namespace mozilla {
+namespace widget {
+class NativeKey;
+} // namespace widget
+} // namespacw mozilla;
+
 /**
  * Native WIN32 window wrapper.
  */
 
 class nsWindow : public nsBaseWidget
 {
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
   typedef mozilla::widget::WindowHook WindowHook;
   typedef mozilla::widget::TaskbarWindowPreview TaskbarWindowPreview;
+  typedef mozilla::widget::NativeKey NativeKey;
 public:
   nsWindow();
   virtual ~nsWindow();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   friend class nsWindowGfx;
 
@@ -216,21 +223,21 @@ public:
   void                    InitEvent(nsGUIEvent& event, nsIntPoint* aPoint = nsnull);
   virtual bool            DispatchMouseEvent(PRUint32 aEventType, WPARAM wParam,
                                              LPARAM lParam,
                                              bool aIsContextMenuKey = false,
                                              PRInt16 aButton = nsMouseEvent::eLeftButton,
                                              PRUint16 aInputSource = nsIDOMMouseEvent::MOZ_SOURCE_MOUSE);
   virtual bool            DispatchWindowEvent(nsGUIEvent* event);
   virtual bool            DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus);
-  virtual bool            DispatchKeyEvent(PRUint32 aEventType, WORD aCharCode,
-                                           const nsTArray<nsAlternativeCharCode>* aAlternativeChars,
-                                           UINT aVirtualCharCode, const MSG *aMsg,
-                                           const nsModifierKeyState &aModKeyState,
-                                           PRUint32 aFlags = 0);
+  void                    InitKeyEvent(nsKeyEvent& aKeyEvent,
+                                       const NativeKey& aNativeKey,
+                                       const nsModifierKeyState &aModKeyState);
+  virtual bool            DispatchKeyEvent(nsKeyEvent& aKeyEvent,
+                                           const MSG *aMsgSentToPlugin);
   void                    DispatchPendingEvents();
   bool                    DispatchPluginEvent(UINT aMessage,
                                               WPARAM aWParam,
                                               LPARAM aLParam,
                                               bool aDispatchPendingEvents);
 
   void                    SuppressBlurEvents(bool aSuppress); // Called from nsFilePicker
   bool                    BlurEventsSuppressed();
@@ -388,32 +395,34 @@ protected:
   }
 
   /**
    * Event handlers
    */
   virtual void            OnDestroy();
   virtual bool            OnMove(PRInt32 aX, PRInt32 aY);
   virtual bool            OnResize(nsIntRect &aWindowRect);
+  /**
+   * @param aVirtualKeyCode     If caller knows which key exactly caused the
+   *                            aMsg, set the virtual key code.
+   *                            Otherwise, 0.
+   * @param aScanCode           If aVirutalKeyCode isn't 0, set the scan code.
+   */
   LRESULT                 OnChar(const MSG &aMsg,
+                                 const NativeKey& aNativeKey,
                                  nsModifierKeyState &aModKeyState,
                                  bool *aEventDispatched,
                                  PRUint32 aFlags = 0);
   LRESULT                 OnKeyDown(const MSG &aMsg,
                                     nsModifierKeyState &aModKeyState,
                                     bool *aEventDispatched,
                                     nsFakeCharMessage* aFakeCharMessage);
   LRESULT                 OnKeyUp(const MSG &aMsg,
                                   nsModifierKeyState &aModKeyState,
                                   bool *aEventDispatched);
-  LRESULT                 OnCharRaw(UINT charCode, UINT aScanCode,
-                                    nsModifierKeyState &aModKeyState,
-                                    PRUint32 aFlags = 0,
-                                    const MSG *aMsg = nsnull,
-                                    bool *aEventDispatched = nsnull);
   bool                    OnGesture(WPARAM wParam, LPARAM lParam);
   bool                    OnTouch(WPARAM wParam, LPARAM lParam);
   bool                    OnHotKey(WPARAM wParam, LPARAM lParam);
   BOOL                    OnInputLangChange(HKL aHKL);
   bool                    OnPaint(HDC aDC, PRUint32 aNestingLevel);
   void                    OnWindowPosChanged(WINDOWPOS *wp, bool& aResult);
   void                    OnWindowPosChanging(LPWINDOWPOS& info);
   void                    OnSysColorChanged();
--- a/widget/windows/nsWindowDefs.h
+++ b/widget/windows/nsWindowDefs.h
@@ -98,16 +98,18 @@
 #ifndef SPI_SETWHEELSCROLLCHARS
 #define SPI_SETWHEELSCROLLCHARS           0x006D
 #endif
 
 #ifndef MAPVK_VSC_TO_VK
 #define MAPVK_VK_TO_VSC                   0
 #define MAPVK_VSC_TO_VK                   1
 #define MAPVK_VK_TO_CHAR                  2
+#define MAPVK_VSC_TO_VK_EX                3
+#define MAPVK_VK_TO_VSC_EX                4
 #endif
 
 // ConstrainPosition window positioning slop value
 #define kWindowPositionSlop               20
 
 // Origin of the system context menu when displayed in full screen mode
 #define MOZ_SYSCONTEXT_X_POS              20
 #define MOZ_SYSCONTEXT_Y_POS              20