Rename to action and legacyaction draft
authorMaja Frydrychowicz <mjzffr@gmail.com>
Thu, 29 Sep 2016 17:44:10 -0400
changeset 421890 e2fe899807fe14e4fce5082c3bb0e6e4c72ad8ad
parent 421889 3c81ac3cdd764cc3242c84731588e872edda34c4
child 421891 66558fd8c1418454b3ddac63835fbad4e8324b71
push id31629
push usermjzffr@gmail.com
push dateFri, 07 Oct 2016 02:36:08 +0000
milestone52.0a1
Rename to action and legacyaction MozReview-Commit-ID: Kj1L66xAhDt
testing/marionette/action.js
testing/marionette/driver.js
testing/marionette/jar.mn
testing/marionette/legacyaction.js
testing/marionette/listener.js
testing/marionette/test_action.js
testing/marionette/test_actions.js
testing/marionette/unit.ini
testing/marionette/webdriverAction.js
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -1,477 +1,287 @@
 /* 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/. */
 
+"use strict";
+
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
 
 Cu.import("chrome://marionette/content/element.js");
-Cu.import("chrome://marionette/content/event.js");
+Cu.import("chrome://marionette/content/error.js");
 
-const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
-const DEFAULT_CONTEXT_MENU_DELAY = 750;  // ms
 
-this.EXPORTED_SYMBOLS = ["action"];
+this.EXPORTED_SYMBOLS = ["action", "inputStateMap"];
 
 const logger = Log.repository.getLogger("Marionette");
 
-this.action = {};
+// map between input id (string) and device state for that input source
+// TODO associate with session
+// populated by "dispatch tick actions"?
+this.inputStateMap = new Map();
+
+
+const ACTION_TYPES = new Set([
+  "none",
+  "key",
+  "pointer",
+]);
+
+this.action = {}
+
+// input source: virtual device that has an string id and a source type
+// This doesn't seem to be really used in spec. Each "actionSequence" has id
+// and type already, which is all we really need.
+// action.InputSource = function InputSource(id, type){
+//   this.id = id;
+//   // null, key, pointer
+//   this.type = type;
+// }
+
+action.NullInputState = function NullInputState(){
+  // a named empty object
+}
+
+action.NullInputState.prototype.toString = function(){
+  return '[object NullInputState]';
+}
+
+action.KeyInputState = function KeyInputState(){
+  this.pressed = new Set();
+  this.alt = false;
+  this.shift = false;
+  this.ctrl = false;
+  this.meta = false;
+}
+
+action.KeyInputState.prototype.toString = function(){
+  return '[object KeyInputState]';
+}
+
+action.PointerInputState = function PointerInputState(subtype, primary){
+  this.pressed = new Set();
+  this.subtype = subtype;
+  this.primary = primary;
+  this.x = 0;
+  this.y = 0;
+}
 
