LibWeb+WebContent: Spawn Worker processes from the chrome

Instead of spawning these processes from the WebContent process, we now
create them in the Browser chrome.

Part 1/N of "all processes are owned by the chrome".
This commit is contained in:
Andrew Kaster 2024-01-06 13:13:59 -07:00 committed by Andrew Kaster
parent 6ea4c248ab
commit 02edd240ae
Notes: sideshowbarker 2024-07-17 02:42:21 +09:00
26 changed files with 152 additions and 99 deletions

View file

@ -4,93 +4,11 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/LexicalPath.h>
#include <LibCore/Socket.h>
#include <LibCore/System.h>
#include <LibWeb/Bindings/HostDefined.h>
#include <LibWeb/HTML/WorkerAgent.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Worker/WebWorkerClient.h>
// FIXME: Deduplicate this code with ladybird!!
#ifndef AK_OS_SERENITY
namespace {
ErrorOr<String> application_directory()
{
auto current_executable_path = TRY(Core::System::current_executable_path());
auto dirname = LexicalPath::dirname(current_executable_path);
return String::from_byte_string(dirname);
}
ErrorOr<Vector<String>> get_paths_for_helper_process(StringView process_name)
{
auto application_path = TRY(application_directory());
Vector<String> paths;
TRY(paths.try_append(TRY(String::formatted("{}/{}", application_path, process_name))));
TRY(paths.try_append(TRY(String::formatted("./{}", process_name))));
// NOTE: Add platform-specific paths here
return paths;
}
ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<String> candidate_web_content_paths)
{
int socket_fds[2] {};
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
int ui_fd = socket_fds[0];
int wc_fd = socket_fds[1];
int fd_passing_socket_fds[2] {};
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds));
int ui_fd_passing_fd = fd_passing_socket_fds[0];
int wc_fd_passing_fd = fd_passing_socket_fds[1];
if (auto child_pid = TRY(Core::System::fork()); child_pid == 0) {
TRY(Core::System::close(ui_fd_passing_fd));
TRY(Core::System::close(ui_fd));
auto takeover_string = TRY(String::formatted("WebWorker:{}", wc_fd));
TRY(Core::System::setenv("SOCKET_TAKEOVER"sv, takeover_string, true));
auto webcontent_fd_passing_socket_string = TRY(String::number(wc_fd_passing_fd));
ErrorOr<void> result;
for (auto const& path : candidate_web_content_paths) {
if (Core::System::access(path, X_OK).is_error())
continue;
auto arguments = Vector {
path.bytes_as_string_view(),
"--fd-passing-socket"sv,
webcontent_fd_passing_socket_string
};
result = Core::System::exec(arguments[0], arguments.span(), Core::System::SearchInPath::Yes);
if (!result.is_error())
break;
}
if (result.is_error())
warnln("Could not launch any of {}: {}", candidate_web_content_paths, result.error());
VERIFY_NOT_REACHED();
}
TRY(Core::System::close(wc_fd_passing_fd));
TRY(Core::System::close(wc_fd));
auto socket = TRY(Core::LocalSocket::adopt_fd(ui_fd));
TRY(socket->set_blocking(true));
auto new_client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Web::HTML::WebWorkerClient(move(socket))));
new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(ui_fd_passing_fd)));
return new_client;
}
}
#endif
namespace Web::HTML {
JS_DEFINE_ALLOCATOR(WorkerAgent);
@ -109,16 +27,20 @@ void WorkerAgent::initialize(JS::Realm& realm)
m_message_port = MessagePort::create(realm);
m_message_port->entangle_with(*m_outside_port);
#ifndef AK_OS_SERENITY
auto paths = MUST(get_paths_for_helper_process("WebWorker"sv));
m_worker_ipc = MUST(launch_web_worker_process(paths));
#else
m_worker_ipc = MUST(Web::HTML::WebWorkerClient::try_create());
#endif
TransferDataHolder data_holder;
MUST(m_message_port->transfer_steps(data_holder));
// NOTE: This blocking IPC call may launch another process.
// If spinning the event loop for this can cause other javascript to execute, we're in trouble.
auto worker_ipc_sockets = Bindings::host_defined_page(realm).client().request_worker_agent();
auto worker_socket = MUST(Core::LocalSocket::adopt_fd(worker_ipc_sockets.socket.take_fd()));
MUST(worker_socket->set_blocking(true));
auto fd_passing_socket = MUST(Core::LocalSocket::adopt_fd(worker_ipc_sockets.fd_passing_socket.take_fd()));
m_worker_ipc = make_ref_counted<WebWorkerClient>(move(worker_socket));
m_worker_ipc->set_fd_passing_socket(move(fd_passing_socket));
m_worker_ipc->async_start_dedicated_worker(m_url, m_worker_options.type, m_worker_options.credentials, m_worker_options.name, move(data_holder));
}