Commit e1061170 authored by Julian Descottes's avatar Julian Descottes
Browse files

Bug 1832031 - [bidi] Deserialize action element origins early in input module...

Bug 1832031 - [bidi] Deserialize action element origins early in input module r=webdriver-reviewers,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D177467
parent 13aa6a0f
Loading
Loading
Loading
Loading
+15 −41
Original line number Diff line number Diff line
@@ -446,35 +446,19 @@ class Origin {

  /**
   * @param {Element|string=} origin - Type of orgin, one of "viewport", "pointer", element or undefined.
   * @param {object} actionOptions
   * @param {Function} actionOptions.getElementFromElementOrigin
   *     Optional function to use to retrieve an element from an ElementOrigin.
   *
   * @returns {Origin} - An origin object representing the origin.
   *
   * @throws {InvalidArgumentError}
   *     If <code>origin</code> isn't a valid origin.
   */
  static fromJSON(origin, actionOptions) {
  static fromJSON(origin) {
    if (origin === undefined || origin === "viewport") {
      return new ViewportOrigin();
    }
    if (origin === "pointer") {
      return new PointerOrigin();
    }

    // Only the BiDi input module should provides a getElementFromElementOrigin
    // callback and supports input.ElementOrigin origins as defined at:
    //   https://w3c.github.io/webdriver-bidi/#type-input-origin
    if (
      origin?.type === "element" &&
      actionOptions?.getElementFromElementOrigin
    ) {
      return new ElementOrigin(
        actionOptions.getElementFromElementOrigin(origin)
      );
    }

    if (lazy.element.isElement(origin)) {
      return new ElementOrigin(origin);
    }
@@ -575,7 +559,7 @@ class Action {
   *     If any <code>actionSequence</code> or <code>actionItem</code>
   *     attributes are invalid.
   */
  static fromJSON(type, id, actionItem, actionOptions) {
  static fromJSON(type, id, actionItem) {
    const subtype = actionItem.type;
    const subtypeMap = actionTypes.get(type);
    if (subtypeMap === undefined) {
@@ -591,7 +575,7 @@ class Action {
        `Unknown subtype ${subtype} for type ${type}`
      );
    }
    return cls.fromJSON(id, actionItem, actionOptions);
    return cls.fromJSON(id, actionItem);
  }
}

@@ -636,7 +620,7 @@ class PauseAction extends NullAction {
    return lazy.Sleep(ms);
  }

  static fromJSON(id, actionItem, actionOptions) {
  static fromJSON(id, actionItem) {
    const { duration } = actionItem;
    if (duration !== undefined) {
      lazy.assert.positiveInteger(
@@ -672,7 +656,7 @@ class KeyAction extends Action {
    return new KeyEventData(value);
  }

  static fromJSON(id, actionItem, actionOptions) {
  static fromJSON(id, actionItem) {
    // TODO countGraphemes
    // TODO key.value could be a single code point like "\uE012"
    // (see rawKey) or "grapheme cluster"
@@ -928,7 +912,7 @@ class PointerDownAction extends PointerAction {
    });
  }

  static fromJSON(id, actionItem, actionOptions) {
  static fromJSON(id, actionItem) {
    const props = PointerAction.validateCommon(actionItem);
    const { button } = actionItem;
    lazy.assert.positiveInteger(
@@ -982,7 +966,7 @@ class PointerUpAction extends PointerAction {
    });
  }

  static fromJSON(id, actionItem, actionOptions) {
  static fromJSON(id, actionItem) {
    const props = PointerAction.validateCommon(actionItem);
    const { button } = actionItem;
    lazy.assert.positiveInteger(
@@ -1083,7 +1067,7 @@ class PointerMoveAction extends PointerAction {
    inputSource.y = target[1];
  }

  static fromJSON(id, actionItem, actionOptions) {
  static fromJSON(id, actionItem) {
    const props = PointerAction.validateCommon(actionItem);
    const { duration, origin, x, y } = actionItem;
    if (duration !== undefined) {
@@ -1092,7 +1076,7 @@ class PointerMoveAction extends PointerAction {
        lazy.pprint`Expected 'duration' (${duration}) to be >= 0`
      );
    }
    const originObject = Origin.fromJSON(origin, actionOptions);
    const originObject = Origin.fromJSON(origin);
    lazy.assert.integer(x, lazy.pprint`Expected 'x' (${x}) to be an Integer`);
    lazy.assert.integer(y, lazy.pprint`Expected 'y' (${y}) to be an Integer`);
    props.duration = duration;
@@ -1135,7 +1119,7 @@ class WheelScrollAction extends WheelAction {
    this.deltaY = deltaY;
  }

  static fromJSON(id, actionItem, actionOptions) {
  static fromJSON(id, actionItem) {
    const { duration, origin, x, y, deltaX, deltaY } = actionItem;
    if (duration !== undefined) {
      lazy.assert.positiveInteger(
@@ -1143,7 +1127,7 @@ class WheelScrollAction extends WheelAction {
        lazy.pprint`Expected 'duration' (${duration}) to be >= 0`
      );
    }
    const originObject = Origin.fromJSON(origin, actionOptions);
    const originObject = Origin.fromJSON(origin);
    lazy.assert.integer(x, lazy.pprint`Expected 'x' (${x}) to be an Integer`);
    lazy.assert.integer(y, lazy.pprint`Expected 'y' (${y}) to be an Integer`);
    lazy.assert.integer(
@@ -1792,15 +1776,12 @@ action.Chain = class extends Array {
   * @param {State} state - Actions state.
   * @param {Array.<object>} actions - Array of objects that each
   * represent an action sequence.
   * @param {object} actionOptions
   * @param {Function} actionOptions.getElementFromElementOrigin
   *     Optional function to use to retrieve an element from an ElementOrigin.
   * @returns {action.Chain} - Object that allows dispatching a chain
   * of actions.
   * @throws {InvalidArgumentError} - If actions doesn't correspond to
   * a valid action chain.
   */
  static fromJSON(state, actions, actionOptions) {
  static fromJSON(state, actions) {
    lazy.assert.array(
      actions,
      lazy.pprint`Expected 'actions' to be an array, got ${actions}`
@@ -1808,11 +1789,7 @@ action.Chain = class extends Array {

    const actionsByTick = new this();
    for (const actionSequence of actions) {
      const inputSourceActions = Sequence.fromJSON(
        state,
        actionSequence,
        actionOptions
      );
      const inputSourceActions = Sequence.fromJSON(state, actionSequence);
      for (let i = 0; i < inputSourceActions.length; i++) {
        // new tick
        if (actionsByTick.length < i + 1) {
@@ -1924,12 +1901,9 @@ class Sequence extends Array {
   * @param {State} state - Actions state.
   * @param {object} actionSequence
   *     Protocol representation of the actions for a specific input source.
   * @param {object} actionOptions
   * @param {Function} actionOptions.getElementFromElementOrigin
   *     Optional function to use to retrieve an element from an ElementOrigin.
   * @returns {Array.<Array>} - Array of [InputSource?,Action|TouchActionGroup]
   */
  static fromJSON(state, actionSequence, actionOptions) {
  static fromJSON(state, actionSequence) {
    // used here to validate 'type' in addition to InputSource type below
    const { id, type, actions } = actionSequence;

@@ -1945,7 +1919,7 @@ class Sequence extends Array {

    const sequence = new this();
    for (const actionItem of actions) {
      sequence.push(Action.fromJSON(type, id, actionItem, actionOptions));
      sequence.push(Action.fromJSON(type, id, actionItem));
    }

    return sequence;
+36 −6
Original line number Diff line number Diff line
@@ -31,10 +31,8 @@ class InputModule extends WindowGlobalBiDiModule {
      });
    }

    const actionChain = lazy.action.Chain.fromJSON(this.#actionState, actions, {
      getElementFromElementOrigin: this.#getElementFromElementOrigin,
    });

    await this.#deserializeActionOrigins(actions);
    const actionChain = lazy.action.Chain.fromJSON(this.#actionState, actions);
    await actionChain.dispatch(this.#actionState, this.messageHandler.window);
  }

@@ -46,7 +44,39 @@ class InputModule extends WindowGlobalBiDiModule {
    this.#actionState = null;
  }

  #getElementFromElementOrigin = origin => {
  /**
   * 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(
@@ -58,7 +88,7 @@ class InputModule extends WindowGlobalBiDiModule {
    return lazy.deserialize(realm, sharedReference, {
      nodeCache: this.nodeCache,
    });
  };
  }
}

export const input = InputModule;