1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1040 @@ 1.4 +/** 1.5 + * EventUtils provides some utility methods for creating and sending DOM events. 1.6 + * Current methods: 1.7 + * sendMouseEvent 1.8 + * sendChar 1.9 + * sendString 1.10 + * sendKey 1.11 + * synthesizeMouse 1.12 + * synthesizeMouseAtCenter 1.13 + * synthesizePointer 1.14 + * synthesizeWheel 1.15 + * synthesizeKey 1.16 + * synthesizeNativeKey 1.17 + * synthesizeMouseExpectEvent 1.18 + * synthesizeKeyExpectEvent 1.19 + * 1.20 + * When adding methods to this file, please add a performance test for it. 1.21 + */ 1.22 + 1.23 +// This file is used both in privileged and unprivileged contexts, so we have to 1.24 +// be careful about our access to Components.interfaces. We also want to avoid 1.25 +// naming collisions with anything that might be defined in the scope that imports 1.26 +// this script. 1.27 +window.__defineGetter__('_EU_Ci', function() { 1.28 + // Even if the real |Components| doesn't exist, we might shim in a simple JS 1.29 + // placebo for compat. An easy way to differentiate this from the real thing 1.30 + // is whether the property is read-only or not. 1.31 + var c = Object.getOwnPropertyDescriptor(window, 'Components'); 1.32 + return c && c.value && !c.writable ? Components.interfaces : SpecialPowers.Ci; 1.33 +}); 1.34 + 1.35 +/** 1.36 + * Send a mouse event to the node aTarget (aTarget can be an id, or an 1.37 + * actual node) . The "event" passed in to aEvent is just a JavaScript 1.38 + * object with the properties set that the real mouse event object should 1.39 + * have. This includes the type of the mouse event. 1.40 + * E.g. to send an click event to the node with id 'node' you might do this: 1.41 + * 1.42 + * sendMouseEvent({type:'click'}, 'node'); 1.43 + */ 1.44 +function getElement(id) { 1.45 + return ((typeof(id) == "string") ? 1.46 + document.getElementById(id) : id); 1.47 +}; 1.48 + 1.49 +this.$ = this.getElement; 1.50 + 1.51 +function sendMouseEvent(aEvent, aTarget, aWindow) { 1.52 + if (['click', 'contextmenu', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) { 1.53 + throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'"); 1.54 + } 1.55 + 1.56 + if (!aWindow) { 1.57 + aWindow = window; 1.58 + } 1.59 + 1.60 + if (!(aTarget instanceof aWindow.Element)) { 1.61 + aTarget = aWindow.document.getElementById(aTarget); 1.62 + } 1.63 + 1.64 + var event = aWindow.document.createEvent('MouseEvent'); 1.65 + 1.66 + var typeArg = aEvent.type; 1.67 + var canBubbleArg = true; 1.68 + var cancelableArg = true; 1.69 + var viewArg = aWindow; 1.70 + var detailArg = aEvent.detail || (aEvent.type == 'click' || 1.71 + aEvent.type == 'mousedown' || 1.72 + aEvent.type == 'mouseup' ? 1 : 1.73 + aEvent.type == 'dblclick'? 2 : 0); 1.74 + var screenXArg = aEvent.screenX || 0; 1.75 + var screenYArg = aEvent.screenY || 0; 1.76 + var clientXArg = aEvent.clientX || 0; 1.77 + var clientYArg = aEvent.clientY || 0; 1.78 + var ctrlKeyArg = aEvent.ctrlKey || false; 1.79 + var altKeyArg = aEvent.altKey || false; 1.80 + var shiftKeyArg = aEvent.shiftKey || false; 1.81 + var metaKeyArg = aEvent.metaKey || false; 1.82 + var buttonArg = aEvent.button || (aEvent.type == 'contextmenu' ? 2 : 0); 1.83 + var relatedTargetArg = aEvent.relatedTarget || null; 1.84 + 1.85 + event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, 1.86 + screenXArg, screenYArg, clientXArg, clientYArg, 1.87 + ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, 1.88 + buttonArg, relatedTargetArg); 1.89 + 1.90 + return SpecialPowers.dispatchEvent(aWindow, aTarget, event); 1.91 +} 1.92 + 1.93 +/** 1.94 + * Send the char aChar to the focused element. This method handles casing of 1.95 + * chars (sends the right charcode, and sends a shift key for uppercase chars). 1.96 + * No other modifiers are handled at this point. 1.97 + * 1.98 + * For now this method only works for ASCII characters and emulates the shift 1.99 + * key state on US keyboard layout. 1.100 + */ 1.101 +function sendChar(aChar, aWindow) { 1.102 + var hasShift; 1.103 + // Emulate US keyboard layout for the shiftKey state. 1.104 + switch (aChar) { 1.105 + case "!": 1.106 + case "@": 1.107 + case "#": 1.108 + case "$": 1.109 + case "%": 1.110 + case "^": 1.111 + case "&": 1.112 + case "*": 1.113 + case "(": 1.114 + case ")": 1.115 + case "_": 1.116 + case "+": 1.117 + case "{": 1.118 + case "}": 1.119 + case ":": 1.120 + case "\"": 1.121 + case "|": 1.122 + case "<": 1.123 + case ">": 1.124 + case "?": 1.125 + hasShift = true; 1.126 + break; 1.127 + default: 1.128 + hasShift = (aChar == aChar.toUpperCase()); 1.129 + break; 1.130 + } 1.131 + synthesizeKey(aChar, { shiftKey: hasShift }, aWindow); 1.132 +} 1.133 + 1.134 +/** 1.135 + * Send the string aStr to the focused element. 1.136 + * 1.137 + * For now this method only works for ASCII characters and emulates the shift 1.138 + * key state on US keyboard layout. 1.139 + */ 1.140 +function sendString(aStr, aWindow) { 1.141 + for (var i = 0; i < aStr.length; ++i) { 1.142 + sendChar(aStr.charAt(i), aWindow); 1.143 + } 1.144 +} 1.145 + 1.146 +/** 1.147 + * Send the non-character key aKey to the focused node. 1.148 + * The name of the key should be the part that comes after "DOM_VK_" in the 1.149 + * KeyEvent constant name for this key. 1.150 + * No modifiers are handled at this point. 1.151 + */ 1.152 +function sendKey(aKey, aWindow) { 1.153 + var keyName = "VK_" + aKey.toUpperCase(); 1.154 + synthesizeKey(keyName, { shiftKey: false }, aWindow); 1.155 +} 1.156 + 1.157 +/** 1.158 + * Parse the key modifier flags from aEvent. Used to share code between 1.159 + * synthesizeMouse and synthesizeKey. 1.160 + */ 1.161 +function _parseModifiers(aEvent) 1.162 +{ 1.163 + const nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils; 1.164 + var mval = 0; 1.165 + if (aEvent.shiftKey) { 1.166 + mval |= nsIDOMWindowUtils.MODIFIER_SHIFT; 1.167 + } 1.168 + if (aEvent.ctrlKey) { 1.169 + mval |= nsIDOMWindowUtils.MODIFIER_CONTROL; 1.170 + } 1.171 + if (aEvent.altKey) { 1.172 + mval |= nsIDOMWindowUtils.MODIFIER_ALT; 1.173 + } 1.174 + if (aEvent.metaKey) { 1.175 + mval |= nsIDOMWindowUtils.MODIFIER_META; 1.176 + } 1.177 + if (aEvent.accelKey) { 1.178 + mval |= (navigator.platform.indexOf("Mac") >= 0) ? 1.179 + nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL; 1.180 + } 1.181 + if (aEvent.altGrKey) { 1.182 + mval |= nsIDOMWindowUtils.MODIFIER_ALTGRAPH; 1.183 + } 1.184 + if (aEvent.capsLockKey) { 1.185 + mval |= nsIDOMWindowUtils.MODIFIER_CAPSLOCK; 1.186 + } 1.187 + if (aEvent.fnKey) { 1.188 + mval |= nsIDOMWindowUtils.MODIFIER_FN; 1.189 + } 1.190 + if (aEvent.numLockKey) { 1.191 + mval |= nsIDOMWindowUtils.MODIFIER_NUMLOCK; 1.192 + } 1.193 + if (aEvent.scrollLockKey) { 1.194 + mval |= nsIDOMWindowUtils.MODIFIER_SCROLLLOCK; 1.195 + } 1.196 + if (aEvent.symbolLockKey) { 1.197 + mval |= nsIDOMWindowUtils.MODIFIER_SYMBOLLOCK; 1.198 + } 1.199 + if (aEvent.osKey) { 1.200 + mval |= nsIDOMWindowUtils.MODIFIER_OS; 1.201 + } 1.202 + 1.203 + return mval; 1.204 +} 1.205 + 1.206 +/** 1.207 + * Synthesize a mouse event on a target. The actual client point is determined 1.208 + * by taking the aTarget's client box and offseting it by aOffsetX and 1.209 + * aOffsetY. This allows mouse clicks to be simulated by calling this method. 1.210 + * 1.211 + * aEvent is an object which may contain the properties: 1.212 + * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type 1.213 + * 1.214 + * If the type is specified, an mouse event of that type is fired. Otherwise, 1.215 + * a mousedown followed by a mouse up is performed. 1.216 + * 1.217 + * aWindow is optional, and defaults to the current window object. 1.218 + * 1.219 + * Returns whether the event had preventDefault() called on it. 1.220 + */ 1.221 +function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) 1.222 +{ 1.223 + var rect = aTarget.getBoundingClientRect(); 1.224 + return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY, 1.225 + aEvent, aWindow); 1.226 +} 1.227 +function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) 1.228 +{ 1.229 + var rect = aTarget.getBoundingClientRect(); 1.230 + synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY, 1.231 + aEvent, aWindow); 1.232 +} 1.233 +function synthesizePointer(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) 1.234 +{ 1.235 + var rect = aTarget.getBoundingClientRect(); 1.236 + return synthesizePointerAtPoint(rect.left + aOffsetX, rect.top + aOffsetY, 1.237 + aEvent, aWindow); 1.238 +} 1.239 + 1.240 +/* 1.241 + * Synthesize a mouse event at a particular point in aWindow. 1.242 + * 1.243 + * aEvent is an object which may contain the properties: 1.244 + * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type 1.245 + * 1.246 + * If the type is specified, an mouse event of that type is fired. Otherwise, 1.247 + * a mousedown followed by a mouse up is performed. 1.248 + * 1.249 + * aWindow is optional, and defaults to the current window object. 1.250 + */ 1.251 +function synthesizeMouseAtPoint(left, top, aEvent, aWindow) 1.252 +{ 1.253 + var utils = _getDOMWindowUtils(aWindow); 1.254 + var defaultPrevented = false; 1.255 + 1.256 + if (utils) { 1.257 + var button = aEvent.button || 0; 1.258 + var clickCount = aEvent.clickCount || 1; 1.259 + var modifiers = _parseModifiers(aEvent); 1.260 + var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0; 1.261 + var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0; 1.262 + var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true; 1.263 + 1.264 + if (("type" in aEvent) && aEvent.type) { 1.265 + defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, 1.266 + clickCount, modifiers, false, 1.267 + pressure, inputSource, 1.268 + synthesized); 1.269 + } 1.270 + else { 1.271 + utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource); 1.272 + utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource); 1.273 + } 1.274 + } 1.275 + 1.276 + return defaultPrevented; 1.277 +} 1.278 +function synthesizeTouchAtPoint(left, top, aEvent, aWindow) 1.279 +{ 1.280 + var utils = _getDOMWindowUtils(aWindow); 1.281 + 1.282 + if (utils) { 1.283 + var id = aEvent.id || 0; 1.284 + var rx = aEvent.rx || 1; 1.285 + var ry = aEvent.rx || 1; 1.286 + var angle = aEvent.angle || 0; 1.287 + var force = aEvent.force || 1; 1.288 + var modifiers = _parseModifiers(aEvent); 1.289 + 1.290 + if (("type" in aEvent) && aEvent.type) { 1.291 + utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers); 1.292 + } 1.293 + else { 1.294 + utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers); 1.295 + utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers); 1.296 + } 1.297 + } 1.298 +} 1.299 +function synthesizePointerAtPoint(left, top, aEvent, aWindow) 1.300 +{ 1.301 + var utils = _getDOMWindowUtils(aWindow); 1.302 + var defaultPrevented = false; 1.303 + 1.304 + if (utils) { 1.305 + var button = aEvent.button || 0; 1.306 + var clickCount = aEvent.clickCount || 1; 1.307 + var modifiers = _parseModifiers(aEvent); 1.308 + var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0; 1.309 + var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0; 1.310 + var synthesized = ("isSynthesized" in aEvent) ? aEvent.isSynthesized : true; 1.311 + 1.312 + if (("type" in aEvent) && aEvent.type) { 1.313 + defaultPrevented = utils.sendPointerEvent(aEvent.type, left, top, button, 1.314 + clickCount, modifiers, false, 1.315 + pressure, inputSource, 1.316 + synthesized); 1.317 + } 1.318 + else { 1.319 + utils.sendPointerEvent("pointerdown", left, top, button, clickCount, modifiers, false, pressure, inputSource); 1.320 + utils.sendPointerEvent("pointerup", left, top, button, clickCount, modifiers, false, pressure, inputSource); 1.321 + } 1.322 + } 1.323 + 1.324 + return defaultPrevented; 1.325 +} 1.326 + 1.327 +// Call synthesizeMouse with coordinates at the center of aTarget. 1.328 +function synthesizeMouseAtCenter(aTarget, aEvent, aWindow) 1.329 +{ 1.330 + var rect = aTarget.getBoundingClientRect(); 1.331 + synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent, 1.332 + aWindow); 1.333 +} 1.334 +function synthesizeTouchAtCenter(aTarget, aEvent, aWindow) 1.335 +{ 1.336 + var rect = aTarget.getBoundingClientRect(); 1.337 + synthesizeTouch(aTarget, rect.width / 2, rect.height / 2, aEvent, 1.338 + aWindow); 1.339 +} 1.340 + 1.341 +/** 1.342 + * Synthesize a wheel event on a target. The actual client point is determined 1.343 + * by taking the aTarget's client box and offseting it by aOffsetX and 1.344 + * aOffsetY. 1.345 + * 1.346 + * aEvent is an object which may contain the properties: 1.347 + * shiftKey, ctrlKey, altKey, metaKey, accessKey, deltaX, deltaY, deltaZ, 1.348 + * deltaMode, lineOrPageDeltaX, lineOrPageDeltaY, isMomentum, isPixelOnlyDevice, 1.349 + * isCustomizedByPrefs, expectedOverflowDeltaX, expectedOverflowDeltaY 1.350 + * 1.351 + * deltaMode must be defined, others are ok even if undefined. 1.352 + * 1.353 + * expectedOverflowDeltaX and expectedOverflowDeltaY take integer value. The 1.354 + * value is just checked as 0 or positive or negative. 1.355 + * 1.356 + * aWindow is optional, and defaults to the current window object. 1.357 + */ 1.358 +function synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow) 1.359 +{ 1.360 + var utils = _getDOMWindowUtils(aWindow); 1.361 + if (!utils) { 1.362 + return; 1.363 + } 1.364 + 1.365 + var modifiers = _parseModifiers(aEvent); 1.366 + var options = 0; 1.367 + if (aEvent.isPixelOnlyDevice && 1.368 + (aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL)) { 1.369 + options |= utils.WHEEL_EVENT_CAUSED_BY_PIXEL_ONLY_DEVICE; 1.370 + } 1.371 + if (aEvent.isMomentum) { 1.372 + options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM; 1.373 + } 1.374 + if (aEvent.isCustomizedByPrefs) { 1.375 + options |= utils.WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS; 1.376 + } 1.377 + if (typeof aEvent.expectedOverflowDeltaX !== "undefined") { 1.378 + if (aEvent.expectedOverflowDeltaX === 0) { 1.379 + options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO; 1.380 + } else if (aEvent.expectedOverflowDeltaX > 0) { 1.381 + options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE; 1.382 + } else { 1.383 + options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE; 1.384 + } 1.385 + } 1.386 + if (typeof aEvent.expectedOverflowDeltaY !== "undefined") { 1.387 + if (aEvent.expectedOverflowDeltaY === 0) { 1.388 + options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO; 1.389 + } else if (aEvent.expectedOverflowDeltaY > 0) { 1.390 + options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE; 1.391 + } else { 1.392 + options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE; 1.393 + } 1.394 + } 1.395 + var isPixelOnlyDevice = 1.396 + aEvent.isPixelOnlyDevice && aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL; 1.397 + 1.398 + // Avoid the JS warnings "reference to undefined property" 1.399 + if (!aEvent.deltaX) { 1.400 + aEvent.deltaX = 0; 1.401 + } 1.402 + if (!aEvent.deltaY) { 1.403 + aEvent.deltaY = 0; 1.404 + } 1.405 + if (!aEvent.deltaZ) { 1.406 + aEvent.deltaZ = 0; 1.407 + } 1.408 + 1.409 + var lineOrPageDeltaX = 1.410 + aEvent.lineOrPageDeltaX != null ? aEvent.lineOrPageDeltaX : 1.411 + aEvent.deltaX > 0 ? Math.floor(aEvent.deltaX) : 1.412 + Math.ceil(aEvent.deltaX); 1.413 + var lineOrPageDeltaY = 1.414 + aEvent.lineOrPageDeltaY != null ? aEvent.lineOrPageDeltaY : 1.415 + aEvent.deltaY > 0 ? Math.floor(aEvent.deltaY) : 1.416 + Math.ceil(aEvent.deltaY); 1.417 + 1.418 + var rect = aTarget.getBoundingClientRect(); 1.419 + utils.sendWheelEvent(rect.left + aOffsetX, rect.top + aOffsetY, 1.420 + aEvent.deltaX, aEvent.deltaY, aEvent.deltaZ, 1.421 + aEvent.deltaMode, modifiers, 1.422 + lineOrPageDeltaX, lineOrPageDeltaY, options); 1.423 +} 1.424 + 1.425 +function _computeKeyCodeFromChar(aChar) 1.426 +{ 1.427 + if (aChar.length != 1) { 1.428 + return 0; 1.429 + } 1.430 + const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent; 1.431 + if (aChar >= 'a' && aChar <= 'z') { 1.432 + return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0); 1.433 + } 1.434 + if (aChar >= 'A' && aChar <= 'Z') { 1.435 + return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0); 1.436 + } 1.437 + if (aChar >= '0' && aChar <= '9') { 1.438 + return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0); 1.439 + } 1.440 + // returns US keyboard layout's keycode 1.441 + switch (aChar) { 1.442 + case '~': 1.443 + case '`': 1.444 + return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE; 1.445 + case '!': 1.446 + return nsIDOMKeyEvent.DOM_VK_1; 1.447 + case '@': 1.448 + return nsIDOMKeyEvent.DOM_VK_2; 1.449 + case '#': 1.450 + return nsIDOMKeyEvent.DOM_VK_3; 1.451 + case '$': 1.452 + return nsIDOMKeyEvent.DOM_VK_4; 1.453 + case '%': 1.454 + return nsIDOMKeyEvent.DOM_VK_5; 1.455 + case '^': 1.456 + return nsIDOMKeyEvent.DOM_VK_6; 1.457 + case '&': 1.458 + return nsIDOMKeyEvent.DOM_VK_7; 1.459 + case '*': 1.460 + return nsIDOMKeyEvent.DOM_VK_8; 1.461 + case '(': 1.462 + return nsIDOMKeyEvent.DOM_VK_9; 1.463 + case ')': 1.464 + return nsIDOMKeyEvent.DOM_VK_0; 1.465 + case '-': 1.466 + case '_': 1.467 + return nsIDOMKeyEvent.DOM_VK_SUBTRACT; 1.468 + case '+': 1.469 + case '=': 1.470 + return nsIDOMKeyEvent.DOM_VK_EQUALS; 1.471 + case '{': 1.472 + case '[': 1.473 + return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET; 1.474 + case '}': 1.475 + case ']': 1.476 + return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET; 1.477 + case '|': 1.478 + case '\\': 1.479 + return nsIDOMKeyEvent.DOM_VK_BACK_SLASH; 1.480 + case ':': 1.481 + case ';': 1.482 + return nsIDOMKeyEvent.DOM_VK_SEMICOLON; 1.483 + case '\'': 1.484 + case '"': 1.485 + return nsIDOMKeyEvent.DOM_VK_QUOTE; 1.486 + case '<': 1.487 + case ',': 1.488 + return nsIDOMKeyEvent.DOM_VK_COMMA; 1.489 + case '>': 1.490 + case '.': 1.491 + return nsIDOMKeyEvent.DOM_VK_PERIOD; 1.492 + case '?': 1.493 + case '/': 1.494 + return nsIDOMKeyEvent.DOM_VK_SLASH; 1.495 + case '\n': 1.496 + return nsIDOMKeyEvent.DOM_VK_RETURN; 1.497 + case ' ': 1.498 + return nsIDOMKeyEvent.DOM_VK_SPACE; 1.499 + default: 1.500 + return 0; 1.501 + } 1.502 +} 1.503 + 1.504 +/** 1.505 + * isKeypressFiredKey() returns TRUE if the given key should cause keypress 1.506 + * event when widget handles the native key event. Otherwise, FALSE. 1.507 + * 1.508 + * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key 1.509 + * name begins with "VK_", or a character. 1.510 + */ 1.511 +function isKeypressFiredKey(aDOMKeyCode) 1.512 +{ 1.513 + if (typeof(aDOMKeyCode) == "string") { 1.514 + if (aDOMKeyCode.indexOf("VK_") == 0) { 1.515 + aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode]; 1.516 + if (!aDOMKeyCode) { 1.517 + throw "Unknown key: " + aDOMKeyCode; 1.518 + } 1.519 + } else { 1.520 + // If the key generates a character, it must cause a keypress event. 1.521 + return true; 1.522 + } 1.523 + } 1.524 + switch (aDOMKeyCode) { 1.525 + case KeyEvent.DOM_VK_SHIFT: 1.526 + case KeyEvent.DOM_VK_CONTROL: 1.527 + case KeyEvent.DOM_VK_ALT: 1.528 + case KeyEvent.DOM_VK_CAPS_LOCK: 1.529 + case KeyEvent.DOM_VK_NUM_LOCK: 1.530 + case KeyEvent.DOM_VK_SCROLL_LOCK: 1.531 + case KeyEvent.DOM_VK_META: 1.532 + return false; 1.533 + default: 1.534 + return true; 1.535 + } 1.536 +} 1.537 + 1.538 +/** 1.539 + * Synthesize a key event. It is targeted at whatever would be targeted by an 1.540 + * actual keypress by the user, typically the focused element. 1.541 + * 1.542 + * aKey should be either a character or a keycode starting with VK_ such as 1.543 + * VK_RETURN. 1.544 + * 1.545 + * aEvent is an object which may contain the properties: 1.546 + * shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location 1.547 + * 1.548 + * Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location. Otherwise, 1.549 + * DOMWindowUtils will choose good location from the keycode. 1.550 + * 1.551 + * If the type is specified, a key event of that type is fired. Otherwise, 1.552 + * a keydown, a keypress and then a keyup event are fired in sequence. 1.553 + * 1.554 + * aWindow is optional, and defaults to the current window object. 1.555 + */ 1.556 +function synthesizeKey(aKey, aEvent, aWindow) 1.557 +{ 1.558 + var utils = _getDOMWindowUtils(aWindow); 1.559 + if (utils) { 1.560 + var keyCode = 0, charCode = 0; 1.561 + if (aKey.indexOf("VK_") == 0) { 1.562 + keyCode = KeyEvent["DOM_" + aKey]; 1.563 + if (!keyCode) { 1.564 + throw "Unknown key: " + aKey; 1.565 + } 1.566 + } else { 1.567 + charCode = aKey.charCodeAt(0); 1.568 + keyCode = _computeKeyCodeFromChar(aKey.charAt(0)); 1.569 + } 1.570 + 1.571 + var modifiers = _parseModifiers(aEvent); 1.572 + var flags = 0; 1.573 + if (aEvent.location != undefined) { 1.574 + switch (aEvent.location) { 1.575 + case KeyboardEvent.DOM_KEY_LOCATION_STANDARD: 1.576 + flags |= utils.KEY_FLAG_LOCATION_STANDARD; 1.577 + break; 1.578 + case KeyboardEvent.DOM_KEY_LOCATION_LEFT: 1.579 + flags |= utils.KEY_FLAG_LOCATION_LEFT; 1.580 + break; 1.581 + case KeyboardEvent.DOM_KEY_LOCATION_RIGHT: 1.582 + flags |= utils.KEY_FLAG_LOCATION_RIGHT; 1.583 + break; 1.584 + case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD: 1.585 + flags |= utils.KEY_FLAG_LOCATION_NUMPAD; 1.586 + break; 1.587 + case KeyboardEvent.DOM_KEY_LOCATION_MOBILE: 1.588 + flags |= utils.KEY_FLAG_LOCATION_MOBILE; 1.589 + break; 1.590 + case KeyboardEvent.DOM_KEY_LOCATION_JOYSTICK: 1.591 + flags |= utils.KEY_FLAG_LOCATION_JOYSTICK; 1.592 + break; 1.593 + } 1.594 + } 1.595 + 1.596 + if (!("type" in aEvent) || !aEvent.type) { 1.597 + // Send keydown + (optional) keypress + keyup events. 1.598 + var keyDownDefaultHappened = 1.599 + utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags); 1.600 + if (isKeypressFiredKey(keyCode) && keyDownDefaultHappened) { 1.601 + utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags); 1.602 + } 1.603 + utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags); 1.604 + } else if (aEvent.type == "keypress") { 1.605 + // Send standalone keypress event. 1.606 + utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags); 1.607 + } else { 1.608 + // Send other standalone event than keypress. 1.609 + utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags); 1.610 + } 1.611 + } 1.612 +} 1.613 + 1.614 +function _parseNativeModifiers(aModifiers) 1.615 +{ 1.616 + var modifiers; 1.617 + if (aModifiers.capsLockKey) { 1.618 + modifiers |= 0x00000001; 1.619 + } 1.620 + if (aModifiers.numLockKey) { 1.621 + modifiers |= 0x00000002; 1.622 + } 1.623 + if (aModifiers.shiftKey) { 1.624 + modifiers |= 0x00000100; 1.625 + } 1.626 + if (aModifiers.shiftRightKey) { 1.627 + modifiers |= 0x00000200; 1.628 + } 1.629 + if (aModifiers.ctrlKey) { 1.630 + modifiers |= 0x00000400; 1.631 + } 1.632 + if (aModifiers.ctrlRightKey) { 1.633 + modifiers |= 0x00000800; 1.634 + } 1.635 + if (aModifiers.altKey) { 1.636 + modifiers |= 0x00001000; 1.637 + } 1.638 + if (aModifiers.altRightKey) { 1.639 + modifiers |= 0x00002000; 1.640 + } 1.641 + if (aModifiers.metaKey) { 1.642 + modifiers |= 0x00004000; 1.643 + } 1.644 + if (aModifiers.metaRightKey) { 1.645 + modifiers |= 0x00008000; 1.646 + } 1.647 + if (aModifiers.helpKey) { 1.648 + modifiers |= 0x00010000; 1.649 + } 1.650 + if (aModifiers.fnKey) { 1.651 + modifiers |= 0x00100000; 1.652 + } 1.653 + if (aModifiers.numericKeyPadKey) { 1.654 + modifiers |= 0x01000000; 1.655 + } 1.656 + 1.657 + if (aModifiers.accelKey) { 1.658 + modifiers |= 1.659 + (navigator.platform.indexOf("Mac") == 0) ? 0x00004000 : 0x00000400; 1.660 + } 1.661 + if (aModifiers.accelRightKey) { 1.662 + modifiers |= 1.663 + (navigator.platform.indexOf("Mac") == 0) ? 0x00008000 : 0x00000800; 1.664 + } 1.665 + if (aModifiers.altGrKey) { 1.666 + modifiers |= 1.667 + (navigator.platform.indexOf("Win") == 0) ? 0x00002800 : 0x00001000; 1.668 + } 1.669 + return modifiers; 1.670 +} 1.671 + 1.672 +// Mac: Any unused number is okay for adding new keyboard layout. 1.673 +// When you add new keyboard layout here, you need to modify 1.674 +// TISInputSourceWrapper::InitByLayoutID(). 1.675 +// Win: These constants can be found by inspecting registry keys under 1.676 +// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts 1.677 + 1.678 +const KEYBOARD_LAYOUT_ARABIC = 1.679 + { name: "Arabic", Mac: 6, Win: 0x00000401 }; 1.680 +const KEYBOARD_LAYOUT_BRAZILIAN_ABNT = 1.681 + { name: "Brazilian ABNT", Mac: null, Win: 0x00000416 }; 1.682 +const KEYBOARD_LAYOUT_DVORAK_QWERTY = 1.683 + { name: "Dvorak-QWERTY", Mac: 4, Win: null }; 1.684 +const KEYBOARD_LAYOUT_EN_US = 1.685 + { name: "US", Mac: 0, Win: 0x00000409 }; 1.686 +const KEYBOARD_LAYOUT_FRENCH = 1.687 + { name: "French", Mac: 7, Win: 0x0000040C }; 1.688 +const KEYBOARD_LAYOUT_GREEK = 1.689 + { name: "Greek", Mac: 1, Win: 0x00000408 }; 1.690 +const KEYBOARD_LAYOUT_GERMAN = 1.691 + { name: "German", Mac: 2, Win: 0x00000407 }; 1.692 +const KEYBOARD_LAYOUT_HEBREW = 1.693 + { name: "Hebrew", Mac: 8, Win: 0x0000040D }; 1.694 +const KEYBOARD_LAYOUT_JAPANESE = 1.695 + { name: "Japanese", Mac: null, Win: 0x00000411 }; 1.696 +const KEYBOARD_LAYOUT_LITHUANIAN = 1.697 + { name: "Lithuanian", Mac: 9, Win: 0x00010427 }; 1.698 +const KEYBOARD_LAYOUT_NORWEGIAN = 1.699 + { name: "Norwegian", Mac: 10, Win: 0x00000414 }; 1.700 +const KEYBOARD_LAYOUT_SPANISH = 1.701 + { name: "Spanish", Mac: 11, Win: 0x0000040A }; 1.702 +const KEYBOARD_LAYOUT_SWEDISH = 1.703 + { name: "Swedish", Mac: 3, Win: 0x0000041D }; 1.704 +const KEYBOARD_LAYOUT_THAI = 1.705 + { name: "Thai", Mac: 5, Win: 0x0002041E }; 1.706 + 1.707 +/** 1.708 + * synthesizeNativeKey() dispatches native key event on active window. 1.709 + * This is implemented only on Windows and Mac. 1.710 + * 1.711 + * @param aKeyboardLayout One of KEYBOARD_LAYOUT_* defined above. 1.712 + * @param aNativeKeyCode A native keycode value defined in 1.713 + * NativeKeyCodes.js. 1.714 + * @param aModifiers Modifier keys. If no modifire key is pressed, 1.715 + * this must be {}. Otherwise, one or more items 1.716 + * referred in _parseNativeModifiers() must be 1.717 + * true. 1.718 + * @param aChars Specify characters which should be generated 1.719 + * by the key event. 1.720 + * @param aUnmodifiedChars Specify characters of unmodified (except Shift) 1.721 + * aChar value. 1.722 + * @return True if this function succeed dispatching 1.723 + * native key event. Otherwise, false. 1.724 + */ 1.725 + 1.726 +function synthesizeNativeKey(aKeyboardLayout, aNativeKeyCode, aModifiers, 1.727 + aChars, aUnmodifiedChars) 1.728 +{ 1.729 + var utils = _getDOMWindowUtils(window); 1.730 + if (!utils) { 1.731 + return false; 1.732 + } 1.733 + var nativeKeyboardLayout = null; 1.734 + if (navigator.platform.indexOf("Mac") == 0) { 1.735 + nativeKeyboardLayout = aKeyboardLayout.Mac; 1.736 + } else if (navigator.platform.indexOf("Win") == 0) { 1.737 + nativeKeyboardLayout = aKeyboardLayout.Win; 1.738 + } 1.739 + if (nativeKeyboardLayout === null) { 1.740 + return false; 1.741 + } 1.742 + utils.sendNativeKeyEvent(nativeKeyboardLayout, aNativeKeyCode, 1.743 + _parseNativeModifiers(aModifiers), 1.744 + aChars, aUnmodifiedChars); 1.745 + return true; 1.746 +} 1.747 + 1.748 +var _gSeenEvent = false; 1.749 + 1.750 +/** 1.751 + * Indicate that an event with an original target of aExpectedTarget and 1.752 + * a type of aExpectedEvent is expected to be fired, or not expected to 1.753 + * be fired. 1.754 + */ 1.755 +function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName) 1.756 +{ 1.757 + if (!aExpectedTarget || !aExpectedEvent) 1.758 + return null; 1.759 + 1.760 + _gSeenEvent = false; 1.761 + 1.762 + var type = (aExpectedEvent.charAt(0) == "!") ? 1.763 + aExpectedEvent.substring(1) : aExpectedEvent; 1.764 + var eventHandler = function(event) { 1.765 + var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget && 1.766 + event.type == type); 1.767 + is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : "")); 1.768 + _gSeenEvent = true; 1.769 + }; 1.770 + 1.771 + aExpectedTarget.addEventListener(type, eventHandler, false); 1.772 + return eventHandler; 1.773 +} 1.774 + 1.775 +/** 1.776 + * Check if the event was fired or not. The event handler aEventHandler 1.777 + * will be removed. 1.778 + */ 1.779 +function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName) 1.780 +{ 1.781 + if (aEventHandler) { 1.782 + var expectEvent = (aExpectedEvent.charAt(0) != "!"); 1.783 + var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1); 1.784 + aExpectedTarget.removeEventListener(type, aEventHandler, false); 1.785 + var desc = type + " event"; 1.786 + if (!expectEvent) 1.787 + desc += " not"; 1.788 + is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired"); 1.789 + } 1.790 + 1.791 + _gSeenEvent = false; 1.792 +} 1.793 + 1.794 +/** 1.795 + * Similar to synthesizeMouse except that a test is performed to see if an 1.796 + * event is fired at the right target as a result. 1.797 + * 1.798 + * aExpectedTarget - the expected originalTarget of the event. 1.799 + * aExpectedEvent - the expected type of the event, such as 'select'. 1.800 + * aTestName - the test name when outputing results 1.801 + * 1.802 + * To test that an event is not fired, use an expected type preceded by an 1.803 + * exclamation mark, such as '!select'. This might be used to test that a 1.804 + * click on a disabled element doesn't fire certain events for instance. 1.805 + * 1.806 + * aWindow is optional, and defaults to the current window object. 1.807 + */ 1.808 +function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent, 1.809 + aExpectedTarget, aExpectedEvent, aTestName, 1.810 + aWindow) 1.811 +{ 1.812 + var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); 1.813 + synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow); 1.814 + _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); 1.815 +} 1.816 + 1.817 +/** 1.818 + * Similar to synthesizeKey except that a test is performed to see if an 1.819 + * event is fired at the right target as a result. 1.820 + * 1.821 + * aExpectedTarget - the expected originalTarget of the event. 1.822 + * aExpectedEvent - the expected type of the event, such as 'select'. 1.823 + * aTestName - the test name when outputing results 1.824 + * 1.825 + * To test that an event is not fired, use an expected type preceded by an 1.826 + * exclamation mark, such as '!select'. 1.827 + * 1.828 + * aWindow is optional, and defaults to the current window object. 1.829 + */ 1.830 +function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent, 1.831 + aTestName, aWindow) 1.832 +{ 1.833 + var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName); 1.834 + synthesizeKey(key, aEvent, aWindow); 1.835 + _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName); 1.836 +} 1.837 + 1.838 +function disableNonTestMouseEvents(aDisable) 1.839 +{ 1.840 + var domutils = _getDOMWindowUtils(); 1.841 + domutils.disableNonTestMouseEvents(aDisable); 1.842 +} 1.843 + 1.844 +function _getDOMWindowUtils(aWindow) 1.845 +{ 1.846 + if (!aWindow) { 1.847 + aWindow = window; 1.848 + } 1.849 + 1.850 + // we need parent.SpecialPowers for: 1.851 + // layout/base/tests/test_reftests_with_caret.html 1.852 + // chrome: toolkit/content/tests/chrome/test_findbar.xul 1.853 + // chrome: toolkit/content/tests/chrome/test_popup_anchor.xul 1.854 + if ("SpecialPowers" in window && window.SpecialPowers != undefined) { 1.855 + return SpecialPowers.getDOMWindowUtils(aWindow); 1.856 + } 1.857 + if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) { 1.858 + return parent.SpecialPowers.getDOMWindowUtils(aWindow); 1.859 + } 1.860 + 1.861 + //TODO: this is assuming we are in chrome space 1.862 + return aWindow.QueryInterface(_EU_Ci.nsIInterfaceRequestor). 1.863 + getInterface(_EU_Ci.nsIDOMWindowUtils); 1.864 +} 1.865 + 1.866 +// Must be synchronized with nsICompositionStringSynthesizer. 1.867 +const COMPOSITION_ATTR_RAWINPUT = 0x02; 1.868 +const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03; 1.869 +const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; 1.870 +const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; 1.871 + 1.872 +/** 1.873 + * Synthesize a composition event. 1.874 + * 1.875 + * @param aEvent The composition event information. This must 1.876 + * have |type| member. The value must be 1.877 + * "compositionstart", "compositionend" or 1.878 + * "compositionupdate". 1.879 + * And also this may have |data| and |locale| which 1.880 + * would be used for the value of each property of 1.881 + * the composition event. Note that the data would 1.882 + * be ignored if the event type were 1.883 + * "compositionstart". 1.884 + * @param aWindow Optional (If null, current |window| will be used) 1.885 + */ 1.886 +function synthesizeComposition(aEvent, aWindow) 1.887 +{ 1.888 + var utils = _getDOMWindowUtils(aWindow); 1.889 + if (!utils) { 1.890 + return; 1.891 + } 1.892 + 1.893 + utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "", 1.894 + aEvent.locale ? aEvent.locale : ""); 1.895 +} 1.896 +/** 1.897 + * Synthesize a text event. 1.898 + * 1.899 + * @param aEvent The text event's information, this has |composition| 1.900 + * and |caret| members. |composition| has |string| and 1.901 + * |clauses| members. |clauses| must be array object. Each 1.902 + * object has |length| and |attr|. And |caret| has |start| and 1.903 + * |length|. See the following tree image. 1.904 + * 1.905 + * aEvent 1.906 + * +-- composition 1.907 + * | +-- string 1.908 + * | +-- clauses[] 1.909 + * | +-- length 1.910 + * | +-- attr 1.911 + * +-- caret 1.912 + * +-- start 1.913 + * +-- length 1.914 + * 1.915 + * Set the composition string to |composition.string|. Set its 1.916 + * clauses information to the |clauses| array. 1.917 + * 1.918 + * When it's composing, set the each clauses' length to the 1.919 + * |composition.clauses[n].length|. The sum of the all length 1.920 + * values must be same as the length of |composition.string|. 1.921 + * Set nsICompositionStringSynthesizer.ATTR_* to the 1.922 + * |composition.clauses[n].attr|. 1.923 + * 1.924 + * When it's not composing, set 0 to the 1.925 + * |composition.clauses[0].length| and 1.926 + * |composition.clauses[0].attr|. 1.927 + * 1.928 + * Set caret position to the |caret.start|. It's offset from 1.929 + * the start of the composition string. Set caret length to 1.930 + * |caret.length|. If it's larger than 0, it should be wide 1.931 + * caret. However, current nsEditor doesn't support wide 1.932 + * caret, therefore, you should always set 0 now. 1.933 + * 1.934 + * @param aWindow Optional (If null, current |window| will be used) 1.935 + */ 1.936 +function synthesizeText(aEvent, aWindow) 1.937 +{ 1.938 + var utils = _getDOMWindowUtils(aWindow); 1.939 + if (!utils) { 1.940 + return; 1.941 + } 1.942 + 1.943 + if (!aEvent.composition || !aEvent.composition.clauses || 1.944 + !aEvent.composition.clauses[0]) { 1.945 + return; 1.946 + } 1.947 + 1.948 + var compositionString = utils.createCompositionStringSynthesizer(); 1.949 + compositionString.setString(aEvent.composition.string); 1.950 + if (aEvent.composition.clauses[0].length) { 1.951 + for (var i = 0; i < aEvent.composition.clauses.length; i++) { 1.952 + switch (aEvent.composition.clauses[i].attr) { 1.953 + case compositionString.ATTR_RAWINPUT: 1.954 + case compositionString.ATTR_SELECTEDRAWTEXT: 1.955 + case compositionString.ATTR_CONVERTEDTEXT: 1.956 + case compositionString.ATTR_SELECTEDCONVERTEDTEXT: 1.957 + compositionString.appendClause(aEvent.composition.clauses[i].length, 1.958 + aEvent.composition.clauses[i].attr); 1.959 + break; 1.960 + case 0: 1.961 + // Ignore dummy clause for the argument. 1.962 + break; 1.963 + default: 1.964 + throw new Error("invalid clause attribute specified"); 1.965 + break; 1.966 + } 1.967 + } 1.968 + } 1.969 + 1.970 + if (aEvent.caret) { 1.971 + compositionString.setCaret(aEvent.caret.start, aEvent.caret.length); 1.972 + } 1.973 + 1.974 + compositionString.dispatchEvent(); 1.975 +} 1.976 + 1.977 +// Must be synchronized with nsIDOMWindowUtils. 1.978 +const QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK = 0x0000; 1.979 +const QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK = 0x0001; 1.980 + 1.981 +const SELECTION_SET_FLAG_USE_NATIVE_LINE_BREAK = 0x0000; 1.982 +const SELECTION_SET_FLAG_USE_XP_LINE_BREAK = 0x0001; 1.983 +const SELECTION_SET_FLAG_REVERSE = 0x0002; 1.984 + 1.985 +/** 1.986 + * Synthesize a query selected text event. 1.987 + * 1.988 + * @param aWindow Optional (If null, current |window| will be used) 1.989 + * @return An nsIQueryContentEventResult object. If this failed, 1.990 + * the result might be null. 1.991 + */ 1.992 +function synthesizeQuerySelectedText(aWindow) 1.993 +{ 1.994 + var utils = _getDOMWindowUtils(aWindow); 1.995 + if (!utils) { 1.996 + return null; 1.997 + } 1.998 + 1.999 + return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0, 1.1000 + QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK); 1.1001 +} 1.1002 + 1.1003 +/** 1.1004 + * Synthesize a query caret rect event. 1.1005 + * 1.1006 + * @param aOffset The caret offset. 0 means left side of the first character 1.1007 + * in the selection root. 1.1008 + * @param aWindow Optional (If null, current |window| will be used) 1.1009 + * @return An nsIQueryContentEventResult object. If this failed, 1.1010 + * the result might be null. 1.1011 + */ 1.1012 +function synthesizeQueryCaretRect(aOffset, aWindow) 1.1013 +{ 1.1014 + var utils = _getDOMWindowUtils(aWindow); 1.1015 + if (!utils) { 1.1016 + return null; 1.1017 + } 1.1018 + return utils.sendQueryContentEvent(utils.QUERY_CARET_RECT, 1.1019 + aOffset, 0, 0, 0, 1.1020 + QUERY_CONTENT_FLAG_USE_NATIVE_LINE_BREAK); 1.1021 +} 1.1022 + 1.1023 +/** 1.1024 + * Synthesize a selection set event. 1.1025 + * 1.1026 + * @param aOffset The character offset. 0 means the first character in the 1.1027 + * selection root. 1.1028 + * @param aLength The length of the text. If the length is too long, 1.1029 + * the extra length is ignored. 1.1030 + * @param aReverse If true, the selection is from |aOffset + aLength| to 1.1031 + * |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|. 1.1032 + * @param aWindow Optional (If null, current |window| will be used) 1.1033 + * @return True, if succeeded. Otherwise false. 1.1034 + */ 1.1035 +function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow) 1.1036 +{ 1.1037 + var utils = _getDOMWindowUtils(aWindow); 1.1038 + if (!utils) { 1.1039 + return false; 1.1040 + } 1.1041 + var flags = aReverse ? SELECTION_SET_FLAG_REVERSE : 0; 1.1042 + return utils.sendSelectionSetEvent(aOffset, aLength, flags); 1.1043 +}