-/**
- * Functionality for (single finger) action chains.
- */
-action.Chain = function(checkForInterrupted) {
-  // for assigning unique ids to all touches
-  this.nextTouchId = 1000;
-  // keep track of active Touches
-  this.touchIds = {};
-  // last touch for each fingerId
-  this.lastCoordinates = null;
-  this.isTap = false;
-  this.scrolling = false;
-  // whether to send mouse event
-  this.mouseEventsOnly = false;
-  this.checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+action.PointerInputState.prototype.toString = function(){
+  return '[object PointerInputState]';
+}
+
+const ACTION_INPUT_STATE = {
+  none: action.NullInputState,
+  key: action.KeyInputState,
+  pointer: action.PointerInputState,
+}
+
+action.Action = function Action(id, type, subtype) {
+  // represents action object for actionByTick
+  this.id = id;
+  this.type = type;
+  this.subtype = subtype;
+};
 
-  if (typeof checkForInterrupted == "function") {
-    this.checkForInterrupted = checkForInterrupted;
-  } else {
-    this.checkForInterrupted = () => {};
+// actions: list of action sequences
+action.extractActionChain = function extractActionChain(actions) {
+  // TODO check current browsing context?
+  if (typeof actions === 'undefined' || !Array.isArray(actions)){
+    throw new InvalidArgumentError(`'actions' ${actions} is not an Array`);
+  }
+  let actionsByTick = [];
+  for (let actionSequence of actions){
+    let inputSourceActions = action.processInputSourceActionSequence(actionSequence);
+    for (var i = 0; i < inputSourceActions.length; i++){
+      if (actionsByTick.length < i + 1){
+        // new tick
+        actionsByTick.push([]);
+      }
+      actionsByTick[i].push(inputSourceActions[i]);
+    }
+  }
+  return actionsByTick;
+};
+
+// action_sequence has a list of actionItems for one input source
+action.processInputSourceActionSequence = function processInputSourceActionSequence(
+    actionSequence) {
+  let type = actionSequence.type;
+  if (!ACTION_TYPES.has(type)){
+    throw new InvalidArgumentError(`'actionSequence.type' must be one of
+                                   ${Array.from(ACTION_TYPES)}`);
+  }
+  let id = actionSequence.id;
+  if (typeof id === 'undefined'){
+    id = element.generateUUID();
+  }
+  else if (typeof id !== 'string'){
+    throw new InvalidArgumentError(`'id' ${id} is not a string`);
+  }
+  let actionItems = actionSequence.actions;
+  if (!Array.isArray(actionItems)){
+    throw new InvalidArgumentError(`'actionSequence.actions' ${actionSequence.actions} is not an Array`);
   }
 
-  // determines if we create touch events
-  this.inputSource = null;
-};
-
-action.Chain.prototype.dispatchActions = function(
-    args,
-    touchId,
-    container,
-    seenEls,
-    touchProvider) {
-  // Some touch events code in the listener needs to do ipc, so we can't
-  // share this code across chrome/content.
-  if (touchProvider) {
-    this.touchProvider = touchProvider;
+  let parameters;
+  if (type === "pointer"){
+    parameters = action.processPointerParameters(actionSequence.parameters);
   }
 
-  this.seenEls = seenEls;
-  this.container = container;
-  let commandArray = element.fromJson(
-      args, seenEls, container.frame, container.shadowRoot);
-
-  if (touchId == null) {
-    touchId = this.nextTouchId++;
-  }
-
-  if (!container.frame.document.createTouch) {
-    this.mouseEventsOnly = true;
+  if (inputStateMap.has(id) && !(inputStateMap.get(id) instanceof ACTION_INPUT_STATE[type])){
+    throw new InvalidArgumentError(
+      `${id} is already mapped to ${inputStateMap.get(id)}`);
   }
 
-  let keyModifiers = {
-    shiftKey: false,
-    ctrlKey: false,
-    altKey: false,
-    metaKey: false,
+  let actions = [];
+  for (let actionItem of actionItems){
+    if (Object.prototype.toString.call(actionItem) !== "[object Object]"){
+      throw new InvalidArgumentError(`${actionItem} is not an object`);
+    }
+    let act;
+    switch(type){
+      case("none"):
+        act = action.processNullAction(id, actionItem);
+        break;
+      case("key"):
+        act = action.processKeyAction(id, actionItem);
+        break;
+      case("pointer"):
+        act = action.processPointerAction(id, parameters, actionItem);
+        break;
+    }
+    actions.push(act);
+  }
+  return actions;
+};
+
+action.processPointerParameters = function processPointerParameters(parametersData){
+  let parameters = {
+    pointerType: "mouse",
+    primary: true,
   };
+  if (typeof parametersData === 'undefined'){
+    return parameters;
+  }
+  if (Object.prototype.toString.call(parametersData) !== "[object Object]"){
+      throw new InvalidArgumentError(`parametersData ${parametersData} is not an object`);
+  }
+  let pointerType = parametersData.pointerType;
+  if (typeof pointerType !== 'undefined'){
+    let types = ["mouse", "pen", "touch"];
+    if(!types.includes(pointerType)){
+      throw new InvalidArgumentError(`pointerType must be one of ${types}`);
+    }
+    parameters.pointerType = pointerType;
+  }
+  let primary = parametersData.primary;
+  if (typeof primary !== 'undefined'){
+    if (typeof primary !== 'boolean'){
+      throw new InvalidArgumentError(`primary ${primary} must be a boolean`);
+    }
+    parameters.primary = primary;
+  }
+  return parameters;
+};
 
-  return new Promise(resolve => {
-    this.actions(commandArray, touchId, 0, keyModifiers, resolve);
-  }).catch(this.resetValues);
+action.processNullAction = function processNullAction(id, actionItem){
+  let subtype = actionItem.type;
+  if (subtype !== "pause"){
+    throw new InvalidArgumentError("subtype must be 'pause'");
+  }
+  let act = new action.Action(id, "none", subtype);
+  action.processPauseAction(actionItem, act);
+  return act;
 };
 
-/**
- * This function emit mouse event.
- *
- * @param {Document} doc
- *     Current document.
- * @param {string} type
- *     Type of event to dispatch.
- * @param {number} clickCount
- *     Number of clicks, button notes the mouse button.
- * @param {number} elClientX
- *     X coordinate of the mouse relative to the viewport.
- * @param {number} elClientY
- *     Y coordinate of the mouse relative to the viewport.
- * @param {Object} modifiers
- *     An object of modifier keys present.
- */
-action.Chain.prototype.emitMouseEvent = function(
-    doc,
-    type,
-    elClientX,
-    elClientY,
-    button,
-    clickCount,
-    modifiers) {
-  if (!this.checkForInterrupted()) {
-    logger.debug(`Emitting ${type} mouse event ` +
-        `at coordinates (${elClientX}, ${elClientY}) ` +
-        `relative to the viewport, ` +
-        `button: ${button}, ` +
-        `clickCount: ${clickCount}`);
+action.processKeyAction = function processKeyAction(id, actionItem){
+  let subtype = actionItem.type;
+  let types = ["keyUp", "keyDown", "pause"];
+  if (!types.includes(subtype)){
+    throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
+  }
+  let act = new action.Action(id, "key", subtype);
+  if (subtype === "pause"){
+    action.processPauseAction(actionItem, act);
+    return act;
+  }
+  let key = actionItem.value
+  // what about key codes like arrow, versus unicode chars?
+  if (typeof key !== 'string' || (typeof key === 'string' && key.length !== 1)){
+    throw new InvalidArgumentError("'key' must be a single-character String");
+  }
+  act.value = key;
+  return act;
+};
+
+action.processPointerAction = function processPointerAction(id, parameters, actionItem){
+  let subtype = actionItem.type;
+  let types = ["pause", "pointerUp", "pointerDown", "pointerMove", "pointerCancel"];
+  if (!types.includes(subtype)){
+    throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
+  }
+
+  if (inputStateMap.has(id) && inputStateMap.get(id).subtype !== subtype){
+    throw new InvalidArgumentError(
+      `${id} is already mapped to InputState whose subtype is `
+      + `${inputStateMap.get(id).subtype} not ${subtype}`);
+  }
 
-    let win = doc.defaultView;
-    let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindowUtils);
+  let act = new action.Action(id, "pointer", subtype);
+  if (subtype === "pause"){
+    action.processPauseAction(actionItem, act);
+    return act;
+  }
+  act.pointerType = parameters.pointerType;
+  act.primary = parameters.primary;
+  if (["pointerUp", "pointerDown"].includes(subtype)){
+    action.processPointerUpDownAction(actionItem, act);
+  }
+  if (subtype == "pointerMove"){
+    action.processPointerMoveAction(actionItem, act);
+  }
+  return act;
+};
 
-    let mods;
-    if (typeof modifiers != "undefined") {
-      mods = event.parseModifiers_(modifiers);
-    } else {
-      mods = 0;
-    }
+action.processPauseAction = function processPauseAction(actionItem, act){
+  act.duration = action.validatePositiveNum(actionItem.duration, 'duration');
+};
 
-    domUtils.sendMouseEvent(
-        type,
-        elClientX,
-        elClientY,
-        button || 0,
-        clickCount || 1,
-        mods,
-        false,
-        0,
-        this.inputSource);
+action.processPointerUpDownAction = function processPointerUpDownAction(
+    actionItem,
+    act){
+  act.button = action.validatePositiveNum(actionItem.button, 'button');
+};
+
+action.processPointerMoveAction = function processPointerMoveAction(actionItem, act){
+  act.duration = action.validatePositiveNum(actionItem.duration, 'duration');
+  let webElement = actionItem.element;
+  let isElement = function(el){
+    let properties = Object.getOwnPropertyNames(el);
+    return properties.includes(element.Key) || properties.includes(element.LegacyKey);
+  }
+  if (typeof webElement !== "undefined" && !isElement(webElement)){
+    throw new InvalidArgumentError(
+      "'actionItem.duration' must be an Object that represents a web element");
+  }
+  act.element = webElement;
+
+  act.x = actionItem.x;
+  if (typeof act.x !== "undefined"){
+    act.x = action.validatePositiveNum(actionItem.x, 'x');
+  }
+  act.y = actionItem.y;
+  if (typeof act.y !== "undefined"){
+    act.y = action.validatePositiveNum(actionItem.y, 'y');
   }
 };
 
-/**
- * Reset any persisted values after a command completes.
- */
-action.Chain.prototype.resetValues = function() {
-  this.container = null;
-  this.seenEls = null;
-  this.touchProvider = null;
-  this.mouseEventsOnly = false;
-};
-
-/**
- * Emit events for each action in the provided chain.
- *
- * To emit touch events for each finger, one might send a [["press", id],
- * ["wait", 5], ["release"]] chain.
- *
- * @param {Array.<Array<?>>} chain
- *     A multi-dimensional array of actions.
- * @param {Object.<string, number>} touchId
- *     Represents the finger ID.
- * @param {number} i
- *     Keeps track of the current action of the chain.
- * @param {Object.<string, boolean>} keyModifiers
- *     Keeps track of keyDown/keyUp pairs through an action chain.
- * @param {function(?)} cb
- *     Called on success.
- *
- * @return {Object.<string, number>}
- *     Last finger ID, or an empty object.
- */
-action.Chain.prototype.actions = function(chain, touchId, i, keyModifiers, cb) {
-  if (i == chain.length) {
-    cb(touchId || null);
-    this.resetValues();
-    return;
-  }
-
-  let pack = chain[i];
-  let command = pack[0];
-  let el;
-  let c;
-  i++;
-
-  if (["press", "wait", "keyDown", "keyUp", "click"].indexOf(command) == -1) {
-    // if mouseEventsOnly, then touchIds isn't used
-    if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
-      this.resetValues();
-      throw new WebDriverError("Element has not been pressed");
-    }
+// helpers
+action.validatePositiveNum = function validatePositiveNum(value, name){
+  let v = parseInt(value);
+  if (isNaN(v) || v < 0){
+    throw new InvalidArgumentError(`Value of '${name}' should be integer >= 0`);
   }
-
-  switch (command) {
-    case "keyDown":
-      event.sendKeyDown(pack[1], keyModifiers, this.container.frame);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-
-    case "keyUp":
-      event.sendKeyUp(pack[1], keyModifiers, this.container.frame);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-
-    case "click":
-      el = this.seenEls.get(pack[1], this.container);
-      let button = pack[2];
-      let clickCount = pack[3];
-      c = element.coordinates(el);
-      this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount, keyModifiers);
-      if (button == 2) {
-        this.emitMouseEvent(el.ownerDocument, "contextmenu", c.x, c.y,
-            button, clickCount, keyModifiers);
-      }
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-
-    case "press":
-      if (this.lastCoordinates) {
-        this.generateEvents(
-            "cancel",
-            this.lastCoordinates[0],
-            this.lastCoordinates[1],
-            touchId,
-            null,
-            keyModifiers);
-        this.resetValues();
-        throw new WebDriverError(
-            "Invalid Command: press cannot follow an active touch event");
-      }
-
-      // look ahead to check if we're scrolling,
-      // needed for APZ touch dispatching
-      if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) {
-        this.scrolling = true;
-      }
-      el = this.seenEls.get(pack[1], this.container);
-      c = element.coordinates(el, pack[2], pack[3]);
-      touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-
-    case "release":
-      this.generateEvents(
-          "release",
-          this.lastCoordinates[0],
-          this.lastCoordinates[1],
-          touchId,
-          null,
-          keyModifiers);
-      this.actions(chain, null, i, keyModifiers, cb);
-      this.scrolling =  false;
-      break;
-
-    case "move":
-      el = this.seenEls.get(pack[1], this.container);
-      c = element.coordinates(el);
-      this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-
-    case "moveByOffset":
-      this.generateEvents(
-          "move",
-          this.lastCoordinates[0] + pack[1],
-          this.lastCoordinates[1] + pack[2],
-          touchId,
-          null,
-          keyModifiers);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-
-    case "wait":
-      if (pack[1] != null) {
-        let time = pack[1] * 1000;
-
-        // standard waiting time to fire contextmenu
-        let standard = Preferences.get(
-            CONTEXT_MENU_DELAY_PREF,
-            DEFAULT_CONTEXT_MENU_DELAY);
-
-        if (time >= standard && this.isTap) {
-          chain.splice(i, 0, ["longPress"], ["wait", (time - standard) / 1000]);
-          time = standard;
-        }
-        this.checkTimer.initWithCallback(
-            () => this.actions(chain, touchId, i, keyModifiers, cb),
-            time, Ci.nsITimer.TYPE_ONE_SHOT);
-      } else {
-        this.actions(chain, touchId, i, keyModifiers, cb);
-      }
-      break;
-
-    case "cancel":
-      this.generateEvents(
-          "cancel",
-          this.lastCoordinates[0],
-          this.lastCoordinates[1],
-          touchId,
-          null,
-          keyModifiers);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      this.scrolling = false;
-      break;
-
-    case "longPress":
-      this.generateEvents(
-          "contextmenu",
-          this.lastCoordinates[0],
-          this.lastCoordinates[1],
-          touchId,
-          null,
-          keyModifiers);
-      this.actions(chain, touchId, i, keyModifiers, cb);
-      break;
-  }
-};
-
-/**
- * Given an element and a pair of coordinates, returns an array of the
- * form [clientX, clientY, pageX, pageY, screenX, screenY].
- */
-action.Chain.prototype.getCoordinateInfo = function(el, corx, cory) {
-  let win = el.ownerDocument.defaultView;
-  return [
-    corx, // clientX
-    cory, // clientY
-    corx + win.pageXOffset, // pageX
-    cory + win.pageYOffset, // pageY
-    corx + win.mozInnerScreenX, // screenX
-    cory + win.mozInnerScreenY // screenY
-  ];
-};
-
-/**
- * @param {number} x
- *     X coordinate of the location to generate the event that is relative
- *     to the viewport.
- * @param {number} y
- *     Y coordinate of the location to generate the event that is relative
- *     to the viewport.
- */
-action.Chain.prototype.generateEvents = function(
-    type, x, y, touchId, target, keyModifiers) {
-  this.lastCoordinates = [x, y];
-  let doc = this.container.frame.document;
-
-  switch (type) {
-    case "tap":
-      if (this.mouseEventsOnly) {
-        this.mouseTap(
-            touch.target.ownerDocument,
-            touch.clientX,
-            touch.clientY,
-            null,
-            null,
-            keyModifiers);
-      } else {
-        touchId = this.nextTouchId++;
-        let touch = this.touchProvider.createATouch(target, x, y, touchId);
-        this.touchProvider.emitTouchEvent("touchstart", touch);
-        this.touchProvider.emitTouchEvent("touchend", touch);
-        this.mouseTap(
-            touch.target.ownerDocument,
-            touch.clientX,
-            touch.clientY,
-            null,
-            null,
-            keyModifiers);
-      }
-      this.lastCoordinates = null;
-      break;
-
-    case "press":
-      this.isTap = true;
-      if (this.mouseEventsOnly) {
-        this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
-        this.emitMouseEvent(doc, "mousedown", x, y, null, null, keyModifiers);
-      } else {
-        touchId = this.nextTouchId++;
-        let touch = this.touchProvider.createATouch(target, x, y, touchId);
-        this.touchProvider.emitTouchEvent("touchstart", touch);
-        this.touchIds[touchId] = touch;
-        return touchId;
-      }
-      break;
-
-    case "release":
-      if (this.mouseEventsOnly) {
-        let [x, y] = this.lastCoordinates;
-        this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
-      } else {
-        let touch = this.touchIds[touchId];
-        let [x, y] = this.lastCoordinates;
-
-        touch = this.touchProvider.createATouch(touch.target, x, y, touchId);
-        this.touchProvider.emitTouchEvent("touchend", touch);
-
-        if (this.isTap) {
-          this.mouseTap(
-              touch.target.ownerDocument,
-              touch.clientX,
-              touch.clientY,
-              null,
-              null,
-              keyModifiers);
-        }
-        delete this.touchIds[touchId];
-      }
-
-      this.isTap = false;
-      this.lastCoordinates = null;
-      break;
-
-    case "cancel":
-      this.isTap = false;
-      if (this.mouseEventsOnly) {
-        let [x, y] = this.lastCoordinates;
-        this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
-      } else {
-        this.touchProvider.emitTouchEvent("touchcancel", this.touchIds[touchId]);
-        delete this.touchIds[touchId];
-      }
-      this.lastCoordinates = null;
-      break;
-
-    case "move":
-      this.isTap = false;
-      if (this.mouseEventsOnly) {
-        this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
-      } else {
-        let touch = this.touchProvider.createATouch(
-            this.touchIds[touchId].target, x, y, touchId);
-        this.touchIds[touchId] = touch;
-        this.touchProvider.emitTouchEvent("touchmove", touch);
-      }
-      break;
-
-    case "contextmenu":
-      this.isTap = false;
-      let event = this.container.frame.document.createEvent("MouseEvents");
-      if (this.mouseEventsOnly) {
-        target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]);
-      } else {
-        target = this.touchIds[touchId].target;
-      }
-
-      let [clientX, clientY, pageX, pageY, screenX, screenY] =
-          this.getCoordinateInfo(target, x, y);
-
-      event.initMouseEvent(
-          "contextmenu",
-          true,
-          true,
-          target.ownerDocument.defaultView,
-          1,
-          screenX,
-          screenY,
-          clientX,
-          clientY,
-          false,
-          false,
-          false,
-          false,
-          0,
-          null);
-      target.dispatchEvent(event);
-      break;
-
-    default:
-      throw new WebDriverError("Unknown event type: " + type);
-  }
-  this.checkForInterrupted();
-};
-
-action.Chain.prototype.mouseTap = function(doc, x, y, button, count, mod) {
-  this.emitMouseEvent(doc, "mousemove", x, y, button, count, mod);
-  this.emitMouseEvent(doc, "mousedown", x, y, button, count, mod);
-  this.emitMouseEvent(doc, "mouseup", x, y, button, count, mod);
-};
+  return v;
+}
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -12,17 +12,17 @@ var loader = Cc["@mozilla.org/moz/jssubs
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
     this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
 
-Cu.import("chrome://marionette/content/action.js");
+Cu.import("chrome://marionette/content/legacyaction.js");
 Cu.import("chrome://marionette/content/atom.js");
 Cu.import("chrome://marionette/content/browser.js");
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/evaluate.js");
 Cu.import("chrome://marionette/content/event.js");
 Cu.import("chrome://marionette/content/interaction.js");
 Cu.import("chrome://marionette/content/logging.js");
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -1,18 +1,18 @@
 # 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/.
 
 marionette.jar:
 % content marionette %content/
   content/server.js (server.js)
   content/driver.js (driver.js)
-  content/webdriverAction.js (webdriverAction.js)
   content/action.js (action.js)
+  content/legacyaction.js (legacyaction.js)
   content/browser.js (browser.js)
   content/interaction.js (interaction.js)
   content/accessibility.js (accessibility.js)
   content/listener.js (listener.js)
   content/element.js (element.js)
   content/simpletest.js (simpletest.js)
   content/frame.js (frame.js)
   content/event.js  (event.js)
copy from testing/marionette/action.js
copy to testing/marionette/legacyaction.js
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -8,17 +8,17 @@ var {classes: Cc, interfaces: Ci, utils:
 
 var uuidGen = Cc["@mozilla.org/uuid-generator;1"]
     .getService(Ci.nsIUUIDGenerator);
 
 var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
     .getService(Ci.mozIJSSubScriptLoader);
 
 Cu.import("chrome://marionette/content/accessibility.js");
-Cu.import("chrome://marionette/content/action.js");
+Cu.import("chrome://marionette/content/legacyaction.js");
 Cu.import("chrome://marionette/content/atom.js");
 Cu.import("chrome://marionette/content/capture.js");
 Cu.import("chrome://marionette/content/cookies.js");
 Cu.import("chrome://marionette/content/element.js");
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/evaluate.js");
 Cu.import("chrome://marionette/content/event.js");
 Cu.import("chrome://marionette/content/interaction.js");
rename from testing/marionette/test_actions.js
rename to testing/marionette/test_action.js
--- a/testing/marionette/test_actions.js
+++ b/testing/marionette/test_action.js
@@ -1,110 +1,112 @@
 /* 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/. */
 
+"use strict";
+
 const {utils: Cu} = Components;
 
 Cu.import("chrome://marionette/content/error.js");
 Cu.import("chrome://marionette/content/element.js");
-Cu.import("chrome://marionette/content/webdriverAction.js");
+Cu.import("chrome://marionette/content/action.js");
 
 add_test(function test_defaultPointerParameters() {
   let defaultParameters = {pointerType: "mouse", primary: true};
-  deepEqual(webdriveraction.processPointerParameters(), defaultParameters);
-  deepEqual(webdriveraction.processPointerParameters({blah: 'nonsense'}), defaultParameters);
+  deepEqual(action.processPointerParameters(), defaultParameters);
+  deepEqual(action.processPointerParameters({blah: 'nonsense'}), defaultParameters);
 
   run_next_test();
 });
 
 add_test(function test_processPointerParameters() {
   let check = function(regex, message, args){
-    checkErrors(regex, webdriveraction.processPointerParameters, args, message);
+    checkErrors(regex, action.processPointerParameters, args, message);
   }
   for (let d of [-1, 'a', [1,2], /blah/]){
     let message = `parametersData: ${getTypeString(d)}`;
     check(/not an object/, message, [d]);
   }
   let parametersData = {pointerType: "foo"};
 
   let message = `parametersData: ${parametersData}`;
   check(/must be one of/, message, [parametersData]);
   parametersData.pointerType = "pen";
   parametersData.primary = 'a';
   check(/must be a boolean/, message, [parametersData]);
 
   parametersData.primary = false;
-  deepEqual(webdriveraction.processPointerParameters(parametersData),
+  deepEqual(action.processPointerParameters(parametersData),
             {pointerType: "pen", primary: false});
 
 
   run_next_test();
 });
 
 add_test(function test_processPointerUpDownAction() {
   let actionItem = {};
   for (let d of [-1, 'a']){
     actionItem = {button: d};
     checkErrors(/should be integer >= 0/,
-                webdriveraction.processPointerUpDownAction,
+                action.processPointerUpDownAction,
                 [actionItem, {}],
                 `button: ${actionItem.button}`);
   }
   actionItem.button = 5;
-  let action = new webdriveraction.Action("some id", "mouse", "pointerDown");
-  webdriveraction.processPointerUpDownAction(actionItem, action);
-  equal(action.button, actionItem.button);
+  let act = new action.Action("some id", "mouse", "pointerDown");
+  action.processPointerUpDownAction(actionItem, act);
+  equal(act.button, actionItem.button);
 
   run_next_test();
 });
 
 
 add_test(function test_processPauseAction() {
   let actionItem = {duration: 5};
-  let action = new webdriveraction.Action("some id", "none", "pause");
-  webdriveraction.processPauseAction(actionItem, action);
-  equal(action.duration, actionItem.duration);
+  let act = new action.Action("some id", "none", "pause");
+  action.processPauseAction(actionItem, act);
+  equal(act.duration, actionItem.duration);
 
   run_next_test();
 });
 
 add_test(function test_validateActionDuration() {
   let actionItem = {};
   let check = function(actionFunc, message){
     let m = message + `; actionFunc: ${actionFunc.name}`;
     checkErrors(/should be integer >= 0/,
                 actionFunc, [actionItem, {}], m);
   };
   for (let d of [-1, 'a', undefined]){
     actionItem.duration = d;
     let message = `duration: ${actionItem.duration}`;
-    check(webdriveraction.processPauseAction, message);
-    check(webdriveraction.processPointerMoveAction, message);
+    check(action.processPauseAction, message);
+    check(action.processPointerMoveAction, message);
   }
   actionItem.duration = 5;
   for (let d of [-1, 'a']){
     for (let name of ['x', 'y']){
       actionItem[name] = d;
       let message = `${name}: ${actionItem[name]}`;
-      check(webdriveraction.processPointerMoveAction, message);
+      check(action.processPointerMoveAction, message);
     }
   }
   run_next_test();
 });
 
 add_test(function test_processPointerMoveActionElementValidation() {
   let actionItem = {
     duration: 5,
     type: "pointerMove",
   };
   for (let d of [-1, 'a', {a:"blah"}]){
     actionItem.element = d;
     checkErrors(/must be an Object that represents a web element/,
-                webdriveraction.processPointerMoveAction,
+                action.processPointerMoveAction,
                 [actionItem, {}],
                 `actionItem.element: (${getTypeString(d)})`);
   }
 
   run_next_test();
 });
 
 add_test(function test_processPointerMoveActionElement() {
@@ -135,23 +137,23 @@ add_test(function test_processPointerMov
       duration: 5,
       type: "pointerMove",
       x: 1,
       y: 2,
       element: undefined,
     },
   ];
   for (let a of actionItems){
-    let action = new webdriveraction.Action("some id", "pointer", a.type);
-    webdriveraction.processPointerMoveAction(a, action);
-    ok(action instanceof webdriveraction.Action);
-    equal(action.duration, a.duration);
-    equal(action.element, a.element);
-    equal(action.x, a.x);
-    equal(action.y, a.y);
+    let act = new action.Action("some id", "pointer", a.type);
+    action.processPointerMoveAction(a, act);
+    ok(act instanceof action.Action);
+    equal(act.duration, a.duration);
+    equal(act.element, a.element);
+    equal(act.x, a.x);
+    equal(act.y, a.y);
   }
   run_next_test();
 });
 
 
 add_test(function test_processPointerAction() {
   let pointerParams = {
     pointerType: "touch",
@@ -168,119 +170,119 @@ add_test(function test_processPointerAct
     },
     {
       type: "pointerUp",
       button: 1,
     }
   ];
   let id = "some_id";
   for (let a of actionItems){
-    let action = webdriveraction.processPointerAction(id, pointerParams, a);
-    ok(action instanceof webdriveraction.Action);
-    equal(action.type, "pointer");
-    equal(action.subtype, a.type);
-    equal(action.id, id);
+    let act = action.processPointerAction(id, pointerParams, a);
+    ok(act instanceof action.Action);
+    equal(act.type, "pointer");
+    equal(act.subtype, a.type);
+    equal(act.id, id);
     if (a.type === "pointerUp"){
-      equal(action.button, a.button);
+      equal(act.button, a.button);
     }
     else {
-      equal(action.duration, a.duration);
+      equal(act.duration, a.duration);
     }
     if (a.type !== "pause"){
-      equal(action.primary, pointerParams.primary);
-      equal(action.pointerType, pointerParams.pointerType);
+      equal(act.primary, pointerParams.primary);
+      equal(act.pointerType, pointerParams.pointerType);
     }
   }
 
   run_next_test();
 });
 
 add_test(function test_processNullAction() {
   let actionItem = {};
   let id = "some_id";
   actionItem.type = "pause";
   actionItem.duration = 5;
-  let action = webdriveraction.processNullAction(id, actionItem);
-  ok(action instanceof webdriveraction.Action);
-  equal(action.type, "none");
-  equal(action.subtype, actionItem.type);
-  equal(action.id, id);
-  equal(action.duration, actionItem.duration);
+  let act = action.processNullAction(id, actionItem);
+  ok(act instanceof action.Action);
+  equal(act.type, "none");
+  equal(act.subtype, actionItem.type);
+  equal(act.id, id);
+  equal(act.duration, actionItem.duration);
 
   run_next_test();
 });
 
 add_test(function test_processActionSubtypeValidation() {
   var actionItem = {};
   var id = "some_id";
   actionItem.type = "dancing";
   let check = function(regex, actionFunc, args=[id, actionItem]){
     let message = `actionItem.type: ${actionItem.type}; actionFunc: ${actionFunc.name}`;
     checkErrors(regex, actionFunc, args, message);
   };
-  check(/subtype must be 'pause'/, webdriveraction.processNullAction);
-  check(/subtype must be one of/, webdriveraction.processKeyAction);
+  check(/subtype must be 'pause'/, action.processNullAction);
+  check(/subtype must be one of/, action.processKeyAction);
   check(/subtype must be one of/,
-        webdriveraction.processPointerAction,
+        action.processPointerAction,
         [id, [], actionItem]);
 
   run_next_test();
 });
 
 add_test(function test_processKeyActionPause() {
   let actionItem = {};
   let id = "some_id";
 
   actionItem.type = "pause";
   actionItem.duration = 5;
-  let action = webdriveraction.processKeyAction(id, actionItem);
-  ok(action instanceof webdriveraction.Action);
-  equal(action.type, "key");
-  equal(action.subtype, actionItem.type);
-  equal(action.id, id);
-  equal(action.duration, actionItem.duration);
+  let act = action.processKeyAction(id, actionItem);
+  ok(act instanceof action.Action);
+  equal(act.type, "key");
+  equal(act.subtype, actionItem.type);
+  equal(act.id, id);
+  equal(act.duration, actionItem.duration);
 
   run_next_test();
 });
 
 add_test(function test_processKeyActionUpDown() {
   let actionItem = {
     type: "keyDown",
   };
   let id = "some_id";
 
   for (let d of [-1, 'bad', undefined, [], ['a'], {length: 1}, null]){
     actionItem.value = d;
     let message = `actionItem.value: (${getTypeString(d)})`;
-    Assert.throws(() => webdriveraction.processKeyAction(id, actionItem),
+    Assert.throws(() => action.processKeyAction(id, actionItem),
                   InvalidArgumentError, message);
-    Assert.throws(() => webdriveraction.processKeyAction(id, actionItem),
+    Assert.throws(() => action.processKeyAction(id, actionItem),
                 /'key' must be a single-character String/,
                 message);
   }
 
   actionItem.value = 'a';
-  let action = webdriveraction.processKeyAction(id, actionItem);
-  ok(action instanceof webdriveraction.Action);
-  equal(action.type, "key");
-  equal(action.subtype, actionItem.type);
-  equal(action.id, id);
-  equal(action.value, actionItem.value);
+  let act = action.processKeyAction(id, actionItem);
+  ok(act instanceof action.Action);
+  equal(act.type, "key");
+  equal(act.subtype, actionItem.type);
+  equal(act.id, id);
+  equal(act.value, actionItem.value);
 
   run_next_test();
 });
 
 
 add_test(function test_processInputSourceActionSequenceValidation() {
   var actionSequence = {
     type: "swim",
     id: "some id",
   };
   let check = function(message, regex){
-    checkErrors(regex, webdriveraction.processInputSourceActionSequence,
+    checkErrors(regex, action.processInputSourceActionSequence,
                 [actionSequence], message);
   };
   check(`actionSequence.type: ${actionSequence.type}`, /type' must be one of/);
 
   actionSequence.type = "none";
   actionSequence.id = -1;
   check(`actionSequence.id: ${getTypeString(actionSequence.id)}`, /is not a string/);
 
@@ -301,19 +303,19 @@ add_test(function test_processInputSourc
     type: "pause",
     duration: 5,
   };
   let actionSequence = {
     type: "none",
     id: "some id",
     actions: [actionItem],
   };
-  let expectedAction = new webdriveraction.Action(actionSequence.id, "none", actionItem.type);
+  let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type);
   expectedAction.duration = actionItem.duration;
-  let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+  let actions = action.processInputSourceActionSequence(actionSequence);
   equal(actions.length, 1);
   deepEqual(actions[0], expectedAction);
   run_next_test();
 });
 
 add_test(function test_processInputSourceActionSequencePointer() {
   let actionItem = {
       type: "pointerDown",
@@ -323,43 +325,43 @@ add_test(function test_processInputSourc
     type: "pointer",
     id: "9",
     actions: [actionItem],
     parameters: {
       pointerType: "pen",
       primary: false,
     },
   };
-  let expectedAction = new webdriveraction.Action(actionSequence.id,
+  let expectedAction = new action.Action(actionSequence.id,
                                                   actionSequence.type,
                                                   actionItem.type);
   expectedAction.pointerType = actionSequence.parameters.pointerType;
   expectedAction.primary = actionSequence.parameters.primary;
   expectedAction.button = actionItem.button;
-  let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+  let actions = action.processInputSourceActionSequence(actionSequence);
   equal(actions.length, 1);
   deepEqual(actions[0], expectedAction);
   run_next_test();
 });
 
 add_test(function test_processInputSourceActionSequenceKey() {
   let actionItem = {
       type: "keyUp",
       value: 'a',
   };
   let actionSequence = {
     type: "key",
     id: "9",
     actions: [actionItem],
   };
-  let expectedAction = new webdriveraction.Action(actionSequence.id,
+  let expectedAction = new action.Action(actionSequence.id,
                                                   actionSequence.type,
                                                   actionItem.type);
   expectedAction.value = actionItem.value;
-  let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+  let actions = action.processInputSourceActionSequence(actionSequence);
   equal(actions.length, 1);
   deepEqual(actions[0], expectedAction);
   run_next_test();
 });
 
 
 add_test(function test_processInputSourceActionSequenceGenerateID() {
   let actionItems = [
@@ -367,92 +369,92 @@ add_test(function test_processInputSourc
       type: "pause",
       duration: 5,
     }
   ];
   let actionSequence = {
     type: "key",
     actions: actionItems,
   };
-  let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+  let actions = action.processInputSourceActionSequence(actionSequence);
   equal(typeof actions[0].id, "string");
   equal(actions[0].id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)[0],
         actions[0].id);
   run_next_test();
 });
 
 add_test(function test_processInputSourceActionSequenceInputStateMap() {
   let actionItem = {
     type: "pause",
     duration: 5,
   };
   let actionSequence = {
     type: "pointer",
     id: "1",
     actions: [actionItem],
   };
-  wrongInputState = new webdriveraction.NullInputState();
+  let wrongInputState = new action.NullInputState();
   inputStateMap.set(actionSequence.id, wrongInputState);
   checkErrors(/already mapped to/,
-              webdriveraction.processInputSourceActionSequence,
+              action.processInputSourceActionSequence,
               [actionSequence],
               `${actionSequence.type} using ${wrongInputState}`);
   inputStateMap.clear();
   run_next_test();
 });
 
 add_test(function test_processPointerActionInputStateMap() {
   let actionItem = {
     type: "pointerDown",
   };
   let id = 1;
   let parameters = {
     pointerType: "mouse",
     primary: true,
   };
-  wrongInputState = new webdriveraction.PointerInputState("pause", true);
+  let wrongInputState = new action.PointerInputState("pause", true);
   inputStateMap.set(id, wrongInputState)
   checkErrors(/already mapped to InputState whose subtype/,
-              webdriveraction.processPointerAction,
+              action.processPointerAction,
               [id, parameters, actionItem],
               `$subtype {actionItem.type} with ${wrongInputState.subtype} in inputState`);
   inputStateMap.clear();
   run_next_test();
 });
 
 add_test(function test_extractActionChainValidation() {
   for (let actions of [-1, 'a', undefined, null]){
     let message = `actions: ${getTypeString(actions)}`
-    Assert.throws(() => webdriveraction.extractActionChain(actions),
+    Assert.throws(() => action.extractActionChain(actions),
                 InvalidArgumentError, message);
-    Assert.throws(() => webdriveraction.extractActionChain(actions),
+    Assert.throws(() => action.extractActionChain(actions),
                 /is not an Array/,
                 message);
   }
   run_next_test();
 });
 
 add_test(function test_extractActionChain_empty() {
-  deepEqual(webdriveraction.extractActionChain([]), []);
+  deepEqual(action.extractActionChain([]), []);
   run_next_test();
 });
 
 add_test(function test_extractActionChain_oneTickOneInput() {
   let actionItem = {
     type: "pause",
     duration: 5,
   };
   let actionSequence = {
     type: "none",
     id: "some id",
     actions: [actionItem],
   };
-  let expectedAction = new webdriveraction.Action(actionSequence.id, "none", actionItem.type);
+  let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type);
   expectedAction.duration = actionItem.duration;
-  let actionsByTick = webdriveraction.extractActionChain([actionSequence]);
+  let actionsByTick = action.extractActionChain([actionSequence]);
   equal(1, actionsByTick.length);
   equal(1, actionsByTick[0].length);
   deepEqual(actionsByTick, [[expectedAction]]);
   run_next_test();
 });
 
 add_test(function test_extractActionChain_twoAndThreeTicks() {
   let mouseActionItems = [
@@ -488,30 +490,30 @@ add_test(function test_extractActionChai
       value: 'a',
     },
   ];
   let keyActionSequence = {
     type: "key",
     id: "1",
     actions: keyActionItems,
   };
-  let actionsByTick = webdriveraction.extractActionChain([keyActionSequence, mouseActionSequence]);
+  let actionsByTick = action.extractActionChain([keyActionSequence, mouseActionSequence]);
   // number of ticks is same as longest action sequence
   equal(keyActionItems.length, actionsByTick.length);
   equal(2, actionsByTick[0].length);
   equal(2, actionsByTick[1].length);
   equal(1, actionsByTick[2].length);
-  let expectedAction = new webdriveraction.Action(keyActionSequence.id,
+  let expectedAction = new action.Action(keyActionSequence.id,
                                                   "key",
                                                   keyActionItems[2].type);
   expectedAction.value = keyActionItems[2].value;
   deepEqual(actionsByTick[2][0], expectedAction);
 
   // one empty action sequence
-  actionsByTick = webdriveraction.extractActionChain([keyActionSequence,
+  actionsByTick = action.extractActionChain([keyActionSequence,
                                                       {type:"none", actions: []}]);
   equal(keyActionItems.length, actionsByTick.length);
   equal(1, actionsByTick[0].length);
   run_next_test();
 });
 
 // helpers
 function getTypeString(obj){
--- a/testing/marionette/unit.ini
+++ b/testing/marionette/unit.ini
@@ -2,13 +2,13 @@
 # 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/.
 
 # xpcshell unit tests for Marionette
 
 [DEFAULT]
 skip-if = appname == "thunderbird"
 
+[test_action.js]
 [test_element.js]
 [test_error.js]
 [test_message.js]
 [test_navigate.js]
-[test_actions.js]
deleted file mode 100644
--- a/testing/marionette/webdriverAction.js
+++ /dev/null
@@ -1,287 +0,0 @@
-/* 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/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-
-Cu.import("chrome://marionette/content/element.js");
-Cu.import("chrome://marionette/content/error.js");
-
-
-this.EXPORTED_SYMBOLS = ["webdriveraction", "inputStateMap"];
-
-const logger = Log.repository.getLogger("Marionette");
-
-// map between input id (string) and device state for that input source
-// TODO associate with session
-// populated by "dispatch tick actions"?
-this.inputStateMap = new Map();
-
-
-const ACTION_TYPES = new Set([
-  "none",
-  "key",
-  "pointer",
-]);
-
-this.webdriveraction = {}
-
-// input source: virtual device that has an string id and a source type
-// This doesn't seem to be really used in spec. Each "actionSequence" has id
-// and type already, which is all we really need.
-// webdriveraction.InputSource = function InputSource(id, type){
-//   this.id = id;
-//   // null, key, pointer
-//   this.type = type;
-// }
-
-webdriveraction.NullInputState = function NullInputState(){
-  // a named empty object
-}
-
-webdriveraction.NullInputState.prototype.toString = function(){
-  return '[object NullInputState]';
-}
-
-webdriveraction.KeyInputState = function KeyInputState(){
-  this.pressed = new Set();
-  this.alt = false;
-  this.shift = false;
-  this.ctrl = false;
-  this.meta = false;
-}
-
-webdriveraction.KeyInputState.prototype.toString = function(){
-  return '[object KeyInputState]';
-}
-
-webdriveraction.PointerInputState = function PointerInputState(subtype, primary){
-  this.pressed = new Set();
-  this.subtype = subtype;
-  this.primary = primary;
-  this.x = 0;
-  this.y = 0;
-}
-
-webdriveraction.PointerInputState.prototype.toString = function(){
-  return '[object PointerInputState]';
-}
-
-const ACTION_INPUT_STATE = {
-  none: webdriveraction.NullInputState,
-  key: webdriveraction.KeyInputState,
-  pointer: webdriveraction.PointerInputState,
-}
-
-webdriveraction.Action = function Action(id, type, subtype) {
-  // represents action object for actionByTick
-  this.id = id;
-  this.type = type;
-  this.subtype = subtype;
-};
-
-// actions: list of action sequences
-webdriveraction.extractActionChain = function extractActionChain(actions) {
-  // TODO check current browsing context?
-  if (typeof actions === 'undefined' || !Array.isArray(actions)){
-    throw new InvalidArgumentError(`'actions' ${actions} is not an Array`);
-  }
-  let actionsByTick = [];
-  for (let actionSequence of actions){
-    let inputSourceActions = webdriveraction.processInputSourceActionSequence(actionSequence);
-    for (var i = 0; i < inputSourceActions.length; i++){
-      if (actionsByTick.length < i + 1){
-        // new tick
-        actionsByTick.push([]);
-      }
-      actionsByTick[i].push(inputSourceActions[i]);
-    }
-  }
-  return actionsByTick;
-};
-
-// action_sequence has a list of actionItems for one input source
-webdriveraction.processInputSourceActionSequence = function processInputSourceActionSequence(
-    actionSequence) {
-  let type = actionSequence.type;
-  if (!ACTION_TYPES.has(type)){
-    throw new InvalidArgumentError(`'actionSequence.type' must be one of
-                                   ${Array.from(ACTION_TYPES)}`);
-  }
-  let id = actionSequence.id;
-  if (typeof id === 'undefined'){
-    id = element.generateUUID();
-  }
-  else if (typeof id !== 'string'){
-    throw new InvalidArgumentError(`'id' ${id} is not a string`);
-  }
-  let actionItems = actionSequence.actions;
-  if (!Array.isArray(actionItems)){
-    throw new InvalidArgumentError(`'actionSequence.actions' ${actionSequence.actions} is not an Array`);
-  }
-
-  let parameters;
-  if (type === "pointer"){
-    parameters = webdriveraction.processPointerParameters(actionSequence.parameters);
-  }
-
-  if (inputStateMap.has(id) && !(inputStateMap.get(id) instanceof ACTION_INPUT_STATE[type])){
-    throw new InvalidArgumentError(
-      `${id} is already mapped to ${inputStateMap.get(id)}`);
-  }
-
-  let actions = [];
-  for (let actionItem of actionItems){
-    if (Object.prototype.toString.call(actionItem) !== "[object Object]"){
-      throw new InvalidArgumentError(`${actionItem} is not an object`);
-    }
-    let action = {};
-    switch(type){
-      case("none"):
-        action = webdriveraction.processNullAction(id, actionItem);
-        break;
-      case("key"):
-        action = webdriveraction.processKeyAction(id, actionItem);
-        break;
-      case("pointer"):
-        action = webdriveraction.processPointerAction(id, parameters, actionItem);
-        break;
-    }
-    actions.push(action);
-  }
-  return actions;
-};
-
-webdriveraction.processPointerParameters = function processPointerParameters(parametersData){
-  let parameters = {
-    pointerType: "mouse",
-    primary: true,
-  };
-  if (typeof parametersData === 'undefined'){
-    return parameters;
-  }
-  if (Object.prototype.toString.call(parametersData) !== "[object Object]"){
-      throw new InvalidArgumentError(`parametersData ${parametersData} is not an object`);
-  }
-  let pointerType = parametersData.pointerType;
-  if (typeof pointerType !== 'undefined'){
-    let types = ["mouse", "pen", "touch"];
-    if(!types.includes(pointerType)){
-      throw new InvalidArgumentError(`pointerType must be one of ${types}`);
-    }
-    parameters.pointerType = pointerType;
-  }
-  let primary = parametersData.primary;
-  if (typeof primary !== 'undefined'){
-    if (typeof primary !== 'boolean'){
-      throw new InvalidArgumentError(`primary ${primary} must be a boolean`);
-    }
-    parameters.primary = primary;
-  }
-  return parameters;
-};
-
-webdriveraction.processNullAction = function processNullAction(id, actionItem){
-  let subtype = actionItem.type;
-  if (subtype !== "pause"){
-    throw new InvalidArgumentError("subtype must be 'pause'");
-  }
-  let action = new webdriveraction.Action(id, "none", subtype);
-  webdriveraction.processPauseAction(actionItem, action);
-  return action;
-};
-
-webdriveraction.processKeyAction = function processKeyAction(id, actionItem){
-  let subtype = actionItem.type;
-  let types = ["keyUp", "keyDown", "pause"];
-  if (!types.includes(subtype)){
-    throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
-  }
-  let action = new webdriveraction.Action(id, "key", subtype);
-  if (subtype === "pause"){
-    webdriveraction.processPauseAction(actionItem, action);
-    return action;
-  }
-  let key = actionItem.value
-  // what about key codes like arrow, versus unicode chars?
-  if (typeof key !== 'string' || (typeof key === 'string' && key.length !== 1)){
-    throw new InvalidArgumentError("'key' must be a single-character String");
-  }
-  action.value = key;
-  return action;
-};
-
-webdriveraction.processPointerAction = function processPointerAction(id, parameters, actionItem){
-  let subtype = actionItem.type;
-  let types = ["pause", "pointerUp", "pointerDown", "pointerMove", "pointerCancel"];
-  if (!types.includes(subtype)){
-    throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
-  }
-
-  if (inputStateMap.has(id) && inputStateMap.get(id).subtype !== subtype){
-    throw new InvalidArgumentError(
-      `${id} is already mapped to InputState whose subtype is `
-      + `${inputStateMap.get(id).subtype} not ${subtype}`);
-  }
-
-  let action = new webdriveraction.Action(id, "pointer", subtype);
-  if (subtype === "pause"){
-    webdriveraction.processPauseAction(actionItem, action);
-    return action;
-  }
-  action.pointerType = parameters.pointerType;
-  action.primary = parameters.primary;
-  if (["pointerUp", "pointerDown"].includes(subtype)){
-    webdriveraction.processPointerUpDownAction(actionItem, action);
-  }
-  if (subtype == "pointerMove"){
-    webdriveraction.processPointerMoveAction(actionItem, action);
-  }
-  return action;
-};
-
-webdriveraction.processPauseAction = function processPauseAction(actionItem, action){
-  action.duration = webdriveraction.validatePositiveNum(actionItem.duration, 'duration');
-};
-
-webdriveraction.processPointerUpDownAction = function processPointerUpDownAction(
-    actionItem,
-    action){
-  action.button = webdriveraction.validatePositiveNum(actionItem.button, 'button');
-};
-
-webdriveraction.processPointerMoveAction = function processPointerMoveAction(actionItem, action){
-  action.duration = webdriveraction.validatePositiveNum(actionItem.duration, 'duration');
-  let webElement = actionItem.element;
-  let isElement = function(el){
-    let properties = Object.getOwnPropertyNames(el);
-    return properties.includes(element.Key) || properties.includes(element.LegacyKey);
-  }
-  if (typeof webElement !== "undefined" && !isElement(webElement)){
-    throw new InvalidArgumentError(
-      "'actionItem.duration' must be an Object that represents a web element");
-  }
-  action.element = webElement;
-
-  action.x = actionItem.x;
-  if (typeof action.x !== "undefined"){
-    action.x = webdriveraction.validatePositiveNum(actionItem.x, 'x');
-  }
-  action.y = actionItem.y;
-  if (typeof action.y !== "undefined"){
-    action.y = webdriveraction.validatePositiveNum(actionItem.y, 'y');
-  }
-};
-
-// helpers
-webdriveraction.validatePositiveNum = function validatePositiveNum(value, name){
-  let v = parseInt(value);
-  if (isNaN(v) || v < 0){
-    throw new InvalidArgumentError(`Value of '${name}' should be integer >= 0`);
-  }
-  return v;
-}