gecko/testing/marionette/marionette-sendkeys.js

383 lines
14 KiB
JavaScript

/*
* Copyright 2007-2009 WebDriver committers
* Copyright 2007-2009 Google Inc.
* Portions copyright 2012 Software Freedom Conservancy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var type = function(doc, element, text, releaseModifiers,
opt_keysState) {
var currentTextLength = element.value ? element.value.length : 0;
element.selectionStart = currentTextLength;
element.selectionEnd = currentTextLength;
// For consistency between native and synthesized events, convert common
// escape sequences to their Key enum aliases.
text = text.replace(new RegExp('\b', 'g'), '\uE003'). // DOM_VK_BACK_SPACE
replace(/\t/g, '\uE004'). // DOM_VK_TAB
replace(/(\r\n|\n|\r)/g, '\uE006'); // DOM_VK_RETURN
var controlKey = false;
var shiftKey = false;
var altKey = false;
var metaKey = false;
if (opt_keysState) {
controlKey = opt_keysState.control;
shiftKey = opt_keysState.shiftKey;
altKey = opt_keysState.alt;
metaKey = opt_keysState.meta;
}
shiftCount = 0;
var upper = text.toUpperCase();
for (var i = 0; i < text.length; i++) {
var c = text.charAt(i);
// NULL key: reset modifier key states, and continue
if (c == '\uE000') {
if (controlKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey = false, shiftKey, altKey, metaKey, false);
}
if (shiftKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, shiftKey = false, altKey, metaKey, false);
}
if (altKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, shiftKey, altKey = false, metaKey, false);
}
if (metaKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, shiftKey, altKey, metaKey = false, false);
}
continue;
}
// otherwise decode keyCode, charCode, modifiers ...
var modifierEvent = "";
var charCode = 0;
var keyCode = 0;
if (c == '\uE001') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CANCEL;
} else if (c == '\uE002') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HELP;
} else if (c == '\uE003') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SPACE;
} else if (c == '\uE004') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_TAB;
} else if (c == '\uE005') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLEAR;
} else if (c == '\uE006') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RETURN;
} else if (c == '\uE007') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ENTER;
} else if (c == '\uE008') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
shiftKey = !shiftKey;
modifierEvent = shiftKey ? "keydown" : "keyup";
} else if (c == '\uE009') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
controlKey = !controlKey;
modifierEvent = controlKey ? "keydown" : "keyup";
} else if (c == '\uE00A') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
altKey = !altKey;
modifierEvent = altKey ? "keydown" : "keyup";
} else if (c == '\uE03D') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
metaKey = !metaKey;
modifierEvent = metaKey ? "keydown" : "keyup";
} else if (c == '\uE00B') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAUSE;
} else if (c == '\uE00C') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ESCAPE;
} else if (c == '\uE00D') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SPACE;
keyCode = charCode = ' '.charCodeAt(0); // printable
} else if (c == '\uE00E') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_UP;
} else if (c == '\uE00F') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN;
} else if (c == '\uE010') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_END;
} else if (c == '\uE011') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HOME;
} else if (c == '\uE012') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_LEFT;
} else if (c == '\uE013') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_UP;
} else if (c == '\uE014') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RIGHT;
} else if (c == '\uE015') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DOWN;
} else if (c == '\uE016') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_INSERT;
} else if (c == '\uE017') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DELETE;
} else if (c == '\uE018') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEMICOLON;
charCode = ';'.charCodeAt(0);
} else if (c == '\uE019') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_EQUALS;
charCode = '='.charCodeAt(0);
} else if (c == '\uE01A') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD0;
charCode = '0'.charCodeAt(0);
} else if (c == '\uE01B') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD1;
charCode = '1'.charCodeAt(0);
} else if (c == '\uE01C') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD2;
charCode = '2'.charCodeAt(0);
} else if (c == '\uE01D') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD3;
charCode = '3'.charCodeAt(0);
} else if (c == '\uE01E') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD4;
charCode = '4'.charCodeAt(0);
} else if (c == '\uE01F') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD5;
charCode = '5'.charCodeAt(0);
} else if (c == '\uE020') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD6;
charCode = '6'.charCodeAt(0);
} else if (c == '\uE021') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD7;
charCode = '7'.charCodeAt(0);
} else if (c == '\uE022') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD8;
charCode = '8'.charCodeAt(0);
} else if (c == '\uE023') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD9;
charCode = '9'.charCodeAt(0);
} else if (c == '\uE024') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_MULTIPLY;
charCode = '*'.charCodeAt(0);
} else if (c == '\uE025') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ADD;
charCode = '+'.charCodeAt(0);
} else if (c == '\uE026') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEPARATOR;
charCode = ','.charCodeAt(0);
} else if (c == '\uE027') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SUBTRACT;
charCode = '-'.charCodeAt(0);
} else if (c == '\uE028') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DECIMAL;
charCode = '.'.charCodeAt(0);
} else if (c == '\uE029') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DIVIDE;
charCode = '/'.charCodeAt(0);
} else if (c == '\uE031') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F1;
} else if (c == '\uE032') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F2;
} else if (c == '\uE033') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F3;
} else if (c == '\uE034') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F4;
} else if (c == '\uE035') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F5;
} else if (c == '\uE036') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F6;
} else if (c == '\uE037') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F7;
} else if (c == '\uE038') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F8;
} else if (c == '\uE039') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F9;
} else if (c == '\uE03A') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F10;
} else if (c == '\uE03B') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F11;
} else if (c == '\uE03C') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F12;
} else if (c == ',' || c == '<') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_COMMA;
charCode = c.charCodeAt(0);
} else if (c == '.' || c == '>') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PERIOD;
charCode = c.charCodeAt(0);
} else if (c == '/' || c == '?') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SLASH;
charCode = text.charCodeAt(i);
} else if (c == '`' || c == '~') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
charCode = c.charCodeAt(0);
} else if (c == '{' || c == '[') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
charCode = c.charCodeAt(0);
} else if (c == '\\' || c == '|') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
charCode = c.charCodeAt(0);
} else if (c == '}' || c == ']') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
charCode = c.charCodeAt(0);
} else if (c == '\'' || c == '"') {
keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_QUOTE;
charCode = c.charCodeAt(0);
} else {
keyCode = upper.charCodeAt(i);
charCode = text.charCodeAt(i);
}
// generate modifier key event if needed, and continue
if (modifierEvent) {
keyEvent(doc, element, modifierEvent, keyCode, 0,
controlKey, shiftKey, altKey, metaKey, false);
continue;
}
// otherwise, shift down if needed
var needsShift = false;
if (charCode) {
needsShift = /[A-Z\!\$\^\*\(\)\+\{\}\:\?\|~@#%&_"<>]/.test(c);
}
if (needsShift && !shiftKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
keyEvent(doc, element, "keydown", kCode, 0,
controlKey, true, altKey, metaKey, false);
shiftCount += 1;
}
// generate key[down/press/up] for key
var pressCode = keyCode;
if (charCode >= 32 && charCode < 127) {
pressCode = 0;
if (!needsShift && shiftKey && charCode > 32) {
// If typing a lowercase character key and the shiftKey is down, the
// charCode should be mapped to the shifted key value. This assumes
// a default 104 international keyboard layout.
if (charCode >= 97 && charCode <= 122) {
charCode = charCode + 65 - 97; // [a-z] -> [A-Z]
} else {
var mapFrom = '`1234567890-=[]\\;\',./';
var mapTo = '~!@#$%^&*()_+{}|:"<>?';
var value = String.fromCharCode(charCode).
replace(/([\[\\\.])/g, '\\$1');
var index = mapFrom.search(value);
if (index >= 0) {
charCode = mapTo.charCodeAt(index);
}
}
}
}
var accepted =
keyEvent(doc, element, "keydown", keyCode, 0,
controlKey, needsShift || shiftKey, altKey, metaKey, false);
keyEvent(doc, element, "keypress", pressCode, charCode,
controlKey, needsShift || shiftKey, altKey, metaKey, !accepted);
keyEvent(doc, element, "keyup", keyCode, 0,
controlKey, needsShift || shiftKey, altKey, metaKey, false);
// shift up if needed
if (needsShift && !shiftKey) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, false, altKey, metaKey, false);
}
}
// exit cleanup: keyup active modifier keys
if (controlKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey = false, shiftKey, altKey, metaKey, false);
}
if (shiftKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, shiftKey = false, altKey, metaKey, false);
}
if (altKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, shiftKey, altKey = false, metaKey, false);
}
if (metaKey && releaseModifiers) {
var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
keyEvent(doc, element, "keyup", kCode, 0,
controlKey, shiftKey, altKey, metaKey = false, false);
}
return {
shiftKey: shiftKey,
alt: altKey,
meta: metaKey,
control: controlKey
};
};
var keyEvent = function(doc, element, type, keyCode, charCode,
controlState, shiftState, altState, metaState,
shouldPreventDefault) {
var preventDefault = shouldPreventDefault == undefined ? false
: shouldPreventDefault;
var keyboardEvent = doc.createEvent("KeyEvents");
var currentView = doc.defaultView;
keyboardEvent.initKeyEvent(
type, // in DOMString typeArg,
true, // in boolean canBubbleArg
true, // in boolean cancelableArg
currentView, // in nsIDOMAbstractView viewArg
controlState, // in boolean ctrlKeyArg
altState, // in boolean altKeyArg
shiftState, // in boolean shiftKeyArg
metaState, // in boolean metaKeyArg
keyCode, // in unsigned long keyCodeArg
charCode); // in unsigned long charCodeArg
if (preventDefault) {
keyboardEvent.preventDefault();
}
var win = doc.defaultView;
var domUtil = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils);
return domUtil.dispatchDOMEventViaPresShell(element, keyboardEvent, true);
};