Bug 1343955 - part 3: Implement similar C++ utility of _computeKeyCodeFromChar() in EventUtils.js and make it accssible with nsITextInputProcessor for EventUtils.js r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 03 Oct 2018 09:24:15 +0000
changeset 495139 4a3ba199155a58cd282ee0383925525141593413
parent 495138 fea8e039767a88ebfb73b1f0d43d1f87ee7394e1
child 495140 ade489313a3d24e51837775e344510de4b144e0d
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1343955
milestone64.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 1343955 - part 3: Implement similar C++ utility of _computeKeyCodeFromChar() in EventUtils.js and make it accssible with nsITextInputProcessor for EventUtils.js r=smaug This implement a helper API to guess keyCode value of a printable key with assuming active keyboard layout is US-English. The reason why this stops computing key value from key value is, most users of such API probably want to emulate input from US-English keyboard layout if they don't specify the detail. Therefore, the new API simply maps each ASCII character to a DOM keyCode which is usually mapped in US-English keyboard layout. Differential Revision: https://phabricator.services.mozilla.com/D5515
dom/base/TextInputProcessor.cpp
dom/base/TextInputProcessor.h
dom/interfaces/base/nsITextInputProcessor.idl
testing/mochitest/tests/SimpleTest/EventUtils.js
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -1557,16 +1557,262 @@ TextInputProcessor::GuessCodeNameIndexOf
     case ' ':
       return CODE_NAME_INDEX_Space;
 
     default:
       return CODE_NAME_INDEX_UNKNOWN;
   }
 }
 
