Bug 677252 part.1 Reimplement keycode computation in cocoa widget r=smaug+smichaud
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 17 May 2012 16:04:16 +0900
changeset 98263 29e512736f30b0aef384e7c3dc7bee0ca326b9fa
parent 98262 ad5db4e9d2f2c7a9e4e07b7aead83a57be57bc4a
child 98264 5c5211cd5a3909f4426ee732f93b26e91a292b5a
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs677252
milestone15.0a1
Bug 677252 part.1 Reimplement keycode computation in cocoa widget r=smaug+smichaud
widget/cocoa/Makefile.in
widget/cocoa/TextInputHandler.h
widget/cocoa/TextInputHandler.mm
widget/tests/test_keycodes.xul
--- a/widget/cocoa/Makefile.in
+++ b/widget/cocoa/Makefile.in
@@ -133,15 +133,16 @@ export::
 endif
 
 export::
 	$(INSTALL) $(srcdir)/cursors $(DIST)/bin/res
 
 LOCAL_INCLUDES	= \
 	$(TK_CFLAGS) \
 	-I$(srcdir)/../xpwidgets \
+	-I$(srcdir)/../shared \
 	$(NULL)
 
 LDFLAGS	+= \
 	-framework QuickTime \
 	-framework IOKit \
 	-F/System/Library/PrivateFrameworks -framework CoreUI \
 	$(NULL)
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -56,26 +56,28 @@ class nsChildView;
 struct nsTextRange;
 
 namespace mozilla {
 namespace widget {
 
 // Key code constants
 enum
 {
+  kSpaceKeyCode       = 0x31,
   kEscapeKeyCode      = 0x35,
   kRCommandKeyCode    = 0x36, // right command key
   kCommandKeyCode     = 0x37,
   kShiftKeyCode       = 0x38,
   kCapsLockKeyCode    = 0x39,
   kOptionkeyCode      = 0x3A,
   kControlKeyCode     = 0x3B,
   kRShiftKeyCode      = 0x3C, // right shift key
   kROptionKeyCode     = 0x3D, // right option key
   kRControlKeyCode    = 0x3E, // right control key
+
   kClearKeyCode       = 0x47,
 
   // function keys
   kF1KeyCode          = 0x7A,
   kF2KeyCode          = 0x78,
   kF3KeyCode          = 0x63,
   kF4KeyCode          = 0x76,
   kF5KeyCode          = 0x60,
@@ -84,16 +86,20 @@ enum
   kF8KeyCode          = 0x64,
   kF9KeyCode          = 0x65,
   kF10KeyCode         = 0x6D,
   kF11KeyCode         = 0x67,
   kF12KeyCode         = 0x6F,
   kF13KeyCode         = 0x69,
   kF14KeyCode         = 0x6B,
   kF15KeyCode         = 0x71,
+  kF16KeyCode         = 0x6A,
+  kF17KeyCode         = 0x40,
+  kF18KeyCode         = 0x4F,
+  kF19KeyCode         = 0x50,
 
   kPrintScreenKeyCode = kF13KeyCode,
   kScrollLockKeyCode  = kF14KeyCode,
   kPauseKeyCode       = kF15KeyCode,
 
   // keypad
   kKeypad0KeyCode     = 0x52,
   kKeypad1KeyCode     = 0x53,
@@ -107,20 +113,24 @@ enum
   kKeypad9KeyCode     = 0x5C,
 
   kKeypadMultiplyKeyCode  = 0x43,
   kKeypadAddKeyCode       = 0x45,
   kKeypadSubtractKeyCode  = 0x4E,
   kKeypadDecimalKeyCode   = 0x41,
   kKeypadDivideKeyCode    = 0x4B,
   kKeypadEqualsKeyCode    = 0x51, // no correpsonding gecko key code
+
   kEnterKeyCode           = 0x4C,
   kReturnKeyCode          = 0x24,
   kPowerbookEnterKeyCode  = 0x34, // Enter on Powerbook's keyboard is different
 
+  // IME keys
+  kJapanese_Kana          = 0x68,
+
   kInsertKeyCode          = 0x72, // also help key
   kDeleteKeyCode          = 0x75, // also forward delete key
   kTabKeyCode             = 0x30,
   kTildeKeyCode           = 0x32,
   kBackspaceKeyCode       = 0x33,
   kHomeKeyCode            = 0x73, 
   kEndKeyCode             = 0x77,
   kPageUpKeyCode          = 0x74,
@@ -282,16 +292,29 @@ public:
    *
    * @param aNativeKeyEvent       A native key event for which you want to
    *                              dispatch a Gecko key event.
    * @param aKeyEvent             The result -- a Gecko key event initialized
    *                              from the native key event.
    */
   void InitKeyEvent(NSEvent *aNativeKeyEvent, nsKeyEvent& aKeyEvent);
 
+  /**
+   * ComputeGeckoKeyCode() returns Gecko keycode for aNativeKeyCode on current
+   * keyboard layout.
+   *
+   * @param aNativeKeyCode        A native keycode.
+   * @param aKbType               A native Keyboard Type value.  Typically,
+   *                              this is a result of ::LMGetKbdType().
+   * @param aCmdIsPressed         TRUE if Cmd key is pressed.  Otherwise, FALSE.
+   * @return                      The computed Gecko keycode.
+   */
+  PRUint32 ComputeGeckoKeyCode(UInt32 aNativeKeyCode, UInt32 aKbType,
+                               bool aCmdIsPressed);
+
 protected:
   /**
    * TranslateToString() computes the inputted text from the native keyCode,
    * modifier flags and keyboard type.
    *
    * @param aKeyCode              A native keyCode.
    * @param aModifiers            Combination of native modifier flags.
    * @param aKbType               A native Keyboard Type value.  Typically,
@@ -312,29 +335,32 @@ protected:
    * @param aKeyCode              A native keyCode.
    * @param aModifiers            Combination of native modifier flags.
    * @param aKbType               A native Keyboard Type value.  Typically,
    *                              this is a result of ::LMGetKbdType().
    * @return                      If succeeded and the result is one character,
    *                              returns the charCode of it.  Otherwise,
    *                              returns 0.
    */
-  PRUint32 TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbdType);
+  PRUint32 TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType);
 
   /**
    * InitKeyPressEvent() initializes aKeyEvent for aNativeKeyEvent.
    * Don't call this method when aKeyEvent isn't NS_KEY_PRESS.
    *
    * @param aNativeKeyEvent       A native key event for which you want to
    *                              dispatch a Gecko key event.
    * @param aKeyEvent             The result -- a Gecko key event initialized
    *                              from the native key event.  This must be
    *                              NS_KEY_PRESS event.
+   * @param aKbType               A native Keyboard Type value.  Typically,
+   *                              this is a result of ::LMGetKbdType().
    */
-  void InitKeyPressEvent(NSEvent *aNativeKeyEvent, nsKeyEvent& aKeyEvent);
+  void InitKeyPressEvent(NSEvent *aNativeKeyEvent, nsKeyEvent& aKeyEvent,
+                         UInt32 aKbType);
 
   bool GetBoolProperty(const CFStringRef aKey);
   bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr);
   bool GetStringProperty(const CFStringRef aKey, nsAString &aStr);
 
   TISInputSourceRef mInputSource;
   CFArrayRef mInputSourceList;
   const UCKeyboardLayout* mUCKeyboardLayout;
@@ -398,35 +424,16 @@ public:
    */
   nsresult SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
                                     PRInt32 aNativeKeyCode,
                                     PRUint32 aModifierFlags,
                                     const nsAString& aCharacters,
                                     const nsAString& aUnmodifiedCharacters);
 
   /**
-   * ComputeGeckoKeyCode() computes Gecko defined keyCode from the native
-   * keyCode or the characters.
-   *
-   * @param aNativeKeyCode        A native keyCode.
-   * @param aCharacters           Characters from the native key event (obtained
-   *                              using charactersIgnoringModifiers).  If the
-   *                              native event contains one or more characters,
-   *                              the result is computed from this.
-   * @return                      Gecko keyCode value for aNativeKeyCode (if
-   *                              aCharacters is empty), otherwise for
-   *                              aCharacters (if aCharacters is non-empty).
-   *                              Or zero if the aCharacters contains one or
-   *                              more Unicode characters, or if aNativeKeyCode
-   *                              cannot be mapped to a Gecko keyCode.
-   */
-  static PRUint32 ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
-                                      NSString *aCharacters);
-
-  /**
    * IsSpecialGeckoKey() checks whether aNativeKeyCode is mapped to a special
    * Gecko keyCode.  A key is "special" if it isn't used for text input.
    *
    * @param aNativeKeyCode        A native keycode.
    * @return                      If the keycode is mapped to a special key,
    *                              TRUE.  Otherwise, FALSE.
    */
   static bool IsSpecialGeckoKey(UInt32 aNativeKeyCode);
@@ -624,26 +631,16 @@ protected:
    * @return                      TRUE if aChar is a printable ASCII character
    *                              or a unicode character.  Otherwise, i.e,
    *                              if aChar is a non-printable ASCII character,
    *                              FALSE.
    */
   static bool IsPrintableChar(PRUnichar aChar);
 
   /**
-   * ComputeGeckoKeyCodeFromChar() computes Gecko defined keyCode value from
-   * aChar.  If aChar is not an ASCII character, this always returns FALSE.
-   *
-   * @param aChar                 A unicode character.
-   * @return                      A Gecko defined keyCode.  Or zero if aChar
-   *                              is a unicode character.
-   */
-  static PRUint32 ComputeGeckoKeyCodeFromChar(PRUnichar aChar);
-
-  /**
    * IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input.
    *
    * @param aKeyEvent             A key event.
    * @return                      TRUE if the key event causes text input.
    *                              Otherwise, FALSE.
    */
   static bool IsNormalCharInputtingEvent(const nsKeyEvent& aKeyEvent);
 
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -46,16 +46,17 @@
 #endif // MOZ_LOGGING
 #include "prlog.h"
 
 #include "nsChildView.h"
 #include "nsObjCExceptions.h"
 #include "nsBidiUtils.h"
 #include "nsToolkit.h"
 #include "nsCocoaUtils.h"
+#include "WidgetUtils.h"
 #include "nsPrintfCString.h"
 
 #ifdef __LP64__
 #include "ComplexTextInputPanel.h"
 #endif // __LP64__
 
 #ifndef NP_NO_CARBON
 #include <objc/runtime.h>
@@ -657,35 +658,38 @@ TISInputSourceWrapper::Clear()
 
 void
 TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent,
                                     nsKeyEvent& aKeyEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
-    ("%p TISInputSourceWrapper::InitKeyEvent, aNativeKeyEvent=%p"
+    ("%p TISInputSourceWrapper::InitKeyEvent, aNativeKeyEvent=%p, "
      "aKeyEvent.message=%s",
      this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent)));
 
   NS_ENSURE_TRUE(aNativeKeyEvent, );
 
   aKeyEvent.time = PR_IntervalNow();
 
   nsCocoaUtils::InitInputEvent(aKeyEvent, aNativeKeyEvent);
 
   aKeyEvent.refPoint = nsIntPoint(0, 0);
   aKeyEvent.isChar = false; // XXX not used in XP level
 
