summaryrefslogtreecommitdiffstats
path: root/remote/webdriver-bidi/modules/windowglobal/input.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/webdriver-bidi/modules/windowglobal/input.sys.mjs')
-rw-r--r--remote/webdriver-bidi/modules/windowglobal/input.sys.mjs103
1 files changed, 103 insertions, 0 deletions
diff --git a/remote/webdriver-bidi/modules/windowglobal/input.sys.mjs b/remote/webdriver-bidi/modules/windowglobal/input.sys.mjs
new file mode 100644
index 0000000000..24259151ee
--- /dev/null
+++ b/remote/webdriver-bidi/modules/windowglobal/input.sys.mjs
@@ -0,0 +1,103 @@
+/* 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/. */
+
+import { WindowGlobalBiDiModule } from "chrome://remote/content/webdriver-bidi/modules/WindowGlobalBiDiModule.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ action: "chrome://remote/content/shared/webdriver/Actions.sys.mjs",
+ deserialize: "chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs",
+ element: "chrome://remote/content/marionette/element.sys.mjs",
+ error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
+});
+
+class InputModule extends WindowGlobalBiDiModule {
+ #actionState;
+
+ constructor(messageHandler) {
+ super(messageHandler);
+
+ this.#actionState = null;
+ }
+
+ destroy() {}
+
+ async performActions(options) {
+ const { actions } = options;
+ if (this.#actionState === null) {
+ this.#actionState = new lazy.action.State({
+ specCompatPointerOrigin: true,
+ });
+ }
+
+ await this.#deserializeActionOrigins(actions);
+ const actionChain = lazy.action.Chain.fromJSON(this.#actionState, actions);
+ await actionChain.dispatch(this.#actionState, this.messageHandler.window);
+ }
+
+ async releaseActions() {
+ if (this.#actionState === null) {
+ return;
+ }
+ await this.#actionState.release(this.messageHandler.window);
+ this.#actionState = null;
+ }
+
+ /**
+ * In the provided array of input.SourceActions, replace all origins matching
+ * the input.ElementOrigin production with the Element corresponding to this
+ * origin.
+ *
+ * Note that this method replaces the content of the `actions` in place, and
+ * does not return a new array.
+ *
+ * @param {Array<input.SourceActions>} actions
+ * The array of SourceActions to deserialize.
+ * @returns {Promise}
+ * A promise which resolves when all ElementOrigin origins have been
+ * deserialized.
+ */
+ async #deserializeActionOrigins(actions) {
+ const promises = [];
+ for (const actionsByTick of actions) {
+ for (const action of actionsByTick.actions) {
+ if (action.origin?.type === "element") {
+ promises.push(
+ (async () => {
+ action.origin = await this.#getElementFromElementOrigin(
+ action.origin
+ );
+ })()
+ );
+ }
+ }
+ }
+ return Promise.all(promises);
+ }
+
+ async #getElementFromElementOrigin(origin) {
+ const sharedReference = origin.element;
+ if (typeof sharedReference?.sharedId !== "string") {
+ throw new lazy.error.InvalidArgumentError(
+ `Expected "origin.element" to be a SharedReference, got: ${sharedReference}`
+ );
+ }
+
+ const realm = this.messageHandler.getRealm();
+
+ const element = lazy.deserialize(realm, sharedReference, {
+ nodeCache: this.nodeCache,
+ });
+ if (!lazy.element.isElement(element)) {
+ throw new lazy.error.NoSuchElementError(
+ `No element found for shared id: ${sharedReference.sharedId}`
+ );
+ }
+
+ return element;
+ }
+}
+
+export const input = InputModule;