+NS_IMETHODIMP
+TextInputProcessor::GuessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
+                      const nsAString& aKeyValue,
+                      JS::Handle<JS::Value> aLocation,
+                      uint8_t aOptionalArgc,
+                      uint32_t* aKeyCodeValue)
+{
+  if (NS_WARN_IF(!aKeyCodeValue)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  Maybe<uint32_t> location;
+  if (aOptionalArgc) {
+    if (aLocation.isNullOrUndefined()) {
+      // location should be nothing.
+    } else if (aLocation.isInt32()) {
+      location = mozilla::Some(static_cast<uint32_t>(aLocation.toInt32()));
+    } else {
+      NS_WARNING_ASSERTION(aLocation.isNullOrUndefined() || aLocation.isInt32(),
+        "aLocation must be undefined, null or int");
+      return NS_ERROR_INVALID_ARG;
+    }
+  }
+
+  *aKeyCodeValue =
+    GuessKeyCodeOfPrintableKeyInUSEnglishLayout(aKeyValue, location);
+  return NS_OK;
+}
+
+// static
+uint32_t
+TextInputProcessor::GuessKeyCodeOfPrintableKeyInUSEnglishLayout(
+                      const nsAString& aKeyValue,
+                      const Maybe<uint32_t>& aLocation)
+{
+  if (aKeyValue.IsEmpty()) {
+    return 0;
+  }
+  // US keyboard layout can input only one character per key.  So, we can
+  // assume that if the key value is 2 or more characters, it's a known
+  // key name of a non-printable key or not a usual key emulation.
+  if (aKeyValue.Length() > 1) {
+    return 0;
+  }
+
+  if (aLocation.isSome() &&
+      aLocation.value() ==
+        dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_NUMPAD) {
+    switch (aKeyValue[0]) {
+      case '+':
+        return dom::KeyboardEvent_Binding::DOM_VK_ADD;
+      case '-':
+        return dom::KeyboardEvent_Binding::DOM_VK_SUBTRACT;
+      case '*':
+        return dom::KeyboardEvent_Binding::DOM_VK_MULTIPLY;
+      case '/':
+        return dom::KeyboardEvent_Binding::DOM_VK_DIVIDE;
+      case '.':
+        return dom::KeyboardEvent_Binding::DOM_VK_DECIMAL;
+      case '0':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD0;
+      case '1':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD1;
+      case '2':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD2;
+      case '3':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD3;
+      case '4':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD4;
+      case '5':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD5;
+      case '6':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD6;
+      case '7':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD7;
+      case '8':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD8;
+      case '9':
+        return dom::KeyboardEvent_Binding::DOM_VK_NUMPAD9;
+      default:
+        return 0;
+    }
+  }
+
+  if (aLocation.isSome() &&
+      aLocation.value() !=
+        dom::KeyboardEvent_Binding::DOM_KEY_LOCATION_STANDARD) {
+    return 0;
+  }
+
+  // TODO: Support characters inputted with option key on macOS.
+  switch (aKeyValue[0]) {
+    case 'a':
+    case 'A':
+      return dom::KeyboardEvent_Binding::DOM_VK_A;
+    case 'b':
+    case 'B':
+      return dom::KeyboardEvent_Binding::DOM_VK_B;
+    case 'c':
+    case 'C':
+      return dom::KeyboardEvent_Binding::DOM_VK_C;
+    case 'd':
+    case 'D':
+      return dom::KeyboardEvent_Binding::DOM_VK_D;
+    case 'e':
+    case 'E':
+      return dom::KeyboardEvent_Binding::DOM_VK_E;
+    case 'f':
+    case 'F':
+      return dom::KeyboardEvent_Binding::DOM_VK_F;
+    case 'g':
+    case 'G':
+      return dom::KeyboardEvent_Binding::DOM_VK_G;
+    case 'h':
+    case 'H':
+      return dom::KeyboardEvent_Binding::DOM_VK_H;
+    case 'i':
+    case 'I':
+      return dom::KeyboardEvent_Binding::DOM_VK_I;
+    case 'j':
+    case 'J':
+      return dom::KeyboardEvent_Binding::DOM_VK_J;
+    case 'k':
+    case 'K':
+      return dom::KeyboardEvent_Binding::DOM_VK_K;
+    case 'l':
+    case 'L':
+      return dom::KeyboardEvent_Binding::DOM_VK_L;
+    case 'm':
+    case 'M':
+      return dom::KeyboardEvent_Binding::DOM_VK_M;
+    case 'n':
+    case 'N':
+      return dom::KeyboardEvent_Binding::DOM_VK_N;
+    case 'o':
+    case 'O':
+      return dom::KeyboardEvent_Binding::DOM_VK_O;
+    case 'p':
+    case 'P':
+      return dom::KeyboardEvent_Binding::DOM_VK_P;
+    case 'q':
+    case 'Q':
+      return dom::KeyboardEvent_Binding::DOM_VK_Q;
+    case 'r':
+    case 'R':
+      return dom::KeyboardEvent_Binding::DOM_VK_R;
+    case 's':
+    case 'S':
+      return dom::KeyboardEvent_Binding::DOM_VK_S;
+    case 't':
+    case 'T':
+      return dom::KeyboardEvent_Binding::DOM_VK_T;
+    case 'u':
+    case 'U':
+      return dom::KeyboardEvent_Binding::DOM_VK_U;
+    case 'v':
+    case 'V':
+      return dom::KeyboardEvent_Binding::DOM_VK_V;
+    case 'w':
+    case 'W':
+      return dom::KeyboardEvent_Binding::DOM_VK_W;
+    case 'x':
+    case 'X':
+      return dom::KeyboardEvent_Binding::DOM_VK_X;
+    case 'y':
+    case 'Y':
+      return dom::KeyboardEvent_Binding::DOM_VK_Y;
+    case 'z':
+    case 'Z':
+      return dom::KeyboardEvent_Binding::DOM_VK_Z;
+
+    case '`':
+    case '~':
+      return dom::KeyboardEvent_Binding::DOM_VK_BACK_QUOTE;
+    case '1':
+    case '!':
+      return dom::KeyboardEvent_Binding::DOM_VK_1;
+    case '2':
+    case '@':
+      return dom::KeyboardEvent_Binding::DOM_VK_2;
+    case '3':
+    case '#':
+      return dom::KeyboardEvent_Binding::DOM_VK_3;
+    case '4':
+    case '$':
+      return dom::KeyboardEvent_Binding::DOM_VK_4;
+    case '5':
+    case '%':
+      return dom::KeyboardEvent_Binding::DOM_VK_5;
+    case '6':
+    case '^':
+      return dom::KeyboardEvent_Binding::DOM_VK_6;
+    case '7':
+    case '&':
+      return dom::KeyboardEvent_Binding::DOM_VK_7;
+    case '8':
+    case '*':
+      return dom::KeyboardEvent_Binding::DOM_VK_8;
+    case '9':
+    case '(':
+      return dom::KeyboardEvent_Binding::DOM_VK_9;
+    case '0':
+    case ')':
+      return dom::KeyboardEvent_Binding::DOM_VK_0;
+    case '-':
+    case '_':
+      return dom::KeyboardEvent_Binding::DOM_VK_HYPHEN_MINUS;
+    case '=':
+    case '+':
+      return dom::KeyboardEvent_Binding::DOM_VK_EQUALS;
+
+    case '[':
+    case '{':
+      return dom::KeyboardEvent_Binding::DOM_VK_OPEN_BRACKET;
+    case ']':
+    case '}':
+      return dom::KeyboardEvent_Binding::DOM_VK_CLOSE_BRACKET;
+    case '\\':
+    case '|':
+      return dom::KeyboardEvent_Binding::DOM_VK_BACK_SLASH;
+
+    case ';':
+    case ':':
+      return dom::KeyboardEvent_Binding::DOM_VK_SEMICOLON;
+    case '\'':
+    case '"':
+      return dom::KeyboardEvent_Binding::DOM_VK_QUOTE;
+
+    case ',':
+    case '<':
+      return dom::KeyboardEvent_Binding::DOM_VK_COMMA;
+    case '.':
+    case '>':
+      return dom::KeyboardEvent_Binding::DOM_VK_PERIOD;
+    case '/':
+    case '?':
+      return dom::KeyboardEvent_Binding::DOM_VK_SLASH;
+
+    case ' ':
+      return dom::KeyboardEvent_Binding::DOM_VK_SPACE;
+
+    default:
+      return 0;
+  }
+}
+
 /******************************************************************************
  * TextInputProcessor::AutoPendingCompositionResetter
  ******************************************************************************/
 TextInputProcessor::AutoPendingCompositionResetter::
   AutoPendingCompositionResetter(TextInputProcessor* aTIP)
   : mTIP(aTIP)
 {
   MOZ_RELEASE_ASSERT(mTIP.get(), "mTIP must not be null");
--- a/dom/base/TextInputProcessor.h
+++ b/dom/base/TextInputProcessor.h
@@ -65,16 +65,35 @@ public:
    * @return                   Returns CODE_NAME_INDEX_UNKNOWN if there is
    *                           no proper key.
    */
   static CodeNameIndex
   GuessCodeNameIndexOfPrintableKeyInUSEnglishLayout(
     const nsAString& aKeyValue,
     const Maybe<uint32_t>& aLocation);
 
+  /**
+   * GuessKeyCodeOfPrintableKeyInUSEnglishLayout() returns a key code value
+   * of a printable key which is in usual keyboard of the platform and when
+   * active keyboard layout is US-English.
+   * Note that this does not aware of option key mapping on macOS.
+   *
+   * @param aKeyValue          The key value. Must be a character which can
+   *                           be inputted with US-English keyboard layout.
+   * @param aLocation          The location of the key.  This is important
+   *                           to distinguish whether the key is in Standard
+   *                           or Numpad. If this is not some, treated as
+   *                           Standard.
+   * @return                   Returns 0 if there is no proper key to input
+   *                           aKeyValue with US-English keyboard layout.
+   */
+  static uint32_t
+  GuessKeyCodeOfPrintableKeyInUSEnglishLayout(const nsAString& aKeyValue,
+                                              const Maybe<uint32_t>& aLocation);
+
 protected:
   virtual ~TextInputProcessor();
 
 private:
   bool IsComposing() const;
   nsresult BeginInputTransactionInternal(
              mozIDOMWindow* aWindow,
              nsITextInputProcessorCallback* aCallback,
--- a/dom/interfaces/base/nsITextInputProcessor.idl
+++ b/dom/interfaces/base/nsITextInputProcessor.idl
@@ -638,16 +638,36 @@ interface nsITextInputProcessor : nsISup
    *                           or Numpad. If this is undefined or null, will
    *                           be treated as Standard.
    * @return                   Returns empty string if there is no proper key.
    */
   [optional_argc]
     AString guessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
               in AString aKeyValue,
               [optional] in jsval aLocation);
+
+  /**
+   * Helper method to guess |.keyCode| value of a printable key which is in
+   * usual keyboard of the platform and when active keyboard layout is
+   * US-English.
+   * Note that this is not aware of option key mapping on macOS.
+   *
+   * @param aKeyValue          The key value.  Must be a character which can
+   *                           be inputted with US-English keyboard layout.
+   * @param aLocation          The location of the key.  This is important
+   *                           to distinguish whether the key is in Standard
+   *                           our Numpad.  If this is undefined or null,
+   *                           will be treated as Standard.
+   * @return                   Returns 0 if there is no proper key to input
+   *                           aKeyValue with US-English keyboard layout.
+   */
+  [optional_argc]
+    unsigned long guessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
+                    in AString aKeyValue,
+                    [optional] in jsval aLocation);
 };
 
 %{C++
 #define TEXT_INPUT_PROCESSOR_CID \
   { 0xcaaab47f, 0x1e31, 0x478e, \
     { 0x89, 0x19, 0x97, 0x09, 0x04, 0xe9, 0xcb, 0x72 } }
 #define TEXT_INPUT_PROCESSOR_CONTRACTID \
   "@mozilla.org/text-input-processor;1"
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -796,95 +796,16 @@ function synthesizeAndWaitNativeMouseMov
     });
   });
   eventRegisteredPromise.then(() => {
     synthesizeNativeMouseMove(aTarget, aOffsetX, aOffsetY, null, aWindow);
   });
   return eventReceivedPromise;
 }
 