-  NSString* str = nil;
-  if ([aNativeKeyEvent type] != NSFlagsChanged) {
-    str = [aNativeKeyEvent charactersIgnoringModifiers];
-  }
+  // If a keyboard layout override is set, we also need to force the keyboard
+  // type to something ANSI to avoid test failures on machines with JIS
+  // keyboards (since the pair of keyboard layout and physical keyboard type
+  // form the actual key layout).  This assumes that the test setting the
+  // override was written assuming an ANSI keyboard.
+  UInt32 kbType = mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType();
+
   aKeyEvent.keyCode =
-    TextInputHandler::ComputeGeckoKeyCode([aNativeKeyEvent keyCode], str);
+    ComputeGeckoKeyCode([aNativeKeyEvent keyCode], kbType, aKeyEvent.IsMeta());
 
   switch ([aNativeKeyEvent keyCode]) {
     case kCommandKeyCode:
     case kShiftKeyCode:
     case kOptionkeyCode:
     case kControlKeyCode:
       aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
       break;
@@ -726,46 +730,51 @@ TISInputSourceWrapper::InitKeyEvent(NSEv
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyEvent, "
      "shift=%s, ctrl=%s, alt=%s, meta=%s",
      this, OnOrOff(aKeyEvent.IsShift()), OnOrOff(aKeyEvent.IsControl()),
      OnOrOff(aKeyEvent.IsAlt()), OnOrOff(aKeyEvent.IsMeta())));
 
   if (aKeyEvent.message == NS_KEY_PRESS &&
       !TextInputHandler::IsSpecialGeckoKey([aNativeKeyEvent keyCode])) {
-    InitKeyPressEvent(aNativeKeyEvent, aKeyEvent);
+    InitKeyPressEvent(aNativeKeyEvent, aKeyEvent, kbType);
     return;
   }
 
   aKeyEvent.charCode = 0;
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyEvent, keyCode=0x%X charCode=0x0",
      this, aKeyEvent.keyCode));
 
   NS_OBJC_END_TRY_ABORT_BLOCK
 }
 
 void
 TISInputSourceWrapper::InitKeyPressEvent(NSEvent *aNativeKeyEvent,
-                                         nsKeyEvent& aKeyEvent)
+                                         nsKeyEvent& aKeyEvent,
+                                         UInt32 aKbType)
 {
   NS_ASSERTION(aKeyEvent.message == NS_KEY_PRESS,
                "aKeyEvent must be NS_KEY_PRESS event");
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyPressEvent, aNativeKeyEvent=%p"
-     "aKeyEvent.message=%s",
-     this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent)));
+     "aKeyEvent.message=%s, aKbType=0x%X, IsOpenedIMEMode()=%s",
+     this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent), aKbType,
+     TrueOrFalse(IsOpenedIMEMode())));
 
   aKeyEvent.isChar = true; // this is not a special key  XXX not used in XP
 
   aKeyEvent.charCode = 0;
   NSString* chars = [aNativeKeyEvent characters];
   if ([chars length] > 0) {
+    // XXX This is wrong at Hiragana or Katakana with Kana-Nyuryoku mode or
+    //     Chinese or Koran IME modes.  We should use ASCII characters for the
+    //     charCode.
     aKeyEvent.charCode = [chars characterAtIndex:0];
   }
 
   // convert control-modified charCode to raw charCode (with appropriate case)
   if (aKeyEvent.IsControl() && aKeyEvent.charCode <= 26) {
     aKeyEvent.charCode += (aKeyEvent.IsShift()) ? ('A' - 1) : ('a' - 1);
   }
 
@@ -780,59 +789,52 @@ TISInputSourceWrapper::InitKeyPressEvent
 
   if (!aKeyEvent.IsControl() && !aKeyEvent.IsMeta() && !aKeyEvent.IsAlt()) {
     return;
   }
 
   TISInputSourceWrapper USLayout("com.apple.keylayout.US");
   bool isRomanKeyboardLayout = IsASCIICapable();
 
-  // If a keyboard layout override is set, we also need to force the
-  // keyboard type to something ANSI to avoid test failures on machines
-  // with JIS keyboards (since the pair of keyboard layout and physical
-  // keyboard type form the actual key layout).  This assumes that the
-  // test setting the override was written assuming an ANSI keyboard.
-  UInt32 kbType = mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType();
-
   UInt32 key = [aNativeKeyEvent keyCode];
 
   // Caps lock and num lock modifier state:
   UInt32 lockState = 0;
   if ([aNativeKeyEvent modifierFlags] & NSAlphaShiftKeyMask) {
     lockState |= alphaLock;
   }
   if ([aNativeKeyEvent modifierFlags] & NSNumericPadKeyMask) {
     lockState |= kEventKeyModifierNumLockMask;
   }
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyPressEvent, "
-     "isRomanKeyboardLayout=%s, kbType=0x%X, key=0x%X",
-     this, TrueOrFalse(isRomanKeyboardLayout), kbType, key));
+     "isRomanKeyboardLayout=%s, key=0x%X",
+     this, TrueOrFalse(isRomanKeyboardLayout), aKbType, key));
 
   nsString str;
 
   // normal chars
-  PRUint32 unshiftedChar = TranslateToChar(key, lockState, kbType);
+  PRUint32 unshiftedChar = TranslateToChar(key, lockState, aKbType);
   UInt32 shiftLockMod = shiftKey | lockState;
-  PRUint32 shiftedChar = TranslateToChar(key, shiftLockMod, kbType);
+  PRUint32 shiftedChar = TranslateToChar(key, shiftLockMod, aKbType);
 
   // characters generated with Cmd key
   // XXX we should remove CapsLock state, which changes characters from
   //     Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
   //     is pressed.
   UInt32 numState = (lockState & ~alphaLock); // only num lock state
-  PRUint32 uncmdedChar = TranslateToChar(key, numState, kbType);
+  PRUint32 uncmdedChar = TranslateToChar(key, numState, aKbType);
   UInt32 shiftNumMod = numState | shiftKey;
-  PRUint32 uncmdedShiftChar = TranslateToChar(key, shiftNumMod, kbType);
-  PRUint32 uncmdedUSChar = USLayout.TranslateToChar(key, numState, kbType);
+  PRUint32 uncmdedShiftChar = TranslateToChar(key, shiftNumMod, aKbType);
+  PRUint32 uncmdedUSChar = USLayout.TranslateToChar(key, numState, aKbType);
   UInt32 cmdNumMod = cmdKey | numState;
-  PRUint32 cmdedChar = TranslateToChar(key, cmdNumMod, kbType);
+  PRUint32 cmdedChar = TranslateToChar(key, cmdNumMod, aKbType);
   UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
-  PRUint32 cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, kbType);
+  PRUint32 cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, aKbType);
 
   // Is the keyboard layout changed by Cmd key?
   // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
   bool isCmdSwitchLayout = uncmdedChar != cmdedChar;
   // Is the keyboard layout for Latin, but Cmd key switches the layout?
   // I.e., Dvorak-QWERTY
   bool isDvorakQWERTY = isCmdSwitchLayout && isRomanKeyboardLayout;
 
@@ -881,21 +883,21 @@ TISInputSourceWrapper::InitKeyPressEvent
       cmdedChar = unshiftedChar;
     }
     if (shiftedChar) {
       cmdedShiftChar = shiftedChar;
     }
   } else if (uncmdedUSChar == cmdedChar) {
     // It looks like characters from a US layout are provided when Command
     // is down.
-    PRUint32 ch = USLayout.TranslateToChar(key, lockState, kbType);
+    PRUint32 ch = USLayout.TranslateToChar(key, lockState, aKbType);
     if (ch) {
       cmdedChar = ch;
     }
-    ch = USLayout.TranslateToChar(key, shiftLockMod, kbType);
+    ch = USLayout.TranslateToChar(key, shiftLockMod, aKbType);
     if (ch) {
       cmdedShiftChar = ch;
     }
   }
 
   // Only charCode (not alternativeCharCodes) is available to javascript,
   // so attempt to set this to the most likely intended (or most useful)
   // character.  Note that cmdedChar and cmdedShiftChar are usually
@@ -941,16 +943,172 @@ TISInputSourceWrapper::InitKeyPressEvent
     aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
   }
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TISInputSourceWrapper::InitKeyPressEvent, "
      "hasCmdShiftOnlyChar=%s, originalCmdedShiftChar=U+%X",
      this, TrueOrFalse(hasCmdShiftOnlyChar), originalCmdedShiftChar));
 }
 
