Auto merge of #26171 - Manishearth:hittest, r=asajeffrey

Implement hit testing API

Depends on https://github.com/servo/webxr/pull/149 , https://github.com/servo/servo/pull/26170

This implements non-transient hit tests.

The tests that do not pass are due to https://github.com/web-platform-tests/wpt/issues/22898 , https://github.com/web-platform-tests/wpt/issues/22900, https://github.com/web-platform-tests/wpt/issues/22901 , and https://github.com/immersive-web/hit-test/issues/86
This commit is contained in:
bors-servo 2020-04-20 00:30:46 -04:00 committed by GitHub
commit 99cd30eaad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 650 additions and 77 deletions

36
Cargo.lock generated
View file

@ -183,9 +183,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "0.1.7" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]] [[package]]
name = "background_hang_monitor" name = "background_hang_monitor"
@ -971,14 +971,15 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.8.0" version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"lazy_static", "lazy_static",
"maybe-uninit",
"memoffset", "memoffset",
"scopeguard", "scopeguard",
] ]
@ -995,9 +996,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.7.0" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
@ -1473,9 +1474,9 @@ dependencies = [
[[package]] [[package]]
name = "euclid" name = "euclid"
version = "0.20.5" version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8813df82772c5ef4c2e9cd4a986773c125ffeafdc08204c9d5c2f06e0abdc17" checksum = "0c6a5b0c779cd0b744c73a1d2083faf181080d696903cdad99a3b03d015d7030"
dependencies = [ dependencies = [
"num-traits", "num-traits",
"serde", "serde",
@ -3846,9 +3847,12 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.4" version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
@ -3926,9 +3930,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.53" version = "0.9.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@ -6270,9 +6274,9 @@ dependencies = [
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.2" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
[[package]] [[package]]
name = "vec_map" name = "vec_map"
@ -6566,7 +6570,7 @@ dependencies = [
[[package]] [[package]]
name = "webxr" name = "webxr"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/servo/webxr#0d9c83f333920b98d95adf9666b0a365258990a3" source = "git+https://github.com/servo/webxr#805811544cafbc8dcebc3cd02c38dd329226088d"
dependencies = [ dependencies = [
"android_injected_glue", "android_injected_glue",
"bindgen", "bindgen",
@ -6590,7 +6594,7 @@ dependencies = [
[[package]] [[package]]
name = "webxr-api" name = "webxr-api"
version = "0.0.1" version = "0.0.1"
source = "git+https://github.com/servo/webxr#0d9c83f333920b98d95adf9666b0a365258990a3" source = "git+https://github.com/servo/webxr#805811544cafbc8dcebc3cd02c38dd329226088d"
dependencies = [ dependencies = [
"euclid", "euclid",
"ipc-channel", "ipc-channel",

View file

@ -157,6 +157,7 @@ use webgpu::{
WebGPUPipelineLayout, WebGPUQueue, WebGPUShaderModule, WebGPUPipelineLayout, WebGPUQueue, WebGPUShaderModule,
}; };
use webrender_api::{DocumentId, ImageKey}; use webrender_api::{DocumentId, ImageKey};
use webxr_api::Ray;
use webxr_api::SwapChainId as WebXRSwapChainId; use webxr_api::SwapChainId as WebXRSwapChainId;
unsafe_no_jsmanaged_fields!(Tm); unsafe_no_jsmanaged_fields!(Tm);
@ -552,7 +553,9 @@ unsafe_no_jsmanaged_fields!(
webxr_api::Session, webxr_api::Session,
webxr_api::Frame, webxr_api::Frame,
webxr_api::InputSource, webxr_api::InputSource,
webxr_api::InputId webxr_api::InputId,
webxr_api::HitTestId,
webxr_api::HitTestResult
); );
unsafe_no_jsmanaged_fields!(ScriptToConstellationChan); unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
unsafe_no_jsmanaged_fields!(InteractiveMetrics); unsafe_no_jsmanaged_fields!(InteractiveMetrics);
@ -750,6 +753,13 @@ unsafe impl<U> JSTraceable for euclid::Rect<i32, U> {
} }
} }
unsafe impl<Space> JSTraceable for Ray<Space> {
#[inline]
unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}
unsafe impl JSTraceable for StyleLocked<FontFaceRule> { unsafe impl JSTraceable for StyleLocked<FontFaceRule> {
unsafe fn trace(&self, _trc: *mut JSTracer) { unsafe fn trace(&self, _trc: *mut JSTracer) {
// Do nothing. // Do nothing.

View file

@ -2,8 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::FakeXRDeviceBinding::{ use crate::dom::bindings::codegen::Bindings::FakeXRDeviceBinding::{
FakeXRDeviceMethods, FakeXRRigidTransformInit, FakeXRViewInit, FakeXRDeviceMethods, FakeXRRegionType, FakeXRRigidTransformInit, FakeXRViewInit,
FakeXRWorldInit,
}; };
use crate::dom::bindings::codegen::Bindings::FakeXRInputControllerBinding::FakeXRInputSourceInit; use crate::dom::bindings::codegen::Bindings::FakeXRInputControllerBinding::FakeXRInputSourceInit;
use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{ use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
@ -21,15 +23,15 @@ use crate::dom::promise::Promise;
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use euclid::{RigidTransform3D, Rotation3D, Transform3D, Vector3D}; use euclid::{Point3D, RigidTransform3D, Rotation3D, Transform3D, Vector3D};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use profile_traits::ipc; use profile_traits::ipc;
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use webxr_api::{ use webxr_api::{
Handedness, InputId, InputSource, MockDeviceMsg, MockInputInit, MockViewInit, MockViewsInit, EntityType, Handedness, InputId, InputSource, MockDeviceMsg, MockInputInit, MockRegion,
TargetRayMode, Visibility, MockViewInit, MockViewsInit, MockWorld, TargetRayMode, Triangle, Visibility,
}; };
#[dom_struct] #[dom_struct]
@ -97,6 +99,7 @@ pub fn view<Eye>(view: &FakeXRViewInit) -> Fallible<MockViewInit<Eye>> {
fov, fov,
}) })
} }
pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<MockViewsInit> { pub fn get_views(views: &[FakeXRViewInit]) -> Fallible<MockViewsInit> {
match views.len() { match views.len() {
1 => Ok(MockViewsInit::Mono(view(&views[0])?)), 1 => Ok(MockViewsInit::Mono(view(&views[0])?)),
@ -133,6 +136,50 @@ pub fn get_origin<T, U>(
Ok(RigidTransform3D::new(o, p)) Ok(RigidTransform3D::new(o, p))
} }
pub fn get_point<T>(pt: &DOMPointInit) -> Point3D<f32, T> {
Point3D::new(pt.x / pt.w, pt.y / pt.w, pt.z / pt.w).cast()
}
pub fn get_world(world: &FakeXRWorldInit) -> Fallible<MockWorld> {
let regions = world
.hitTestRegions
.iter()
.map(|region| {
let ty = region.type_.into();
let faces = region
.faces
.iter()
.map(|face| {
if face.vertices.len() != 3 {
return Err(Error::Type(
"Incorrectly sized array for triangle list".into(),
));
}
Ok(Triangle {
first: get_point(&face.vertices[0]),
second: get_point(&face.vertices[1]),
third: get_point(&face.vertices[2]),
})
})
.collect::<Fallible<Vec<_>>>()?;
Ok(MockRegion { faces, ty })
})
.collect::<Fallible<Vec<_>>>()?;
Ok(MockWorld { regions })
}
impl From<FakeXRRegionType> for EntityType {
fn from(x: FakeXRRegionType) -> Self {
match x {
FakeXRRegionType::Point => EntityType::Point,
FakeXRRegionType::Plane => EntityType::Plane,
FakeXRRegionType::Mesh => EntityType::Mesh,
}
}
}
impl FakeXRDeviceMethods for FakeXRDevice { impl FakeXRDeviceMethods for FakeXRDevice {
/// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md /// https://github.com/immersive-web/webxr-test-api/blob/master/explainer.md
fn SetViews(&self, views: Vec<FakeXRViewInit>) -> Fallible<()> { fn SetViews(&self, views: Vec<FakeXRViewInit>) -> Fallible<()> {
@ -172,6 +219,17 @@ impl FakeXRDeviceMethods for FakeXRDevice {
Ok(()) Ok(())
} }
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-clearworld
fn ClearWorld(&self) {
let _ = self.sender.send(MockDeviceMsg::ClearWorld);
}
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-setworld
fn SetWorld(&self, world: &FakeXRWorldInit) -> Fallible<()> {
let _ = self.sender.send(MockDeviceMsg::SetWorld(get_world(world)?));
Ok(())
}
/// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-simulatevisibilitychange /// https://immersive-web.github.io/webxr-test-api/#dom-fakexrdevice-simulatevisibilitychange
fn SimulateVisibilityChange(&self, v: XRVisibilityState) { fn SimulateVisibilityChange(&self, v: XRVisibilityState) {
let v = match v { let v = match v {

View file

@ -572,6 +572,8 @@ pub mod xmlhttprequesteventtarget;
pub mod xmlhttprequestupload; pub mod xmlhttprequestupload;
pub mod xmlserializer; pub mod xmlserializer;
pub mod xrframe; pub mod xrframe;
pub mod xrhittestresult;
pub mod xrhittestsource;
pub mod xrinputsource; pub mod xrinputsource;
pub mod xrinputsourcearray; pub mod xrinputsourcearray;
pub mod xrinputsourceevent; pub mod xrinputsourceevent;
@ -579,6 +581,7 @@ pub mod xrinputsourceschangeevent;
pub mod xrlayer; pub mod xrlayer;
pub mod xrmediabinding; pub mod xrmediabinding;
pub mod xrpose; pub mod xrpose;
pub mod xrray;
pub mod xrreferencespace; pub mod xrreferencespace;
pub mod xrrenderstate; pub mod xrrenderstate;
pub mod xrrigidtransform; pub mod xrrigidtransform;

View file

@ -25,6 +25,10 @@ interface FakeXRDevice {
// behaves as if device was disconnected // behaves as if device was disconnected
Promise<void> disconnect(); Promise<void> disconnect();
// Hit test extensions:
[Throws] void setWorld(FakeXRWorldInit world);
void clearWorld();
}; };
// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport // https://immersive-web.github.io/webxr/#dom-xrwebgllayer-getviewport
@ -61,3 +65,26 @@ dictionary FakeXRFieldOfViewInit {
required float leftDegrees; required float leftDegrees;
required float rightDegrees; required float rightDegrees;
}; };
// hit testing
dictionary FakeXRWorldInit {
required sequence<FakeXRRegionInit> hitTestRegions;
};
dictionary FakeXRRegionInit {
required sequence<FakeXRTriangleInit> faces;
required FakeXRRegionType type;
};
dictionary FakeXRTriangleInit {
required sequence<DOMPointInit> vertices; // size = 3
};
enum FakeXRRegionType {
"point",
"plane",
"mesh"
};

View file

@ -10,5 +10,5 @@ interface XRFrame {
[Throws] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace); [Throws] XRViewerPose? getViewerPose(XRReferenceSpace referenceSpace);
[Throws] XRPose? getPose(XRSpace space, XRSpace relativeTo); [Throws] XRPose? getPose(XRSpace space, XRSpace relativeTo);
// XRInputPose? getInputPose(XRInputSource inputSource, optional XRReferenceSpace referenceSpace); sequence<XRHitTestResult> getHitTestResults(XRHitTestSource hitTestSource);
}; };

View file

@ -0,0 +1,10 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://immersive-web.github.io/hit-test/#xrhittestresult-interface
[SecureContext, Exposed=Window]
interface XRHitTestResult {
XRPose? getPose(XRSpace baseSpace);
};

View file

@ -0,0 +1,22 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://immersive-web.github.io/hit-test/#xrhittestsource-interface
enum XRHitTestTrackableType {
"point",
"plane",
"mesh"
};
dictionary XRHitTestOptionsInit {
required XRSpace space;
sequence<XRHitTestTrackableType> entityTypes;
XRRay offsetRay;
};
[SecureContext, Exposed=Window]
interface XRHitTestSource {
void cancel();
};

View file

@ -0,0 +1,21 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://immersive-web.github.io/hit-test/#xrray-interface
dictionary XRRayDirectionInit {
double x = 0;
double y = 0;
double z = -1;
double w = 0;
};
[SecureContext, Exposed=Window, Pref="dom.webxr.enabled"]
interface XRRay {
[Throws] constructor(optional DOMPointInit origin = {}, optional XRRayDirectionInit direction = {});
[Throws] constructor(XRRigidTransform transform);
[SameObject] readonly attribute DOMPointReadOnly origin;
[SameObject] readonly attribute DOMPointReadOnly direction;
[SameObject] readonly attribute Float32Array matrix;
};

View file

@ -36,6 +36,9 @@ interface XRSession : EventTarget {
Promise<void> end(); Promise<void> end();
// hit test module
Promise<XRHitTestSource> requestHitTestSource(XRHitTestOptionsInit options);
// // Events // // Events
attribute EventHandler onend; attribute EventHandler onend;
attribute EventHandler onselect; attribute EventHandler onselect;

View file

@ -20,13 +20,14 @@ interface XRTest {
}; };
dictionary FakeXRDeviceInit { dictionary FakeXRDeviceInit {
required boolean supportsImmersive; boolean supportsImmersive = false;
sequence<XRSessionMode> supportedModes;
required sequence<FakeXRViewInit> views; required sequence<FakeXRViewInit> views;
// this is actually sequence<any>, but we don't support // this is actually sequence<any>, but we don't support
// non-string features anyway // non-string features anyway
sequence<DOMString> supportedFeatures; sequence<DOMString> supportedFeatures;
boolean supportsUnbounded = false;
// Whether the space supports tracking in inline sessions // Whether the space supports tracking in inline sessions
boolean supportsTrackingInInline = true; boolean supportsTrackingInInline = true;
// The bounds coordinates. If null, bounded reference spaces are not supported. // The bounds coordinates. If null, bounded reference spaces are not supported.
@ -34,5 +35,8 @@ dictionary FakeXRDeviceInit {
// Eye level used for calculating floor-level spaces // Eye level used for calculating floor-level spaces
FakeXRRigidTransformInit floorOrigin; FakeXRRigidTransformInit floorOrigin;
FakeXRRigidTransformInit viewerOrigin; FakeXRRigidTransformInit viewerOrigin;
// Hit test extensions:
FakeXRWorldInit world;
}; };

View file

@ -8,9 +8,11 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::xrhittestresult::XRHitTestResult;
use crate::dom::xrhittestsource::XRHitTestSource;
use crate::dom::xrpose::XRPose; use crate::dom::xrpose::XRPose;
use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrreferencespace::XRReferenceSpace;
use crate::dom::xrsession::XRSession; use crate::dom::xrsession::{ApiPose, XRSession};
use crate::dom::xrspace::XRSpace; use crate::dom::xrspace::XRSpace;
use crate::dom::xrviewerpose::XRViewerPose; use crate::dom::xrviewerpose::XRViewerPose;
use dom_struct::dom_struct; use dom_struct::dom_struct;
@ -51,6 +53,10 @@ impl XRFrame {
pub fn set_animation_frame(&self, animation_frame: bool) { pub fn set_animation_frame(&self, animation_frame: bool) {
self.animation_frame.set(animation_frame); self.animation_frame.set(animation_frame);
} }
pub fn get_pose(&self, space: &XRSpace) -> Option<ApiPose> {
space.get_pose(&self.data)
}
} }
impl XRFrameMethods for XRFrame { impl XRFrameMethods for XRFrame {
@ -92,12 +98,12 @@ impl XRFrameMethods for XRFrame {
if !self.active.get() { if !self.active.get() {
return Err(Error::InvalidState); return Err(Error::InvalidState);
} }
let space = if let Some(space) = space.get_pose(&self.data) { let space = if let Some(space) = self.get_pose(space) {
space space
} else { } else {
return Ok(None); return Ok(None);
}; };
let relative_to = if let Some(r) = relative_to.get_pose(&self.data) { let relative_to = if let Some(r) = self.get_pose(relative_to) {
r r
} else { } else {
return Ok(None); return Ok(None);
@ -105,4 +111,14 @@ impl XRFrameMethods for XRFrame {
let pose = relative_to.inverse().pre_transform(&space); let pose = relative_to.inverse().pre_transform(&space);
Ok(Some(XRPose::new(&self.global(), pose))) Ok(Some(XRPose::new(&self.global(), pose)))
} }
/// https://immersive-web.github.io/hit-test/#dom-xrframe-gethittestresults
fn GetHitTestResults(&self, source: &XRHitTestSource) -> Vec<DomRoot<XRHitTestResult>> {
self.data
.hit_test_results
.iter()
.filter(|r| r.id == source.id())
.map(|r| XRHitTestResult::new(&self.global(), *r, self))
.collect()
}
} }

View file

@ -0,0 +1,51 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::XRHitTestResultBinding::XRHitTestResultMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::xrframe::XRFrame;
use crate::dom::xrpose::XRPose;
use crate::dom::xrspace::XRSpace;
use dom_struct::dom_struct;
use webxr_api::HitTestResult;
#[dom_struct]
pub struct XRHitTestResult {
reflector_: Reflector,
#[ignore_malloc_size_of = "defined in webxr"]
result: HitTestResult,
frame: Dom<XRFrame>,
}
impl XRHitTestResult {
fn new_inherited(result: HitTestResult, frame: &XRFrame) -> XRHitTestResult {
XRHitTestResult {
reflector_: Reflector::new(),
result,
frame: Dom::from_ref(frame),
}
}
pub fn new(
global: &GlobalScope,
result: HitTestResult,
frame: &XRFrame,
) -> DomRoot<XRHitTestResult> {
reflect_dom_object(
Box::new(XRHitTestResult::new_inherited(result, frame)),
global,
)
}
}
impl XRHitTestResultMethods for XRHitTestResult {
// https://immersive-web.github.io/hit-test/#dom-xrhittestresult-getpose
fn GetPose(&self, base: &XRSpace) -> Option<DomRoot<XRPose>> {
let base = self.frame.get_pose(base)?;
let pose = base.inverse().pre_transform(&self.result.space);
Some(XRPose::new(&self.global(), pose.cast_unit()))
}
}

View file

@ -0,0 +1,51 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestSourceMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::globalscope::GlobalScope;
use crate::dom::xrsession::XRSession;
use dom_struct::dom_struct;
use webxr_api::HitTestId;
#[dom_struct]
pub struct XRHitTestSource {
reflector_: Reflector,
#[ignore_malloc_size_of = "defined in webxr"]
id: HitTestId,
session: Dom<XRSession>,
}
impl XRHitTestSource {
fn new_inherited(id: HitTestId, session: &XRSession) -> XRHitTestSource {
XRHitTestSource {
reflector_: Reflector::new(),
id,
session: Dom::from_ref(session),
}
}
pub fn new(
global: &GlobalScope,
id: HitTestId,
session: &XRSession,
) -> DomRoot<XRHitTestSource> {
reflect_dom_object(
Box::new(XRHitTestSource::new_inherited(id, session)),
global,
)
}
pub fn id(&self) -> HitTestId {
self.id
}
}
impl XRHitTestSourceMethods for XRHitTestSource {
// https://immersive-web.github.io/hit-test/#dom-xrhittestsource-cancel
fn Cancel(&self) {
self.session.with_session(|s| s.cancel_hit_test(self.id));
}
}

View file

@ -0,0 +1,147 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
use crate::dom::bindings::codegen::Bindings::XRRayBinding::{XRRayDirectionInit, XRRayMethods};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::utils::create_typed_array;
use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::dom::xrrigidtransform::XRRigidTransform;
use crate::script_runtime::JSContext;
use dom_struct::dom_struct;
use euclid::{Angle, RigidTransform3D, Rotation3D, Vector3D};
use js::jsapi::{Heap, JSObject};
use std::ptr::NonNull;
use webxr_api::{ApiSpace, Ray};
#[dom_struct]
pub struct XRRay {
reflector_: Reflector,
#[ignore_malloc_size_of = "defined in webxr"]
ray: Ray<ApiSpace>,
#[ignore_malloc_size_of = "defined in mozjs"]
matrix: Heap<*mut JSObject>,
}
impl XRRay {
fn new_inherited(ray: Ray<ApiSpace>) -> XRRay {
XRRay {
reflector_: Reflector::new(),
ray,
matrix: Heap::default(),
}
}
pub fn new(global: &GlobalScope, ray: Ray<ApiSpace>) -> DomRoot<XRRay> {
reflect_dom_object(Box::new(XRRay::new_inherited(ray)), global)
}
#[allow(non_snake_case)]
/// https://immersive-web.github.io/hit-test/#dom-xrray-xrray
pub fn Constructor(
window: &Window,
origin: &DOMPointInit,
direction: &XRRayDirectionInit,
) -> Fallible<DomRoot<Self>> {
if origin.w != 1.0 {
return Err(Error::Type("Origin w coordinate must be 1".into()));
}
if *direction.w != 0.0 {
return Err(Error::Type("Direction w coordinate must be 0".into()));
}
if *direction.x == 0.0 && *direction.y == 0.0 && *direction.z == 0.0 {
return Err(Error::Type(
"Direction vector cannot have zero length".into(),
));
}
let origin = Vector3D::new(origin.x as f32, origin.y as f32, origin.z as f32);
let direction = Vector3D::new(
*direction.x as f32,
*direction.y as f32,
*direction.z as f32,
)
.normalize();
Ok(Self::new(&window.global(), Ray { origin, direction }))
}
#[allow(non_snake_case)]
/// https://immersive-web.github.io/hit-test/#dom-xrray-xrray-transform
pub fn Constructor_(window: &Window, transform: &XRRigidTransform) -> Fallible<DomRoot<Self>> {
let transform = transform.transform();
let origin = transform.translation;
let direction = transform
.rotation
.transform_vector3d(Vector3D::new(0., 0., -1.));
Ok(Self::new(&window.global(), Ray { origin, direction }))
}
pub fn ray(&self) -> Ray<ApiSpace> {
self.ray
}
}
impl XRRayMethods for XRRay {
/// https://immersive-web.github.io/hit-test/#dom-xrray-origin
fn Origin(&self) -> DomRoot<DOMPointReadOnly> {
DOMPointReadOnly::new(
&self.global(),
self.ray.origin.x as f64,
self.ray.origin.y as f64,
self.ray.origin.z as f64,
1.,
)
}
/// https://immersive-web.github.io/hit-test/#dom-xrray-direction
fn Direction(&self) -> DomRoot<DOMPointReadOnly> {
DOMPointReadOnly::new(
&self.global(),
self.ray.direction.x as f64,
self.ray.direction.y as f64,
self.ray.direction.z as f64,
0.,
)
}
/// https://immersive-web.github.io/hit-test/#dom-xrray-matrix
fn Matrix(&self, _cx: JSContext) -> NonNull<JSObject> {
// https://immersive-web.github.io/hit-test/#xrray-obtain-the-matrix
// Step 1
if self.matrix.get().is_null() {
let cx = self.global().get_cx();
// Step 2
let z = Vector3D::new(0., 0., -1.);
// Step 3
let axis = z.cross(self.ray.direction);
// Step 4
let cos_angle = z.dot(self.ray.direction);
// Step 5
let rotation = if cos_angle > -1. && cos_angle < 1. {
Rotation3D::around_axis(axis, Angle::radians(cos_angle.acos()))
} else if cos_angle == -1. {
let axis = Vector3D::new(1., 0., 0.);
Rotation3D::around_axis(axis, Angle::radians(cos_angle.acos()))
} else {
Rotation3D::identity()
};
// Step 6
let translation = self.ray.origin;
// Step 7
// According to the spec all matrices are column-major,
// however euclid uses row vectors so we use .to_row_major_array()
let arr = RigidTransform3D::new(rotation, translation)
.to_transform()
.to_row_major_array();
create_typed_array(cx, &arr, &self.matrix);
}
NonNull::new(self.matrix.get()).unwrap()
}
}

View file

@ -13,7 +13,7 @@ use crate::dom::xrsession::{cast_transform, ApiPose, ApiViewerPose, XRSession};
use crate::dom::xrspace::XRSpace; use crate::dom::xrspace::XRSpace;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::RigidTransform3D; use euclid::RigidTransform3D;
use webxr_api::Frame; use webxr_api::{BaseSpace, Frame, Space};
#[dom_struct] #[dom_struct]
pub struct XRReferenceSpace { pub struct XRReferenceSpace {
@ -57,6 +57,17 @@ impl XRReferenceSpace {
global, global,
) )
} }
pub fn space(&self) -> Space {
let base = match self.ty {
XRReferenceSpaceType::Local => BaseSpace::Local,
XRReferenceSpaceType::Viewer => BaseSpace::Viewer,
XRReferenceSpaceType::Local_floor => BaseSpace::Floor,
_ => panic!("unsupported reference space found"),
};
let offset = self.offset.transform();
Space { base, offset }
}
} }
impl XRReferenceSpaceMethods for XRReferenceSpace { impl XRReferenceSpaceMethods for XRReferenceSpace {

View file

@ -6,6 +6,8 @@ use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods; use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestOptionsInit;
use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::XRHitTestTrackableType;
use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType; use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit; use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit;
use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods; use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateMethods;
@ -28,6 +30,7 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::performance::reduce_timing_resolution; use crate::dom::performance::reduce_timing_resolution;
use crate::dom::promise::Promise; use crate::dom::promise::Promise;
use crate::dom::xrframe::XRFrame; use crate::dom::xrframe::XRFrame;
use crate::dom::xrhittestsource::XRHitTestSource;
use crate::dom::xrinputsourcearray::XRInputSourceArray; use crate::dom::xrinputsourcearray::XRInputSourceArray;
use crate::dom::xrinputsourceevent::XRInputSourceEvent; use crate::dom::xrinputsourceevent::XRInputSourceEvent;
use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrreferencespace::XRReferenceSpace;
@ -37,18 +40,20 @@ use crate::dom::xrspace::XRSpace;
use crate::realms::InRealm; use crate::realms::InRealm;
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::{Rect, RigidTransform3D, Transform3D}; use euclid::{Rect, RigidTransform3D, Transform3D, Vector3D};
use ipc_channel::ipc::IpcReceiver; use ipc_channel::ipc::IpcReceiver;
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use metrics::ToMs; use metrics::ToMs;
use profile_traits::ipc; use profile_traits::ipc;
use std::cell::Cell; use std::cell::Cell;
use std::collections::HashMap;
use std::f64::consts::{FRAC_PI_2, PI}; use std::f64::consts::{FRAC_PI_2, PI};
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use webxr_api::{ use webxr_api::{
self, util, Display, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind, self, util, ApiSpace, Display, EntityTypes, EnvironmentBlendMode, Event as XREvent, Frame,
Session, SessionId, View, Viewer, Visibility, FrameUpdateEvent, HitTestId, HitTestSource, Ray, SelectEvent, SelectKind, Session, SessionId,
View, Viewer, Visibility,
}; };
#[dom_struct] #[dom_struct]
@ -75,6 +80,10 @@ pub struct XRSession {
end_promises: DomRefCell<Vec<Rc<Promise>>>, end_promises: DomRefCell<Vec<Rc<Promise>>>,
/// https://immersive-web.github.io/webxr/#ended /// https://immersive-web.github.io/webxr/#ended
ended: Cell<bool>, ended: Cell<bool>,
#[ignore_malloc_size_of = "defined in webxr"]
next_hit_test_id: Cell<HitTestId>,
#[ignore_malloc_size_of = "defined in webxr"]
pending_hit_test_promises: DomRefCell<HashMap<HitTestId, Rc<Promise>>>,
/// Opaque framebuffers need to know the session is "outside of a requestAnimationFrame" /// Opaque framebuffers need to know the session is "outside of a requestAnimationFrame"
/// https://immersive-web.github.io/webxr/#opaque-framebuffer /// https://immersive-web.github.io/webxr/#opaque-framebuffer
outside_raf: Cell<bool>, outside_raf: Cell<bool>,
@ -104,6 +113,8 @@ impl XRSession {
input_sources: Dom::from_ref(input_sources), input_sources: Dom::from_ref(input_sources),
end_promises: DomRefCell::new(vec![]), end_promises: DomRefCell::new(vec![]),
ended: Cell::new(false), ended: Cell::new(false),
next_hit_test_id: Cell::new(HitTestId(0)),
pending_hit_test_promises: DomRefCell::new(HashMap::new()),
outside_raf: Cell::new(true), outside_raf: Cell::new(true),
} }
} }
@ -373,7 +384,7 @@ impl XRSession {
} }
for event in frame.events.drain(..) { for event in frame.events.drain(..) {
self.session.borrow_mut().apply_event(event) self.handle_frame_update(event);
} }
// Step 2 // Step 2
@ -480,6 +491,22 @@ impl XRSession {
} }
} }
} }
fn handle_frame_update(&self, event: FrameUpdateEvent) {
match event {
FrameUpdateEvent::HitTestSourceAdded(id) => {
if let Some(promise) = self.pending_hit_test_promises.borrow_mut().remove(&id) {
promise.resolve_native(&XRHitTestSource::new(&self.global(), id, &self));
} else {
warn!(
"received hit test add request for unknown hit test {:?}",
id
)
}
},
_ => self.session.borrow_mut().apply_event(event),
}
}
} }
impl XRSessionMethods for XRSession { impl XRSessionMethods for XRSession {
@ -709,10 +736,66 @@ impl XRSessionMethods for XRSession {
self.session.borrow_mut().end_session(); self.session.borrow_mut().end_session();
p p
} }
// https://immersive-web.github.io/hit-test/#dom-xrsession-requesthittestsource
fn RequestHitTestSource(&self, options: &XRHitTestOptionsInit) -> Rc<Promise> {
let p = Promise::new(&self.global());
if self
.session
.borrow()
.granted_features()
.iter()
.find(|f| &**f == "hit-test")
.is_none()
{
p.reject_error(Error::NotSupported);
return p;
}
let id = self.next_hit_test_id.get();
self.next_hit_test_id.set(HitTestId(id.0 + 1));
let space = options.space.space();
let ray = if let Some(ref ray) = options.offsetRay {
ray.ray()
} else {
Ray {
origin: Vector3D::new(0., 0., 0.),
direction: Vector3D::new(0., 0., -1.),
}
};
let mut types = EntityTypes::default();
if let Some(ref tys) = options.entityTypes {
for ty in tys {
match ty {
XRHitTestTrackableType::Point => types.point = true,
XRHitTestTrackableType::Plane => types.plane = true,
XRHitTestTrackableType::Mesh => types.mesh = true,
}
}
} else {
types.plane = true;
}
let source = HitTestSource {
id,
space,
ray,
types,
};
self.pending_hit_test_promises
.borrow_mut()
.insert(id, p.clone());
self.session.borrow().request_hit_test(source);
p
}
} }
#[derive(Clone, Copy, Debug)]
pub struct ApiSpace;
// The pose of an object in native-space. Should never be exposed. // The pose of an object in native-space. Should never be exposed.
pub type ApiPose = RigidTransform3D<f32, ApiSpace, webxr_api::Native>; pub type ApiPose = RigidTransform3D<f32, ApiSpace, webxr_api::Native>;
// The pose of the viewer in some api-space. // The pose of the viewer in some api-space.

View file

@ -11,7 +11,8 @@ use crate::dom::xrinputsource::XRInputSource;
use crate::dom::xrreferencespace::XRReferenceSpace; use crate::dom::xrreferencespace::XRReferenceSpace;
use crate::dom::xrsession::{cast_transform, ApiPose, XRSession}; use crate::dom::xrsession::{cast_transform, ApiPose, XRSession};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use webxr_api::Frame; use euclid::RigidTransform3D;
use webxr_api::{BaseSpace, Frame, Space};
#[dom_struct] #[dom_struct]
pub struct XRSpace { pub struct XRSpace {
@ -56,6 +57,24 @@ impl XRSpace {
global, global,
) )
} }
pub fn space(&self) -> Space {
if let Some(rs) = self.downcast::<XRReferenceSpace>() {
rs.space()
} else if let Some(source) = self.input_source.get() {
let base = if self.is_grip_space {
BaseSpace::Grip(source.id())
} else {
BaseSpace::TargetRay(source.id())
};
Space {
base,
offset: RigidTransform3D::identity(),
}
} else {
panic!("invalid space found")
}
}
} }
impl XRSpace { impl XRSpace {

View file

@ -9,11 +9,12 @@
use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function; use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::codegen::Bindings::XRSystemBinding::XRSessionMode;
use crate::dom::bindings::codegen::Bindings::XRTestBinding::{FakeXRDeviceInit, XRTestMethods}; use crate::dom::bindings::codegen::Bindings::XRTestBinding::{FakeXRDeviceInit, XRTestMethods};
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::fakexrdevice::{get_origin, get_views, FakeXRDevice}; use crate::dom::fakexrdevice::{get_origin, get_views, get_world, FakeXRDevice};
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise; use crate::dom::promise::Promise;
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
@ -106,13 +107,40 @@ impl XRTestMethods for XRTest {
vec![] vec![]
}; };
let world = if let Some(ref w) = init.world {
let w = match get_world(w) {
Ok(w) => w,
Err(e) => {
p.reject_error(e);
return p;
},
};
Some(w)
} else {
None
};
let (mut supports_inline, mut supports_vr, mut supports_ar) = (false, false, false);
if let Some(ref modes) = init.supportedModes {
for mode in modes {
match mode {
XRSessionMode::Immersive_vr => supports_vr = true,
XRSessionMode::Immersive_ar => supports_ar = true,
XRSessionMode::Inline => supports_inline = true,
}
}
}
let init = MockDeviceInit { let init = MockDeviceInit {
viewer_origin: origin, viewer_origin: origin,
views, views,
supports_immersive: init.supportsImmersive, supports_inline,
supports_unbounded: init.supportsUnbounded, supports_vr,
supports_ar,
floor_origin, floor_origin,
supported_features, supported_features,
world,
}; };
let global = self.global(); let global = self.global();

View file

@ -1,4 +0,0 @@
[xrDevice_isSessionSupported_immersive-ar.https.html]
[isSessionSupported resolves to false for immersive-ar on an unsupported device]
expected: FAIL

View file

@ -1,4 +0,0 @@
[xrDevice_requestSession_immersive-ar.https.html]
[Tests requestSession rejects immersive-ar mode when unsupported]
expected: FAIL

View file

@ -1,4 +1,5 @@
[ar_dom_overlay.https.html] [ar_dom_overlay.https.html]
expected: ERROR
[Ensures DOM Overlay element selection works] [Ensures DOM Overlay element selection works]
expected: FAIL expected: FAIL
@ -6,7 +7,7 @@
expected: FAIL expected: FAIL
[Ensures DOM Overlay input deduplication works] [Ensures DOM Overlay input deduplication works]
expected: FAIL expected: TIMEOUT
[Ensures DOM Overlay feature works for immersive-ar, body element] [Ensures DOM Overlay feature works for immersive-ar, body element]
expected: FAIL expected: FAIL
@ -15,11 +16,11 @@
expected: FAIL expected: FAIL
[Ensures DOM Overlay Fullscreen API doesn't change DOM overlay] [Ensures DOM Overlay Fullscreen API doesn't change DOM overlay]
expected: FAIL expected: NOTRUN
[Ensures DOM Overlay feature works for immersive-ar, div element] [Ensures DOM Overlay feature works for immersive-ar, div element]
expected: FAIL expected: FAIL
[Ensures DOM Overlay interactions on cross origin iframe are ignored] [Ensures DOM Overlay interactions on cross origin iframe are ignored]
expected: FAIL expected: NOTRUN

View file

@ -1,2 +1,4 @@
[ar_dom_overlay_hit_test.https.html] [ar_dom_overlay_hit_test.https.html]
expected: ERROR [Ensures DOM Overlay interactions on cross origin iframe do not cause hit test results to come up]
expected: FAIL

View file

@ -1,2 +1,10 @@
[ar_hittest_subscription_inputSources.https.html] [ar_hittest_subscription_inputSources.https.html]
expected: ERROR [Ensures subscription to hit test works with an XRSpace from input source - after move - no results]
expected: FAIL
[Ensures subscription to hit test works with an XRSpace from input source - after move - 1 result]
expected: FAIL
[Ensures subscription to hit test works with an XRSpace from input source - no move]
expected: FAIL

View file

@ -1,2 +1,10 @@
[ar_hittest_subscription_refSpaces.https.html] [ar_hittest_subscription_refSpaces.https.html]
expected: ERROR [Ensures subscription to hit test works with viewer space - straight ahead - plane]
expected: FAIL
[Ensures subscription to hit test works with local space]
expected: FAIL
[Ensures subscription to hit test works with local-floor space]
expected: FAIL

View file

@ -1,10 +0,0 @@
[ar_hittest_subscription_states_regular.https.html]
[Hit test subscription succeeds if the feature was requested]
expected: FAIL
[Hit test subscription fails if the feature was not requested]
expected: FAIL
[Hit test subscription fails if the feature was requested but the session already ended]
expected: FAIL

View file

@ -1,2 +1,11 @@
[ar_hittest_subscription_transientInputSources.https.html] [ar_hittest_subscription_transientInputSources.https.html]
expected: ERROR expected: ERROR
[Ensures subscription to transient hit test works with an XRSpace from input source - after move - 1 result]
expected: NOTRUN
[Ensures subscription to transient hit test works with an XRSpace from input source - after move - no results]
expected: NOTRUN
[Ensures subscription to transient hit test works with an XRSpace from input source - no move]
expected: TIMEOUT

View file

@ -1,4 +0,0 @@
[xrRay_constructor.https.html]
[XRRay constructors work]
expected: FAIL

View file

@ -1,4 +0,0 @@
[xrRay_matrix.https.html]
[XRRay matrix works]
expected: FAIL

View file

@ -13870,7 +13870,7 @@
] ]
], ],
"interfaces.html": [ "interfaces.html": [
"dc9a1f5f378bc50487bfb7dc3db6d121d2f325ca", "1a579837cc22d31a7792566615d9e321b3d7fe39",
[ [
null, null,
{} {}
@ -14609,21 +14609,21 @@
}, },
"webxr": { "webxr": {
"create_session.html": [ "create_session.html": [
"af76c5a812d7d05a0158194560933def3fbdb9f9", "5b5d485b372bfffb22204bc162c9e182306395cb",
[ [
null, null,
{} {}
] ]
], ],
"layers.html": [ "layers.html": [
"31f4b6bd51cfcca47666331857bd2bbdf84d2f5e", "49821d7661f92bc9cf22232d3fcb391c2cdc7295",
[ [
null, null,
{} {}
] ]
], ],
"obtain_frame.html": [ "obtain_frame.html": [
"74fda5bad43e8ea95552e65380e83952680e8469", "d9ff25729f9cdfd348e7c9914ce2dacd231e13a0",
[ [
null, null,
{} {}

View file

@ -266,11 +266,14 @@ test_interfaces([
"XMLHttpRequestUpload", "XMLHttpRequestUpload",
"XMLSerializer", "XMLSerializer",
"XRFrame", "XRFrame",
"XRHitTestResult",
"XRHitTestSource",
"XRInputSource", "XRInputSource",
"XRInputSourceArray", "XRInputSourceArray",
"XRInputSourceEvent", "XRInputSourceEvent",
"XRPose", "XRPose",
"XRReferenceSpace", "XRReferenceSpace",
"XRRay",
"XRRenderState", "XRRenderState",
"XRRigidTransform", "XRRigidTransform",
"XRSession", "XRSession",

View file

@ -8,7 +8,7 @@
<script> <script>
async_test(function(t) { async_test(function(t) {
navigator.xr.test.simulateDeviceConnection({ navigator.xr.test.simulateDeviceConnection({
supportsImmersive: true, supportedModes: ["immersive-vr"],
views: TEST_VIEWS, views: TEST_VIEWS,
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] } viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
}).then((m) => { }).then((m) => {

View file

@ -12,7 +12,7 @@
let gl = canvas.getContext('webgl'); let gl = canvas.getContext('webgl');
promise_test(async function() { promise_test(async function() {
let mock = await navigator.xr.test.simulateDeviceConnection({ let mock = await navigator.xr.test.simulateDeviceConnection({
supportsImmersive: true, supportedModes: ["immersive-vr"],
views: TEST_VIEWS, views: TEST_VIEWS,
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] } viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
}); });

View file

@ -12,7 +12,7 @@
let gl = canvas.getContext('webgl'); let gl = canvas.getContext('webgl');
promise_test(async function() { promise_test(async function() {
let mock = await navigator.xr.test.simulateDeviceConnection({ let mock = await navigator.xr.test.simulateDeviceConnection({
supportsImmersive: true, supportedModes: ["immersive-vr"],
views: TEST_VIEWS, views: TEST_VIEWS,
viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] } viewerOrigin: {position: [0.5, 0.1, 0.1], orientation: [1, 0, 0, 1] }
}); });