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