ladybird/Libraries/LibWebView/BrowserProcess.cpp
Andreas Kling 164ed80244 Meta: Enable exit-time destructor warnings for libraries
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.

Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
2026-06-04 19:20:49 +02:00

200 lines
6.4 KiB
C++

/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteString.h>
#include <AK/NeverDestroyed.h>
#include <LibCore/Process.h>
#include <LibCore/Socket.h>
#include <LibCore/System.h>
#include <LibIPC/ConnectionToServer.h>
#include <LibIPC/Transport.h>
#if defined(AK_OS_MACOS)
# include <LibIPC/TransportBootstrapMach.h>
#endif
#include <LibWebView/Application.h>
#include <LibWebView/BrowserProcess.h>
#include <LibWebView/URL.h>
#include <LibWebView/Utilities.h>
namespace WebView {
static HashMap<int, RefPtr<UIProcessConnectionFromClient>>& connections()
{
static NeverDestroyed<HashMap<int, RefPtr<UIProcessConnectionFromClient>>> connections;
return *connections;
}
class UIProcessClient final
: public IPC::ConnectionToServer<UIProcessClientEndpoint, UIProcessServerEndpoint> {
C_OBJECT(UIProcessClient);
private:
explicit UIProcessClient(NonnullOwnPtr<IPC::Transport>);
};
ErrorOr<BrowserProcess::ProcessDisposition> BrowserProcess::connect(Vector<ByteString> const& raw_urls, NewWindow new_window)
{
static constexpr auto process_name = "Ladybird"sv;
auto [socket_path, pid_path] = TRY(Process::paths_for_process(process_name));
if (auto pid = TRY(Process::get_process_pid(process_name, pid_path)); pid.has_value()) {
#if defined(AK_OS_MACOS)
TRY(connect_as_client(*pid, raw_urls, new_window));
#else
TRY(connect_as_client(socket_path, raw_urls, new_window));
#endif
return ProcessDisposition::ExitProcess;
}
#if defined(AK_OS_MACOS)
TRY(connect_as_server());
#else
TRY(connect_as_server(socket_path));
#endif
m_pid_path = pid_path;
m_pid_file = TRY(Core::File::open(pid_path, Core::File::OpenMode::Write));
TRY(m_pid_file->write_until_depleted(ByteString::number(Core::System::getpid())));
return ProcessDisposition::ContinueMainProcess;
}
#if defined(AK_OS_MACOS)
ErrorOr<void> BrowserProcess::connect_as_client(pid_t pid, Vector<ByteString> const& raw_urls, NewWindow new_window)
{
auto transport_ports = TRY(IPC::bootstrap_transport_from_mach_server(mach_server_name_for_process("Ladybird"sv, pid)));
auto client = UIProcessClient::construct(make<IPC::Transport>(move(transport_ports.receive_right), move(transport_ports.send_right)));
switch (new_window) {
case NewWindow::Yes:
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewWindow>(raw_urls))
dbgln("Failed to send CreateNewWindow message to UIProcess");
return {};
case NewWindow::No:
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewTab>(raw_urls))
dbgln("Failed to send CreateNewTab message to UIProcess");
return {};
}
VERIFY_NOT_REACHED();
}
#else
ErrorOr<void> BrowserProcess::connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, NewWindow new_window)
{
auto socket = TRY(Core::LocalSocket::connect(socket_path));
auto transport = TRY(IPC::Transport::from_socket(move(socket)));
auto client = UIProcessClient::construct(move(transport));
switch (new_window) {
case NewWindow::Yes:
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewWindow>(raw_urls))
dbgln("Failed to send CreateNewWindow message to UIProcess");
return {};
case NewWindow::No:
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewTab>(raw_urls))
dbgln("Failed to send CreateNewTab message to UIProcess");
return {};
}
VERIFY_NOT_REACHED();
}
#endif
#if defined(AK_OS_MACOS)
ErrorOr<void> BrowserProcess::connect_as_server()
{
Application::the().set_browser_process_transport_handler([this](auto transport) {
accept_transport(move(transport));
});
return {};
}
#else
ErrorOr<void> BrowserProcess::connect_as_server(ByteString const& socket_path)
{
auto socket_fd = TRY(Process::create_ipc_socket(socket_path));
m_socket_path = socket_path;
m_local_server = Core::LocalServer::construct();
TRY(m_local_server->take_over_fd(socket_fd));
m_local_server->on_accept = [this](auto client_socket) {
auto transport = IPC::Transport::from_socket(move(client_socket));
if (transport.is_error()) {
dbgln("Failed to create IPC transport for UIProcess client: {}", transport.error());
return;
}
accept_transport(transport.release_value());
};
return {};
}
#endif
void BrowserProcess::accept_transport(NonnullOwnPtr<IPC::Transport> transport)
{
auto client = UIProcessConnectionFromClient::construct(move(transport), ++m_next_client_id);
client->on_new_tab = [this](auto raw_urls) {
if (this->on_new_tab)
this->on_new_tab(raw_urls);
};
client->on_new_window = [this](auto raw_urls) {
if (this->on_new_window)
this->on_new_window(raw_urls);
};
}
BrowserProcess::~BrowserProcess()
{
#if defined(AK_OS_MACOS)
Application::the().set_browser_process_transport_handler({});
#endif
if (m_pid_file) {
MUST(m_pid_file->truncate(0));
#if defined(AK_OS_WINDOWS)
// NOTE: On Windows, System::open() duplicates the underlying OS file handle,
// so we need to explicitly close said handle, otherwise the unlink() call fails due
// to permission errors and we crash on shutdown.
m_pid_file->close();
#endif
MUST(Core::System::unlink(m_pid_path));
}
if (!m_socket_path.is_empty())
MUST(Core::System::unlink(m_socket_path));
}
UIProcessClient::UIProcessClient(NonnullOwnPtr<IPC::Transport> transport)
: IPC::ConnectionToServer<UIProcessClientEndpoint, UIProcessServerEndpoint>(*this, move(transport))
{
}
UIProcessConnectionFromClient::UIProcessConnectionFromClient(NonnullOwnPtr<IPC::Transport> transport, int client_id)
: IPC::ConnectionFromClient<UIProcessClientEndpoint, UIProcessServerEndpoint>(*this, move(transport), client_id)
{
connections().set(client_id, *this);
}
void UIProcessConnectionFromClient::die()
{
connections().remove(client_id());
}
void UIProcessConnectionFromClient::create_new_tab(Vector<ByteString> urls)
{
if (on_new_tab)
on_new_tab(sanitize_urls(urls));
}
void UIProcessConnectionFromClient::create_new_window(Vector<ByteString> urls)
{
if (on_new_window)
on_new_window(sanitize_urls(urls));
}
}