mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-18 18:00:31 +00:00
221 lines
9.1 KiB
C++
221 lines
9.1 KiB
C++
/*
|
||
* 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>
|
||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||
#include <LibWeb/WebIDL/Promise.h>
|
||
#include <LibWeb/WebXR/XRSession.h>
|
||
#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);
|
||
|
||
visitor.visit(m_active_immersive_session);
|
||
visitor.visit(m_list_of_inline_sessions);
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
|
||
// 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; });
|
||
}
|
||
|
||
}
|