Bug 1400256 - Adapt actions for implicitly unmarshaled elements. r?automatedtester draft
authorAndreas Tolfsen <ato@sny.no>
Mon, 09 Oct 2017 19:55:27 +0100
changeset 683399 9d24e8126c088b22c52fbe718cbcceaf6102ac6e
parent 683398 917e12d621b54ac99b8e0f60a2250819e2e128a3
child 736637 5bdbb6a8673250d14970c562999339b3a90bb086
push id85365
push userbmo:ato@sny.no
push dateThu, 19 Oct 2017 17:00:27 +0000
reviewersautomatedtester
bugs1400256
milestone58.0a1
Bug 1400256 - Adapt actions for implicitly unmarshaled elements. r?automatedtester Since web element references are now implicitly unmarshaled when they are passed to the content frame script, there is no need for the actions module to check that the element origin is a reference and try to look it up from the known element store. MozReview-Commit-ID: 3BGBIBQMtR3
testing/marionette/action.js
testing/marionette/listener.js
testing/marionette/test_action.js
--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -4,20 +4,17 @@
 
 /* eslint no-dupe-keys:off */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("chrome://marionette/content/assert.js");
-const {
-  element,
-  WebElement,
-} = Cu.import("chrome://marionette/content/element.js", {});
+const {element} = Cu.import("chrome://marionette/content/element.js", {});
 const {
   InvalidArgumentError,
   MoveTargetOutOfBoundsError,
   UnsupportedOperationError,
 } = Cu.import("chrome://marionette/content/error.js", {});
 Cu.import("chrome://marionette/content/event.js");
 const {pprint} = Cu.import("chrome://marionette/content/format.js", {});
 Cu.import("chrome://marionette/content/interaction.js");
