|
1 /* |
|
2 * Copyright 2007-2009 WebDriver committers |
|
3 * Copyright 2007-2009 Google Inc. |
|
4 * Portions copyright 2012 Software Freedom Conservancy |
|
5 * |
|
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 |
|
9 * |
|
10 * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 * |
|
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. |
|
17 */ |
|
18 |
|
19 |
|
20 var type = function(doc, element, text, releaseModifiers, |
|
21 opt_keysState) { |
|
22 |
|
23 var currentTextLength = element.value ? element.value.length : 0; |
|
24 element.selectionStart = currentTextLength; |
|
25 element.selectionEnd = currentTextLength; |
|
26 |
|
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 |
|
32 |
|
33 var controlKey = false; |
|
34 var shiftKey = false; |
|
35 var altKey = false; |
|
36 var metaKey = false; |
|
37 if (opt_keysState) { |
|
38 controlKey = opt_keysState.control; |
|
39 shiftKey = opt_keysState.shiftKey; |
|
40 altKey = opt_keysState.alt; |
|
41 metaKey = opt_keysState.meta; |
|
42 } |
|
43 |
|
44 shiftCount = 0; |
|
45 |
|
46 var upper = text.toUpperCase(); |
|
47 |
|
48 for (var i = 0; i < text.length; i++) { |
|
49 var c = text.charAt(i); |
|
50 |
|
51 // NULL key: reset modifier key states, and continue |
|
52 |
|
53 if (c == '\uE000') { |
|
54 if (controlKey) { |
|
55 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL; |
|
56 keyEvent(doc, element, "keyup", kCode, 0, |
|
57 controlKey = false, shiftKey, altKey, metaKey, false); |
|
58 } |
|
59 |
|
60 if (shiftKey) { |
|
61 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT; |
|
62 keyEvent(doc, element, "keyup", kCode, 0, |
|
63 controlKey, shiftKey = false, altKey, metaKey, false); |
|
64 } |
|
65 |
|
66 if (altKey) { |
|
67 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT; |
|
68 keyEvent(doc, element, "keyup", kCode, 0, |
|
69 controlKey, shiftKey, altKey = false, metaKey, false); |
|
70 } |
|
71 |
|
72 if (metaKey) { |
|
73 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META; |
|
74 keyEvent(doc, element, "keyup", kCode, 0, |
|
75 controlKey, shiftKey, altKey, metaKey = false, false); |
|
76 } |
|
77 |
|
78 continue; |
|
79 } |
|
80 |
|
81 // otherwise decode keyCode, charCode, modifiers ... |
|
82 |
|
83 var modifierEvent = ""; |
|
84 var charCode = 0; |
|
85 var keyCode = 0; |
|
86 |
|
87 if (c == '\uE001') { |
|
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' || c == '\uE007') { |
|
98 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RETURN; |
|
99 } else if (c == '\uE008') { |
|
100 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT; |
|
101 shiftKey = !shiftKey; |
|
102 modifierEvent = shiftKey ? "keydown" : "keyup"; |
|
103 } else if (c == '\uE009') { |
|
104 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL; |
|
105 controlKey = !controlKey; |
|
106 modifierEvent = controlKey ? "keydown" : "keyup"; |
|
107 } else if (c == '\uE00A') { |
|
108 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT; |
|
109 altKey = !altKey; |
|
110 modifierEvent = altKey ? "keydown" : "keyup"; |
|
111 } else if (c == '\uE03D') { |
|
112 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META; |
|
113 metaKey = !metaKey; |
|
114 modifierEvent = metaKey ? "keydown" : "keyup"; |
|
115 } else if (c == '\uE00B') { |
|
116 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAUSE; |
|
117 } else if (c == '\uE00C') { |
|
118 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ESCAPE; |
|
119 } else if (c == '\uE00D') { |
|
120 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SPACE; |
|
121 keyCode = charCode = ' '.charCodeAt(0); // printable |
|
122 } else if (c == '\uE00E') { |
|
123 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_UP; |
|
124 } else if (c == '\uE00F') { |
|
125 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN; |
|
126 } else if (c == '\uE010') { |
|
127 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_END; |
|
128 } else if (c == '\uE011') { |
|
129 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_HOME; |
|
130 } else if (c == '\uE012') { |
|
131 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_LEFT; |
|
132 } else if (c == '\uE013') { |
|
133 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_UP; |
|
134 } else if (c == '\uE014') { |
|
135 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_RIGHT; |
|
136 } else if (c == '\uE015') { |
|
137 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DOWN; |
|
138 } else if (c == '\uE016') { |
|
139 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_INSERT; |
|
140 } else if (c == '\uE017') { |
|
141 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DELETE; |
|
142 } else if (c == '\uE018') { |
|
143 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEMICOLON; |
|
144 charCode = ';'.charCodeAt(0); |
|
145 } else if (c == '\uE019') { |
|
146 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_EQUALS; |
|
147 charCode = '='.charCodeAt(0); |
|
148 } else if (c == '\uE01A') { |
|
149 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD0; |
|
150 charCode = '0'.charCodeAt(0); |
|
151 } else if (c == '\uE01B') { |
|
152 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD1; |
|
153 charCode = '1'.charCodeAt(0); |
|
154 } else if (c == '\uE01C') { |
|
155 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD2; |
|
156 charCode = '2'.charCodeAt(0); |
|
157 } else if (c == '\uE01D') { |
|
158 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD3; |
|
159 charCode = '3'.charCodeAt(0); |
|
160 } else if (c == '\uE01E') { |
|
161 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD4; |
|
162 charCode = '4'.charCodeAt(0); |
|
163 } else if (c == '\uE01F') { |
|
164 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD5; |
|
165 charCode = '5'.charCodeAt(0); |
|
166 } else if (c == '\uE020') { |
|
167 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD6; |
|
168 charCode = '6'.charCodeAt(0); |
|
169 } else if (c == '\uE021') { |
|
170 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD7; |
|
171 charCode = '7'.charCodeAt(0); |
|
172 } else if (c == '\uE022') { |
|
173 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD8; |
|
174 charCode = '8'.charCodeAt(0); |
|
175 } else if (c == '\uE023') { |
|
176 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_NUMPAD9; |
|
177 charCode = '9'.charCodeAt(0); |
|
178 } else if (c == '\uE024') { |
|
179 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_MULTIPLY; |
|
180 charCode = '*'.charCodeAt(0); |
|
181 } else if (c == '\uE025') { |
|
182 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ADD; |
|
183 charCode = '+'.charCodeAt(0); |
|
184 } else if (c == '\uE026') { |
|
185 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SEPARATOR; |
|
186 charCode = ','.charCodeAt(0); |
|
187 } else if (c == '\uE027') { |
|
188 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SUBTRACT; |
|
189 charCode = '-'.charCodeAt(0); |
|
190 } else if (c == '\uE028') { |
|
191 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DECIMAL; |
|
192 charCode = '.'.charCodeAt(0); |
|
193 } else if (c == '\uE029') { |
|
194 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_DIVIDE; |
|
195 charCode = '/'.charCodeAt(0); |
|
196 } else if (c == '\uE031') { |
|
197 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F1; |
|
198 } else if (c == '\uE032') { |
|
199 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F2; |
|
200 } else if (c == '\uE033') { |
|
201 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F3; |
|
202 } else if (c == '\uE034') { |
|
203 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F4; |
|
204 } else if (c == '\uE035') { |
|
205 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F5; |
|
206 } else if (c == '\uE036') { |
|
207 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F6; |
|
208 } else if (c == '\uE037') { |
|
209 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F7; |
|
210 } else if (c == '\uE038') { |
|
211 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F8; |
|
212 } else if (c == '\uE039') { |
|
213 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F9; |
|
214 } else if (c == '\uE03A') { |
|
215 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F10; |
|
216 } else if (c == '\uE03B') { |
|
217 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F11; |
|
218 } else if (c == '\uE03C') { |
|
219 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_F12; |
|
220 } else if (c == ',' || c == '<') { |
|
221 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_COMMA; |
|
222 charCode = c.charCodeAt(0); |
|
223 } else if (c == '.' || c == '>') { |
|
224 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_PERIOD; |
|
225 charCode = c.charCodeAt(0); |
|
226 } else if (c == '/' || c == '?') { |
|
227 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SLASH; |
|
228 charCode = text.charCodeAt(i); |
|
229 } else if (c == '`' || c == '~') { |
|
230 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; |
|
231 charCode = c.charCodeAt(0); |
|
232 } else if (c == '{' || c == '[') { |
|
233 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; |
|
234 charCode = c.charCodeAt(0); |
|
235 } else if (c == '\\' || c == '|') { |
|
236 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_BACK_SLASH; |
|
237 charCode = c.charCodeAt(0); |
|
238 } else if (c == '}' || c == ']') { |
|
239 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; |
|
240 charCode = c.charCodeAt(0); |
|
241 } else if (c == '\'' || c == '"') { |
|
242 keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_QUOTE; |
|
243 charCode = c.charCodeAt(0); |
|
244 } else { |
|
245 keyCode = upper.charCodeAt(i); |
|
246 charCode = text.charCodeAt(i); |
|
247 } |
|
248 |
|
249 // generate modifier key event if needed, and continue |
|
250 |
|
251 if (modifierEvent) { |
|
252 keyEvent(doc, element, modifierEvent, keyCode, 0, |
|
253 controlKey, shiftKey, altKey, metaKey, false); |
|
254 continue; |
|
255 } |
|
256 |
|
257 // otherwise, shift down if needed |
|
258 |
|
259 var needsShift = false; |
|
260 if (charCode) { |
|
261 needsShift = /[A-Z\!\$\^\*\(\)\+\{\}\:\?\|~@#%&_"<>]/.test(c); |
|
262 } |
|
263 |
|
264 if (needsShift && !shiftKey) { |
|
265 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT; |
|
266 keyEvent(doc, element, "keydown", kCode, 0, |
|
267 controlKey, true, altKey, metaKey, false); |
|
268 shiftCount += 1; |
|
269 } |
|
270 |
|
271 // generate key[down/press/up] for key |
|
272 |
|
273 var pressCode = keyCode; |
|
274 if (charCode >= 32 && charCode < 127) { |
|
275 pressCode = 0; |
|
276 if (!needsShift && shiftKey && charCode > 32) { |
|
277 // If typing a lowercase character key and the shiftKey is down, the |
|
278 // charCode should be mapped to the shifted key value. This assumes |
|
279 // a default 104 international keyboard layout. |
|
280 if (charCode >= 97 && charCode <= 122) { |
|
281 charCode = charCode + 65 - 97; // [a-z] -> [A-Z] |
|
282 } else { |
|
283 var mapFrom = '`1234567890-=[]\\;\',./'; |
|
284 var mapTo = '~!@#$%^&*()_+{}|:"<>?'; |
|
285 |
|
286 var value = String.fromCharCode(charCode). |
|
287 replace(/([\[\\\.])/g, '\\$1'); |
|
288 var index = mapFrom.search(value); |
|
289 if (index >= 0) { |
|
290 charCode = mapTo.charCodeAt(index); |
|
291 } |
|
292 } |
|
293 } |
|
294 } |
|
295 |
|
296 var accepted = |
|
297 keyEvent(doc, element, "keydown", keyCode, 0, |
|
298 controlKey, needsShift || shiftKey, altKey, metaKey, false); |
|
299 |
|
300 keyEvent(doc, element, "keypress", pressCode, charCode, |
|
301 controlKey, needsShift || shiftKey, altKey, metaKey, !accepted); |
|
302 |
|
303 keyEvent(doc, element, "keyup", keyCode, 0, |
|
304 controlKey, needsShift || shiftKey, altKey, metaKey, false); |
|
305 |
|
306 // shift up if needed |
|
307 |
|
308 if (needsShift && !shiftKey) { |
|
309 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT; |
|
310 keyEvent(doc, element, "keyup", kCode, 0, |
|
311 controlKey, false, altKey, metaKey, false); |
|
312 } |
|
313 } |
|
314 |
|
315 // exit cleanup: keyup active modifier keys |
|
316 |
|
317 if (controlKey && releaseModifiers) { |
|
318 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_CONTROL; |
|
319 keyEvent(doc, element, "keyup", kCode, 0, |
|
320 controlKey = false, shiftKey, altKey, metaKey, false); |
|
321 } |
|
322 |
|
323 if (shiftKey && releaseModifiers) { |
|
324 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_SHIFT; |
|
325 keyEvent(doc, element, "keyup", kCode, 0, |
|
326 controlKey, shiftKey = false, altKey, metaKey, false); |
|
327 } |
|
328 |
|
329 if (altKey && releaseModifiers) { |
|
330 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_ALT; |
|
331 keyEvent(doc, element, "keyup", kCode, 0, |
|
332 controlKey, shiftKey, altKey = false, metaKey, false); |
|
333 } |
|
334 |
|
335 if (metaKey && releaseModifiers) { |
|
336 var kCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_META; |
|
337 keyEvent(doc, element, "keyup", kCode, 0, |
|
338 controlKey, shiftKey, altKey, metaKey = false, false); |
|
339 } |
|
340 |
|
341 return { |
|
342 shiftKey: shiftKey, |
|
343 alt: altKey, |
|
344 meta: metaKey, |
|
345 control: controlKey |
|
346 }; |
|
347 }; |
|
348 |
|
349 |
|
350 var keyEvent = function(doc, element, type, keyCode, charCode, |
|
351 controlState, shiftState, altState, metaState, |
|
352 shouldPreventDefault) { |
|
353 var preventDefault = shouldPreventDefault == undefined ? false |
|
354 : shouldPreventDefault; |
|
355 |
|
356 var keyboardEvent = doc.createEvent("KeyEvents"); |
|
357 var currentView = doc.defaultView; |
|
358 |
|
359 keyboardEvent.initKeyEvent( |
|
360 type, // in DOMString typeArg, |
|
361 true, // in boolean canBubbleArg |
|
362 true, // in boolean cancelableArg |
|
363 currentView, // in nsIDOMAbstractView viewArg |
|
364 controlState, // in boolean ctrlKeyArg |
|
365 altState, // in boolean altKeyArg |
|
366 shiftState, // in boolean shiftKeyArg |
|
367 metaState, // in boolean metaKeyArg |
|
368 keyCode, // in unsigned long keyCodeArg |
|
369 charCode); // in unsigned long charCodeArg |
|
370 |
|
371 if (preventDefault) { |
|
372 keyboardEvent.preventDefault(); |
|
373 } |
|
374 |
|
375 var win = doc.defaultView; |
|
376 var domUtil = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor) |
|
377 .getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
378 return domUtil.dispatchDOMEventViaPresShell(element, keyboardEvent, true); |
|
379 }; |
|
380 |