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 stylo_atoms::Atom;
21use webxr_api::{
22 self, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode,
23 Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray,
24 SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility, util,
25};
26
27use crate::conversions::Convert;
28use crate::canvas_context::CanvasContext;
29use crate::dom::bindings::trace::HashMapTracedValues;
30use crate::dom::bindings::buffer_source::create_buffer_source;
31use crate::dom::bindings::callback::ExceptionHandling;
32use crate::dom::bindings::cell::DomRefCell;
33use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
34use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
35use crate::dom::bindings::codegen::Bindings::XRHitTestSourceBinding::{
36 XRHitTestOptionsInit, XRHitTestTrackableType,
37};
38use crate::dom::bindings::codegen::Bindings::XRInputSourceArrayBinding::XRInputSourceArray_Binding::XRInputSourceArrayMethods;
39use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType;
40use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::{
41 XRRenderStateInit, XRRenderStateMethods,
42};
43use crate::dom::bindings::codegen::Bindings::XRSessionBinding::{
44 XREnvironmentBlendMode, XRFrameRequestCallback, XRInteractionMode, XRSessionMethods,
45 XRVisibilityState,
46};
47use crate::dom::bindings::codegen::Bindings::XRSystemBinding::XRSessionMode;
48use crate::dom::bindings::error::{Error, ErrorResult};
49use crate::dom::bindings::inheritance::Castable;
50use crate::dom::bindings::num::Finite;
51use crate::dom::bindings::refcounted::Trusted;
52use crate::dom::bindings::reflector::{reflect_dom_object, DomGlobal};
53use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom};
54use crate::dom::bindings::utils::to_frozen_array;
55use crate::dom::event::Event;
56use crate::dom::eventtarget::EventTarget;
57use crate::dom::promise::Promise;
58use crate::dom::window::Window;
59use crate::dom::xrboundedreferencespace::XRBoundedReferenceSpace;
60use crate::dom::xrframe::XRFrame;
61use crate::dom::xrhittestsource::XRHitTestSource;
62use crate::dom::xrinputsourcearray::XRInputSourceArray;
63use crate::dom::xrinputsourceevent::XRInputSourceEvent;
64use crate::dom::xrreferencespace::XRReferenceSpace;
65use crate::dom::xrreferencespaceevent::XRReferenceSpaceEvent;
66use crate::dom::xrrenderstate::XRRenderState;
67use crate::dom::xrrigidtransform::XRRigidTransform;
68use crate::dom::xrsessionevent::XRSessionEvent;
69use crate::dom::xrspace::XRSpace;
70use crate::realms::InRealm;
71use crate::script_runtime::JSContext;
72use crate::script_runtime::CanGc;
73
74#[dom_struct]
75pub(crate) struct XRSession {
76 eventtarget: EventTarget,
77 blend_mode: XREnvironmentBlendMode,
78 mode: XRSessionMode,
79 visibility_state: Cell<XRVisibilityState>,
80 viewer_space: MutNullableDom<XRSpace>,
81 #[ignore_malloc_size_of = "defined in webxr"]
82 #[no_trace]
83 session: DomRefCell<Session>,
84 frame_requested: Cell<bool>,
85 pending_render_state: MutNullableDom<XRRenderState>,
86 active_render_state: MutDom<XRRenderState>,
87 #[no_trace]
89 inline_projection_matrix: DomRefCell<Transform3D<f32, Viewer, Display>>,
90
91 next_raf_id: Cell<i32>,
92 #[ignore_malloc_size_of = "closures are hard"]
93 raf_callback_list: DomRefCell<Vec<(i32, Option<Rc<XRFrameRequestCallback>>)>>,
94 #[ignore_malloc_size_of = "closures are hard"]
95 current_raf_callback_list: DomRefCell<Vec<(i32, Option<Rc<XRFrameRequestCallback>>)>>,
96 input_sources: Dom<XRInputSourceArray>,
97 #[ignore_malloc_size_of = "promises are hard"]
99 end_promises: DomRefCell<Vec<Rc<Promise>>>,
100 ended: Cell<bool>,
102 #[ignore_malloc_size_of = "defined in webxr"]
103 #[no_trace]
104 next_hit_test_id: Cell<HitTestId>,
105 #[ignore_malloc_size_of = "defined in webxr"]
106 pending_hit_test_promises: DomRefCell<HashMapTracedValues<HitTestId, Rc<Promise>>>,
107 outside_raf: Cell<bool>,
110 #[ignore_malloc_size_of = "defined in webxr"]
111 #[no_trace]
112 input_frames: DomRefCell<HashMap<InputId, InputFrame>>,
113 framerate: Cell<f32>,
114 #[ignore_malloc_size_of = "promises are hard"]
115 update_framerate_promise: DomRefCell<Option<Rc<Promise>>>,
116 reference_spaces: DomRefCell<Vec<Dom<XRReferenceSpace>>>,
117}
118
119impl XRSession {
120 fn new_inherited(
121 session: Session,
122 render_state: &XRRenderState,
123 input_sources: &XRInputSourceArray,
124 mode: XRSessionMode,
125 ) -> XRSession {
126 XRSession {
127 eventtarget: EventTarget::new_inherited(),
128 blend_mode: session.environment_blend_mode().convert(),
129 mode,
130 visibility_state: Cell::new(XRVisibilityState::Visible),
131 viewer_space: Default::default(),
132 session: DomRefCell::new(session),
133 frame_requested: Cell::new(false),
134 pending_render_state: MutNullableDom::new(None),
135 active_render_state: MutDom::new(render_state),
136 inline_projection_matrix: Default::default(),
137
138 next_raf_id: Cell::new(0),
139 raf_callback_list: DomRefCell::new(vec![]),
140 current_raf_callback_list: DomRefCell::new(vec![]),
141 input_sources: Dom::from_ref(input_sources),
142 end_promises: DomRefCell::new(vec![]),
143 ended: Cell::new(false),
144 next_hit_test_id: Cell::new(HitTestId(0)),
145 pending_hit_test_promises: DomRefCell::new(HashMapTracedValues::new()),
146 outside_raf: Cell::new(true),
147 input_frames: DomRefCell::new(HashMap::new()),
148 framerate: Cell::new(0.0),
149 update_framerate_promise: DomRefCell::new(None),
150 reference_spaces: DomRefCell::new(Vec::new()),
151 }
152 }
153
154 pub(crate) fn new(
155 window: &Window,
156 session: Session,
157 mode: XRSessionMode,
158 frame_receiver: IpcReceiver<Frame>,
159 can_gc: CanGc,
160 ) -> DomRoot<XRSession> {
161 let ivfov = if mode == XRSessionMode::Inline {
162 Some(FRAC_PI_2)
163 } else {
164 None
165 };
166 let render_state = XRRenderState::new(window, 0.1, 1000.0, ivfov, None, Vec::new(), can_gc);
167 let input_sources = XRInputSourceArray::new(window, can_gc);
168 let ret = reflect_dom_object(
169 Box::new(XRSession::new_inherited(
170 session,
171 &render_state,
172 &input_sources,
173 mode,
174 )),
175 window,
176 can_gc,
177 );
178 ret.attach_event_handler();
179 ret.setup_raf_loop(frame_receiver);
180 ret
181 }
182
183 pub(crate) fn with_session<R, F: FnOnce(&Session) -> R>(&self, with: F) -> R {
184 let session = self.session.borrow();
185 with(&session)
186 }
187
188 pub(crate) fn is_ended(&self) -> bool {
189 self.ended.get()
190 }
191
192 pub(crate) fn is_immersive(&self) -> bool {
193 self.mode != XRSessionMode::Inline
194 }
195
196 pub(crate) fn has_layers_feature(&self) -> bool {
198 false
201 }
202
203 fn setup_raf_loop(&self, frame_receiver: IpcReceiver<Frame>) {
204 let this = Trusted::new(self);
205 let global = self.global();
206 let task_source = global
207 .task_manager()
208 .dom_manipulation_task_source()
209 .to_sendable();
210 ROUTER.add_typed_route(
211 frame_receiver,
212 Box::new(move |message| {
213 let frame: Frame = message.unwrap();
214 let time = CrossProcessInstant::now();
215 let this = this.clone();
216 task_source.queue(task!(xr_raf_callback: move || {
217 this.root().raf_callback(frame, time, CanGc::note());
218 }));
219 }),
220 );
221
222 self.session.borrow_mut().start_render_loop();
223 }
224
225 pub(crate) fn is_outside_raf(&self) -> bool {
226 self.outside_raf.get()
227 }
228
229 fn attach_event_handler(&self) {
230 let this = Trusted::new(self);
231 let global = self.global();
232 let task_source = global
233 .task_manager()
234 .dom_manipulation_task_source()
235 .to_sendable();
236 let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
237
238 ROUTER.add_typed_route(
239 receiver.to_ipc_receiver(),
240 Box::new(move |message| {
241 let this = this.clone();
242 task_source.queue(task!(xr_event_callback: move || {
243 this.root().event_callback(message.unwrap(), CanGc::note());
244 }));
245 }),
246 );
247
248 self.session.borrow_mut().set_event_dest(sender);
250 }
251
252 pub(crate) fn setup_initial_inputs(&self) {
258 let initial_inputs = self.session.borrow().initial_inputs().to_owned();
259
260 if initial_inputs.is_empty() {
261 return;
263 }
264
265 let this = Trusted::new(self);
266 self.global()
269 .task_manager()
270 .dom_manipulation_task_source()
271 .queue(task!(session_initial_inputs: move || {
272 let this = this.root();
273 this.input_sources.add_input_sources(&this, &initial_inputs, CanGc::note());
274 }));
275 }
276
277 fn event_callback(&self, event: XREvent, can_gc: CanGc) {
278 match event {
279 XREvent::SessionEnd => {
280 self.ended.set(true);
283 self.global().as_window().Navigator().Xr().end_session(self);
285 for promise in self.end_promises.borrow_mut().drain(..) {
289 promise.resolve_native(&(), can_gc);
290 }
291 let event = XRSessionEvent::new(
293 self.global().as_window(),
294 atom!("end"),
295 false,
296 false,
297 self,
298 can_gc,
299 );
300 event.upcast::<Event>().fire(self.upcast(), can_gc);
301 },
302 XREvent::Select(input, kind, ty, frame) => {
303 use stylo_atoms::Atom;
304 const START_ATOMS: [Atom; 2] = [atom!("selectstart"), atom!("squeezestart")];
305 const EVENT_ATOMS: [Atom; 2] = [atom!("select"), atom!("squeeze")];
306 const END_ATOMS: [Atom; 2] = [atom!("selectend"), atom!("squeezeend")];
307
308 let source = self.input_sources.find(input);
310 let atom_index = if kind == SelectKind::Squeeze { 1 } else { 0 };
311 if let Some(source) = source {
312 let frame = XRFrame::new(self.global().as_window(), self, frame, can_gc);
313 frame.set_active(true);
314 if ty == SelectEvent::Start {
315 let event = XRInputSourceEvent::new(
316 self.global().as_window(),
317 START_ATOMS[atom_index].clone(),
318 false,
319 false,
320 &frame,
321 &source,
322 can_gc,
323 );
324 event.upcast::<Event>().fire(self.upcast(), can_gc);
325 } else {
326 if ty == SelectEvent::Select {
327 let event = XRInputSourceEvent::new(
328 self.global().as_window(),
329 EVENT_ATOMS[atom_index].clone(),
330 false,
331 false,
332 &frame,
333 &source,
334 can_gc,
335 );
336 event.upcast::<Event>().fire(self.upcast(), can_gc);
337 }
338 let event = XRInputSourceEvent::new(
339 self.global().as_window(),
340 END_ATOMS[atom_index].clone(),
341 false,
342 false,
343 &frame,
344 &source,
345 can_gc,
346 );
347 event.upcast::<Event>().fire(self.upcast(), can_gc);
348 }
349 frame.set_active(false);
350 }
351 },
352 XREvent::VisibilityChange(v) => {
353 let v = match v {
354 Visibility::Visible => XRVisibilityState::Visible,
355 Visibility::VisibleBlurred => XRVisibilityState::Visible_blurred,
356 Visibility::Hidden => XRVisibilityState::Hidden,
357 };
358 self.visibility_state.set(v);
359 let event = XRSessionEvent::new(
360 self.global().as_window(),
361 atom!("visibilitychange"),
362 false,
363 false,
364 self,
365 can_gc,
366 );
367 event.upcast::<Event>().fire(self.upcast(), can_gc);
368 self.dirty_layers();
371 },
372 XREvent::AddInput(info) => {
373 self.input_sources.add_input_sources(self, &[info], can_gc);
374 },
375 XREvent::RemoveInput(id) => {
376 self.input_sources.remove_input_source(self, id, can_gc);
377 },
378 XREvent::UpdateInput(id, source) => {
379 self.input_sources
380 .add_remove_input_source(self, id, source, can_gc);
381 },
382 XREvent::InputChanged(id, frame) => {
383 self.input_frames.borrow_mut().insert(id, frame);
384 },
385 XREvent::ReferenceSpaceChanged(base_space, transform) => {
386 self.reference_spaces
387 .borrow()
388 .iter()
389 .filter(|space| {
390 let base = match space.ty() {
391 XRReferenceSpaceType::Local => webxr_api::BaseSpace::Local,
392 XRReferenceSpaceType::Viewer => webxr_api::BaseSpace::Viewer,
393 XRReferenceSpaceType::Local_floor => webxr_api::BaseSpace::Floor,
394 XRReferenceSpaceType::Bounded_floor => {
395 webxr_api::BaseSpace::BoundedFloor
396 },
397 _ => panic!("unsupported reference space found"),
398 };
399 base == base_space
400 })
401 .for_each(|space| {
402 let offset =
403 XRRigidTransform::new(self.global().as_window(), transform, can_gc);
404 let event = XRReferenceSpaceEvent::new(
405 self.global().as_window(),
406 atom!("reset"),
407 false,
408 false,
409 space,
410 Some(&*offset),
411 can_gc,
412 );
413 event.upcast::<Event>().fire(space.upcast(), can_gc);
414 });
415 },
416 }
417 }
418
419 fn raf_callback(&self, mut frame: Frame, time: CrossProcessInstant, can_gc: CanGc) {
421 debug!("WebXR RAF callback {:?}", frame);
422
423 if let Some(pending) = self.pending_render_state.take() {
427 self.active_render_state.set(&pending);
431 if !self.is_immersive() {
434 self.update_inline_projection_matrix()
435 }
436 }
437
438 for event in frame.events.drain(..) {
440 self.handle_frame_event(event, can_gc);
441 }
442
443 if !self
449 .active_render_state
450 .get()
451 .has_sub_images(&frame.sub_images[..])
452 {
453 warn!("Rendering blank XR frame");
458 self.session.borrow_mut().render_animation_frame();
459 return;
460 }
461
462 {
466 let mut current = self.current_raf_callback_list.borrow_mut();
467 assert!(current.is_empty());
468 mem::swap(&mut *self.raf_callback_list.borrow_mut(), &mut current);
469 }
470
471 let time = self.global().performance().to_dom_high_res_time_stamp(time);
472 let frame = XRFrame::new(self.global().as_window(), self, frame, CanGc::note());
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(&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 let callback = self.current_raf_callback_list.borrow()[i].1.clone();
489 if let Some(callback) = callback {
490 let _ = callback.Call__(time, &frame, ExceptionHandling::Report, can_gc);
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, frame: &XRFrame) {
552 if let Some(layer) = self.active_render_state.get().GetBaseLayer() {
553 layer.begin_frame(frame);
554 }
555 self.active_render_state.get().with_layers(|layers| {
556 for layer in layers {
557 layer.begin_frame(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, event: FrameUpdateEvent, can_gc: CanGc) {
586 match event {
587 FrameUpdateEvent::HitTestSourceAdded(id) => {
588 if let Some(promise) = self.pending_hit_test_promises.borrow_mut().remove(&id) {
589 promise.resolve_native(
590 &XRHitTestSource::new(self.global().as_window(), id, self, can_gc),
591 can_gc,
592 );
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, rate: f32, can_gc: CanGc) {
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 self.global().as_window(),
614 Atom::from("frameratechange"),
615 false,
616 false,
617 self,
618 can_gc,
619 );
620 event.upcast::<Event>().fire(self.upcast(), can_gc);
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, init: &XRRenderStateInit, _: InRealm) -> ErrorResult {
670 if self.ended.get() {
672 return Err(Error::InvalidState);
673 }
674 if let Some(Some(ref layer)) = init.baseLayer {
676 if Dom::from_ref(layer.session()) != Dom::from_ref(self) {
677 return Err(Error::InvalidState);
678 }
679 }
680
681 if init.inlineVerticalFieldOfView.is_some() && self.is_immersive() {
683 return Err(Error::InvalidState);
684 }
685
686 if init.baseLayer.is_some() && (self.has_layers_feature() || init.layers.is_some()) {
689 return Err(Error::NotSupported);
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(String::from("Duplicate entry in WebXR layers")));
701 }
702 }
703
704 for layer in layers {
706 if layer.session() != self {
707 return Err(Error::Type(String::from(
708 "Layer from different session in WebXR layers",
709 )));
710 }
711 }
712 }
713
714 let pending = self
716 .pending_render_state
717 .or_init(|| self.active_render_state.get().clone_object());
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: Rc<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)));
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 ty: XRReferenceSpaceType,
833 comp: InRealm,
834 can_gc: CanGc,
835 ) -> Rc<Promise> {
836 let p = Promise::new_in_current_realm(comp, can_gc);
837
838 if !self.is_immersive() &&
844 (ty == XRReferenceSpaceType::Bounded_floor || ty == XRReferenceSpaceType::Unbounded)
845 {
846 p.reject_error(Error::NotSupported, can_gc);
847 return p;
848 }
849
850 match ty {
851 XRReferenceSpaceType::Unbounded => {
852 p.reject_error(Error::NotSupported, can_gc)
854 },
855 ty => {
856 if ty != XRReferenceSpaceType::Viewer &&
857 (!self.is_immersive() || ty != XRReferenceSpaceType::Local)
858 {
859 let s = ty.as_str();
860 if !self
861 .session
862 .borrow()
863 .granted_features()
864 .iter()
865 .any(|f| *f == s)
866 {
867 p.reject_error(Error::NotSupported, can_gc);
868 return p;
869 }
870 }
871 if ty == XRReferenceSpaceType::Bounded_floor {
872 let space =
873 XRBoundedReferenceSpace::new(self.global().as_window(), self, can_gc);
874 self.reference_spaces
875 .borrow_mut()
876 .push(Dom::from_ref(space.reference_space()));
877 p.resolve_native(&space, can_gc);
878 } else {
879 let space = XRReferenceSpace::new(self.global().as_window(), self, ty, can_gc);
880 self.reference_spaces
881 .borrow_mut()
882 .push(Dom::from_ref(&*space));
883 p.resolve_native(&space, can_gc);
884 }
885 },
886 }
887 p
888 }
889
890 fn InputSources(&self) -> DomRoot<XRInputSourceArray> {
892 DomRoot::from_ref(&*self.input_sources)
893 }
894
895 fn End(&self, can_gc: CanGc) -> Rc<Promise> {
897 let global = self.global();
898 let p = Promise::new(&global, can_gc);
899 if self.ended.get() && self.end_promises.borrow().is_empty() {
900 p.resolve_native(&(), can_gc);
910 return p;
911 }
912 self.end_promises.borrow_mut().push(p.clone());
913 self.ended.set(true);
917 global.as_window().Navigator().Xr().end_session(self);
918 self.session.borrow_mut().end_session();
919 for source in 0..self.input_sources.Length() {
921 self.input_sources
922 .remove_input_source(self, InputId(source), can_gc);
923 }
924 p
925 }
926
927 fn RequestHitTestSource(&self, options: &XRHitTestOptionsInit, can_gc: CanGc) -> Rc<Promise> {
929 let p = Promise::new(&self.global(), can_gc);
930
931 if !self
932 .session
933 .borrow()
934 .granted_features()
935 .iter()
936 .any(|f| f == "hit-test")
937 {
938 p.reject_error(Error::NotSupported, can_gc);
939 return p;
940 }
941
942 let id = self.next_hit_test_id.get();
943 self.next_hit_test_id.set(HitTestId(id.0 + 1));
944
945 let space = options.space.space();
946 let ray = if let Some(ref ray) = options.offsetRay {
947 ray.ray()
948 } else {
949 Ray {
950 origin: Vector3D::new(0., 0., 0.),
951 direction: Vector3D::new(0., 0., -1.),
952 }
953 };
954
955 let mut types = EntityTypes::default();
956
957 if let Some(ref tys) = options.entityTypes {
958 for ty in tys {
959 match ty {
960 XRHitTestTrackableType::Point => types.point = true,
961 XRHitTestTrackableType::Plane => types.plane = true,
962 XRHitTestTrackableType::Mesh => types.mesh = true,
963 }
964 }
965 } else {
966 types.plane = true;
967 }
968
969 let source = HitTestSource {
970 id,
971 space,
972 ray,
973 types,
974 };
975 self.pending_hit_test_promises
976 .borrow_mut()
977 .insert(id, p.clone());
978
979 self.session.borrow().request_hit_test(source);
980
981 p
982 }
983
984 fn InteractionMode(&self) -> XRInteractionMode {
986 XRInteractionMode::World_space
989 }
990
991 fn GetFrameRate(&self) -> Option<Finite<f32>> {
993 let session = self.session.borrow();
994 if self.mode == XRSessionMode::Inline || session.supported_frame_rates().is_empty() {
995 None
996 } else {
997 Finite::new(self.framerate.get())
998 }
999 }
1000
1001 fn GetSupportedFrameRates(&self, cx: JSContext, can_gc: CanGc) -> Option<Float32Array> {
1003 let session = self.session.borrow();
1004 if self.mode == XRSessionMode::Inline || session.supported_frame_rates().is_empty() {
1005 None
1006 } else {
1007 let framerates = session.supported_frame_rates();
1008 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
1009 Some(
1010 create_buffer_source(cx, framerates, array.handle_mut(), can_gc)
1011 .expect("Failed to construct supported frame rates array"),
1012 )
1013 }
1014 }
1015
1016 fn EnabledFeatures(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
1018 let session = self.session.borrow();
1019 let features = session.granted_features();
1020 to_frozen_array(features, cx, retval, can_gc)
1021 }
1022
1023 fn IsSystemKeyboardSupported(&self) -> bool {
1025 false
1028 }
1029
1030 fn UpdateTargetFrameRate(
1032 &self,
1033 rate: Finite<f32>,
1034 comp: InRealm,
1035 can_gc: CanGc,
1036 ) -> Rc<Promise> {
1037 let promise = Promise::new_in_current_realm(comp, can_gc);
1038 {
1039 let session = self.session.borrow();
1040 let supported_frame_rates = session.supported_frame_rates();
1041
1042 if self.mode == XRSessionMode::Inline ||
1043 supported_frame_rates.is_empty() ||
1044 self.ended.get()
1045 {
1046 promise.reject_error(Error::InvalidState, can_gc);
1047 return promise;
1048 }
1049
1050 if !supported_frame_rates.contains(&*rate) {
1051 promise.reject_error(
1052 Error::Type("Provided framerate not supported".into()),
1053 can_gc,
1054 );
1055 return promise;
1056 }
1057 }
1058
1059 *self.update_framerate_promise.borrow_mut() = Some(promise.clone());
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 let (sender, receiver) = ipc::channel(global.time_profiler_chan().clone()).unwrap();
1068
1069 ROUTER.add_typed_route(
1070 receiver.to_ipc_receiver(),
1071 Box::new(move |message| {
1072 let this = this.clone();
1073 task_source.queue(task!(update_session_framerate: move || {
1074 let session = this.root();
1075 session.apply_nominal_framerate(message.unwrap(), CanGc::note());
1076 if let Some(promise) = session.update_framerate_promise.borrow_mut().take() {
1077 promise.resolve_native(&(), CanGc::note());
1078 };
1079 }));
1080 }),
1081 );
1082
1083 self.session.borrow_mut().update_frame_rate(*rate, sender);
1084
1085 promise
1086 }
1087}
1088
1089pub(crate) type ApiPose = RigidTransform3D<f32, ApiSpace, webxr_api::Native>;
1091pub(crate) type ApiRigidTransform = RigidTransform3D<f32, ApiSpace, ApiSpace>;
1093
1094#[derive(Clone, Copy)]
1095pub(crate) struct BaseSpace;
1096
1097pub(crate) type BaseTransform = RigidTransform3D<f32, webxr_api::Native, BaseSpace>;
1098
1099#[allow(unsafe_code)]
1100pub(crate) fn cast_transform<T, U, V, W>(
1101 transform: RigidTransform3D<f32, T, U>,
1102) -> RigidTransform3D<f32, V, W> {
1103 unsafe { mem::transmute(transform) }
1104}
1105
1106impl Convert<XREnvironmentBlendMode> for EnvironmentBlendMode {
1107 fn convert(self) -> XREnvironmentBlendMode {
1108 match self {
1109 EnvironmentBlendMode::Opaque => XREnvironmentBlendMode::Opaque,
1110 EnvironmentBlendMode::AlphaBlend => XREnvironmentBlendMode::Alpha_blend,
1111 EnvironmentBlendMode::Additive => XREnvironmentBlendMode::Additive,
1112 }
1113 }
1114}