@@ -342,37 +339,38 @@ const KEY_CODE_LOOKUP = {
 action.PointerOrigin = {
   Viewport: "viewport",
   Pointer: "pointer",
 };
 
 /**
  * Look up a PointerOrigin.
  *
- * @param {(undefined|string|WebElement)} obj
- *     Origin for a pointerMove action.
+ * @param {(string|Element)=} obj
+ *     Origin for a <code>pointerMove</code> action.  Must be one of
+ *     "viewport" (default), "pointer", or a DOM element or a DOM element.
  *
  * @return {action.PointerOrigin}
- *     A pointer origin that is either "viewport" (default), "pointer", or a
- *     web-element reference.
+ *     Pointer origin.
  *
  * @throws {InvalidArgumentError}
- *     If <code>obj</code> is not a valid origin.
+ *     If <var>obj</var> is not a valid origin.
  */
 action.PointerOrigin.get = function(obj) {
   let origin = obj;
   if (typeof obj == "undefined") {
     origin = this.Viewport;
   } else if (typeof obj == "string") {
     let name = capitalize(obj);
     assert.in(name, this, pprint`Unknown pointer-move origin: ${obj}`);
     origin = this[name];
-  } else if (!WebElement.isReference(obj)) {
-    throw new InvalidArgumentError("Expected 'origin' to be a string or a " +
-      pprint`web element reference, got ${obj}`);
+  } else if (!element.isDOMElement(obj)) {
+    throw new InvalidArgumentError("Expected 'origin' to be undefined, " +
+        '"viewport", "pointer", ' +
+        pprint`or an element, got: ${obj}`);
   }
   return origin;
 };
 
 /** Represents possible subtypes for a pointer input source. */
 action.PointerType = {
   Mouse: "mouse",
   // TODO For now, only mouse is supported
@@ -967,31 +965,28 @@ action.Mouse = class {
  * This is done by creating a Promise for each tick that resolves once
  * all the Promises for individual tick-actions are resolved.  The next
  * tick's actions are not dispatched until the Promise for the current
  * tick is resolved.
  *
  * @param {action.Chain} chain
  *     Actions grouped by tick; each element in |chain| is a sequence of
  *     actions for one tick.
- * @param {element.Store} seenEls
- *     Element store.
  * @param {WindowProxy} window
  *     Current window global.
  *
  * @return {Promise}
  *     Promise for dispatching all actions in |chain|.
  */
-action.dispatch = function(chain, seenEls, window) {
+action.dispatch = function(chain, window) {
   let chainEvents = (async () => {
     for (let tickActions of chain) {
       await action.dispatchTickActions(
           tickActions,
           action.computeTickDuration(tickActions),
-          seenEls,
           window);
     }
   })();
   return chainEvents;
 };
 
 /**
  * Dispatch sequence of actions for one tick.
@@ -1003,28 +998,25 @@ action.dispatch = function(chain, seenEl
  *
  * Note that the tick-actions are dispatched in order, but they may have
  * different durations and therefore may not end in the same order.
  *
  * @param {Array.<action.Action>} tickActions
  *     List of actions for one tick.
  * @param {number} tickDuration
  *     Duration in milliseconds of this tick.
- * @param {element.Store} seenEls
- *     Element store.
  * @param {WindowProxy} window
  *     Current window global.
  *
  * @return {Promise}
  *     Promise for dispatching all tick-actions and pending DOM events.
  */
 action.dispatchTickActions = function(
-    tickActions, tickDuration, seenEls, window) {
-  let pendingEvents = tickActions.map(
-      toEvents(tickDuration, seenEls, window));
+    tickActions, tickDuration, window) {
+  let pendingEvents = tickActions.map(toEvents(tickDuration, window));
   return Promise.all(pendingEvents).then(
       () => interaction.flushEventLoop(window));
 };
 
 /**
  * Compute tick duration in milliseconds for a collection of actions.
  *
  * @param {Array.<action.Action>} tickActions
@@ -1080,26 +1072,24 @@ action.computePointerDestination = funct
   return {"x": x, "y": y};
 };
 
 /**
  * Create a closure to use as a map from action definitions to Promise events.
  *
  * @param {number} tickDuration
  *     Duration in milliseconds of this tick.
- * @param {element.Store} seenEls
- *     Element store.
  * @param {WindowProxy} window
  *     Current window global.
  *
  * @return {function(action.Action): Promise}
  *     Function that takes an action and returns a Promise for dispatching
  *     the event that corresponds to that action.
  */
-function toEvents(tickDuration, seenEls, window) {
+function toEvents(tickDuration, window) {
   return a => {
     let inputState = action.inputStateMap.get(a.id);
 
     switch (a.subtype) {
       case action.KeyUp:
         return dispatchKeyUp(a, inputState, window);
 
       case action.KeyDown:
@@ -1108,17 +1098,17 @@ function toEvents(tickDuration, seenEls,
       case action.PointerDown:
         return dispatchPointerDown(a, inputState, window);
 
       case action.PointerUp:
         return dispatchPointerUp(a, inputState, window);
 
       case action.PointerMove:
         return dispatchPointerMove(
-            a, inputState, tickDuration, seenEls, window);
+            a, inputState, tickDuration, window);
 
       case action.PointerCancel:
         throw new UnsupportedOperationError();
 
       case action.Pause:
         return dispatchPause(a, tickDuration);
     }
 
@@ -1288,38 +1278,36 @@ function dispatchPointerUp(a, inputState
         throw new TypeError(`Unknown pointer type: ${inputState.subtype}`);
     }
 
     resolve();
   });
 }
 
 /**
- * Dispatch a pointerMove action equivalent to moving pointer device in
- * a line.
+ * Dispatch a pointerMove action equivalent to moving pointer device
+ * in a line.
  *
  * If the action duration is 0, the pointer jumps immediately to the
  * target coordinates.  Otherwise, events are synthesized to mimic a
  * pointer travelling in a discontinuous, approximately straight line,
  * with the pointer coordinates being updated around 60 times per second.
  *
  * @param {action.Action} a
  *     Action to dispatch.
  * @param {action.InputState} inputState
  *     Input state for this action's input source.
- * @param {element.Store} seenEls
- *     Element store.
  * @param {WindowProxy} window
  *     Current window global.
  *
  * @return {Promise}
  *     Promise to dispatch at least one pointermove event, as well as
  *     mousemove events as appropriate.
  */
-function dispatchPointerMove(a, inputState, tickDuration, seenEls, window) {
+function dispatchPointerMove(a, inputState, tickDuration, window) {
   const timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   // interval between pointermove increments in ms, based on common vsync
   const fps60 = 17;
 
   return new Promise(resolve => {
     const start = Date.now();
     const [startX, startY] = [inputState.x, inputState.y];
 
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -892,28 +892,28 @@ function createATouch(el, corx, cory, to
  * Perform a series of grouped actions at the specified points in time.
  *
  * @param {obj} msg
  *      Object with an |actions| attribute that is an Array of objects
  *      each of which represents an action sequence.
  */
 async function performActions(msg) {
   let chain = action.Chain.fromJSON(msg.actions);
-  await action.dispatch(chain, seenEls, curContainer.frame);
+  await action.dispatch(chain, curContainer.frame);
 }
 
 /**
  * The release actions command is used to release all the keys and pointer
  * buttons that are currently depressed. This causes events to be fired
  * as if the state was released by an explicit series of actions. It also
  * clears all the internal state of the virtual devices.
  */
 async function releaseActions() {
   await action.dispatchTickActions(
-      action.inputsToCancel.reverse(), 0, seenEls, curContainer.frame);
+      action.inputsToCancel.reverse(), 0, curContainer.frame);
   action.inputsToCancel.length = 0;
   action.inputStateMap.clear();
 }
 
 /**
  * Start action chain on one finger.
  */
 function actionChain(chain, touchId) {
--- a/testing/marionette/test_action.js
+++ b/testing/marionette/test_action.js
@@ -5,16 +5,24 @@
 "use strict";
 
 const {utils: Cu} = Components;
 
 Cu.import("chrome://marionette/content/action.js");
 const {ContentWebElement} = Cu.import("chrome://marionette/content/element.js", {});
 Cu.import("chrome://marionette/content/error.js");
 
+const XHTMLNS = "http://www.w3.org/1999/xhtml";
+
+const domEl = {
+  nodeType: 1,
+  ELEMENT_NODE: 1,
+  namespaceURI: XHTMLNS,
+};
+
 action.inputStateMap = new Map();
 
 add_test(function test_createAction() {
   Assert.throws(() => new action.Action(), InvalidArgumentError,
       "Missing Action constructor args");
   Assert.throws(() => new action.Action(1, 2), InvalidArgumentError,
       "Missing Action constructor args");
   Assert.throws(
@@ -91,17 +99,17 @@ add_test(function test_validateActionDur
 });
 
 add_test(function test_processPointerMoveActionOriginValidation() {
   let actionSequence = {type: "pointer", id: "some_id"};
   let actionItem = {duration: 5000, type: "pointerMove"};
   for (let d of [-1, {a: "blah"}, []]) {
     actionItem.origin = d;
 
-    checkErrors(/Expected \'origin\' to be a string or a web element reference/,
+    checkErrors(/Expected \'origin\' to be undefined, "viewport", "pointer", or an element/,
         action.Action.fromJSON,
         [actionSequence, actionItem],
         `actionItem.origin: (${getTypeString(d)})`);
   }
 
   run_next_test();
 });
 
@@ -117,17 +125,17 @@ add_test(function test_processPointerMov
   }
 
   run_next_test();
 });
 
 add_test(function test_processPointerMoveActionElementOrigin() {
   let actionSequence = {type: "pointer", id: "some_id"};
   let actionItem = {duration: 5000, type: "pointerMove"};
-  actionItem.origin = {[ContentWebElement.Identifier]: "something"};
+  actionItem.origin = domEl;
   let a = action.Action.fromJSON(actionSequence, actionItem);
   deepEqual(a.origin, actionItem.origin);
   run_next_test();
 });
 
 add_test(function test_processPointerMoveActionDefaultOrigin() {
   let actionSequence = {type: "pointer", id: "some_id"};
   // origin left undefined
@@ -145,17 +153,17 @@ add_test(function test_processPointerMov
       type: "pointerMove",
       origin: undefined,
       x: undefined,
       y: undefined,
     },
     {
       duration: undefined,
       type: "pointerMove",
-      origin: {[ContentWebElement.Identifier]: "id", [ContentWebElement.LegacyKey]: "id"},
+      origin: domEl,
       x: undefined,
       y: undefined,
     },
     {
       duration: 5000,
       type: "pointerMove",
       x: 0,
       y: undefined,