ladybird/Libraries/LibDevTools/DevToolsServer.cpp
Timothy Flynn 24a5e4e7d5 LibDevTools: Move message data into a structure
This is to prepare for an upcoming change where we will need to track
replies to messages by ID. We will be able to add parameters to this
structure without having to edit every single actor subclass header
file.
2025-03-13 16:56:28 -04:00

120 lines
3.2 KiB
C++

/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <LibCore/EventLoop.h>
#include <LibCore/Socket.h>
#include <LibCore/TCPServer.h>
#include <LibDevTools/Actors/DeviceActor.h>
#include <LibDevTools/Actors/PreferenceActor.h>
#include <LibDevTools/Actors/ProcessActor.h>
#include <LibDevTools/Actors/TabActor.h>
#include <LibDevTools/Connection.h>
#include <LibDevTools/DevToolsServer.h>
namespace DevTools {
static u64 s_server_count = 0;
ErrorOr<NonnullOwnPtr<DevToolsServer>> DevToolsServer::create(DevToolsDelegate& delegate, u16 port)
{
auto address = IPv4Address::from_string("0.0.0.0"sv).release_value();
auto server = TRY(Core::TCPServer::try_create());
TRY(server->listen(address, port, Core::TCPServer::AllowAddressReuse::Yes));
return adopt_own(*new DevToolsServer(delegate, move(server)));
}
DevToolsServer::DevToolsServer(DevToolsDelegate& delegate, NonnullRefPtr<Core::TCPServer> server)
: m_server(move(server))
, m_delegate(delegate)
, m_server_id(s_server_count++)
{
m_server->on_ready_to_accept = [this]() {
if (auto result = on_new_client(); result.is_error())
warnln("Failed to accept DevTools client: {}", result.error());
};
}
DevToolsServer::~DevToolsServer() = default;
void DevToolsServer::refresh_tab_list()
{
if (!m_root_actor)
return;
m_actor_registry.remove_all_matching([](auto const&, auto const& actor) {
return is<TabActor>(*actor);
});
m_root_actor->send_tab_list_changed_message();
}
ErrorOr<void> DevToolsServer::on_new_client()
{
if (m_connection)
return Error::from_string_literal("Only one active DevTools connection is currently allowed");
auto client = TRY(m_server->accept());
auto buffered_socket = TRY(Core::BufferedTCPSocket::create(move(client)));
m_connection = Connection::create(move(buffered_socket));
m_connection->on_connection_closed = [this]() {
close_connection();
};
m_connection->on_message_received = [this](auto message) {
on_message_received(move(message));
};
m_root_actor = register_actor<RootActor>();
register_actor<DeviceActor>();
register_actor<PreferenceActor>();
register_actor<ProcessActor>(ProcessDescription { .is_parent = true });
return {};
}
void DevToolsServer::on_message_received(JsonObject message)
{
auto to = message.get_string("to"sv);
if (!to.has_value()) {
m_root_actor->send_missing_parameter_error("to"sv);
return;
}
auto actor = m_actor_registry.find(*to);
if (actor == m_actor_registry.end()) {
m_root_actor->send_unknown_actor_error(*to);
return;
}
auto type = message.get_string("type"sv);
if (!type.has_value()) {
actor->value->send_missing_parameter_error("type"sv);
return;
}
actor->value->message_received(*type, move(message));
}
void DevToolsServer::close_connection()
{
dbgln_if(DEVTOOLS_DEBUG, "Lost connection to the DevTools client");
Core::deferred_invoke([this]() {
m_connection = nullptr;
m_actor_registry.clear();
m_root_actor = nullptr;
});
}
}