+PRUint32
+TISInputSourceWrapper::ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
+                                           UInt32 aKbType,
+                                           bool aCmdIsPressed)
+{
+  PR_LOG(gLog, PR_LOG_ALWAYS,
+    ("%p TISInputSourceWrapper::ComputeGeckoKeyCode, aNativeKeyCode=0x%X, "
+     "aKbType=0x%X, aCmdIsPressed=%s, IsOpenedIMEMode()=%s, "
+     "IsASCIICapable()=%s",
+     this, aNativeKeyCode, aKbType, TrueOrFalse(aCmdIsPressed),
+     TrueOrFalse(IsOpenedIMEMode()), TrueOrFalse(IsASCIICapable())));
+
+  switch (aNativeKeyCode) {
+    case kSpaceKeyCode:         return NS_VK_SPACE;
+    case kEscapeKeyCode:        return NS_VK_ESCAPE;
+
+    // modifiers
+    case kRCommandKeyCode:
+    case kCommandKeyCode:       return NS_VK_META;
+    case kRShiftKeyCode:
+    case kShiftKeyCode:         return NS_VK_SHIFT;
+    case kCapsLockKeyCode:      return NS_VK_CAPS_LOCK;
+    case kRControlKeyCode:
+    case kControlKeyCode:       return NS_VK_CONTROL;
+    case kROptionKeyCode:
+    case kOptionkeyCode:        return NS_VK_ALT;
+
+    case kClearKeyCode:         return NS_VK_CLEAR;
+
+    // function keys
+    case kF1KeyCode:            return NS_VK_F1;
+    case kF2KeyCode:            return NS_VK_F2;
+    case kF3KeyCode:            return NS_VK_F3;
+    case kF4KeyCode:            return NS_VK_F4;
+    case kF5KeyCode:            return NS_VK_F5;
+    case kF6KeyCode:            return NS_VK_F6;
+    case kF7KeyCode:            return NS_VK_F7;
+    case kF8KeyCode:            return NS_VK_F8;
+    case kF9KeyCode:            return NS_VK_F9;
+    case kF10KeyCode:           return NS_VK_F10;
+    case kF11KeyCode:           return NS_VK_F11;
+    case kF12KeyCode:           return NS_VK_F12;
+    // case kF13KeyCode:           return NS_VK_F13;  // clash with the 3 below
+    // case kF14KeyCode:           return NS_VK_F14;
+    // case kF15KeyCode:           return NS_VK_F15;
+    case kF16KeyCode:           return NS_VK_F16;
+    case kF17KeyCode:           return NS_VK_F17;
+    case kF18KeyCode:           return NS_VK_F18;
+    case kF19KeyCode:           return NS_VK_F19;
+
+    case kPauseKeyCode:         return NS_VK_PAUSE;
+    case kScrollLockKeyCode:    return NS_VK_SCROLL_LOCK;
+    case kPrintScreenKeyCode:   return NS_VK_PRINTSCREEN;
+
+    // keypad
+    case kKeypad0KeyCode:       return NS_VK_NUMPAD0;
+    case kKeypad1KeyCode:       return NS_VK_NUMPAD1;
+    case kKeypad2KeyCode:       return NS_VK_NUMPAD2;
+    case kKeypad3KeyCode:       return NS_VK_NUMPAD3;
+    case kKeypad4KeyCode:       return NS_VK_NUMPAD4;
+    case kKeypad5KeyCode:       return NS_VK_NUMPAD5;
+    case kKeypad6KeyCode:       return NS_VK_NUMPAD6;
+    case kKeypad7KeyCode:       return NS_VK_NUMPAD7;
+    case kKeypad8KeyCode:       return NS_VK_NUMPAD8;
+    case kKeypad9KeyCode:       return NS_VK_NUMPAD9;
+
+    case kKeypadMultiplyKeyCode:  return NS_VK_MULTIPLY;
+    case kKeypadAddKeyCode:       return NS_VK_ADD;
+    case kKeypadSubtractKeyCode:  return NS_VK_SUBTRACT;
+    case kKeypadDecimalKeyCode:   return NS_VK_DECIMAL;
+    case kKeypadDivideKeyCode:    return NS_VK_DIVIDE;
+
+    // IME keys
+    case kJapanese_Kana:        return NS_VK_KANA;
+
+    // these may clash with forward delete and help
+    case kInsertKeyCode:        return NS_VK_INSERT;
+    case kDeleteKeyCode:        return NS_VK_DELETE;
+
+    case kBackspaceKeyCode:     return NS_VK_BACK;
+    case kTabKeyCode:           return NS_VK_TAB;
+
+    case kHomeKeyCode:          return NS_VK_HOME;
+    case kEndKeyCode:           return NS_VK_END;
+
+    case kPageUpKeyCode:        return NS_VK_PAGE_UP;
+    case kPageDownKeyCode:      return NS_VK_PAGE_DOWN;
+
+    case kLeftArrowKeyCode:     return NS_VK_LEFT;
+    case kRightArrowKeyCode:    return NS_VK_RIGHT;
+    case kUpArrowKeyCode:       return NS_VK_UP;
+    case kDownArrowKeyCode:     return NS_VK_DOWN;
+
+    case kVK_ANSI_1:            return NS_VK_1;
+    case kVK_ANSI_2:            return NS_VK_2;
+    case kVK_ANSI_3:            return NS_VK_3;
+    case kVK_ANSI_4:            return NS_VK_4;
+    case kVK_ANSI_5:            return NS_VK_5;
+    case kVK_ANSI_6:            return NS_VK_6;
+    case kVK_ANSI_7:            return NS_VK_7;
+    case kVK_ANSI_8:            return NS_VK_8;
+    case kVK_ANSI_9:            return NS_VK_9;
+    case kVK_ANSI_0:            return NS_VK_0;
+
+    case kEnterKeyCode:
+    case kReturnKeyCode:
+    case kPowerbookEnterKeyCode: return NS_VK_RETURN;
+  }
+
+  // If Cmd key is pressed, that causes switching keyboard layout temporarily.
+  // E.g., Dvorak-QWERTY.  Therefore, if Cmd key is pressed, we should honor it.
+  UInt32 modifiers = aCmdIsPressed ? cmdKey : 0;
+
+  PRUint32 charCode = TranslateToChar(aNativeKeyCode, modifiers, aKbType);
+
+  // Special case for Mac.  Mac inputs Yen sign (U+00A5) directly instead of
+  // Back slash (U+005C).  We should return NS_VK_BACK_SLASH for compatibility
+  // with other platforms.
+  // XXX How about Won sign (U+20A9) which has same problem as Yen sign?
+  if (charCode == 0x00A5) {
+    return NS_VK_BACK_SLASH;
+  }
+
+  PRUint32 keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
+  if (keyCode) {
+    return keyCode;
+  }
+
+  // If the unshifed char isn't an ASCII character, use shifted char.
+  charCode = TranslateToChar(aNativeKeyCode, modifiers | shiftKey, aKbType);
+  keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
+  if (keyCode) {
+    return keyCode;
+  }
+
+  // If this is ASCII capable, give up to compute it.
+  if (IsASCIICapable()) {
+    return 0;
+  }
+
+  // Retry with ASCII capable keyboard layout.
+  TISInputSourceWrapper currentKeyboardLayout;
+  currentKeyboardLayout.InitByCurrentASCIICapableKeyboardLayout();
+  NS_ENSURE_TRUE(mInputSource != currentKeyboardLayout.mInputSource, 0);
+  keyCode = currentKeyboardLayout.ComputeGeckoKeyCode(aNativeKeyCode, aKbType,
+                                                      aCmdIsPressed);
+
+  // However, if keyCode isn't for an alphabet keys or a numeric key, we should
+  // ignore it.  For example, comma key of Thai layout is same as close-square-
+  // bracket key of US layout and an unicode character key of Thai layout is
+  // same as comma key of US layout.  If we return NS_VK_COMMA for latter key,
+  // web application developers cannot distinguish with the former key.
+  return ((keyCode >= NS_VK_A && keyCode <= NS_VK_Z) ||
+          (keyCode >= NS_VK_0 && keyCode <= NS_VK_9)) ? keyCode : 0;
+}
+
 
 #pragma mark -
 
 
 /******************************************************************************
  *
  *  TextInputHandler implementation (static methods)
  *
@@ -1370,60 +1528,53 @@ TextInputHandler::InsertText(NSAttribute
   if (currentKeyEvent && currentKeyEvent->mKeyPressDispatched) {
     return;
   }
 
   nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
 
   // Dispatch keypress event with char instead of textEvent
   nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
-  keypressEvent.charCode  = str.CharAt(0);
-  keypressEvent.keyCode   = 0;
-  keypressEvent.isChar    = true;
 
   // Don't set other modifiers from the current event, because here in
   // -insertText: they've already been taken into account in creating
   // the input string.
 
   // create event for use by plugins
 #ifndef NP_NO_CARBON
   EventRecord carbonEvent;
 #endif // #ifndef NP_NO_CARBON
 
   if (currentKeyEvent) {
     NSEvent* keyEvent = currentKeyEvent->mKeyEvent;
-    nsCocoaUtils::InitInputEvent(keypressEvent, keyEvent);
+    InitKeyEvent(keyEvent, keypressEvent);
 
     // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
     // might send the keyDown event with wrong keyboard layout if other
     // keyboard layouts are already loaded. In that case, the native event
     // doesn't match to this gecko event...
 #ifndef NP_NO_CARBON
     if ([mView pluginEventModel] == NPEventModelCarbon) {
       ConvertCocoaKeyEventToCarbonEvent(keyEvent, carbonEvent, true);
       keypressEvent.pluginEvent = &carbonEvent;
     }
 #endif // #ifndef NP_NO_CARBON
 
     if (currentKeyEvent->mKeyDownHandled) {
       keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
     }
-
-    if (!IsPrintableChar(keypressEvent.charCode)) {
-      keypressEvent.keyCode =
-        ComputeGeckoKeyCode([keyEvent keyCode],
-                            [keyEvent charactersIgnoringModifiers]);
-      keypressEvent.charCode = 0;
-    }
   } else {
     nsCocoaUtils::InitInputEvent(keypressEvent, static_cast<NSEvent*>(nsnull));
+    keypressEvent.charCode = str.CharAt(0);
+    keypressEvent.keyCode = 0;
+    keypressEvent.isChar = true;
     // Note that insertText is not called only at key pressing.
     if (!IsPrintableChar(keypressEvent.charCode)) {
       keypressEvent.keyCode =
-        ComputeGeckoKeyCodeFromChar(keypressEvent.charCode);
+        WidgetUtils::ComputeKeyCodeFromChar(keypressEvent.charCode);
       keypressEvent.charCode = 0;
     }
   }
 
   // Remove basic modifiers from keypress event because if they are included,
   // nsPlaintextEditor ignores the event.
   keypressEvent.modifiers &= ~(widget::MODIFIER_CONTROL |
                                widget::MODIFIER_ALT |
@@ -3132,35 +3283,41 @@ PluginTextInputHandler::HandleCarbonPlug
   }
 
   UInt32 macKeyCode;
   status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
                                typeUInt32, NULL, sizeof(macKeyCode), NULL,
                                &macKeyCode);
   NS_ENSURE_TRUE(status == noErr, );
 
+  TISInputSourceWrapper currentKeyboardLayout;
+  currentKeyboardLayout.InitByCurrentKeyboardLayout();
+
   EventRef cloneEvent = ::CopyEvent(aKeyEvent);
   for (PRUint32 i = 0; i < numCharCodes; ++i) {
     status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
                                  typeChar, 1, charCodes.Elements() + i);
     NS_ENSURE_TRUE(status == noErr, );
 
     EventRecord eventRec;
     if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
       nsKeyEvent keydownEvent(true, NS_KEY_DOWN, mWidget);
       nsCocoaUtils::InitInputEvent(keydownEvent, cocoaModifiers);
 
-      PRUint32 keyCode = ComputeGeckoKeyCode(macKeyCode, @"");
+      PRUint32 keyCode =
+        currentKeyboardLayout.ComputeGeckoKeyCode(macKeyCode, ::LMGetKbdType(),
+                                                  keydownEvent.IsMeta());
       PRUint32 charCode(charCodes.ElementAt(i));
 
       keydownEvent.time = PR_IntervalNow();
       keydownEvent.pluginEvent = &eventRec;
       if (IsSpecialGeckoKey(macKeyCode)) {
         keydownEvent.keyCode = keyCode;
       } else {
+        // XXX This is wrong. charCode must be 0 for keydown event.
         keydownEvent.charCode = charCode;
         keydownEvent.isChar   = true;
       }
       DispatchEvent(keydownEvent);
       if (Destroyed()) {
         break;
       }
     }
@@ -3652,166 +3809,16 @@ TextInputHandlerBase::SynthesizeNativeKe
 }
 
 /* static */ bool
 TextInputHandlerBase::IsPrintableChar(PRUnichar aChar)
 {
   return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
 }
 
