2 * Copyright 2007-2009 WebDriver committers
3 * Copyright 2007-2009 Google Inc.
4 * Portions copyright 2012 Software Freedom Conservancy
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
20 var type = function(doc, element, text, releaseModifiers,
23 var currentTextLength = element.value ? element.value.length : 0;
24 element.selectionStart = currentTextLength;
25 element.selectionEnd = currentTextLength;
27 // For consistency between native and synthesized events, convert common
28 // escape sequences to their Key enum aliases.
29 text = text.replace(new RegExp('\b', 'g'), '\uE003'). // DOM_VK_BACK_SPACE
30 replace(/\t/g, '\uE004'). // DOM_VK_TAB
31 replace(/(\r\n|\n|\r)/g, '\uE006'); // DOM_VK_RETURN
33 var controlKey = false;
38 controlKey = opt_keysState.control;
39 shiftKey = opt_keysState.shiftKey;
40 altKey = opt_keysState.alt;
41 metaKey = opt_keysState.meta;
46 var upper = text.toUpperCase();
48 for (var i = 0; i < text.length; i++) {
49 var c = text.charAt(i);
51 // NULL key: reset modifier key states, and continue
55 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
56 keyEvent(doc, element, "keyup", kCode, 0,
57 controlKey = false, shiftKey, altKey, metaKey, false);
61 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
62 keyEvent(doc, element, "keyup", kCode, 0,
63 controlKey, shiftKey = false, altKey, metaKey, false);
67 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
68 keyEvent(doc, element, "keyup", kCode, 0,
69 controlKey, shiftKey, altKey = false, metaKey, false);
73 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
74 keyEvent(doc, element, "keyup", kCode, 0,
75 controlKey, shiftKey, altKey, metaKey = false, false);
81 // otherwise decode keyCode, charCode, modifiers ...
83 var modifierEvent = "";
88 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CANCEL;
89 } else if (c == '\uE002') {
90 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HELP;
91 } else if (c == '\uE003') {
92 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SPACE;
93 } else if (c == '\uE004') {
94 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_TAB;
95 } else if (c == '\uE005') {
96 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLEAR;
97 } else if (c == '\uE006') {
98 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RETURN;
99 } else if (c == '\uE007') {
100 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ENTER;
101 } else if (c == '\uE008') {
102 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
103 shiftKey = !shiftKey;
104 modifierEvent = shiftKey ? "keydown" : "keyup";
105 } else if (c == '\uE009') {
106 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
107 controlKey = !controlKey;
108 modifierEvent = controlKey ? "keydown" : "keyup";
109 } else if (c == '\uE00A') {
110 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
112 modifierEvent = altKey ? "keydown" : "keyup";
113 } else if (c == '\uE03D') {
114 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
116 modifierEvent = metaKey ? "keydown" : "keyup";
117 } else if (c == '\uE00B') {
118 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAUSE;
119 } else if (c == '\uE00C') {
120 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ESCAPE;
121 } else if (c == '\uE00D') {
122 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SPACE;
123 keyCode = charCode = ' '.charCodeAt(0); // printable
124 } else if (c == '\uE00E') {
125 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_UP;
126 } else if (c == '\uE00F') {
127 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN;
128 } else if (c == '\uE010') {
129 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_END;
130 } else if (c == '\uE011') {
131 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HOME;
132 } else if (c == '\uE012') {
133 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_LEFT;
134 } else if (c == '\uE013') {
135 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_UP;
136 } else if (c == '\uE014') {
137 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RIGHT;
138 } else if (c == '\uE015') {
139 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DOWN;
140 } else if (c == '\uE016') {
141 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_INSERT;
142 } else if (c == '\uE017') {
143 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DELETE;
144 } else if (c == '\uE018') {
145 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEMICOLON;
146 charCode = ';'.charCodeAt(0);
147 } else if (c == '\uE019') {
148 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_EQUALS;
149 charCode = '='.charCodeAt(0);
150 } else if (c == '\uE01A') {
151 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD0;
152 charCode = '0'.charCodeAt(0);
153 } else if (c == '\uE01B') {
154 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD1;
155 charCode = '1'.charCodeAt(0);
156 } else if (c == '\uE01C') {
157 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD2;
158 charCode = '2'.charCodeAt(0);
159 } else if (c == '\uE01D') {
160 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD3;
161 charCode = '3'.charCodeAt(0);
162 } else if (c == '\uE01E') {
163 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD4;
164 charCode = '4'.charCodeAt(0);
165 } else if (c == '\uE01F') {
166 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD5;
167 charCode = '5'.charCodeAt(0);
168 } else if (c == '\uE020') {
169 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD6;
170 charCode = '6'.charCodeAt(0);
171 } else if (c == '\uE021') {
172 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD7;
173 charCode = '7'.charCodeAt(0);
174 } else if (c == '\uE022') {
175 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD8;
176 charCode = '8'.charCodeAt(0);
177 } else if (c == '\uE023') {
178 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD9;
179 charCode = '9'.charCodeAt(0);
180 } else if (c == '\uE024') {
181 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_MULTIPLY;
182 charCode = '*'.charCodeAt(0);
183 } else if (c == '\uE025') {
184 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ADD;
185 charCode = '+'.charCodeAt(0);
186 } else if (c == '\uE026') {
187 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEPARATOR;
188 charCode = ','.charCodeAt(0);
189 } else if (c == '\uE027') {
190 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SUBTRACT;
191 charCode = '-'.charCodeAt(0);
192 } else if (c == '\uE028') {
193 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DECIMAL;
194 charCode = '.'.charCodeAt(0);
195 } else if (c == '\uE029') {
196 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DIVIDE;
197 charCode = '/'.charCodeAt(0);
198 } else if (c == '\uE031') {
199 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F1;
200 } else if (c == '\uE032') {
201 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F2;
202 } else if (c == '\uE033') {
203 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F3;
204 } else if (c == '\uE034') {
205 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F4;
206 } else if (c == '\uE035') {
207 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F5;
208 } else if (c == '\uE036') {
209 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F6;
210 } else if (c == '\uE037') {
211 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F7;
212 } else if (c == '\uE038') {
213 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F8;
214 } else if (c == '\uE039') {
215 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F9;
216 } else if (c == '\uE03A') {
217 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F10;
218 } else if (c == '\uE03B') {
219 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F11;
220 } else if (c == '\uE03C') {
221 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F12;
222 } else if (c == ',' || c == '<') {
223 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_COMMA;
224 charCode = c.charCodeAt(0);
225 } else if (c == '.' || c == '>') {
226 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PERIOD;
227 charCode = c.charCodeAt(0);
228 } else if (c == '/' || c == '?') {
229 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SLASH;
230 charCode = text.charCodeAt(i);
231 } else if (c == '`' || c == '~') {
232 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
233 charCode = c.charCodeAt(0);
234 } else if (c == '{' || c == '[') {
235 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
236 charCode = c.charCodeAt(0);
237 } else if (c == '\\' || c == '|') {
238 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
239 charCode = c.charCodeAt(0);
240 } else if (c == '}' || c == ']') {
241 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
242 charCode = c.charCodeAt(0);
243 } else if (c == '\'' || c == '"') {
244 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_QUOTE;
245 charCode = c.charCodeAt(0);
247 keyCode = upper.charCodeAt(i);
248 charCode = text.charCodeAt(i);
251 // generate modifier key event if needed, and continue
254 keyEvent(doc, element, modifierEvent, keyCode, 0,
255 controlKey, shiftKey, altKey, metaKey, false);
259 // otherwise, shift down if needed
261 var needsShift = false;
263 needsShift = /[A-Z\!\$\^\*\(\)\+\{\}\:\?\|~@#%&_"<>]/.test(c);
266 if (needsShift && !shiftKey) {
267 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
268 keyEvent(doc, element, "keydown", kCode, 0,
269 controlKey, true, altKey, metaKey, false);
273 // generate key[down/press/up] for key
275 var pressCode = keyCode;
276 if (charCode >= 32 && charCode < 127) {
278 if (!needsShift && shiftKey && charCode > 32) {
279 // If typing a lowercase character key and the shiftKey is down, the
280 // charCode should be mapped to the shifted key value. This assumes
281 // a default 104 international keyboard layout.
282 if (charCode >= 97 && charCode <= 122) {
283 charCode = charCode + 65 - 97; // [a-z] -> [A-Z]
285 var mapFrom = '`1234567890-=[]\\;\',./';
286 var mapTo = '~!@#$%^&*()_+{}|:"<>?';
288 var value = String.fromCharCode(charCode).
289 replace(/([\[\\\.])/g, '\\$1');
290 var index = mapFrom.search(value);
292 charCode = mapTo.charCodeAt(index);
299 keyEvent(doc, element, "keydown", keyCode, 0,
300 controlKey, needsShift || shiftKey, altKey, metaKey, false);
302 keyEvent(doc, element, "keypress", pressCode, charCode,
303 controlKey, needsShift || shiftKey, altKey, metaKey, !accepted);
305 keyEvent(doc, element, "keyup", keyCode, 0,
306 controlKey, needsShift || shiftKey, altKey, metaKey, false);
308 // shift up if needed
310 if (needsShift && !shiftKey) {
311 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
312 keyEvent(doc, element, "keyup", kCode, 0,
313 controlKey, false, altKey, metaKey, false);
317 // exit cleanup: keyup active modifier keys
319 if (controlKey && releaseModifiers) {
320 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL;
321 keyEvent(doc, element, "keyup", kCode, 0,
322 controlKey = false, shiftKey, altKey, metaKey, false);
325 if (shiftKey && releaseModifiers) {
326 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT;
327 keyEvent(doc, element, "keyup", kCode, 0,
328 controlKey, shiftKey = false, altKey, metaKey, false);
331 if (altKey && releaseModifiers) {
332 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT;
333 keyEvent(doc, element, "keyup", kCode, 0,
334 controlKey, shiftKey, altKey = false, metaKey, false);
337 if (metaKey && releaseModifiers) {
338 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META;
339 keyEvent(doc, element, "keyup", kCode, 0,
340 controlKey, shiftKey, altKey, metaKey = false, false);
352 var keyEvent = function(doc, element, type, keyCode, charCode,
353 controlState, shiftState, altState, metaState,
354 shouldPreventDefault) {
355 var preventDefault = shouldPreventDefault == undefined ? false
356 : shouldPreventDefault;
358 var keyboardEvent = doc.createEvent("KeyEvents");
359 var currentView = doc.defaultView;
361 keyboardEvent.initKeyEvent(
362 type, // in DOMString typeArg,
363 true, // in boolean canBubbleArg
364 true, // in boolean cancelableArg
365 currentView, // in nsIDOMAbstractView viewArg
366 controlState, // in boolean ctrlKeyArg
367 altState, // in boolean altKeyArg
368 shiftState, // in boolean shiftKeyArg
369 metaState, // in boolean metaKeyArg
370 keyCode, // in unsigned long keyCodeArg
371 charCode); // in unsigned long charCodeArg
373 if (preventDefault) {
374 keyboardEvent.preventDefault();
377 var win = doc.defaultView;
378 var domUtil = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
379 .getInterface(Components.interfaces.nsIDOMWindowUtils);
380 return domUtil.dispatchDOMEventViaPresShell(element, keyboardEvent, true);