mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
Registering multiple Mach port names with the bootstrap server at runtime is not how macOS expects it to be used — the bootstrap server is meant for static services, and the only reason we used it originally was so child processes could reach back to the UI process. Remove bootstrap_transport_over_socket(), which had both sides register dynamic names with the bootstrap server and exchange them over a socket. Instead, WebDriver and BrowserProcess connections now go through MachPortServer instances directly. When a non-child process contacts a MachPortServer, the server creates a port pair on demand (detected via sysctl ppid check) and returns the local half immediately. This keeps bootstrap server usage limited to the one original case: child processes looking up their parent's MachPortServer. WebDriver Session now runs its own MachPortServer per session. --webdriver-content-path becomes --webdriver-mach-server-name on macOS. Spare WebContent launches are skipped when a WebDriver session is active to avoid bootstrap races.
109 lines
4.7 KiB
C++
109 lines
4.7 KiB
C++
/*
|
|
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Debug.h>
|
|
#include <LibCore/Platform/MachMessageTypes.h>
|
|
#include <LibThreading/Thread.h>
|
|
#include <LibWebView/MachPortServer.h>
|
|
|
|
namespace WebView {
|
|
|
|
MachPortServer::MachPortServer(ByteString server_port_name)
|
|
: m_thread(Threading::Thread::construct("MachPortServer"sv, [this]() -> intptr_t { thread_loop(); return 0; }))
|
|
, m_server_port_name(move(server_port_name))
|
|
{
|
|
if (auto err = allocate_server_port(); err.is_error())
|
|
dbgln("Failed to allocate server port: {}", err.error());
|
|
else
|
|
start();
|
|
}
|
|
|
|
MachPortServer::~MachPortServer()
|
|
{
|
|
stop();
|
|
}
|
|
|
|
void MachPortServer::start()
|
|
{
|
|
m_thread->start();
|
|
}
|
|
|
|
void MachPortServer::stop()
|
|
{
|
|
// FIXME: We should join instead (after storing should_stop = false) when we have a way to interrupt the thread's mach_msg call
|
|
m_thread->detach();
|
|
m_should_stop.store(true, MemoryOrder::memory_order_release);
|
|
}
|
|
|
|
bool MachPortServer::is_initialized()
|
|
{
|
|
return MACH_PORT_VALID(m_server_port_recv_right.port()) && MACH_PORT_VALID(m_server_port_send_right.port());
|
|
}
|
|
|
|
ErrorOr<void> MachPortServer::allocate_server_port()
|
|
{
|
|
m_server_port_recv_right = TRY(Core::MachPort::create_with_right(Core::MachPort::PortRight::Receive));
|
|
m_server_port_send_right = TRY(m_server_port_recv_right.insert_right(Core::MachPort::MessageRight::MakeSend));
|
|
TRY(m_server_port_recv_right.register_with_bootstrap_server(m_server_port_name));
|
|
|
|
dbgln_if(MACH_PORT_DEBUG, "Success! we created and attached mach port {:x} to bootstrap server with name {}", m_server_port_recv_right.port(), m_server_port_name);
|
|
return {};
|
|
}
|
|
|
|
void MachPortServer::thread_loop()
|
|
{
|
|
while (!m_should_stop.load(MemoryOrder::memory_order_acquire)) {
|
|
Core::Platform::ReceivedMachMessage message {};
|
|
|
|
// Get the pid of the child from the audit trailer so we can associate the port w/it
|
|
mach_msg_options_t const options = MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
|
|
|
|
// FIXME: How can we interrupt this call during application shutdown?
|
|
auto const ret = mach_msg(&message.header, options, 0, sizeof(message), m_server_port_recv_right.port(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
|
if (ret != KERN_SUCCESS) {
|
|
dbgln("mach_msg failed: {}", mach_error_string(ret));
|
|
break;
|
|
}
|
|
|
|
if (message.header.msgh_id == Core::Platform::BACKING_STORE_IOSURFACES_MESSAGE_ID) {
|
|
auto pid = static_cast<pid_t>(message.body.parent_iosurface.trailer.msgh_audit.val[5]);
|
|
auto const& backing_stores_message = message.body.parent_iosurface;
|
|
auto front_child_port = Core::MachPort::adopt_right(backing_stores_message.front_descriptor.name, Core::MachPort::PortRight::Send);
|
|
auto back_child_port = Core::MachPort::adopt_right(backing_stores_message.back_descriptor.name, Core::MachPort::PortRight::Send);
|
|
auto const& metadata = backing_stores_message.metadata;
|
|
if (on_receive_backing_stores)
|
|
on_receive_backing_stores({ .pid = pid,
|
|
.page_id = metadata.page_id,
|
|
.front_backing_store_id = metadata.front_backing_store_id,
|
|
.back_backing_store_id = metadata.back_backing_store_id,
|
|
.front_backing_store_port = move(front_child_port),
|
|
.back_backing_store_port = move(back_child_port) });
|
|
continue;
|
|
}
|
|
|
|
if (message.header.msgh_id == Core::Platform::SELF_TASK_PORT_MESSAGE_ID) {
|
|
auto const& task_port_message = message.body.parent;
|
|
VERIFY(MACH_MSGH_BITS_LOCAL(message.header.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND);
|
|
VERIFY(task_port_message.body.msgh_descriptor_count == 1);
|
|
VERIFY(task_port_message.port_descriptor.type == MACH_MSG_PORT_DESCRIPTOR);
|
|
auto pid = static_cast<pid_t>(task_port_message.trailer.msgh_audit.val[5]);
|
|
auto child_port = Core::MachPort::adopt_right(task_port_message.port_descriptor.name, Core::MachPort::PortRight::Send);
|
|
|
|
// Extract reply port from the message header (kernel swaps local/remote on receive)
|
|
auto reply_port = Core::MachPort::adopt_right(message.header.msgh_remote_port, Core::MachPort::PortRight::SendOnce);
|
|
|
|
dbgln_if(MACH_PORT_DEBUG, "Received child port {:x} from pid {} (reply port {:x})", child_port.port(), pid, reply_port.port());
|
|
if (on_receive_child_mach_port)
|
|
on_receive_child_mach_port({ pid, move(child_port), move(reply_port) });
|
|
continue;
|
|
}
|
|
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
}
|