--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -361,18 +361,19 @@ action.PointerOrigin.get = function(obj)
`web element reference, got: ${obj}`);
}
return origin;
};
/** Represents possible subtypes for a pointer input source. */
action.PointerType = {
Mouse: "mouse",
- Pen: "pen",
- Touch: "touch",
+ // TODO For now, only mouse is supported
+ //Pen: "pen",
+ //Touch: "touch",
};
/**
* Look up a PointerType.
*
* @param {string} str
* Name of pointer type.
*
@@ -528,17 +529,17 @@ action.InputState.Key = class Key extend
/**
* Remove |key| from the set of pressed keys.
*
* @param {string} key
* Normalized key value.
*
* @return {boolean}
- * True if |key| is removed successfully, false otherwise.
+ * True if |key| was present before removal, false otherwise.
*/
release(key) {
return this.pressed.delete(key);
}
};
/**
* Input state not associated with a specific physical device.
@@ -563,16 +564,58 @@ action.InputState.Pointer = class Pointe
constructor(subtype) {
super();
this.pressed = new Set();
assert.defined(subtype, error.pprint`Expected subtype to be defined, got: ${subtype}`);
this.subtype = action.PointerType.get(subtype);
this.x = 0;
this.y = 0;
}
+
+ /**
+ * Check whether |button| is pressed.
+ *
+ * @param {number} button
+ * Positive integer that refers to a mouse button.
+ *
+ * @return {boolean}
+ * True if |button| is in set of pressed buttons.
+ */
+ isPressed(button) {
+ assert.positiveInteger(button);
+ return this.pressed.has(button);
+ }
+
+ /**
+ * Add |button| to the set of pressed keys.
+ *
+ * @param {number} button
+ * Positive integer that refers to a mouse button.
+ *
+ * @return {Set}
+ * Set of pressed buttons.
+ */
+ press(button) {
+ assert.positiveInteger(button);
+ return this.pressed.add(button);
+ }
+
+ /**
+ * Remove |button| from the set of pressed buttons.
+ *
+ * @param {number} button
+ * A positive integer that refers to a mouse button.
+ *
+ * @return {boolean}
+ * True if |button| was present before removals, false otherwise.
+ */
+ release(button) {
+ assert.positiveInteger(button);
+ return this.pressed.delete(button);
+ }
};
/**
* Repesents an action for dispatch. Used in |action.Chain| and |action.Sequence|.
*
* @param {string} id
* Input source ID.
* @param {string} type
@@ -853,16 +896,31 @@ action.Key = class {
update(inputState) {
this.altKey = inputState.alt;
this.shiftKey = inputState.shift;
this.ctrlKey = inputState.ctrl;
this.metaKey = inputState.meta;
}
};
+/** Collect properties associated with MouseEvent */
+action.Mouse = class {
+ constructor(type, button = 0) {
+ this.type = type;
+ assert.positiveInteger(button);
+ this.button = button;
+ this.buttons = 0;
+ }
+
+ update(inputState) {
+ let allButtons = Array.from(inputState.pressed);
+ this.buttons = allButtons.reduce((a, i) => a + Math.pow(2, i), 0);
+ }
+};
+
/**
* Dispatch a chain of actions over |chain.length| ticks.
*
* 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
@@ -957,17 +1015,21 @@ function toEvents(tickDuration, seenEls,
switch (a.subtype) {
case action.KeyUp:
return dispatchKeyUp(a, inputState, container.frame);
case action.KeyDown:
return dispatchKeyDown(a, inputState, container.frame);
case action.PointerDown:
+ return dispatchPointerDown(a, inputState, container.frame);
+
case action.PointerUp:
+ return dispatchPointerUp(a, inputState, container.frame);
+
case action.PointerMove:
case action.PointerCancel:
throw new UnsupportedOperationError();
case action.Pause:
return dispatchPause(a, tickDuration);
}
};
@@ -1030,16 +1092,94 @@ function dispatchKeyUp(a, inputState, wi
keyEvent.update(inputState);
event.sendKeyUp(keyEvent.key, keyEvent, win);
resolve();
});
}
/**
+ * Dispatch a pointerDown action equivalent to pressing a pointer-device
+ * button.
+ *
+ * @param {action.Action} a
+ * Action to dispatch.
+ * @param {action.InputState} inputState
+ * Input state for this action's input source.
+ * @param {nsIDOMWindow} win
+ * Current window.
+ *
+ * @return {Promise}
+ * Promise to dispatch at least a pointerdown event.
+ */
+function dispatchPointerDown(a, inputState, win) {
+ return new Promise(resolve => {
+ if (inputState.isPressed(a.button)) {
+ resolve();
+ return;
+ }
+ inputState.press(a.button);
+ // Append a copy of |a| with pointerUp subtype
+ action.inputsToCancel.push(Object.assign({}, a, {subtype: action.PointerUp}));
+ switch (inputState.subtype) {
+ case action.PointerType.Mouse:
+ let mouseEvent = new action.Mouse("mousedown", a.button);
+ mouseEvent.update(inputState);
+ event.synthesizeMouseAtPoint(inputState.x, inputState.y, mouseEvent, win);
+ break;
+ case action.PointerType.Pen:
+ case action.PointerType.Touch:
+ throw new UnsupportedOperationError("Only 'mouse' pointer type is supported.");
+ break;
+ default:
+ throw new TypeError(`Unknown pointer type: ${inputState.subtype}`);
+ }
+ resolve();
+ });
+}
+
+/**
+ * Dispatch a pointerUp action equivalent to releasing a pointer-device
+ * button.
+ *
+ * @param {action.Action} a
+ * Action to dispatch.
+ * @param {action.InputState} inputState
+ * Input state for this action's input source.
+ * @param {nsIDOMWindow} win
+ * Current window.
+ *
+ * @return {Promise}
+ * Promise to dispatch at least a pointerup event.
+ */
+function dispatchPointerUp(a, inputState, win) {
+ return new Promise(resolve => {
+ if (!inputState.isPressed(a.button)) {
+ resolve();
+ return;
+ }
+ inputState.release(a.button);
+ switch (inputState.subtype) {
+ case action.PointerType.Mouse:
+ let mouseEvent = new action.Mouse("mouseup", a.button);
+ mouseEvent.update(inputState);
+ event.synthesizeMouseAtPoint(inputState.x, inputState.y,
+ mouseEvent, win);
+ break;
+ case action.PointerType.Pen:
+ case action.PointerType.Touch:
+ throw new UnsupportedOperationError("Only 'mouse' pointer type is supported.");
+ default:
+ throw new TypeError(`Unknown pointer type: ${inputState.subtype}`);
+ }
+ resolve();
+ });
+}
+
+/**
* Dispatch a pause action equivalent waiting for |a.duration| milliseconds, or a
* default time interval of |tickDuration|.
*
* @param {action.Action} a
* Action to dispatch.
* @param {number} tickDuration
* Duration in milliseconds of this tick.
*
--- a/testing/web-platform/tests/webdriver/actions/mouse.py
+++ b/testing/web-platform/tests/webdriver/actions/mouse.py
@@ -1,8 +1,8 @@
from support.refine import get_events
-#def test_nothing(session, test_actions_page, mouse_chain):
-# mouse_chain \
-# .pointer_down(1) \
-# .perform()
-# assert True
+def test_nothing(session, test_actions_page, mouse_chain):
+ mouse_chain \
+ .pointer_down(0) \
+ .perform()
+ assert True