summaryrefslogtreecommitdiffstats
path: root/remote/marionette/event.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/marionette/event.sys.mjs')
-rw-r--r--remote/marionette/event.sys.mjs317
1 files changed, 317 insertions, 0 deletions
diff --git a/remote/marionette/event.sys.mjs b/remote/marionette/event.sys.mjs
new file mode 100644
index 0000000000..cf5ff717a2
--- /dev/null
+++ b/remote/marionette/event.sys.mjs
@@ -0,0 +1,317 @@
+/* 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, {
+ keyData: "chrome://remote/content/shared/webdriver/KeyData.sys.mjs",
+});
+
+/** Provides functionality for creating and sending DOM events. */
+export const event = {};
+
+XPCOMUtils.defineLazyGetter(lazy, "dblclickTimer", () => {
+ return Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+});
+
+const _eventUtils = new WeakMap();
+
+function _getEventUtils(win) {
+ if (!_eventUtils.has(win)) {
+ const eventUtilsObject = {
+ window: win,
+ parent: win,
+ _EU_Ci: Ci,
+ _EU_Cc: Cc,
+ };
+ Services.scriptloader.loadSubScript(
+ "chrome://remote/content/external/EventUtils.js",
+ eventUtilsObject
+ );
+ _eventUtils.set(win, eventUtilsObject);
+ }
+ return _eventUtils.get(win);
+}
+
+// Max interval between two clicks that should result in a dblclick (in ms)
+const DBLCLICK_INTERVAL = 640;
+
+event.MouseEvents = {
+ click: 0,
+ dblclick: 1,
+ mousedown: 2,
+ mouseup: 3,
+ mouseover: 4,
+ mouseout: 5,
+};
+
+event.Modifiers = {
+ shiftKey: 0,
+ ctrlKey: 1,
+ altKey: 2,
+ metaKey: 3,
+};
+
+event.MouseButton = {
+ isPrimary(button) {
+ return button === 0;
+ },
+ isAuxiliary(button) {
+ return button === 1;
+ },
+ isSecondary(button) {
+ return button === 2;
+ },
+};
+
+event.DoubleClickTracker = {
+ firstClick: false,
+ isClicked() {
+ return event.DoubleClickTracker.firstClick;
+ },
+ setClick() {
+ if (!event.DoubleClickTracker.firstClick) {
+ event.DoubleClickTracker.firstClick = true;
+ event.DoubleClickTracker.startTimer();
+ }
+ },
+ resetClick() {
+ event.DoubleClickTracker.firstClick = false;
+ event.DoubleClickTracker.cancelTimer();
+ },
+ startTimer() {
+ lazy.dblclickTimer.initWithCallback(
+ event.DoubleClickTracker.resetClick,
+ DBLCLICK_INTERVAL,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+ },
+ cancelTimer() {
+ lazy.dblclickTimer.cancel();
+ },
+};
+
+// Only used by legacyactions.js
+event.parseModifiers_ = function (modifiers, win) {
+ return _getEventUtils(win)._parseModifiers(modifiers);
+};
+
+/**
+ * Synthesise a mouse event at a point.
+ *
+ * If the type is specified in opts, an mouse event of that type is
+ * fired. Otherwise, a mousedown followed by a mouseup is performed.
+ *
+ * @param {number} left
+ * Offset from viewport left, in CSS pixels
+ * @param {number} top
+ * Offset from viewport top, in CSS pixels
+ * @param {object} opts
+ * Object which may contain the properties "shiftKey", "ctrlKey",
+ * "altKey", "metaKey", "accessKey", "clickCount", "button", and
+ * "type".
+ * @param {Window} win
+ * Window object.
+ *
+ * @returns {boolean} defaultPrevented
+ */
+event.synthesizeMouseAtPoint = function (left, top, opts, win) {
+ return _getEventUtils(win).synthesizeMouseAtPoint(left, top, opts, win);
+};
+
+/**
+ * Synthesise a touch event at a point.
+ *
+ * If the type is specified in opts, a touch event of that type is
+ * fired. Otherwise, a touchstart followed by a touchend is performed.
+ *
+ * @param {number} left
+ * Offset from viewport left, in CSS pixels
+ * @param {number} top
+ * Offset from viewport top, in CSS pixels
+ * @param {object} opts
+ * Object which may contain the properties "id", "rx", "ry", "angle",
+ * "force", "shiftKey", "ctrlKey", "altKey", "metaKey", "accessKey",
+ * "type".
+ * @param {Window} win
+ * Window object.
+ *
+ * @returns {boolean} defaultPrevented
+ */
+event.synthesizeTouchAtPoint = function (left, top, opts, win) {
+ return _getEventUtils(win).synthesizeTouchAtPoint(left, top, opts, win);
+};
+
+/**
+ * Synthesise a wheel scroll event at a point.
+ *
+ * @param {number} left
+ * Offset from viewport left, in CSS pixels
+ * @param {number} top
+ * Offset from viewport top, in CSS pixels
+ * @param {object} opts
+ * Object which may contain the properties "shiftKey", "ctrlKey",
+ * "altKey", "metaKey", "accessKey", "deltaX", "deltaY", "deltaZ",
+ * "deltaMode", "lineOrPageDeltaX", "lineOrPageDeltaY", "isMomentum",
+ * "isNoLineOrPageDelta", "isCustomizedByPrefs", "expectedOverflowDeltaX",
+ * "expectedOverflowDeltaY"
+ * @param {Window} win
+ * Window object.
+ */
+event.synthesizeWheelAtPoint = function (left, top, opts, win) {
+ return _getEventUtils(win).synthesizeWheelAtPoint(left, top, opts, win);
+};
+
+event.synthesizeMultiTouch = function (opts, win) {
+ const modifiers = _getEventUtils(win)._parseModifiers(opts);
+ win.windowUtils.sendTouchEvent(
+ opts.type,
+ opts.id,
+ opts.x,
+ opts.y,
+ opts.rx,
+ opts.ry,
+ opts.angle,
+ opts.force,
+ opts.tiltx,
+ opts.tilty,
+ opts.twist,
+ modifiers
+ );
+};
+
+/**
+ * Synthesize a keydown event for a single key.
+ *
+ * @param {object} key
+ * Key data as returned by keyData.getData
+ * @param {Window} win
+ * Window object.
+ */
+event.sendKeyDown = function (key, win) {
+ event.sendSingleKey(key, win, "keydown");
+};
+
+/**
+ * Synthesize a keyup event for a single key.
+ *
+ * @param {object} key
+ * Key data as returned by keyData.getData
+ * @param {Window} win
+ * Window object.
+ */
+event.sendKeyUp = function (key, win) {
+ event.sendSingleKey(key, win, "keyup");
+};
+
+/**
+ * Synthesize a key event for a single key.
+ *
+ * @param {object} key
+ * Key data as returned by keyData.getData
+ * @param {Window} win
+ * Window object.
+ * @param {string=} type
+ * Event to emit. By default the full keydown/keypressed/keyup event
+ * sequence is emitted.
+ */
+event.sendSingleKey = function (key, win, type = null) {
+ let keyValue = key.key;
+ if (!key.printable) {
+ keyValue = `KEY_${keyValue}`;
+ }
+ const event = {
+ code: key.code,
+ location: key.location,
+ altKey: key.altKey ?? false,
+ shiftKey: key.shiftKey ?? false,
+ ctrlKey: key.ctrlKey ?? false,
+ metaKey: key.metaKey ?? false,
+ repeat: key.repeat ?? false,
+ };
+ if (type) {
+ event.type = type;
+ }
+ _getEventUtils(win).synthesizeKey(keyValue, event, win);
+};
+
+/**
+ * Send a string as a series of keypresses.
+ *
+ * @param {string} keyString
+ * Sequence of characters to send as key presses
+ * @param {Window} win
+ * Window object
+ */
+event.sendKeys = function (keyString, win) {
+ const modifiers = {};
+ for (let modifier in event.Modifiers) {
+ modifiers[modifier] = false;
+ }
+
+ for (let i = 0; i < keyString.length; i++) {
+ let keyValue = keyString.charAt(i);
+ if (modifiers.shiftKey) {
+ keyValue = lazy.keyData.getShiftedKey(keyValue);
+ }
+ const data = lazy.keyData.getData(keyValue);
+ const key = { ...data, ...modifiers };
+ if (data.modifier) {
+ // Negating the state of the modifier here is not spec compliant but
+ // makes us compatible to Chrome's behavior for now. That's fine unless
+ // we know the correct behavior.
+ //
+ // @see: https://github.com/w3c/webdriver/issues/1734
+ modifiers[data.modifier] = !modifiers[data.modifier];
+ }
+ event.sendSingleKey(key, win);
+ }
+};
+
+event.sendEvent = function (eventType, el, modifiers = {}, opts = {}) {
+ opts.canBubble = opts.canBubble || true;
+
+ let doc = el.ownerDocument || el.document;
+ let ev = doc.createEvent("Event");
+
+ ev.shiftKey = modifiers.shift;
+ ev.metaKey = modifiers.meta;
+ ev.altKey = modifiers.alt;
+ ev.ctrlKey = modifiers.ctrl;
+
+ ev.initEvent(eventType, opts.canBubble, true);
+ el.dispatchEvent(ev);
+};
+
+event.mouseover = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("mouseover", el, modifiers, opts);
+};
+
+event.mousemove = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("mousemove", el, modifiers, opts);
+};
+
+event.mousedown = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("mousedown", el, modifiers, opts);
+};
+
+event.mouseup = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("mouseup", el, modifiers, opts);
+};
+
+event.click = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("click", el, modifiers, opts);
+};
+
+event.change = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("change", el, modifiers, opts);
+};
+
+event.input = function (el, modifiers = {}, opts = {}) {
+ return event.sendEvent("input", el, modifiers, opts);
+};