testing/mochitest/tests/SimpleTest/EventUtils.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /**
michael@0 2 * EventUtils provides some utility methods for creating and sending DOM events.
michael@0 3 * Current methods:
michael@0 4 * sendMouseEvent
michael@0 5 * sendChar
michael@0 6 * sendString
michael@0 7 * sendKey
michael@0 8 * synthesizeMouse
michael@0 9 * synthesizeMouseAtCenter
michael@0 10 * synthesizePointer
michael@0 11 * synthesizeWheel
michael@0 12 * synthesizeKey
michael@0 13 * synthesizeNativeKey
michael@0 14 * synthesizeMouseExpectEvent
michael@0 15 * synthesizeKeyExpectEvent
michael@0 16 *
michael@0 17 * When adding methods to this file, please add a performance test for it.
michael@0 18 */
michael@0 19
michael@0 20 // This file is used both in privileged and unprivileged contexts, so we have to
michael@0 21 // be careful about our access to Components.interfaces. We also want to avoid
michael@0 22 // naming collisions with anything that might be defined in the scope that imports
michael@0 23 // this script.
michael@0 24 window.__defineGetter__('_EU_Ci', function() {
michael@0 25 // Even if the real |Components| doesn't exist, we might shim in a simple JS
michael@0 26 // placebo for compat. An easy way to differentiate this from the real thing
michael@0 27 // is whether the property is read-only or not.
michael@0 28 var c = Object.getOwnPropertyDescriptor(window, 'Components');
michael@0 29 return c && c.value && !c.writable ? Components.interfaces : SpecialPowers.Ci;
michael@0 30 });
michael@0 31
michael@0 32 /**
michael@0 33 * Send a mouse event to the node aTarget (aTarget can be an id, or an
michael@0 34 * actual node) . The "event" passed in to aEvent is just a JavaScript
michael@0 35 * object with the properties set that the real mouse event object should
michael@0 36 * have. This includes the type of the mouse event.
michael@0 37 * E.g. to send an click event to the node with id 'node' you might do this:
michael@0 38 *
michael@0 39 * sendMouseEvent({type:'click'}, 'node');
michael@0 40 */
michael@0 41 function getElement(id) {
michael@0 42 return ((typeof(id) == "string") ?
michael@0 43 document.getElementById(id) : id);
michael@0 44 };
michael@0 45
michael@0 46 this.$ = this.getElement;
michael@0 47
michael@0 48 function sendMouseEvent(aEvent, aTarget, aWindow) {
michael@0 49 if (['click', 'contextmenu', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
michael@0 50 throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
michael@0 51 }
michael@0 52
michael@0 53 if (!aWindow) {
michael@0 54 aWindow = window;
michael@0 55 }
michael@0 56
michael@0 57 if (!(aTarget instanceof aWindow.Element)) {
michael@0 58 aTarget = aWindow.document.getElementById(aTarget);
michael@0 59 }
michael@0 60
michael@0 61 var event = aWindow.document.createEvent('MouseEvent');
michael@0 62
michael@0 63 var typeArg = aEvent.type;
michael@0 64 var canBubbleArg = true;
michael@0 65 var cancelableArg = true;
michael@0 66 var viewArg = aWindow;
michael@0 67 var detailArg = aEvent.detail || (aEvent.type == 'click' ||
michael@0 68 aEvent.type == 'mousedown' ||
michael@0 69 aEvent.type == 'mouseup' ? 1 :
michael@0 70 aEvent.type == 'dblclick'? 2 : 0);
michael@0 71 var screenXArg = aEvent.screenX || 0;
michael@0 72 var screenYArg = aEvent.screenY || 0;
michael@0 73 var clientXArg = aEvent.clientX || 0;
michael@0 74 var clientYArg = aEvent.clientY || 0;
michael@0 75 var ctrlKeyArg = aEvent.ctrlKey || false;
michael@0 76 var altKeyArg = aEvent.altKey || false;
michael@0 77 var shiftKeyArg = aEvent.shiftKey || false;
michael@0 78 var metaKeyArg = aEvent.metaKey || false;
michael@0 79 var buttonArg = aEvent.button || (aEvent.type == 'contextmenu' ? 2 : 0);
michael@0 80 var relatedTargetArg = aEvent.relatedTarget || null;
michael@0 81
michael@0 82 event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
michael@0 83 screenXArg, screenYArg, clientXArg, clientYArg,
michael@0 84 ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
michael@0 85 buttonArg, relatedTargetArg);
michael@0 86
michael@0 87 return SpecialPowers.dispatchEvent(aWindow, aTarget, event);
michael@0 88 }
michael@0 89
michael@0 90 /**
michael@0 91 * Send the char aChar to the focused element. This method handles casing of
michael@0 92 * chars (sends the right charcode, and sends a shift key for uppercase chars).
michael@0 93 * No other modifiers are handled at this point.
michael@0 94 *
michael@0 95 * For now this method only works for ASCII characters and emulates the shift
michael@0 96 * key state on US keyboard layout.
michael@0 97 */
michael@0 98 function sendChar(aChar, aWindow) {
michael@0 99 var hasShift;
michael@0 100 // Emulate US keyboard layout for the shiftKey state.
michael@0 101 switch (aChar) {
michael@0 102 case "!":
michael@0 103 case "@":
michael@0 104 case "#":
michael@0 105 case "$":
michael@0 106 case "%":
michael@0 107 case "^":
michael@0 108 case "&":
michael@0 109 case "*":
michael@0 110 case "(":
michael@0 111 case ")":
michael@0 112 case "_":
michael@0 113 case "+":
michael@0 114 case "{":
michael@0 115 case "}":
michael@0 116 case ":":
michael@0 117 case "\"":
michael@0 118 case "|":
michael@0 119 case "<":
michael@0 120 case ">":
michael@0 121 case "?":
michael@0 122 hasShift = true;
michael@0 123 break;
michael@0 124 default:
michael@0 125 hasShift = (aChar == aChar.toUpperCase());
michael@0 126 break;
michael@0 127 }
michael@0 128 synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
michael@0 129 }
michael@0 130
michael@0 131 /**
michael@0 132 * Send the string aStr to the focused element.
michael@0 133 *
michael@0 134 * For now this method only works for ASCII characters and emulates the shift
michael@0 135 * key state on US keyboard layout.
michael@0 136 */
michael@0 137 function sendString(aStr, aWindow) {
michael@0 138 for (var i = 0; i < aStr.length; ++i) {
michael@0 139 sendChar(aStr.charAt(i), aWindow);
michael@0 140 }
michael@0 141 }
michael@0 142
michael@0 143 /**
michael@0 144 * Send the non-character key aKey to the focused node.
michael@0 145 * The name of the key should be the part that comes after "DOM_VK_" in the
michael@0 146 * KeyEvent constant name for this key.
michael@0 147 * No modifiers are handled at this point.
michael@0 148 */
michael@0 149 function sendKey(aKey, aWindow) {
michael@0 150 var keyName = "VK_" + aKey.toUpperCase();
michael@0 151 synthesizeKey(keyName, { shiftKey: false }, aWindow);
michael@0 152 }
michael@0 153
michael@0 154 /**
michael@0 155 * Parse the key modifier flags from aEvent. Used to share code between
michael@0 156 * synthesizeMouse and synthesizeKey.
michael@0 157 */
michael@0 158 function _parseModifiers(aEvent)
michael@0 159 {
michael@0 160 const nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils;
michael@0 161 var mval = 0;
michael@0 162 if (aEvent.shiftKey) {
michael@0 163 mval |= nsIDOMWindowUtils.MODIFIER_SHIFT;
michael@0 164 }
michael@0 165 if (aEvent.ctrlKey) {
michael@0 166 mval |= nsIDOMWindowUtils.MODIFIER_CONTROL;
michael@0 167 }
michael@0 168 if (aEvent.altKey) {
michael@0 169 mval |= nsIDOMWindowUtils.MODIFIER_ALT;
michael@0 170 }
michael@0 171 if (aEvent.metaKey) {
michael@0 172 mval |= nsIDOMWindowUtils.MODIFIER_META;
michael@0 173 }
michael@0 174 if (aEvent.accelKey) {
michael@0 175 mval |= (navigator.platform.indexOf("Mac") >= 0) ?
michael@0 176 nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL;
michael@0 177 }
michael@0 178 if (aEvent.altGrKey) {
michael@0 179 mval |= nsIDOMWindowUtils.MODIFIER_ALTGRAPH;
michael@0 180 }
michael@0 181 if (aEvent.capsLockKey) {
michael@0 182 mval |= nsIDOMWindowUtils.MODIFIER_CAPSLOCK;
michael@0 183 }
michael@0 184 if (aEvent.fnKey) {
michael@0 185 mval |= nsIDOMWindowUtils.MODIFIER_FN;
michael@0 186 }
michael@0 187 if (aEvent.numLockKey) {
michael@0 188 mval |= nsIDOMWindowUtils.MODIFIER_NUMLOCK;
michael@0 189 }
michael@0 190 if (aEvent.scrollLockKey) {
michael@0 191 mval |= nsIDOMWindowUtils.MODIFIER_SCROLLLOCK;
michael@0 192 }
michael@0 193 if (aEvent.symbolLockKey) {
michael@0 194 mval |= nsIDOMWindowUtils.MODIFIER_SYMBOLLOCK;
michael@0 195 }
michael@0 196 if (aEvent.osKey) {
michael@0 197 mval |= nsIDOMWindowUtils.MODIFIER_OS;
michael@0 198 }
michael@0 199
michael@0 200 return mval;
michael@0 201 }
michael@0 202
michael@0 203 /**
michael@0 204 * Synthesize a mouse event on a target. The actual client point is determined
michael@0 205 * by taking the aTarget's client box and offseting it by aOffsetX and
michael@0 206 * aOffsetY. This allows mouse clicks to be simulated by calling this method.
michael@0 207 *
michael@0 208 * aEvent is an object which may contain the properties:
michael@0 209 * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
michael@0 210 *
michael@0 211 * If the type is specified, an mouse event of that type is fired. Otherwise,
michael@0 212 * a mousedown followed by a mouse up is performed.
michael@0 213 *
michael@0 214 * aWindow is optional, and defaults to the current window object.
michael@0 215 *
michael@0 216 * Returns whether the event had preventDefault() called on it.
michael@0 217 */
michael@0 218 function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
michael@0 219 {
michael@0 220 var rect = aTarget.getBoundingClientRect();
michael@0 221 return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
michael@0 222 aEvent, aWindow);
michael@0 223 }
michael@0 224 function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
michael@0 225 {
michael@0 226 var rect = aTarget.getBoundingClientRect();
michael@0 227 synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
michael@0 228 aEvent, aWindow);
michael@0 229 }
michael@0 230 function synthesizePointer(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
michael@0 231 {
michael@0 232 var rect = aTarget.getBoundingClientRect();
michael@0 233 return synthesizePointerAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
michael@0 234 aEvent, aWindow);
michael@0 235 }
michael@0 236
michael@0 237 /*
michael@0 238 * Synthesize a mouse event at a particular point in aWindow.
michael@0 239 *
michael@0 240 * aEvent is an object which may contain the properties:
michael@0 241 * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
michael@0 242 *
michael@0 243 * If the type is specified, an mouse event of that type is fired. Otherwise,
michael@0 244 * a mousedown followed by a mouse up is performed.
michael@0 245 *
michael@0 246 * aWindow is optional, and defaults to the current window object.
michael@0 247 */
michael@0 248 function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
michael@0 249 {
michael@0 250 var utils = _getDOMWindowUtils(aWindow);
michael@0 251 var defaultPrevented = false;
michael@0 252
michael@0 253 if (utils) {
michael@0 254 var button = aEvent.button || 0;
michael@0 255 var clickCount = aEvent.clickCount || 1;
michael@0 256 var modifiers = _parseModifiers(aEvent);
michael@0 257 var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
michael@0 258 var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
michael@0 259 var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true;
michael@0 260
michael@0 261 if (("type" in aEvent) && aEvent.type) {
michael@0 262 defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button,
michael@0 263 clickCount, modifiers, false,
michael@0 264 pressure, inputSource,
michael@0 265 synthesized);
michael@0 266 }
michael@0 267 else {
michael@0 268 utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
michael@0 269 utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
michael@0 270 }
michael@0 271 }
michael@0 272
michael@0 273 return defaultPrevented;
michael@0 274 }
michael@0 275 function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
michael@0 276 {
michael@0 277 var utils = _getDOMWindowUtils(aWindow);
michael@0 278
michael@0 279 if (utils) {
michael@0 280 var id = aEvent.id || 0;
michael@0 281 var rx = aEvent.rx || 1;
michael@0 282 var ry = aEvent.rx || 1;
michael@0 283 var angle = aEvent.angle || 0;
michael@0 284 var force = aEvent.force || 1;
michael@0 285 var modifiers = _parseModifiers(aEvent);
michael@0 286
michael@0 287 if (("type" in aEvent) && aEvent.type) {
michael@0 288 utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
michael@0 289 }
michael@0 290 else {
michael@0 291 utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
michael@0 292 utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
michael@0 293 }
michael@0 294 }
michael@0 295 }
michael@0 296 function synthesizePointerAtPoint(left, top, aEvent, aWindow)
michael@0 297 {
michael@0 298 var utils = _getDOMWindowUtils(aWindow);
michael@0 299 var defaultPrevented = false;
michael@0 300
michael@0 301 if (utils) {
michael@0 302 var button = aEvent.button || 0;
michael@0 303 var clickCount = aEvent.clickCount || 1;
michael@0 304 var modifiers = _parseModifiers(aEvent);
michael@0 305 var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
michael@0 306 var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
michael@0 307 var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true;
michael@0 308
michael@0 309 if (("type" in aEvent) && aEvent.type) {
michael@0 310 defaultPrevented = utils.sendPointerEvent(aEvent.type, left, top, button,
michael@0 311 clickCount, modifiers, false,
michael@0 312 pressure, inputSource,
michael@0 313 synthesized);
michael@0 314 }
michael@0 315 else {
michael@0 316 utils.sendPointerEvent("pointerdown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
michael@0 317 utils.sendPointerEvent("pointerup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
michael@0 318 }
michael@0 319 }
michael@0 320
michael@0 321 return defaultPrevented;
michael@0 322 }
michael@0 323
michael@0 324 // Call synthesizeMouse with coordinates at the center of aTarget.
michael@0 325 function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
michael@0 326 {
michael@0 327 var rect = aTarget.getBoundingClientRect();
michael@0 328 synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
michael@0 329 aWindow);
michael@0 330 }
michael@0 331 function synthesizeTouchAtCenter(aTarget, aEvent, aWindow)
michael@0 332 {
michael@0 333 var rect = aTarget.getBoundingClientRect();
michael@0 334 synthesizeTouch(aTarget, rect.width / 2, rect.height / 2, aEvent,
michael@0 335 aWindow);
michael@0 336 }
michael@0 337
michael@0 338 /**
michael@0 339 * Synthesize a wheel event on a target. The actual client point is determined
michael@0 340 * by taking the aTarget's client box and offseting it by aOffsetX and
michael@0 341 * aOffsetY.
michael@0 342 *
michael@0 343 * aEvent is an object which may contain the properties:
michael@0 344 * shiftKey, ctrlKey, altKey, metaKey, accessKey, deltaX, deltaY, deltaZ,
michael@0 345 * deltaMode, lineOrPageDeltaX, lineOrPageDeltaY, isMomentum, isPixelOnlyDevice,
michael@0 346 * isCustomizedByPrefs, expectedOverflowDeltaX, expectedOverflowDeltaY
michael@0 347 *
michael@0 348 * deltaMode must be defined, others are ok even if undefined.
michael@0 349 *
michael@0 350 * expectedOverflowDeltaX and expectedOverflowDeltaY take integer value. The
michael@0 351 * value is just checked as 0 or positive or negative.
michael@0 352 *
michael@0 353 * aWindow is optional, and defaults to the current window object.
michael@0 354 */
michael@0 355 function synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
michael@0 356 {
michael@0 357 var utils = _getDOMWindowUtils(aWindow);
michael@0 358 if (!utils) {
michael@0 359 return;
michael@0 360 }
michael@0 361
michael@0 362 var modifiers = _parseModifiers(aEvent);
michael@0 363 var options = 0;
michael@0 364 if (aEvent.isPixelOnlyDevice &&
michael@0 365 (aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL)) {
michael@0 366 options |= utils.WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE;
michael@0 367 }
michael@0 368 if (aEvent.isMomentum) {
michael@0 369 options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM;
michael@0 370 }
michael@0 371 if (aEvent.isCustomizedByPrefs) {
michael@0 372 options |= utils.WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS;
michael@0 373 }
michael@0 374 if (typeof aEvent.expectedOverflowDeltaX !== "undefined") {
michael@0 375 if (aEvent.expectedOverflowDeltaX === 0) {
michael@0 376 options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO;
michael@0 377 } else if (aEvent.expectedOverflowDeltaX > 0) {
michael@0 378 options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE;
michael@0 379 } else {
michael@0 380 options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE;
michael@0 381 }
michael@0 382 }
michael@0 383 if (typeof aEvent.expectedOverflowDeltaY !== "undefined") {
michael@0 384 if (aEvent.expectedOverflowDeltaY === 0) {
michael@0 385 options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO;
michael@0 386 } else if (aEvent.expectedOverflowDeltaY > 0) {
michael@0 387 options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE;
michael@0 388 } else {
michael@0 389 options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE;
michael@0 390 }
michael@0 391 }
michael@0 392 var isPixelOnlyDevice =
michael@0 393 aEvent.isPixelOnlyDevice && aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL;
michael@0 394
michael@0 395 // Avoid the JS warnings "reference to undefined property"
michael@0 396 if (!aEvent.deltaX) {
michael@0 397 aEvent.deltaX = 0;
michael@0 398 }
michael@0 399 if (!aEvent.deltaY) {
michael@0 400 aEvent.deltaY = 0;
michael@0 401 }
michael@0 402 if (!aEvent.deltaZ) {
michael@0 403 aEvent.deltaZ = 0;
michael@0 404 }
michael@0 405
michael@0 406 var lineOrPageDeltaX =
michael@0 407 aEvent.lineOrPageDeltaX != null ? aEvent.lineOrPageDeltaX :
michael@0 408 aEvent.deltaX > 0 ? Math.floor(aEvent.deltaX) :
michael@0 409 Math.ceil(aEvent.deltaX);
michael@0 410 var lineOrPageDeltaY =
michael@0 411 aEvent.lineOrPageDeltaY != null ? aEvent.lineOrPageDeltaY :
michael@0 412 aEvent.deltaY > 0 ? Math.floor(aEvent.deltaY) :
michael@0 413 Math.ceil(aEvent.deltaY);
michael@0 414
michael@0 415 var rect = aTarget.getBoundingClientRect();
michael@0 416 utils.sendWheelEvent(rect.left + aOffsetX, rect.top + aOffsetY,
michael@0 417 aEvent.deltaX, aEvent.deltaY, aEvent.deltaZ,
michael@0 418 aEvent.deltaMode, modifiers,
michael@0 419 lineOrPageDeltaX, lineOrPageDeltaY, options);
michael@0 420 }
michael@0 421
michael@0 422 function _computeKeyCodeFromChar(aChar)
michael@0 423 {
michael@0 424 if (aChar.length != 1) {
michael@0 425 return 0;
michael@0 426 }
michael@0 427 const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent;
michael@0 428 if (aChar >= 'a' && aChar <= 'z') {
michael@0 429 return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
michael@0 430 }
michael@0 431 if (aChar >= 'A' && aChar <= 'Z') {
michael@0 432 return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
michael@0 433 }
michael@0 434 if (aChar >= '0' && aChar <= '9') {
michael@0 435 return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
michael@0 436 }
michael@0 437 // returns US keyboard layout's keycode
michael@0 438 switch (aChar) {
michael@0 439 case '~':
michael@0 440 case '`':
michael@0 441 return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
michael@0 442 case '!':
michael@0 443 return nsIDOMKeyEvent.DOM_VK_1;
michael@0 444 case '@':
michael@0 445 return nsIDOMKeyEvent.DOM_VK_2;
michael@0 446 case '#':
michael@0 447 return nsIDOMKeyEvent.DOM_VK_3;
michael@0 448 case '$':
michael@0 449 return nsIDOMKeyEvent.DOM_VK_4;
michael@0 450 case '%':
michael@0 451 return nsIDOMKeyEvent.DOM_VK_5;
michael@0 452 case '^':
michael@0 453 return nsIDOMKeyEvent.DOM_VK_6;
michael@0 454 case '&':
michael@0 455 return nsIDOMKeyEvent.DOM_VK_7;
michael@0 456 case '*':
michael@0 457 return nsIDOMKeyEvent.DOM_VK_8;
michael@0 458 case '(':
michael@0 459 return nsIDOMKeyEvent.DOM_VK_9;
michael@0 460 case ')':
michael@0 461 return nsIDOMKeyEvent.DOM_VK_0;
michael@0 462 case '-':
michael@0 463 case '_':
michael@0 464 return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
michael@0 465 case '+':
michael@0 466 case '=':
michael@0 467 return nsIDOMKeyEvent.DOM_VK_EQUALS;
michael@0 468 case '{':
michael@0 469 case '[':
michael@0 470 return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
michael@0 471 case '}':
michael@0 472 case ']':
michael@0 473 return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
michael@0 474 case '|':
michael@0 475 case '\\':
michael@0 476 return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
michael@0 477 case ':':
michael@0 478 case ';':
michael@0 479 return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
michael@0 480 case '\'':
michael@0 481 case '"':
michael@0 482 return nsIDOMKeyEvent.DOM_VK_QUOTE;
michael@0 483 case '<':
michael@0 484 case ',':
michael@0 485 return nsIDOMKeyEvent.DOM_VK_COMMA;
michael@0 486 case '>':
michael@0 487 case '.':
michael@0 488 return nsIDOMKeyEvent.DOM_VK_PERIOD;
michael@0 489 case '?':
michael@0 490 case '/':
michael@0 491 return nsIDOMKeyEvent.DOM_VK_SLASH;
michael@0 492 case '\n':
michael@0 493 return nsIDOMKeyEvent.DOM_VK_RETURN;
michael@0 494 case ' ':
michael@0 495 return nsIDOMKeyEvent.DOM_VK_SPACE;
michael@0 496 default:
michael@0 497 return 0;
michael@0 498 }
michael@0 499 }
michael@0 500
michael@0 501 /**
michael@0 502 * isKeypressFiredKey() returns TRUE if the given key should cause keypress
michael@0 503 * event when widget handles the native key event. Otherwise, FALSE.
michael@0 504 *
michael@0 505 * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
michael@0 506 * name begins with "VK_", or a character.
michael@0 507 */
michael@0 508 function isKeypressFiredKey(aDOMKeyCode)
michael@0 509 {
michael@0 510 if (typeof(aDOMKeyCode) == "string") {
michael@0 511 if (aDOMKeyCode.indexOf("VK_") == 0) {
michael@0 512 aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
michael@0 513 if (!aDOMKeyCode) {
michael@0 514 throw "Unknown key: " + aDOMKeyCode;
michael@0 515 }
michael@0 516 } else {
michael@0 517 // If the key generates a character, it must cause a keypress event.
michael@0 518 return true;
michael@0 519 }
michael@0 520 }
michael@0 521 switch (aDOMKeyCode) {
michael@0 522 case KeyEvent.DOM_VK_SHIFT:
michael@0 523 case KeyEvent.DOM_VK_CONTROL:
michael@0 524 case KeyEvent.DOM_VK_ALT:
michael@0 525 case KeyEvent.DOM_VK_CAPS_LOCK:
michael@0 526 case KeyEvent.DOM_VK_NUM_LOCK:
michael@0 527 case KeyEvent.DOM_VK_SCROLL_LOCK:
michael@0 528 case KeyEvent.DOM_VK_META:
michael@0 529 return false;
michael@0 530 default:
michael@0 531 return true;
michael@0 532 }
michael@0 533 }
michael@0 534
michael@0 535 /**
michael@0 536 * Synthesize a key event. It is targeted at whatever would be targeted by an
michael@0 537 * actual keypress by the user, typically the focused element.
michael@0 538 *
michael@0 539 * aKey should be either a character or a keycode starting with VK_ such as
michael@0 540 * VK_RETURN.
michael@0 541 *
michael@0 542 * aEvent is an object which may contain the properties:
michael@0 543 * shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location
michael@0 544 *
michael@0 545 * Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location. Otherwise,
michael@0 546 * DOMWindowUtils will choose good location from the keycode.
michael@0 547 *
michael@0 548 * If the type is specified, a key event of that type is fired. Otherwise,
michael@0 549 * a keydown, a keypress and then a keyup event are fired in sequence.
michael@0 550 *
michael@0 551 * aWindow is optional, and defaults to the current window object.
michael@0 552 */
michael@0 553 function synthesizeKey(aKey, aEvent, aWindow)
michael@0 554 {
michael@0 555 var utils = _getDOMWindowUtils(aWindow);
michael@0 556 if (utils) {
michael@0 557 var keyCode = 0, charCode = 0;
michael@0 558 if (aKey.indexOf("VK_") == 0) {
michael@0 559 keyCode = KeyEvent["DOM_" + aKey];
michael@0 560 if (!keyCode) {
michael@0 561 throw "Unknown key: " + aKey;
michael@0 562 }
michael@0 563 } else {
michael@0 564 charCode = aKey.charCodeAt(0);
michael@0 565 keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
michael@0 566 }
michael@0 567
michael@0 568 var modifiers = _parseModifiers(aEvent);
michael@0 569 var flags = 0;
michael@0 570 if (aEvent.location != undefined) {
michael@0 571 switch (aEvent.location) {
michael@0 572 case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
michael@0 573 flags |= utils.KEY_FLAG_LOCATION_STANDARD;
michael@0 574 break;
michael@0 575 case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
michael@0 576 flags |= utils.KEY_FLAG_LOCATION_LEFT;
michael@0 577 break;
michael@0 578 case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
michael@0 579 flags |= utils.KEY_FLAG_LOCATION_RIGHT;
michael@0 580 break;
michael@0 581 case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
michael@0 582 flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
michael@0 583 break;
michael@0 584 case KeyboardEvent.DOM_KEY_LOCATION_MOBILE:
michael@0 585 flags |= utils.KEY_FLAG_LOCATION_MOBILE;
michael@0 586 break;
michael@0 587 case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK:
michael@0 588 flags |= utils.KEY_FLAG_LOCATION_JOYSTICK;
michael@0 589 break;
michael@0 590 }
michael@0 591 }
michael@0 592
michael@0 593 if (!("type" in aEvent) || !aEvent.type) {
michael@0 594 // Send keydown + (optional) keypress + keyup events.
michael@0 595 var keyDownDefaultHappened =
michael@0 596 utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
michael@0 597 if (isKeypressFiredKey(keyCode) && keyDownDefaultHappened) {
michael@0 598 utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags);
michael@0 599 }
michael@0 600 utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags);
michael@0 601 } else if (aEvent.type == "keypress") {
michael@0 602 // Send standalone keypress event.
michael@0 603 utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags);
michael@0 604 } else {
michael@0 605 // Send other standalone event than keypress.
michael@0 606 utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags);
michael@0 607 }
michael@0 608 }
michael@0 609 }
michael@0 610
michael@0 611 function _parseNativeModifiers(aModifiers)
michael@0 612 {
michael@0 613 var modifiers;
michael@0 614 if (aModifiers.capsLockKey) {
michael@0 615 modifiers |= 0x00000001;
michael@0 616 }
michael@0 617 if (aModifiers.numLockKey) {
michael@0 618 modifiers |= 0x00000002;
michael@0 619 }
michael@0 620 if (aModifiers.shiftKey) {
michael@0 621 modifiers |= 0x00000100;
michael@0 622 }
michael@0 623 if (aModifiers.shiftRightKey) {
michael@0 624 modifiers |= 0x00000200;
michael@0 625 }
michael@0 626 if (aModifiers.ctrlKey) {
michael@0 627 modifiers |= 0x00000400;
michael@0 628 }
michael@0 629 if (aModifiers.ctrlRightKey) {
michael@0 630 modifiers |= 0x00000800;
michael@0 631 }
michael@0 632 if (aModifiers.altKey) {
michael@0 633 modifiers |= 0x00001000;
michael@0 634 }
michael@0 635 if (aModifiers.altRightKey) {
michael@0 636 modifiers |= 0x00002000;
michael@0 637 }
michael@0 638 if (aModifiers.metaKey) {
michael@0 639 modifiers |= 0x00004000;
michael@0 640 }
michael@0 641 if (aModifiers.metaRightKey) {
michael@0 642 modifiers |= 0x00008000;
michael@0 643 }
michael@0 644 if (aModifiers.helpKey) {
michael@0 645 modifiers |= 0x00010000;
michael@0 646 }
michael@0 647 if (aModifiers.fnKey) {
michael@0 648 modifiers |= 0x00100000;
michael@0 649 }
michael@0 650 if (aModifiers.numericKeyPadKey) {
michael@0 651 modifiers |= 0x01000000;
michael@0 652 }
michael@0 653
michael@0 654 if (aModifiers.accelKey) {
michael@0 655 modifiers |=
michael@0 656 (navigator.platform.indexOf("Mac") == 0) ? 0x00004000 : 0x00000400;
michael@0 657 }
michael@0 658 if (aModifiers.accelRightKey) {
michael@0 659 modifiers |=
michael@0 660 (navigator.platform.indexOf("Mac") == 0) ? 0x00008000 : 0x00000800;
michael@0 661 }
michael@0 662 if (aModifiers.altGrKey) {
michael@0 663 modifiers |=
michael@0 664 (navigator.platform.indexOf("Win") == 0) ? 0x00002800 : 0x00001000;
michael@0 665 }
michael@0 666 return modifiers;
michael@0 667 }
michael@0 668
michael@0 669 // Mac: Any unused number is okay for adding new keyboard layout.
michael@0 670 // When you add new keyboard layout here, you need to modify
michael@0 671 // TISInputSourceWrapper::InitByLayoutID().
michael@0 672 // Win: These constants can be found by inspecting registry keys under
michael@0 673 // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts
michael@0 674
michael@0 675 const KEYBOARD_LAYOUT_ARABIC =
michael@0 676 { name: "Arabic", Mac: 6, Win: 0x00000401 };
michael@0 677 const KEYBOARD_LAYOUT_BRAZILIAN_ABNT =
michael@0 678 { name: "Brazilian ABNT", Mac: null, Win: 0x00000416 };
michael@0 679 const KEYBOARD_LAYOUT_DVORAK_QWERTY =
michael@0 680 { name: "Dvorak-QWERTY", Mac: 4, Win: null };
michael@0 681 const KEYBOARD_LAYOUT_EN_US =
michael@0 682 { name: "US", Mac: 0, Win: 0x00000409 };
michael@0 683 const KEYBOARD_LAYOUT_FRENCH =
michael@0 684 { name: "French", Mac: 7, Win: 0x0000040C };
michael@0 685 const KEYBOARD_LAYOUT_GREEK =
michael@0 686 { name: "Greek", Mac: 1, Win: 0x00000408 };
michael@0 687 const KEYBOARD_LAYOUT_GERMAN =
michael@0 688 { name: "German", Mac: 2, Win: 0x00000407 };
michael@0 689 const KEYBOARD_LAYOUT_HEBREW =
michael@0 690 { name: "Hebrew", Mac: 8, Win: 0x0000040D };
michael@0 691 const KEYBOARD_LAYOUT_JAPANESE =
michael@0 692 { name: "Japanese", Mac: null, Win: 0x00000411 };
michael@0 693 const KEYBOARD_LAYOUT_LITHUANIAN =
michael@0 694 { name: "Lithuanian", Mac: 9, Win: 0x00010427 };
michael@0 695 const KEYBOARD_LAYOUT_NORWEGIAN =
michael@0 696 { name: "Norwegian", Mac: 10, Win: 0x00000414 };
michael@0 697 const KEYBOARD_LAYOUT_SPANISH =
michael@0 698 { name: "Spanish", Mac: 11, Win: 0x0000040A };
michael@0 699 const KEYBOARD_LAYOUT_SWEDISH =
michael@0 700 { name: "Swedish", Mac: 3, Win: 0x0000041D };
michael@0 701 const KEYBOARD_LAYOUT_THAI =
michael@0 702 { name: "Thai", Mac: 5, Win: 0x0002041E };
michael@0 703
michael@0 704 /**
michael@0 705 * synthesizeNativeKey() dispatches native key event on active window.
michael@0 706 * This is implemented only on Windows and Mac.
michael@0 707 *
michael@0 708 * @param aKeyboardLayout One of KEYBOARD_LAYOUT_* defined above.
michael@0 709 * @param aNativeKeyCode A native keycode value defined in
michael@0 710 * NativeKeyCodes.js.
michael@0 711 * @param aModifiers Modifier keys. If no modifire key is pressed,
michael@0 712 * this must be {}. Otherwise, one or more items
michael@0 713 * referred in _parseNativeModifiers() must be
michael@0 714 * true.
michael@0 715 * @param aChars Specify characters which should be generated
michael@0 716 * by the key event.
michael@0 717 * @param aUnmodifiedChars Specify characters of unmodified (except Shift)
michael@0 718 * aChar value.
michael@0 719 * @return True if this function succeed dispatching
michael@0 720 * native key event. Otherwise, false.
michael@0 721 */
michael@0 722
michael@0 723 function synthesizeNativeKey(aKeyboardLayout, aNativeKeyCode, aModifiers,
michael@0 724 aChars, aUnmodifiedChars)
michael@0 725 {
michael@0 726 var utils = _getDOMWindowUtils(window);
michael@0 727 if (!utils) {
michael@0 728 return false;
michael@0 729 }
michael@0 730 var nativeKeyboardLayout = null;
michael@0 731 if (navigator.platform.indexOf("Mac") == 0) {
michael@0 732 nativeKeyboardLayout = aKeyboardLayout.Mac;
michael@0 733 } else if (navigator.platform.indexOf("Win") == 0) {
michael@0 734 nativeKeyboardLayout = aKeyboardLayout.Win;
michael@0 735 }
michael@0 736 if (nativeKeyboardLayout === null) {
michael@0 737 return false;
michael@0 738 }
michael@0 739 utils.sendNativeKeyEvent(nativeKeyboardLayout, aNativeKeyCode,
michael@0 740 _parseNativeModifiers(aModifiers),
michael@0 741 aChars, aUnmodifiedChars);
michael@0 742 return true;
michael@0 743 }
michael@0 744
michael@0 745 var _gSeenEvent = false;
michael@0 746
michael@0 747 /**
michael@0 748 * Indicate that an event with an original target of aExpectedTarget and
michael@0 749 * a type of aExpectedEvent is expected to be fired, or not expected to
michael@0 750 * be fired.
michael@0 751 */
michael@0 752 function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
michael@0 753 {
michael@0 754 if (!aExpectedTarget || !aExpectedEvent)
michael@0 755 return null;
michael@0 756
michael@0 757 _gSeenEvent = false;
michael@0 758
michael@0 759 var type = (aExpectedEvent.charAt(0) == "!") ?
michael@0 760 aExpectedEvent.substring(1) : aExpectedEvent;
michael@0 761 var eventHandler = function(event) {
michael@0 762 var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
michael@0 763 event.type == type);
michael@0 764 is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
michael@0 765 _gSeenEvent = true;
michael@0 766 };
michael@0 767
michael@0 768 aExpectedTarget.addEventListener(type, eventHandler, false);
michael@0 769 return eventHandler;
michael@0 770 }
michael@0 771
michael@0 772 /**
michael@0 773 * Check if the event was fired or not. The event handler aEventHandler
michael@0 774 * will be removed.
michael@0 775 */
michael@0 776 function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
michael@0 777 {
michael@0 778 if (aEventHandler) {
michael@0 779 var expectEvent = (aExpectedEvent.charAt(0) != "!");
michael@0 780 var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
michael@0 781 aExpectedTarget.removeEventListener(type, aEventHandler, false);
michael@0 782 var desc = type + " event";
michael@0 783 if (!expectEvent)
michael@0 784 desc += " not";
michael@0 785 is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
michael@0 786 }
michael@0 787
michael@0 788 _gSeenEvent = false;
michael@0 789 }
michael@0 790
michael@0 791 /**
michael@0 792 * Similar to synthesizeMouse except that a test is performed to see if an
michael@0 793 * event is fired at the right target as a result.
michael@0 794 *
michael@0 795 * aExpectedTarget - the expected originalTarget of the event.
michael@0 796 * aExpectedEvent - the expected type of the event, such as 'select'.
michael@0 797 * aTestName - the test name when outputing results
michael@0 798 *
michael@0 799 * To test that an event is not fired, use an expected type preceded by an
michael@0 800 * exclamation mark, such as '!select'. This might be used to test that a
michael@0 801 * click on a disabled element doesn't fire certain events for instance.
michael@0 802 *
michael@0 803 * aWindow is optional, and defaults to the current window object.
michael@0 804 */
michael@0 805 function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
michael@0 806 aExpectedTarget, aExpectedEvent, aTestName,
michael@0 807 aWindow)
michael@0 808 {
michael@0 809 var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
michael@0 810 synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
michael@0 811 _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
michael@0 812 }
michael@0 813
michael@0 814 /**
michael@0 815 * Similar to synthesizeKey except that a test is performed to see if an
michael@0 816 * event is fired at the right target as a result.
michael@0 817 *
michael@0 818 * aExpectedTarget - the expected originalTarget of the event.
michael@0 819 * aExpectedEvent - the expected type of the event, such as 'select'.
michael@0 820 * aTestName - the test name when outputing results
michael@0 821 *
michael@0 822 * To test that an event is not fired, use an expected type preceded by an
michael@0 823 * exclamation mark, such as '!select'.
michael@0 824 *
michael@0 825 * aWindow is optional, and defaults to the current window object.
michael@0 826 */
michael@0 827 function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
michael@0 828 aTestName, aWindow)
michael@0 829 {
michael@0 830 var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
michael@0 831 synthesizeKey(key, aEvent, aWindow);
michael@0 832 _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
michael@0 833 }
michael@0 834
michael@0 835 function disableNonTestMouseEvents(aDisable)
michael@0 836 {
michael@0 837 var domutils = _getDOMWindowUtils();
michael@0 838 domutils.disableNonTestMouseEvents(aDisable);
michael@0 839 }
michael@0 840
michael@0 841 function _getDOMWindowUtils(aWindow)
michael@0 842 {
michael@0 843 if (!aWindow) {
michael@0 844 aWindow = window;
michael@0 845 }
michael@0 846
michael@0 847 // we need parent.SpecialPowers for:
michael@0 848 // layout/base/tests/test_reftests_with_caret.html
michael@0 849 // chrome: toolkit/content/tests/chrome/test_findbar.xul
michael@0 850 // chrome: toolkit/content/tests/chrome/test_popup_anchor.xul
michael@0 851 if ("SpecialPowers" in window && window.SpecialPowers != undefined) {
michael@0 852 return SpecialPowers.getDOMWindowUtils(aWindow);
michael@0 853 }
michael@0 854 if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) {
michael@0 855 return parent.SpecialPowers.getDOMWindowUtils(aWindow);
michael@0 856 }
michael@0 857
michael@0 858 //TODO: this is assuming we are in chrome space
michael@0 859 return aWindow.QueryInterface(_EU_Ci.nsIInterfaceRequestor).
michael@0 860 getInterface(_EU_Ci.nsIDOMWindowUtils);
michael@0 861 }
michael@0 862
michael@0 863 // Must be synchronized with nsICompositionStringSynthesizer.
michael@0 864 const COMPOSITION_ATTR_RAWINPUT = 0x02;
michael@0 865 const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
michael@0 866 const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
michael@0 867 const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
michael@0 868
michael@0 869 /**
michael@0 870 * Synthesize a composition event.
michael@0 871 *
michael@0 872 * @param aEvent The composition event information. This must
michael@0 873 * have |type| member. The value must be
michael@0 874 * "compositionstart", "compositionend" or
michael@0 875 * "compositionupdate".
michael@0 876 * And also this may have |data| and |locale| which
michael@0 877 * would be used for the value of each property of
michael@0 878 * the composition event. Note that the data would
michael@0 879 * be ignored if the event type were
michael@0 880 * "compositionstart".
michael@0 881 * @param aWindow Optional (If null, current |window| will be used)
michael@0 882 */
michael@0 883 function synthesizeComposition(aEvent, aWindow)
michael@0 884 {
michael@0 885 var utils = _getDOMWindowUtils(aWindow);
michael@0 886 if (!utils) {
michael@0 887 return;
michael@0 888 }
michael@0 889
michael@0 890 utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "",
michael@0 891 aEvent.locale ? aEvent.locale : "");
michael@0 892 }
michael@0 893 /**
michael@0 894 * Synthesize a text event.
michael@0 895 *
michael@0 896 * @param aEvent The text event's information, this has |composition|
michael@0 897 * and |caret| members. |composition| has |string| and
michael@0 898 * |clauses| members. |clauses| must be array object. Each
michael@0 899 * object has |length| and |attr|. And |caret| has |start| and
michael@0 900 * |length|. See the following tree image.
michael@0 901 *
michael@0 902 * aEvent
michael@0 903 * +-- composition
michael@0 904 * | +-- string
michael@0 905 * | +-- clauses[]
michael@0 906 * | +-- length
michael@0 907 * | +-- attr
michael@0 908 * +-- caret
michael@0 909 * +-- start
michael@0 910 * +-- length
michael@0 911 *
michael@0 912 * Set the composition string to |composition.string|. Set its
michael@0 913 * clauses information to the |clauses| array.
michael@0 914 *
michael@0 915 * When it's composing, set the each clauses' length to the
michael@0 916 * |composition.clauses[n].length|. The sum of the all length
michael@0 917 * values must be same as the length of |composition.string|.
michael@0 918 * Set nsICompositionStringSynthesizer.ATTR_* to the
michael@0 919 * |composition.clauses[n].attr|.
michael@0 920 *
michael@0 921 * When it's not composing, set 0 to the
michael@0 922 * |composition.clauses[0].length| and
michael@0 923 * |composition.clauses[0].attr|.
michael@0 924 *
michael@0 925 * Set caret position to the |caret.start|. It's offset from
michael@0 926 * the start of the composition string. Set caret length to
michael@0 927 * |caret.length|. If it's larger than 0, it should be wide
michael@0 928 * caret. However, current nsEditor doesn't support wide
michael@0 929 * caret, therefore, you should always set 0 now.
michael@0 930 *
michael@0 931 * @param aWindow Optional (If null, current |window| will be used)
michael@0 932 */
michael@0 933 function synthesizeText(aEvent, aWindow)
michael@0 934 {
michael@0 935 var utils = _getDOMWindowUtils(aWindow);
michael@0 936 if (!utils) {
michael@0 937 return;
michael@0 938 }
michael@0 939
michael@0 940 if (!aEvent.composition || !aEvent.composition.clauses ||
michael@0 941 !aEvent.composition.clauses[0]) {
michael@0 942 return;
michael@0 943 }
michael@0 944
michael@0 945 var compositionString = utils.createCompositionStringSynthesizer();
michael@0 946 compositionString.setString(aEvent.composition.string);
michael@0 947 if (aEvent.composition.clauses[0].length) {
michael@0 948 for (var i = 0; i < aEvent.composition.clauses.length; i++) {
michael@0 949 switch (aEvent.composition.clauses[i].attr) {
michael@0 950 case compositionString.ATTR_RAWINPUT:
michael@0 951 case compositionString.ATTR_SELECTEDRAWTEXT:
michael@0 952 case compositionString.ATTR_CONVERTEDTEXT:
michael@0 953 case compositionString.ATTR_SELECTEDCONVERTEDTEXT:
michael@0 954 compositionString.appendClause(aEvent.composition.clauses[i].length,
michael@0 955 aEvent.composition.clauses[i].attr);
michael@0 956 break;
michael@0 957 case 0:
michael@0 958 // Ignore dummy clause for the argument.
michael@0 959 break;
michael@0 960 default:
michael@0 961 throw new Error("invalid clause attribute specified");
michael@0 962 break;
michael@0 963 }
michael@0 964 }
michael@0 965 }
michael@0 966
michael@0 967 if (aEvent.caret) {
michael@0 968 compositionString.setCaret(aEvent.caret.start, aEvent.caret.length);
michael@0 969 }
michael@0 970
michael@0 971 compositionString.dispatchEvent();
michael@0 972 }
michael@0 973
michael@0 974 // Must be synchronized with nsIDOMWindowUtils.
michael@0 975 const QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK = 0x0000;
michael@0 976 const QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK = 0x0001;
michael@0 977
michael@0 978 const SELECTION_SET_FLAG_USE_NATIVE_LINE_BREAK = 0x0000;
michael@0 979 const SELECTION_SET_FLAG_USE_XP_LINE_BREAK = 0x0001;
michael@0 980 const SELECTION_SET_FLAG_REVERSE = 0x0002;
michael@0 981
michael@0 982 /**
michael@0 983 * Synthesize a query selected text event.
michael@0 984 *
michael@0 985 * @param aWindow Optional (If null, current |window| will be used)
michael@0 986 * @return An nsIQueryContentEventResult object. If this failed,
michael@0 987 * the result might be null.
michael@0 988 */
michael@0 989 function synthesizeQuerySelectedText(aWindow)
michael@0 990 {
michael@0 991 var utils = _getDOMWindowUtils(aWindow);
michael@0 992 if (!utils) {
michael@0 993 return null;
michael@0 994 }
michael@0 995
michael@0 996 return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0,
michael@0 997 QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK);
michael@0 998 }
michael@0 999
michael@0 1000 /**
michael@0 1001 * Synthesize a query caret rect event.
michael@0 1002 *
michael@0 1003 * @param aOffset The caret offset. 0 means left side of the first character
michael@0 1004 * in the selection root.
michael@0 1005 * @param aWindow Optional (If null, current |window| will be used)
michael@0 1006 * @return An nsIQueryContentEventResult object. If this failed,
michael@0 1007 * the result might be null.
michael@0 1008 */
michael@0 1009 function synthesizeQueryCaretRect(aOffset, aWindow)
michael@0 1010 {
michael@0 1011 var utils = _getDOMWindowUtils(aWindow);
michael@0 1012 if (!utils) {
michael@0 1013 return null;
michael@0 1014 }
michael@0 1015 return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT,
michael@0 1016 aOffset, 0, 0, 0,
michael@0 1017 QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK);
michael@0 1018 }
michael@0 1019
michael@0 1020 /**
michael@0 1021 * Synthesize a selection set event.
michael@0 1022 *
michael@0 1023 * @param aOffset The character offset. 0 means the first character in the
michael@0 1024 * selection root.
michael@0 1025 * @param aLength The length of the text. If the length is too long,
michael@0 1026 * the extra length is ignored.
michael@0 1027 * @param aReverse If true, the selection is from |aOffset + aLength| to
michael@0 1028 * |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|.
michael@0 1029 * @param aWindow Optional (If null, current |window| will be used)
michael@0 1030 * @return True, if succeeded. Otherwise false.
michael@0 1031 */
michael@0 1032 function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow)
michael@0 1033 {
michael@0 1034 var utils = _getDOMWindowUtils(aWindow);
michael@0 1035 if (!utils) {
michael@0 1036 return false;
michael@0 1037 }
michael@0 1038 var flags = aReverse ? SELECTION_SET_FLAG_REVERSE : 0;
michael@0 1039 return utils.sendSelectionSetEvent(aOffset, aLength, flags);
michael@0 1040 }

mercurial