false,
specCompat = false
) {
const a11y = lazy.accessibility.get(strict);
if (lazy.dom.isXULElement(el)) {
await chromeClick(el, a11y);
} else if (specCompat) {
await webdriverClickElement(el, a11y);
} else {
lazy.logger.trace(`Using non spec-compatible element click`);
await seleniumClickElement(el, a11y);
}
};
async function webdriverClickElement(el, a11y) {
const win = getWindow(el);
// step 3
if (el.localName == "input" && el.type == "file") {
throw new lazy.error.InvalidArgumentError(
"Cannot click elements"
);
}
let containerEl = lazy.dom.getContainer(el);
// step 4
if (!lazy.dom.isInView(containerEl)) {
lazy.dom.scrollIntoView(containerEl);
}
// step 5
// TODO(ato): wait for containerEl to be in view
// step 6
// if we cannot bring the container element into the viewport
// there is no point in checking if it is pointer-interactable
if (!lazy.dom.isInView(containerEl)) {
throw new lazy.error.ElementNotInteractableError(
lazy.pprint`Element ${el} could not be scrolled into view`
);
}
// step 7
let rects = containerEl.getClientRects();
let clickPoint = lazy.dom.getInViewCentrePoint(rects[0], win);
if (lazy.dom.isObscured(containerEl)) {
throw new lazy.error.ElementClickInterceptedError(
null,
{},
containerEl,
clickPoint
);
}
let acc = await a11y.assertAccessible(el, true);
a11y.assertVisible(acc, el, true);
a11y.assertEnabled(acc, el, true);
a11y.assertActionable(acc, el);
// step 8
if (el.localName == "option") {
interaction.selectOption(el);
} else {
// Synthesize a pointerMove action.
await lazy.event.synthesizeMouseAtPoint(
clickPoint.x,
clickPoint.y,
{
type: "mousemove",
allowToHandleDragDrop: true,
},
win
);
if (lazy.dragService?.getCurrentSession(win)) {
// Special handling is required if the mousemove started a drag session.
// In this case, mousedown event shouldn't be fired, and the mouseup should
// end the session. Therefore, we should synthesize only mouseup.
await lazy.event.synthesizeMouseAtPoint(
clickPoint.x,
clickPoint.y,
{
type: "mouseup",
allowToHandleDragDrop: true,
},
win
);
} else {
// step 9
let clicked = interaction.flushEventLoop(containerEl);
// Synthesize a pointerDown + pointerUp action.
await lazy.event.synthesizeMouseAtPoint(
clickPoint.x,
clickPoint.y,
{ allowToHandleDragDrop: true },
win
);
await clicked;
}
}
// step 10
// if the click causes navigation, the post-navigation checks are
// handled by navigate.js
}
async function chromeClick(el, a11y) {
if (!(await lazy.dom.isEnabled(el))) {
throw new lazy.error.InvalidElementStateError("Element is not enabled");
}
let acc = await a11y.assertAccessible(el, true);
a11y.assertVisible(acc, el, true);
a11y.assertEnabled(acc, el, true);
a11y.assertActionable(acc, el);
if (el.localName == "option") {
interaction.selectOption(el);
} else {
el.click();
}
}
async function seleniumClickElement(el, a11y) {
let win = getWindow(el);
let visibilityCheckEl = el;
if (el.localName == "option") {
visibilityCheckEl = lazy.dom.getContainer(el);
}
if (!(await lazy.dom.isVisible(visibilityCheckEl))) {
throw new lazy.error.ElementNotInteractableError();
}
if (!(await lazy.dom.isEnabled(el))) {
throw new lazy.error.InvalidElementStateError("Element is not enabled");
}
let acc = await a11y.assertAccessible(el, true);
a11y.assertVisible(acc, el, true);
a11y.assertEnabled(acc, el, true);
a11y.assertActionable(acc, el);
if (el.localName == "option") {
interaction.selectOption(el);
} else {
let rects = el.getClientRects();
let centre = lazy.dom.getInViewCentrePoint(rects[0], win);
let opts = {};
await lazy.event.synthesizeMouseAtPoint(centre.x, centre.y, opts, win);
}
}
/**
* Select <option> element in a <select>
* list.
*
* Because the dropdown list of select elements are implemented using
* native widget technology, our trusted synthesised events are not able
* to reach them. Dropdowns are instead handled mimicking DOM events,
* which for obvious reasons is not ideal, but at the current point in
* time considered to be good enough.
*
* @param {HTMLOptionElement} el
* Option element to select.
*
* @throws {TypeError}
* If el is a XUL element or not an <option>
* element.
* @throws {Error}
* If unable to find el's parent <select>
* element.
*/
interaction.selectOption = function (el) {
if (lazy.dom.isXULElement(el)) {
throw new TypeError("XUL dropdowns not supported");
}
if (el.localName != "option") {
throw new TypeError(lazy.pprint`Expected