rename from testing/marionette/EventUtils.js
rename to testing/marionette/event.js
--- a/testing/marionette/EventUtils.js
+++ b/testing/marionette/event.js
@@ -1,673 +1,954 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-/**
- * EventUtils provides some utility methods for creating and sending DOM events.
- * Current methods:
- * sendMouseEvent
- * sendChar
- * sendString
- * sendKey
- * synthesizeMouse
- * synthesizeMouseAtCenter
- * synthesizeMouseScroll
- * synthesizeKey
- * synthesizeMouseExpectEvent
- * synthesizeKeyExpectEvent
- *
- * When adding methods to this file, please add a performance test for it.
- */
+// Provides functionality for creating and sending DOM events.
+
+"use strict";
+
+const {interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Log.jsm");
+const logger = Log.repository.getLogger("Marionette");
+
+Cu.import("chrome://marionette/content/elements.js");
+Cu.import("chrome://marionette/content/error.js");
-/**
- * Send a mouse event to the node aTarget (aTarget can be an id, or an
- * actual node) . The "event" passed in to aEvent is just a JavaScript
- * object with the properties set that the real mouse event object should
- * have. This includes the type of the mouse event.
- * E.g. to send an click event to the node with id 'node' you might do this:
- *
- * sendMouseEvent({type:'click'}, 'node');
- */
-function getElement(id) {
- return ((typeof(id) == "string") ?
- document.getElementById(id) : id);
-};
+this.EXPORTED_SYMBOLS = ["event"];
-this.$ = this.getElement;
-const KeyEvent = Components.interfaces.nsIDOMKeyEvent;
+// must be synchronised with nsIDOMWindowUtils
+const COMPOSITION_ATTR_RAWINPUT = 0x02;
+const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
+const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
+const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
-function sendMouseEvent(aEvent, aTarget, aWindow) {
- if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
- throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
+// TODO(ato): Document!
+let seenEvent = false;
+
+function getDOMWindowUtils(win) {
+ if (!win) {
+ win = window;
}
- if (!aWindow) {
- aWindow = window;
- }
+ // this assumes we are operating in chrome space
+ return win.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+}
- if (!(aTarget instanceof Element)) {
- aTarget = aWindow.document.getElementById(aTarget);
- }
-
- var event = aWindow.document.createEvent('MouseEvent');
+this.event = {};
- var typeArg = aEvent.type;
- var canBubbleArg = true;
- var cancelableArg = true;
- var viewArg = aWindow;
- var detailArg = aEvent.detail || (aEvent.type == 'click' ||
- aEvent.type == 'mousedown' ||
- aEvent.type == 'mouseup' ? 1 :
- aEvent.type == 'dblclick'? 2 : 0);
- var screenXArg = aEvent.screenX || 0;
- var screenYArg = aEvent.screenY || 0;
- var clientXArg = aEvent.clientX || 0;
- var clientYArg = aEvent.clientY || 0;
- var ctrlKeyArg = aEvent.ctrlKey || false;
- var altKeyArg = aEvent.altKey || false;
- var shiftKeyArg = aEvent.shiftKey || false;
- var metaKeyArg = aEvent.metaKey || false;
- var buttonArg = aEvent.button || 0;
- var relatedTargetArg = aEvent.relatedTarget || null;
+event.MouseEvents = {
+ click: 0,
+ dblclick: 1,
+ mousedown: 2,
+ mouseup: 3,
+ mouseover: 4,
+ mouseout: 5,
+};
- event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
- screenXArg, screenYArg, clientXArg, clientYArg,
- ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
- buttonArg, relatedTargetArg);
-
- //removed: SpecialPowers.dispatchEvent(aWindow, aTarget, event);
-}
+event.Modifiers = {
+ shiftKey: 0,
+ ctrlKey: 1,
+ altKey: 2,
+ metaKey: 3,
+};
/**
- * Send the char aChar to the focused element. This method handles casing of
- * chars (sends the right charcode, and sends a shift key for uppercase chars).
- * No other modifiers are handled at this point.
+ * Sends a mouse event to given target.
*
- * For now this method only works for English letters (lower and upper case)
- * and the digits 0-9.
- */
-function sendChar(aChar, aWindow) {
- // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9.
- var hasShift = (aChar == aChar.toUpperCase());
- synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
-}
-
-/**
- * Send the string aStr to the focused element.
+ * @param {nsIDOMMouseEvent} mouseEvent
+ * Event to send.
+ * @param {(Element|string)} target
+ * Target of event. Can either be an Element or the ID of an element.
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
*
- * For now this method only works for English letters (lower and upper case)
- * and the digits 0-9.
+ * @throws {TypeError}
+ * If the event is unsupported.
*/
-function sendString(aStr, aWindow) {
- for (var i = 0; i < aStr.length; ++i) {
- sendChar(aStr.charAt(i), aWindow);
+event.sendMouseEvent = function(mouseEvent, target, window = undefined) {
+ if (event.MouseEvents.hasOwnProperty(mouseEvent.type)) {
+ throw new TypeError("Unsupported event type: " + mouseEvent.type);
+ }
+
+ if (!(target instanceof Element)) {
+ target = window.document.getElementById(target);
}
-}
+
+ let ev = window.document.createEvent("MouseEvent");
+
+ let type = mouseEvent.type;
+ let view = window;
+
+ let detail = mouseEvent.detail;
+ if (!detail) {
+ if (mouseEvent.type in ["click", "mousedown", "mouseup"]) {
+ detail = 1;
+ } else if (mouseEvent.type == "dblclick") {
+ detail = 2;
+ } else {
+ detail = 0;
+ }
+ }
-/**
- * Send the non-character key aKey to the focused node.
- * The name of the key should be the part that comes after "DOM_VK_" in the
- * KeyEvent constant name for this key.
- * No modifiers are handled at this point.
- */
-function sendKey(aKey, aWindow) {
- var keyName = "VK_" + aKey.toUpperCase();
- synthesizeKey(keyName, { shiftKey: false }, aWindow);
-}
+ let screenX = mouseEvent.screenX || 0;
+ let screenY = mouseEvent.screenY || 0;
+ let clientX = mouseEvent.clientX || 0;
+ let clientY = mouseEvent.clientY || 0;
+ let ctrlKey = mouseEvent.ctrlKey || false;
+ let altKey = mouseEvent.altKey || false;
+ let shiftKey = mouseEvent.shiftKey || false;
+ let metaKey = mouseEvent.metaKey || false;
+ let button = mouseEvent.button || 0;
+ let relatedTarget = mouseEvent.relatedTarget || null;
+
+ ev.initMouseEvent(
+ mouseEvent.type,
+ /* canBubble */ true,
+ /* cancelable */ true,
+ view,
+ detail,
+ screenX,
+ screenY,
+ clientX,
+ clientY,
+ ctrlKey,
+ altKey,
+ shiftKey,
+ metaKey,
+ button,
+ relatedTarget);
+};
/**
- * Parse the key modifier flags from aEvent. Used to share code between
- * synthesizeMouse and synthesizeKey.
+ * Send character to the currently focused element.
+ *
+ * This function handles casing of characters (sends the right charcode,
+ * and sends a shift key for uppercase chars). No other modifiers are
+ * handled at this point.
+ *
+ * For now this method only works for English letters (lower and upper
+ * case) and the digits 0-9.
*/
-function _parseModifiers(aEvent)
-{
- const masks = Components.interfaces.nsIDOMNSEvent;
- var mval = 0;
- if (aEvent.shiftKey)
- mval |= masks.SHIFT_MASK;
- if (aEvent.ctrlKey)
- mval |= masks.CONTROL_MASK;
- if (aEvent.altKey)
- mval |= masks.ALT_MASK;
- if (aEvent.metaKey)
- mval |= masks.META_MASK;
- if (aEvent.accelKey)
- mval |= (navigator.platform.indexOf("Mac") >= 0) ? masks.META_MASK :
- masks.CONTROL_MASK;
+event.sendChar = function(char, window = undefined) {
+ // DOM event charcodes match ASCII (JS charcodes) for a-zA-Z0-9
+ let hasShift = (char == char.toUpperCase());
+ event.synthesizeKey(char, {shiftKey: hasShift}, window);
+};
- return mval;
-}
+/**
+ * Send string to the focused element.
+ *
+ * For now this method only works for English letters (lower and upper
+ * case) and the digits 0-9.
+ */
+event.sendString = function(string, window = undefined) {
+ for (let i = 0; i < string.length; ++i) {
+ event.sendChar(string.charAt(i), window);
+ }
+};
/**
- * Synthesize a mouse event on a target. The actual client point is determined
- * by taking the aTarget's client box and offseting it by aOffsetX and
- * aOffsetY. This allows mouse clicks to be simulated by calling this method.
- *
- * aEvent is an object which may contain the properties:
- * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
+ * Send the non-character key to the focused element.
*
- * If the type is specified, an mouse event of that type is fired. Otherwise,
- * a mousedown followed by a mouse up is performed.
- *
- * aWindow is optional, and defaults to the current window object.
+ * The name of the key should be the part that comes after "DOM_VK_"
+ * in the nsIDOMKeyEvent constant name for this key. No modifiers are
+ * handled at this point.
*/
-function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
-{
- var rect = aTarget.getBoundingClientRect();
- synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
- aEvent, aWindow);
-}
+event.sendKey = function(key, window = undefined) {
+ let keyName = "VK_" + key.toUpperCase();
+ event.synthesizeKey(keyName, {shiftKey: false}, window);
+};
+
+// TODO(ato): Unexpose this when actions.Chain#emitMouseEvent
+// no longer emits its own events
+event.parseModifiers_ = function(event) {
+ let mval = 0;
+ if (event.shiftKey) {
+ mval |= Ci.nsIDOMNSEvent.SHIFT_MASK;
+ }
+ if (event.ctrlKey) {
+ mval |= Ci.nsIDOMNSEvent.CONTROL_MASK;
+ }
+ if (event.altKey) {
+ mval |= Ci.nsIDOMNSEvent.ALT_MASK;
+ }
+ if (event.metaKey) {
+ mval |= Ci.nsIDOMNSEvent.META_MASK;
+ }
+ if (event.accelKey) {
+ if (navigator.platform.indexOf("Mac") >= 0) {
+ mval |= Ci.nsIDOMNSEvent.META_MASK;
+ } else {
+ mval |= Ci.nsIDOMNSEvent.CONTROL_MASK;
+ }
+ }
+ return mval;
+};
+
+/**
+ * Synthesise a mouse event on a target.
+ *
+ * The actual client point is determined by taking the aTarget's client
+ * box and offseting it by offsetX and offsetY. This allows mouse clicks
+ * to be simulated by calling this method.
+ *
+ * If the type is specified, an mouse event of that type is
+ * fired. Otherwise, a mousedown followed by a mouse up is performed.
+ *
+ * @param {Element} element
+ * Element to click.
+ * @param {number} offsetX
+ * Horizontal offset to click from the target's bounding box.
+ * @param {number} offsetY
+ * Vertical offset to click from the target's bounding box.
+ * @param {Object.<string, ?>} opts
+ * Object which may contain the properties "shiftKey", "ctrlKey",
+ * "altKey", "metaKey", "accessKey", "clickCount", "button", and
+ * "type".
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
+ */
+event.synthesizeMouse = function(
+ element, offsetX, offsetY, opts, window = undefined) {
+ let rect = element.getBoundingClientRect();
+ event.synthesizeMouseAtPoint(
+ rect.left + offsetX, rect.top + offsetY, opts, window);
+};
/*
- * Synthesize a mouse event at a particular point in aWindow.
+ * Synthesize a mouse event at a particular point in a window.
*
- * aEvent is an object which may contain the properties:
- * shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
+ * If the type of the event is specified, a mouse event of that type is
+ * fired. Otherwise, a mousedown followed by a mouse up is performed.
*
- * If the type is specified, an mouse event of that type is fired. Otherwise,
- * a mousedown followed by a mouse up is performed.
- *
- * aWindow is optional, and defaults to the current window object.
+ * @param {number} left
+ * CSS pixels from the left document margin.
+ * @param {number} top
+ * CSS pixels from the top document margin.
+ * @param {Object.<string, ?>} event
+ * Object which may contain the properties "shiftKey", "ctrlKey",
+ * "altKey", "metaKey", "accessKey", "clickCount", "button", and
+ * "type".
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
*/
-function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
+event.synthesizeMouseAtPoint = function(
+ left, top, opts, window = undefined) {
+
+ let domutils = getDOMWindowUtils(window);
+
+ let button = event.button || 0;
+ let clickCount = event.clickCount || 1;
+ let modifiers = event.parseModifiers_(event);
- if (utils) {
- var button = aEvent.button || 0;
- var clickCount = aEvent.clickCount || 1;
- var modifiers = _parseModifiers(aEvent);
+ if (("type" in event) && event.type) {
+ domutils.sendMouseEvent(
+ event.type, left, top, button, clickCount, modifiers);
+ } else {
+ domutils.sendMouseEvent(
+ "mousedown", left, top, button, clickCount, modifiers);
+ domutils.sendMouseEvent(
+ "mouseup", left, top, button, clickCount, modifiers);
+ }
+};
- if (("type" in aEvent) && aEvent.type) {
- utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
- }
- else {
- utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers);
- utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers);
- }
- }
-}
-
-// Call synthesizeMouse with coordinates at the center of aTarget.
-function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
-{
- var rect = aTarget.getBoundingClientRect();
- synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
- aWindow);
-}
+/**
+ * Call event.synthesizeMouse with coordinates at the centre of the
+ * target.
+ */
+event.synthesizeMouseAtCenter = function(element, event, window) {
+ let rect = element.getBoundingClientRect();
+ event.synthesizeMouse(
+ element,
+ rect.width / 2,
+ rect.height / 2,
+ event,
+ window);
+};
/**
- * Synthesize a mouse scroll event on a target. The actual client point is determined
- * by taking the aTarget's client box and offseting it by aOffsetX and
- * aOffsetY.
+ * Synthesise a mouse scroll event on a target.
+ *
+ * The actual client point is determined by taking the target's client
+ * box and offseting it by |offsetX| and |offsetY|.
+ *
+ * If the |type| property is specified for the |event| argument, a mouse
+ * scroll event of that type is fired. Otherwise, DOMMouseScroll is used.
*
- * aEvent is an object which may contain the properties:
- * shiftKey, ctrlKey, altKey, metaKey, accessKey, button, type, axis, delta, hasPixels
+ * If the |axis| is specified, it must be one of "horizontal" or
+ * "vertical". If not specified, "vertical" is used.
*
- * If the type is specified, a mouse scroll event of that type is fired. Otherwise,
- * "DOMMouseScroll" is used.
+ * |delta| is the amount to scroll by (can be positive or negative).
+ * It must be specified.
*
- * If the axis is specified, it must be one of "horizontal" or "vertical". If not specified,
- * "vertical" is used.
+ * |hasPixels| specifies whether kHasPixels should be set in the
+ * |scrollFlags|.
*
- * 'delta' is the amount to scroll by (can be positive or negative). It must
- * be specified.
- *
- * 'hasPixels' specifies whether kHasPixels should be set in the scrollFlags.
+ * |isMomentum| specifies whether kIsMomentum should be set in the
+ * |scrollFlags|.
*
- * 'isMomentum' specifies whether kIsMomentum should be set in the scrollFlags.
- *
- * aWindow is optional, and defaults to the current window object.
+ * @param {Element} target
+ * @param {number} offsetY
+ * @param {number} offsetY
+ * @param {Object.<string, ?>} event
+ * Object which may contain the properties shiftKey, ctrlKey, altKey,
+ * metaKey, accessKey, button, type, axis, delta, and hasPixels.
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
*/
-function synthesizeMouseScroll(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
+event.synthesizeMouseScroll = function(
+ target, offsetX, offsetY, ev, window = undefined) {
+
+ let domutils = getDOMWindowUtils(window);
- if (utils) {
- // See nsMouseScrollFlags in nsGUIEvent.h
- const kIsVertical = 0x02;
- const kIsHorizontal = 0x04;
- const kHasPixels = 0x08;
- const kIsMomentum = 0x40;
+ // see nsMouseScrollFlags in nsGUIEvent.h
+ const kIsVertical = 0x02;
+ const kIsHorizontal = 0x04;
+ const kHasPixels = 0x08;
+ const kIsMomentum = 0x40;
- var button = aEvent.button || 0;
- var modifiers = _parseModifiers(aEvent);
+ let button = ev.button || 0;
+ let modifiers = event.parseModifiers_(ev);
- var rect = aTarget.getBoundingClientRect();
-
- var left = rect.left;
- var top = rect.top;
+ let rect = target.getBoundingClientRect();
+ let left = rect.left;
+ let top = rect.top;
- var type = (("type" in aEvent) && aEvent.type) || "DOMMouseScroll";
- var axis = aEvent.axis || "vertical";
- var scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical;
- if (aEvent.hasPixels) {
- scrollFlags |= kHasPixels;
- }
- if (aEvent.isMomentum) {
- scrollFlags |= kIsMomentum;
- }
- utils.sendMouseScrollEvent(type, left + aOffsetX, top + aOffsetY, button,
- scrollFlags, aEvent.delta, modifiers);
+ let type = (("type" in ev) && ev.type) || "DOMMouseScroll";
+ let axis = ev.axis || "vertical";
+ let scrollFlags = (axis == "horizontal") ? kIsHorizontal : kIsVertical;
+ if (ev.hasPixels) {
+ scrollFlags |= kHasPixels;
+ }
+ if (ev.isMomentum) {
+ scrollFlags |= kIsMomentum;
}
-}
-function _computeKeyCodeFromChar(aChar)
-{
- if (aChar.length != 1) {
+ domutils.sendMouseScrollEvent(
+ type,
+ left + offsetX,
+ top + offsetY,
+ button,
+ scrollFlags,
+ ev.delta,
+ modifiers);
+};
+
+function computeKeyCodeFromChar_(char) {
+ if (char.length != 1) {
return 0;
}
- const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent;
- if (aChar >= 'a' && aChar <= 'z') {
- return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
+
+ if (char >= "a" && char <= "z") {
+ return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "a".charCodeAt(0);
}
- if (aChar >= 'A' && aChar <= 'Z') {
- return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
+ if (char >= "A" && char <= "Z") {
+ return Ci.nsIDOMKeyEvent.DOM_VK_A + char.charCodeAt(0) - "A".charCodeAt(0);
}
- if (aChar >= '0' && aChar <= '9') {
- return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
+ if (char >= "0" && char <= "9") {
+ return Ci.nsIDOMKeyEvent.DOM_VK_0 + char.charCodeAt(0) - "0".charCodeAt(0);
}
+
// returns US keyboard layout's keycode
- switch (aChar) {
- case '~':
- case '`':
- return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
- case '!':
- return nsIDOMKeyEvent.DOM_VK_1;
- case '@':
- return nsIDOMKeyEvent.DOM_VK_2;
- case '#':
- return nsIDOMKeyEvent.DOM_VK_3;
- case '$':
- return nsIDOMKeyEvent.DOM_VK_4;
- case '%':
- return nsIDOMKeyEvent.DOM_VK_5;
- case '^':
- return nsIDOMKeyEvent.DOM_VK_6;
- case '&':
- return nsIDOMKeyEvent.DOM_VK_7;
- case '*':
- return nsIDOMKeyEvent.DOM_VK_8;
- case '(':
- return nsIDOMKeyEvent.DOM_VK_9;
- case ')':
- return nsIDOMKeyEvent.DOM_VK_0;
- case '-':
- case '_':
- return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
- case '+':
- case '=':
- return nsIDOMKeyEvent.DOM_VK_EQUALS;
- case '{':
- case '[':
- return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
- case '}':
- case ']':
- return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
- case '|':
- case '\\':
- return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
- case ':':
- case ';':
- return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
- case '\'':
- case '"':
- return nsIDOMKeyEvent.DOM_VK_QUOTE;
- case '<':
- case ',':
- return nsIDOMKeyEvent.DOM_VK_COMMA;
- case '>':
- case '.':
- return nsIDOMKeyEvent.DOM_VK_PERIOD;
- case '?':
- case '/':
- return nsIDOMKeyEvent.DOM_VK_SLASH;
- case '\n':
- return nsIDOMKeyEvent.DOM_VK_RETURN;
+ switch (char) {
+ case "~":
+ case "`":
+ return Ci.nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
+
+ case "!":
+ return Ci.nsIDOMKeyEvent.DOM_VK_1;
+
+ case "@":
+ return Ci.nsIDOMKeyEvent.DOM_VK_2;
+
+ case "#":
+ return Ci.nsIDOMKeyEvent.DOM_VK_3;
+
+ case "$":
+ return Ci.nsIDOMKeyEvent.DOM_VK_4;
+
+ case "%":
+ return Ci.nsIDOMKeyEvent.DOM_VK_5;
+
+ case "^":
+ return Ci.nsIDOMKeyEvent.DOM_VK_6;
+
+ case "&":
+ return Ci.nsIDOMKeyEvent.DOM_VK_7;
+
+ case "*":
+ return Ci.nsIDOMKeyEvent.DOM_VK_8;
+
+ case "(":
+ return Ci.nsIDOMKeyEvent.DOM_VK_9;
+
+ case ")":
+ return Ci.nsIDOMKeyEvent.DOM_VK_0;
+
+ case "-":
+ case "_":
+ return Ci.nsIDOMKeyEvent.DOM_VK_SUBTRACT;
+
+ case "+":
+ case "=":
+ return Ci.nsIDOMKeyEvent.DOM_VK_EQUALS;
+
+ case "{":
+ case "[":
+ return Ci.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
+
+ case "}":
+ case "]":
+ return Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
+
+ case "|":
+ case "\\":
+ return Ci.nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
+
+ case ":":
+ case ";":
+ return Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON;
+
+ case "'":
+ case "\"":
+ return Ci.nsIDOMKeyEvent.DOM_VK_QUOTE;
+
+ case "<":
+ case ",":
+ return Ci.nsIDOMKeyEvent.DOM_VK_COMMA;
+
+ case ">":
+ case ".":
+ return Ci.nsIDOMKeyEvent.DOM_VK_PERIOD;
+
+ case "?":
+ case "/":
+ return Ci.nsIDOMKeyEvent.DOM_VK_SLASH;
+
+ case "\n":
+ return Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
+
default:
return 0;
}
}
/**
- * isKeypressFiredKey() returns TRUE if the given key should cause keypress
- * event when widget handles the native key event. Otherwise, FALSE.
+ * Returns true if the given key should cause keypress event when widget
+ * handles the native key event. Otherwise, false.
*
- * aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
- * name begins with "VK_", or a character.
+ * The key code should be one of consts of nsIDOMKeyEvent.DOM_VK_*,
+ * or a key name begins with "VK_", or a character.
*/
-function isKeypressFiredKey(aDOMKeyCode)
-{
- const KeyEvent = Components.interfaces.nsIDOMKeyEvent;
- if (typeof(aDOMKeyCode) == "string") {
- if (aDOMKeyCode.indexOf("VK_") == 0) {
- aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
- if (!aDOMKeyCode) {
- throw "Unknown key: " + aDOMKeyCode;
+event.isKeypressFiredKey = function(key) {
+ if (typeof key == "string") {
+ if (key.indexOf("VK_") === 0) {
+ key = Ci.nsIDOMKeyEvent["DOM_" + key];
+ if (!key) {
+ throw new TypeError("Unknown key: " + key);
}
+
+ // if key generates a character, it must cause a keypress event
} else {
- // If the key generates a character, it must cause a keypress event.
return true;
}
}
- switch (aDOMKeyCode) {
- case KeyEvent.DOM_VK_SHIFT:
- case KeyEvent.DOM_VK_CONTROL:
- case KeyEvent.DOM_VK_ALT:
- case KeyEvent.DOM_VK_CAPS_LOCK:
- case KeyEvent.DOM_VK_NUM_LOCK:
- case KeyEvent.DOM_VK_SCROLL_LOCK:
- case KeyEvent.DOM_VK_META:
+
+ switch (key) {
+ case Ci.nsIDOMKeyEvent.DOM_VK_SHIFT:
+ case Ci.nsIDOMKeyEvent.DOM_VK_CONTROL:
+ case Ci.nsIDOMKeyEvent.DOM_VK_ALT:
+ case Ci.nsIDOMKeyEvent.DOM_VK_CAPS_LOCK:
+ case Ci.nsIDOMKeyEvent.DOM_VK_NUM_LOCK:
+ case Ci.nsIDOMKeyEvent.DOM_VK_SCROLL_LOCK:
+ case Ci.nsIDOMKeyEvent.DOM_VK_META:
return false;
+
default:
return true;
}
+};
+
+/**
+ * Synthesise a key event.
+ *
+ * It is targeted at whatever would be targeted by an actual keypress
+ * by the user, typically the focused element.
+ *
+ * @param {string} key
+ * Key to synthesise. Should either be a character or a key code
+ * starting with "VK_" such as VK_RETURN.
+ * @param {Object.<string, ?>} event
+ * Object which may contain the properties shiftKey, ctrlKey, altKey,
+ * metaKey, accessKey, type. If the type is specified, a key event
+ * of that type is fired. Otherwise, a keydown, a keypress, and then a
+ * keyup event are fired in sequence.
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
+ *
+ * @throws {TypeError}
+ * If unknown key.
+ */
+event.synthesizeKey = function(key, ev, window = undefined) {
+ let domutils = getDOMWindowUtils(window);
+
+ let keyCode = 0;
+ let charCode = 0;
+ if (key.indexOf("VK_") === 0) {
+ keyCode = Ci.nsIDOMKeyEvent["DOM_" + key];
+ if (!keyCode) {
+ throw new TypeError("Unknown key: " + key);
+ }
+ } else {
+ charCode = key.charCodeAt(0);
+ keyCode = computeKeyCodeFromChar_(key.charAt(0));
+ }
+
+ let modifiers = event.parseModifiers_(ev);
+
+ // send keydown + (optional) keypress + keyup events
+ if (!("type" in ev) || !ev.type) {
+ let keyDownDefaultHappened = domutils.sendKeyEvent(
+ "keydown", keyCode, 0, modifiers);
+ if (event.isKeypressFiredKey(keyCode)) {
+ domutils.sendKeyEvent(
+ "keypress",
+ charCode ? 0 : keyCode,
+ charCode,
+ modifiers,
+ !keyDownDefaultHappened);
+ }
+ domutils.sendKeyEvent("keyup", keyCode, 0, modifiers);
+
+ // send standalone keypress event
+ } else if (ev.type == "keypress") {
+ domutils.sendKeyEvent(
+ ev.type,
+ charCode ? 0 : keyCode,
+ charCode,
+ modifiers);
+
+ // send other standalone event than keypress
+ } else {
+ domutils.sendKeyEvent(ev.type, keyCode, 0, modifiers);
+ }
+};
+
+/**
+ * Indicate that an event with an original target and type is expected
+ * to be fired, or not expected to be fired.
+ */
+function expectEvent_(expectedTarget, expectedEvent, testName) {
+ if (!expectedTarget || !expectedEvent) {
+ return null;
+ }
+
+ seenEvent = false;
+
+ let type;
+ if (expectedEvent.charAt(0) == "!") {
+ type = expectedEvent.substring(1);
+ } else {
+ type = expectedEvent;
+ }
+
+ let handler = ev => {
+ let pass = (!seenEvent && ev.originalTarget == expectedTarget && ev.type == type);
+ is(pass, true, `${testName} ${type} event target ${seenEvent ? "twice" : ""}`);
+ seenEvent = true;
+ };
+
+ expectedTarget.addEventListener(type, handler, false);
+ return handler;
}
/**
- * Synthesize a key event. It is targeted at whatever would be targeted by an
- * actual keypress by the user, typically the focused element.
+ * Check if the event was fired or not. The provided event handler will
+ * be removed.
+ */
+function checkExpectedEvent_(
+ expectedTarget, expectedEvent, eventHandler, testName) {
+
+ if (eventHandler) {
+ let expectEvent = (expectedEvent.charAt(0) != "!");
+ let type = expectEvent;
+ if (!type) {
+ type = expectedEvent.substring(1);
+ }
+ expectedTarget.removeEventListener(type, eventHandler, false);
+
+ let desc = `${type} event`;
+ if (!expectEvent) {
+ desc += " not";
+ }
+ is(seenEvent, expectEvent, `${testName} ${desc} fired`);
+ }
+
+ seenEvent = false;
+}
+
+/**
+ * Similar to event.synthesizeMouse except that a test is performed to
+ * see if an event is fired at the right target as a result.
*
- * aKey should be either a character or a keycode starting with VK_ such as
- * VK_RETURN.
+ * To test that an event is not fired, use an expected type preceded by
+ * an exclamation mark, such as "!select". This might be used to test that
+ * a click on a disabled element doesn't fire certain events for instance.
*
- * aEvent is an object which may contain the properties:
- * shiftKey, ctrlKey, altKey, metaKey, accessKey, type
+ * @param {Element} target
+ * Synthesise the mouse event on this target.
+ * @param {number} offsetX
+ * Horizontal offset from the target's bounding box.
+ * @param {number} offsetY
+ * Vertical offset from the target's bounding box.
+ * @param {Object.<string, ?>} ev
+ * Object which may contain the properties shiftKey, ctrlKey, altKey,
+ * metaKey, accessKey, type.
+ * @param {Element} expectedTarget
+ * Expected originalTarget of the event.
+ * @param {DOMEvent} expectedEvent
+ * Expected type of the event, such as "select".
+ * @param {string} testName
+ * Test name when outputing results.
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
+ */
+event.synthesizeMouseExpectEvent = function(
+ target, offsetX, offsetY, ev, expectedTarget, expectedEvent,
+ testName, window = undefined) {
+
+ let eventHandler = expectEvent_(
+ expectedTarget,
+ expectedEvent,
+ testName);
+ event.synthesizeMouse(target, offsetX, offsetY, ev, window);
+ checkExpectedEvent_(
+ expectedTarget,
+ expectedEvent,
+ eventHandler,
+ testName);
+};
+
+/**
+ * Similar to synthesizeKey except that a test is performed to see if
+ * an event is fired at the right target as a result.
*
- * If the type is specified, a key event of that type is fired. Otherwise,
- * a keydown, a keypress and then a keyup event are fired in sequence.
+ * @param {string} key
+ * Key to synthesise.
+ * @param {Object.<string, ?>} ev
+ * Object which may contain the properties shiftKey, ctrlKey, altKey,
+ * metaKey, accessKey, type.
+ * @param {Element} expectedTarget
+ * Expected originalTarget of the event.
+ * @param {DOMEvent} expectedEvent
+ * Expected type of the event, such as "select".
+ * @param {string} testName
+ * Test name when outputing results
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
+ *
+ * To test that an event is not fired, use an expected type preceded by an
+ * exclamation mark, such as "!select".
*
* aWindow is optional, and defaults to the current window object.
*/
-function synthesizeKey(aKey, aEvent, aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
- if (utils) {
- var keyCode = 0, charCode = 0;
- if (aKey.indexOf("VK_") == 0) {
- keyCode = KeyEvent["DOM_" + aKey];
- if (!keyCode) {
- throw "Unknown key: " + aKey;
- }
- } else {
- charCode = aKey.charCodeAt(0);
- keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
- }
-
- var modifiers = _parseModifiers(aEvent);
-
- if (!("type" in aEvent) || !aEvent.type) {
- // Send keydown + (optional) keypress + keyup events.
- var keyDownDefaultHappened =
- utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
- if (isKeypressFiredKey(keyCode)) {
- utils.sendKeyEvent("keypress", charCode ? 0 : keyCode, charCode,
- modifiers, !keyDownDefaultHappened);
- }
- utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
- } else if (aEvent.type == "keypress") {
- // Send standalone keypress event.
- utils.sendKeyEvent(aEvent.type, charCode ? 0 : keyCode,
- charCode, modifiers);
- } else {
- // Send other standalone event than keypress.
- utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers);
- }
- }
-}
-
-var _gSeenEvent = false;
-
-/**
- * Indicate that an event with an original target of aExpectedTarget and
- * a type of aExpectedEvent is expected to be fired, or not expected to
- * be fired.
- */
-function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
-{
- if (!aExpectedTarget || !aExpectedEvent)
- return null;
-
- _gSeenEvent = false;
-
- var type = (aExpectedEvent.charAt(0) == "!") ?
- aExpectedEvent.substring(1) : aExpectedEvent;
- var eventHandler = function(event) {
- var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
- event.type == type);
- is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
- _gSeenEvent = true;
- };
-
- aExpectedTarget.addEventListener(type, eventHandler, false);
- return eventHandler;
-}
+event.synthesizeKeyExpectEvent = function(
+ key, ev, expectedTarget, expectedEvent, testName,
+ window = undefined) {
-/**
- * Check if the event was fired or not. The event handler aEventHandler
- * will be removed.
- */
-function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
-{
- if (aEventHandler) {
- var expectEvent = (aExpectedEvent.charAt(0) != "!");
- var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
- aExpectedTarget.removeEventListener(type, aEventHandler, false);
- var desc = type + " event";
- if (!expectEvent)
- desc += " not";
- is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
- }
-
- _gSeenEvent = false;
-}
-
-/**
- * Similar to synthesizeMouse except that a test is performed to see if an
- * event is fired at the right target as a result.
- *
- * aExpectedTarget - the expected originalTarget of the event.
- * aExpectedEvent - the expected type of the event, such as 'select'.
- * aTestName - the test name when outputing results
- *
- * To test that an event is not fired, use an expected type preceded by an
- * exclamation mark, such as '!select'. This might be used to test that a
- * click on a disabled element doesn't fire certain events for instance.
- *
- * aWindow is optional, and defaults to the current window object.
- */
-function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
- aExpectedTarget, aExpectedEvent, aTestName,
- aWindow)
-{
- var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
- synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
- _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
-}
-
-/**
- * Similar to synthesizeKey except that a test is performed to see if an
- * event is fired at the right target as a result.
- *
- * aExpectedTarget - the expected originalTarget of the event.
- * aExpectedEvent - the expected type of the event, such as 'select'.
- * aTestName - the test name when outputing results
- *
- * To test that an event is not fired, use an expected type preceded by an
- * exclamation mark, such as '!select'.
- *
- * aWindow is optional, and defaults to the current window object.
- */
-function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
- aTestName, aWindow)
-{
- var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
- synthesizeKey(key, aEvent, aWindow);
- _checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
-}
-
-function disableNonTestMouseEvents(aDisable)
-{
- var domutils = _getDOMWindowUtils();
- domutils.disableNonTestMouseEvents(aDisable);
-}
-
-function _getDOMWindowUtils(aWindow)
-{
- if (!aWindow) {
- aWindow = window;
- }
-
- //TODO: this is assuming we are in chrome space
- return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
- getInterface(Components.interfaces.nsIDOMWindowUtils);
-}
-
-// Must be synchronized with nsIDOMWindowUtils.
-const COMPOSITION_ATTR_RAWINPUT = 0x02;
-const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
-const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
-const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
+ let eventHandler = expectEvent_(
+ expectedTarget,
+ expectedEvent,
+ testName);
+ event.synthesizeKey(key, ev, window);
+ checkExpectedEvent_(
+ expectedTarget,
+ expectedEvent,
+ eventHandler,
+ testName);
+};
/**
* Synthesize a composition event.
*
- * @param aEvent The composition event information. This must
- * have |type| member. The value must be
- * "compositionstart", "compositionend" or
- * "compositionupdate".
- * And also this may have |data| and |locale| which
- * would be used for the value of each property of
- * the composition event. Note that the data would
- * be ignored if the event type were
- * "compositionstart".
- * @param aWindow Optional (If null, current |window| will be used)
+ * @param {DOMEvent} ev
+ * The composition event information. This must have |type|
+ * member. The value must be "compositionstart", "compositionend" or
+ * "compositionupdate". And also this may have |data| and |locale|
+ * which would be used for the value of each property of the
+ * composition event. Note that the data would be ignored if the
+ * event type were "compositionstart".
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
*/
-function synthesizeComposition(aEvent, aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
- if (!utils) {
- return;
- }
+event.synthesizeComposition = function(ev, window = undefined) {
+ let domutils = getDOMWindowUtils(window);
+ domutils.sendCompositionEvent(ev.type, ev.data || "", ev.locale || "");
+};
- utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "",
- aEvent.locale ? aEvent.locale : "");
-}
/**
* Synthesize a text event.
*
- * @param aEvent The text event's information, this has |composition|
- * and |caret| members. |composition| has |string| and
- * |clauses| members. |clauses| must be array object. Each
- * object has |length| and |attr|. And |caret| has |start| and
- * |length|. See the following tree image.
+ * The text event's information, this has |composition| and |caret|
+ * members. |composition| has |string| and |clauses| members. |clauses|
+ * must be array object. Each object has |length| and |attr|.
+ * And |caret| has |start| and |length|. See the following tree image.
*
- * aEvent
- * +-- composition
- * | +-- string
- * | +-- clauses[]
- * | +-- length
- * | +-- attr
- * +-- caret
- * +-- start
- * +-- length
+ * ev
+ * +-- composition
+ * | +-- string
+ * | +-- clauses[]
+ * | +-- length
+ * | +-- attr
+ * +-- caret
+ * +-- start
+ * +-- length
*
- * Set the composition string to |composition.string|. Set its
- * clauses information to the |clauses| array.
+ * Set the composition string to |composition.string|. Set its clauses
+ * information to the |clauses| array.
*
- * When it's composing, set the each clauses' length to the
- * |composition.clauses[n].length|. The sum of the all length
- * values must be same as the length of |composition.string|.
- * Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
- * |composition.clauses[n].attr|.
+ * When it's composing, set the each clauses' length
+ * to the |composition.clauses[n].length|. The sum
+ * of the all length values must be same as the length of
+ * |composition.string|. Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
+ * |composition.clauses[n].attr|.
*
- * When it's not composing, set 0 to the
- * |composition.clauses[0].length| and
- * |composition.clauses[0].attr|.
+ * When it's not composing, set 0 to the |composition.clauses[0].length|
+ * and |composition.clauses[0].attr|.
*
- * Set caret position to the |caret.start|. It's offset from
- * the start of the composition string. Set caret length to
- * |caret.length|. If it's larger than 0, it should be wide
- * caret. However, current nsEditor doesn't support wide
- * caret, therefore, you should always set 0 now.
+ * Set caret position to the |caret.start|. Its offset from the start of
+ * the composition string. Set caret length to |caret.length|. If it's
+ * larger than 0, it should be wide caret. However, current nsEditor
+ * doesn't support wide caret, therefore, you should always set 0 now.
*
- * @param aWindow Optional (If null, current |window| will be used)
+ * @param {Object.<string, ?>} ev
+ * The text event's information,
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
*/
-function synthesizeText(aEvent, aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
- if (!utils) {
+event.synthesizeText = function(ev, window = undefined) {
+ let domutils = getDOMWindowUtils(window);
+
+ if (!ev.composition ||
+ !ev.composition.clauses ||
+ !ev.composition.clauses[0]) {
return;
}
- if (!aEvent.composition || !aEvent.composition.clauses ||
- !aEvent.composition.clauses[0]) {
- return;
- }
-
- var firstClauseLength = aEvent.composition.clauses[0].length;
- var firstClauseAttr = aEvent.composition.clauses[0].attr;
- var secondClauseLength = 0;
- var secondClauseAttr = 0;
- var thirdClauseLength = 0;
- var thirdClauseAttr = 0;
- if (aEvent.composition.clauses[1]) {
- secondClauseLength = aEvent.composition.clauses[1].length;
- secondClauseAttr = aEvent.composition.clauses[1].attr;
- if (aEvent.composition.clauses[2]) {
- thirdClauseLength = aEvent.composition.clauses[2].length;
- thirdClauseAttr = aEvent.composition.clauses[2].attr;
+ let firstClauseLength = ev.composition.clauses[0].length;
+ let firstClauseAttr = ev.composition.clauses[0].attr;
+ let secondClauseLength = 0;
+ let secondClauseAttr = 0;
+ let thirdClauseLength = 0;
+ let thirdClauseAttr = 0;
+ if (ev.composition.clauses[1]) {
+ secondClauseLength = ev.composition.clauses[1].length;
+ secondClauseAttr = ev.composition.clauses[1].attr;
+ if (event.composition.clauses[2]) {
+ thirdClauseLength = ev.composition.clauses[2].length;
+ thirdClauseAttr = ev.composition.clauses[2].attr;
}
}
- var caretStart = -1;
- var caretLength = 0;
- if (aEvent.caret) {
- caretStart = aEvent.caret.start;
- caretLength = aEvent.caret.length;
+ let caretStart = -1;
+ let caretLength = 0;
+ if (event.caret) {
+ caretStart = ev.caret.start;
+ caretLength = ev.caret.length;
}
- utils.sendTextEvent(aEvent.composition.string,
- firstClauseLength, firstClauseAttr,
- secondClauseLength, secondClauseAttr,
- thirdClauseLength, thirdClauseAttr,
- caretStart, caretLength);
-}
+ domutils.sendTextEvent(
+ ev.composition.string,
+ firstClauseLength,
+ firstClauseAttr,
+ secondClauseLength,
+ secondClauseAttr,
+ thirdClauseLength,
+ thirdClauseAttr,
+ caretStart,
+ caretLength);
+};
/**
* Synthesize a query selected text event.
*
- * @param aWindow Optional (If null, current |window| will be used)
- * @return An nsIQueryContentEventResult object. If this failed,
- * the result might be null.
+ * @param {Window=}
+ * Window object. Defaults to the current window.
+ *
+ * @return {(nsIQueryContentEventResult|null)}
+ * Event's result, or null if it failed.
*/
-function synthesizeQuerySelectedText(aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
- if (!utils) {
- return null;
- }
-
- return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
-}
+event.synthesizeQuerySelectedText = function(window = undefined) {
+ let domutils = getDOMWindowUtils(window);
+ return domutils.sendQueryContentEvent(
+ domutils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
+};
/**
* Synthesize a selection set event.
*
- * @param aOffset The character offset. 0 means the first character in the
- * selection root.
- * @param aLength The length of the text. If the length is too long,
- * the extra length is ignored.
- * @param aReverse If true, the selection is from |aOffset + aLength| to
- * |aOffset|. Otherwise, from |aOffset| to |aOffset + aLength|.
- * @param aWindow Optional (If null, current |window| will be used)
+ * @param {number} offset
+ * Character offset. 0 means the first character in the selection
+ * root.
+ * @param {number} length
+ * Length of the text. If the length is too long, the extra length
+ * is ignored.
+ * @param {boolean} reverse
+ * If true, the selection is from |aOffset + aLength| to |aOffset|.
+ * Otherwise, from |aOffset| to |aOffset + aLength|.
+ * @param {Window=} window
+ * Window object. Defaults to the current window.
+ *
* @return True, if succeeded. Otherwise false.
*/
-function synthesizeSelectionSet(aOffset, aLength, aReverse, aWindow)
-{
- var utils = _getDOMWindowUtils(aWindow);
- if (!utils) {
- return false;
+event.synthesizeSelectionSet = function(
+ offset, length, reverse, window = undefined) {
+ let domutils = getDOMWindowUtils(window);
+ return domutils.sendSelectionSetEvent(offset, length, reverse);
+};
+
+const KEYCODES_LOOKUP = {
+ "VK_SHIFT": "shiftKey",
+ "VK_CONTROL": "ctrlKey",
+ "VK_ALT": "altKey",
+ "VK_META": "metaKey",
+};
+
+const VIRTUAL_KEYCODE_LOOKUP = {
+ "\uE001": "VK_CANCEL",
+ "\uE002": "VK_HELP",
+ "\uE003": "VK_BACK_SPACE",
+ "\uE004": "VK_TAB",
+ "\uE005": "VK_CLEAR",
+ "\uE006": "VK_RETURN",
+ "\uE007": "VK_RETURN",
+ "\uE008": "VK_SHIFT",
+ "\uE009": "VK_CONTROL",
+ "\uE00A": "VK_ALT",
+ "\uE03D": "VK_META",
+ "\uE00B": "VK_PAUSE",
+ "\uE00C": "VK_ESCAPE",
+ "\uE00D": "VK_SPACE", // printable
+ "\uE00E": "VK_PAGE_UP",
+ "\uE00F": "VK_PAGE_DOWN",
+ "\uE010": "VK_END",
+ "\uE011": "VK_HOME",
+ "\uE012": "VK_LEFT",
+ "\uE013": "VK_UP",
+ "\uE014": "VK_RIGHT",
+ "\uE015": "VK_DOWN",
+ "\uE016": "VK_INSERT",
+ "\uE017": "VK_DELETE",
+ "\uE018": "VK_SEMICOLON",
+ "\uE019": "VK_EQUALS",
+ "\uE01A": "VK_NUMPAD0",
+ "\uE01B": "VK_NUMPAD1",
+ "\uE01C": "VK_NUMPAD2",
+ "\uE01D": "VK_NUMPAD3",
+ "\uE01E": "VK_NUMPAD4",
+ "\uE01F": "VK_NUMPAD5",
+ "\uE020": "VK_NUMPAD6",
+ "\uE021": "VK_NUMPAD7",
+ "\uE022": "VK_NUMPAD8",
+ "\uE023": "VK_NUMPAD9",
+ "\uE024": "VK_MULTIPLY",
+ "\uE025": "VK_ADD",
+ "\uE026": "VK_SEPARATOR",
+ "\uE027": "VK_SUBTRACT",
+ "\uE028": "VK_DECIMAL",
+ "\uE029": "VK_DIVIDE",
+ "\uE031": "VK_F1",
+ "\uE032": "VK_F2",
+ "\uE033": "VK_F3",
+ "\uE034": "VK_F4",
+ "\uE035": "VK_F5",
+ "\uE036": "VK_F6",
+ "\uE037": "VK_F7",
+ "\uE038": "VK_F8",
+ "\uE039": "VK_F9",
+ "\uE03A": "VK_F10",
+ "\uE03B": "VK_F11",
+ "\uE03C": "VK_F12",
+};
+
+function getKeyCode(c) {
+ if (c in VIRTUAL_KEYCODE_LOOKUP) {
+ return VIRTUAL_KEYCODE_LOOKUP[c];
}
- return utils.sendSelectionSetEvent(aOffset, aLength, aReverse);
+ return c;
}
+
+event.sendKeyDown = function(keyToSend, modifiers, document) {
+ modifiers.type = "keydown";
+ event.sendSingleKey(keyToSend, modifiers, document);
+ if (["VK_SHIFT", "VK_CONTROL", "VK_ALT", "VK_META"].indexOf(getKeyCode(keyToSend)) < 0) {
+ modifiers.type = "keypress";
+ event.sendSingleKey(keyToSend, modifiers, document);
+ }
+ delete modifiers.type;
+};
+
+event.sendKeyUp = function(keyToSend, modifiers, window = undefined) {
+ modifiers.type = "keyup";
+ event.sendSingleKey(keyToSend, modifiers, window);
+ delete modifiers.type;
+};
+
+event.sendSingleKey = function(keyToSend, modifiers, window = undefined) {
+ let keyCode = getKeyCode(keyToSend);
+ if (keyCode in KEYCODES_LOOKUP) {
+ let modName = KEYCODES_LOOKUP[keyCode];
+ modifiers[modName] = !modifiers[modName];
+ } else if (modifiers.shiftKey) {
+ keyCode = keyCode.toUpperCase();
+ }
+ event.synthesizeKey(keyCode, modifiers, window);
+};
+
+/**
+ * Focus element and, if a textual input field and no previous selection
+ * state exists, move the caret to the end of the input field.
+ *
+ * @param {Element} element
+ * Element to focus.
+ */
+function focusElement(element) {
+ let t = element.type;
+ if (t && (t == "text" || t == "textarea")) {
+ if (element.selectionEnd == 0) {
+ let len = element.value.length;
+ element.setSelectionRange(len, len);
+ }
+ }
+ element.focus();
+}
+
+/**
+ * @param {Array.<string>} keySequence
+ * @param {Element} element
+ * @param {Object.<string, boolean>=} opts
+ * @param {Window=} window
+ */
+event.sendKeysToElement = function(
+ keySequence, element, opts = {}, window = undefined) {
+
+ if (opts.ignoreVisibility || elements.checkVisible(element, window)) {
+ focusElement(element);
+
+ // make Object.<modifier, false> map
+ let modifiers = Object.create(event.Modifiers);
+ for (let modifier in event.Modifiers) {
+ modifiers[modifier] = false;
+ }
+
+ let value = keySequence.join("");
+ for (let i = 0; i < value.length; i++) {
+ let c = value.charAt(i);
+ event.sendSingleKey(c, modifiers, window);
+ }
+
+ } else {
+ throw new ElementNotVisibleError("Element is not visible");
+ }
+};