mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 07:07:44 +00:00
Everywhere: Merge the WebSocket service into RequestServer
This keeps the APIs separate as they are wildly different, a future improvement could be to somehow unify the APIs (if possible). Closes #23080.
This commit is contained in:
parent
daf5484d6b
commit
6dfb2f9dc8
56 changed files with 231 additions and 845 deletions
|
@ -26,7 +26,6 @@ if (SERENITYOS)
|
|||
add_subdirectory(TelnetServer)
|
||||
add_subdirectory(WebContent)
|
||||
add_subdirectory(WebDriver)
|
||||
add_subdirectory(WebSocket)
|
||||
add_subdirectory(WebWorker)
|
||||
add_subdirectory(WindowServer)
|
||||
endif()
|
||||
|
|
|
@ -26,4 +26,4 @@ set(GENERATED_SOURCES
|
|||
)
|
||||
|
||||
serenity_bin(RequestServer)
|
||||
target_link_libraries(RequestServer PRIVATE LibCore LibCrypto LibIPC LibGemini LibHTTP LibMain LibTLS)
|
||||
target_link_libraries(RequestServer PRIVATE LibCore LibCrypto LibIPC LibGemini LibHTTP LibMain LibTLS LibWebSocket)
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <AK/Weakable.h>
|
||||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibWebSocket/ConnectionInfo.h>
|
||||
#include <LibWebSocket/Message.h>
|
||||
#include <RequestServer/ConnectionFromClient.h>
|
||||
#include <RequestServer/Protocol.h>
|
||||
#include <RequestServer/Request.h>
|
||||
|
@ -187,4 +189,80 @@ void ConnectionFromClient::ensure_connection(URL const& url, ::RequestServer::Ca
|
|||
dbgln("EnsureConnection: Invalid URL scheme: '{}'", url.scheme());
|
||||
}
|
||||
|
||||
static i32 s_next_websocket_id = 1;
|
||||
Messages::RequestServer::WebsocketConnectResponse ConnectionFromClient::websocket_connect(URL const& url, ByteString const& origin, Vector<ByteString> const& protocols, Vector<ByteString> const& extensions, HashMap<ByteString, ByteString> const& additional_request_headers)
|
||||
{
|
||||
if (!url.is_valid()) {
|
||||
dbgln("WebSocket::Connect: Invalid URL requested: '{}'", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
WebSocket::ConnectionInfo connection_info(url);
|
||||
connection_info.set_origin(origin);
|
||||
connection_info.set_protocols(protocols);
|
||||
connection_info.set_extensions(extensions);
|
||||
|
||||
Vector<WebSocket::ConnectionInfo::Header> headers;
|
||||
for (auto const& header : additional_request_headers) {
|
||||
headers.append({ header.key, header.value });
|
||||
}
|
||||
connection_info.set_headers(headers);
|
||||
|
||||
auto id = ++s_next_websocket_id;
|
||||
auto connection = WebSocket::WebSocket::create(move(connection_info));
|
||||
connection->on_open = [this, id]() {
|
||||
async_websocket_connected(id);
|
||||
};
|
||||
connection->on_message = [this, id](auto message) {
|
||||
async_websocket_received(id, message.is_text(), message.data());
|
||||
};
|
||||
connection->on_error = [this, id](auto message) {
|
||||
async_websocket_errored(id, (i32)message);
|
||||
};
|
||||
connection->on_close = [this, id](u16 code, ByteString reason, bool was_clean) {
|
||||
async_websocket_closed(id, code, move(reason), was_clean);
|
||||
};
|
||||
|
||||
connection->start();
|
||||
m_websockets.set(id, move(connection));
|
||||
return id;
|
||||
}
|
||||
|
||||
Messages::RequestServer::WebsocketReadyStateResponse ConnectionFromClient::websocket_ready_state(i32 connection_id)
|
||||
{
|
||||
if (auto connection = m_websockets.get(connection_id).value_or({}))
|
||||
return (u32)connection->ready_state();
|
||||
return (u32)WebSocket::ReadyState::Closed;
|
||||
}
|
||||
|
||||
Messages::RequestServer::WebsocketSubprotocolInUseResponse ConnectionFromClient::websocket_subprotocol_in_use(i32 connection_id)
|
||||
{
|
||||
if (auto connection = m_websockets.get(connection_id).value_or({}))
|
||||
return connection->subprotocol_in_use();
|
||||
return ByteString::empty();
|
||||
}
|
||||
|
||||
void ConnectionFromClient::websocket_send(i32 connection_id, bool is_text, ByteBuffer const& data)
|
||||
{
|
||||
if (auto connection = m_websockets.get(connection_id).value_or({}); connection && connection->ready_state() == WebSocket::ReadyState::Open)
|
||||
connection->send(WebSocket::Message { data, is_text });
|
||||
}
|
||||
|
||||
void ConnectionFromClient::websocket_close(i32 connection_id, u16 code, ByteString const& reason)
|
||||
{
|
||||
if (auto connection = m_websockets.get(connection_id).value_or({}); connection && connection->ready_state() == WebSocket::ReadyState::Open)
|
||||
connection->close(code, reason);
|
||||
}
|
||||
|
||||
Messages::RequestServer::WebsocketSetCertificateResponse ConnectionFromClient::websocket_set_certificate(i32 connection_id, ByteString const&, ByteString const&)
|
||||
{
|
||||
auto success = false;
|
||||
if (auto connection = m_websockets.get(connection_id).value_or({}); connection) {
|
||||
// NO OP here
|
||||
// connection->set_certificate(certificate, key);
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibWebSocket/WebSocket.h>
|
||||
#include <RequestServer/Forward.h>
|
||||
#include <RequestServer/RequestClientEndpoint.h>
|
||||
#include <RequestServer/RequestServerEndpoint.h>
|
||||
|
@ -37,7 +38,15 @@ private:
|
|||
virtual Messages::RequestServer::SetCertificateResponse set_certificate(i32, ByteString const&, ByteString const&) override;
|
||||
virtual void ensure_connection(URL const& url, ::RequestServer::CacheLevel const& cache_level) override;
|
||||
|
||||
virtual Messages::RequestServer::WebsocketConnectResponse websocket_connect(URL const&, ByteString const&, Vector<ByteString> const&, Vector<ByteString> const&, HashMap<ByteString, ByteString> const&) override;
|
||||
virtual Messages::RequestServer::WebsocketReadyStateResponse websocket_ready_state(i32) override;
|
||||
virtual Messages::RequestServer::WebsocketSubprotocolInUseResponse websocket_subprotocol_in_use(i32) override;
|
||||
virtual void websocket_send(i32, bool, ByteBuffer const&) override;
|
||||
virtual void websocket_close(i32, u16, ByteString const&) override;
|
||||
virtual Messages::RequestServer::WebsocketSetCertificateResponse websocket_set_certificate(i32, ByteString const&, ByteString const&) override;
|
||||
|
||||
HashMap<i32, OwnPtr<Request>> m_requests;
|
||||
HashMap<i32, RefPtr<WebSocket::WebSocket>> m_websockets;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,14 @@ endpoint RequestClient
|
|||
request_finished(i32 request_id, bool success, u64 total_size) =|
|
||||
headers_became_available(i32 request_id, HashMap<ByteString,ByteString,CaseInsensitiveStringTraits> response_headers, Optional<u32> status_code) =|
|
||||
|
||||
// Websocket API
|
||||
// FIXME: See if this can be merged with the regular APIs
|
||||
websocket_connected(i32 connection_id) =|
|
||||
websocket_received(i32 connection_id, bool is_text, ByteBuffer data) =|
|
||||
websocket_errored(i32 connection_id, i32 message) =|
|
||||
websocket_closed(i32 connection_id, u16 code, ByteString reason, bool clean) =|
|
||||
websocket_certificate_requested(i32 request_id) =|
|
||||
|
||||
// Certificate requests
|
||||
certificate_requested(i32 request_id) =|
|
||||
}
|
||||
|
|
|
@ -11,4 +11,12 @@ endpoint RequestServer
|
|||
set_certificate(i32 request_id, ByteString certificate, ByteString key) => (bool success)
|
||||
|
||||
ensure_connection(URL url, ::RequestServer::CacheLevel cache_level) =|
|
||||
|
||||
// Websocket Connection API
|
||||
websocket_connect(URL url, ByteString origin, Vector<ByteString> protocols, Vector<ByteString> extensions, HashMap<ByteString,ByteString> additional_request_headers) => (i32 connection_id)
|
||||
websocket_ready_state(i32 connection_id) => (u32 ready_state)
|
||||
websocket_subprotocol_in_use(i32 connection_id) => (ByteString subprotocol_in_use)
|
||||
websocket_send(i32 connection_id, bool is_text, ByteBuffer data) =|
|
||||
websocket_close(i32 connection_id, u16 code, ByteString reason) =|
|
||||
websocket_set_certificate(i32 request_id, ByteString certificate, ByteString key) => (bool success)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ include(accelerated_graphics)
|
|||
serenity_component(
|
||||
WebContent
|
||||
TARGETS WebContent
|
||||
DEPENDS ImageDecoder RequestServer WebSocket
|
||||
DEPENDS ImageDecoder RequestServer
|
||||
)
|
||||
|
||||
compile_ipc(WebContentServer.ipc WebContentServerEndpoint.h)
|
||||
|
|
|
@ -40,7 +40,6 @@ ErrorOr<int> serenity_main(Main::Arguments)
|
|||
TRY(Core::System::unveil("/tmp/session/%sid/portal/audio", "rw"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/request", "rw"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/image", "rw"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/websocket", "rw"));
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity);
|
||||
|
@ -51,7 +50,6 @@ ErrorOr<int> serenity_main(Main::Arguments)
|
|||
return Web::Platform::AudioCodecPluginAgnostic::create(move(loader));
|
||||
});
|
||||
|
||||
Web::WebSockets::WebSocketClientManager::initialize(TRY(WebView::WebSocketClientManagerAdapter::try_create()));
|
||||
Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create()));
|
||||
TRY(Web::Bindings::initialize_main_thread_vm());
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
serenity_component(
|
||||
WebSocket
|
||||
TARGETS WebSocketServer
|
||||
)
|
||||
|
||||
compile_ipc(WebSocketServer.ipc WebSocketServerEndpoint.h)
|
||||
compile_ipc(WebSocketClient.ipc WebSocketClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ConnectionFromClient.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set(GENERATED_SOURCES
|
||||
WebSocketClientEndpoint.h
|
||||
WebSocketServerEndpoint.h
|
||||
)
|
||||
|
||||
# Note: We use a target name of WebSocketServer here to deconflict with the
|
||||
# Lagom namespaced target name for LibWebSocket, Lagom::WebSocket.
|
||||
# The server binary name is still WebSocket.
|
||||
serenity_bin(WebSocketServer)
|
||||
set_target_properties(WebSocketServer PROPERTIES OUTPUT_NAME WebSocket)
|
||||
target_link_libraries(WebSocketServer PRIVATE LibCore LibIPC LibWebSocket LibMain LibTLS)
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWebSocket/ConnectionInfo.h>
|
||||
#include <LibWebSocket/Message.h>
|
||||
#include <WebSocket/ConnectionFromClient.h>
|
||||
#include <WebSocket/WebSocketClientEndpoint.h>
|
||||
|
||||
namespace WebSocket {
|
||||
|
||||
static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
|
||||
|
||||
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket)
|
||||
: IPC::ConnectionFromClient<WebSocketClientEndpoint, WebSocketServerEndpoint>(*this, move(socket), 1)
|
||||
{
|
||||
s_connections.set(1, *this);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
if (s_connections.is_empty())
|
||||
Core::EventLoop::current().quit(0);
|
||||
}
|
||||
|
||||
Messages::WebSocketServer::ConnectResponse ConnectionFromClient::connect(URL const& url, ByteString const& origin,
|
||||
Vector<ByteString> const& protocols, Vector<ByteString> const& extensions, HashMap<ByteString, ByteString> const& additional_request_headers)
|
||||
{
|
||||
if (!url.is_valid()) {
|
||||
dbgln("WebSocket::Connect: Invalid URL requested: '{}'", url);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ConnectionInfo connection_info(url);
|
||||
connection_info.set_origin(origin);
|
||||
connection_info.set_protocols(protocols);
|
||||
connection_info.set_extensions(extensions);
|
||||
|
||||
Vector<ConnectionInfo::Header> headers;
|
||||
for (auto const& header : additional_request_headers) {
|
||||
headers.append({ header.key, header.value });
|
||||
}
|
||||
connection_info.set_headers(headers);
|
||||
|
||||
VERIFY(m_connection_ids < NumericLimits<i32>::max());
|
||||
auto id = ++m_connection_ids;
|
||||
auto connection = WebSocket::create(move(connection_info));
|
||||
connection->on_open = [this, id]() {
|
||||
did_connect(id);
|
||||
};
|
||||
connection->on_message = [this, id](auto message) {
|
||||
did_receive_message(id, move(message));
|
||||
};
|
||||
connection->on_error = [this, id](auto message) {
|
||||
did_error(id, (i32)message);
|
||||
};
|
||||
connection->on_close = [this, id](u16 code, ByteString reason, bool was_clean) {
|
||||
did_close(id, code, move(reason), was_clean);
|
||||
};
|
||||
|
||||
connection->start();
|
||||
m_connections.set(id, move(connection));
|
||||
return id;
|
||||
}
|
||||
|
||||
Messages::WebSocketServer::ReadyStateResponse ConnectionFromClient::ready_state(i32 connection_id)
|
||||
{
|
||||
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
|
||||
if (connection) {
|
||||
return (u32)connection->ready_state();
|
||||
}
|
||||
return (u32)ReadyState::Closed;
|
||||
}
|
||||
|
||||
Messages::WebSocketServer::SubprotocolInUseResponse ConnectionFromClient::subprotocol_in_use(i32 connection_id)
|
||||
{
|
||||
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
|
||||
if (connection) {
|
||||
return connection->subprotocol_in_use();
|
||||
}
|
||||
return ByteString::empty();
|
||||
}
|
||||
|
||||
void ConnectionFromClient::send(i32 connection_id, bool is_text, ByteBuffer const& data)
|
||||
{
|
||||
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
|
||||
if (connection && connection->ready_state() == ReadyState::Open) {
|
||||
Message websocket_message(data, is_text);
|
||||
connection->send(websocket_message);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionFromClient::close(i32 connection_id, u16 code, ByteString const& reason)
|
||||
{
|
||||
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
|
||||
if (connection && connection->ready_state() == ReadyState::Open)
|
||||
connection->close(code, reason);
|
||||
}
|
||||
|
||||
Messages::WebSocketServer::SetCertificateResponse ConnectionFromClient::set_certificate(i32 connection_id,
|
||||
[[maybe_unused]] ByteString const& certificate, [[maybe_unused]] ByteString const& key)
|
||||
{
|
||||
RefPtr<WebSocket> connection = m_connections.get(connection_id).value_or({});
|
||||
bool success = false;
|
||||
if (connection) {
|
||||
// NO OP here
|
||||
// connection->set_certificate(certificate, key);
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void ConnectionFromClient::did_connect(i32 connection_id)
|
||||
{
|
||||
async_connected(connection_id);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::did_receive_message(i32 connection_id, Message message)
|
||||
{
|
||||
async_received(connection_id, message.is_text(), message.data());
|
||||
}
|
||||
|
||||
void ConnectionFromClient::did_error(i32 connection_id, i32 message)
|
||||
{
|
||||
async_errored(connection_id, message);
|
||||
}
|
||||
|
||||
void ConnectionFromClient::did_close(i32 connection_id, u16 code, ByteString reason, bool was_clean)
|
||||
{
|
||||
async_closed(connection_id, code, reason, was_clean);
|
||||
deferred_invoke([this, connection_id] {
|
||||
m_connections.remove(connection_id);
|
||||
});
|
||||
}
|
||||
|
||||
void ConnectionFromClient::did_request_certificates(i32 connection_id)
|
||||
{
|
||||
async_certificate_requested(connection_id);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibWebSocket/WebSocket.h>
|
||||
#include <WebSocket/WebSocketClientEndpoint.h>
|
||||
#include <WebSocket/WebSocketServerEndpoint.h>
|
||||
|
||||
namespace WebSocket {
|
||||
|
||||
class ConnectionFromClient final
|
||||
: public IPC::ConnectionFromClient<WebSocketClientEndpoint, WebSocketServerEndpoint> {
|
||||
C_OBJECT(ConnectionFromClient);
|
||||
|
||||
public:
|
||||
~ConnectionFromClient() override = default;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
private:
|
||||
explicit ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>);
|
||||
|
||||
virtual Messages::WebSocketServer::ConnectResponse connect(URL const&, ByteString const&, Vector<ByteString> const&, Vector<ByteString> const&, HashMap<ByteString, ByteString> const&) override;
|
||||
virtual Messages::WebSocketServer::ReadyStateResponse ready_state(i32) override;
|
||||
virtual Messages::WebSocketServer::SubprotocolInUseResponse subprotocol_in_use(i32) override;
|
||||
virtual void send(i32, bool, ByteBuffer const&) override;
|
||||
virtual void close(i32, u16, ByteString const&) override;
|
||||
virtual Messages::WebSocketServer::SetCertificateResponse set_certificate(i32, ByteString const&, ByteString const&) override;
|
||||
|
||||
void did_connect(i32);
|
||||
void did_receive_message(i32, Message);
|
||||
void did_error(i32, i32 message);
|
||||
void did_close(i32, u16 code, ByteString reason, bool was_clean);
|
||||
void did_request_certificates(i32);
|
||||
|
||||
i32 m_connection_ids { 0 };
|
||||
HashMap<i32, RefPtr<WebSocket>> m_connections;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <AK/URL.h>
|
||||
|
||||
endpoint WebSocketClient
|
||||
{
|
||||
// Connection API
|
||||
connected(i32 connection_id) =|
|
||||
received(i32 connection_id, bool is_text, ByteBuffer data) =|
|
||||
errored(i32 connection_id, i32 message) =|
|
||||
closed(i32 connection_id, u16 code, ByteString reason, bool clean) =|
|
||||
|
||||
// Certificate requests
|
||||
certificate_requested(i32 connection_id) =|
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <AK/URL.h>
|
||||
|
||||
endpoint WebSocketServer
|
||||
{
|
||||
// Connection API
|
||||
connect(URL url, ByteString origin, Vector<ByteString> protocols, Vector<ByteString> extensions, HashMap<ByteString,ByteString> additional_request_headers) => (i32 connection_id)
|
||||
ready_state(i32 connection_id) => (u32 ready_state)
|
||||
subprotocol_in_use(i32 connection_id) => (ByteString subprotocol_in_use)
|
||||
send(i32 connection_id, bool is_text, ByteBuffer data) =|
|
||||
close(i32 connection_id, u16 code, ByteString reason) =|
|
||||
|
||||
set_certificate(i32 connection_id, ByteString certificate, ByteString key) => (bool success)
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibIPC/SingleServer.h>
|
||||
#include <LibMain/Main.h>
|
||||
#include <LibTLS/Certificate.h>
|
||||
#include <WebSocket/ConnectionFromClient.h>
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments)
|
||||
{
|
||||
TRY(Core::System::pledge("stdio inet unix rpath sendfd recvfd"));
|
||||
|
||||
// Ensure the certificates are read out here.
|
||||
// FIXME: Allow specifying extra certificates on the command line, or in other configuration.
|
||||
[[maybe_unused]] auto& certs = DefaultRootCACertificates::the();
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
// FIXME: Establish a connection to LookupServer and then drop "unix"?
|
||||
TRY(Core::System::unveil("/tmp/portal/lookup", "rw"));
|
||||
TRY(Core::System::unveil("/etc/timezone", "r"));
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
auto client = TRY(IPC::take_over_accepted_client_from_system_server<WebSocket::ConnectionFromClient>());
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
|
@ -30,13 +30,11 @@ ErrorOr<int> serenity_main(Main::Arguments)
|
|||
TRY(Core::System::unveil("/etc/timezone", "r"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/request", "rw"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/image", "rw"));
|
||||
TRY(Core::System::unveil("/tmp/session/%sid/portal/websocket", "rw"));
|
||||
TRY(Core::System::unveil(nullptr, nullptr));
|
||||
|
||||
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity);
|
||||
Web::Platform::FontPlugin::install(*new Web::Platform::FontPluginSerenity);
|
||||
|
||||
Web::WebSockets::WebSocketClientManager::initialize(TRY(WebView::WebSocketClientManagerAdapter::try_create()));
|
||||
Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create()));
|
||||
TRY(Web::Bindings::initialize_main_thread_vm());
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue