|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 /** |
|
6 * EventUtils provides some utility methods for creating and sending DOM events. |
|
7 * Current methods: |
|
8 * sendMouseEvent |
|
9 * sendChar |
|
10 * sendString |
|
11 * sendKey |
|
12 * synthesizeMouse |
|
13 * synthesizeMouseAtCenter |
|
14 * synthesizeMouseScroll |
|
15 * synthesizeKey |
|
16 * synthesizeMouseExpectEvent |
|
17 * synthesizeKeyExpectEvent |
|
18 * |
|
19 * When adding methods to this file, please add a performance test for it. |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Send a mouse event to the node aTarget (aTarget can be an id, or an |
|
24 * actual node) . The "event" passed in to aEvent is just a JavaScript |
|
25 * object with the properties set that the real mouse event object should |
|
26 * have. This includes the type of the mouse event. |
|
27 * E.g. to send an click event to the node with id 'node' you might do this: |
|
28 * |
|
29 * sendMouseEvent({type:'click'}, 'node'); |
|
30 */ |
|
31 function getElement(id) { |
|
32 return ((typeof(id) == "string") ? |
|
33 document.getElementById(id) : id); |
|
34 }; |
|
35 |
|
36 this.$ = this.getElement; |
|
37 const KeyEvent = Components.interfaces.nsIDOMKeyEvent; |
|
38 |
|
39 function sendMouseEvent(aEvent, aTarget, aWindow) { |
|
40 if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) { |
|
41 throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'"); |
|
42 } |
|
43 |
|
44 if (!aWindow) { |
|
45 aWindow = window; |
|
46 } |
|
47 |
|
48 if (!(aTarget instanceof Element)) { |
|
49 aTarget = aWindow.document.getElementById(aTarget); |
|
50 } |
|
51 |
|
52 var event = aWindow.document.createEvent('MouseEvent'); |
|
53 |
|
54 var typeArg = aEvent.type; |
|
55 var canBubbleArg = true; |
|
56 var cancelableArg = true; |
|
57 var viewArg = aWindow; |
|
58 var detailArg = aEvent.detail || (aEvent.type == 'click' || |
|
59 aEvent.type == 'mousedown' || |
|
60 aEvent.type == 'mouseup' ? 1 : |
|
61 aEvent.type == 'dblclick'? 2 : 0); |
|
62 var screenXArg = aEvent.screenX || 0; |
|
63 var screenYArg = aEvent.screenY || 0; |
|
64 var clientXArg = aEvent.clientX || 0; |
|
65 var clientYArg = aEvent.clientY || 0; |
|
66 var ctrlKeyArg = aEvent.ctrlKey || false; |
|
67 var altKeyArg = aEvent.altKey || false; |
|
68 var shiftKeyArg = aEvent.shiftKey || false; |
|
69 var metaKeyArg = aEvent.metaKey || false; |
|
70 var buttonArg = aEvent.button || 0; |
|
71 var relatedTargetArg = aEvent.relatedTarget || null; |
|
72 |
|
73 event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, |
|
74 screenXArg, screenYArg, clientXArg, clientYArg, |
|
75 ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, |
|
76 buttonArg, relatedTargetArg); |
|
77 |
|
78 //removed: SpecialPowers.dispatchEvent(aWindow, aTarget, event); |
|
79 } |
|
80 |
|
81 /** |
|
82 * Send the char aChar to the focused element. This method handles casing of |
|
83 * chars (sends the right charcode, and sends a shift key for uppercase chars). |
|
84 * No other modifiers are handled at this point. |
|
85 * |
|
86 * For now this method only works for English letters (lower and upper case) |
|
87 * and the digits 0-9. |
|
88 */ |
|
89 function sendChar(aChar, aWindow) { |
|
90 // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9. |
|
91 var hasShift = (aChar == aChar.toUpperCase()); |
|
92 synthesizeKey(aChar, { shiftKey: hasShift }, aWindow); |
|
93 } |
|
94 |
|
95 /** |
|
96 * Send the string aStr to the focused element. |
|
97 * |
|
98 * For now this method only works for English letters (lower and upper case) |
|
99 * and the digits 0-9. |
|
100 */ |
|
101 function sendString(aStr, aWindow) { |
|
102 for (var i = 0; i < aStr.length; ++i) { |
|
103 sendChar(aStr.charAt(i), aWindow); |
|
104 } |
|
105 } |
|
106 |
|
107 /** |
|
108 * Send the non-character key aKey to the focused node. |
|
109 * The name of the key should be the part that comes after "DOM_VK_" in the |
|
110 * KeyEvent constant name for this key. |
|
111 * No modifiers are handled at this point. |
|
112 */ |
|
113 function sendKey(aKey, aWindow) { |
|
114 var keyName = "VK_" + aKey.toUpperCase(); |
|
115 synthesizeKey(keyName, { shiftKey: false }, aWindow); |
|
116 } |
|
117 |
|
118 /** |
|
119 * Parse the key modifier flags from aEvent. Used to share code between |
|
120 * synthesizeMouse and synthesizeKey. |
|
121 */ |
|
122 function _parseModifiers(aEvent) |
|
123 { |
|
124 const masks = Components.interfaces.nsIDOMNSEvent; |
|
125 var mval = 0; |
|
126 if (aEvent.shiftKey) |
|
127 mval |= masks.SHIFT_MASK; |
|
128 if (aEvent.ctrlKey) |
|
129 mval |= masks.CONTROL_MASK; |
|
130 if (aEvent.altKey) |
|
131 mval |= masks.ALT_MASK; |
|
132 if (aEvent.metaKey) |
|
133 mval |= masks.META_MASK; |
|
134 if (aEvent.accelKey) |
|
135 mval |= (navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK : |
|
136 masks.CONTROL_MASK; |
|
137 |
|
138 return mval; |
|
139 } |
|
140 |
|
141 /** |
|
142 * Synthesize a mouse event on a target. The actual client point is determined |
|
143 * by taking the aTarget's client box and offseting it by aOffsetX and |
|
144 * aOffsetY. This allows mouse clicks to be simulated by calling this method. |
|
145 * |
|
146 * aEvent is an object which may contain the properties: |
|
147 * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type |
|
148 * |
|
149 * If the type is specified, an mouse event of that type is fired. Otherwise, |
|
150 * a mousedown followed by a mouse up is performed. |
|
151 * |
|
152 * aWindow is optional, and defaults to the current window object. |
|
153 */ |
|
154 function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) |
|
155 { |
|
156 var rect = aTarget.getBoundingClientRect(); |
|
157 synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY, |
|
158 aEvent, aWindow); |
|
159 } |
|
160 |
|
161 /* |
|
162 * Synthesize a mouse event at a particular point in aWindow. |
|
163 * |
|
164 * aEvent is an object which may contain the properties: |
|
165 * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type |
|
166 * |
|
167 * If the type is specified, an mouse event of that type is fired. Otherwise, |
|
168 * a mousedown followed by a mouse up is performed. |
|
169 * |
|
170 * aWindow is optional, and defaults to the current window object. |
|
171 */ |
|
172 function synthesizeMouseAtPoint(left, top, aEvent, aWindow) |
|
173 { |
|
174 var utils = _getDOMWindowUtils(aWindow); |
|
175 |
|
176 if (utils) { |
|
177 var button = aEvent.button || 0; |
|
178 var clickCount = aEvent.clickCount || 1; |
|
179 var modifiers = _parseModifiers(aEvent); |
|
180 |
|
181 if (("type" in aEvent) && aEvent.type) { |
|
182 utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers); |
|
183 } |
|
184 else { |
|
185 utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers); |
|
186 utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers); |
|
187 } |
|
188 } |
|
189 } |
|
190 |
|
191 // Call synthesizeMouse with coordinates at the center of aTarget. |
|
192 function synthesizeMouseAtCenter(aTarget, aEvent, aWindow) |
|
193 { |
|
194 var rect = aTarget.getBoundingClientRect(); |
|
195 synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent, |
|
196 aWindow); |
|
197 } |
|
198 |
|
199 /** |
|
200 * Synthesize a mouse scroll event on a target. The actual client point is determined |
|
201 * by taking the aTarget's client box and offseting it by aOffsetX and |
|
202 * aOffsetY. |
|
203 * |
|
204 * aEvent is an object which may contain the properties: |
|
205 * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels |
|
206 * |
|
207 * If the type is specified, a mouse scroll event of that type is fired. Otherwise, |
|
208 * "DOMMouseScroll" is used. |
|
209 * |
|
210 * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified, |
|
211 * "vertical" is used. |
|
212 * |
|
213 * 'delta' is the amount to scroll by (can be positive or negative). It must |
|
214 * be specified. |
|
215 * |
|
216 * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags. |
|
217 * |
|
218 * 'isMomentum' specifies whether kIsMomentum should be set in the scrollFlags. |
|
219 * |
|
220 * aWindow is optional, and defaults to the current window object. |
|
221 */ |
|
222 function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) |
|
223 { |
|
224 var utils = _getDOMWindowUtils(aWindow); |
|
225 |
|
226 if (utils) { |
|
227 // See nsMouseScrollFlags in nsGUIEvent.h |
|
228 const kIsVertical = 0x02; |
|
229 const kIsHorizontal = 0x04; |
|
230 const kHasPixels = 0x08; |
|
231 const kIsMomentum = 0x40; |
|
232 |
|
233 var button = aEvent.button || 0; |
|
234 var modifiers = _parseModifiers(aEvent); |
|
235 |
|
236 var rect = aTarget.getBoundingClientRect(); |
|
237 |
|
238 var left = rect.left; |
|
239 var top = rect.top; |
|
240 |
|
241 var type = (("type" in aEvent) && aEvent.type) || "DOMMouseScroll"; |
|
242 var axis = aEvent.axis || "vertical"; |
|
243 var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical; |
|
244 if (aEvent.hasPixels) { |
|
245 scrollFlags |= kHasPixels; |
|
246 } |
|
247 if (aEvent.isMomentum) { |
|
248 scrollFlags |= kIsMomentum; |
|
249 } |
|
250 utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button, |
|
251 scrollFlags, aEvent.delta, modifiers); |
|
252 } |
|
253 } |
|
254 |
|
255 function _computeKeyCodeFromChar(aChar) |
|
256 { |
|
257 if (aChar.length != 1) { |
|
258 return 0; |
|
259 } |
|
260 const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent; |
|
261 if (aChar >= 'a' && aChar <= 'z') { |
|
262 return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0); |
|
263 } |
|
264 if (aChar >= 'A' && aChar <= 'Z') { |
|
265 return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0); |
|
266 } |
|
267 if (aChar >= '0' && aChar <= '9') { |
|
268 return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0); |
|
269 } |
|
270 // returns US keyboard layout's keycode |
|
271 switch (aChar) { |
|
272 case '~': |
|
273 case '`': |
|
274 return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; |
|
275 case '!': |
|
276 return nsIDOMKeyEvent.DOM_VK_1; |
|
277 case '@': |
|
278 return nsIDOMKeyEvent.DOM_VK_2; |
|
279 case '#': |
|
280 return nsIDOMKeyEvent.DOM_VK_3; |
|
281 case '$': |
|
282 return nsIDOMKeyEvent.DOM_VK_4; |
|
283 case '%': |
|
284 return nsIDOMKeyEvent.DOM_VK_5; |
|
285 case '^': |
|
286 return nsIDOMKeyEvent.DOM_VK_6; |
|
287 case '&': |
|
288 return nsIDOMKeyEvent.DOM_VK_7; |
|
289 case '*': |
|
290 return nsIDOMKeyEvent.DOM_VK_8; |
|
291 case '(': |
|
292 return nsIDOMKeyEvent.DOM_VK_9; |
|
293 case ')': |
|
294 return nsIDOMKeyEvent.DOM_VK_0; |
|
295 case '-': |
|
296 case '_': |
|
297 return nsIDOMKeyEvent.DOM_VK_SUBTRACT; |
|
298 case '+': |
|
299 case '=': |
|
300 return nsIDOMKeyEvent.DOM_VK_EQUALS; |
|
301 case '{': |
|
302 case '[': |
|
303 return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; |
|
304 case '}': |
|
305 case ']': |
|
306 return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; |
|
307 case '|': |
|
308 case '\\': |
|
309 return nsIDOMKeyEvent.DOM_VK_BACK_SLASH; |
|
310 case ':': |
|
311 case ';': |
|
312 return nsIDOMKeyEvent.DOM_VK_SEMICOLON; |
|
313 case '\'': |
|
314 case '"': |
|
315 return nsIDOMKeyEvent.DOM_VK_QUOTE; |
|
316 case '<': |
|
317 case ',': |
|
318 return nsIDOMKeyEvent.DOM_VK_COMMA; |
|
319 case '>': |
|
320 case '.': |
|
321 return nsIDOMKeyEvent.DOM_VK_PERIOD; |
|
322 case '?': |
|
323 case '/': |
|
324 return nsIDOMKeyEvent.DOM_VK_SLASH; |
|
325 case '\n': |
|
326 return nsIDOMKeyEvent.DOM_VK_RETURN; |
|
327 default: |
|
328 return 0; |
|
329 } |
|
330 } |
|
331 |
|
332 /** |
|
333 * isKeypressFiredKey() returns TRUE if the given key should cause keypress |
|
334 * event when widget handles the native key event. Otherwise, FALSE. |
|
335 * |
|
336 * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key |
|
337 * name begins with "VK_", or a character. |
|
338 */ |
|
339 function isKeypressFiredKey(aDOMKeyCode) |
|
340 { |
|
341 const KeyEvent = Components.interfaces.nsIDOMKeyEvent; |
|
342 if (typeof(aDOMKeyCode) == "string") { |
|
343 if (aDOMKeyCode.indexOf("VK_") == 0) { |
|
344 aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode]; |
|
345 if (!aDOMKeyCode) { |
|
346 throw "Unknown key: " + aDOMKeyCode; |
|
347 } |
|
348 } else { |
|
349 // If the key generates a character, it must cause a keypress event. |
|
350 return true; |
|
351 } |
|
352 } |
|
353 switch (aDOMKeyCode) { |
|
354 case KeyEvent.DOM_VK_SHIFT: |
|
355 case KeyEvent.DOM_VK_CONTROL: |
|
356 case KeyEvent.DOM_VK_ALT: |
|
357 case KeyEvent.DOM_VK_CAPS_LOCK: |
|
358 case KeyEvent.DOM_VK_NUM_LOCK: |
|
359 case KeyEvent.DOM_VK_SCROLL_LOCK: |
|
360 case KeyEvent.DOM_VK_META: |
|
361 return false; |
|
362 default: |
|
363 return true; |
|
364 } |
|
365 } |
|
366 |
|
367 /** |
|
368 * Synthesize a key event. It is targeted at whatever would be targeted by an |
|
369 * actual keypress by the user, typically the focused element. |
|
370 * |
|
371 * aKey should be either a character or a keycode starting with VK_ such as |
|
372 * VK_RETURN. |
|
373 * |
|
374 * aEvent is an object which may contain the properties: |
|
375 * shiftKey, ctrlKey, altKey, metaKey, accessKey, type |
|
376 * |
|
377 * If the type is specified, a key event of that type is fired. Otherwise, |
|
378 * a keydown, a keypress and then a keyup event are fired in sequence. |
|
379 * |
|
380 * aWindow is optional, and defaults to the current window object. |
|
381 */ |
|
382 function synthesizeKey(aKey, aEvent, aWindow) |
|
383 { |
|
384 var utils = _getDOMWindowUtils(aWindow); |
|
385 if (utils) { |
|
386 var keyCode = 0, charCode = 0; |
|
387 if (aKey.indexOf("VK_") == 0) { |
|
388 keyCode = KeyEvent["DOM_" + aKey]; |
|
389 if (!keyCode) { |
|
390 throw "Unknown key: " + aKey; |
|
391 } |
|
392 } else { |
|
393 charCode = aKey.charCodeAt(0); |
|
394 keyCode = _computeKeyCodeFromChar(aKey.charAt(0)); |
|
395 } |
|
396 |
|
397 var modifiers = _parseModifiers(aEvent); |
|
398 |
|
399 if (!("type" in aEvent) || !aEvent.type) { |
|
400 // Send keydown + (optional) keypress + keyup events. |
|
401 var keyDownDefaultHappened = |
|
402 utils.sendKeyEvent("keydown", keyCode, 0, modifiers); |
|
403 if (isKeypressFiredKey(keyCode)) { |
|
404 utils.sendKeyEvent("keypress", charCode ? 0 : keyCode, charCode, |
|
405 modifiers, !keyDownDefaultHappened); |
|
406 } |
|
407 utils.sendKeyEvent("keyup", keyCode, 0, modifiers); |
|
408 } else if (aEvent.type == "keypress") { |
|
409 // Send standalone keypress event. |
|
410 utils.sendKeyEvent(aEvent.type, charCode ? 0 : keyCode, |
|
411 charCode, modifiers); |
|
412 } else { |
|
413 // Send other standalone event than keypress. |
|
414 utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers); |
|
415 } |
|
416 } |
|
417 } |
|
418 |
|
419 var _gSeenEvent = false; |
|
420 |
|
421 /** |
|
422 * Indicate that an event with an original target of aExpectedTarget and |
|
423 * a type of aExpectedEvent is expected to be fired, or not expected to |
|
424 * be fired. |
|
425 */ |
|
426 function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName) |
|
427 { |
|
428 if (!aExpectedTarget || !aExpectedEvent) |
|
429 return null; |
|
430 |
|
431 _gSeenEvent = false; |
|
432 |
|
433 var type = (aExpectedEvent.charAt(0) == "!") ? |
|
434 aExpectedEvent.substring(1) : aExpectedEvent; |
|
435 var eventHandler = function(event) { |
|
436 var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget && |
|
437 event.type == type); |
|
438 is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : "")); |
|
439 _gSeenEvent = true; |
|
440 }; |
|
441 |
|
442 aExpectedTarget.addEventListener(type, eventHandler, false); |
|
443 return eventHandler; |
|
444 } |
|
445 |
|
446 /** |
|
447 * Check if the event was fired or not. The event handler aEventHandler |
|
448 * will be removed. |
|
449 */ |
|
450 function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName) |
|
451 { |
|
452 if (aEventHandler) { |
|
453 var expectEvent = (aExpectedEvent.charAt(0) != "!"); |
|
454 var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1); |
|
455 aExpectedTarget.removeEventListener(type, aEventHandler, false); |
|
456 var desc = type + " event"; |
|
457 if (!expectEvent) |
|
458 desc += " not"; |
|
459 is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired"); |
|
460 } |
|
461 |
|
462 _gSeenEvent = false; |
|
463 } |
|
464 |
|
465 /** |
|
466 * Similar to synthesizeMouse except that a test is performed to see if an |
|
467 * event is fired at the right target as a result. |
|
468 * |
|
469 * aExpectedTarget - the expected originalTarget of the event. |
|
470 * aExpectedEvent - the expected type of the event, such as 'select'. |
|
471 * aTestName - the test name when outputing results |
|
472 * |
|
473 * To test that an event is not fired, use an expected type preceded by an |
|
474 * exclamation mark, such as '!select'. This might be used to test that a |
|
475 * click on a disabled element doesn't fire certain events for instance. |
|
476 * |
|
477 * aWindow is optional, and defaults to the current window object. |
|
478 */ |
|
479 function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent, |
|
480 aExpectedTarget, aExpectedEvent, aTestName, |
|
481 aWindow) |
|
482 { |
|
483 var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); |
|
484 synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow); |
|
485 _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); |
|
486 } |
|
487 |
|
488 /** |
|
489 * Similar to synthesizeKey except that a test is performed to see if an |
|
490 * event is fired at the right target as a result. |
|
491 * |
|
492 * aExpectedTarget - the expected originalTarget of the event. |
|
493 * aExpectedEvent - the expected type of the event, such as 'select'. |
|
494 * aTestName - the test name when outputing results |
|
495 * |
|
496 * To test that an event is not fired, use an expected type preceded by an |
|
497 * exclamation mark, such as '!select'. |
|
498 * |
|
499 * aWindow is optional, and defaults to the current window object. |
|
500 */ |
|
501 function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent, |
|
502 aTestName, aWindow) |
|
503 { |
|
504 var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); |
|
505 synthesizeKey(key, aEvent, aWindow); |
|
506 _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); |
|
507 } |
|
508 |
|
509 function disableNonTestMouseEvents(aDisable) |
|
510 { |
|
511 var domutils = _getDOMWindowUtils(); |
|
512 domutils.disableNonTestMouseEvents(aDisable); |
|
513 } |
|
514 |
|
515 function _getDOMWindowUtils(aWindow) |
|
516 { |
|
517 if (!aWindow) { |
|
518 aWindow = window; |
|
519 } |
|
520 |
|
521 //TODO: this is assuming we are in chrome space |
|
522 return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor). |
|
523 getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
524 } |
|
525 |
|
526 // Must be synchronized with nsIDOMWindowUtils. |
|
527 const COMPOSITION_ATTR_RAWINPUT = 0x02; |
|
528 const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; |
|
529 const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; |
|
530 const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; |
|
531 |
|
532 /** |
|
533 * Synthesize a composition event. |
|
534 * |
|
535 * @param aEvent The composition event information. This must |
|
536 * have |type| member. The value must be |
|
537 * "compositionstart", "compositionend" or |
|
538 * "compositionupdate". |
|
539 * And also this may have |data| and |locale| which |
|
540 * would be used for the value of each property of |
|
541 * the composition event. Note that the data would |
|
542 * be ignored if the event type were |
|
543 * "compositionstart". |
|
544 * @param aWindow Optional (If null, current |window| will be used) |
|
545 */ |
|
546 function synthesizeComposition(aEvent, aWindow) |
|
547 { |
|
548 var utils = _getDOMWindowUtils(aWindow); |
|
549 if (!utils) { |
|
550 return; |
|
551 } |
|
552 |
|
553 utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "", |
|
554 aEvent.locale ? aEvent.locale : ""); |
|
555 } |
|
556 /** |
|
557 * Synthesize a text event. |
|
558 * |
|
559 * @param aEvent The text event's information, this has |composition| |
|
560 * and |caret| members. |composition| has |string| and |
|
561 * |clauses| members. |clauses| must be array object. Each |
|
562 * object has |length| and |attr|. And |caret| has |start| and |
|
563 * |length|. See the following tree image. |
|
564 * |
|
565 * aEvent |
|
566 * +-- composition |
|
567 * | +-- string |
|
568 * | +-- clauses[] |
|
569 * | +-- length |
|
570 * | +-- attr |
|
571 * +-- caret |
|
572 * +-- start |
|
573 * +-- length |
|
574 * |
|
575 * Set the composition string to |composition.string|. Set its |
|
576 * clauses information to the |clauses| array. |
|
577 * |
|
578 * When it's composing, set the each clauses' length to the |
|
579 * |composition.clauses[n].length|. The sum of the all length |
|
580 * values must be same as the length of |composition.string|. |
|
581 * Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the |
|
582 * |composition.clauses[n].attr|. |
|
583 * |
|
584 * When it's not composing, set 0 to the |
|
585 * |composition.clauses[0].length| and |
|
586 * |composition.clauses[0].attr|. |
|
587 * |
|
588 * Set caret position to the |caret.start|. It's offset from |
|
589 * the start of the composition string. Set caret length to |
|
590 * |caret.length|. If it's larger than 0, it should be wide |
|
591 * caret. However, current nsEditor doesn't support wide |
|
592 * caret, therefore, you should always set 0 now. |
|
593 * |
|
594 * @param aWindow Optional (If null, current |window| will be used) |
|
595 */ |
|
596 function synthesizeText(aEvent, aWindow) |
|
597 { |
|
598 var utils = _getDOMWindowUtils(aWindow); |
|
599 if (!utils) { |
|
600 return; |
|
601 } |
|
602 |
|
603 if (!aEvent.composition || !aEvent.composition.clauses || |
|
604 !aEvent.composition.clauses[0]) { |
|
605 return; |
|
606 } |
|
607 |
|
608 var firstClauseLength = aEvent.composition.clauses[0].length; |
|
609 var firstClauseAttr = aEvent.composition.clauses[0].attr; |
|
610 var secondClauseLength = 0; |
|
611 var secondClauseAttr = 0; |
|
612 var thirdClauseLength = 0; |
|
613 var thirdClauseAttr = 0; |
|
614 if (aEvent.composition.clauses[1]) { |
|
615 secondClauseLength = aEvent.composition.clauses[1].length; |
|
616 secondClauseAttr = aEvent.composition.clauses[1].attr; |
|
617 if (aEvent.composition.clauses[2]) { |
|
618 thirdClauseLength = aEvent.composition.clauses[2].length; |
|
619 thirdClauseAttr = aEvent.composition.clauses[2].attr; |
|
620 } |
|
621 } |
|
622 |
|
623 var caretStart = -1; |
|
624 var caretLength = 0; |
|
625 if (aEvent.caret) { |
|
626 caretStart = aEvent.caret.start; |
|
627 caretLength = aEvent.caret.length; |
|
628 } |
|
629 |
|
630 utils.sendTextEvent(aEvent.composition.string, |
|
631 firstClauseLength, firstClauseAttr, |
|
632 secondClauseLength, secondClauseAttr, |
|
633 thirdClauseLength, thirdClauseAttr, |
|
634 caretStart, caretLength); |
|
635 } |
|
636 |
|
637 /** |
|
638 * Synthesize a query selected text event. |
|
639 * |
|
640 * @param aWindow Optional (If null, current |window| will be used) |
|
641 * @return An nsIQueryContentEventResult object. If this failed, |
|
642 * the result might be null. |
|
643 */ |
|
644 function synthesizeQuerySelectedText(aWindow) |
|
645 { |
|
646 var utils = _getDOMWindowUtils(aWindow); |
|
647 if (!utils) { |
|
648 return null; |
|
649 } |
|
650 |
|
651 return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0); |
|
652 } |
|
653 |
|
654 /** |
|
655 * Synthesize a selection set event. |
|
656 * |
|
657 * @param aOffset The character offset. 0 means the first character in the |
|
658 * selection root. |
|
659 * @param aLength The length of the text. If the length is too long, |
|
660 * the extra length is ignored. |
|
661 * @param aReverse If true, the selection is from |aOffset + aLength| to |
|
662 * |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|. |
|
663 * @param aWindow Optional (If null, current |window| will be used) |
|
664 * @return True, if succeeded. Otherwise false. |
|
665 */ |
|
666 function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow) |
|
667 { |
|
668 var utils = _getDOMWindowUtils(aWindow); |
|
669 if (!utils) { |
|
670 return false; |
|
671 } |
|
672 return utils.sendSelectionSetEvent(aOffset, aLength, aReverse); |
|
673 } |