mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-18 18:00:31 +00:00
148 lines
5.3 KiB
C++
148 lines
5.3 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/Bindings/XRSessionPrototype.h>
|
||
#include <LibWeb/HTML/EventNames.h>
|
||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||
#include <LibWeb/WebIDL/Promise.h>
|
||
#include <LibWeb/WebXR/XRSession.h>
|
||
#include <LibWeb/WebXR/XRSessionEvent.h>
|
||
#include <LibWeb/WebXR/XRSystem.h>
|
||
|
||
namespace Web::WebXR {
|
||
|
||
GC_DEFINE_ALLOCATOR(XRSession);
|
||
|
||
GC::Ref<XRSession> XRSession::create(JS::Realm& realm, GC::Ref<XRSystem> xr_system)
|
||
{
|
||
return realm.create<XRSession>(realm, xr_system);
|
||
}
|
||
|
||
XRSession::XRSession(JS::Realm& realm, XRSystem& xr_system)
|
||
: DOM::EventTarget(realm)
|
||
, m_xr_system(xr_system)
|
||
{
|
||
}
|
||
|
||
void XRSession::initialize(JS::Realm& realm)
|
||
{
|
||
WEB_SET_PROTOTYPE_FOR_INTERFACE(XRSession);
|
||
Base::initialize(realm);
|
||
}
|
||
|
||
void XRSession::visit_edges(Cell::Visitor& visitor)
|
||
{
|
||
Base::visit_edges(visitor);
|
||
|
||
visitor.visit(m_xr_system);
|
||
visitor.visit(m_outstanding_promises);
|
||
}
|
||
|
||
GC::Ref<WebIDL::Promise> XRSession::create_promise(JS::Realm& realm)
|
||
{
|
||
auto promise = WebIDL::create_promise(realm);
|
||
|
||
m_outstanding_promises.append(promise);
|
||
|
||
return promise;
|
||
}
|
||
void XRSession::resolve_promise(JS::Realm& realm, WebIDL::Promise const& promise, JS::Value value)
|
||
{
|
||
WebIDL::resolve_promise(realm, promise, value);
|
||
m_outstanding_promises.remove_first_matching([&](auto& entry) { return entry == &promise; });
|
||
}
|
||
void XRSession::reject_promise(JS::Realm& realm, WebIDL::Promise const& promise, JS::Value value)
|
||
{
|
||
WebIDL::reject_promise(realm, promise, value);
|
||
m_outstanding_promises.remove_first_matching([&](auto& entry) { return entry == &promise; });
|
||
}
|
||
|
||
// https://immersive-web.github.io/webxr/#dom-xrsession-updaterenderstate
|
||
void XRSession::update_render_state(XRRenderStateInit const&)
|
||
{
|
||
dbgln("FIXME: stubbed out XRSession.updateRenderState()");
|
||
}
|
||
|
||
GC::Ref<WebIDL::Promise> XRSession::end()
|
||
{
|
||
// 1. Let promise be a new Promise in the relevant realm of this XRSession.
|
||
auto& realm = HTML::relevant_realm(*this);
|
||
auto promise = WebIDL::create_promise(realm);
|
||
|
||
// 2. If the ended value of this is true, reject promise with a "InvalidStateError" DOMException and return promise.
|
||
if (m_ended)
|
||
WebIDL::reject_promise(realm, promise, WebIDL::InvalidStateError::create(realm, "Session already ended."_utf16));
|
||
|
||
// 3. Shut down this.
|
||
shut_down();
|
||
|
||
// 4. Queue a task to perform the following steps:
|
||
HTML::queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr, GC::create_function(realm.heap(), [&realm, promise]() {
|
||
// 1. Wait until any platform-specific steps related to shutting down the session have completed.
|
||
// FIXME: Do this once we have any.
|
||
|
||
// 2. Resolve promise.
|
||
HTML::TemporaryExecutionContext context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||
WebIDL::resolve_promise(realm, promise);
|
||
}));
|
||
|
||
// 5. Return promise.
|
||
return promise;
|
||
}
|
||
|
||
// https://immersive-web.github.io/webxr/#shut-down-the-session
|
||
void XRSession::shut_down()
|
||
{
|
||
auto& realm = HTML::relevant_realm(*this);
|
||
|
||
// 1. Set session’s ended value to true.
|
||
m_ended = true;
|
||
|
||
// 2. If the active immersive session is equal to session, set the active immersive session to null.
|
||
if (m_xr_system->active_immersive_session() == this)
|
||
m_xr_system->set_active_immersive_session({});
|
||
|
||
// 3. Remove session from the list of inline sessions.
|
||
m_xr_system->remove_inline_session(*this);
|
||
|
||
// 4. Reject any outstanding promises returned by session with an InvalidStateError, except for any promises returned by end().
|
||
for (auto promise : m_outstanding_promises) {
|
||
WebIDL::reject_promise(realm, promise, WebIDL::InvalidStateError::create(realm, "Session ended."_utf16));
|
||
}
|
||
m_outstanding_promises.clear();
|
||
|
||
// 5. If no other features of the user agent are actively using them, perform the necessary platform-specific steps to shut down the device’s tracking and rendering capabilities. This MUST include:
|
||
// - Releasing exclusive access to the XR device if session is an immersive session.
|
||
// - Deallocating any graphics resources acquired by session for presentation to the XR device.
|
||
// - Putting the XR device in a state such that a different source may be able to initiate a session with the same device if session is an immersive session.
|
||
// FIXME: Implement this once we have any of this.
|
||
|
||
// 6. Queue a task that fires an XRSessionEvent named end on session.
|
||
HTML::queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr, GC::create_function(realm.heap(), [this, &realm]() {
|
||
XRSessionEventInit init;
|
||
init.session = this;
|
||
auto event = XRSessionEvent::create(realm, HTML::EventNames::end, init);
|
||
this->dispatch_event(event);
|
||
}));
|
||
}
|
||
|
||
// https://immersive-web.github.io/webxr/#dom-xrsession-onend
|
||
GC::Ptr<WebIDL::CallbackType> XRSession::onend()
|
||
{
|
||
return event_handler_attribute(HTML::EventNames::end);
|
||
}
|
||
|
||
// https://www.w3.org/TR/web-animations-1/#dom-animation-onfinish
|
||
void XRSession::set_onend(GC::Ptr<WebIDL::CallbackType> event_handler)
|
||
{
|
||
set_event_handler_attribute(HTML::EventNames::end, event_handler);
|
||
}
|
||
|
||
}
|