Bug 912858 part.5 Implement KeyboardEvent.key for printable keys on Android r=jchen+smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 11 Dec 2013 01:14:54 +0900
changeset 159681 55fbb1c094867835f4e10f96af52c056ea19bbd6
parent 159680 31e994f78a9e4748b5453a7f885a89544fee4798
child 159682 5decddfef63daa3391b14d791c931ef742a7157d
push id37389
push usermasayuki@d-toybox.com
push dateTue, 10 Dec 2013 16:15:04 +0000
treeherdermozilla-inbound@65ef767aa205 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjchen
bugs912858
milestone29.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 912858 part.5 Implement KeyboardEvent.key for printable keys on Android r=jchen+smaug
mobile/android/base/GeckoEvent.java
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsWindow.cpp
--- a/mobile/android/base/GeckoEvent.java
+++ b/mobile/android/base/GeckoEvent.java
@@ -161,16 +161,17 @@ public class GeckoEvent {
     private double mY;
     private double mZ;
 
     private int mMetaState;
     private int mFlags;
     private int mKeyCode;
     private int mUnicodeChar;
     private int mBaseUnicodeChar; // mUnicodeChar without meta states applied
+    private int mDOMPrintableKeyValue;
     private int mRepeatCount;
     private int mCount;
     private int mStart;
     private int mEnd;
     private String mCharacters;
     private String mCharactersExtra;
     private String mData;
     private int mRangeType;
@@ -248,16 +249,27 @@ public class GeckoEvent {
         mFlags = k.getFlags();
         mKeyCode = k.getKeyCode();
         mUnicodeChar = k.getUnicodeChar(mMetaState);
         // e.g. for Ctrl+A, Android returns 0 for mUnicodeChar,
         // but Gecko expects 'a', so we return that in mBaseUnicodeChar
         mBaseUnicodeChar = k.getUnicodeChar(0);
         mRepeatCount = k.getRepeatCount();
         mCharacters = k.getCharacters();
+        if (mUnicodeChar >= ' ') {
+            mDOMPrintableKeyValue = mUnicodeChar;
+        } else {
+            int unmodifiedMetaState =
+                mMetaState & ~(KeyEvent.META_ALT_MASK |
+                               KeyEvent.META_CTRL_MASK |
+                               KeyEvent.META_META_MASK);
+            if (unmodifiedMetaState != mMetaState) {
+                mDOMPrintableKeyValue = k.getUnicodeChar(unmodifiedMetaState);
+            }
+        }
         mDomKeyLocation = isJoystickButton(mKeyCode) ? DomKeyLocation.DOM_KEY_LOCATION_JOYSTICK
                                                      : DomKeyLocation.DOM_KEY_LOCATION_MOBILE;
     }
 
     /**
      * This method tests if a key is one of the described in:
      * https://bugzilla.mozilla.org/show_bug.cgi?id=756504#c0
      * @param keyCode int with the key code (Android key constant from KeyEvent)
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -30,16 +30,17 @@ jfieldID AndroidGeckoEvent::jYField = 0;
 jfieldID AndroidGeckoEvent::jZField = 0;
 jfieldID AndroidGeckoEvent::jDistanceField = 0;
 jfieldID AndroidGeckoEvent::jRectField = 0;
 jfieldID AndroidGeckoEvent::jNativeWindowField = 0;
 
 jfieldID AndroidGeckoEvent::jCharactersField = 0;
 jfieldID AndroidGeckoEvent::jCharactersExtraField = 0;
 jfieldID AndroidGeckoEvent::jDataField = 0;
+jfieldID AndroidGeckoEvent::jDOMPrintableKeyValueField = 0;
 jfieldID AndroidGeckoEvent::jKeyCodeField = 0;
 jfieldID AndroidGeckoEvent::jMetaStateField = 0;
 jfieldID AndroidGeckoEvent::jDomKeyLocationField = 0;
 jfieldID AndroidGeckoEvent::jFlagsField = 0;
 jfieldID AndroidGeckoEvent::jUnicodeCharField = 0;
 jfieldID AndroidGeckoEvent::jBaseUnicodeCharField = 0;
 jfieldID AndroidGeckoEvent::jRepeatCountField = 0;
 jfieldID AndroidGeckoEvent::jCountField = 0;
@@ -141,16 +142,17 @@ AndroidGeckoEvent::InitGeckoEventClass(J
     jCharactersExtraField = getField("mCharactersExtra", "Ljava/lang/String;");
     jDataField = getField("mData", "Ljava/lang/String;");
     jKeyCodeField = getField("mKeyCode", "I");
     jMetaStateField = getField("mMetaState", "I");
     jDomKeyLocationField = getField("mDomKeyLocation", "Lorg/mozilla/gecko/GeckoEvent$DomKeyLocation;");
     jFlagsField = getField("mFlags", "I");
     jUnicodeCharField = getField("mUnicodeChar", "I");
     jBaseUnicodeCharField = getField("mBaseUnicodeChar", "I");
+    jDOMPrintableKeyValueField = getField("mDOMPrintableKeyValue", "I");
     jRepeatCountField = getField("mRepeatCount", "I");
     jCountField = getField("mCount", "I");
     jStartField = getField("mStart", "I");
     jEndField = getField("mEnd", "I");
     jPointerIndexField = getField("mPointerIndex", "I");
     jRangeTypeField = getField("mRangeType", "I");
     jRangeStylesField = getField("mRangeStyles", "I");
     jRangeLineStyleField = getField("mRangeLineStyle", "I");
@@ -419,16 +421,18 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jo
         case IME_KEY_EVENT:
             mTime = jenv->GetLongField(jobj, jTimeField);
             mMetaState = jenv->GetIntField(jobj, jMetaStateField);
             mDomKeyLocation = ReadDomKeyLocation(jenv, jobj);
             mFlags = jenv->GetIntField(jobj, jFlagsField);
             mKeyCode = jenv->GetIntField(jobj, jKeyCodeField);
             mUnicodeChar = jenv->GetIntField(jobj, jUnicodeCharField);
             mBaseUnicodeChar = jenv->GetIntField(jobj, jBaseUnicodeCharField);
+            mDOMPrintableKeyValue =
+                jenv->GetIntField(jobj, jDOMPrintableKeyValueField);
             mRepeatCount = jenv->GetIntField(jobj, jRepeatCountField);
             ReadCharactersField(jenv);
             break;
 
         case NATIVE_GESTURE_EVENT:
             mTime = jenv->GetLongField(jobj, jTimeField);
             mMetaState = jenv->GetIntField(jobj, jMetaStateField);
             mCount = jenv->GetIntField(jobj, jCountField);
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -506,16 +506,17 @@ public:
     uint32_t DomKeyLocation() { return mDomKeyLocation; }
     bool IsAltPressed() const { return (mMetaState & AMETA_ALT_MASK) != 0; }
     bool IsShiftPressed() const { return (mMetaState & AMETA_SHIFT_MASK) != 0; }
     bool IsCtrlPressed() const { return (mMetaState & AMETA_CTRL_MASK) != 0; }
     bool IsMetaPressed() const { return (mMetaState & AMETA_META_MASK) != 0; }
     int Flags() { return mFlags; }
     int UnicodeChar() { return mUnicodeChar; }
     int BaseUnicodeChar() { return mBaseUnicodeChar; }
+    int DOMPrintableKeyValue() { return mDOMPrintableKeyValue; }
     int RepeatCount() const { return mRepeatCount; }
     int Count() { return mCount; }
     int Start() { return mStart; }
     int End() { return mEnd; }
     int PointerIndex() { return mPointerIndex; }
     int RangeType() { return mRangeType; }
     int RangeStyles() { return mRangeStyles; }
     int RangeLineStyle() { return mRangeLineStyle; }
@@ -547,17 +548,17 @@ protected:
     nsTArray<nsIntPoint> mPoints;
     nsTArray<nsIntPoint> mPointRadii;
     nsTArray<int> mPointIndicies;
     nsTArray<float> mOrientations;
     nsTArray<float> mPressures;
     nsIntRect mRect;
     int mFlags, mMetaState;
     uint32_t mDomKeyLocation;
-    int mKeyCode, mUnicodeChar, mBaseUnicodeChar;
+    int mKeyCode, mUnicodeChar, mBaseUnicodeChar, mDOMPrintableKeyValue;
     int mRepeatCount;
     int mCount;
     int mStart, mEnd;
     int mRangeType, mRangeStyles, mRangeLineStyle;
     bool mRangeBoldLine;
     int mRangeForeColor, mRangeBackColor, mRangeLineColor;
     double mX, mY, mZ;
     int mPointerIndex;
@@ -611,16 +612,17 @@ protected:
     static jfieldID jZField;
     static jfieldID jDistanceField;
     static jfieldID jRectField;
     static jfieldID jNativeWindowField;
 
     static jfieldID jCharactersField;
     static jfieldID jCharactersExtraField;
     static jfieldID jDataField;
+    static jfieldID jDOMPrintableKeyValueField;
     static jfieldID jKeyCodeField;
     static jfieldID jMetaStateField;
     static jfieldID jDomKeyLocationField;
     static jfieldID jFlagsField;
     static jfieldID jCountField;
     static jfieldID jStartField;
     static jfieldID jEndField;
     static jfieldID jPointerIndexField;
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1388,74 +1388,82 @@ static unsigned int ConvertAndroidKeyCod
 
         default:
             ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
                  "No DOM keycode for Android keycode %d", androidKeyCode);
         return 0;
     }
 }
 
-static KeyNameIndex ConvertAndroidKeyCodeToKeyNameIndex(int aAndroidKeyCode)
+static KeyNameIndex
+ConvertAndroidKeyCodeToKeyNameIndex(AndroidGeckoEvent& aAndroidGeckoEvent)
 {
+    int keyCode = aAndroidGeckoEvent.KeyCode();
     // Special-case alphanumeric keycodes because they are most common.
-    if (aAndroidKeyCode >= AKEYCODE_A && aAndroidKeyCode <= AKEYCODE_Z) {
-        return KEY_NAME_INDEX_PrintableKey;
+    if (keyCode >= AKEYCODE_A && keyCode <= AKEYCODE_Z) {
+        return KEY_NAME_INDEX_USE_STRING;
     }
 
-    if (aAndroidKeyCode >= AKEYCODE_0 && aAndroidKeyCode <= AKEYCODE_9) {
-        return KEY_NAME_INDEX_PrintableKey;
+    if (keyCode >= AKEYCODE_0 && keyCode <= AKEYCODE_9) {
+        return KEY_NAME_INDEX_USE_STRING;
     }
 
-    switch (aAndroidKeyCode) {
+    switch (keyCode) {
 
 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
         case aNativeKey: return aKeyNameIndex;
 
 #include "NativeKeyToDOMKeyName.h"
 
 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
 
         // KEYCODE_0 (7) ... KEYCODE_9 (16)
         case AKEYCODE_STAR:               // '*' key
         case AKEYCODE_POUND:              // '#' key
 
         // KEYCODE_A (29) ... KEYCODE_Z (54)
 
         case AKEYCODE_COMMA:              // ',' key
         case AKEYCODE_PERIOD:             // '.' key
+        case AKEYCODE_SPACE:
         case AKEYCODE_GRAVE:              // '`' key
         case AKEYCODE_MINUS:              // '-' key
         case AKEYCODE_EQUALS:             // '=' key
         case AKEYCODE_LEFT_BRACKET:       // '[' key
         case AKEYCODE_RIGHT_BRACKET:      // ']' key
         case AKEYCODE_BACKSLASH:          // '\' key
         case AKEYCODE_SEMICOLON:          // ';' key
         case AKEYCODE_APOSTROPHE:         // ''' key
         case AKEYCODE_SLASH:              // '/' key
         case AKEYCODE_AT:                 // '@' key
         case AKEYCODE_PLUS:               // '+' key
 
-        case AKEYCODE_UNKNOWN:
         case AKEYCODE_NUMPAD_0:
         case AKEYCODE_NUMPAD_1:
         case AKEYCODE_NUMPAD_2:
         case AKEYCODE_NUMPAD_3:
         case AKEYCODE_NUMPAD_4:
         case AKEYCODE_NUMPAD_5:
         case AKEYCODE_NUMPAD_6:
         case AKEYCODE_NUMPAD_7:
         case AKEYCODE_NUMPAD_8:
         case AKEYCODE_NUMPAD_9:
-
+        case AKEYCODE_NUMPAD_DIVIDE:
+        case AKEYCODE_NUMPAD_MULTIPLY:
+        case AKEYCODE_NUMPAD_SUBTRACT:
+        case AKEYCODE_NUMPAD_ADD:
+        case AKEYCODE_NUMPAD_DOT:
+        case AKEYCODE_NUMPAD_COMMA:
+        case AKEYCODE_NUMPAD_EQUALS:
         case AKEYCODE_NUMPAD_LEFT_PAREN:
         case AKEYCODE_NUMPAD_RIGHT_PAREN:
 
         case AKEYCODE_YEN:                // yen sign key
         case AKEYCODE_RO:                 // Japanese Ro key
-            return KEY_NAME_INDEX_PrintableKey;
+            return KEY_NAME_INDEX_USE_STRING;
 
         case AKEYCODE_SOFT_LEFT:
         case AKEYCODE_SOFT_RIGHT:
         case AKEYCODE_CALL:
         case AKEYCODE_ENDCALL:
         case AKEYCODE_SYM:                // Symbol modifier
         case AKEYCODE_NUM:                // XXX Not sure
         case AKEYCODE_HEADSETHOOK:
@@ -1516,19 +1524,28 @@ static KeyNameIndex ConvertAndroidKeyCod
         case AKEYCODE_CALENDAR:
         case AKEYCODE_MUSIC:
         case AKEYCODE_CALCULATOR:
 
         case AKEYCODE_ZENKAKU_HANKAKU:
         case AKEYCODE_KATAKANA_HIRAGANA:
             return KEY_NAME_INDEX_Unidentified;
 
+        case AKEYCODE_UNKNOWN:
+            MOZ_ASSERT(
+                aAndroidGeckoEvent.Action() != AKEY_EVENT_ACTION_MULTIPLE,
+                "Don't call this when action is AKEY_EVENT_ACTION_MULTIPLE!");
+            // It's actually an unknown key if the action isn't ACTION_MULTIPLE.
+            // However, it might cause text input.  So, let's check the value.
+            return aAndroidGeckoEvent.DOMPrintableKeyValue() ?
+                KEY_NAME_INDEX_USE_STRING : KEY_NAME_INDEX_Unidentified;
+
         default:
             ALOG("ConvertAndroidKeyCodeToKeyNameIndex: "
-                 "No DOM key name index for Android keycode %d", aAndroidKeyCode);
+                 "No DOM key name index for Android keycode %d", keyCode);
             return KEY_NAME_INDEX_Unidentified;
     }
 }
 
 static void InitPluginEvent(ANPEvent* pluginEvent, ANPKeyActions keyAction,
                             AndroidGeckoEvent& key)
 {
     int androidKeyCode = key.KeyCode();
@@ -1549,19 +1566,24 @@ static void InitPluginEvent(ANPEvent* pl
     pluginEvent->data.key.modifiers = modifiers;
     pluginEvent->data.key.repeatCount = key.RepeatCount();
 }
 
 void
 nsWindow::InitKeyEvent(WidgetKeyboardEvent& event, AndroidGeckoEvent& key,
                        ANPEvent* pluginEvent)
 {
-    int androidKeyCode = key.KeyCode();
-    event.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(androidKeyCode);
-    uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(androidKeyCode);
+    event.mKeyNameIndex = ConvertAndroidKeyCodeToKeyNameIndex(key);
+    if (event.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) {
+        int keyValue = key.DOMPrintableKeyValue();
+        if (keyValue) {
+            event.mKeyValue = static_cast<PRUnichar>(keyValue);
+        }
+    }
+    uint32_t domKeyCode = ConvertAndroidKeyCodeToDOMKeyCode(key.KeyCode());
 
     if (event.message == NS_KEY_PRESS) {
         // Android gives us \n, so filter out some control characters.
         int charCode = key.UnicodeChar();
         if (!charCode) {
             charCode = key.BaseUnicodeChar();
         }
         event.isChar = (charCode >= ' ');