Bug 1332279 - Include keyCode in KeyboardEvents synthesized for key actions; r=ato
authorMaja Frydrychowicz <mjzffr@gmail.com>
Sat, 01 Apr 2017 02:37:30 -0400
changeset 350886 14abe9aca211c250c557b0bccd7a3ec01ec10d44
parent 350885 902c526280718bae423df6fd5749c64f8ad41926
child 350887 e5a01985b6ce5f59f737fcea36272ad6b3862bb6
push id39972
push usermjzffr@gmail.com
push dateMon, 03 Apr 2017 03:34:06 +0000
treeherderautoland@4a7345bdd3d4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato
bugs1332279
milestone55.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 1332279 - Include keyCode in KeyboardEvents synthesized for key actions; r=ato The key dispatch functions now pass the raw key to event.js, which determines the keyCode for the event. Note the change in Normalized Key Value for Enter versus Return. The browser throws an exception when the event key attribute is set to "Return" and KEY_NON_PRINTABLE_KEY is set, which implies that the key value isn't valid. Changing it to Enter fixes the issue. MozReview-Commit-ID: 831f4EcqI1P
testing/marionette/action.js
testing/marionette/event.js
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -54,17 +54,17 @@ const MODIFIER_NAME_LOOKUP = {
 /** Map from raw key (codepoint) to normalized key value */
 const NORMALIZED_KEY_LOOKUP = {
   "\uE000": "Unidentified",
   "\uE001": "Cancel",
   "\uE002": "Help",
   "\uE003": "Backspace",
   "\uE004": "Tab",
   "\uE005": "Clear",
-  "\uE006": "Return",
+  "\uE006": "Enter",
   "\uE007": "Enter",
   "\uE008": "Shift",
   "\uE009": "Control",
   "\uE00A": "Alt",
   "\uE00B": "Pause",
   "\uE00C": "Escape",
   "\uE00D": " ",
   "\uE00E": "PageUp",
@@ -874,18 +874,17 @@ action.Key = class {
     this.code =  KEY_CODE_LOOKUP[rawKey];
     this.location = KEY_LOCATION_LOOKUP[rawKey] || 0;
     this.altKey = false;
     this.shiftKey = false;
     this.ctrlKey = false;
     this.metaKey = false;
     this.repeat = false;
     this.isComposing = false;
-    // Prevent keyCode from being guessed in event.js; we don't want to use it anyway.
-    this.keyCode = 0;
+    // keyCode will be computed by event.sendKeyDown
   }
 
   update(inputState) {
     this.altKey = inputState.alt;
     this.shiftKey = inputState.shift;
     this.ctrlKey = inputState.ctrl;
     this.metaKey = inputState.meta;
   }
@@ -1077,17 +1076,17 @@ function dispatchKeyDown(a, inputState, 
     keyEvent.repeat = inputState.isPressed(keyEvent.key);
     inputState.press(keyEvent.key);
     if (keyEvent.key in MODIFIER_NAME_LOOKUP) {
       inputState.setModState(keyEvent.key, true);
     }
     // Append a copy of |a| with keyUp subtype
     action.inputsToCancel.push(Object.assign({}, a, {subtype: action.KeyUp}));
     keyEvent.update(inputState);
-    event.sendKeyDown(keyEvent.key, keyEvent, win);
+    event.sendKeyDown(a.value, keyEvent, win);
 
     resolve();
   });
 }
 
 /**
  * Dispatch a keyUp action equivalent to releasing a key on a keyboard.
  *
@@ -1108,17 +1107,17 @@ function dispatchKeyUp(a, inputState, wi
       resolve();
       return;
     }
     if (keyEvent.key in MODIFIER_NAME_LOOKUP) {
       inputState.setModState(keyEvent.key, false);
     }
     inputState.release(keyEvent.key);
     keyEvent.update(inputState);
-    event.sendKeyUp(keyEvent.key, keyEvent, win);
+    event.sendKeyUp(a.value, keyEvent, win);
 
     resolve();
   });
 }
 
 /**
  * Dispatch a pointerDown action equivalent to pressing a pointer-device
  * button.
--- a/testing/marionette/event.js
+++ b/testing/marionette/event.js
@@ -352,16 +352,20 @@ event.synthesizeMouseScroll = function (
       modifiers);
 };
 
 function computeKeyCodeFromChar_(char) {
   if (char.length != 1) {
     return 0;
   }
 
+  if (char in VIRTUAL_KEYCODE_LOOKUP) {
+    return Ci.nsIDOMKeyEvent["DOM_" + VIRTUAL_KEYCODE_LOOKUP[char]];
+  }
+
   if (char >= "a" && char <= "z") {
     return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "a".charCodeAt(0);
   }
   if (char >= "A" && char <= "Z") {
     return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "A".charCodeAt(0);
   }
   if (char >= "0" && char <= "9") {
     return Ci.nsIDOMKeyEvent.DOM_VK_0 + char.charCodeAt(0) - "0".charCodeAt(0);
@@ -577,50 +581,52 @@ function getKeyboardEvent_(win = window)
   if (typeof content != "undefined" && ("KeyboardEvent" in content)) {
     return content.KeyboardEvent;
   }
   return win.KeyboardEvent;
 }
 
 function createKeyboardEventDictionary_(key, keyEvent, win = window) {
   var result = { dictionary: null, flags: 0 };
-  var keyCodeIsDefined = "keyCode" in keyEvent;
+  var keyCodeIsDefined = "keyCode" in keyEvent && keyEvent.keyCode != undefined;
   var keyCode =
     (keyCodeIsDefined && keyEvent.keyCode >= 0 && keyEvent.keyCode <= 255) ?
       keyEvent.keyCode : 0;
   var keyName = "Unidentified";
   if (key.indexOf("KEY_") == 0) {
     keyName = key.substr("KEY_".length);
     result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
   } else if (key.indexOf("VK_") == 0) {
     keyCode = Ci.nsIDOMKeyEvent["DOM_" + key];
     if (!keyCode) {
       throw "Unknown key: " + key;
     }
     keyName = guessKeyNameFromKeyCode_(keyCode, win);
-    result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
+    if (!isPrintable(keyCode, win)) {
+     result.flags |= Ci.nsITextInputProcessor.KEY_NON_PRINTABLE_KEY;
+    }
   } else if (key != "") {
     keyName = key;
     if (!keyCodeIsDefined) {
       keyCode = computeKeyCodeFromChar_(key.charAt(0));
     }
     if (!keyCode) {
       result.flags |= Ci.nsITextInputProcessor.KEY_KEEP_KEYCODE_ZERO;
     }
-    // keyName was already determined in keyEvent so no fall-back needed
-    if (!("key" in keyEvent && keyName == keyEvent.key)) {
+    // only force printable if "raw character" and event key match, like "a"
+    if (!("key" in keyEvent && key != keyEvent.key)) {
       result.flags |= Ci.nsITextInputProcessor.KEY_FORCE_PRINTABLE_KEY;
     }
   }
   var locationIsDefined = "location" in keyEvent;
   if (locationIsDefined && keyEvent.location === 0) {
     result.flags |= Ci.nsITextInputProcessor.KEY_KEEP_KEY_LOCATION_STANDARD;
   }
   result.dictionary = {
-    key: keyName,
+    key: "key" in keyEvent ? keyEvent.key : keyName,
     code: "code" in keyEvent ? keyEvent.code : "",
     location: locationIsDefined ? keyEvent.location : 0,
     repeat: "repeat" in keyEvent ? keyEvent.repeat === true : false,
     keyCode: keyCode,
   };
   return result;
 }
 
@@ -1215,16 +1221,92 @@ const VIRTUAL_KEYCODE_LOOKUP = {
 
 function getKeyCode(c) {
   if (c in VIRTUAL_KEYCODE_LOOKUP) {
     return VIRTUAL_KEYCODE_LOOKUP[c];
   }
   return c;
 }
 
+function isPrintable(c, win = window) {
+  let KeyboardEvent = getKeyboardEvent_(win);
+  let NON_PRINT_KEYS = [
+    KeyboardEvent.DOM_VK_CANCEL,
+    KeyboardEvent.DOM_VK_HELP,
+    KeyboardEvent.DOM_VK_BACK_SPACE,
+    KeyboardEvent.DOM_VK_TAB,
+    KeyboardEvent.DOM_VK_CLEAR,
+    KeyboardEvent.DOM_VK_SHIFT,
+    KeyboardEvent.DOM_VK_CONTROL,
+    KeyboardEvent.DOM_VK_ALT,
+    KeyboardEvent.DOM_VK_PAUSE,
+    KeyboardEvent.DOM_VK_EISU,
+    KeyboardEvent.DOM_VK_ESCAPE,
+    KeyboardEvent.DOM_VK_CONVERT,
+    KeyboardEvent.DOM_VK_NONCONVERT,
+    KeyboardEvent.DOM_VK_ACCEPT,
+    KeyboardEvent.DOM_VK_MODECHANGE,
+    KeyboardEvent.DOM_VK_PAGE_UP,
+    KeyboardEvent.DOM_VK_PAGE_DOWN,
+    KeyboardEvent.DOM_VK_END,
+    KeyboardEvent.DOM_VK_HOME,
+    KeyboardEvent.DOM_VK_LEFT,
+    KeyboardEvent.DOM_VK_UP,
+    KeyboardEvent.DOM_VK_RIGHT,
+    KeyboardEvent.DOM_VK_DOWN,
+    KeyboardEvent.DOM_VK_SELECT,
+    KeyboardEvent.DOM_VK_PRINT,
+    KeyboardEvent.DOM_VK_EXECUTE,
+    KeyboardEvent.DOM_VK_PRINTSCREEN,
+    KeyboardEvent.DOM_VK_INSERT,
+    KeyboardEvent.DOM_VK_DELETE,
+    KeyboardEvent.DOM_VK_WIN,
+    KeyboardEvent.DOM_VK_CONTEXT_MENU,
+    KeyboardEvent.DOM_VK_SLEEP,
+    KeyboardEvent.DOM_VK_F1,
+    KeyboardEvent.DOM_VK_F2,
+    KeyboardEvent.DOM_VK_F3,
+    KeyboardEvent.DOM_VK_F4,
+    KeyboardEvent.DOM_VK_F5,
+    KeyboardEvent.DOM_VK_F6,
+    KeyboardEvent.DOM_VK_F7,
+    KeyboardEvent.DOM_VK_F8,
+    KeyboardEvent.DOM_VK_F9,
+    KeyboardEvent.DOM_VK_F10,
+    KeyboardEvent.DOM_VK_F11,
+    KeyboardEvent.DOM_VK_F12,
+    KeyboardEvent.DOM_VK_F13,
+    KeyboardEvent.DOM_VK_F14,
+    KeyboardEvent.DOM_VK_F15,
+    KeyboardEvent.DOM_VK_F16,
+    KeyboardEvent.DOM_VK_F17,
+    KeyboardEvent.DOM_VK_F18,
+    KeyboardEvent.DOM_VK_F19,
+    KeyboardEvent.DOM_VK_F20,
+    KeyboardEvent.DOM_VK_F21,
+    KeyboardEvent.DOM_VK_F22,
+    KeyboardEvent.DOM_VK_F23,
+    KeyboardEvent.DOM_VK_F24,
+    KeyboardEvent.DOM_VK_NUM_LOCK,
+    KeyboardEvent.DOM_VK_SCROLL_LOCK,
+    KeyboardEvent.DOM_VK_VOLUME_MUTE,
+    KeyboardEvent.DOM_VK_VOLUME_DOWN,
+    KeyboardEvent.DOM_VK_VOLUME_UP,
+    KeyboardEvent.DOM_VK_META,
+    KeyboardEvent.DOM_VK_ALTGR,
+    KeyboardEvent.DOM_VK_ATTN,
+    KeyboardEvent.DOM_VK_CRSEL,
+    KeyboardEvent.DOM_VK_EXSEL,
+    KeyboardEvent.DOM_VK_EREOF,
+    KeyboardEvent.DOM_VK_PLAY,
+    KeyboardEvent.DOM_VK_RETURN,
+  ];
+  return !(NON_PRINT_KEYS.includes(c));
+}
+
 event.sendKeyDown = function (keyToSend, modifiers, document) {
   modifiers.type = "keydown";
   event.sendSingleKey(keyToSend, modifiers, document);
   // TODO This doesn't do anything since |synthesizeKeyEvent| ignores explicit
   // keypress request, and instead figures out itself when to send keypress
   if (["VK_SHIFT", "VK_CONTROL", "VK_ALT", "VK_META"].indexOf(getKeyCode(keyToSend)) < 0) {
     modifiers.type = "keypress";
     event.sendSingleKey(keyToSend, modifiers, document);
@@ -1246,27 +1328,27 @@ event.sendKeyUp = function (keyToSend, m
  * @param {?} modifiers
  *     Object with properties used in KeyboardEvent (shiftkey, repeat, ...)
  *     as well as, the event |type| such as keydown. All properties are optional.
  * @param {Window=} window
  *     Window object.  If |window| is undefined, the event is synthesized in
  *     current window.
  */
 event.sendSingleKey = function (keyToSend, modifiers, window = undefined) {
-  let keyCode = getKeyCode(keyToSend);
-  if (keyCode in KEYCODES_LOOKUP) {
+  let keyName = getKeyCode(keyToSend);
+  if (keyName in KEYCODES_LOOKUP) {
     // We assume that if |keyToSend| is a raw code point (like "\uE009") then
     // |modifiers| does not already have correct value for corresponding
     // |modName| attribute (like ctrlKey), so that value needs to be flipped
-    let modName = KEYCODES_LOOKUP[keyCode];
+    let modName = KEYCODES_LOOKUP[keyName];
     modifiers[modName] = !modifiers[modName];
-  } else if (modifiers.shiftKey && keyCode != "Shift") {
-    keyCode = keyCode.toUpperCase();
+  } else if (modifiers.shiftKey && keyName != "Shift") {
+    keyName = keyName.toUpperCase();
   }
-  event.synthesizeKey(keyCode, modifiers, window);
+  event.synthesizeKey(keyName, modifiers, window);
 };
 
 /**
  * Focus element and, if a textual input field and no previous selection
  * state exists, move the caret to the end of the input field.
  *
  * @param {Element} element
  *     Element to focus.