Bug 1245153 - Convert EventUtils.js to a module; r=automatedtester draft
authorAndreas Tolfsen <ato@mozilla.com>
Wed, 03 Feb 2016 18:47:08 +0000
changeset 332771 6094b11b4325b279dc40b844df327d61a9b90ca6
parent 332770 a94ae7fff63530a4cc4d1875bb4894657834ecb0
child 332772 1dbc1d1b97c6ca3abc3e9b78ce54c0a5a5883f6e
push id11232
push useratolfsen@mozilla.com
push dateSun, 21 Feb 2016 12:02:10 +0000
reviewersautomatedtester
bugs1245153
milestone47.0a1
Bug 1245153 - Convert EventUtils.js to a module; r=automatedtester testing/marionette/sendkeys.js has been merged into the new testing/marionette/event.js module, together with testing/marionette/EventUtils.js. There is a lot of functionality still left in this module that we can probably remove, as it is not in use by Marionette. MozReview-Commit-ID: GrjNuK9VPjp
testing/marionette/EventUtils.js
testing/marionette/event.js
testing/marionette/sendkeys.js
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");
+  }
+};
deleted file mode 100644
--- a/testing/marionette/sendkeys.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- *  Copyright 2007-2009 WebDriver committers
- *  Copyright 2007-2009 Google Inc.
- *  Portions copyright 2012 Software Freedom Conservancy
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("chrome://marionette/content/error.js");
-
-var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
-    .getService(Ci.mozIJSSubScriptLoader);
-
-var utils = {};
-loader.loadSubScript("chrome://marionette/content/EventUtils.js", utils);
-loader.loadSubScript("chrome://marionette/content/ChromeUtils.js", utils);
-
-var keyModifierNames = {
-    "VK_SHIFT": 'shiftKey',
-    "VK_CONTROL": 'ctrlKey',
-    "VK_ALT": 'altKey',
-    "VK_META": 'metaKey'
-};
-
-var keyCodes = {
-  '\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 keyCodes) {
-    return keyCodes[c];
-  }
-  return c;
-};
-
-function sendKeyDown (keyToSend, modifiers, document) {
-  modifiers.type = "keydown";
-  sendSingleKey(keyToSend, modifiers, document);
-  if (["VK_SHIFT", "VK_CONTROL",
-       "VK_ALT", "VK_META"].indexOf(getKeyCode(keyToSend)) == -1) {
-    modifiers.type = "keypress";
-    sendSingleKey(keyToSend, modifiers, document);
-  }
-  delete modifiers.type;
-}
-
-function sendKeyUp (keyToSend, modifiers, document) {
-  modifiers.type = "keyup";
-  sendSingleKey(keyToSend, modifiers, document);
-  delete modifiers.type;
-}
-
-function sendSingleKey (keyToSend, modifiers, document) {
-  let keyCode = getKeyCode(keyToSend);
-  if (keyCode in keyModifierNames) {
-    let modName = keyModifierNames[keyCode];
-    modifiers[modName] = !modifiers[modName];
-  } else if (modifiers.shiftKey) {
-    keyCode = keyCode.toUpperCase();
-  }
-  utils.synthesizeKey(keyCode, modifiers, document);
-}
-
-/**
- * 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} el
- *     Element to focus.
- */
-function focusElement(el) {
-  let t = el.type;
-  if (t && (t == "text" || t == "textarea")) {
-    if (el.selectionEnd == 0) {
-      let len = el.value.length;
-      el.setSelectionRange(len, len);
-    }
-  }
-  el.focus();
-}
-
-function sendKeysToElement(document, element, keysToSend, ignoreVisibility) {
-  if (ignoreVisibility || checkVisible(element)) {
-    focusElement(element);
-
-    let modifiers = {
-      shiftKey: false,
-      ctrlKey: false,
-      altKey: false,
-      metaKey: false
-    };
-    let value = keysToSend.join("");
-    for (var i = 0; i < value.length; i++) {
-      var c = value.charAt(i);
-      sendSingleKey(c, modifiers, document);
-    }
-  } else {
-    throw new ElementNotVisibleError("Element is not visible");
-  }
-};