Bug 1332279 - Include keyCode in KeyboardEvents synthesized for key actions. r=ato, a=test-only
authorMaja Frydrychowicz <mjzffr@gmail.com>
Sat, 01 Apr 2017 02:37:30 -0400
changeset 379455 71b9bf4fdb00ad93d363774dc7f086fd24dde3e9
parent 379454 474495111e5487124a72b6b98ea681c6a2ec41dc
child 379456 7641dfbfb01bb1baef35dceb1cf76340bd78a5ba
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato, test-only
bugs1332279
milestone53.0
Bug 1332279 - Include keyCode in KeyboardEvents synthesized for key actions. r=ato, a=test-only 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.