1use std::cell::Cell;
6use std::collections::HashMap;
7use std::f64::consts::{FRAC_PI_2, PI};
8use std::rc::Rc;
9use std::{mem, ptr};
10
11use script_bindings::reflector::reflect_weak_referenceable_dom_object;
12use dom_struct::dom_struct;
13use js::context::JSContext;
14use js::realm::CurrentRealm;
15use euclid::{RigidTransform3D, Transform3D, Vector3D};
16use js::jsapi::JSObject;
17use js::rust::MutableHandleValue;
18use js::typedarray::HeapFloat32Array;
19use profile_traits::generic_callback::GenericCallback as ProfileGenericCallback;
20use rustc_hash::FxBuildHasher;
21use script_bindings::callback::{RootedCallback, TracedCallback};
22use script_bindings::trace::RootedTraceableBox;
23use servo_base::cross_process_instant::CrossProcessInstant;
24use stylo_atoms::Atom;
25use webxr_api::{
26 self, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode,
27 Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray,
28 SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility, util,
29};
30
31use crate::conversions::Convert;
32use crate::canvas_context::CanvasContext;
33use crate::dom::{RootedPromise, TracedPromise};
34use crate::dom::bindings::trace::HashMapTracedValues;
35use crate::dom::bindings::buffer_source::create_buffer_source;
36use crate::dom::bindings::callback::ExceptionHandling;
37use script_bindings::cell::DomRefCell;
38use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
39use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
40use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::{
41 XRHitTestOptionsInit, XRHitTestTrackableType,
42};
43use crate::dom::bindings::codegen::Bindings::XRInputSourceArrayBinding::XRInputSourceArray_Binding::XRInputSourceArrayMethods;
44use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
45use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::{
46 XRRenderStateInit, XRRenderStateMethods,
47};
48use crate::dom::bindings::codegen::Bindings::XRSessionBinding::{
49 XREnvironmentBlendMode, XRFrameRequestCallback, XRInteractionMode, XRSessionMethods,
50 XRVisibilityState,
51};
52use crate::dom::bindings::codegen::Bindings::XRSystemBinding::XRSessionMode;
53use crate::dom::bindings::error::{Error, ErrorResult};
54use crate::dom::bindings::inheritance::Castable;
55use crate::dom::bindings::num::Finite;
56use crate::dom::bindings::refcounted::Trusted;
57use crate::dom::bindings::reflector::{DomGlobal};
58use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom};
59use crate::dom::bindings::utils::to_frozen_array;
60use crate::dom::event::Event;
61use crate::dom::eventtarget::EventTarget;
62use crate::dom::promise::Promise;
63use crate::dom::window::Window;
64use crate::dom::xrboundedreferencespace::XRBoundedReferenceSpace;
65use crate::dom::xrframe::XRFrame;
66use crate::dom::xrhittestsource::XRHitTestSource;
67use crate::dom::xrinputsourcearray::XRInputSourceArray;
68use crate::dom::xrinputsourceevent::XRInputSourceEvent;
69use crate::dom::xrreferencespace::XRReferenceSpace;
70use crate::dom::xrreferencespaceevent::XRReferenceSpaceEvent;
71use crate::dom::xrrenderstate::XRRenderState;
72use crate::dom::xrrigidtransform::XRRigidTransform;
73use crate::dom::xrsessionevent::XRSessionEvent;
74use crate::dom::xrspace::XRSpace;
75
76#[dom_struct]
77pub(crate) struct XRSession {
78 eventtarget: EventTarget,
79 blend_mode: XREnvironmentBlendMode,
80 mode: XRSessionMode,
81 visibility_state: Cell<XRVisibilityState>,
82 viewer_space: MutNullableDom<XRSpace>,
83 #[no_trace]
84 session: DomRefCell<Session>,
85 frame_requested: Cell<bool>,
86 pending_render_state: MutNullableDom<XRRenderState>,
87 active_render_state: MutDom<XRRenderState>,
88 #[no_trace]
90 inline_projection_matrix: DomRefCell<Transform3D<f32, Viewer, Display>>,
91
92 next_raf_id: Cell<i32>,
93 #[ignore_malloc_size_of = "closures are hard"]
94 raf_callback_list: DomRefCell<Vec<(i32, Option<TracedCallback<XRFrameRequestCallback>>)>>,
95 #[ignore_malloc_size_of = "closures are hard"]
96 current_raf_callback_list:
97 DomRefCell<Vec<(i32, Option<TracedCallback<XRFrameRequestCallback>>)>>,
98 input_sources: Dom<XRInputSourceArray>,
99 end_promises: DomRefCell<Vec<TracedPromise>>,
101 ended: Cell<bool>,
103 #[no_trace]
104 next_hit_test_id: Cell<HitTestId>,
105 pending_hit_test_promises:
106 DomRefCell<HashMapTracedValues<HitTestId, TracedPromise, FxBuildHasher>>,
107 outside_raf: Cell<bool>,
110 #[no_trace]
111 input_frames: DomRefCell<HashMap<InputId, InputFrame>>,
112 framerate: Cell<f32>,
113 update_framerate_promise: DomRefCell<Option<TracedPromise>>,
114 reference_spaces: DomRefCell<Vec<Dom<XRReferenceSpace>>>,
115}
116
117impl XRSession {
118 fn new_inherited(
119 session: Session,
120 render_state: &XRRenderState,
121 input_sources: &XRInputSourceArray,
122 mode: XRSessionMode,
123 ) -> XRSession {
124 XRSession {
125 eventtarget: EventTarget::new_inherited(),
126 blend_mode: session.environment_blend_mode().convert(),
127 mode,
128 visibility_state: Cell::new(XRVisibilityState::Visible),
129 viewer_space: Default::default(),
130 session: DomRefCell::new(session),
131 frame_requested: Cell::new(false),
132 pending_render_state: MutNullableDom::new(None),
133 active_render_state: MutDom::new(render_state),
134 inline_projection_matrix: Default::default(),
135
136 next_raf_id: Cell::new(0),
137 raf_callback_list: DomRefCell::new(vec![]),
138 current_raf_callback_list: DomRefCell::new(vec![]),
139 input_sources: Dom::from_ref(input_sources),
140 end_promises: DomRefCell::new(vec![]),
141 ended: Cell::new(false),
142 next_hit_test_id: Cell::new(HitTestId(0)),
143 pending_hit_test_promises: DomRefCell::new(HashMapTracedValues::new_fx()),
144 outside_raf: Cell::new(true),
145 input_frames: DomRefCell::new(HashMap::new()),
146 framerate: Cell::new(0.0),
147 update_framerate_promise: DomRefCell::new(None),
148 reference_spaces: DomRefCell::new(Vec::new()),
149 }
150 }
151
152 pub(crate) fn new(
153 cx: &mut JSContext,
154 window: &Window,
155 session: Session,
156 mode: XRSessionMode,
157 ) -> DomRoot<XRSession> {
158 let ivfov = if mode == XRSessionMode::Inline {
159 Some(FRAC_PI_2)
160 } else {
161 None
162 };
163 let render_state = XRRenderState::new(cx, window, 0.1, 1000.0, ivfov, None, Vec::new());
164 let input_sources = XRInputSourceArray::new(cx, window);
165 let ret = reflect_weak_referenceable_dom_object(
166 cx,
167 Rc::new(XRSession::new_inherited(
168 session,
169 &render_state,
170 &input_sources,
171 mode,
172 )),
173 window,
174 );
175 ret.attach_event_handler();
176 ret.setup_raf_loop();
177 ret
178 }
179
180 pub(crate) fn with_session<R, F: FnOnce(&Session) -> R>(&self, with: F) -> R {
181 let session = self.session.borrow();
182 with(&session)
183 }
184
185 pub(crate) fn is_ended(&self) -> bool {
186 self.ended.get()
187 }
188
189 pub(crate) fn is_immersive(&self) -> bool {
190 self.mode != XRSessionMode::Inline
191 }
192
193 pub(crate) fn has_layers_feature(&self) -> bool {
195 false
198 }
199
200 fn setup_raf_loop(&self) {
201 let this = Trusted::new(self);
202 let global = self.global();
203 let task_source = global
204 .task_manager()
205 .dom_manipulation_task_source()
206 .to_sendable();
207 let callback = ProfileGenericCallback::new(move |message| {
208 let frame: Frame = message.unwrap();
209 let time = CrossProcessInstant::now();
210 let this = this.clone();
211 task_source.queue(task!(xr_raf_callback: move |cx| {
212 this.root().raf_callback(cx, frame, time);
213 }));
214 })
215 .expect("Could not create callback");
216 self.session.borrow_mut().set_frame_dest(callback);
217
218 self.session.borrow_mut().start_render_loop();
219 }
220
221 pub(crate) fn is_outside_raf(&self) -> bool {
222 self.outside_raf.get()
223 }
224
225 fn attach_event_handler(&self) {
226 let this = Trusted::new(self);
227 let global = self.global();
228 let task_source = global
229 .task_manager()
230 .dom_manipulation_task_source()
231 .to_sendable();
232 let callback = ProfileGenericCallback::new(move |message| {
233 let this = this.clone();
234 task_source.queue(task!(xr_event_callback: move |cx| {
235 this.root().event_callback(cx, message.unwrap());
236 }))
237 })
238 .expect("Could not create callback");
239
240 self.session.borrow_mut().set_event_dest(callback);
242 }
243
244 pub(crate) fn setup_initial_inputs(&self) {
250 let initial_inputs = self.session.borrow().initial_inputs().to_owned();
251
252 if initial_inputs.is_empty() {
253 return;
255 }
256
257 let this = Trusted::new(self);
258 self.global()
261 .task_manager()
262 .dom_manipulation_task_source()
263 .queue(task!(session_initial_inputs: move |cx| {
264 let this = this.root();
265 this.input_sources.add_input_sources(cx, &this, &initial_inputs);
266 }));
267 }
268
269 fn event_callback(&self, cx: &mut JSContext, event: XREvent) {
270 match event {
271 XREvent::SessionEnd => {
272 self.ended.set(true);
275 self.global()
277 .as_window()
278 .Navigator(cx)
279 .Xr(cx)
280 .end_session(self);
281 for promise in self.end_promises.borrow().iter() {
285 promise.resolve_native(cx, &());
286 }
287 self.end_promises.safe_borrow_mut(cx.no_gc()).clear();
288 let event = XRSessionEvent::new(
290 cx,
291 self.global().as_window(),
292 atom!("end"),
293 false,
294 false,
295 self,
296 );
297 event.upcast::<Event>().fire(cx, self.upcast());
298 },
299 XREvent::Select(input, kind, ty, frame) => {
300 use stylo_atoms::Atom;
301 const START_ATOMS: [Atom; 2] = [atom!("selectstart"), atom!("squeezestart")];
302 const EVENT_ATOMS: [Atom; 2] = [atom!("select"), atom!("squeeze")];
303 const END_ATOMS: [Atom; 2] = [atom!("selectend"), atom!("squeezeend")];
304
305 let source = self.input_sources.find(input);
307 let atom_index = if kind == SelectKind::Squeeze { 1 } else { 0 };
308 if let Some(source) = source {
309 let frame = XRFrame::new(cx, self.global().as_window(), self, frame);
310 frame.set_active(true);
311 if ty == SelectEvent::Start {
312 let event = XRInputSourceEvent::new(
313 cx,
314 self.global().as_window(),
315 START_ATOMS[atom_index].clone(),
316 false,
317 false,
318 &frame,
319 &source,
320 );
321 event.upcast::<Event>().fire(cx, self.upcast());
322 } else {
323 if ty == SelectEvent::Select {
324 let event = XRInputSourceEvent::new(
325 cx,
326 self.global().as_window(),
327 EVENT_ATOMS[atom_index].clone(),
328 false,
329 false,
330 &frame,
331 &source,
332 );
333 event.upcast::<Event>().fire(cx, self.upcast());
334 }
335 let event = XRInputSourceEvent::new(
336 cx,
337 self.global().as_window(),
338 END_ATOMS[atom_index].clone(),
339 false,
340 false,
341 &frame,
342 &source,
343 );
344 event.upcast::<Event>().fire(cx, self.upcast());
345 }
346 frame.set_active(false);
347 }
348 },
349 XREvent::VisibilityChange(v) => {
350 let v = match v {
351 Visibility::Visible => XRVisibilityState::Visible,
352 Visibility::VisibleBlurred => XRVisibilityState::Visible_blurred,
353 Visibility::Hidden => XRVisibilityState::Hidden,
354 };
355 self.visibility_state.set(v);
356 let event = XRSessionEvent::new(
357 cx,
358 self.global().as_window(),
359 atom!("visibilitychange"),
360 false,
361 false,
362 self,
363 );
364 event.upcast::<Event>().fire(cx, self.upcast());
365 self.dirty_layers();
368 },
369 XREvent::AddInput(info) => {
370 self.input_sources.add_input_sources(cx, self, &[info]);
371 },
372 XREvent::RemoveInput(id) => {
373 self.input_sources.remove_input_source(cx, self, id);
374 },
375 XREvent::UpdateInput(id, source) => {
376 self.input_sources
377 .add_remove_input_source(cx, self, id, source);
378 },
379 XREvent::InputChanged(id, frame) => {
380 self.input_frames.borrow_mut().insert(id, frame);
381 },
382 XREvent::ReferenceSpaceChanged(base_space, transform) => {
383 self.reference_spaces
384 .borrow()
385 .iter()
386 .filter(|space| {
387 let base = match space.ty() {
388 XRReferenceSpaceType::Local => webxr_api::BaseSpace::Local,
389 XRReferenceSpaceType::Viewer => webxr_api::BaseSpace::Viewer,
390 XRReferenceSpaceType::Local_floor => webxr_api::BaseSpace::Floor,
391 XRReferenceSpaceType::Bounded_floor => {
392 webxr_api::BaseSpace::BoundedFloor
393 },
394 _ => panic!("unsupported reference space found"),
395 };
396 base == base_space
397 })
398 .for_each(|space| {
399 let offset =
400 XRRigidTransform::new(cx, self.global().as_window(), transform);
401 let event = XRReferenceSpaceEvent::new(
402 cx,
403 self.global().as_window(),
404 atom!("reset"),
405 false,
406 false,
407 space,
408 Some(&*offset),
409 );
410 event.upcast::<Event>().fire(cx, space.upcast());
411 });
412 },
413 }
414 }
415
416 fn raf_callback(&self, cx: &mut JSContext, mut frame: Frame, time: CrossProcessInstant) {
418 debug!("WebXR RAF callback {:?}", frame);
419
420 if let Some(pending) = self.pending_render_state.take() {
424 self.active_render_state.set(&pending);
428 if !self.is_immersive() {
431 self.update_inline_projection_matrix()
432 }
433 }
434
435 for event in frame.events.drain(..) {
437 self.handle_frame_event(cx, event);
438 }
439
440 if !self
446 .active_render_state
447 .get()
448 .has_sub_images(&frame.sub_images[..])
449 {
450 warn!("Rendering blank XR frame");
455 self.session.borrow_mut().render_animation_frame();
456 return;
457 }
458
459 {
463 let mut current = self.current_raf_callback_list.borrow_mut();
464 assert!(current.is_empty());
465 mem::swap(&mut *self.raf_callback_list.borrow_mut(), &mut current);
466 }
467
468 let time = self
469 .global()
470 .performance(cx)
471 .to_dom_high_res_time_stamp(time);
472 let frame = XRFrame::new(cx, self.global().as_window(), self, frame);
473
474 frame.set_active(true);
476 frame.set_animation_frame(true);
477
478 self.apply_frame_updates(&frame);
480
481 self.layers_begin_frame(cx, &frame);
483
484 self.outside_raf.set(false);
486 let len = self.current_raf_callback_list.borrow().len();
487 for i in 0..len {
488 rooted!(&in(cx) let callback = self.current_raf_callback_list.borrow()[i].1.clone());
489 if let Some(ref callback) = *callback {
490 let _ = callback.Call__(cx, time, &frame, ExceptionHandling::Report);
491 }
492 }
493 self.outside_raf.set(true);
494 *self.current_raf_callback_list.borrow_mut() = vec![];
495
496 self.layers_end_frame(&frame);
498
499 frame.set_active(false);
501
502 self.session.borrow_mut().render_animation_frame();
504 }
505
506 fn update_inline_projection_matrix(&self) {
507 debug_assert!(!self.is_immersive());
508 let render_state = self.active_render_state.get();
509 let size = if let Some(base) = render_state.GetBaseLayer() {
510 base.size()
511 } else {
512 return;
513 };
514 let mut clip_planes = util::ClipPlanes::default();
515 let near = *render_state.DepthNear() as f32;
516 let far = *render_state.DepthFar() as f32;
517 clip_planes.update(near, far);
518 let top = *render_state
519 .GetInlineVerticalFieldOfView()
520 .expect("IVFOV should be non null for inline sessions") /
521 2.;
522 let top = near * top.tan() as f32;
523 let bottom = top;
524 let left = top * size.width as f32 / size.height as f32;
525 let right = left;
526 let matrix = util::frustum_to_projection_matrix(left, right, top, bottom, clip_planes);
527 *self.inline_projection_matrix.borrow_mut() = matrix;
528 }
529
530 pub(crate) fn inline_view(&self) -> View<Viewer> {
532 debug_assert!(!self.is_immersive());
533 View {
534 transform: RigidTransform3D::identity(),
536 projection: *self.inline_projection_matrix.borrow(),
537 }
538 }
539
540 pub(crate) fn session_id(&self) -> SessionId {
541 self.session.borrow().id()
542 }
543
544 pub(crate) fn dirty_layers(&self) {
545 if let Some(layer) = self.RenderState().GetBaseLayer() {
546 layer.context().mark_as_dirty();
547 }
548 }
549
550 fn layers_begin_frame(&self, cx: &mut JSContext, frame: &XRFrame) {
552 if let Some(layer) = self.active_render_state.get().GetBaseLayer() {
553 layer.begin_frame(cx, frame);
554 }
555 self.active_render_state.get().with_layers(|layers| {
556 for layer in layers {
557 layer.begin_frame(cx, frame);
558 }
559 });
560 }
561
562 fn layers_end_frame(&self, frame: &XRFrame) {
564 if let Some(layer) = self.active_render_state.get().GetBaseLayer() {
565 layer.end_frame(frame);
566 }
567 self.active_render_state.get().with_layers(|layers| {
568 for layer in layers {
569 layer.end_frame(frame);
570 }
571 });
572 }
573
574 fn apply_frame_updates(&self, _frame: &XRFrame) {
576 for (id, frame) in self.input_frames.borrow_mut().drain() {
578 let source = self.input_sources.find(id);
579 if let Some(source) = source {
580 source.update_gamepad_state(frame);
581 }
582 }
583 }
584
585 fn handle_frame_event(&self, cx: &mut JSContext, event: FrameUpdateEvent) {
586 match event {
587 FrameUpdateEvent::HitTestSourceAdded(id) => {
588 rooted!(&in(cx) let promise = self.pending_hit_test_promises.borrow_mut().remove(&id));
589 if let Some(ref promise) = *promise {
590 let hit_test_source =
591 XRHitTestSource::new(cx, self.global().as_window(), id, self);
592 promise.resolve_native(cx, &hit_test_source);
593 } else {
594 warn!(
595 "received hit test add request for unknown hit test {:?}",
596 id
597 )
598 }
599 },
600 _ => self.session.borrow_mut().apply_event(event),
601 }
602 }
603
604 fn apply_nominal_framerate(&self, cx: &mut JSContext, rate: f32) {
606 if self.framerate.get() == rate || self.ended.get() {
607 return;
608 }
609
610 self.framerate.set(rate);
611
612 let event = XRSessionEvent::new(
613 cx,
614 self.global().as_window(),
615 Atom::from("frameratechange"),
616 false,
617 false,
618 self,
619 );
620 event.upcast::<Event>().fire(cx, self.upcast());
621 }
622}
623
624impl XRSessionMethods<crate::DomTypeHolder> for XRSession {
625 event_handler!(end, GetOnend, SetOnend);
627
628 event_handler!(select, GetOnselect, SetOnselect);
630
631 event_handler!(selectstart, GetOnselectstart, SetOnselectstart);
633
634 event_handler!(selectend, GetOnselectend, SetOnselectend);
636
637 event_handler!(squeeze, GetOnsqueeze, SetOnsqueeze);
639
640 event_handler!(squeezestart, GetOnsqueezestart, SetOnsqueezestart);
642
643 event_handler!(squeezeend, GetOnsqueezeend, SetOnsqueezeend);
645
646 event_handler!(
648 visibilitychange,
649 GetOnvisibilitychange,
650 SetOnvisibilitychange
651 );
652
653 event_handler!(
655 inputsourceschange,
656 GetOninputsourceschange,
657 SetOninputsourceschange
658 );
659
660 event_handler!(frameratechange, GetOnframeratechange, SetOnframeratechange);
662
663 fn RenderState(&self) -> DomRoot<XRRenderState> {
665 self.active_render_state.get()
666 }
667
668 fn UpdateRenderState(&self, cx: &mut JSContext, init: &XRRenderStateInit) -> ErrorResult {
670 if self.ended.get() {
672 return Err(Error::InvalidState(None));
673 }
674 if let Some(Some(ref layer)) = init.baseLayer &&
676 Dom::from_ref(layer.session()) != Dom::from_ref(self)
677 {
678 return Err(Error::InvalidState(None));
679 }
680
681 if init.inlineVerticalFieldOfView.is_some() && self.is_immersive() {
683 return Err(Error::InvalidState(None));
684 }
685
686 if init.baseLayer.is_some() && (self.has_layers_feature() || init.layers.is_some()) {
689 return Err(Error::NotSupported(None));
690 }
691
692 if let Some(Some(ref layers)) = init.layers {
693 for layer in layers {
695 let count = layers
696 .iter()
697 .filter(|other| other.layer_id() == layer.layer_id())
698 .count();
699 if count > 1 {
700 return Err(Error::Type(c"Duplicate entry in WebXR layers".to_owned()));
701 }
702 }
703
704 for layer in layers {
706 if layer.session() != self {
707 return Err(Error::Type(
708 c"Layer from different session in WebXR layers".to_owned(),
709 ));
710 }
711 }
712 }
713
714 let pending = self
716 .pending_render_state
717 .or_init(|| self.active_render_state.get().clone_object(cx));
718
719 if let Some(ref layers) = init.layers {
721 let layers = layers.as_deref().unwrap_or_default();
722 pending.set_base_layer(None);
723 pending.set_layers(layers.iter().map(|x| &**x).collect());
724 let layers = layers
725 .iter()
726 .filter_map(|layer| {
727 let context_id = WebXRContextId::from(layer.context_id());
728 let layer_id = layer.layer_id()?;
729 Some((context_id, layer_id))
730 })
731 .collect();
732 self.session.borrow_mut().set_layers(layers);
733 }
734
735 if let Some(near) = init.depthNear {
738 let mut near = *near;
739 if near < 0. {
743 near = 0.;
744 }
745 pending.set_depth_near(near);
746 }
747 if let Some(far) = init.depthFar {
748 let mut far = *far;
749 if far < 0. {
755 far = 0.;
756 }
757 pending.set_depth_far(far);
758 }
759 if let Some(fov) = init.inlineVerticalFieldOfView {
760 let mut fov = *fov;
761 if fov < 0. {
765 fov = 0.0001;
766 } else if fov > PI {
767 fov = PI - 0.0001;
768 }
769 pending.set_inline_vertical_fov(fov);
770 }
771 if let Some(ref layer) = init.baseLayer {
772 pending.set_base_layer(layer.as_deref());
773 pending.set_layers(Vec::new());
774 let layers = layer
775 .iter()
776 .filter_map(|layer| {
777 let context_id = WebXRContextId::from(layer.context_id());
778 let layer_id = layer.layer_id()?;
779 Some((context_id, layer_id))
780 })
781 .collect();
782 self.session.borrow_mut().set_layers(layers);
783 }
784
785 if init.depthFar.is_some() || init.depthNear.is_some() {
786 self.session
787 .borrow_mut()
788 .update_clip_planes(*pending.DepthNear() as f32, *pending.DepthFar() as f32);
789 }
790
791 Ok(())
792 }
793
794 fn RequestAnimationFrame(&self, callback: RootedCallback<XRFrameRequestCallback>) -> i32 {
796 let raf_id = self.next_raf_id.get();
798 self.next_raf_id.set(raf_id + 1);
799 self.raf_callback_list
800 .borrow_mut()
801 .push((raf_id, Some(callback.to_traced())));
802
803 raf_id
804 }
805
806 fn CancelAnimationFrame(&self, frame: i32) {
808 let mut list = self.raf_callback_list.borrow_mut();
809 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == frame) {
810 pair.1 = None;
811 }
812
813 let mut list = self.current_raf_callback_list.borrow_mut();
814 if let Some(pair) = list.iter_mut().find(|pair| pair.0 == frame) {
815 pair.1 = None;
816 }
817 }
818
819 fn EnvironmentBlendMode(&self) -> XREnvironmentBlendMode {
821 self.blend_mode
822 }
823
824 fn VisibilityState(&self) -> XRVisibilityState {
826 self.visibility_state.get()
827 }
828
829 fn RequestReferenceSpace(
831 &self,
832 cx: &mut CurrentRealm,
833 ty: XRReferenceSpaceType,
834 ) -> RootedPromise {
835 let p = Promise::new_in_realm(cx);
836
837 if !self.is_immersive() &&
843 (ty == XRReferenceSpaceType::Bounded_floor || ty == XRReferenceSpaceType::Unbounded)
844 {
845 p.reject_error(cx, Error::NotSupported(None));
846 return p;
847 }
848
849 match ty {
850 XRReferenceSpaceType::Unbounded => {
851 p.reject_error(cx, Error::NotSupported(None))
853 },
854 ty => {
855 if ty != XRReferenceSpaceType::Viewer &&
856 (!self.is_immersive() || ty != XRReferenceSpaceType::Local)
857 {
858 let s = ty.as_str();
859 if !self
860 .session
861 .borrow()
862 .granted_features()
863 .iter()
864 .any(|f| *f == s)
865 {
866 p.reject_error(cx, Error::NotSupported(None));
867 return p;
868 }
869 }
870 if ty == XRReferenceSpaceType::Bounded_floor {
871 let space = XRBoundedReferenceSpace::new(cx, self.global().as_window(), self);
872 self.reference_spaces
873 .borrow_mut()
874 .push(Dom::from_ref(space.reference_space()));
875 p.resolve_native(cx, &space);
876 } else {
877 let space = XRReferenceSpace::new(cx, self.global().as_window(), self, ty);
878 self.reference_spaces
879 .borrow_mut()
880 .push(Dom::from_ref(&*space));
881 p.resolve_native(cx, &space);
882 }
883 },
884 }
885 p
886 }
887
888 fn InputSources(&self) -> DomRoot<XRInputSourceArray> {
890 DomRoot::from_ref(&*self.input_sources)
891 }
892
893 fn End(&self, cx: &mut CurrentRealm) -> RootedPromise {
895 let p = Promise::new_in_realm(cx);
896 if self.ended.get() && self.end_promises.borrow().is_empty() {
897 p.resolve_native(cx, &());
907 return p;
908 }
909 self.end_promises.borrow_mut().push(p.to_traced());
910 self.ended.set(true);
914 self.global()
915 .as_window()
916 .Navigator(cx)
917 .Xr(cx)
918 .end_session(self);
919 self.session.borrow_mut().end_session();
920 for source in 0..self.input_sources.Length() {
922 self.input_sources
923 .remove_input_source(cx, self, InputId(source));
924 }
925 p
926 }
927
928 fn RequestHitTestSource(
930 &self,
931 cx: &mut CurrentRealm,
932 options: &XRHitTestOptionsInit,
933 ) -> RootedPromise {
934 let p = Promise::new_in_realm(cx);
935
936 if !self
937 .session
938 .borrow()
939 .granted_features()
940 .iter()
941 .any(|f| f == "hit-test")
942 {
943 p.reject_error(cx, Error::NotSupported(None));
944 return p;
945 }
946
947 let id = self.next_hit_test_id.get();
948 self.next_hit_test_id.set(HitTestId(id.0 + 1));
949
950 let space = options.space.space();
951 let ray = if let Some(ref ray) = options.offsetRay {
952 ray.ray()
953 } else {
954 Ray {
955 origin: Vector3D::new(0., 0., 0.),
956 direction: Vector3D::new(0., 0., -1.),
957 }
958 };
959
960 let mut types = EntityTypes::default();
961
962 if let Some(ref tys) = options.entityTypes {
963 for ty in tys {
964 match ty {
965 XRHitTestTrackableType::Point => types.point = true,
966 XRHitTestTrackableType::Plane => types.plane = true,
967 XRHitTestTrackableType::Mesh => types.mesh = true,
968 }
969 }
970 } else {
971 types.plane = true;
972 }
973
974 let source = HitTestSource {
975 id,
976 space,
977 ray,
978 types,
979 };
980 self.pending_hit_test_promises
981 .borrow_mut()
982 .insert(id, p.to_traced());
983
984 self.session.borrow().request_hit_test(source);
985
986 p
987 }
988
989 fn InteractionMode(&self) -> XRInteractionMode {
991 XRInteractionMode::World_space
994 }
995
996 fn GetFrameRate(&self) -> Option<Finite<f32>> {
998 let session = self.session.borrow();
999 if self.mode == XRSessionMode::Inline || session.supported_frame_rates().is_empty() {
1000 None
1001 } else {
1002 Finite::new(self.framerate.get())
1003 }
1004 }
1005
1006 fn GetSupportedFrameRates(
1008 &self,
1009 cx: &mut JSContext,
1010 ) -> Option<RootedTraceableBox<HeapFloat32Array>> {
1011 let session = self.session.borrow();
1012 if self.mode == XRSessionMode::Inline || session.supported_frame_rates().is_empty() {
1013 None
1014 } else {
1015 let framerates = session.supported_frame_rates();
1016 rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
1017 Some(
1018 create_buffer_source(cx, framerates, array.handle_mut())
1019 .expect("Failed to construct supported frame rates array"),
1020 )
1021 }
1022 }
1023
1024 fn EnabledFeatures(&self, cx: &mut JSContext, retval: MutableHandleValue) {
1026 let session = self.session.borrow();
1027 let features = session.granted_features();
1028 to_frozen_array(cx, features, retval)
1029 }
1030
1031 fn IsSystemKeyboardSupported(&self) -> bool {
1033 false
1036 }
1037
1038 fn UpdateTargetFrameRate(&self, cx: &mut CurrentRealm, rate: Finite<f32>) -> RootedPromise {
1040 let promise = Promise::new_in_realm(cx);
1041 {
1042 let session = self.session.borrow();
1043 let supported_frame_rates = session.supported_frame_rates();
1044
1045 if self.mode == XRSessionMode::Inline ||
1046 supported_frame_rates.is_empty() ||
1047 self.ended.get()
1048 {
1049 promise.reject_error(cx, Error::InvalidState(None));
1050 return promise;
1051 }
1052
1053 if !supported_frame_rates.contains(&*rate) {
1054 promise.reject_error(cx, Error::Type(c"Provided framerate not supported".into()));
1055 return promise;
1056 }
1057 }
1058
1059 *self.update_framerate_promise.borrow_mut() = Some(promise.to_traced());
1060
1061 let this = Trusted::new(self);
1062 let global = self.global();
1063 let task_source = global
1064 .task_manager()
1065 .dom_manipulation_task_source()
1066 .to_sendable();
1067
1068 let callback = ProfileGenericCallback::new(move |message| {
1069 let this = this.clone();
1070 task_source.queue(task!(update_session_framerate: move |cx| {
1071 let session = this.root();
1072 session.apply_nominal_framerate(cx, message.unwrap());
1073 rooted!(&in(cx) let promise = session.update_framerate_promise.borrow_mut().take());
1074 if let Some(ref promise) = *promise {
1075 promise.resolve_native(cx, &());
1076 };
1077 }));
1078 })
1079 .expect("Could not create callback");
1080
1081 self.session.borrow_mut().update_frame_rate(*rate, callback);
1082
1083 promise
1084 }
1085}
1086
1087pub(crate) type ApiPose = RigidTransform3D<f32, ApiSpace, webxr_api::Native>;
1089pub(crate) type ApiRigidTransform = RigidTransform3D<f32, ApiSpace, ApiSpace>;
1091
1092#[derive(Clone, Copy)]
1093pub(crate) struct BaseSpace;
1094
1095pub(crate) type BaseTransform = RigidTransform3D<f32, webxr_api::Native, BaseSpace>;
1096
1097pub(crate) fn cast_transform<T, U, V, W>(
1098 transform: RigidTransform3D<f32, T, U>,
1099) -> RigidTransform3D<f32, V, W> {
1100 transform.cast_unit()
1101}
1102
1103impl Convert<XREnvironmentBlendMode> for EnvironmentBlendMode {
1104 fn convert(self) -> XREnvironmentBlendMode {
1105 match self {
1106 EnvironmentBlendMode::Opaque => XREnvironmentBlendMode::Opaque,
1107 EnvironmentBlendMode::AlphaBlend => XREnvironmentBlendMode::Alpha_blend,
1108 EnvironmentBlendMode::Additive => XREnvironmentBlendMode::Additive,
1109 }
1110 }
1111}