mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 11:36:10 +00:00
LibWeb+LibWebView+WebContent: Introduce a WebUI framework
When we build internal pages (e.g. about:settings), there is currently quite a lot of boilerplate needed to communicate between the browser and the page. This includes creating IDL for the page and the IPC for every message sent between the processes. These internal pages are also special in that they have privileged access to and control over the browser process. The framework introduced here serves to ease the setup of new internal pages and to reduce the access that WebContent processes have to the browser process. WebUI pages can send requests to the browser process via a `ladybird.sendMessage` API. Responses from the browser are passed through a WebUIMessage event. So, for example, an internal page may: ladybird.sendMessage("getDataFor", { id: 123 }); document.addEventListener("WebUIMessage", event => { if (event.name === "gotData") { console.assert(event.data.id === 123); } }); To handle these messages, we set up a new IPC connection between the browser and WebContent processes. This connection is torn down when the user navigates away from the internal page.
This commit is contained in:
parent
f72d87931f
commit
41aeb9e63a
Notes:
github-actions[bot]
2025-03-28 11:32:17 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/41aeb9e63a5 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4068
25 changed files with 416 additions and 3 deletions
|
@ -568,6 +568,7 @@ set(SOURCES
|
|||
Internals/InternalsBase.cpp
|
||||
Internals/Processes.cpp
|
||||
Internals/Settings.cpp
|
||||
Internals/WebUI.cpp
|
||||
IntersectionObserver/IntersectionObserver.cpp
|
||||
IntersectionObserver/IntersectionObserverEntry.cpp
|
||||
Layout/AudioBox.cpp
|
||||
|
|
|
@ -628,6 +628,7 @@ class RequestList;
|
|||
namespace Web::Internals {
|
||||
class Internals;
|
||||
class Processes;
|
||||
class WebUI;
|
||||
}
|
||||
|
||||
namespace Web::IntersectionObserver {
|
||||
|
|
34
Libraries/LibWeb/Internals/WebUI.cpp
Normal file
34
Libraries/LibWeb/Internals/WebUI.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/WebUIPrototype.h>
|
||||
#include <LibWeb/Internals/WebUI.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
||||
namespace Web::Internals {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(WebUI);
|
||||
|
||||
WebUI::WebUI(JS::Realm& realm)
|
||||
: InternalsBase(realm)
|
||||
{
|
||||
}
|
||||
|
||||
WebUI::~WebUI() = default;
|
||||
|
||||
void WebUI::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(WebUI);
|
||||
}
|
||||
|
||||
void WebUI::send_message(String const& name, JS::Value data)
|
||||
{
|
||||
page().client().received_message_from_web_ui(name, data);
|
||||
}
|
||||
|
||||
}
|
29
Libraries/LibWeb/Internals/WebUI.h
Normal file
29
Libraries/LibWeb/Internals/WebUI.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibWeb/Internals/InternalsBase.h>
|
||||
|
||||
namespace Web::Internals {
|
||||
|
||||
class WebUI final : public InternalsBase {
|
||||
WEB_PLATFORM_OBJECT(WebUI, InternalsBase);
|
||||
GC_DECLARE_ALLOCATOR(WebUI);
|
||||
|
||||
public:
|
||||
virtual ~WebUI() override;
|
||||
|
||||
void send_message(String const& name, JS::Value data);
|
||||
|
||||
private:
|
||||
explicit WebUI(JS::Realm&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
};
|
||||
|
||||
}
|
4
Libraries/LibWeb/Internals/WebUI.idl
Normal file
4
Libraries/LibWeb/Internals/WebUI.idl
Normal file
|
@ -0,0 +1,4 @@
|
|||
[Exposed=Nobody]
|
||||
interface WebUI {
|
||||
undefined sendMessage(DOMString name, optional any data = null);
|
||||
};
|
|
@ -400,6 +400,8 @@ public:
|
|||
|
||||
virtual void page_did_mutate_dom([[maybe_unused]] FlyString const& type, [[maybe_unused]] DOM::Node const& target, [[maybe_unused]] DOM::NodeList& added_nodes, [[maybe_unused]] DOM::NodeList& removed_nodes, [[maybe_unused]] GC::Ptr<DOM::Node> previous_sibling, [[maybe_unused]] GC::Ptr<DOM::Node> next_sibling, [[maybe_unused]] Optional<String> const& attribute_name) { }
|
||||
|
||||
virtual void received_message_from_web_ui([[maybe_unused]] String const& name, [[maybe_unused]] JS::Value data) { }
|
||||
|
||||
virtual void update_process_statistics() { }
|
||||
|
||||
virtual void request_current_settings() { }
|
||||
|
|
|
@ -268,6 +268,7 @@ libweb_js_bindings(Internals/InternalAnimationTimeline)
|
|||
libweb_js_bindings(Internals/Internals)
|
||||
libweb_js_bindings(Internals/Processes)
|
||||
libweb_js_bindings(Internals/Settings)
|
||||
libweb_js_bindings(Internals/WebUI)
|
||||
libweb_js_bindings(IntersectionObserver/IntersectionObserver)
|
||||
libweb_js_bindings(IntersectionObserver/IntersectionObserverEntry)
|
||||
libweb_js_bindings(MathML/MathMLElement)
|
||||
|
|
|
@ -24,6 +24,7 @@ set(SOURCES
|
|||
Utilities.cpp
|
||||
ViewImplementation.cpp
|
||||
WebContentClient.cpp
|
||||
WebUI.cpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
@ -69,6 +70,8 @@ set(GENERATED_SOURCES
|
|||
../../Services/WebContent/WebContentServerEndpoint.h
|
||||
../../Services/WebContent/WebDriverClientEndpoint.h
|
||||
../../Services/WebContent/WebDriverServerEndpoint.h
|
||||
../../Services/WebContent/WebUIClientEndpoint.h
|
||||
../../Services/WebContent/WebUIServerEndpoint.h
|
||||
NativeStyleSheetSource.cpp
|
||||
UIProcessClientEndpoint.h
|
||||
UIProcessServerEndpoint.h
|
||||
|
|
|
@ -18,6 +18,7 @@ class ProcessManager;
|
|||
class Settings;
|
||||
class ViewImplementation;
|
||||
class WebContentClient;
|
||||
class WebUI;
|
||||
|
||||
struct Attribute;
|
||||
struct ConsoleOutput;
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "WebContentClient.h"
|
||||
#include "Application.h"
|
||||
#include "ViewImplementation.h"
|
||||
#include <LibWeb/Cookie/ParsedCookie.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/CookieJar.h>
|
||||
#include <LibWebView/HelperProcess.h>
|
||||
#include <LibWebView/ViewImplementation.h>
|
||||
#include <LibWebView/WebContentClient.h>
|
||||
#include <LibWebView/WebUI.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
|
@ -68,6 +69,11 @@ void WebContentClient::unregister_view(u64 page_id)
|
|||
}
|
||||
}
|
||||
|
||||
void WebContentClient::web_ui_disconnected(Badge<WebUI>)
|
||||
{
|
||||
m_web_ui.clear();
|
||||
}
|
||||
|
||||
void WebContentClient::did_paint(u64 page_id, Gfx::IntRect rect, i32 bitmap_id)
|
||||
{
|
||||
if (auto view = view_for_page_id(page_id); view.has_value())
|
||||
|
@ -95,6 +101,13 @@ void WebContentClient::did_start_loading(u64 page_id, URL::URL url, bool is_redi
|
|||
|
||||
void WebContentClient::did_finish_loading(u64 page_id, URL::URL url)
|
||||
{
|
||||
if (url.scheme() == "about"sv && url.paths().size() == 1) {
|
||||
if (auto web_ui = WebUI::create(*this, url.paths().first()); web_ui.is_error())
|
||||
warnln("Could not create WebUI for {}: {}", url, web_ui.error());
|
||||
else
|
||||
m_web_ui = web_ui.release_value();
|
||||
}
|
||||
|
||||
if (auto view = view_for_page_id(page_id); view.has_value()) {
|
||||
view->set_url({}, url);
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
void register_view(u64 page_id, ViewImplementation&);
|
||||
void unregister_view(u64 page_id);
|
||||
|
||||
void web_ui_disconnected(Badge<WebUI>);
|
||||
|
||||
Function<void()> on_web_content_process_crash;
|
||||
|
||||
pid_t pid() const { return m_process_handle.pid; }
|
||||
|
@ -142,6 +144,8 @@ private:
|
|||
|
||||
ProcessHandle m_process_handle;
|
||||
|
||||
RefPtr<WebUI> m_web_ui;
|
||||
|
||||
static HashTable<WebContentClient*> s_clients;
|
||||
};
|
||||
|
||||
|
|
75
Libraries/LibWebView/WebUI.cpp
Normal file
75
Libraries/LibWebView/WebUI.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibWebView/WebContentClient.h>
|
||||
#include <LibWebView/WebUI.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
template<typename WebUIType>
|
||||
static ErrorOr<NonnullRefPtr<WebUIType>> create_web_ui(WebContentClient& client, String host)
|
||||
{
|
||||
Array<int, 2> socket_fds { 0, 0 };
|
||||
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds.data()));
|
||||
|
||||
auto client_socket = Core::LocalSocket::adopt_fd(socket_fds[0]);
|
||||
if (client_socket.is_error()) {
|
||||
close(socket_fds[0]);
|
||||
close(socket_fds[1]);
|
||||
|
||||
return client_socket.release_error();
|
||||
}
|
||||
|
||||
auto web_ui = WebUIType::create(client, IPC::Transport { client_socket.release_value() }, move(host));
|
||||
client.async_connect_to_web_ui(0, IPC::File::adopt_fd(socket_fds[1]));
|
||||
|
||||
return web_ui;
|
||||
}
|
||||
|
||||
ErrorOr<RefPtr<WebUI>> WebUI::create(WebContentClient&, String)
|
||||
{
|
||||
RefPtr<WebUI> web_ui;
|
||||
|
||||
if (web_ui)
|
||||
web_ui->register_interfaces();
|
||||
|
||||
return web_ui;
|
||||
}
|
||||
|
||||
WebUI::WebUI(WebContentClient& client, IPC::Transport transport, String host)
|
||||
: IPC::ConnectionToServer<WebUIClientEndpoint, WebUIServerEndpoint>(*this, move(transport))
|
||||
, m_client(client)
|
||||
, m_host(move(host))
|
||||
{
|
||||
}
|
||||
|
||||
WebUI::~WebUI() = default;
|
||||
|
||||
void WebUI::die()
|
||||
{
|
||||
m_client.web_ui_disconnected({});
|
||||
}
|
||||
|
||||
void WebUI::register_interface(StringView name, Interface interface)
|
||||
{
|
||||
auto result = m_interfaces.set(name, move(interface));
|
||||
VERIFY(result == HashSetResult::InsertedNewEntry);
|
||||
}
|
||||
|
||||
void WebUI::received_message(String name, JsonValue data)
|
||||
{
|
||||
auto interface = m_interfaces.get(name);
|
||||
if (!interface.has_value()) {
|
||||
warnln("Received message from WebUI for unrecognized interface: {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
interface.value()(move(data));
|
||||
}
|
||||
|
||||
}
|
63
Libraries/LibWebView/WebUI.h
Normal file
63
Libraries/LibWebView/WebUI.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibIPC/ConnectionToServer.h>
|
||||
#include <LibIPC/Transport.h>
|
||||
#include <LibWebView/Forward.h>
|
||||
#include <WebContent/WebUIClientEndpoint.h>
|
||||
#include <WebContent/WebUIServerEndpoint.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
class WebUI
|
||||
: public IPC::ConnectionToServer<WebUIClientEndpoint, WebUIServerEndpoint>
|
||||
, public WebUIClientEndpoint {
|
||||
public:
|
||||
static ErrorOr<RefPtr<WebUI>> create(WebContentClient&, String host);
|
||||
virtual ~WebUI();
|
||||
|
||||
String const& host() const { return m_host; }
|
||||
|
||||
protected:
|
||||
WebUI(WebContentClient&, IPC::Transport, String host);
|
||||
|
||||
using Interface = Function<void(JsonValue)>;
|
||||
|
||||
virtual void register_interfaces() { }
|
||||
void register_interface(StringView name, Interface);
|
||||
|
||||
private:
|
||||
virtual void die() override;
|
||||
virtual void received_message(String name, JsonValue data) override;
|
||||
|
||||
WebContentClient& m_client;
|
||||
String m_host;
|
||||
|
||||
HashMap<StringView, Interface> m_interfaces;
|
||||
};
|
||||
|
||||
#define WEB_UI(WebUIType) \
|
||||
public: \
|
||||
static NonnullRefPtr<WebUIType> create(WebContentClient& client, IPC::Transport transport, String host) \
|
||||
{ \
|
||||
return adopt_ref(*new WebUIType(client, move(transport), move(host))); \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
WebUIType(WebContentClient& client, IPC::Transport transport, String host) \
|
||||
: WebView::WebUI(client, move(transport), move(host)) \
|
||||
{ \
|
||||
}
|
||||
|
||||
}
|
|
@ -433,6 +433,8 @@ compile_ipc(${SERENITY_PROJECT_ROOT}/Services/WebContent/WebContentServer.ipc Se
|
|||
compile_ipc(${SERENITY_PROJECT_ROOT}/Services/WebContent/WebContentClient.ipc Services/WebContent/WebContentClientEndpoint.h)
|
||||
compile_ipc(${SERENITY_PROJECT_ROOT}/Services/WebContent/WebDriverClient.ipc Services/WebContent/WebDriverClientEndpoint.h)
|
||||
compile_ipc(${SERENITY_PROJECT_ROOT}/Services/WebContent/WebDriverServer.ipc Services/WebContent/WebDriverServerEndpoint.h)
|
||||
compile_ipc(${SERENITY_PROJECT_ROOT}/Services/WebContent/WebUIClient.ipc Services/WebContent/WebUIClientEndpoint.h)
|
||||
compile_ipc(${SERENITY_PROJECT_ROOT}/Services/WebContent/WebUIServer.ipc Services/WebContent/WebUIServerEndpoint.h)
|
||||
|
||||
foreach(lib IN LISTS lagom_standard_libraries)
|
||||
add_serenity_subdirectory("Libraries/Lib${lib}")
|
||||
|
|
|
@ -9,6 +9,7 @@ set(SOURCES
|
|||
PageHost.cpp
|
||||
WebContentConsoleClient.cpp
|
||||
WebDriverConnection.cpp
|
||||
WebUIConnection.cpp
|
||||
)
|
||||
|
||||
if (ANDROID)
|
||||
|
|
|
@ -124,6 +124,15 @@ void ConnectionFromClient::connect_to_webdriver(u64 page_id, ByteString webdrive
|
|||
}
|
||||
}
|
||||
|
||||
void ConnectionFromClient::connect_to_web_ui(u64 page_id, IPC::File web_ui_socket)
|
||||
{
|
||||
if (auto page = this->page(page_id); page.has_value()) {
|
||||
// FIXME: Propagate this error back to the browser.
|
||||
if (auto result = page->connect_to_web_ui(move(web_ui_socket)); result.is_error())
|
||||
dbgln("Unable to connect to the WebUI host: {}", result.error());
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionFromClient::connect_to_image_decoder(IPC::File image_decoder_socket)
|
||||
{
|
||||
if (on_image_decoder_connection)
|
||||
|
|
|
@ -61,6 +61,7 @@ private:
|
|||
virtual Messages::WebContentServer::GetWindowHandleResponse get_window_handle(u64 page_id) override;
|
||||
virtual void set_window_handle(u64 page_id, String handle) override;
|
||||
virtual void connect_to_webdriver(u64 page_id, ByteString webdriver_ipc_path) override;
|
||||
virtual void connect_to_web_ui(u64 page_id, IPC::File web_ui_socket) override;
|
||||
virtual void connect_to_image_decoder(IPC::File image_decoder_socket) override;
|
||||
virtual void update_system_theme(u64 page_id, Core::AnonymousBuffer) override;
|
||||
virtual void update_screen_rects(u64 page_id, Vector<Web::DevicePixelRect>, u32) override;
|
||||
|
|
|
@ -15,5 +15,6 @@ class PageHost;
|
|||
class PageClient;
|
||||
class WebContentConsoleClient;
|
||||
class WebDriverConnection;
|
||||
class WebUIConnection;
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <WebContent/PageHost.h>
|
||||
#include <WebContent/WebContentClientEndpoint.h>
|
||||
#include <WebContent/WebDriverConnection.h>
|
||||
#include <WebContent/WebUIConnection.h>
|
||||
|
||||
namespace WebContent {
|
||||
|
||||
|
@ -95,6 +96,8 @@ void PageClient::visit_edges(JS::Cell::Visitor& visitor)
|
|||
|
||||
if (m_webdriver)
|
||||
m_webdriver->visit_edges(visitor);
|
||||
if (m_web_ui)
|
||||
m_web_ui->visit_edges(visitor);
|
||||
}
|
||||
|
||||
ConnectionFromClient& PageClient::client() const
|
||||
|
@ -372,6 +375,8 @@ void PageClient::page_did_change_active_document_in_top_level_browsing_context(W
|
|||
{
|
||||
auto& realm = document.realm();
|
||||
|
||||
m_web_ui.clear();
|
||||
|
||||
if (auto console_client = document.console_client()) {
|
||||
auto& web_content_console_client = as<WebContentConsoleClient>(*console_client);
|
||||
m_top_level_document_console_client = web_content_console_client;
|
||||
|
@ -737,6 +742,24 @@ ErrorOr<void> PageClient::connect_to_webdriver(ByteString const& webdriver_ipc_p
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> PageClient::connect_to_web_ui(IPC::File web_ui_socket)
|
||||
{
|
||||
auto* active_document = page().top_level_browsing_context().active_document();
|
||||
if (!active_document || !active_document->window())
|
||||
return {};
|
||||
|
||||
VERIFY(!m_web_ui);
|
||||
m_web_ui = TRY(WebUIConnection::connect(move(web_ui_socket), *active_document));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void PageClient::received_message_from_web_ui(String const& name, JS::Value data)
|
||||
{
|
||||
if (m_web_ui)
|
||||
m_web_ui->received_message_from_web_ui(name, data);
|
||||
}
|
||||
|
||||
void PageClient::initialize_js_console(Web::DOM::Document& document)
|
||||
{
|
||||
if (document.is_temporary_document_for_fragment_parsing())
|
||||
|
@ -910,4 +933,5 @@ void PageClient::queue_screenshot_task(Optional<Web::UniqueNodeID> node_id)
|
|||
m_screenshot_tasks.enqueue({ node_id });
|
||||
page().top_level_traversable()->set_needs_repaint();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ public:
|
|||
virtual Web::Page const& page() const override { return *m_page; }
|
||||
|
||||
ErrorOr<void> connect_to_webdriver(ByteString const& webdriver_ipc_path);
|
||||
ErrorOr<void> connect_to_web_ui(IPC::File);
|
||||
|
||||
virtual void paint_next_frame() override;
|
||||
virtual void process_screenshot_requests() override;
|
||||
|
@ -173,6 +174,7 @@ private:
|
|||
virtual void page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) override;
|
||||
virtual IPC::File request_worker_agent() override;
|
||||
virtual void page_did_mutate_dom(FlyString const& type, Web::DOM::Node const& target, Web::DOM::NodeList& added_nodes, Web::DOM::NodeList& removed_nodes, GC::Ptr<Web::DOM::Node> previous_sibling, GC::Ptr<Web::DOM::Node> next_sibling, Optional<String> const& attribute_name) override;
|
||||
virtual void received_message_from_web_ui(String const& name, JS::Value data) override;
|
||||
virtual void update_process_statistics() override;
|
||||
virtual void request_current_settings() override;
|
||||
virtual void restore_default_settings() override;
|
||||
|
@ -211,6 +213,7 @@ private:
|
|||
Web::CSS::PreferredMotion m_preferred_motion { Web::CSS::PreferredMotion::NoPreference };
|
||||
|
||||
RefPtr<WebDriverConnection> m_webdriver;
|
||||
RefPtr<WebUIConnection> m_web_ui;
|
||||
|
||||
BackingStoreManager m_backing_store_manager;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ endpoint WebContentServer
|
|||
set_window_handle(u64 page_id, String handle) =|
|
||||
|
||||
connect_to_webdriver(u64 page_id, ByteString webdriver_ipc_path) =|
|
||||
connect_to_web_ui(u64 page_id, IPC::File socket_fd) =|
|
||||
connect_to_image_decoder(IPC::File socket_fd) =|
|
||||
|
||||
update_system_theme(u64 page_id, Core::AnonymousBuffer theme_buffer) =|
|
||||
|
|
3
Services/WebContent/WebUIClient.ipc
Normal file
3
Services/WebContent/WebUIClient.ipc
Normal file
|
@ -0,0 +1,3 @@
|
|||
endpoint WebUIClient {
|
||||
received_message(String name, JsonValue data) =|
|
||||
}
|
95
Services/WebContent/WebUIConnection.cpp
Normal file
95
Services/WebContent/WebUIConnection.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <LibWeb/DOM/CustomEvent.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Event.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Internals/WebUI.h>
|
||||
#include <LibWeb/WebDriver/JSON.h>
|
||||
#include <WebContent/WebUIConnection.h>
|
||||
|
||||
namespace WebContent {
|
||||
|
||||
static auto LADYBIRD_PROPERTY = JS::PropertyKey { "ladybird"_fly_string };
|
||||
static auto WEB_UI_LOADED_EVENT = "WebUILoaded"_fly_string;
|
||||
static auto WEB_UI_MESSAGE_EVENT = "WebUIMessage"_fly_string;
|
||||
|
||||
ErrorOr<NonnullRefPtr<WebUIConnection>> WebUIConnection::connect(IPC::File web_ui_socket, Web::DOM::Document& document)
|
||||
{
|
||||
auto socket = TRY(Core::LocalSocket::adopt_fd(web_ui_socket.take_fd()));
|
||||
TRY(socket->set_blocking(true));
|
||||
|
||||
return adopt_ref(*new WebUIConnection(IPC::Transport { move(socket) }, document));
|
||||
}
|
||||
|
||||
WebUIConnection::WebUIConnection(IPC::Transport transport, Web::DOM::Document& document)
|
||||
: IPC::ConnectionFromClient<WebUIClientEndpoint, WebUIServerEndpoint>(*this, move(transport), 1)
|
||||
, m_document(document)
|
||||
{
|
||||
auto& realm = m_document->realm();
|
||||
m_document->window()->define_direct_property(LADYBIRD_PROPERTY, realm.create<Web::Internals::WebUI>(realm), JS::default_attributes);
|
||||
|
||||
Web::HTML::queue_a_task(Web::HTML::Task::Source::Unspecified, nullptr, m_document, GC::create_function(realm.heap(), [&document = *m_document]() {
|
||||
document.dispatch_event(Web::DOM::Event::create(document.realm(), WEB_UI_LOADED_EVENT));
|
||||
}));
|
||||
}
|
||||
|
||||
WebUIConnection::~WebUIConnection()
|
||||
{
|
||||
if (!m_document->window())
|
||||
return;
|
||||
|
||||
(void)m_document->window()->internal_delete(LADYBIRD_PROPERTY);
|
||||
}
|
||||
|
||||
void WebUIConnection::visit_edges(JS::Cell::Visitor& visitor)
|
||||
{
|
||||
visitor.visit(m_document);
|
||||
}
|
||||
|
||||
void WebUIConnection::send_message(String name, JsonValue data)
|
||||
{
|
||||
if (!m_document->browsing_context())
|
||||
return;
|
||||
|
||||
JsonObject detail;
|
||||
detail.set("name"sv, move(name));
|
||||
detail.set("data"sv, move(data));
|
||||
|
||||
auto& realm = m_document->realm();
|
||||
Web::HTML::TemporaryExecutionContext context { realm };
|
||||
|
||||
auto serialized_detail = Web::WebDriver::json_deserialize(*m_document->browsing_context(), detail);
|
||||
if (serialized_detail.is_error()) {
|
||||
warnln("Unable to serialize JSON data from browser: {}", serialized_detail.error());
|
||||
return;
|
||||
}
|
||||
|
||||
Web::DOM::CustomEventInit event_init {};
|
||||
event_init.detail = serialized_detail.value();
|
||||
|
||||
m_document->dispatch_event(Web::DOM::CustomEvent::create(realm, WEB_UI_MESSAGE_EVENT, event_init));
|
||||
}
|
||||
|
||||
void WebUIConnection::received_message_from_web_ui(String const& name, JS::Value data)
|
||||
{
|
||||
if (!m_document->browsing_context())
|
||||
return;
|
||||
|
||||
auto deserialized_data = Web::WebDriver::json_clone(*m_document->browsing_context(), data);
|
||||
if (deserialized_data.is_error()) {
|
||||
warnln("Unable to deserialize JS data from WebUI: {}", deserialized_data.error());
|
||||
return;
|
||||
}
|
||||
|
||||
async_received_message(name, deserialized_data.value());
|
||||
}
|
||||
|
||||
}
|
39
Services/WebContent/WebUIConnection.h
Normal file
39
Services/WebContent/WebUIConnection.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibIPC/Transport.h>
|
||||
#include <LibJS/Heap/Cell.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <WebContent/WebUIClientEndpoint.h>
|
||||
#include <WebContent/WebUIServerEndpoint.h>
|
||||
|
||||
namespace WebContent {
|
||||
|
||||
class WebUIConnection final : public IPC::ConnectionFromClient<WebUIClientEndpoint, WebUIServerEndpoint> {
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<WebUIConnection>> connect(IPC::File, Web::DOM::Document&);
|
||||
virtual ~WebUIConnection() override;
|
||||
|
||||
void visit_edges(JS::Cell::Visitor&);
|
||||
|
||||
void received_message_from_web_ui(String const& name, JS::Value data);
|
||||
|
||||
private:
|
||||
WebUIConnection(IPC::Transport, Web::DOM::Document&);
|
||||
|
||||
virtual void die() override { }
|
||||
virtual void send_message(String name, JsonValue data) override;
|
||||
|
||||
GC::Ref<Web::DOM::Document> m_document;
|
||||
};
|
||||
|
||||
}
|
3
Services/WebContent/WebUIServer.ipc
Normal file
3
Services/WebContent/WebUIServer.ipc
Normal file
|
@ -0,0 +1,3 @@
|
|||
endpoint WebUIServer {
|
||||
send_message(String name, JsonValue data) =|
|
||||
}
|
Loading…
Add table
Reference in a new issue