Bug 1119609 part.14 Reimplement/redesign EventUtils.synthesizeKey() with nsITextInputProcessor r=smaug

This commit is contained in:
Masayuki Nakano 2015-02-19 15:50:20 +09:00
parent 26f706d27e
commit 019965a1f6
14 changed files with 74 additions and 169 deletions

View File

@ -45,7 +45,7 @@ function escapeTest(contentWindow) {
}
contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled,
false);
EventUtils.synthesizeKey("VK_ESCAPE", { }, contentWindow);
EventUtils.synthesizeKey("KEY_Escape", { code: "Escape" }, contentWindow);
}
function toggleTabViewTest(contentWindow) {
@ -57,5 +57,6 @@ function toggleTabViewTest(contentWindow) {
}
contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false);
// Use keyboard shortcut to toggle back to browser view
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true });
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true,
code: "KeyE", keyCode: KeyboardEvent.DOM_VK_E }, contentWindow);
}

View File

@ -28,7 +28,7 @@ function onTabViewWindowLoaded() {
// verify that the keyboard combo works (this is the crux of bug 595518)
// Prepare the key combo
window.addEventListener("tabviewshown", onTabViewShown, false);
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true }, contentWindow);
EventUtils.synthesizeKey("e", { accelKey: true, shiftKey: true, code: "KeyE", keyCode: KeyboardEvent.DOM_VK_E }, contentWindow);
}
let onTabViewShown = function() {

View File

@ -72,7 +72,8 @@ if (observers.hasMoreElements()) {
ok(true, "formnovalidate should not apply on if not set on the submit " +
"control used for the submission");
var c = document.getElementById('c');
c.focus(); synthesizeKey("VK_RETURN", {type: "keypress"});
c.focus();
synthesizeKey("KEY_Enter", { code: "Enter" });
}, false);
document.getElementById('c').addEventListener("invalid", function(aEvent) {

View File

@ -189,15 +189,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=851780
if (isDesktop) { // up/down arrow keys not supported on android/b2g
number.value = "";
number.focus();
synthesizeKey("VK_UP", {});
synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
is(numberInput, 1, "input event should be dispatched for up/down arrow key keypress");
is(number.value, 1, "sanity check value of number control after keypress");
synthesizeKey("VK_DOWN", { type: "keydown" });
synthesizeKey("VK_DOWN", { type: "keypress" });
synthesizeKey("VK_DOWN", { type: "keypress" });
synthesizeKey("VK_DOWN", { type: "keypress" });
synthesizeKey("VK_DOWN", { type: "keyup" });
synthesizeKey("KEY_ArrowDown", { code: "ArrowDown", repeat: 3 });
is(numberInput, 4, "input event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated");
is(number.value, -2, "sanity check value of number control after multiple keydown events");

View File

@ -48,7 +48,8 @@ document.forms[1].addEventListener("submit", function(aEvent) {
aEvent.target.removeEventListener("submit", arguments.callee, false);
ok(true, "novalidate has been correctly used for second form");
var c = document.getElementById('c');
c.focus(); synthesizeKey("VK_RETURN", {type: "keypress"});
c.focus();
synthesizeKey("KEY_Enter", { code: "Enter" });
}, false);
document.forms[2].addEventListener("submit", function(aEvent) {

View File

@ -107,7 +107,8 @@ function runTest()
document.getElementById('a').click();
document.getElementById('b').click();
var c = document.getElementById('c');
c.focus(); synthesizeKey("VK_RETURN", {type: "keypress"});
c.focus();
synthesizeKey("KEY_Enter", { code: "Enter" });
document.getElementById('s2').click();
}

View File

@ -127,34 +127,6 @@ function runTests()
aElement.focus();
is(SpecialPowers.unwrap(fm.focusedElement), aElement, aDescription + "failed to move focus");
// Modifier keys:
// Basically, modifier keys shouldn't cause keypress event. However,
// even if it were dispatched by widget's bug, editor should consume
// it when editor is editable.
reset("");
synthesizeKey("VK_META", { type: "keypress" });
check(aDescription + "Meta", true, true, !aIsReadonly);
reset("");
synthesizeKey("VK_WIN", { type: "keypress" });
check(aDescription + "OS", true, true, !aIsReadonly);
reset("");
synthesizeKey("VK_SHIFT", { type: "keypress" });
check(aDescription + "Shift", true, true, !aIsReadonly);
reset("");
synthesizeKey("VK_CONTROL", { type: "keypress" });
check(aDescription + "Control", true, true, !aIsReadonly);
// Alt key press event installs menubar key event listener, so,
// we should pass Alt key testing on Windows and Linux.
if (!kIsWin && !kIsLinux) {
reset("");
synthesizeKey("VK_ALT", { type: "keypress" });
check(aDescription + "Alt", true, true, !aIsReadonly);
}
// Backspace key:
// If editor is readonly, it doesn't consume.
// If editor is editable, it consumes backspace and shift+backspace.

View File

@ -113,34 +113,6 @@ function runTests()
aElement.focus();
is(SpecialPowers.unwrap(fm.focusedElement), aElement, aDescription + "failed to move focus");
// Modifier keys:
// Basically, modifier keys shouldn't cause keypress event. However,
// even if it were dispatched by widget's bug, editor should consume
// it when editor is editable.
reset("");
synthesizeKey("VK_META", { type: "keypress" });
check(aDescription + "Meta", true, true, !aIsReadonly);
reset("");
synthesizeKey("VK_WIN", { type: "keypress" });
check(aDescription + "OS", true, true, !aIsReadonly);
reset("");
synthesizeKey("VK_SHIFT", { type: "keypress" });
check(aDescription + "Shift", true, true, !aIsReadonly);
reset("");
synthesizeKey("VK_CONTROL", { type: "keypress" });
check(aDescription + "Control", true, true, !aIsReadonly);
// Alt key press event installs menubar key event listener, so,
// we should pass Alt key testing on Windows and Linux.
if (!kIsWin && !kIsLinux) {
reset("");
synthesizeKey("VK_ALT", { type: "keypress" });
check(aDescription + "Alt", true, true, !aIsReadonly);
}
// Backspace key:
// If editor is readonly, it doesn't consume.
// If editor is editable, it consumes backspace and shift+backspace.

View File

@ -31,11 +31,10 @@ function runTest()
// key events should not be retargeted when the focus changes to an
// element in the same document.
synthesizeKey("a", { type: "keydown" });
synthesizeKey("a", { type: "keydown", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_B });
is(document.activeElement, $("i2"), "input 2 in focused");
synthesizeKey("a", { type: "keypress" });
synthesizeKey("a", { type: "keyup" });
synthesizeKey("a", { type: "keyup", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_B });
is(gKeyDown1, 1, "keydown on input 1");
is(gKeyPress1, 0, "keypress on input 1");
@ -53,9 +52,7 @@ function runTest()
var childWinObj = frames[0].wrappedJSObject;
synthesizeKey("b", { type: "keydown" });
synthesizeKey("b", { type: "keypress" });
synthesizeKey("b", { type: "keyup" });
synthesizeKey("b", { code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B });
is(gKeyDown3, 1, "keydown on input 3");
is(gKeyPress3, 1, "keypress on input 3");
is(gKeyUp3, 1, "keyup on input 3");
@ -74,9 +71,7 @@ function runTest()
// element in a chrome document from a content document.
i4.addEventListener("keydown", function () $("i3").focus(), false);
synthesizeKey("c", { type: "keydown" });
synthesizeKey("c", { type: "keypress" });
synthesizeKey("c", { type: "keyup" });
synthesizeKey("c", { code: "KeyC", keyCode: KeyboardEvent.DOM_VK_C });
is(gKeyDown3, 1, "keydown on input 3");
is(gKeyPress3, 1, "keypress on input 3");

View File

@ -53,7 +53,7 @@ function doe3() {
document.getElementById('a').focus();
document.body.style.display = 'none';
synthesizeKey('VK_TAB', {type: "keypress", shiftKey: true});
synthesizeKey('KEY_Tab', { code: "Tab", shiftKey: true });
is(0, 0, "this is a crash/assertion test, so we're ok if we survived this far");
setTimeout(function() {document.body.style.display = ''; SimpleTest.finish();}, 0);

View File

@ -509,101 +509,73 @@ function _computeKeyCodeFromChar(aChar)
}
}
/**
* isKeypressFiredKey() returns TRUE if the given key should cause keypress
* event when widget handles the native key event. Otherwise, FALSE.
*
* aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
* name begins with "VK_", or a character.
*/
function isKeypressFiredKey(aDOMKeyCode)
{
if (typeof(aDOMKeyCode) == "string") {
if (aDOMKeyCode.indexOf("VK_") == 0) {
aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
if (!aDOMKeyCode) {
throw "Unknown key: " + aDOMKeyCode;
}
} else {
// If the key generates a character, it must cause a keypress event.
return true;
}
}
switch (aDOMKeyCode) {
case KeyEvent.DOM_VK_SHIFT:
case KeyEvent.DOM_VK_CONTROL:
case KeyEvent.DOM_VK_ALT:
case KeyEvent.DOM_VK_CAPS_LOCK:
case KeyEvent.DOM_VK_NUM_LOCK:
case KeyEvent.DOM_VK_SCROLL_LOCK:
case KeyEvent.DOM_VK_META:
return false;
default:
return true;
}
}
/**
* 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 either a character or a keycode starting with VK_ such as
* VK_RETURN.
* 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.
* - keyCode name starting with "VK_" (e.g., VK_RETURN). This is available
* only for compatibility with legacy API. Don't use this with new tests.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location
*
* Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location. Otherwise,
* DOMWindowUtils will choose good location from the keycode.
*
* If the type is specified, a key event of that type is fired. Otherwise,
* a keydown, a keypress and then a keyup event are fired in sequence.
* - code: If you emulates a physical keyboard's key event, this should be
* specified.
* - repeat: If you emulates auto-repeat, you should set the count of repeat.
* This method will automatically synthesize keydown (and keypress).
* - location: If you want to specify this, you can specify this explicitly.
* However, if you don't specify this value, it will be computed
* from code value.
* - type: Basically, you shouldn't specify this. Then, this function will
* synthesize keydown (, keypress) and keyup.
* If keydown is specified, this only fires keydown (and keypress if
* it should be fired).
* If keyup is specified, this only fires keyup.
* - altKey, altGraphKey, ctrlKey, capsLockKey, fnKey, fnLockKey, numLockKey,
* metaKey, osKey, scrollLockKey, shiftKey, symbolKey, symbolLockKey:
* Basically, you shouldn't use these attributes. nsITextInputProcessor
* manages modifier key state when you synthesize modifier key events.
* However, if some of these attributes are true, this function activates
* the modifiers only during dispatching the key events.
* Note that if some of these values are false, they are ignored (i.e.,
* not inactivated with this function).
* - keyCode: Must be 0 - 255 (0xFF). If this is specified explicitly,
* .keyCode value is initialized with this value.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeKey(aKey, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (utils) {
var keyEventDict = _createKeyboardEventDictionary(aKey, aEvent);
var keyCode = keyEventDict.dictionary.keyCode;
var charCode =
(aKey.indexOf("VK_") == 0) ?
0 : ((keyEventDict.dictionary.key != "") ?
keyEventDict.dictionary.key.charCodeAt(0) : 0);
var TIP = _getTIP(aWindow);
if (!TIP) {
return;
}
var modifiers = _emulateToActivateModifiers(TIP, aEvent);
var keyEventDict = _createKeyboardEventDictionary(aKey, aEvent);
var keyEvent = new KeyboardEvent("", keyEventDict.dictionary);
var dispatchKeydown =
!("type" in aEvent) || aEvent.type === "keydown" || !aEvent.type;
var dispatchKeyup =
!("type" in aEvent) || aEvent.type === "keyup" || !aEvent.type;
var modifiers = _parseModifiers(aEvent);
var flags = 0;
switch (keyEventDict.dictionary.location) {
case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
flags |= utils.KEY_FLAG_LOCATION_STANDARD;
break;
case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
flags |= utils.KEY_FLAG_LOCATION_LEFT;
break;
case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
flags |= utils.KEY_FLAG_LOCATION_RIGHT;
break;
case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
break;
}
if (!("type" in aEvent) || !aEvent.type) {
// Send keydown + (optional) keypress + keyup events.
var keyDownDefaultHappened =
utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
if (isKeypressFiredKey(keyCode) && keyDownDefaultHappened) {
utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags);
try {
if (dispatchKeydown) {
TIP.keydown(keyEvent, keyEventDict.flags);
if ("repeat" in aEvent && aEvent.repeat > 1) {
keyEventDict.dictionary.repeat = true;
var repeatedKeyEvent = new KeyboardEvent("", keyEventDict.dictionary);
for (var i = 1; i < aEvent.repeat; i++) {
TIP.keydown(repeatedKeyEvent, keyEventDict.flags);
}
}
utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags);
} else if (aEvent.type == "keypress") {
// Send standalone keypress event.
utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags);
} else {
// Send other standalone event than keypress.
utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags);
}
if (dispatchKeyup) {
TIP.keyup(keyEvent, keyEventDict.flags);
}
} finally {
_emulateToInactivateModifiers(TIP, modifiers);
}
}
@ -1074,7 +1046,7 @@ function _createKeyboardEventDictionary(aKey, aKeyEvent)
key: keyName,
code: "code" in aKeyEvent ? aKeyEvent.code : "",
location: locationIsDefined ? aKeyEvent.location : 0,
repeat: "repeat" in aKeyEvent ? aKeyEvent.repeat : false,
repeat: "repeat" in aKeyEvent ? aKeyEvent.repeat === true : false,
keyCode: keyCode,
};
return result;

View File

@ -69,7 +69,7 @@ function test_richlistbox()
ok(richListBox.currentIndex > currentIndex, "Selection should move upwards");
richListBox.selectedItem = richListBox.lastChild;
richListBox.focus();
synthesizeKey("VK_DOWN", {shiftKey: true, type: "keypress"}, window);
synthesizeKey("KEY_ArrowDown", { shiftKey: true, code: "ArrowDown" }, window);
let items = [richListBox.selectedItems[0],
richListBox.selectedItems[1]];
is(items[0], richListBox.lastChild, "The last element should still be selected");
@ -115,7 +115,7 @@ function test_richlistbox()
ok(richListBox.currentIndex < currentIndex, "Selection should move upwards");
richListBox.selectedItem = richListBox.firstChild;
richListBox.focus();
synthesizeKey("VK_DOWN", {shiftKey: true, type: "keypress"}, window);
synthesizeKey("KEY_ArrowDown", { shiftKey: true, code: "ArrowDown" }, window);
items = [richListBox.selectedItems[0],
richListBox.selectedItems[1]];
is(items[0], richListBox.firstChild, "The last element should still be selected");

View File

@ -784,9 +784,6 @@ var popupTests = [
}, false);
newWindow.close();
thisWindow.focus();
// Note that our window dispatches a hacky key event if IMM is installed
// on the system during focus change only if Alt key is pressed.
synthesizeKey("`", { type: "keypress" }, thisWindow);
}, false);
}
}

View File

@ -139,10 +139,7 @@ function startTests()
clear();
synthesizeKey("a", { type: "keydown" });
checkKeyEvents(true, false, false, false, "a keydown");
clear();
synthesizeKey("a", { type: "keypress" });
checkKeyEvents(false, true, false, true, "a keypress");
checkKeyEvents(true, true, false, true, "a keydown and a keypress");
is(textarea.value, "a", "textarea value isn't 'a'");
clear();
synthesizeKey("a", { type: "keyup" });