chromium/chrome/test/data/xr/e2e_test_files/html/test_webxr_gamepad_support.html

<!doctype html>
<!--
Tests that a Gamepad API gamepad is or is not returned under different
circumstances.
-->
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
  </head>
  <body>
    <canvas id="webgl-canvas"></canvas>
    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
    <script src="../resources/webxr_e2e.js"></script>
    <script src="../resources/webxr_boilerplate.js"></script>
    <script>
      // We apparently need to register a listener, otherwise all gamepads are
      // always null.
      window.addEventListener("gamepadconnected", function(e) {});

      let selectCount = 0;

      function onSelect() {
        selectCount++;
        console.log('Got select event number ' + selectCount);
      }

      function currentImmersiveSession() {
        return sessionInfos[sessionTypes.IMMERSIVE].currentSession;
      }

      function stepSetupListeners() {
        currentImmersiveSession().addEventListener('select', onSelect, false);
      }

      function navigatorGamepadCount() {
        let numGamepads = 0;
        for (gamepad of navigator.getGamepads()) {
          if (gamepad !== null) {
            numGamepads++;
          }
        }
        return numGamepads;
      }

      function inputSourceCount() {
        return currentImmersiveSession().inputSources.length;
      }

      function inputSourceWithGamepadCount() {
        let numGamepads = 0;
        for (source of currentImmersiveSession().inputSources) {
          if (source.gamepad !== null) {
            numGamepads++;
          }
        }
        return numGamepads;
      }

      // Especially on Android, there can be a small delay between when the test
      // code sends simulated input and when that input is registered by WebXR.
      // These functions return bools instead of asserting so that test code can
      // poll on the return values to avoid race conditions.
      function inputSourceHasNoGamepad() {
        // We don't expect to have attached any non-vr gamepads, and vr
        // gamepads shouldn't show up in navigator.getGamepads()
        if (navigatorGamepadCount() != 0) {
          return false;
        }

        // There should only be one input source, but there should not be
        // enough data to make a gamepad.
        if (inputSourceCount() != 1) {
          return false;
        }
        if (inputSourceWithGamepadCount() != 0) {
          return false;
        }

        return true;
      }

      // Especially on Android, there can be a small delay between when the test
      // code sends simulated input and when that input is registered by WebXR.
      // These functions return bools instead of asserting so that test code can
      // poll on the return values to avoid race conditions.
      function isGamepadAsExpected(verificationFunction, index) {
        if (index === undefined) {
          index = 0;
        }

        if (index >= inputSourceCount()) {
          return false;
        }

        // We don't expect to have attached any non-vr gamepads, and vr
        // gamepads shouldn't show up in navigator.getGamepads()
        if (navigatorGamepadCount() != 0) {
          return false;
        }

        if (verificationFunction) {
          let gamepad = currentImmersiveSession().inputSources[index].gamepad;
          if (gamepad.index !== -1) {
            // The spec requires all WebXR Gamepads to have index -1.
            return false;
          }
          if (gamepad.id !== "") {
            // The spec requires all WebXR Gamepads to have the empty string for
            // their ID. Related information should go on the XRInputSource
            // profiles array instead.
            return false;
          }

          if (!verificationFunction(gamepad)) {
            return false;
          }
        }

        return true;
      }

      function isInputSourceAsExpected(verificationFunction, index) {
        if (index === undefined) {
          index = 0;
        }

        if (index >= inputSourceCount()) {
          return false;
        }

        if (verificationFunction) {
          let source = currentImmersiveSession().inputSources[index];
          if (!verificationFunction(source)) {
            return false;
          }
        }

        return true;
      }

      function isMappingEqualTo(expected_mapping, index) {
        let verificationFunction = function(gamepad) {
          return gamepad.mapping == expected_mapping;
        }

        return isGamepadAsExpected(verificationFunction, index);
      }

      function isButtonCountEqualTo(expected_count, index) {
        let verificationFunction = function(gamepad) {
          return gamepad.buttons.length == expected_count;
        }

        return isGamepadAsExpected(verificationFunction, index);
      }

      // WebXR only exposes axes in (x, y) pairs because triggers are reported
      // as buttons on the Gamepad. The GamepadButton interface has a "value"
      // attribute that supports one-dimensional analog input.
      function isAxisPairCountEqualTo(expected_count, index) {
        let verificationFunction = function(gamepad) {
          return gamepad.axes.length == (expected_count * 2);
        }

        return isGamepadAsExpected(verificationFunction, index);
      }

      function isButtonPressedEqualTo(button_index, expected_pressed, index) {
        let verificationFunction = function(gamepad) {
          if (button_index >= gamepad.buttons.length) {
            return false;
          }

          return gamepad.buttons[button_index].pressed == expected_pressed;
        }

        return isGamepadAsExpected(verificationFunction, index);
      }

      function isButtonTouchedEqualTo(button_index, expected_touched, index) {
        let verificationFunction = function(gamepad) {
          if (button_index >= gamepad.buttons.length) {
            return false;
          }

          return gamepad.buttons[button_index].touched == expected_touched;
        }

        return isGamepadAsExpected(verificationFunction, index);
      }

      function areAxesValuesEqualTo(axes_pair_index, x_axis_value, y_axis_value, index) {
        let verificationFunction = function(gamepad) {
          let epsilon = 0.001;
          let x_index = (2 * axes_pair_index);
          let y_index = (2 * axes_pair_index) + 1;

          if (y_index >= gamepad.axes.length) {
            return false;
          }

          function approxEquals(a, b) {
            return Math.abs(a - b) < epsilon;
          }

          if (!approxEquals(gamepad.axes[x_index], x_axis_value)) {
            return false;
          }

          if (!approxEquals(gamepad.axes[y_index], y_axis_value)) {
            return false;
          }

          return true;
        }

        return isGamepadAsExpected(verificationFunction, index);
      }

      function isProfileCountEqualTo(expected_count, index) {
        let verificationFunction = function(source) {
          return source.profiles.length == expected_count;
        }

        return isInputSourceAsExpected(verificationFunction, index);
      }

      function isProfileEqualTo(profile_index, expected_string, index) {
        let verificationFunction = function(source) {
          if (profile_index >= source.profiles.length) {
            return false;
          }

          return source.profiles[profile_index] === expected_string;
        }

        return isInputSourceAsExpected(verificationFunction, index);
      }
    </script>
  </body>
</html>