Bug 1137563 part.4 Implement IMEInputHandler::WillDispatchKeyboardEvent() r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 16 Mar 2016 13:47:50 +0900
changeset 288908 c0f93cbdbc17f7ea0b8b533f6e0d93845bef96c0
parent 288907 d8b11f1455eeb17e50a8553365428cd6f79c0c79
child 288909 eb7c36e2ef5d48262bc8566da9ea37623e7d0883
push id73609
push usermasayuki@d-toybox.com
push dateWed, 16 Mar 2016 04:47:58 +0000
treeherdermozilla-inbound@eb7c36e2ef5d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1137563
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1137563 part.4 Implement IMEInputHandler::WillDispatchKeyboardEvent() r=m_kato
widget/cocoa/TextInputHandler.h
widget/cocoa/TextInputHandler.mm
widget/tests/test_keycodes.xul
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -222,16 +222,36 @@ public:
    *                              if caller sets null to this, this method will
    *                              compute the character to be input from
    *                              characters of aNativeKeyEvent.
    */
   void InitKeyEvent(NSEvent *aNativeKeyEvent, WidgetKeyboardEvent& aKeyEvent,
                     const nsAString *aInsertString = nullptr);
 
   /**
+   * WillDispatchKeyboardEvent() computes aKeyEvent.alternativeCharCodes and
+   * recompute aKeyEvent.charCode if it's necessary.
+   *
+   * @param aNativeKeyEvent       A native key event for which you want to
+   *                              dispatch a Gecko key event.
+   * @param aInsertString         If caller expects that the event will cause
+   *                              a character to be input (say in an editor),
+   *                              the caller should set this.  Otherwise,
+   *                              if caller sets null to this, this method will
+   *                              compute the character to be input from
+   *                              characters of aNativeKeyEvent.
+   * @param aKeyEvent             The result -- a Gecko key event initialized
+   *                              from the native key event.  This must be
+   *                              eKeyPress event.
+   */
+  void WillDispatchKeyboardEvent(NSEvent* aNativeKeyEvent,
+                                 const nsAString* aInsertString,
+                                 WidgetKeyboardEvent& 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.
@@ -281,33 +301,44 @@ protected:
    *                              this is a result of ::LMGetKbdType().
    * @return                      If succeeded and the result is one character,
    *                              returns the charCode of it.  Otherwise,
    *                              returns 0.
    */
   uint32_t TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbType);
 
   /**
-   * InitKeyPressEvent() initializes aKeyEvent for aNativeKeyEvent.
-   * Don't call this method when aKeyEvent isn't eKeyPress.
+   * ComputeInsertString() computes string to be inserted with the key event.
    *
-   * @param aNativeKeyEvent       A native key event for which you want to
-   *                              dispatch a Gecko key event.
-   * @param aInsertChar           A character to be input in an editor by the
-   *                              event.
-   * @param aKeyEvent             The result -- a Gecko key event initialized
-   *                              from the native key event.  This must be
-   *                              eKeyPress event.
-   * @param aKbType               A native Keyboard Type value.  Typically,
-   *                              this is a result of ::LMGetKbdType().
+   * @param aNativeKeyEvent     The native key event which causes our keyboard
+   *                            event(s).
+   * @param aKeyEvent           A Gecko key event which was partially
+   *                            initialized with aNativeKeyEvent.
+   * @param aInsertString       The string to be inputting by aNativeKeyEvent.
+   *                            This should be specified by InsertText().
+   *                            In other words, if the key event doesn't cause
+   *                            a call of InsertText(), this can be nullptr.
+   * @param aResult             The string which should be set to charCode of
+   *                            keypress event(s).
    */
-  void InitKeyPressEvent(NSEvent *aNativeKeyEvent,
-                         char16_t aInsertChar,
-                         WidgetKeyboardEvent& aKeyEvent,
-                         UInt32 aKbType);
+  void ComputeInsertStringForCharCode(NSEvent* aNativeKeyEvent,
+                                      const WidgetKeyboardEvent& aKeyEvent,
+                                      const nsAString* aInsertString,
+                                      nsAString& aResult);
+
+  /**
+   * IsPrintableKeyEvent() returns true if aNativeKeyEvent is caused by
+   * a printable key.  Otherwise, returns false.
+   */
+  bool IsPrintableKeyEvent(NSEvent* aNativeKeyEvent) const;
+
+  /**
+   * GetKbdType() returns physical keyboard type.
+   */
+  UInt32 GetKbdType() const;
 
   bool GetBoolProperty(const CFStringRef aKey);
   bool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr);
   bool GetStringProperty(const CFStringRef aKey, nsAString &aStr);
 
   TISInputSourceRef mInputSource;
   TISInputSourceRef mKeyboardLayout;
   CFArrayRef mInputSourceList;