-/* static */ PRUint32
-TextInputHandlerBase::ComputeGeckoKeyCodeFromChar(PRUnichar aChar)
-{
-  // We don't support the key code for non-ASCII characters
-  if (aChar > 0x7E) {
-    return 0;
-  }
-
-  // lowercase
-  if (aChar >= 'a' && aChar <= 'z') {
-    return PRUint32(toupper(aChar));
-  }
-  // uppercase
-  if (aChar >= 'A' && aChar <= 'Z') {
-    return PRUint32(aChar);
-  }
-  // numeric
-  if (aChar >= '0' && aChar <= '9') {
-    return PRUint32(aChar - '0' + NS_VK_0);
-  }
-
-  switch (aChar) {
-    case kReturnCharCode:
-    case kEnterCharCode:
-    case '\n':
-      return NS_VK_RETURN;
-    case '{':
-    case '[':
-      return NS_VK_OPEN_BRACKET;
-    case '}':
-    case ']':
-      return NS_VK_CLOSE_BRACKET;
-    case '\'':
-    case '"':
-      return NS_VK_QUOTE;
-
-    case '\\':                  return NS_VK_BACK_SLASH;
-    case ' ':                   return NS_VK_SPACE;
-    case ';':                   return NS_VK_SEMICOLON;
-    case '=':                   return NS_VK_EQUALS;
-    case ',':                   return NS_VK_COMMA;
-    case '.':                   return NS_VK_PERIOD;
-    case '/':                   return NS_VK_SLASH;
-    case '`':                   return NS_VK_BACK_QUOTE;
-    case '\t':                  return NS_VK_TAB;
-    case '-':                   return NS_VK_SUBTRACT;
-    case '+':                   return NS_VK_ADD;
-
-    default:
-      if (!IsPrintableChar(aChar)) {
-        NS_WARNING("ComputeGeckoKeyCodeFromChar() has failed.");
-      }
-      return 0;
-  }
-}
-
-/* static */ PRUint32
-TextInputHandlerBase::ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
-                                          NSString *aCharacters)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
-
-  switch (aNativeKeyCode) {
-    // modifiers. We don't get separate events for these
-    case kEscapeKeyCode:        return NS_VK_ESCAPE;
-    case kRCommandKeyCode:
-    case kCommandKeyCode:       return NS_VK_META;
-    case kRShiftKeyCode:
-    case kShiftKeyCode:         return NS_VK_SHIFT;
-    case kCapsLockKeyCode:      return NS_VK_CAPS_LOCK;
-    case kRControlKeyCode:
-    case kControlKeyCode:       return NS_VK_CONTROL;
-    case kROptionKeyCode:
-    case kOptionkeyCode:        return NS_VK_ALT;
-    case kClearKeyCode:         return NS_VK_CLEAR;
-
-    // function keys
-    case kF1KeyCode:            return NS_VK_F1;
-    case kF2KeyCode:            return NS_VK_F2;
-    case kF3KeyCode:            return NS_VK_F3;
-    case kF4KeyCode:            return NS_VK_F4;
-    case kF5KeyCode:            return NS_VK_F5;
-    case kF6KeyCode:            return NS_VK_F6;
-    case kF7KeyCode:            return NS_VK_F7;
-    case kF8KeyCode:            return NS_VK_F8;
-    case kF9KeyCode:            return NS_VK_F9;
-    case kF10KeyCode:           return NS_VK_F10;
-    case kF11KeyCode:           return NS_VK_F11;
-    case kF12KeyCode:           return NS_VK_F12;
-    // case kF13KeyCode:           return NS_VK_F13;  // clash with the 3 below
-    // case kF14KeyCode:           return NS_VK_F14;
-    // case kF15KeyCode:           return NS_VK_F15;
-    case kPauseKeyCode:         return NS_VK_PAUSE;
-    case kScrollLockKeyCode:    return NS_VK_SCROLL_LOCK;
-    case kPrintScreenKeyCode:   return NS_VK_PRINTSCREEN;
-
-    // keypad
-    case kKeypad0KeyCode:       return NS_VK_NUMPAD0;
-    case kKeypad1KeyCode:       return NS_VK_NUMPAD1;
-    case kKeypad2KeyCode:       return NS_VK_NUMPAD2;
-    case kKeypad3KeyCode:       return NS_VK_NUMPAD3;
-    case kKeypad4KeyCode:       return NS_VK_NUMPAD4;
-    case kKeypad5KeyCode:       return NS_VK_NUMPAD5;
-    case kKeypad6KeyCode:       return NS_VK_NUMPAD6;
-    case kKeypad7KeyCode:       return NS_VK_NUMPAD7;
-    case kKeypad8KeyCode:       return NS_VK_NUMPAD8;
-    case kKeypad9KeyCode:       return NS_VK_NUMPAD9;
-
-    case kKeypadMultiplyKeyCode:  return NS_VK_MULTIPLY;
-    case kKeypadAddKeyCode:       return NS_VK_ADD;
-    case kKeypadSubtractKeyCode:  return NS_VK_SUBTRACT;
-    case kKeypadDecimalKeyCode:   return NS_VK_DECIMAL;
-    case kKeypadDivideKeyCode:    return NS_VK_DIVIDE;
-
-    // these may clash with forward delete and help
-    case kInsertKeyCode:        return NS_VK_INSERT;
-    case kDeleteKeyCode:        return NS_VK_DELETE;
-
-    case kBackspaceKeyCode:     return NS_VK_BACK;
-    case kTabKeyCode:           return NS_VK_TAB;
-    case kHomeKeyCode:          return NS_VK_HOME;
-    case kEndKeyCode:           return NS_VK_END;
-    case kPageUpKeyCode:        return NS_VK_PAGE_UP;
-    case kPageDownKeyCode:      return NS_VK_PAGE_DOWN;
-    case kLeftArrowKeyCode:     return NS_VK_LEFT;
-    case kRightArrowKeyCode:    return NS_VK_RIGHT;
-    case kUpArrowKeyCode:       return NS_VK_UP;
-    case kDownArrowKeyCode:     return NS_VK_DOWN;
-    case kVK_ANSI_1:            return NS_VK_1;
-    case kVK_ANSI_2:            return NS_VK_2;
-    case kVK_ANSI_3:            return NS_VK_3;
-    case kVK_ANSI_4:            return NS_VK_4;
-    case kVK_ANSI_5:            return NS_VK_5;
-    case kVK_ANSI_6:            return NS_VK_6;
-    case kVK_ANSI_7:            return NS_VK_7;
-    case kVK_ANSI_8:            return NS_VK_8;
-    case kVK_ANSI_9:            return NS_VK_9;
-    case kVK_ANSI_0:            return NS_VK_0;
-
-    default:
-      // if we haven't gotten the key code already, look at the char code
-      if ([aCharacters length] > 0) {
-        return ComputeGeckoKeyCodeFromChar([aCharacters characterAtIndex:0]);
-      }
-  }
-
-  return 0;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
-}
 
 /* static */ bool
 TextInputHandlerBase::IsSpecialGeckoKey(UInt32 aNativeKeyCode)
 {
   // this table is used to determine which keys are special and should not
   // generate a charCode
   switch (aNativeKeyCode) {
     // modifiers - we don't get separate events for these yet
@@ -3838,22 +3845,28 @@ TextInputHandlerBase::IsSpecialGeckoKey(
     case kF8KeyCode:
     case kF9KeyCode:
     case kF10KeyCode:
     case kF11KeyCode:
     case kF12KeyCode:
     case kPauseKeyCode:
     case kScrollLockKeyCode:
     case kPrintScreenKeyCode:
+    case kF16KeyCode:
+    case kF17KeyCode:
+    case kF18KeyCode:
+    case kF19KeyCode:
 
     case kInsertKeyCode:
     case kDeleteKeyCode:
     case kTabKeyCode:
     case kBackspaceKeyCode:
 
+    case kJapanese_Kana:
+
     case kHomeKeyCode:
     case kEndKeyCode:
     case kPageUpKeyCode:
     case kPageDownKeyCode:
     case kLeftArrowKeyCode:
     case kRightArrowKeyCode:
     case kUpArrowKeyCode:
     case kDownArrowKeyCode:
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -98,17 +98,19 @@ if (IS_MAC) {
     "Japanese":0x411,
     "Norwegian":0x414,
     "Lithuanian":0x10427
   };
 }
 
 function eventToString(aEvent)
 {
-  var name = aEvent.layout + " '" + aEvent.chars + "'";
+  var name = aEvent.layout + " keyCode=" +
+    aEvent.keyCode + " (0x" + aEvent.keyCode.toString(16).toUpperCase() +
+      ") chars='" + aEvent.chars + "'";
   if (aEvent.shift) {
     name += " [Shift]";
   }
   if (aEvent.shiftRight) {
     name += " [Right Shift]";
   }
   if (aEvent.ctrl) {
     name += " [Ctrl]";
@@ -299,139 +301,985 @@ function runKeyEventTests()
     // TextInputHandler::HandleKeyUpEvent in the log). Use the International system
     // preferences widget to enable other keyboard layouts and select them from the
     // input menu to see what keyboard events they generate.
     // Note that it's possible to send bogus key events here, e.g.
     // {keyCode:0, chars:"z", unmodifiedChars:"P"} --- sendNativeKeyEvent
     // makes no attempt to verify that the keyCode matches the characters. So only
     // test key event records that you saw Cocoa send.
 
-    // Plain text input
-    testKey({layout:"US", keyCode:0, chars:"a", unmodifiedChars:"a"},
-            -1, "a", SHOULD_DELIVER_ALL);
-    testKey({layout:"US", keyCode:11, chars:"b", unmodifiedChars:"b"},
-            -1, "b", SHOULD_DELIVER_ALL);
-    testKey({layout:"US", keyCode:0, shift:1, chars:"A", unmodifiedChars:"A"},
-            -1, "A", SHOULD_DELIVER_ALL);
-
     // Ctrl keys
     testKey({layout:"US", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
-            -1, "a", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL);
     testKey({layout:"US", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"A"},
-            -1, "A", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL);
 
     // Alt keys
     testKey({layout:"US", keyCode:0, alt:1, chars:"\u00e5", unmodifiedChars:"a"},
-            -1, "\u00e5", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "\u00e5", SHOULD_DELIVER_ALL);
     testKey({layout:"US", keyCode:0, alt:1, shift:1, chars:"\u00c5", unmodifiedChars:"A"},
-            -1, "\u00c5", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "\u00c5", SHOULD_DELIVER_ALL);
     
     // Command keys
     testKey({layout:"US", keyCode:0, command:1, chars:"a", unmodifiedChars:"a"},
-            -1, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     // Shift-cmd gives us the shifted character
     testKey({layout:"US", keyCode:0, command:1, shift:1, chars:"a", unmodifiedChars:"A"},
-            -1, "A", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     // Ctrl-cmd gives us the unshifted character
     testKey({layout:"US", keyCode:0, command:1, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
-            -1, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     // Alt-cmd gives us the shifted character
     testKey({layout:"US", keyCode:0, command:1, alt:1, chars:"\u00e5", unmodifiedChars:"a"},
-            -1, "\u00e5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "\u00e5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     testKey({layout:"US", keyCode:0, command:1, alt:1, shift:1, chars:"\u00c5", unmodifiedChars:"a"},
-            -1, "\u00c5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "\u00c5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
 
     // Greek ctrl keys produce Latin charcodes
     testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
-            -1, "a", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL);
     testKey({layout:"Greek", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"\u0391"},
-            -1, "A", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL);
 
     // Greek command keys
     testKey({layout:"Greek", keyCode:0, command:1, chars:"a", unmodifiedChars:"\u03b1"},
-            -1, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     // Shift-cmd gives us the shifted character
     testKey({layout:"Greek", keyCode:0, command:1, shift:1, chars:"a", unmodifiedChars:"\u0391"},
-            -1, "A", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     // Ctrl-cmd gives us the unshifted character
     testKey({layout:"Greek", keyCode:0, command:1, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
-            -1, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     // Alt-cmd gives us the shifted character
     testKey({layout:"Greek", keyCode:0, command:1, alt:1, chars:"\u00a8", unmodifiedChars:"\u03b1"},
-            -1, "\u00a8", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "\u00a8", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     testKey({layout:"Greek", keyCode:0, command:1, alt:1, shift:1, chars:"\u00b9", unmodifiedChars:"\u0391"},
-            -1, "\u00b9", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_A, "\u00b9", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
 
-    // German (KCHR/KeyTranslate case)
+    // German
     testKey({layout:"German", keyCode:0, chars:"a", unmodifiedChars:"a"},
-            -1, "a", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL);
     testKey({layout:"German", keyCode:33, chars:"\u00fc", unmodifiedChars:"\u00fc"},
-            -1, "\u00fc", SHOULD_DELIVER_ALL);
+            0, "\u00fc", SHOULD_DELIVER_ALL);
     testKey({layout:"German", keyCode:27, chars:"\u00df", unmodifiedChars:"\u00df"},
-            -1, "\u00df", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_QUESTION_MARK, "\u00df", SHOULD_DELIVER_ALL);
     testKey({layout:"German", keyCode:27, shift:1, chars:"?", unmodifiedChars:"?"},
-            -1, "?", SHOULD_DELIVER_ALL);
+            nsIDOMKeyEvent.DOM_VK_QUESTION_MARK, "?", SHOULD_DELIVER_ALL);
+    // Note that Shift+SS is '?' but Cmd+Shift+SS is '/' on German layout.
+    // Therefore, when Cmd key is pressed, the SS key's keycode is changed.
     testKey({layout:"German", keyCode:27, command:1, chars:"\u00df", unmodifiedChars:"\u00df"},
-            -1, "\u00df", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
-    // Shift+SS is '?' but Cmd+Shift+SS is '/' on German layout.
+            nsIDOMKeyEvent.DOM_VK_SLASH, "\u00df", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
     testKey({layout:"German", keyCode:27, command:1, shift:1, chars:"/", unmodifiedChars:"?"},
-            -1, "?", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+            nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
 
     // Caps Lock key event
     // XXX keyup event of Caps Lock key is not fired.
     testKey({layout:"US", keyCode:57, capsLock:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_CAPS_LOCK, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:57, capsLock:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_CAPS_LOCK, "", SHOULD_DELIVER_KEYDOWN);
 
     // Shift/RightShift key event
     testKey({layout:"US", keyCode:56, shift:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:56, shift:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYUP);
     testKey({layout:"US", keyCode:60, shiftRight:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:60, shiftRight:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_SHIFT, "", SHOULD_DELIVER_KEYUP);
 
     // Control/RightControl key event
     testKey({layout:"US", keyCode:59, ctrl:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:59, ctrl:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYUP);
     testKey({layout:"US", keyCode:62, ctrlRight:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:62, ctrlRight:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_CONTROL, "", SHOULD_DELIVER_KEYUP);
 
     // Option/RightOption key event
     testKey({layout:"US", keyCode:58, alt:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:58, alt:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYUP);
     testKey({layout:"US", keyCode:61, altRight:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:61, altRight:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_ALT, "", SHOULD_DELIVER_KEYUP);
 
     // Command/RightCommand key event
     testKey({layout:"US", keyCode:55, command:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:55, command:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYUP);
     testKey({layout:"US", keyCode:54, commandRight:1, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYDOWN);
+            nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYDOWN);
     testKey({layout:"US", keyCode:54, commandRight:0, chars:"", unmodifiedChars:""},
-            -1, "", SHOULD_DELIVER_KEYUP);
+            nsIDOMKeyEvent.DOM_VK_META, "", SHOULD_DELIVER_KEYUP);
+
+    // all keys on keyboard (keyCode test)
+    testKey({layout:"US", keyCode:0x30, chars:"\t", unmodifiedChars:"\t"},
+            nsIDOMKeyEvent.DOM_VK_TAB, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x47, chars:"\uF739", unmodifiedChars:"\uF739"},
+            nsIDOMKeyEvent.DOM_VK_CLEAR, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x24, chars:"\u000D", unmodifiedChars:"\u000D"},
+            nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x71, chars:"\uF712", unmodifiedChars:"\uF712"},
+            nsIDOMKeyEvent.DOM_VK_PAUSE, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x35, chars:"\u001B", unmodifiedChars:"\u001B"},
+            nsIDOMKeyEvent.DOM_VK_ESCAPE, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x31, chars:" ", unmodifiedChars:" "},
+            nsIDOMKeyEvent.DOM_VK_SPACE, " ", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x74, chars:"\uF72C", unmodifiedChars:"\uF72C"},
+            nsIDOMKeyEvent.DOM_VK_PAGE_UP, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x79, chars:"\uF72D", unmodifiedChars:"\uF72D"},
+            nsIDOMKeyEvent.DOM_VK_PAGE_DOWN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x77, chars:"\uF72B", unmodifiedChars:"\uF72B"},
+            nsIDOMKeyEvent.DOM_VK_END, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x73, chars:"\uF729", unmodifiedChars:"\uF729"},
+            nsIDOMKeyEvent.DOM_VK_HOME, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x7B, chars:"\uF702", unmodifiedChars:"\uF702"},
+            nsIDOMKeyEvent.DOM_VK_LEFT, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x7E, chars:"\uF700", unmodifiedChars:"\uF700"},
+            nsIDOMKeyEvent.DOM_VK_UP, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x7C, chars:"\uF703", unmodifiedChars:"\uF703"},
+            nsIDOMKeyEvent.DOM_VK_RIGHT, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x7D, chars:"\uF701", unmodifiedChars:"\uF701"},
+            nsIDOMKeyEvent.DOM_VK_DOWN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x69, chars:"\uF710", unmodifiedChars:"\uF710"},
+            nsIDOMKeyEvent.DOM_VK_PRINTSCREEN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x75, chars:"\uF728", unmodifiedChars:"\uF728"},
+            nsIDOMKeyEvent.DOM_VK_DELETE, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x6B, chars:"\uF711", unmodifiedChars:"\uF711"},
+            nsIDOMKeyEvent.DOM_VK_SCROLL_LOCK, "", SHOULD_DELIVER_ALL);
+
+    testKey({layout:"US", keyCode:0x7A, function:1, chars:"\uF704", unmodifiedChars:"\uF704"},
+            nsIDOMKeyEvent.DOM_VK_F1, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x78, function:1, chars:"\uF705", unmodifiedChars:"\uF705"},
+            nsIDOMKeyEvent.DOM_VK_F2, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x63, function:1, chars:"\uF706", unmodifiedChars:"\uF706"},
+            nsIDOMKeyEvent.DOM_VK_F3, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x76, function:1, chars:"\uF707", unmodifiedChars:"\uF707"},
+            nsIDOMKeyEvent.DOM_VK_F4, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x60, function:1, chars:"\uF708", unmodifiedChars:"\uF708"},
+            nsIDOMKeyEvent.DOM_VK_F5, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x61, function:1, chars:"\uF709", unmodifiedChars:"\uF709"},
+            nsIDOMKeyEvent.DOM_VK_F6, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x62, function:1, chars:"\uF70A", unmodifiedChars:"\uF70A"},
+            nsIDOMKeyEvent.DOM_VK_F7, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x64, function:1, chars:"\uF70B", unmodifiedChars:"\uF70B"},
+            nsIDOMKeyEvent.DOM_VK_F8, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x65, function:1, chars:"\uF70C", unmodifiedChars:"\uF70C"},
+            nsIDOMKeyEvent.DOM_VK_F9, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x6D, function:1, chars:"\uF70D", unmodifiedChars:"\uF70D"},
+            nsIDOMKeyEvent.DOM_VK_F10, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x67, function:1, chars:"\uF70E", unmodifiedChars:"\uF70E"},
+            nsIDOMKeyEvent.DOM_VK_F11, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x6F, function:1, chars:"\uF70F", unmodifiedChars:"\uF70F"},
+            nsIDOMKeyEvent.DOM_VK_F12, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x6A, function:1, chars:"\uF713", unmodifiedChars:"\uF713"},
+            nsIDOMKeyEvent.DOM_VK_F16, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x40, function:1, chars:"\uF714", unmodifiedChars:"\uF714"},
+            nsIDOMKeyEvent.DOM_VK_F17, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4F, function:1, chars:"\uF715", unmodifiedChars:"\uF715"},
+            nsIDOMKeyEvent.DOM_VK_F18, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x50, function:1, chars:"\uF716", unmodifiedChars:"\uF716"},
+            nsIDOMKeyEvent.DOM_VK_F19, "", SHOULD_DELIVER_ALL);
+
+    // US
+    // Alphabet
+    testKey({layout:"US", keyCode:0x00, chars:"a", unmodifiedChars:"a"},
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x00, shift:1, chars:"A", unmodifiedChars:"A"},
+            nsIDOMKeyEvent.DOM_VK_A, "A", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x00, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x00, alt:1, chars:"\u00E5", unmodifiedChars:"a"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u00E5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x00, command:1, chars:"a", unmodifiedChars:"a"},
+            nsIDOMKeyEvent.DOM_VK_A, "a", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x0B, chars:"b", unmodifiedChars:"b"},
+            nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0B, shift:1, chars:"B", unmodifiedChars:"B"},
+            nsIDOMKeyEvent.DOM_VK_B, "B", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0B, ctrl:1, chars:"\u0002", unmodifiedChars:"b"},
+            nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0B, alt:1, chars:"\u222B", unmodifiedChars:"b"},
+            nsIDOMKeyEvent.DOM_VK_B, "\u222B", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0B, command:1, chars:"b", unmodifiedChars:"b"},
+            nsIDOMKeyEvent.DOM_VK_B, "b", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x08, chars:"c", unmodifiedChars:"c"},
+            nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x08, shift:1, chars:"C", unmodifiedChars:"C"},
+            nsIDOMKeyEvent.DOM_VK_C, "C", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x08, ctrl:1, chars:"\u0003", unmodifiedChars:"c"},
+            nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x08, alt:1, chars:"\u00E7", unmodifiedChars:"c"},
+            nsIDOMKeyEvent.DOM_VK_C, "\u00E7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x08, command:1, chars:"c", unmodifiedChars:"c"},
+            nsIDOMKeyEvent.DOM_VK_C, "c", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x02, chars:"d", unmodifiedChars:"d"},
+            nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x02, shift:1, chars:"D", unmodifiedChars:"D"},
+            nsIDOMKeyEvent.DOM_VK_D, "D", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x02, ctrl:1, chars:"\u0004", unmodifiedChars:"d"},
+            nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x02, alt:1, chars:"\u2202", unmodifiedChars:"d"},
+            nsIDOMKeyEvent.DOM_VK_D, "\u2202", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x02, command:1, chars:"d", unmodifiedChars:"d"},
+            nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x0E, chars:"e", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0E, shift:1, chars:"E", unmodifiedChars:"E"},
+            nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0E, ctrl:1, chars:"\u0005", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0E, alt:1, chars:"", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "", SHOULD_DELIVER_NONE); // dead key
+    testKey({layout:"US", keyCode:0x0E, command:1, chars:"e", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x03, chars:"f", unmodifiedChars:"f"},
+            nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x03, shift:1, chars:"F", unmodifiedChars:"F"},
+            nsIDOMKeyEvent.DOM_VK_F, "F", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x03, ctrl:1, chars:"\u0006", unmodifiedChars:"f"},
+            nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x03, alt:1, chars:"\u0192", unmodifiedChars:"f"},
+            nsIDOMKeyEvent.DOM_VK_F, "\u0192", SHOULD_DELIVER_ALL);
+    // XXX This test starts fullscreen mode.
+    // testKey({layout:"US", keyCode:0x03, command:1, chars:"f", unmodifiedChars:"f"},
+    //         nsIDOMKeyEvent.DOM_VK_F, "f", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x05, chars:"g", unmodifiedChars:"g"},
+            nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x05, shift:1, chars:"G", unmodifiedChars:"G"},
+            nsIDOMKeyEvent.DOM_VK_G, "G", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x05, ctrl:1, chars:"\u0007", unmodifiedChars:"g"},
+            nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x05, alt:1, chars:"\u00A9", unmodifiedChars:"g"},
+            nsIDOMKeyEvent.DOM_VK_G, "\u00A9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x05, command:1, chars:"g", unmodifiedChars:"g"},
+            nsIDOMKeyEvent.DOM_VK_G, "g", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x04, chars:"h", unmodifiedChars:"h"},
+            nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x04, shift:1, chars:"H", unmodifiedChars:"H"},
+            nsIDOMKeyEvent.DOM_VK_H, "H", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x04, ctrl:1, chars:"\u0008", unmodifiedChars:"h"},
+            nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x04, alt:1, chars:"\u02D9", unmodifiedChars:"h"},
+            nsIDOMKeyEvent.DOM_VK_H, "\u02D9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x04, command:1, chars:"h", unmodifiedChars:"h"},
+            nsIDOMKeyEvent.DOM_VK_H, "h", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x22, chars:"i", unmodifiedChars:"i"},
+            nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x22, shift:1, chars:"I", unmodifiedChars:"I"},
+            nsIDOMKeyEvent.DOM_VK_I, "I", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x22, ctrl:1, chars:"\u0009", unmodifiedChars:"i"},
+            nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x22, alt:1, chars:"", unmodifiedChars:"i"},
+            nsIDOMKeyEvent.DOM_VK_I, "", SHOULD_DELIVER_NONE); // dead key
+    // XXX This test causes memory leak.
+    // testKey({layout:"US", keyCode:0x22, command:1, chars:"i", unmodifiedChars:"i"},
+    //         nsIDOMKeyEvent.DOM_VK_I, "i", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x26, chars:"j", unmodifiedChars:"j"},
+            nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x26, shift:1, chars:"J", unmodifiedChars:"J"},
+            nsIDOMKeyEvent.DOM_VK_J, "J", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x26, ctrl:1, chars:"\u000A", unmodifiedChars:"j"},
+            nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x26, alt:1, chars:"\u2206", unmodifiedChars:"j"},
+            nsIDOMKeyEvent.DOM_VK_J, "\u2206", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x26, command:1, chars:"j", unmodifiedChars:"j"},
+            nsIDOMKeyEvent.DOM_VK_J, "j", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x28, chars:"k", unmodifiedChars:"k"},
+            nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x28, shift:1, chars:"K", unmodifiedChars:"K"},
+            nsIDOMKeyEvent.DOM_VK_K, "K", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x28, ctrl:1, chars:"\u000B", unmodifiedChars:"k"},
+            nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x28, alt:1, chars:"\u02DA", unmodifiedChars:"k"},
+            nsIDOMKeyEvent.DOM_VK_K, "\u02DA", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x28, command:1, chars:"k", unmodifiedChars:"k"},
+            nsIDOMKeyEvent.DOM_VK_K, "k", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x25, chars:"l", unmodifiedChars:"l"},
+            nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x25, shift:1, chars:"L", unmodifiedChars:"L"},
+            nsIDOMKeyEvent.DOM_VK_L, "L", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x25, ctrl:1, chars:"\u000C", unmodifiedChars:"l"},
+            nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x25, alt:1, chars:"\u00AC", unmodifiedChars:"l"},
+            nsIDOMKeyEvent.DOM_VK_L, "\u00AC", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x25, command:1, chars:"l", unmodifiedChars:"l"},
+            nsIDOMKeyEvent.DOM_VK_L, "l", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x2E, chars:"m", unmodifiedChars:"m"},
+            nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2E, shift:1, chars:"M", unmodifiedChars:"M"},
+            nsIDOMKeyEvent.DOM_VK_M, "M", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2E, ctrl:1, chars:"\u000D", unmodifiedChars:"m"},
+            nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2E, alt:1, chars:"\u00B5", unmodifiedChars:"m"},
+            nsIDOMKeyEvent.DOM_VK_M, "\u00B5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2E, command:1, chars:"m", unmodifiedChars:"m"},
+            nsIDOMKeyEvent.DOM_VK_M, "m", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x2D, chars:"n", unmodifiedChars:"n"},
+            nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2D, shift:1, chars:"N", unmodifiedChars:"N"},
+            nsIDOMKeyEvent.DOM_VK_N, "N", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2D, ctrl:1, chars:"\u000E", unmodifiedChars:"n"},
+            nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2D, alt:1, chars:"", unmodifiedChars:"n"},
+            nsIDOMKeyEvent.DOM_VK_N, "", SHOULD_DELIVER_NONE); // dead key
+    testKey({layout:"US", keyCode:0x2D, command:1, chars:"n", unmodifiedChars:"n"},
+            nsIDOMKeyEvent.DOM_VK_N, "n", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x1F, chars:"o", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1F, shift:1, chars:"O", unmodifiedChars:"O"},
+            nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1F, ctrl:1, chars:"\u000F", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1F, alt:1, chars:"\u00F8", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "\u00F8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1F, command:1, chars:"o", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x23, chars:"p", unmodifiedChars:"p"},
+            nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x23, shift:1, chars:"P", unmodifiedChars:"P"},
+            nsIDOMKeyEvent.DOM_VK_P, "P", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x23, ctrl:1, chars:"\u0010", unmodifiedChars:"p"},
+            nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x23, alt:1, chars:"\u03C0", unmodifiedChars:"p"},
+            nsIDOMKeyEvent.DOM_VK_P, "\u03C0", SHOULD_DELIVER_ALL);
+    // XXX This test starts private browsing mode (stopped at the confirmation dialog)
+    // testKey({layout:"US", keyCode:0x23, command:1, chars:"p", unmodifiedChars:"p"},
+    //         nsIDOMKeyEvent.DOM_VK_P, "p", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x0C, chars:"q", unmodifiedChars:"q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0C, shift:1, chars:"Q", unmodifiedChars:"Q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "Q", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0C, ctrl:1, chars:"\u0011", unmodifiedChars:"q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0C, alt:1, chars:"\u0153", unmodifiedChars:"q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "\u0153", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0C, command:1, chars:"q", unmodifiedChars:"q"},
+            nsIDOMKeyEvent.DOM_VK_Q, "q", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x0F, chars:"r", unmodifiedChars:"r"},
+            nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0F, shift:1, chars:"R", unmodifiedChars:"R"},
+            nsIDOMKeyEvent.DOM_VK_R, "R", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0F, ctrl:1, chars:"\u0012", unmodifiedChars:"r"},
+            nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0F, alt:1, chars:"\u00AE", unmodifiedChars:"r"},
+            nsIDOMKeyEvent.DOM_VK_R, "\u00AE", SHOULD_DELIVER_ALL);
+    // XXX This test makes some tabs and dialogs.
+    // testKey({layout:"US", keyCode:0x0F, command:1, chars:"r", unmodifiedChars:"r"},
+    //         nsIDOMKeyEvent.DOM_VK_R, "r", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x01, chars:"s", unmodifiedChars:"s"},
+            nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x01, shift:1, chars:"S", unmodifiedChars:"S"},
+            nsIDOMKeyEvent.DOM_VK_S, "S", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x01, ctrl:1, chars:"\u0013", unmodifiedChars:"s"},
+            nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x01, alt:1, chars:"\u00DF", unmodifiedChars:"s"},
+            nsIDOMKeyEvent.DOM_VK_S, "\u00DF", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x01, command:1, chars:"s", unmodifiedChars:"s"},
+            nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x11, chars:"t", unmodifiedChars:"t"},
+            nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x11, shift:1, chars:"T", unmodifiedChars:"T"},
+            nsIDOMKeyEvent.DOM_VK_T, "T", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x11, ctrl:1, chars:"\u0014", unmodifiedChars:"t"},
+            nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x11, alt:1, chars:"\u2020", unmodifiedChars:"t"},
+            nsIDOMKeyEvent.DOM_VK_T, "\u2020", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x11, command:1, chars:"t", unmodifiedChars:"t"},
+            nsIDOMKeyEvent.DOM_VK_T, "t", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x20, chars:"u", unmodifiedChars:"u"},
+            nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x20, shift:1, chars:"U", unmodifiedChars:"U"},
+            nsIDOMKeyEvent.DOM_VK_U, "U", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x20, ctrl:1, chars:"\u0015", unmodifiedChars:"u"},
+            nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x20, alt:1, chars:"", unmodifiedChars:"u"},
+            nsIDOMKeyEvent.DOM_VK_U, "", SHOULD_DELIVER_NONE); // dead key
+    testKey({layout:"US", keyCode:0x20, command:1, chars:"u", unmodifiedChars:"u"},
+            nsIDOMKeyEvent.DOM_VK_U, "u", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x09, chars:"v", unmodifiedChars:"v"},
+            nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x09, shift:1, chars:"V", unmodifiedChars:"V"},
+            nsIDOMKeyEvent.DOM_VK_V, "V", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x09, ctrl:1, chars:"\u0016", unmodifiedChars:"v"},
+            nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x09, alt:1, chars:"\u221A", unmodifiedChars:"v"},
+            nsIDOMKeyEvent.DOM_VK_V, "\u221A", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x09, command:1, chars:"v", unmodifiedChars:"v"},
+            nsIDOMKeyEvent.DOM_VK_V, "v", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x0D, chars:"w", unmodifiedChars:"w"},
+            nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0D, shift:1, chars:"W", unmodifiedChars:"W"},
+            nsIDOMKeyEvent.DOM_VK_W, "W", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0D, ctrl:1, chars:"\u0017", unmodifiedChars:"w"},
+            nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0D, alt:1, chars:"\u2211", unmodifiedChars:"w"},
+            nsIDOMKeyEvent.DOM_VK_W, "\u2211", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x0D, command:1, chars:"w", unmodifiedChars:"w"},
+            nsIDOMKeyEvent.DOM_VK_W, "w", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x07, chars:"x", unmodifiedChars:"x"},
+            nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x07, shift:1, chars:"X", unmodifiedChars:"X"},
+            nsIDOMKeyEvent.DOM_VK_X, "X", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x07, ctrl:1, chars:"\u0018", unmodifiedChars:"x"},
+            nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x07, alt:1, chars:"\u2248", unmodifiedChars:"x"},
+            nsIDOMKeyEvent.DOM_VK_X, "\u2248", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x07, command:1, chars:"x", unmodifiedChars:"x"},
+            nsIDOMKeyEvent.DOM_VK_X, "x", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x10, chars:"y", unmodifiedChars:"y"},
+            nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x10, shift:1, chars:"Y", unmodifiedChars:"Y"},
+            nsIDOMKeyEvent.DOM_VK_Y, "Y", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x10, ctrl:1, chars:"\u0019", unmodifiedChars:"y"},
+            nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x10, alt:1, chars:"\u00A5", unmodifiedChars:"y"},
+            nsIDOMKeyEvent.DOM_VK_Y, "\u00A5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x10, command:1, chars:"y", unmodifiedChars:"y"},
+            nsIDOMKeyEvent.DOM_VK_Y, "y", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x06, chars:"z", unmodifiedChars:"z"},
+            nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x06, shift:1, chars:"Z", unmodifiedChars:"Z"},
+            nsIDOMKeyEvent.DOM_VK_Z, "Z", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x06, ctrl:1, chars:"\u001A", unmodifiedChars:"z"},
+            nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x06, alt:1, chars:"\u03A9", unmodifiedChars:"z"},
+            nsIDOMKeyEvent.DOM_VK_Z, "\u03A9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x06, command:1, chars:"z", unmodifiedChars:"z"},
+            nsIDOMKeyEvent.DOM_VK_Z, "z", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
 
