diff --git a/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp index c351b95501..4c32160716 100644 --- a/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Dex♪ + * Copyright (c) 2021-2022, Dex♪ * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include @@ -28,29 +26,27 @@ namespace Web::WebSockets { +static RefPtr s_websocket_client_manager; + +void WebSocketClientManager::initialize(RefPtr websocket_client_manager) +{ + s_websocket_client_manager = websocket_client_manager; +} + WebSocketClientManager& WebSocketClientManager::the() { - static RefPtr s_the; - if (!s_the) - s_the = WebSocketClientManager::try_create().release_value_but_fixme_should_propagate_errors(); - return *s_the; + if (!s_websocket_client_manager) [[unlikely]] { + dbgln("Web::WebSockets::WebSocketClientManager was not initialized!"); + VERIFY_NOT_REACHED(); + } + return *s_websocket_client_manager; } -ErrorOr> WebSocketClientManager::try_create() -{ - auto websocket_client = TRY(Protocol::WebSocketClient::try_create()); - return adopt_nonnull_ref_or_enomem(new (nothrow) WebSocketClientManager(move(websocket_client))); -} +WebSocketClientSocket::WebSocketClientSocket() = default; -WebSocketClientManager::WebSocketClientManager(NonnullRefPtr websocket_client) - : m_websocket_client(move(websocket_client)) -{ -} +WebSocketClientSocket::~WebSocketClientSocket() = default; -RefPtr WebSocketClientManager::connect(const AK::URL& url, String const& origin) -{ - return m_websocket_client->connect(url, origin); -} +WebSocketClientManager::WebSocketClientManager() = default; // https://websockets.spec.whatwg.org/#dom-websocket-websocket DOM::ExceptionOr> WebSocket::create_with_global_object(Bindings::WindowObject& window, String const& url) @@ -107,18 +103,7 @@ WebSocket::ReadyState WebSocket::ready_state() const { if (!m_websocket) return WebSocket::ReadyState::Closed; - auto ready_state = const_cast(*m_websocket).ready_state(); - switch (ready_state) { - case Protocol::WebSocket::ReadyState::Connecting: - return WebSocket::ReadyState::Connecting; - case Protocol::WebSocket::ReadyState::Open: - return WebSocket::ReadyState::Open; - case Protocol::WebSocket::ReadyState::Closing: - return WebSocket::ReadyState::Closing; - case Protocol::WebSocket::ReadyState::Closed: - return WebSocket::ReadyState::Closed; - } - return WebSocket::ReadyState::Closed; + return const_cast(*m_websocket).ready_state(); } // https://websockets.spec.whatwg.org/#dom-websocket-extensions @@ -212,7 +197,7 @@ void WebSocket::on_close(u16 code, String reason, bool was_clean) // https://websockets.spec.whatwg.org/#feedback-from-the-protocol void WebSocket::on_message(ByteBuffer message, bool is_text) { - if (m_websocket->ready_state() != Protocol::WebSocket::ReadyState::Open) + if (m_websocket->ready_state() != WebSocket::ReadyState::Open) return; if (is_text) { auto text_message = String(ReadonlyBytes(message)); diff --git a/Userland/Libraries/LibWeb/WebSockets/WebSocket.h b/Userland/Libraries/LibWeb/WebSockets/WebSocket.h index 7d2b292592..2bc3b42838 100644 --- a/Userland/Libraries/LibWeb/WebSockets/WebSocket.h +++ b/Userland/Libraries/LibWeb/WebSockets/WebSocket.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Dex♪ + * Copyright (c) 2021-2022, Dex♪ * * SPDX-License-Identifier: BSD-2-Clause */ @@ -23,26 +23,10 @@ E(onopen, HTML::EventNames::open) \ E(onmessage, HTML::EventNames::message) -namespace Protocol { -class WebSocketClient; -class WebSocket; -} - namespace Web::WebSockets { -class WebSocketClientManager : public Core::Object { - C_OBJECT_ABSTRACT(WebSocketClientManager) -public: - static WebSocketClientManager& the(); - - RefPtr connect(const AK::URL&, String const& origin); - -private: - static ErrorOr> try_create(); - WebSocketClientManager(NonnullRefPtr); - - RefPtr m_websocket_client; -}; +class WebSocketClientSocket; +class WebSocketClientManager; class WebSocket final : public RefCounted @@ -106,7 +90,55 @@ private: AK::URL m_url; String m_binary_type { "blob" }; - RefPtr m_websocket; + RefPtr m_websocket; +}; + +class WebSocketClientSocket : public RefCounted { +public: + virtual ~WebSocketClientSocket(); + + struct CertificateAndKey { + String certificate; + String key; + }; + + struct Message { + ByteBuffer data; + bool is_text { false }; + }; + + enum class Error { + CouldNotEstablishConnection, + ConnectionUpgradeFailed, + ServerClosedSocket, + }; + + virtual Web::WebSockets::WebSocket::ReadyState ready_state() = 0; + + virtual void send(ByteBuffer binary_or_text_message, bool is_text) = 0; + virtual void send(StringView text_message) = 0; + virtual void close(u16 code = 1005, String reason = {}) = 0; + + Function on_open; + Function on_message; + Function on_error; + Function on_close; + Function on_certificate_requested; + +protected: + explicit WebSocketClientSocket(); +}; + +class WebSocketClientManager : public Core::Object { + C_OBJECT_ABSTRACT(WebSocketClientManager) +public: + static void initialize(RefPtr); + static WebSocketClientManager& the(); + + virtual RefPtr connect(AK::URL const&, String const& origin) = 0; + +protected: + explicit WebSocketClientManager(); }; } diff --git a/Userland/Libraries/LibWebView/CMakeLists.txt b/Userland/Libraries/LibWebView/CMakeLists.txt index d0da3aef1e..4fa461b816 100644 --- a/Userland/Libraries/LibWebView/CMakeLists.txt +++ b/Userland/Libraries/LibWebView/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES OutOfProcessWebView.cpp StylePropertiesModel.cpp WebContentClient.cpp + WebSocketClientAdapter.cpp ) set(GENERATED_SOURCES @@ -14,6 +15,6 @@ set(GENERATED_SOURCES ) serenity_lib(LibWebView webview) -target_link_libraries(LibWebView LibGfx LibGUI LibImageDecoderClient LibIPC LibWeb) +target_link_libraries(LibWebView LibGfx LibGUI LibImageDecoderClient LibIPC LibProtocol LibWeb) add_subdirectory(DumpLayoutTree) diff --git a/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp b/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp new file mode 100644 index 0000000000..7c1f0982e8 --- /dev/null +++ b/Userland/Libraries/LibWebView/WebSocketClientAdapter.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2022, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace WebView { + +RefPtr WebSocketClientSocketAdapter::create(NonnullRefPtr websocket) +{ + return adopt_ref(*new WebSocketClientSocketAdapter(move(websocket))); +} + +WebSocketClientSocketAdapter::WebSocketClientSocketAdapter(NonnullRefPtr websocket) + : m_websocket(move(websocket)) +{ + m_websocket->on_open = [weak_this = make_weak_ptr()] { + if (auto strong_this = weak_this.strong_ref()) + if (strong_this->on_open) + strong_this->on_open(); + }; + m_websocket->on_message = [weak_this = make_weak_ptr()](auto message) { + if (auto strong_this = weak_this.strong_ref()) { + if (strong_this->on_message) { + strong_this->on_message(Web::WebSockets::WebSocketClientSocket::Message { + .data = move(message.data), + .is_text = message.is_text, + }); + } + } + }; + m_websocket->on_error = [weak_this = make_weak_ptr()](auto error) { + if (auto strong_this = weak_this.strong_ref()) { + if (strong_this->on_error) { + switch (error) { + case Protocol::WebSocket::Error::CouldNotEstablishConnection: + strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::CouldNotEstablishConnection); + return; + case Protocol::WebSocket::Error::ConnectionUpgradeFailed: + strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::ConnectionUpgradeFailed); + return; + case Protocol::WebSocket::Error::ServerClosedSocket: + strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::ServerClosedSocket); + return; + } + VERIFY_NOT_REACHED(); + } + } + }; + m_websocket->on_close = [weak_this = make_weak_ptr()](u16 code, String reason, bool was_clean) { + if (auto strong_this = weak_this.strong_ref()) + if (strong_this->on_close) + strong_this->on_close(code, move(reason), was_clean); + }; + m_websocket->on_certificate_requested = [weak_this = make_weak_ptr()] { + if (auto strong_this = weak_this.strong_ref()) { + if (strong_this->on_certificate_requested) { + auto certificate_and_key = weak_this->on_certificate_requested(); + return Protocol::WebSocket::CertificateAndKey { + .certificate = move(certificate_and_key.certificate), + .key = move(certificate_and_key.key), + }; + } + } + return Protocol::WebSocket::CertificateAndKey {}; + }; +} + +WebSocketClientSocketAdapter::~WebSocketClientSocketAdapter() = default; + +Web::WebSockets::WebSocket::ReadyState WebSocketClientSocketAdapter::ready_state() +{ + switch (m_websocket->ready_state()) { + case Protocol::WebSocket::ReadyState::Connecting: + return Web::WebSockets::WebSocket::ReadyState::Connecting; + case Protocol::WebSocket::ReadyState::Open: + return Web::WebSockets::WebSocket::ReadyState::Open; + case Protocol::WebSocket::ReadyState::Closing: + return Web::WebSockets::WebSocket::ReadyState::Closing; + case Protocol::WebSocket::ReadyState::Closed: + return Web::WebSockets::WebSocket::ReadyState::Closed; + } + VERIFY_NOT_REACHED(); +} + +void WebSocketClientSocketAdapter::send(ByteBuffer binary_or_text_message, bool is_text) +{ + m_websocket->send(binary_or_text_message, is_text); +} + +void WebSocketClientSocketAdapter::send(StringView text_message) +{ + m_websocket->send(text_message); +} + +void WebSocketClientSocketAdapter::close(u16 code, String reason) +{ + m_websocket->close(code, reason); +} + +ErrorOr> WebSocketClientManagerAdapter::try_create() +{ + auto websocket_client = TRY(Protocol::WebSocketClient::try_create()); + return adopt_nonnull_ref_or_enomem(new (nothrow) WebSocketClientManagerAdapter(move(websocket_client))); +} + +WebSocketClientManagerAdapter::WebSocketClientManagerAdapter(NonnullRefPtr websocket_client) + : m_websocket_client(move(websocket_client)) +{ +} + +WebSocketClientManagerAdapter::~WebSocketClientManagerAdapter() = default; + +RefPtr WebSocketClientManagerAdapter::connect(const AK::URL& url, String const& origin) +{ + auto underlying_websocket = m_websocket_client->connect(url, origin); + if (!underlying_websocket) + return {}; + return WebSocketClientSocketAdapter::create(underlying_websocket.release_nonnull()); +} + +} diff --git a/Userland/Libraries/LibWebView/WebSocketClientAdapter.h b/Userland/Libraries/LibWebView/WebSocketClientAdapter.h new file mode 100644 index 0000000000..8f7856c5e5 --- /dev/null +++ b/Userland/Libraries/LibWebView/WebSocketClientAdapter.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022, Dex♪ + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Protocol { +class WebSocket; +class WebSocketClient; +}; + +namespace WebView { + +class WebSocketClientSocketAdapter + : public Web::WebSockets::WebSocketClientSocket + , public Weakable { +public: + static RefPtr create(NonnullRefPtr); + virtual ~WebSocketClientSocketAdapter() override; + + virtual Web::WebSockets::WebSocket::ReadyState ready_state() override; + + virtual void send(ByteBuffer binary_or_text_message, bool is_text) override; + virtual void send(StringView text_message) override; + virtual void close(u16 code = 1005, String reason = {}) override; + +private: + WebSocketClientSocketAdapter(NonnullRefPtr); + + NonnullRefPtr m_websocket; +}; + +class WebSocketClientManagerAdapter : public Web::WebSockets::WebSocketClientManager { +public: + static ErrorOr> try_create(); + + virtual ~WebSocketClientManagerAdapter() override; + + virtual RefPtr connect(const AK::URL&, String const& origin) override; + +private: + WebSocketClientManagerAdapter(NonnullRefPtr); + + NonnullRefPtr m_websocket_client; +}; + +} diff --git a/Userland/Services/WebContent/main.cpp b/Userland/Services/WebContent/main.cpp index 42c5292dd9..b7bfccf6e4 100644 --- a/Userland/Services/WebContent/main.cpp +++ b/Userland/Services/WebContent/main.cpp @@ -10,7 +10,9 @@ #include #include #include +#include #include +#include #include ErrorOr serenity_main(Main::Arguments) @@ -25,6 +27,7 @@ ErrorOr serenity_main(Main::Arguments) TRY(Core::System::unveil(nullptr, nullptr)); Web::ImageDecoding::Decoder::initialize(WebView::ImageDecoderClientAdapter::create()); + Web::WebSockets::WebSocketClientManager::initialize(TRY(WebView::WebSocketClientManagerAdapter::try_create())); auto client = TRY(IPC::take_over_accepted_client_from_system_server()); return event_loop.exec();