@@ -462,16 +493,19 @@ protected:
    * handling a native keydown event, we need to store the event for insertText,
    * doCommandBySelector and various action message handlers of NSResponder
    * such as [NSResponder insertNewline:sender].
    */
   struct KeyEventState
   {
     // Handling native key event
     NSEvent* mKeyEvent;
+    // String specified by InsertText().  This is not null only during a
+    // call of InsertText().
+    nsAString* mInsertString;
     // Whether keydown event was consumed by web contents or chrome contents.
     bool mKeyDownHandled;
     // Whether keypress event was dispatched for mKeyEvent.
     bool mKeyPressDispatched;
     // Whether keypress event was consumed by web contents or chrome contents.
     bool mKeyPressHandled;
     // Whether the key event causes other key events via IME or something.
     bool mCausedOtherKeyEvents;
@@ -512,16 +546,17 @@ protected:
     }
 
     void Clear()
     {
       if (mKeyEvent) {
         [mKeyEvent release];
         mKeyEvent = nullptr;
       }
+      mInsertString = nullptr;
       mKeyDownHandled = false;
       mKeyPressDispatched = false;
       mKeyPressHandled = false;
       mCausedOtherKeyEvents = false;
     }
 
     bool IsDefaultPrevented() const
     {
@@ -530,17 +565,17 @@ protected:
 
     bool CanDispatchKeyPressEvent() const
     {
       return !mKeyPressDispatched && !IsDefaultPrevented();
     }
   };
 
   /**
-   * Helper class for guaranteeing cleaning mCurrentKeyEvent
+   * Helper classes for guaranteeing cleaning mCurrentKeyEvent
    */
   class AutoKeyEventStateCleaner
   {
   public:
     explicit AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) :
       mHandler(aHandler)
     {
     }
@@ -548,16 +583,33 @@ protected:
     ~AutoKeyEventStateCleaner()
     {
       mHandler->RemoveCurrentKeyEvent();
     }
   private:
     RefPtr<TextInputHandlerBase> mHandler;
   };
 
+  class MOZ_STACK_CLASS AutoInsertStringClearer
+  {
+  public:
+    explicit AutoInsertStringClearer(KeyEventState* aState)
+      : mState(aState)
+    {
+    }
+    ~AutoInsertStringClearer()
+    {
+      if (mState) {
+        mState->mInsertString = nullptr;
+      }
+    }
+  private:
+    KeyEventState* mState;
+  };
+
   /**
    * mCurrentKeyEvents stores all key events which are being processed.
    * When we call interpretKeyEvents, IME may generate other key events.
    * mCurrentKeyEvents[0] is the latest key event.
    */
   nsTArray<KeyEventState*> mCurrentKeyEvents;
 
   /**
@@ -611,16 +663,32 @@ protected:
   KeyEventState* GetCurrentKeyEvent()
   {
     if (mCurrentKeyEvents.Length() == 0) {
       return nullptr;
     }
     return mCurrentKeyEvents[mCurrentKeyEvents.Length() - 1];
   }
 
+  struct KeyboardLayoutOverride final
+  {
+    int32_t mKeyboardLayout;
+    bool mOverrideEnabled;
+
+    KeyboardLayoutOverride() :
+      mKeyboardLayout(0), mOverrideEnabled(false)
+    {
+    }
+  };
+
+  const KeyboardLayoutOverride& KeyboardLayoutOverrideRef() const
+  {
+    return mKeyboardOverride;
+  }
+
   /**
    * IsPrintableChar() checks whether the unicode character is
    * a non-printable ASCII character or not.  Note that this returns
    * TRUE even if aChar is a non-printable UNICODE character.
    *
    * @param aChar                 A unicode character.
    * @return                      TRUE if aChar is a printable ASCII character
    *                              or a unicode character.  Otherwise, i.e,
@@ -643,26 +711,16 @@ protected:
    *
    * @param aNativeKeyCode        A native keyCode.
    * @return                      TRUE if aNativeKeyCode is for a modifier key.
    *                              Otherwise, FALSE.
    */
   static bool IsModifierKey(UInt32 aNativeKeyCode);
 
 private:
-  struct KeyboardLayoutOverride {
-    int32_t mKeyboardLayout;
-    bool mOverrideEnabled;
-
-    KeyboardLayoutOverride() :
-      mKeyboardLayout(0), mOverrideEnabled(false)
-    {
-    }
-  };
-
   KeyboardLayoutOverride mKeyboardOverride;
 
   static int32_t sSecureEventInputCount;
 };
 
 /**
  * IMEInputHandler manages:
  *   1. The IME/keyboard layout statement of nsChildView.
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -748,16 +748,135 @@ TISInputSourceWrapper::Clear()
   mInputSourceList = nullptr;
   mInputSource = nullptr;
   mKeyboardLayout = nullptr;
   mIsRTL = -1;
   mUCKeyboardLayout = nullptr;
   mOverrideKeyboard = false;
 }
 
+bool
+TISInputSourceWrapper::IsPrintableKeyEvent(NSEvent* aNativeKeyEvent) const
+{
+  UInt32 nativeKeyCode = [aNativeKeyEvent keyCode];
+
+  bool isPrintableKey = !TextInputHandler::IsSpecialGeckoKey(nativeKeyCode);
+  if (isPrintableKey &&
+      [aNativeKeyEvent type] != NSKeyDown &&
+      [aNativeKeyEvent type] != NSKeyUp) {
+    NS_WARNING("Why the printable key doesn't cause NSKeyDown or NSKeyUp?");
+    isPrintableKey = false;
+  }
+  return isPrintableKey;
+}
+
+UInt32
+TISInputSourceWrapper::GetKbdType() const
+{
+  // 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.
+  return mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType();
+}
+
+void
+TISInputSourceWrapper::ComputeInsertStringForCharCode(
+                         NSEvent* aNativeKeyEvent,
+                         const WidgetKeyboardEvent& aKeyEvent,
+                         const nsAString* aInsertString,
+                         nsAString& aResult)
+{
+  if (aInsertString) {
+    // If the caller expects that the aInsertString will be input, we shouldn't
+    // change it.
+    aResult = *aInsertString;
+  } else if (IsPrintableKeyEvent(aNativeKeyEvent)) {
+    // If IME is open, [aNativeKeyEvent characters] may be a character
+    // which will be appended to the composition string.  However, especially,
+    // while IME is disabled, most users and developers expect the key event
+    // works as IME closed.  So, we should compute the aResult with
+    // the ASCII capable keyboard layout.
+    // NOTE: Such keyboard layouts typically change the layout to its ASCII
+    //       capable layout when Command key is pressed.  And we don't worry
+    //       when Control key is pressed too because it causes inputting
+    //       control characters.
+    UInt32 nativeKeyCode = [aNativeKeyEvent keyCode];
+    if (!aKeyEvent.IsMeta() && !aKeyEvent.IsControl() && IsOpenedIMEMode()) {
+      UInt32 state =
+        nsCocoaUtils::ConvertToCarbonModifier([aNativeKeyEvent modifierFlags]);
+      uint32_t ch = TranslateToChar(nativeKeyCode, state, GetKbdType());
+      if (ch) {
+        aResult = ch;
+      }
+    } else {
+      // If the caller isn't sure what string will be input, let's use
+      // characters of NSEvent.
+      nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], aResult);
+    }
+
+    // If control key is pressed and the eventChars is a non-printable control
+    // character, we should convert it to ASCII alphabet.
+    if (aKeyEvent.IsControl() &&
+        !aResult.IsEmpty() && aResult[0] <= char16_t(26)) {
+      aResult = (aKeyEvent.IsShift() ^ aKeyEvent.IsCapsLocked()) ?
+        static_cast<char16_t>(aResult[0] + ('A' - 1)) :
+        static_cast<char16_t>(aResult[0] + ('a' - 1));
+    }
+    // If Meta key is pressed, it may cause to switch the keyboard layout like
+    // Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
+    else if (aKeyEvent.IsMeta() &&
+             !(aKeyEvent.IsControl() || aKeyEvent.IsAlt())) {
+      UInt32 kbType = GetKbdType();
+      UInt32 numLockState =
+        aKeyEvent.IsNumLocked() ? kEventKeyModifierNumLockMask : 0;
+      UInt32 capsLockState = aKeyEvent.IsCapsLocked() ? alphaLock : 0;
+      UInt32 shiftState = aKeyEvent.IsShift() ? shiftKey : 0;
+      uint32_t uncmdedChar =
+        TranslateToChar(nativeKeyCode, numLockState, kbType);
+      uint32_t cmdedChar =
+        TranslateToChar(nativeKeyCode, cmdKey | numLockState, kbType);
+      // If we can make a good guess at the characters that the user would
+      // expect this key combination to produce (with and without Shift) then
+      // use those characters.  This also corrects for CapsLock.
+      uint32_t ch = 0;
+      if (uncmdedChar == cmdedChar) {
+        // The characters produced with Command seem similar to those without
+        // Command.
+        ch = TranslateToChar(nativeKeyCode,
+                             shiftState | capsLockState | numLockState, kbType);
+      } else {
+        TISInputSourceWrapper USLayout("com.apple.keylayout.US");
+        uint32_t uncmdedUSChar =
+          USLayout.TranslateToChar(nativeKeyCode, numLockState, kbType);
+        // If it looks like characters from US keyboard layout when Command key
+        // is pressed, we should compute a character in the layout.
+        if (uncmdedUSChar == cmdedChar) {
+          ch = USLayout.TranslateToChar(nativeKeyCode,
+                          shiftState | capsLockState | numLockState, kbType);
+        }
+      }
+
+      // If there is a more preferred character for the commanded key event,
+      // we should use it.
+      if (ch) {
+        aResult = ch;
+      }
+    }
+  }
+
+  // Remove control characters which shouldn't be inputted on editor.
+  // XXX Currently, we don't find any cases inserting control characters with
+  //     printable character.  So, just checking first character is enough.
+  if (!aResult.IsEmpty() && IsControlChar(aResult[0])) {
+    aResult.Truncate();
+  }
+}
+
 void
 TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent,
                                     WidgetKeyboardEvent& aKeyEvent,
                                     const nsAString *aInsertString)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   MOZ_LOG(gLog, LogLevel::Info,
@@ -786,120 +905,21 @@ TISInputSourceWrapper::InitKeyEvent(NSEv
     nsCocoaUtils::GetStringForNSString([aNativeKeyEvent charactersIgnoringModifiers], nativeCharsIgnoringModifiers);
     aKeyEvent.mNativeCharactersIgnoringModifiers.Assign(nativeCharsIgnoringModifiers);
   } else if ([aNativeKeyEvent type] == NSFlagsChanged) {
     aKeyEvent.mNativeKeyCode = [aNativeKeyEvent keyCode];
     aKeyEvent.mNativeModifierFlags = [aNativeKeyEvent modifierFlags];
   }
 
   aKeyEvent.refPoint = LayoutDeviceIntPoint(0, 0);
-
-  // 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.isChar = false; // XXX not used in XP level
+
+  UInt32 kbType = GetKbdType();
   UInt32 nativeKeyCode = [aNativeKeyEvent keyCode];
 
-  bool isPrintableKey = !TextInputHandler::IsSpecialGeckoKey(nativeKeyCode);
-  if (isPrintableKey &&
-      [aNativeKeyEvent type] != NSKeyDown &&
-      [aNativeKeyEvent type] != NSKeyUp) {
-    NS_WARNING("Why the printable key doesn't cause NSKeyDown or NSKeyUp?");
-    isPrintableKey = false;
-  }
-
-  // Decide what string will be input.
-  nsAutoString insertString;
-  if (aInsertString) {
-    // If the caller expects that the aInsertString will be input, we shouldn't
-    // change it.
-    insertString = *aInsertString;
-  } else if (isPrintableKey) {
-    // If IME is open, [aNativeKeyEvent characters] may be a character
-    // which will be appended to the composition string.  However, especially,
-    // while IME is disabled, most users and developers expect the key event
-    // works as IME closed.  So, we should compute the insertString with
-    // the ASCII capable keyboard layout.
-    // NOTE: Such keyboard layouts typically change the layout to its ASCII
-    //       capable layout when Command key is pressed.  And we don't worry
-    //       when Control key is pressed too because it causes inputting
-    //       control characters.
-    if (!aKeyEvent.IsMeta() && !aKeyEvent.IsControl() && IsOpenedIMEMode()) {
-      UInt32 state =
-        nsCocoaUtils::ConvertToCarbonModifier([aNativeKeyEvent modifierFlags]);
-      uint32_t ch = TranslateToChar(nativeKeyCode, state, kbType);
-      if (ch) {
-        insertString = ch;
-      }
-    } else {
-      // If the caller isn't sure what string will be input, let's use
-      // characters of NSEvent.
-      nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters],
-                                         insertString);
-    }
-
-    // If control key is pressed and the eventChars is a non-printable control
-    // character, we should convert it to ASCII alphabet.
-    if (aKeyEvent.IsControl() &&
-        !insertString.IsEmpty() && insertString[0] <= char16_t(26)) {
-      insertString = (aKeyEvent.IsShift() ^ aKeyEvent.IsCapsLocked()) ?
-        static_cast<char16_t>(insertString[0] + ('A' - 1)) :
-        static_cast<char16_t>(insertString[0] + ('a' - 1));
-    }
-    // If Meta key is pressed, it may cause to switch the keyboard layout like
-    // Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
-    else if (aKeyEvent.IsMeta() &&
-             !(aKeyEvent.IsControl() || aKeyEvent.IsAlt())) {
-      UInt32 numLockState =
-        aKeyEvent.IsNumLocked() ? kEventKeyModifierNumLockMask : 0;
-      UInt32 capsLockState = aKeyEvent.IsCapsLocked() ? alphaLock : 0;
-      UInt32 shiftState = aKeyEvent.IsShift() ? shiftKey : 0;
-      uint32_t uncmdedChar =
-        TranslateToChar(nativeKeyCode, numLockState, kbType);
-      uint32_t cmdedChar =
-        TranslateToChar(nativeKeyCode, cmdKey | numLockState, kbType);
-      // If we can make a good guess at the characters that the user would
-      // expect this key combination to produce (with and without Shift) then
-      // use those characters.  This also corrects for CapsLock.
-      uint32_t ch = 0;
-      if (uncmdedChar == cmdedChar) {
-        // The characters produced with Command seem similar to those without
-        // Command.
-        ch = TranslateToChar(nativeKeyCode,
-                             shiftState | capsLockState | numLockState, kbType);
-      } else {
-        TISInputSourceWrapper USLayout("com.apple.keylayout.US");
-        uint32_t uncmdedUSChar =
-          USLayout.TranslateToChar(nativeKeyCode, numLockState, kbType);
-        // If it looks like characters from US keyboard layout when Command key
-        // is pressed, we should compute a character in the layout.
-        if (uncmdedUSChar == cmdedChar) {
-          ch = USLayout.TranslateToChar(nativeKeyCode,
-                          shiftState | capsLockState | numLockState, kbType);
-        }
-      }
-
-      // If there is a more preferred character for the commanded key event,
-      // we should use it.
-      if (ch) {
-        insertString = ch;
-      }
-    }
-  }
-
-  // Remove control characters which shouldn't be inputted on editor.
-  // XXX Currently, we don't find any cases inserting control characters with
-  //     printable character.  So, just checking first character is enough.
-  if (!insertString.IsEmpty() && IsControlChar(insertString[0])) {
-    insertString.Truncate();
-  }
-
   aKeyEvent.keyCode =
     ComputeGeckoKeyCode(nativeKeyCode, kbType, aKeyEvent.IsMeta());
 
   switch (nativeKeyCode) {
     case kVK_Command:
     case kVK_Shift:
     case kVK_Option:
     case kVK_Control:
@@ -944,33 +964,17 @@ TISInputSourceWrapper::InitKeyEvent(NSEv
     ([aNativeKeyEvent type] == NSKeyDown) ? [aNativeKeyEvent isARepeat] : false;
 
   MOZ_LOG(gLog, LogLevel::Info,
     ("%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.mMessage == eKeyPress &&
-      (isPrintableKey || !insertString.IsEmpty())) {
-    InitKeyPressEvent(aNativeKeyEvent,
-                      insertString.IsEmpty() ? 0 : insertString[0],
-                      aKeyEvent, kbType);
-    MOZ_ASSERT(!aKeyEvent.charCode || !IsControlChar(aKeyEvent.charCode),
-               "charCode must not be a control character");
-  } else {
-    aKeyEvent.charCode = 0;
-    aKeyEvent.isChar = false; // XXX not used in XP level
-
-    MOZ_LOG(gLog, LogLevel::Info,
-      ("%p TISInputSourceWrapper::InitKeyEvent, keyCode=0x%X charCode=0x0",
-       this, aKeyEvent.keyCode));
-  }
-
-  if (isPrintableKey) {
+  if (IsPrintableKeyEvent(aNativeKeyEvent)) {
     aKeyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
     // If insertText calls this method, let's use the string.
     if (aInsertString && !aInsertString->IsEmpty() &&
         !IsControlChar((*aInsertString)[0])) {
       aKeyEvent.mKeyValue = *aInsertString;
     }
     // If meta key is pressed, the printable key layout may be switched from
     // non-ASCII capable layout to ASCII capable, or from Dvorak to QWERTY.
@@ -1027,95 +1031,105 @@ TISInputSourceWrapper::InitKeyEvent(NSEv
 
   aKeyEvent.mCodeNameIndex = ComputeGeckoCodeNameIndex(nativeKeyCode);
   MOZ_ASSERT(aKeyEvent.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING);
 
   NS_OBJC_END_TRY_ABORT_BLOCK
 }
 
 void
-TISInputSourceWrapper::InitKeyPressEvent(NSEvent *aNativeKeyEvent,
-                                         char16_t aInsertChar,
-                                         WidgetKeyboardEvent& aKeyEvent,
-                                         UInt32 aKbType)
+TISInputSourceWrapper::WillDispatchKeyboardEvent(
+                         NSEvent* aNativeKeyEvent,
+                         const nsAString* aInsertString,
+                         WidgetKeyboardEvent& aKeyEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  NS_ASSERTION(aKeyEvent.mMessage == eKeyPress,
-               "aKeyEvent must be eKeyPress event");
+  UInt32 kbType = GetKbdType();
 
   if (MOZ_LOG_TEST(gLog, LogLevel::Info)) {
     nsAutoString chars;
     nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], chars);
     NS_ConvertUTF16toUTF8 utf8Chars(chars);
-    char16_t expectedChar = static_cast<char16_t>(aInsertChar);
-    NS_ConvertUTF16toUTF8 utf8ExpectedChar(&expectedChar, 1);
+    char16_t uniChar = static_cast<char16_t>(aKeyEvent.charCode);
     MOZ_LOG(gLog, LogLevel::Info,
-      ("%p TISInputSourceWrapper::InitKeyPressEvent, aNativeKeyEvent=%p, "
-       "[aNativeKeyEvent characters]=\"%s\", aInsertChar=0x%X(%s), "
-       "aKeyEvent.mMessage=%s, aKbType=0x%X, IsOpenedIMEMode()=%s",
-       this, aNativeKeyEvent, utf8Chars.get(), aInsertChar,
-       utf8ExpectedChar.get(), GetGeckoKeyEventType(aKeyEvent), aKbType,
-       TrueOrFalse(IsOpenedIMEMode())));
+      ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, "
+       "aNativeKeyEvent=%p, [aNativeKeyEvent characters]=\"%s\", "
+       "aKeyEvent={ mMessage=%s, charCode=0x%X(%s) }, kbType=0x%X, "
+       "IsOpenedIMEMode()=%s",
+       this, aNativeKeyEvent, utf8Chars.get(),
+       GetGeckoKeyEventType(aKeyEvent), aKeyEvent.charCode,
+       uniChar ? NS_ConvertUTF16toUTF8(&uniChar, 1).get() : "",
+       kbType, TrueOrFalse(IsOpenedIMEMode())));
   }
 
-  aKeyEvent.isChar = true; // this is not a special key  XXX not used in XP
-  aKeyEvent.charCode = aInsertChar;
-  if (aKeyEvent.charCode != 0) {
-    aKeyEvent.keyCode = 0;
+  nsAutoString insertStringForCharCode;
+  ComputeInsertStringForCharCode(aNativeKeyEvent, aKeyEvent, aInsertString,
+                                 insertStringForCharCode);
+  uint32_t charCode =
+    insertStringForCharCode.IsEmpty() ? 0 : insertStringForCharCode[0];
+  if (aKeyEvent.mMessage == eKeyPress) {
+    aKeyEvent.charCode = charCode;
+    aKeyEvent.isChar = true; // this is not a special key  XXX not used in XP
+  } else if (charCode) {
+    // If it's not a keypress event, we need to set alternative char code
+    // to charCode value for shortcut key event handlers.
+    AlternativeCharCode altCharCodes(0, 0);
+    if (!aKeyEvent.IsShift()) {
+      altCharCodes.mUnshiftedCharCode = charCode;
+    } else {
+      altCharCodes.mShiftedCharCode = charCode;
+    }
+    aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
   }
 
   MOZ_LOG(gLog, LogLevel::Info,
-    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
+    ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, "
      "aKeyEvent.keyCode=0x%X, aKeyEvent.charCode=0x%X",
      this, aKeyEvent.keyCode, aKeyEvent.charCode));
 
-  if (!aKeyEvent.IsControl() && !aKeyEvent.IsMeta() && !aKeyEvent.IsAlt()) {
-    return;
-  }
-
   TISInputSourceWrapper USLayout("com.apple.keylayout.US");
   bool isRomanKeyboardLayout = IsASCIICapable();
 
   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;
   }
 
   MOZ_LOG(gLog, LogLevel::Info,
-    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
+    ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, "
      "isRomanKeyboardLayout=%s, key=0x%X",
-     this, TrueOrFalse(isRomanKeyboardLayout), aKbType, key));
+     this, TrueOrFalse(isRomanKeyboardLayout), kbType, key));
 
   nsString str;
 
   // normal chars
-  uint32_t unshiftedChar = TranslateToChar(key, lockState, aKbType);
+  uint32_t unshiftedChar = TranslateToChar(key, lockState, kbType);
   UInt32 shiftLockMod = shiftKey | lockState;
-  uint32_t shiftedChar = TranslateToChar(key, shiftLockMod, aKbType);
+  uint32_t shiftedChar = TranslateToChar(key, shiftLockMod, kbType);
 
   // 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
-  uint32_t uncmdedChar = TranslateToChar(key, numState, aKbType);
+  uint32_t uncmdedChar = TranslateToChar(key, numState, kbType);
   UInt32 shiftNumMod = numState | shiftKey;
-  uint32_t uncmdedShiftChar = TranslateToChar(key, shiftNumMod, aKbType);
-  uint32_t uncmdedUSChar = USLayout.TranslateToChar(key, numState, aKbType);
+  uint32_t uncmdedShiftChar = TranslateToChar(key, shiftNumMod, kbType);
+  uint32_t uncmdedUSChar = USLayout.TranslateToChar(key, numState, kbType);
   UInt32 cmdNumMod = cmdKey | numState;
-  uint32_t cmdedChar = TranslateToChar(key, cmdNumMod, aKbType);
+  uint32_t cmdedChar = TranslateToChar(key, cmdNumMod, kbType);
   UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
-  uint32_t cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, aKbType);
+  uint32_t cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, kbType);
 
   // 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;
 
@@ -1124,17 +1138,17 @@ TISInputSourceWrapper::InitKeyPressEvent
   // normal characters.  These are the characters that the user is most
   // likely to associate with this key.
   if ((unshiftedChar || shiftedChar) &&
       (!aKeyEvent.IsMeta() || !isDvorakQWERTY)) {
     AlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
     aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
   }
   MOZ_LOG(gLog, LogLevel::Info,
-    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
+    ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, "
      "aKeyEvent.isMeta=%s, isDvorakQWERTY=%s, "
      "unshiftedChar=U+%X, shiftedChar=U+%X",
      this, OnOrOff(aKeyEvent.IsMeta()), TrueOrFalse(isDvorakQWERTY),
      unshiftedChar, shiftedChar));
 
   // Most keyboard layouts provide the same characters in the NSEvents
   // with Command+Shift as with Command.  However, with Command+Shift we
   // want the character on the second level.  e.g. With a US QWERTY
@@ -1164,21 +1178,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.
-    uint32_t ch = USLayout.TranslateToChar(key, lockState, aKbType);
+    uint32_t ch = USLayout.TranslateToChar(key, lockState, kbType);
     if (ch) {
       cmdedChar = ch;
     }
-    ch = USLayout.TranslateToChar(key, shiftLockMod, aKbType);
+    ch = USLayout.TranslateToChar(key, shiftLockMod, kbType);
     if (ch) {
       cmdedShiftChar = ch;
     }
   }
 
   // If the current keyboard layout is switched by the Cmd key,
   // we should append cmdedChar and shiftedCmdChar that are
   // Latin char for the key.
@@ -1186,29 +1200,29 @@ TISInputSourceWrapper::InitKeyPressEvent
   // command key is pressed because when command key isn't pressed, uncmded
   // chars have been appended already.
   if ((cmdedChar || cmdedShiftChar) && isCmdSwitchLayout &&
       (aKeyEvent.IsMeta() || !isDvorakQWERTY)) {
     AlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
     aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
   }
   MOZ_LOG(gLog, LogLevel::Info,
-    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
+    ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, "
      "hasCmdShiftOnlyChar=%s, isCmdSwitchLayout=%s, isDvorakQWERTY=%s, "
      "cmdedChar=U+%X, cmdedShiftChar=U+%X",
      this, TrueOrFalse(hasCmdShiftOnlyChar), TrueOrFalse(isDvorakQWERTY),
      TrueOrFalse(isDvorakQWERTY), cmdedChar, cmdedShiftChar));
   // Special case for 'SS' key of German layout. See the comment of
   // hasCmdShiftOnlyChar definition for the detail.
   if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
     AlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
     aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
   }
   MOZ_LOG(gLog, LogLevel::Info,
-    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
+    ("%p TISInputSourceWrapper::WillDispatchKeyboardEvent, "
      "hasCmdShiftOnlyChar=%s, originalCmdedShiftChar=U+%X",
      this, TrueOrFalse(hasCmdShiftOnlyChar), originalCmdedShiftChar));
 
   NS_OBJC_END_TRY_ABORT_BLOCK
 }
 
 uint32_t
 TISInputSourceWrapper::ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
@@ -2117,16 +2131,22 @@ TextInputHandler::InsertText(NSAttribute
 
   InputContext context = mWidget->GetInputContext();
   bool isEditable = (context.mIMEState.mEnabled == IMEState::ENABLED ||
                      context.mIMEState.mEnabled == IMEState::PASSWORD);
   NSRange selectedRange = SelectedRange();
 
   nsAutoString str;
   nsCocoaUtils::GetStringForNSString([aAttrString string], str);
+
+  AutoInsertStringClearer clearer(currentKeyEvent);
+  if (currentKeyEvent) {
+    currentKeyEvent->mInsertString = &str;
+  }
+
   if (!IsIMEComposing() && str.IsEmpty()) {
     // nothing to do if there is no content which can be removed.
     if (!isEditable) {
       return;
     }
     // If replacement range is specified, we need to remove the range.
     // Otherwise, we need to remove the selected range if it's not collapsed.
     if (aReplacementRange && aReplacementRange->location != NSNotFound) {
@@ -2519,17 +2539,33 @@ IMEInputHandler::OnRemovedFrom(TextEvent
 
 NS_IMETHODIMP_(void)
 IMEInputHandler::WillDispatchKeyboardEvent(
                    TextEventDispatcher* aTextEventDispatcher,
                    WidgetKeyboardEvent& aKeyboardEvent,
                    uint32_t aIndexOfKeypress,
                    void* aData)
 {
-  // TODO: Implement this later.
+  // If the keyboard event is not caused by a native key event, we can do
+  // nothing here.
+  if (!aData) {
+    return;
+  }
+
+  KeyEventState* currentKeyEvent = static_cast<KeyEventState*>(aData);
+  NSEvent* nativeEvent = currentKeyEvent->mKeyEvent;
+  nsAString* insertString = currentKeyEvent->mInsertString;
+  if (KeyboardLayoutOverrideRef().mOverrideEnabled) {
+    TISInputSourceWrapper tis;
+    tis.InitByLayoutID(KeyboardLayoutOverrideRef().mKeyboardLayout, true);
+    tis.WillDispatchKeyboardEvent(nativeEvent, insertString, aKeyboardEvent);
+    return;
+  }
+  TISInputSourceWrapper::CurrentInputSource().
+    WillDispatchKeyboardEvent(nativeEvent, insertString, aKeyboardEvent);
 }
 
 void
 IMEInputHandler::NotifyIMEOfFocusChangeInGecko()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   MOZ_LOG(gLog, LogLevel::Info,
@@ -4255,26 +4291,21 @@ TextInputHandlerBase::IsSpecialGeckoKey(
   return false;
 }
 
 /* static */ bool
 TextInputHandlerBase::IsNormalCharInputtingEvent(
                         const WidgetKeyboardEvent& aKeyEvent)
 {
   // this is not character inputting event, simply.
-  if (!aKeyEvent.isChar || !aKeyEvent.charCode || aKeyEvent.IsMeta()) {
+  if (aKeyEvent.mNativeCharacters.IsEmpty() ||
+      aKeyEvent.IsMeta()) {
     return false;
   }
-  // if this is unicode char inputting event, we don't need to check
-  // ctrl/alt/command keys
-  if (aKeyEvent.charCode > 0x7F) {
-    return true;
-  }
-  // ASCII chars should be inputted without ctrl/alt/command keys
-  return !aKeyEvent.IsControl() && !aKeyEvent.IsAlt();
+  return !IsControlChar(aKeyEvent.mNativeCharacters[0]);
 }
 
 /* static */ bool
 TextInputHandlerBase::IsModifierKey(UInt32 aNativeKeyCode)
 {
   switch (aNativeKeyCode) {
     case kVK_CapsLock:
     case kVK_RightCommand:
--- a/widget/tests/test_keycodes.xul
+++ b/widget/tests/test_keycodes.xul
@@ -1157,17 +1157,17 @@ function* runKeyEventTests()
                   nsIDOMKeyEvent.DOM_VK_6, "\u00A7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_6,
                    modifiers:{metaKey:1}, chars:"6", unmodifiedChars:"6"},
                   nsIDOMKeyEvent.DOM_VK_6, "6", SHOULD_DELIVER_KEYDOWN_KEYPRESS, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
                    modifiers:{}, chars:"7", unmodifiedChars:"7"},
                   nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
-                   modifiers:{shiftKey:1}, chars:"\u0026;", unmodifiedChars:"\u0026;"},
+                   modifiers:{shiftKey:1}, chars:"\u0026", unmodifiedChars:"\u0026"},
                   nsIDOMKeyEvent.DOM_VK_7, "\u0026", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
                    modifiers:{ctrlKey:1}, chars:"7", unmodifiedChars:"7"},
                   nsIDOMKeyEvent.DOM_VK_7, "7", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,
                    modifiers:{altKey:1}, chars:"\u00B6", unmodifiedChars:"7"},
                   nsIDOMKeyEvent.DOM_VK_7, "\u00B6", SHOULD_DELIVER_ALL, KeyboardEvent.DOM_KEY_LOCATION_STANDARD);
     yield testKey({layout:KEYBOARD_LAYOUT_EN_US, keyCode:MAC_VK_ANSI_7,