-   testKey({layout:"Dvorak-Qwerty", keyCode:0x22, command:1, alt:1, chars:"^", unmodifiedChars:"c"},
-           -1, "^", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
-   testKey({layout:"Dvorak-Qwerty", keyCode:0x22, command:1, alt:1, shift:1, chars:"\u02C6", unmodifiedChars:"C"},
-           -1, "\u02C6", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
-  }
+    // numeric
+    testKey({layout:"US", keyCode:0x12, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x12, shift:1, chars:"!", unmodifiedChars:"!"},
+            nsIDOMKeyEvent.DOM_VK_1, "!", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x12, ctrl:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x12, alt:1, chars:"\u00A1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_1, "\u00A1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x12, command:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x13, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x13, shift:1, chars:"@", unmodifiedChars:"@"},
+            nsIDOMKeyEvent.DOM_VK_2, "@", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x13, ctrl:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x13, alt:1, chars:"\u00A1", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_2, "\u00A1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x13, command:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x14, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x14, shift:1, chars:"#", unmodifiedChars:"#"},
+            nsIDOMKeyEvent.DOM_VK_3, "#", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x14, ctrl:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x14, alt:1, chars:"\u00A3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_3, "\u00A3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x14, command:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x15, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x15, shift:1, chars:"$", unmodifiedChars:"$"},
+            nsIDOMKeyEvent.DOM_VK_4, "$", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x15, ctrl:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x15, alt:1, chars:"\u00A2", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_4, "\u00A2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x15, command:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x17, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x17, shift:1, chars:"%", unmodifiedChars:"%"},
+            nsIDOMKeyEvent.DOM_VK_5, "%", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x17, ctrl:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x17, alt:1, chars:"\u221E", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_5, "\u221E", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x17, command:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x16, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x16, shift:1, chars:"^", unmodifiedChars:"^"},
+            nsIDOMKeyEvent.DOM_VK_6, "^", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x16, ctrl:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x16, alt:1, chars:"\u00A7", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_6, "\u00A7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x16, command:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x1A, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1A, shift:1, chars:"\u0026;", unmodifiedChars:"\u0026;"},
+            nsIDOMKeyEvent.DOM_VK_7, "\u0026", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1A, ctrl:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1A, alt:1, chars:"\u00B6", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_7, "\u00B6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1A, command:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x1C, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1C, shift:1, chars:"*", unmodifiedChars:"*"},
+            nsIDOMKeyEvent.DOM_VK_8, "*", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1C, ctrl:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1C, alt:1, chars:"\u2022", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_8, "\u2022", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1C, command:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x19, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x19, shift:1, chars:"(", unmodifiedChars:"("},
+            nsIDOMKeyEvent.DOM_VK_9, "(", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x19, ctrl:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x19, alt:1, chars:"\u00AA", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_9, "\u00AA", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x19, command:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x1D, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1D, shift:1, chars:")", unmodifiedChars:")"},
+            nsIDOMKeyEvent.DOM_VK_0, ")", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1D, ctrl:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1D, alt:1, chars:"\u00BA", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_0, "\u00BA", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1D, command:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+
+    // other chracters
+    testKey({layout:"US", keyCode:0x32, chars:"`", unmodifiedChars:"`"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x32, shift:1, chars:"~", unmodifiedChars:"~"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "~", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x32, ctrl:1, chars:"`", unmodifiedChars:"`"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x32, alt:1, chars:"", unmodifiedChars:"`"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "", SHOULD_DELIVER_NONE); // dead key
+    testKey({layout:"US", keyCode:0x32, command:1, chars:"`", unmodifiedChars:"`"},
+            nsIDOMKeyEvent.DOM_VK_BACK_QUOTE, "`", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x1B, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1B, shift:1, chars:"_", unmodifiedChars:"_"},
+            nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "_", SHOULD_DELIVER_ALL);
+    // TODO:
+    // testKey({layout:"US", keyCode:0x1B, ctrl:1, chars:"\u001F", unmodifiedChars:"-"},
+    //         nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1B, alt:1, chars:"\u2013", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "\u2013", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1B, command:1, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_HYPHEN_MINUS, "-", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x18, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x18, shift:1, chars:"+", unmodifiedChars:"+"},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "+", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x18, ctrl:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x18, alt:1, chars:"\u2260", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "\u2260", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x18, command:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x21, chars:"[", unmodifiedChars:"["},
+            nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x21, shift:1, chars:"{", unmodifiedChars:"{"},
+            nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "{", SHOULD_DELIVER_ALL);
+    // TODO:
+    // testKey({layout:"US", keyCode:0x21, ctrl:1, chars:"\u001B", unmodifiedChars:"["},
+    //         nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x21, alt:1, chars:"\u201C", unmodifiedChars:"["},
+            nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "\u201C", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x21, command:1, chars:"[", unmodifiedChars:"["},
+            nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, "[", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x1E, chars:"]", unmodifiedChars:"]"},
+            nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1E, shift:1, chars:"}", unmodifiedChars:"}"},
+            nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "}", SHOULD_DELIVER_ALL);
+    // TODO:
+    // testKey({layout:"US", keyCode:0x1E, ctrl:1, chars:"\u001D", unmodifiedChars:"]"},
+    //         nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1E, alt:1, chars:"\u2018", unmodifiedChars:"]"},
+            nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "\u2018", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x1E, command:1, chars:"]", unmodifiedChars:"]"},
+            nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, "]", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x2A, chars:"\\", unmodifiedChars:"\\"},
+            nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2A, shift:1, chars:"|", unmodifiedChars:"|"},
+            nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "|", SHOULD_DELIVER_ALL);
+    // TODO:
+    // testKey({layout:"US", keyCode:0x2A, ctrl:1, chars:"\u001C", unmodifiedChars:"\\"},
+    //         nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2A, alt:1, chars:"\u00AB", unmodifiedChars:"\\"},
+            nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\u00AB", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2A, command:1, chars:"\\", unmodifiedChars:"\\"},
+            nsIDOMKeyEvent.DOM_VK_BACK_SLASH, "\\", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x29, chars:";", unmodifiedChars:";"},
+            nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x29, shift:1, chars:":", unmodifiedChars:":"},
+            nsIDOMKeyEvent.DOM_VK_SEMICOLON, ":", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x29, ctrl:1, chars:";", unmodifiedChars:";"},
+            nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x29, alt:1, chars:"\u2026", unmodifiedChars:";"},
+            nsIDOMKeyEvent.DOM_VK_SEMICOLON, "\u2026", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x29, command:1, chars:";", unmodifiedChars:";"},
+            nsIDOMKeyEvent.DOM_VK_SEMICOLON, ";", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x27, chars:"'", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x27, shift:1, chars:"\"", unmodifiedChars:"\""},
+            nsIDOMKeyEvent.DOM_VK_QUOTE, "\"", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x27, ctrl:1, chars:"'", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x27, alt:1, chars:"\u00E6", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_QUOTE, "\u00E6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x27, command:1, chars:"'", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_QUOTE, "'", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x2B, chars:",", unmodifiedChars:","},
+            nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2B, shift:1, chars:"\u003C", unmodifiedChars:"\u003C"},
+            nsIDOMKeyEvent.DOM_VK_COMMA, "\u003C", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2B, ctrl:1, chars:",", unmodifiedChars:","},
+            nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2B, alt:1, chars:"\u2264", unmodifiedChars:","},
+            nsIDOMKeyEvent.DOM_VK_COMMA, "\u2264", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2B, command:1, chars:",", unmodifiedChars:","},
+            nsIDOMKeyEvent.DOM_VK_COMMA, ",", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x2F, chars:".", unmodifiedChars:"."},
+            nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2F, shift:1, chars:"\u003E", unmodifiedChars:"\u003E"},
+            nsIDOMKeyEvent.DOM_VK_PERIOD, "\u003E", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2F, ctrl:1, chars:".", unmodifiedChars:"."},
+            nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2F, alt:1, chars:"\u2265", unmodifiedChars:"."},
+            nsIDOMKeyEvent.DOM_VK_PERIOD, "\u2265", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2F, command:1, chars:".", unmodifiedChars:"."},
+            nsIDOMKeyEvent.DOM_VK_PERIOD, ".", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x2C, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2C, shift:1, chars:"?", unmodifiedChars:"?"},
+            nsIDOMKeyEvent.DOM_VK_SLASH, "?", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2C, ctrl:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2C, alt:1, chars:"\u00F7", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_SLASH, "\u00F7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x2C, command:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_SLASH, "/", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+
+    // numpad
+    testKey({layout:"US", keyCode:0x53, numLock:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x53, numLock:1, shift:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x53, numLock:1, ctrl:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x53, numLock:1, alt:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x53, numLock:1, command:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD1, "1", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x54, numLock:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x54, numLock:1, shift:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x54, numLock:1, ctrl:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x54, numLock:1, alt:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x54, numLock:1, command:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD2, "2", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x55, numLock:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x55, numLock:1, shift:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x55, numLock:1, ctrl:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x55, numLock:1, alt:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x55, numLock:1, command:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD3, "3", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x56, numLock:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x56, numLock:1, shift:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x56, numLock:1, ctrl:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x56, numLock:1, alt:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x56, numLock:1, command:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD4, "4", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x57, numLock:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x57, numLock:1, shift:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x57, numLock:1, ctrl:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x57, numLock:1, alt:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x57, numLock:1, command:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD5, "5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x58, numLock:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x58, numLock:1, shift:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x58, numLock:1, ctrl:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x58, numLock:1, alt:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x58, numLock:1, command:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x59, numLock:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x59, numLock:1, shift:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x59, numLock:1, ctrl:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x59, numLock:1, alt:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x59, numLock:1, command:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD7, "7", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x5B, numLock:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5B, numLock:1, shift:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5B, numLock:1, ctrl:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5B, numLock:1, alt:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5B, numLock:1, command:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD8, "8", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x5C, numLock:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5C, numLock:1, shift:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5C, numLock:1, ctrl:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5C, numLock:1, alt:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x5C, numLock:1, command:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD9, "9", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x52, numLock:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x52, numLock:1, shift:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x52, numLock:1, ctrl:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x52, numLock:1, alt:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x52, numLock:1, command:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_NUMPAD0, "0", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x51, numLock:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x51, numLock:1, shift:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x51, numLock:1, ctrl:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x51, numLock:1, alt:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x51, numLock:1, command:1, chars:"=", unmodifiedChars:"="},
+            nsIDOMKeyEvent.DOM_VK_EQUALS, "=", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x4B, numLock:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4B, numLock:1, shift:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4B, numLock:1, ctrl:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4B, numLock:1, alt:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4B, numLock:1, command:1, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_DIVIDE, "/", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x43, numLock:1, chars:"*", unmodifiedChars:"*"},
+            nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x43, numLock:1, shift:1, chars:"*", unmodifiedChars:"*"},
+            nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x43, numLock:1, ctrl:1, chars:"*", unmodifiedChars:"*"},
+            nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x43, numLock:1, alt:1, chars:"*", unmodifiedChars:"*"},
+            nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x43, numLock:1, command:1, chars:"*", unmodifiedChars:"*"},
+            nsIDOMKeyEvent.DOM_VK_MULTIPLY, "*", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x4E, numLock:1, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4E, numLock:1, shift:1, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4E, numLock:1, ctrl:1, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4E, numLock:1, alt:1, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4E, numLock:1, command:1, chars:"-", unmodifiedChars:"-"},
+            nsIDOMKeyEvent.DOM_VK_SUBTRACT, "-", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x45, numLock:1, chars:"+", unmodifiedChars:"+"},
+            nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x45, numLock:1, shift:1, chars:"+", unmodifiedChars:"+"},
+            nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x45, numLock:1, ctrl:1, chars:"+", unmodifiedChars:"+"},
+            nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x45, numLock:1, alt:1, chars:"+", unmodifiedChars:"+"},
+            nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x45, numLock:1, command:1, chars:"+", unmodifiedChars:"+"},
+            nsIDOMKeyEvent.DOM_VK_ADD, "+", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"US", keyCode:0x4C, numLock:1, chars:"\u0003", unmodifiedChars:"\u0003"},
+            nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4C, numLock:1, shift:1, chars:"\u0003", unmodifiedChars:"\u0003"},
+            nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4C, numLock:1, ctrl:1, chars:"\u0003", unmodifiedChars:"\u0003"},
+            nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4C, numLock:1, alt:1, chars:"\u0003", unmodifiedChars:"\u0003"},
+            nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"US", keyCode:0x4C, numLock:1, command:1, chars:"\u0003", unmodifiedChars:"\u0003"},
+            nsIDOMKeyEvent.DOM_VK_RETURN, "", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+
+    // French, numeric
+    testKey({layout:"French", keyCode:0x12, chars:"\u0026", unmodifiedChars:"\u0026"},
+            nsIDOMKeyEvent.DOM_VK_1, "\u0026", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x12, shift:1, chars:"1", unmodifiedChars:"1"},
+            nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x12, ctrl:1, chars:"1", unmodifiedChars:"\u0026"},
+            nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x12, command:1, chars:"\u0026", unmodifiedChars:"\u0026"},
+            nsIDOMKeyEvent.DOM_VK_1, "1", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x13, chars:"\u00E9", unmodifiedChars:"\u00E9"},
+            nsIDOMKeyEvent.DOM_VK_2, "\u00E9", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x13, shift:1, chars:"2", unmodifiedChars:"2"},
+            nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x13, ctrl:1, chars:"2", unmodifiedChars:"\u00E9"},
+            nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x13, command:1, chars:"\u00E9", unmodifiedChars:"\u00E9"},
+            nsIDOMKeyEvent.DOM_VK_2, "2", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x14, chars:"\"", unmodifiedChars:"\""},
+            nsIDOMKeyEvent.DOM_VK_3, "\"", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x14, shift:1, chars:"3", unmodifiedChars:"3"},
+            nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x14, ctrl:1, chars:"3", unmodifiedChars:"\""},
+            nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x14, command:1, chars:"\"", unmodifiedChars:"\""},
+            nsIDOMKeyEvent.DOM_VK_3, "3", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x15, chars:"'", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_4, "'", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x15, shift:1, chars:"4", unmodifiedChars:"4"},
+            nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x15, ctrl:1, chars:"4", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x15, command:1, chars:"'", unmodifiedChars:"'"},
+            nsIDOMKeyEvent.DOM_VK_4, "4", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x17, chars:"(", unmodifiedChars:"("},
+            nsIDOMKeyEvent.DOM_VK_5, "(", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x17, shift:1, chars:"5", unmodifiedChars:"5"},
+            nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x17, ctrl:1, chars:"5", unmodifiedChars:"("},
+            nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x17, command:1, chars:"(", unmodifiedChars:"("},
+            nsIDOMKeyEvent.DOM_VK_5, "5", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x16, chars:"\u00A7", unmodifiedChars:"\u00A7"},
+            nsIDOMKeyEvent.DOM_VK_6, "\u00A7", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x16, shift:1, chars:"6", unmodifiedChars:"6"},
+            nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_ALL);
+    // TODO:
+    // testKey({layout:"French", keyCode:0x16, ctrl:1, chars:"\u001D", unmodifiedChars:"\u00A7"},
+    //         nsIDOMKeyEvent.DOM_VK_6, "", SHOULD_DELIVER_ALL); // Ctrl+6 sets strange char
+    testKey({layout:"French", keyCode:0x16, command:1, chars:"\u00A7", unmodifiedChars:"\u00A7"},
+            nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x1A, chars:"\u00E8", unmodifiedChars:"\u00E8"},
+            nsIDOMKeyEvent.DOM_VK_7, "\u00E8", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1A, shift:1, chars:"7", unmodifiedChars:"7"},
+            nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1A, ctrl:1, chars:"7", unmodifiedChars:"\u00E8"},
+            nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1A, command:1, chars:"\u00E8", unmodifiedChars:"\u00E8"},
+            nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x1C, chars:"!", unmodifiedChars:"!"},
+            nsIDOMKeyEvent.DOM_VK_8, "!", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1C, shift:1, chars:"8", unmodifiedChars:"8"},
+            nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1C, ctrl:1, chars:"8", unmodifiedChars:"!"},
+            nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1C, command:1, chars:"!", unmodifiedChars:"!"},
+            nsIDOMKeyEvent.DOM_VK_8, "8", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x19, chars:"\u00E7", unmodifiedChars:"\u00E7"},
+            nsIDOMKeyEvent.DOM_VK_9, "\u00E7", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x19, shift:1, chars:"9", unmodifiedChars:"9"},
+            nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_ALL);
+    // TODO:
+    // testKey({layout:"French", keyCode:0x19, ctrl:1, chars:"\u001C", unmodifiedChars:"\u00E7"},
+    //         nsIDOMKeyEvent.DOM_VK_9, "", SHOULD_DELIVER_ALL); // Ctrl+9 sets strange char
+    testKey({layout:"French", keyCode:0x19, command:1, chars:"\u00E7", unmodifiedChars:"\u00E7"},
+            nsIDOMKeyEvent.DOM_VK_9, "9", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"French", keyCode:0x1D, chars:"\u00E0", unmodifiedChars:"\u00E0"},
+            nsIDOMKeyEvent.DOM_VK_0, "\u00E0", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1D, shift:1, chars:"0", unmodifiedChars:"0"},
+            nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_ALL);
+    // XXX No events fired, not sure the reason.
+    // testKey({layout:"French", keyCode:0x1D, ctrl:1, chars:"", unmodifiedChars:"\u00E0"},
+    //         nsIDOMKeyEvent.DOM_VK_0, "", SHOULD_DELIVER_ALL);
+    testKey({layout:"French", keyCode:0x1D, command:1, chars:"\u00E0", unmodifiedChars:"\u00E0"},
+            nsIDOMKeyEvent.DOM_VK_0, "0", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+
+    // Thai
+    // keycode should be DOM_VK_[A-Z] of the key on the latest ASCII capable keyboard layout is for alphabet
+    testKey({layout:"Thai", keyCode:0x00, chars:"\u0E1F", unmodifiedChars:"\u0E1F"},
+            nsIDOMKeyEvent.DOM_VK_A, "\u0E1F", SHOULD_DELIVER_ALL);
+    // keycode should be shifted character if unshifted character isn't an ASCII character
+    testKey({layout:"Thai", keyCode:0x27, chars:"\u0E07", unmodifiedChars:"\u0E07"},
+            nsIDOMKeyEvent.DOM_VK_PERIOD, "\u0E07", SHOULD_DELIVER_ALL);
+    // keycode should be zero if the character of the key on the latest ASCII capable keyboard layout isn't for alphabet
+    testKey({layout:"Thai", keyCode:0x2F, chars:"\u0E43", unmodifiedChars:"\u0E43"},
+            0, "\u0E43", SHOULD_DELIVER_ALL);
+    // keycode should be DOM_VK_[0-9] if the key on the latest ASCII capable keyboard layout is for numeric
+    testKey({layout:"Thai", keyCode:0x12, chars:"\u0E45", unmodifiedChars:"\u0E45"},
+            nsIDOMKeyEvent.DOM_VK_1, "\u0E45", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x13, chars:"/", unmodifiedChars:"/"},
+            nsIDOMKeyEvent.DOM_VK_2, "/", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x14, chars:"_", unmodifiedChars:"_"},
+            nsIDOMKeyEvent.DOM_VK_3, "_", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x15, chars:"\u0E20", unmodifiedChars:"\u0E20"},
+            nsIDOMKeyEvent.DOM_VK_4, "\u0E20", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x17, chars:"\u0E16", unmodifiedChars:"\u0E16"},
+            nsIDOMKeyEvent.DOM_VK_5, "\u0E16", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x16, chars:"\u0E38", unmodifiedChars:"\u0E38"},
+            nsIDOMKeyEvent.DOM_VK_6, "\u0E38", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x1A, chars:"\u0E36", unmodifiedChars:"\u0E36"},
+            nsIDOMKeyEvent.DOM_VK_7, "\u0E36", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x1C, chars:"\u0E04", unmodifiedChars:"\u0E04"},
+            nsIDOMKeyEvent.DOM_VK_8, "\u0E04", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x19, chars:"\u0E15", unmodifiedChars:"\u0E15"},
+            nsIDOMKeyEvent.DOM_VK_9, "\u0E15", SHOULD_DELIVER_ALL);
+    testKey({layout:"Thai", keyCode:0x1D, chars:"\u0E08", unmodifiedChars:"\u0E08"},
+            nsIDOMKeyEvent.DOM_VK_0, "\u0E08", SHOULD_DELIVER_ALL);
+
+    // Dvorak-Qwerty, layout should be changed when Command key is pressed.
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x01, chars:"o", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x01, shift:1, chars:"O", unmodifiedChars:"O"},
+            nsIDOMKeyEvent.DOM_VK_O, "O", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x01, ctrl:1, chars:"\u000F", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "o", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x01, alt:1, chars:"\u00F8", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_O, "\u00F8", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x01, command:1, chars:"s", unmodifiedChars:"o"},
+            nsIDOMKeyEvent.DOM_VK_S, "s", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x02, chars:"e", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x02, shift:1, chars:"E", unmodifiedChars:"E"},
+            nsIDOMKeyEvent.DOM_VK_E, "E", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x02, ctrl:1, chars:"\u0005", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "e", SHOULD_DELIVER_ALL);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x02, alt:1, chars:"", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_E, "", SHOULD_DELIVER_NONE); // dead key
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x02, command:1, chars:"d", unmodifiedChars:"e"},
+            nsIDOMKeyEvent.DOM_VK_D, "d", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x22, command:1, alt:1, chars:"^", unmodifiedChars:"c"},
+            nsIDOMKeyEvent.DOM_VK_I, "^", SHOULD_DELIVER_KEYDOWN_KEYPRESS);
+    testKey({layout:"Dvorak-Qwerty", keyCode:0x22, command:1, alt:1, shift:1, chars:"\u02C6", unmodifiedChars:"C"},
+            nsIDOMKeyEvent.DOM_VK_I, "\u02C6", SHOULD_DELIVER_KEYDOWN_KEYPRESS);  }
   else if (IS_WIN) {
     // On Windows, you can use Spy++ or Winspector (free) to watch window messages.
     // The keyCode is given by the wParam of the last WM_KEYDOWN message. The
     // chars string is given by the wParam of the WM_CHAR message. unmodifiedChars
     // is not needed on Windows.
 
     // Plain text input
     testKey({layout:"US", keyCode:65, chars:"a"},