-function _computeKeyCodeFromChar(aChar)
-{
-  if (aChar.length != 1) {
-    return 0;
-  }
-  var KeyEvent = _getKeyboardEvent();
-  if (aChar >= 'a' && aChar <= 'z') {
-    return KeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
-  }
-  if (aChar >= 'A' && aChar <= 'Z') {
-    return KeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
-  }
-  if (aChar >= '0' && aChar <= '9') {
-    return KeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
-  }
-  // returns US keyboard layout's keycode
-  switch (aChar) {
-    case '~':
-    case '`':
-      return KeyEvent.DOM_VK_BACK_QUOTE;
-    case '!':
-      return KeyEvent.DOM_VK_1;
-    case '@':
-      return KeyEvent.DOM_VK_2;
-    case '#':
-      return KeyEvent.DOM_VK_3;
-    case '$':
-      return KeyEvent.DOM_VK_4;
-    case '%':
-      return KeyEvent.DOM_VK_5;
-    case '^':
-      return KeyEvent.DOM_VK_6;
-    case '&':
-      return KeyEvent.DOM_VK_7;
-    case '*':
-      return KeyEvent.DOM_VK_8;
-    case '(':
-      return KeyEvent.DOM_VK_9;
-    case ')':
-      return KeyEvent.DOM_VK_0;
-    case '-':
-    case '_':
-      return KeyEvent.DOM_VK_SUBTRACT;
-    case '+':
-    case '=':
-      return KeyEvent.DOM_VK_EQUALS;
-    case '{':
-    case '[':
-      return KeyEvent.DOM_VK_OPEN_BRACKET;
-    case '}':
-    case ']':
-      return KeyEvent.DOM_VK_CLOSE_BRACKET;
-    case '|':
-    case '\\':
-      return KeyEvent.DOM_VK_BACK_SLASH;
-    case ':':
-    case ';':
-      return KeyEvent.DOM_VK_SEMICOLON;
-    case '\'':
-    case '"':
-      return KeyEvent.DOM_VK_QUOTE;
-    case '<':
-    case ',':
-      return KeyEvent.DOM_VK_COMMA;
-    case '>':
-    case '.':
-      return KeyEvent.DOM_VK_PERIOD;
-    case '?':
-    case '/':
-      return KeyEvent.DOM_VK_SLASH;
-    case '\n':
-      return KeyEvent.DOM_VK_RETURN;
-    case ' ':
-      return KeyEvent.DOM_VK_SPACE;
-    default:
-      return 0;
-  }
-}
-
 /**
  * Synthesize a key event. It is targeted at whatever would be targeted by an
  * actual keypress by the user, typically the focused element.
  *
  * aKey should be:
  *  - key value (recommended).  If you specify a non-printable key name,
  *    append "KEY_" prefix.  Otherwise, specifying a printable key, the
  *    key value should be specified.
@@ -1547,17 +1468,19 @@ function _createKeyboardEventDictionary(
     result.flags |= _EU_Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
     if (code === undefined) {
       code =
         aTIP.computeCodeValueOfNonPrintableKey(keyName, aKeyEvent.location);
     }
   } else if (aKey != "") {
     keyName = aKey;
     if (!keyCodeIsDefined) {
-      keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
+      keyCode =
+        aTIP.guessKeyCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
+               aKey, aKeyEvent.location);
     }
     if (!keyCode) {
       result.flags |= _EU_Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
     }
     result.flags |= _EU_Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
     if (code === undefined) {
       code = aTIP.guessCodeValueOfPrintableKeyInUSEnglishKeyboardLayout(
                keyName, aKeyEvent.location);