Commit 5ff3cde4 authored by Alex Cooper's avatar Alex Cooper Committed by Commit Bot

Update WebXR inputsourceschanged event type and ordering

This adds the XRInputSourcesChangeEvent type and updates the code to
fire an event of that type rather than of the XRSessionEvent type, this
requires tracking added/removed input sources when processing updates.

This change also guarantees updates the ordering of inputsourceschange
events and select events, particularly for sub-frame selects or when an
input source shows up with it's primary input already pressed.  The
required ordering for a transient clicked input source (e.g. voice
select) is:
1) inputsourceschange (with the new source in added)
2) selectstart
3) select
4) selectend
5) inputsourceschange (with the new source removed)

This change also adds relevant tests for these two scenarios.

Bug: 963011,962745
Change-Id: Idcdeee69a4bbfad344151026f9b468a48b93097a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1614693
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: default avatarNate Chapin <japhet@chromium.org>
Reviewed-by: default avatarBrian Sheedy <bsheedy@chromium.org>
Reviewed-by: default avatarKlaus Weidner <klausw@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660627}
parent f8badf4c
...@@ -200,6 +200,70 @@ void WebXrControllerInputMock::OnFrameSubmitted( ...@@ -200,6 +200,70 @@ void WebXrControllerInputMock::OnFrameSubmitted(
std::move(callback).Run(); std::move(callback).Run();
} }
// Test that inputsourceschange events contain only the expected added/removed
// input sources when a mock controller is connected/disconnected.
// Also validates that if an input source changes substantially we get an event
// containing both the removal of the old one and the additon of the new one,
// rather than two events.
IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard, TestInputSourcesChange) {
WebXrControllerInputMock my_mock;
// TODO(crbug.com/963676): Figure out if the race is a product or test bug.
// There's a potential for a race causing the input sources change event to
// fire multiple times if we disconnect a controller that has a gamepad.
uint64_t insufficient_buttons =
vr::ButtonMaskFromId(vr::k_EButton_SteamVR_Trigger);
std::map<vr::EVRButtonId, unsigned int> insufficient_axis_types = {
{vr::k_EButton_SteamVR_Trigger, vr::k_eControllerAxis_Trigger},
};
unsigned int controller_index = my_mock.CreateAndConnectController(
device::ControllerRole::kControllerRoleRight, insufficient_axis_types,
insufficient_buttons);
LoadUrlAndAwaitInitialization(
GetFileUrlForHtmlTestFile("test_webxr_input_sources_change_event"));
EnterSessionWithUserGestureOrFail();
// Wait for the first changed event
PollJavaScriptBooleanOrFail("inputChangeEvents === 1", kPollTimeoutShort);
// Validate that we only have one controller added, and no controller removed.
RunJavaScriptOrFail("validateAdded(1)");
RunJavaScriptOrFail("validateRemoved(0)");
RunJavaScriptOrFail("updateCachedInputSource(0)");
RunJavaScriptOrFail("validateCachedAddedPresence(true)");
// Disconnect the controller and validate that we only have one controller
// removed, and that our previously cached controller is in the removed array.
my_mock.DisconnectController(controller_index);
PollJavaScriptBooleanOrFail("inputChangeEvents === 2", kPollTimeoutShort);
RunJavaScriptOrFail("validateAdded(0)");
RunJavaScriptOrFail("validateRemoved(1)");
RunJavaScriptOrFail("validateCachedRemovedPresence(true)");
// Connect a controller, and then change enough properties that the system
// recalculates its status as a valid controller, so that we can verify
// it is both added and removed.
// Since we're changing the controller state without disconnecting it, we can
// (and should) use the minimal gamepad here.
controller_index = my_mock.CreateAndConnectMinimalGamepad();
PollJavaScriptBooleanOrFail("inputChangeEvents === 3", kPollTimeoutShort);
RunJavaScriptOrFail("updateCachedInputSource(0)");
my_mock.UpdateControllerSupport(controller_index, insufficient_axis_types,
insufficient_buttons);
PollJavaScriptBooleanOrFail("inputChangeEvents === 4", kPollTimeoutShort);
RunJavaScriptOrFail("validateAdded(1)");
RunJavaScriptOrFail("validateRemoved(1)");
RunJavaScriptOrFail("validateCachedAddedPresence(false)");
RunJavaScriptOrFail("validateCachedRemovedPresence(true)");
RunJavaScriptOrFail("done()");
EndTest();
}
// Ensure that changes to a gamepad object respect that it is the same object // Ensure that changes to a gamepad object respect that it is the same object
// and that if whether or not an input source has a gamepad changes that the // and that if whether or not an input source has a gamepad changes that the
// input source change event is fired and a new input source is created. // input source change event is fired and a new input source is created.
......
<!doctype html>
<!--
A collection of helper functions and listeners to confirm the state of input
sources for the same object tests.
-->
<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>
let inputChangeEvents = 0;
let lastAdded = null;
let lastRemoved = null;
function onInputSourcesChange(event) {
lastAdded = event.added;
lastRemoved = event.removed;
inputChangeEvents++;
}
onSessionStartedCallback = function(session) {
if (session.mode.startsWith("immersive")) {
session.addEventListener('inputsourceschange', onInputSourcesChange, false);
}
}
function getCurrentInputSources() {
let currentSession = sessionInfos[sessionTypes.IMMERSIVE].currentSession;
return currentSession.getInputSources();
}
let cached_input_source = null;
function updateCachedInputSource(id) {
let input_sources = getCurrentInputSources();
assert_less_than(id, input_sources.length);
cached_input_source = input_sources[id];
}
function validateAdded(length) {
assert_not_equals(lastAdded, null);
assert_equals(lastAdded.length, length,
"Added length matches expectations");
let currentSources = getCurrentInputSources();
lastAdded.forEach((source) => {
assert_true(currentSources.includes(source),
"Every element in added should be in the input source list");
});
}
function validateRemoved(length) {
assert_not_equals(lastRemoved, null);
assert_equals(lastRemoved.length, length,
"Removed length matches expectations");
let currentSources = getCurrentInputSources();
lastRemoved.forEach((source) => {
assert_false(currentSources.includes(source),
"No element in removed should be in the input source list");
});
}
function validateCachedAddedPresence(presence) {
assert_not_equals(lastAdded, null);
assert_not_equals(cached_input_source, null);
assert_equals(lastAdded.includes(cached_input_source), presence,
"Presence of cached input in lastAdded matches expectation");
}
function validateCachedRemovedPresence(presence) {
assert_not_equals(lastRemoved, null);
assert_not_equals(cached_input_source, null);
assert_equals(lastRemoved.includes(cached_input_source), presence,
"Presence of cached input in lastRemoved matches expectation");
}
</script>
</body>
</html>
...@@ -70,6 +70,7 @@ generate_event_interfaces("modules_bindings_generated_event_interfaces") { ...@@ -70,6 +70,7 @@ generate_event_interfaces("modules_bindings_generated_event_interfaces") {
"//third_party/blink/renderer/modules/webmidi/midi_message_event.idl", "//third_party/blink/renderer/modules/webmidi/midi_message_event.idl",
"//third_party/blink/renderer/modules/websockets/close_event.idl", "//third_party/blink/renderer/modules/websockets/close_event.idl",
"//third_party/blink/renderer/modules/xr/xr_input_source_event.idl", "//third_party/blink/renderer/modules/xr/xr_input_source_event.idl",
"//third_party/blink/renderer/modules/xr/xr_input_sources_change_event.idl",
"//third_party/blink/renderer/modules/xr/xr_reference_space_event.idl", "//third_party/blink/renderer/modules/xr/xr_reference_space_event.idl",
"//third_party/blink/renderer/modules/xr/xr_session_event.idl", "//third_party/blink/renderer/modules/xr/xr_session_event.idl",
] ]
......
...@@ -497,6 +497,7 @@ modules_idl_files = ...@@ -497,6 +497,7 @@ modules_idl_files =
"xr/xr_hit_result.idl", "xr/xr_hit_result.idl",
"xr/xr_input_source.idl", "xr/xr_input_source.idl",
"xr/xr_input_source_event.idl", "xr/xr_input_source_event.idl",
"xr/xr_input_sources_change_event.idl",
"xr/xr_layer.idl", "xr/xr_layer.idl",
"xr/xr_plane.idl", "xr/xr_plane.idl",
"xr/xr_plane_detection_state.idl", "xr/xr_plane_detection_state.idl",
...@@ -838,6 +839,7 @@ modules_dictionary_idl_files = ...@@ -838,6 +839,7 @@ modules_dictionary_idl_files =
"webusb/usb_device_filter.idl", "webusb/usb_device_filter.idl",
"webusb/usb_device_request_options.idl", "webusb/usb_device_request_options.idl",
"xr/xr_input_source_event_init.idl", "xr/xr_input_source_event_init.idl",
"xr/xr_input_sources_change_event_init.idl",
"xr/xr_plane_detection_state_init.idl", "xr/xr_plane_detection_state_init.idl",
"xr/xr_reference_space_event_init.idl", "xr/xr_reference_space_event_init.idl",
"xr/xr_reference_space_options.idl", "xr/xr_reference_space_options.idl",
......
...@@ -30,6 +30,8 @@ blink_modules_sources("xr") { ...@@ -30,6 +30,8 @@ blink_modules_sources("xr") {
"xr_input_source.h", "xr_input_source.h",
"xr_input_source_event.cc", "xr_input_source_event.cc",
"xr_input_source_event.h", "xr_input_source_event.h",
"xr_input_sources_change_event.cc",
"xr_input_sources_change_event.h",
"xr_layer.cc", "xr_layer.cc",
"xr_layer.h", "xr_layer.h",
"xr_plane.cc", "xr_plane.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/xr/xr_input_sources_change_event.h"
namespace blink {
XRInputSourcesChangeEvent::XRInputSourcesChangeEvent(
const AtomicString& type,
XRSession* session,
const HeapVector<Member<XRInputSource>>& added,
const HeapVector<Member<XRInputSource>>& removed)
: Event(type, Bubbles::kYes, Cancelable::kNo),
session_(session),
added_(added),
removed_(removed) {}
XRInputSourcesChangeEvent::XRInputSourcesChangeEvent(
const AtomicString& type,
const XRInputSourcesChangeEventInit* initializer)
: Event(type, initializer) {
if (initializer->hasSession())
session_ = initializer->session();
if (initializer->hasAdded())
added_ = initializer->added();
if (initializer->hasRemoved())
removed_ = initializer->removed();
}
XRInputSourcesChangeEvent::~XRInputSourcesChangeEvent() = default;
const AtomicString& XRInputSourcesChangeEvent::InterfaceName() const {
return event_interface_names::kXRInputSourcesChangeEvent;
}
void XRInputSourcesChangeEvent::Trace(blink::Visitor* visitor) {
visitor->Trace(session_);
visitor->Trace(added_);
visitor->Trace(removed_);
Event::Trace(visitor);
}
} // namespace blink
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCES_CHANGE_EVENT_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCES_CHANGE_EVENT_H_
#include "third_party/blink/renderer/modules/event_modules.h"
#include "third_party/blink/renderer/modules/xr/xr_input_source.h"
#include "third_party/blink/renderer/modules/xr/xr_input_sources_change_event_init.h"
#include "third_party/blink/renderer/modules/xr/xr_session.h"
namespace blink {
class XRInputSourcesChangeEvent final : public Event {
DEFINE_WRAPPERTYPEINFO();
public:
static XRInputSourcesChangeEvent* Create(
const AtomicString& type,
XRSession* session,
const HeapVector<Member<XRInputSource>>& added,
const HeapVector<Member<XRInputSource>>& removed) {
return MakeGarbageCollected<XRInputSourcesChangeEvent>(type, session, added,
removed);
}
static XRInputSourcesChangeEvent* Create(
const AtomicString& type,
const XRInputSourcesChangeEventInit* initializer) {
return MakeGarbageCollected<XRInputSourcesChangeEvent>(type, initializer);
}
XRInputSourcesChangeEvent(const AtomicString& type,
XRSession*,
const HeapVector<Member<XRInputSource>>&,
const HeapVector<Member<XRInputSource>>&);
XRInputSourcesChangeEvent(const AtomicString&,
const XRInputSourcesChangeEventInit*);
~XRInputSourcesChangeEvent() override;
XRSession* session() const { return session_; }
const HeapVector<Member<XRInputSource>>& added() const { return added_; }
const HeapVector<Member<XRInputSource>>& removed() const { return removed_; }
const AtomicString& InterfaceName() const override;
void Trace(blink::Visitor*) override;
private:
Member<XRSession> session_;
HeapVector<Member<XRInputSource>> added_;
HeapVector<Member<XRInputSource>> removed_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_INPUT_SOURCES_CHANGE_EVENT_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
[
SecureContext,
Exposed=Window,
RuntimeEnabled=WebXR,
Constructor(DOMString type, XRInputSourcesChangeEventInit eventInitDict)
] interface XRInputSourcesChangeEvent : Event {
[SameObject] readonly attribute XRSession session;
[SameObject] readonly attribute FrozenArray<XRInputSource> added;
[SameObject] readonly attribute FrozenArray<XRInputSource> removed;
};
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
dictionary XRInputSourcesChangeEventInit : EventInit {
required XRSession session;
required FrozenArray<XRInputSource> added;
required FrozenArray<XRInputSource> removed;
};
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h" #include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
#include "third_party/blink/renderer/modules/xr/xr_hit_result.h" #include "third_party/blink/renderer/modules/xr/xr_hit_result.h"
#include "third_party/blink/renderer/modules/xr/xr_input_source_event.h" #include "third_party/blink/renderer/modules/xr/xr_input_source_event.h"
#include "third_party/blink/renderer/modules/xr/xr_input_sources_change_event.h"
#include "third_party/blink/renderer/modules/xr/xr_layer.h" #include "third_party/blink/renderer/modules/xr/xr_layer.h"
#include "third_party/blink/renderer/modules/xr/xr_presentation_context.h" #include "third_party/blink/renderer/modules/xr/xr_presentation_context.h"
#include "third_party/blink/renderer/modules/xr/xr_ray.h" #include "third_party/blink/renderer/modules/xr/xr_ray.h"
...@@ -766,10 +767,11 @@ void XRSession::OnInputStateChange( ...@@ -766,10 +767,11 @@ void XRSession::OnInputStateChange(
int16_t frame_id, int16_t frame_id,
const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>& const WTF::Vector<device::mojom::blink::XRInputSourceStatePtr>&
input_states) { input_states) {
bool input_sources_changed = false; HeapVector<Member<XRInputSource>> added;
HeapVector<Member<XRInputSource>> removed;
// Update any input sources with new state information. Any updated input // Build up our added array, and update the frame id of any active input
// sources are marked as active. // sources so we can flag the ones that are no longer active.
for (const auto& input_state : input_states) { for (const auto& input_state : input_states) {
XRInputSource* stored_input_source = XRInputSource* stored_input_source =
input_sources_.at(input_state->source_id); input_sources_.at(input_state->source_id);
...@@ -779,33 +781,46 @@ void XRSession::OnInputStateChange( ...@@ -779,33 +781,46 @@ void XRSession::OnInputStateChange(
// Using pointer equality to determine if the pointer needs to be set. // Using pointer equality to determine if the pointer needs to be set.
if (stored_input_source != input_source) { if (stored_input_source != input_source) {
input_sources_.Set(input_state->source_id, input_source); input_sources_.Set(input_state->source_id, input_source);
input_sources_changed = true; added.push_back(input_source);
// If we previously had a stored_input_source
if (stored_input_source)
removed.push_back(stored_input_source);
} }
input_source->active_frame_id = frame_id; input_source->active_frame_id = frame_id;
UpdateSelectState(input_source, input_state);
} }
// Remove any input sources that are inactive. Note that this is done in // Remove any input sources that are inactive. Note that this is done in
// two passes because HeapHashMap makes no guarantees about iterators on // two passes because HeapHashMap makes no guarantees about iterators on
// removal. // removal.
// We use a separate array of inactive sources here rather than just
// processing removed, because if we replaced any input sources, they would
// also be in removed, and we'd remove our newly added source.
std::vector<uint32_t> inactive_sources; std::vector<uint32_t> inactive_sources;
for (const auto& input_source : input_sources_.Values()) { for (const auto& input_source : input_sources_.Values()) {
if (input_source->active_frame_id != frame_id) { if (input_source->active_frame_id != frame_id) {
inactive_sources.push_back(input_source->source_id()); inactive_sources.push_back(input_source->source_id());
input_sources_changed = true; removed.push_back(input_source);
} }
} }
if (!inactive_sources.empty()) { for (uint32_t source_id : inactive_sources) {
for (uint32_t source_id : inactive_sources) { input_sources_.erase(source_id);
input_sources_.erase(source_id);
}
} }
if (input_sources_changed) { // If there have been any changes, fire the input sources change event.
DispatchEvent( if (!added.IsEmpty() || !removed.IsEmpty()) {
*XRSessionEvent::Create(event_type_names::kInputsourceschange, this)); DispatchEvent(*XRInputSourcesChangeEvent::Create(
event_type_names::kInputsourceschange, this, added, removed));
}
// Now that we've fired the input sources change event (if needed), update and
// fire events for any select state changes.
for (const auto& input_state : input_states) {
XRInputSource* input_source = input_sources_.at(input_state->source_id);
DCHECK(input_source);
UpdateSelectState(input_source, input_state);
} }
} }
......
This is a testharness.js-based test. This is a testharness.js-based test.
Found 217 tests; 198 PASS, 19 FAIL, 0 TIMEOUT, 0 NOTRUN. Found 217 tests; 207 PASS, 10 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS idl_test setup PASS idl_test setup
PASS Partial interface Navigator: original interface defined PASS Partial interface Navigator: original interface defined
PASS Partial dictionary WebGLContextAttributes: original dictionary defined PASS Partial dictionary WebGLContextAttributes: original dictionary defined
...@@ -197,15 +197,15 @@ PASS XRInputSourceEvent interface: existence and properties of interface prototy ...@@ -197,15 +197,15 @@ PASS XRInputSourceEvent interface: existence and properties of interface prototy
PASS XRInputSourceEvent interface: attribute frame PASS XRInputSourceEvent interface: attribute frame
PASS XRInputSourceEvent interface: attribute inputSource PASS XRInputSourceEvent interface: attribute inputSource
PASS XRInputSourceEvent interface: attribute buttonIndex PASS XRInputSourceEvent interface: attribute buttonIndex
FAIL XRInputSourcesChangeEvent interface: existence and properties of interface object assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: existence and properties of interface object
FAIL XRInputSourcesChangeEvent interface object length assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface object length
FAIL XRInputSourcesChangeEvent interface object name assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface object name
FAIL XRInputSourcesChangeEvent interface: existence and properties of interface prototype object assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: existence and properties of interface prototype object
FAIL XRInputSourcesChangeEvent interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: existence and properties of interface prototype object's "constructor" property
FAIL XRInputSourcesChangeEvent interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: existence and properties of interface prototype object's @@unscopables property
FAIL XRInputSourcesChangeEvent interface: attribute session assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: attribute session
FAIL XRInputSourcesChangeEvent interface: attribute added assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: attribute added
FAIL XRInputSourcesChangeEvent interface: attribute removed assert_own_property: self does not have own property "XRInputSourcesChangeEvent" expected property "XRInputSourcesChangeEvent" missing PASS XRInputSourcesChangeEvent interface: attribute removed
PASS XRReferenceSpaceEvent interface: existence and properties of interface object PASS XRReferenceSpaceEvent interface: existence and properties of interface object
PASS XRReferenceSpaceEvent interface object length PASS XRReferenceSpaceEvent interface object length
PASS XRReferenceSpaceEvent interface object name PASS XRReferenceSpaceEvent interface object name
......
...@@ -10605,6 +10605,12 @@ interface XRInputSourceEvent : Event ...@@ -10605,6 +10605,12 @@ interface XRInputSourceEvent : Event
getter frame getter frame
getter inputSource getter inputSource
method constructor method constructor
interface XRInputSourcesChangeEvent : Event
attribute @@toStringTag
getter added
getter removed
getter session
method constructor
interface XRLayer interface XRLayer
attribute @@toStringTag attribute @@toStringTag
method constructor method constructor
......
<!DOCTYPE html>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
<script src="file:///gen/device/vr/public/mojom/vr_service.mojom.js"></script>
<script src="../external/wpt/resources/chromium/webxr-test.js"></script>
<script src="../external/wpt/webxr/resources/webxr_test_constants.js"></script>
<script src="../xr/resources/xr-internal-device-mocking.js"></script>
<script src="../xr/resources/xr-test-utils.js"></script>
<canvas id="webgl-canvas"></canvas>
<script>
let testName = "Transient input sources fire events in the right order";
let watcherDone = new Event("watcherdone");
let fakeDeviceInitParams = { supportsImmersive:true };
let requestSessionModes = ['immersive-vr'];
let testFunction = function(session, t, fakeDeviceController) {
let eventWatcher = new EventWatcher(
t, session, ["inputsourceschange", "selectstart", "select", "selectend", "watcherdone"]);
let eventPromise = eventWatcher.wait_for(
["inputsourceschange", "selectstart", "select", "selectend", "inputsourceschange", "watcherdone"]);
// Need to have a valid pose or input events don't process.
fakeDeviceController.setXRPresentationFrameData(VALID_POSE_MATRIX, [{
eye:"left",
projectionMatrix: VALID_PROJECTION_MATRIX,
viewMatrix: VALID_VIEW_MATRIX
}, {
eye:"right",
projectionMatrix: VALID_PROJECTION_MATRIX,
viewMatrix: VALID_VIEW_MATRIX
}]);
let inputChangeEvents = 0;
let cached_input_source = null;
function onInputSourcesChange(event) {
t.step(() => {
inputChangeEvents++;
assert_equals(event.session, session);
// The first change event should be adding our controller.
if (inputChangeEvents === 1) {
validateAdded(event.added, 1);
validateRemoved(event.removed, 0);
cached_input_source = session.getInputSources()[0];
assert_not_equals(cached_input_source, null);
} else if (inputChangeEvents === 2) {
// The second event should be removing our controller.
validateAdded(event.added, 0);
validateRemoved(event.removed, 1);
assert_true(event.removed.includes(cached_input_source));
session.dispatchEvent(watcherDone);
}
});
}
function validateAdded(added, length) {
t.step(() => {
assert_not_equals(added, null);
assert_equals(added.length, length,
"Added length matches expectations");
let currentSources = session.getInputSources();
added.forEach((source) => {
assert_true(currentSources.includes(source),
"Every element in added should be in the input source list");
});
});
}
function validateRemoved(removed, length) {
t.step(() => {
assert_not_equals(removed, null);
assert_equals(removed.length, length,
"Removed length matches expectations");
let currentSources = session.getInputSources();
removed.forEach((source) => {
assert_false(currentSources.includes(source),
"No element in removed should be in the input source list");
});
});
}
session.addEventListener('inputsourceschange', onInputSourcesChange, false);
// Session must have a baseLayer or frame requests will be ignored.
session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
// Create our input source and immediately toggle the primary input so that
// it appears as already needing to send a click event when it appears.
let input_source = new MockXRInputSource();
input_source.primaryInputPressed = true;
input_source.primaryInputPressed = false;
fakeDeviceController.addInputSource(input_source);
// Make our input source disappear after one frame, and wait an additional
// frame for that disappearance to propogate.
session.requestAnimationFrame((time, xrFrame) => {
fakeDeviceController.removeInputSource(input_source);
session.requestAnimationFrame((time, xrFrame) => {
});
});
return eventPromise;
};
xr_session_promise_test(
testFunction, fakeDeviceInitParams, requestSessionModes, testName);
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment