Bug 1792529 - [marionette] Remove "Marionette:SingleTap" and legacy actions module. r=webdriver-reviewers,jgraham

Differential Revision: https://phabricator.services.mozilla.com/D158191
This commit is contained in:
Henrik Skupin 2023-06-29 10:10:39 +00:00
parent 3a0c1ccb2f
commit 7ebedfced8
7 changed files with 0 additions and 707 deletions

View File

@ -17,7 +17,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
evaluate: "chrome://remote/content/marionette/evaluate.sys.mjs",
interaction: "chrome://remote/content/marionette/interaction.sys.mjs",
json: "chrome://remote/content/marionette/json.sys.mjs",
legacyaction: "chrome://remote/content/marionette/legacyaction.sys.mjs",
Log: "chrome://remote/content/shared/Log.sys.mjs",
sandbox: "chrome://remote/content/marionette/evaluate.sys.mjs",
Sandboxes: "chrome://remote/content/marionette/evaluate.sys.mjs",
@ -47,17 +46,6 @@ export class MarionetteCommandsChild extends JSWindowActorChild {
return this.manager.innerWindowId;
}
/**
* Lazy getter to create a legacyaction Chain instance for touch events.
*/
get legacyactions() {
if (!this._legacyactions) {
this._legacyactions = new lazy.legacyaction.Chain();
}
return this._legacyactions;
}
actorCreated() {
lazy.logger.trace(
`[${this.browsingContext.id}] MarionetteCommands actor created ` +
@ -164,10 +152,6 @@ export class MarionetteCommandsChild extends JSWindowActorChild {
result = await this.sendKeysToElement(data);
waitForNextTick = true;
break;
case "MarionetteCommandsParent:singleTap":
result = await this.singleTap(data);
waitForNextTick = true;
break;
case "MarionetteCommandsParent:switchToFrame":
result = await this.switchToFrame(data);
waitForNextTick = true;
@ -551,14 +535,6 @@ export class MarionetteCommandsChild extends JSWindowActorChild {
return lazy.interaction.sendKeysToElement(elem, text, opts);
}
/**
* Perform a single tap.
*/
async singleTap(options = {}) {
const { capabilities, elem, x, y } = options;
return this.legacyactions.singleTap(elem, x, y, capabilities);
}
/**
* Switch to the specified frame.
*

View File

@ -249,15 +249,6 @@ export class MarionetteCommandsParent extends JSWindowActorParent {
return this.sendQuery("MarionetteCommandsParent:releaseActions");
}
async singleTap(webEl, x, y, capabilities) {
return this.sendQuery("MarionetteCommandsParent:singleTap", {
capabilities: capabilities.toJSON(),
elem: webEl,
x,
y,
});
}
async switchToFrame(id) {
const { browsingContextId } = await this.sendQuery(
"MarionetteCommandsParent:switchToFrame",
@ -339,7 +330,6 @@ export function getMarionetteCommandsActorProxy(browsingContextFn) {
"performActions",
"releaseActions",
"sendKeysToElement",
"singleTap",
];
return new Proxy(

View File

@ -1410,21 +1410,6 @@ GeckoDriver.prototype.setTimeouts = function (cmd) {
this.currentSession.timeouts = lazy.Timeouts.fromJSON(merged);
};
/** Single tap. */
GeckoDriver.prototype.singleTap = async function (cmd) {
lazy.assert.open(this.getBrowsingContext());
let { id, x, y } = cmd.parameters;
let webEl = lazy.WebElement.fromUUID(id).toJSON();
await this.getActor().singleTap(
webEl,
x,
y,
this.currentSession.capabilities
);
};
/**
* Perform a series of grouped actions at the specified points in time.
*
@ -3299,7 +3284,6 @@ GeckoDriver.prototype.commands = {
"Marionette:Quit": GeckoDriver.prototype.quit,
"Marionette:SetContext": GeckoDriver.prototype.setContext,
"Marionette:SetScreenOrientation": GeckoDriver.prototype.setScreenOrientation,
"Marionette:SingleTap": GeckoDriver.prototype.singleTap,
// Addon service
"Addon:Install": GeckoDriver.prototype.installAddon,

View File

@ -96,11 +96,6 @@ event.DoubleClickTracker = {
},
};
// Only used by legacyactions.js
event.parseModifiers_ = function (modifiers, win) {
return _getEventUtils(win)._parseModifiers(modifiers);
};
/**
* Synthesise a mouse event at a point.
*

View File

@ -22,7 +22,6 @@ remote.jar:
content/marionette/interaction.sys.mjs (interaction.sys.mjs)
content/marionette/json.sys.mjs (json.sys.mjs)
content/marionette/l10n.sys.mjs (l10n.sys.mjs)
content/marionette/legacyaction.sys.mjs (legacyaction.sys.mjs)
content/marionette/message.sys.mjs (message.sys.mjs)
content/marionette/modal.sys.mjs (modal.sys.mjs)
content/marionette/navigate.sys.mjs (navigate.sys.mjs)

View File

@ -1,640 +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/. */
/* eslint-disable no-restricted-globals */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
Preferences: "resource://gre/modules/Preferences.sys.mjs",
accessibility: "chrome://remote/content/marionette/accessibility.sys.mjs",
dom: "chrome://remote/content/shared/DOM.sys.mjs",
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
json: "chrome://remote/content/marionette/json.sys.mjs",
event: "chrome://remote/content/marionette/event.sys.mjs",
Log: "chrome://remote/content/shared/Log.sys.mjs",
WebElement: "chrome://remote/content/marionette/web-reference.sys.mjs",
});
XPCOMUtils.defineLazyGetter(lazy, "logger", () =>
lazy.Log.get(lazy.Log.TYPES.MARIONETTE)
);
const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms
/** @namespace */
export const legacyaction = {};
const action = legacyaction;
/**
* Functionality for (single finger) action chains.
*/
action.Chain = function () {
// 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);
// determines if we create touch events
this.inputSource = null;
};
/**
* Create a touch based event.
*
* @param {Element} elem
* The Element on which the touch event should be created.
* @param {number} x
* x coordinate relative to the viewport.
* @param {number} y
* y coordinate relative to the viewport.
* @param {number} touchId
* Touch event id used by legacyactions.
*/
action.Chain.prototype.createATouch = function (elem, x, y, touchId) {
const doc = elem.ownerDocument;
const win = doc.defaultView;
const [clientX, clientY, pageX, pageY, screenX, screenY] =
this.getCoordinateInfo(elem, x, y);
const atouch = doc.createTouch(
win,
elem,
touchId,
pageX,
pageY,
screenX,
screenY,
clientX,
clientY
);
return atouch;
};
action.Chain.prototype.dispatchActions = function (
args,
touchId,
container,
seenEls
) {
this.seenEls = seenEls;
this.container = container;
let commandArray = lazy.json.deserialize(args, seenEls, container.frame);
if (touchId == null) {
touchId = this.nextTSouchId++;
}
if (!container.frame.document.createTouch) {
this.mouseEventsOnly = true;
}
let keyModifiers = {
shiftKey: false,
ctrlKey: false,
altKey: false,
metaKey: false,
};
return new Promise(resolve => {
this.actions(commandArray, touchId, 0, keyModifiers, resolve);
}).catch(this.resetValues.bind(this));
};
/**
* This function emit mouse event.
*
* @param {Document} doc
* Current document.
* @param {string} type
* Type of event to dispatch.
* @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 {number} button
* The button number.
* @param {number} clickCount
* Number of clicks, button notes the mouse button.
* @param {object} modifiers
* An object of modifier keys present.
*/
action.Chain.prototype.emitMouseEvent = function (
doc,
type,
elClientX,
elClientY,
button,
clickCount,
modifiers
) {
lazy.logger.debug(
`Emitting ${type} mouse event ` +
`at coordinates (${elClientX}, ${elClientY}) ` +
`relative to the viewport, ` +
`button: ${button}, ` +
`clickCount: ${clickCount}`
);
let win = doc.defaultView;
let domUtils = win.windowUtils;
let mods;
if (typeof modifiers != "undefined") {
mods = lazy.event.parseModifiers_(modifiers, win);
} else {
mods = 0;
}
domUtils.sendMouseEvent(
type,
elClientX,
elClientY,
button || 0,
clickCount || 1,
mods,
false,
0,
this.inputSource
);
};
action.Chain.prototype.emitTouchEvent = function (doc, type, touch) {
lazy.logger.info(
`Emitting Touch event of type ${type} ` +
`to element with id: ${touch.target.id} ` +
`and tag name: ${touch.target.tagName} ` +
`at coordinates (${touch.clientX}), ` +
`${touch.clientY}) relative to the viewport`
);
const win = doc.defaultView;
if (win.docShell.asyncPanZoomEnabled && this.scrolling) {
lazy.logger.debug(
`Cannot emit touch event with asyncPanZoomEnabled and legacyactions.scrolling`
);
return;
}
// we get here if we're not in asyncPacZoomEnabled land, or if we're
// the main process
win.windowUtils.sendTouchEvent(
type,
[touch.identifier],
[touch.clientX],
[touch.clientY],
[touch.radiusX],
[touch.radiusY],
[touch.rotationAngle],
[touch.force],
[0],
[0],
[0],
0
);
};
/**
* Reset any persisted values after a command completes.
*/
action.Chain.prototype.resetValues = function () {
this.container = null;
this.seenEls = null;
this.mouseEventsOnly = false;
};
/**
* Function that performs a single tap.
*/
action.Chain.prototype.singleTap = async function (
el,
corx,
cory,
capabilities
) {
const doc = el.ownerDocument;
// after this block, the element will be scrolled into view
let visible = lazy.dom.isVisible(el, corx, cory);
if (!visible) {
throw new lazy.error.ElementNotInteractableError(
"Element is not currently visible and may not be manipulated"
);
}
let a11y = lazy.accessibility.get(capabilities["moz:accessibilityChecks"]);
let acc = await a11y.assertAccessible(el, true);
a11y.assertVisible(acc, el, visible);
a11y.assertActionable(acc, el);
if (!doc.createTouch) {
this.mouseEventsOnly = true;
}
let c = lazy.dom.coordinates(el, corx, cory);
if (!this.mouseEventsOnly) {
let touchId = this.nextTouchId++;
let touch = this.createATouch(el, c.x, c.y, touchId);
this.emitTouchEvent(doc, "touchstart", touch);
this.emitTouchEvent(doc, "touchend", touch);
}
this.mouseTap(doc, c.x, c.y);
};
/**
* 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.
*
* @returns {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 webEl;
let el;
let c;
i++;
if (!["press", "wait", "keyDown", "keyUp", "click"].includes(command)) {
// if mouseEventsOnly, then touchIds isn't used
if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
this.resetValues();
throw new lazy.error.WebDriverError("Element has not been pressed");
}
}
switch (command) {
case "keyDown":
lazy.event.sendKeyDown(pack[1], keyModifiers, this.container.frame);
this.actions(chain, touchId, i, keyModifiers, cb);
break;
case "keyUp":
lazy.event.sendKeyUp(pack[1], keyModifiers, this.container.frame);
this.actions(chain, touchId, i, keyModifiers, cb);
break;
case "click":
webEl = lazy.WebElement.fromUUID(pack[1]);
el = this.seenEls.get(webEl);
let button = pack[2];
let clickCount = pack[3];
c = lazy.dom.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 lazy.error.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].includes("move")) {
this.scrolling = true;
}
webEl = lazy.WebElement.fromUUID(pack[1]);
el = this.seenEls.get(webEl);
c = lazy.dom.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":
webEl = lazy.WebElement.fromUUID(pack[1]);
el = this.seenEls.get(webEl);
c = lazy.dom.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 = lazy.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.ownerGlobal;
return [
corx, // clientX
cory, // clientY
corx + win.pageXOffset, // pageX
cory + win.pageYOffset, // pageY
corx + win.mozInnerScreenX, // screenX
cory + win.mozInnerScreenY, // screenY
];
};
/**
* @param {string} type
* The event type (eg "tap", "press", ...).
* @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.
* @param {number} touchId
* The current touch id.
* @param {Element} target
* The Element on which the events should be created.
*/
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) {
let touch = this.createATouch(target, x, y, touchId);
this.mouseTap(
touch.target.ownerDocument,
touch.clientX,
touch.clientY,
null,
null,
keyModifiers
);
} else {
touchId = this.nextTouchId++;
let touch = this.createATouch(target, x, y, touchId);
this.emitTouchEvent(doc, "touchstart", touch);
this.emitTouchEvent(doc, "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.createATouch(target, x, y, touchId);
this.emitTouchEvent(doc, "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.createATouch(touch.target, x, y, touchId);
this.emitTouchEvent(doc, "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.emitTouchEvent(doc, "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.createATouch(
this.touchIds[touchId].target,
x,
y,
touchId
);
this.touchIds[touchId] = touch;
this.emitTouchEvent(doc, "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, , , screenX, screenY] = this.getCoordinateInfo(
target,
x,
y
);
event.initMouseEvent(
"contextmenu",
true,
true,
target.ownerGlobal,
1,
screenX,
screenY,
clientX,
clientY,
false,
false,
false,
false,
0,
null
);
target.dispatchEvent(event);
break;
default:
throw new lazy.error.WebDriverError("Unknown event type: " + type);
}
return null;
};
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);
};

View File

@ -255,17 +255,6 @@ class HTMLElement(object):
"""Simulates a click on the element."""
self.marionette._send_message("WebDriver:ElementClick", {"id": self.id})
def tap(self, x=None, y=None):
"""Simulates a set of tap events on the element.
:param x: X coordinate of tap event. If not given, default to
the centre of the element.
:param y: Y coordinate of tap event. If not given, default to
the centre of the element.
"""
body = {"id": self.id, "x": x, "y": y}
self.marionette._send_message("Marionette:SingleTap", body)
@property
def text(self):
"""Returns the visible text of the element, and its child elements."""