2025-11-13 17:53:47 -03:00
/*
* Copyright ( c ) 2025 , Psychpsyo < psychpsyo @ gmail . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/Realm.h>
# include <LibWeb/Bindings/Intrinsics.h>
# include <LibWeb/Bindings/PlatformObject.h>
# include <LibWeb/HTML/Scripting/Environments.h>
2025-11-15 14:48:56 -03:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
# include <LibWeb/Platform/EventLoopPlugin.h>
2025-11-13 17:53:47 -03:00
# include <LibWeb/WebIDL/Promise.h>
2025-11-15 14:48:56 -03:00
# include <LibWeb/WebXR/XRSession.h>
2025-11-13 17:53:47 -03:00
# include <LibWeb/WebXR/XRSystem.h>
namespace Web : : WebXR {
GC_DEFINE_ALLOCATOR ( XRSystem ) ;
GC : : Ref < XRSystem > XRSystem : : create ( JS : : Realm & realm )
{
return realm . create < XRSystem > ( realm ) ;
}
XRSystem : : XRSystem ( JS : : Realm & realm )
: DOM : : EventTarget ( realm )
{
}
void XRSystem : : initialize ( JS : : Realm & realm )
{
WEB_SET_PROTOTYPE_FOR_INTERFACE ( XRSystem ) ;
Base : : initialize ( realm ) ;
}
void XRSystem : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2025-11-15 14:48:56 -03:00
visitor . visit ( m_active_immersive_session ) ;
visitor . visit ( m_list_of_inline_sessions ) ;
2025-11-13 17:53:47 -03:00
}
// https://immersive-web.github.io/webxr/#dom-xrsystem-issessionsupported
GC : : Ref < WebIDL : : Promise > XRSystem : : is_session_supported ( Bindings : : XRSessionMode mode ) const
{
// 1. Let promise be a new Promise in the relevant realm of this XRSystem.
auto & realm = HTML : : relevant_realm ( * this ) ;
auto promise = WebIDL : : create_promise ( realm ) ;
// 2. If mode is "inline", resolve promise with true and return it.
if ( mode = = Bindings : : XRSessionMode : : Inline ) {
WebIDL : : resolve_promise ( realm , promise , JS : : Value ( true ) ) ;
return promise ;
}
// 3. If the requesting document’ s origin is not allowed to use the "xr-spatial-tracking" permissions policy, reject promise with a "SecurityError" DOMException and return it.
// FIXME: Implement this.
// 4. Check whether the session mode is supported as follows:
// -> If the user agent and system are known to never support mode sessions
// Resolve promise with false.
WebIDL : : resolve_promise ( realm , promise , JS : : Value ( false ) ) ;
// -> If the user agent and system are known to usually support mode sessions
// promise MAY be resolved with true provided that all instances of this user agent
// indistinguishable by user agent string produce the same result here.
// FIXME: Implement this.
// -> Otherwise
// Run the following steps in parallel:
// FIXME: We currently never end up here.
// Add all these steps once WebXR is more supported.
// 5. Return promise.
return promise ;
}
2025-11-15 14:48:56 -03:00
// https://immersive-web.github.io/webxr/#dom-xrsystem-requestsession
GC : : Ref < WebIDL : : Promise > XRSystem : : request_session ( Bindings : : XRSessionMode mode , XRSessionInit options )
{
// 1. Let promise be a new Promise in the relevant realm of this XRSystem.
auto & realm = HTML : : relevant_realm ( * this ) ;
auto promise = WebIDL : : create_promise ( realm ) ;
// 2. Let immersive be true if mode is an immersive session mode, and false otherwise.
auto immersive = mode ! = Bindings : : XRSessionMode : : Inline ;
// 3. Let global object be the relevant Global object for the XRSystem on which this method was invoked.
auto & global_object = HTML : : relevant_global_object ( * this ) ;
// 4. Check whether the session request is allowed as follows:
// -> If immersive is true:
if ( immersive ) {
// 1. Check if an immersive session request is allowed for the global object, and if not
// reject promise with a "SecurityError" DOMException and return promise.
// FIXME: Implement this.
( void ) global_object ;
// 2. If pending immersive session is true or active immersive session is not null, reject
// promise with an "InvalidStateError" DOMException and return promise.
if ( m_pending_immersive_session | | m_active_immersive_session )
WebIDL : : reject_promise ( realm , promise , WebIDL : : InvalidStateError : : create ( realm , " An immersive session is already pending or active. " _utf16 ) ) ;
// 3. Set pending immersive session to true.
m_pending_immersive_session = true ;
}
// -> Otherwise:
else {
// Check if an inline session request is allowed for the global object, and if not reject promise with
// a "SecurityError" DOMException and return promise.
// FIXME: Implement this.
}
// 5. Run the following steps in parallel:
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( GC : : create_function ( realm . heap ( ) , [ this , & realm , promise , options , immersive ] ( ) {
// 1. Let requiredFeatures be options’ requiredFeatures.
auto required_features = options . required_features ;
// 2. Let optionalFeatures be options’ optionalFeatures.
auto optional_features = options . optional_features ;
// 3. Set device to the result of obtaining the current device for mode, requiredFeatures, and optionalFeatures.
// FIXME: Implement https://immersive-web.github.io/webxr/#obtain-the-current-device
( void ) required_features ;
( void ) optional_features ;
// 4. Queue a task to perform the following steps:
HTML : : queue_a_task ( HTML : : Task : : Source : : Unspecified , nullptr , nullptr , GC : : create_function ( realm . heap ( ) , [ this , & realm , promise , immersive ] ( ) {
HTML : : TemporaryExecutionContext context { realm , HTML : : TemporaryExecutionContext : : CallbacksEnabled : : Yes } ;
// 1. If device is null or device’ s list of supported modes does not contain mode, run the following steps:
// AD-HOC: Just reject immersive sessions here until we have devices.
if ( immersive ) {
// 1. Reject promise with a "NotSupportedError" DOMException.
WebIDL : : reject_promise ( realm , promise , WebIDL : : NotSupportedError : : create ( realm , " Sessions of this mode are not supported. " _utf16 ) ) ;
// 2. If immersive is true, set pending immersive session to false.
if ( immersive )
set_pending_immersive_session ( false ) ;
// 3. Abort these steps.
return ;
}
// FIXME:
// 2. Let descriptor be an XRPermissionDescriptor initialized with mode, requiredFeatures, and optionalFeatures
// 3. Let status be an XRPermissionStatus, initially null
// 4. Request the xr permission with descriptor and status.
// 5. If status’ state is "denied" run the following steps:
if ( false ) {
// 1. Reject promise with a "NotSupportedError" DOMException.
WebIDL : : reject_promise ( realm , promise , WebIDL : : NotSupportedError : : create ( realm , " The XR Permissions are denied. " _utf16 ) ) ;
// 2. If immersive is true, set pending immersive session to false.
if ( immersive )
m_pending_immersive_session = false ;
// 3. Abort these steps.
return ;
}
// 6. Let granted be a set obtained from status’ granted.
// 7. Let session be a new XRSession object in the relevant realm of this XRSystem.
auto session = XRSession : : create ( realm , * this ) ;
// 8. Initialize the session with session, mode, granted, and device.
// FIXME: Implement https://immersive-web.github.io/webxr/#initialize-the-session
// 9. Potentially set the active immersive session as follows:
// -> If immersive is true:
if ( immersive ) {
// Set the active immersive session to session, and set pending immersive session to false.
m_active_immersive_session = session ;
m_pending_immersive_session = false ;
}
// -> Otherwise:
else {
// Append session to the list of inline sessions.
m_list_of_inline_sessions . append ( session ) ;
}
// 10. Resolve promise with session.
WebIDL : : resolve_promise ( realm , promise , session ) ;
// 11. Queue a task to perform the following steps:
HTML : : queue_a_task ( HTML : : Task : : Source : : Unspecified , nullptr , nullptr , GC : : create_function ( realm . heap ( ) , [ session ] ( ) {
// Note: These steps ensure that initial inputsourceschange events occur after the initial session is resolved.
// 1. Set session’ s promise resolved flag to true.
session - > set_promise_resolved ( true ) ;
// 2. Let sources be any existing input sources attached to session.
// FIXME: Implement this.
// 3. If sources is non-empty, perform the following steps:
if ( false ) {
// 1. Set session’ s list of active XR input sources to sources.
// FIXME: Implement this.
// 2. Fire an XRInputSourcesChangeEvent named inputsourceschange on session with added set to sources.
// FIXME: Implement this.
}
} ) ) ;
} ) ) ;
} ) ) ;
// 6. Return promise.
return promise ;
}
void XRSystem : : remove_inline_session ( GC : : Ref < XRSession > session )
{
m_list_of_inline_sessions . remove_first_matching ( [ & ] ( auto & entry ) { return entry = = session ; } ) ;
}
2025-11-13 17:53:47 -03:00
}