WebWorker control connections can transfer handles from the browser process to the worker process, including RequestServer and ImageDecoder transport handles sent during worker startup. On Windows, serializing those attachments needs the destination process id so DuplicateHandle and WSADuplicateSocketW can target the peer process. WebWorker was excluded from the generic helper-process InitTransport handshake, leaving the transport without a peer pid before any attachment-bearing message was sent. Add InitTransport to the WebWorker server endpoint, implement the server-side peer pid exchange, expose the message type through WebWorkerClient, and let the shared helper launcher perform the handshake for workers as it does for other Windows IPC clients.
303 lines
13 KiB
C++
303 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Enumerate.h>
|
|
#include <LibCore/Process.h>
|
|
#include <LibCore/System.h>
|
|
#include <LibWebView/Application.h>
|
|
#include <LibWebView/CompositorClient.h>
|
|
#include <LibWebView/HelperProcess.h>
|
|
#include <LibWebView/Utilities.h>
|
|
|
|
namespace WebView {
|
|
|
|
template<typename ClientType, typename... ClientArguments>
|
|
static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
|
|
StringView server_name,
|
|
Vector<ByteString> arguments,
|
|
ClientArguments&&... client_arguments)
|
|
{
|
|
auto process_type = WebView::process_type_from_name(server_name);
|
|
auto const& browser_options = WebView::Application::browser_options();
|
|
|
|
auto candidate_server_paths = TRY(get_paths_for_helper_process(server_name));
|
|
|
|
if (browser_options.profile_helper_process == process_type) {
|
|
arguments.prepend({
|
|
"--tool=callgrind"sv,
|
|
"--instr-atstart=no"sv,
|
|
""sv, // Placeholder for the process path.
|
|
});
|
|
}
|
|
|
|
if (browser_options.debug_helper_processes.contains_slow(process_type))
|
|
arguments.append("--wait-for-debugger"sv);
|
|
|
|
for (auto [i, path] : enumerate(candidate_server_paths)) {
|
|
Core::ProcessSpawnOptions options { .name = server_name, .arguments = arguments };
|
|
|
|
if (browser_options.profile_helper_process == process_type) {
|
|
options.executable = "valgrind"sv;
|
|
options.search_for_executable_in_path = true;
|
|
arguments[2] = path;
|
|
} else {
|
|
options.executable = path;
|
|
}
|
|
|
|
bool capture_output = WebView::Application::the().should_capture_web_content_output();
|
|
auto result = WebView::Process::spawn<ClientType>(process_type, move(options), capture_output, forward<ClientArguments>(client_arguments)...);
|
|
|
|
if (!result.is_error()) {
|
|
auto&& [process, client] = result.release_value();
|
|
|
|
if constexpr (requires { client->set_pid(pid_t {}); })
|
|
client->set_pid(process.pid());
|
|
|
|
if constexpr (requires { client->transport().set_peer_pid(0); }) {
|
|
auto response = client->template send_sync<typename ClientType::InitTransport>(Core::System::getpid());
|
|
client->transport().set_peer_pid(response->peer_pid());
|
|
}
|
|
|
|
WebView::Application::the().add_child_process(move(process));
|
|
|
|
if (browser_options.profile_helper_process == process_type) {
|
|
dbgln();
|
|
dbgln("\033[1;34mLaunched {} process under callgrind!\033[0m", server_name);
|
|
dbgln("\033[1;36mRun `\033[4mcallgrind_control -i on\033[24m` to start instrumentation and `\033[4mcallgrind_control -i off\033[24m` stop it again.\033[0m");
|
|
dbgln();
|
|
}
|
|
|
|
return move(client);
|
|
}
|
|
|
|
if (i == candidate_server_paths.size() - 1) {
|
|
warnln("Could not launch any of {}: {}", candidate_server_paths, result.error());
|
|
return result.release_error();
|
|
}
|
|
}
|
|
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(u64 initial_page_id)
|
|
{
|
|
auto const& browser_options = WebView::Application::browser_options();
|
|
auto const& web_content_options = WebView::Application::web_content_options();
|
|
|
|
Vector<ByteString> arguments;
|
|
|
|
if (browser_options.headless_mode.has_value())
|
|
arguments.append("--headless"sv);
|
|
|
|
if (web_content_options.config_path.has_value()) {
|
|
arguments.append("--config-path"sv);
|
|
arguments.append(web_content_options.config_path.value());
|
|
}
|
|
if (web_content_options.is_test_mode == WebView::IsTestMode::Yes)
|
|
arguments.append("--test-mode"sv);
|
|
if (web_content_options.log_all_js_exceptions == WebView::LogAllJSExceptions::Yes)
|
|
arguments.append("--log-all-js-exceptions"sv);
|
|
if (web_content_options.disable_site_isolation == WebView::DisableSiteIsolation::Yes)
|
|
arguments.append("--disable-site-isolation"sv);
|
|
if (web_content_options.enable_idl_tracing == WebView::EnableIDLTracing::Yes)
|
|
arguments.append("--enable-idl-tracing"sv);
|
|
if (web_content_options.enable_http_memory_cache == WebView::EnableMemoryHTTPCache::Yes)
|
|
arguments.append("--enable-http-memory-cache"sv);
|
|
if (web_content_options.expose_experimental_interfaces == WebView::ExposeExperimentalInterfaces::Yes)
|
|
arguments.append("--expose-experimental-interfaces"sv);
|
|
if (web_content_options.expose_internals_object == WebView::ExposeInternalsObject::Yes)
|
|
arguments.append("--expose-internals-object"sv);
|
|
if (web_content_options.force_cpu_painting == WebView::ForceCPUPainting::Yes)
|
|
arguments.append("--force-cpu-painting"sv);
|
|
if (web_content_options.force_fontconfig == WebView::ForceFontconfig::Yes)
|
|
arguments.append("--force-fontconfig"sv);
|
|
if (web_content_options.collect_garbage_on_every_allocation == WebView::CollectGarbageOnEveryAllocation::Yes)
|
|
arguments.append("--collect-garbage-on-every-allocation"sv);
|
|
if (web_content_options.paint_viewport_scrollbars == PaintViewportScrollbars::No)
|
|
arguments.append("--disable-scrollbar-painting"sv);
|
|
if (web_content_options.enable_async_scrolling == EnableAsyncScrolling::No)
|
|
arguments.append("--disable-async-scrolling"sv);
|
|
if (web_content_options.file_scheme_urls_have_tuple_origins == FileSchemeUrlsHaveTupleOrigins::Yes)
|
|
arguments.append("--tuple-file-origins"sv);
|
|
if (web_content_options.report_session_history_updates_in_test_mode == ReportSessionHistoryUpdatesInTestMode::Yes)
|
|
arguments.append("--report-session-history-updates-in-test-mode"sv);
|
|
if (browser_options.disable_sandbox == DisableSandbox::Yes)
|
|
arguments.append("--disable-sandbox"sv);
|
|
|
|
if (auto const maybe_echo_server_port = web_content_options.echo_server_port; maybe_echo_server_port.has_value()) {
|
|
arguments.append("--echo-server-port"sv);
|
|
arguments.append(ByteString::number(maybe_echo_server_port.value()));
|
|
}
|
|
|
|
if (web_content_options.default_time_zone.has_value()) {
|
|
arguments.append("--default-time-zone");
|
|
arguments.append(web_content_options.default_time_zone.value());
|
|
}
|
|
if (web_content_options.style_invalidation_counter_dump_interval.has_value()) {
|
|
arguments.append("--dump-style-invalidation-counters"sv);
|
|
arguments.append(ByteString::number(*web_content_options.style_invalidation_counter_dump_interval));
|
|
}
|
|
|
|
if (auto server = mach_server_name(); server.has_value()) {
|
|
arguments.append("--mach-server-name"sv);
|
|
arguments.append(server.value());
|
|
}
|
|
return launch_server_process<WebView::WebContentClient>("WebContent"sv, move(arguments), initial_page_id);
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process()
|
|
{
|
|
auto const& browser_options = WebView::Application::browser_options();
|
|
|
|
Vector<ByteString> arguments;
|
|
if (browser_options.disable_sandbox == DisableSandbox::Yes)
|
|
arguments.append("--disable-sandbox"sv);
|
|
if (auto server = mach_server_name(); server.has_value()) {
|
|
arguments.append("--mach-server-name"sv);
|
|
arguments.append(server.value());
|
|
}
|
|
|
|
return launch_server_process<ImageDecoderClient::Client>("ImageDecoder"sv, arguments);
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<WebView::CompositorClient>> launch_compositor_process()
|
|
{
|
|
auto const& browser_options = WebView::Application::browser_options();
|
|
auto const& web_content_options = WebView::Application::web_content_options();
|
|
|
|
Vector<ByteString> arguments;
|
|
if (browser_options.disable_sandbox == DisableSandbox::Yes)
|
|
arguments.append("--disable-sandbox"sv);
|
|
if (web_content_options.force_cpu_painting == WebView::ForceCPUPainting::Yes)
|
|
arguments.append("--force-cpu-painting"sv);
|
|
if (web_content_options.force_fontconfig == WebView::ForceFontconfig::Yes)
|
|
arguments.append("--force-fontconfig"sv);
|
|
if (web_content_options.enable_async_scrolling == EnableAsyncScrolling::No)
|
|
arguments.append("--disable-async-scrolling"sv);
|
|
if (auto server = mach_server_name(); server.has_value()) {
|
|
arguments.append("--mach-server-name"sv);
|
|
arguments.append(server.value());
|
|
}
|
|
|
|
return launch_server_process<WebView::CompositorClient>("Compositor"sv, move(arguments));
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<WebWorkerClient>> launch_web_worker_process(Web::Bindings::AgentType type, Web::HTML::WorkerAgentId agent_id)
|
|
{
|
|
auto const& browser_options = WebView::Application::browser_options();
|
|
auto const& web_content_options = WebView::Application::web_content_options();
|
|
|
|
Vector<ByteString> arguments;
|
|
|
|
if (browser_options.disable_sandbox == DisableSandbox::Yes)
|
|
arguments.append("--disable-sandbox"sv);
|
|
if (web_content_options.expose_experimental_interfaces == WebView::ExposeExperimentalInterfaces::Yes)
|
|
arguments.append("--expose-experimental-interfaces"sv);
|
|
if (web_content_options.enable_http_memory_cache == WebView::EnableMemoryHTTPCache::Yes)
|
|
arguments.append("--enable-http-memory-cache"sv);
|
|
if (web_content_options.file_scheme_urls_have_tuple_origins == FileSchemeUrlsHaveTupleOrigins::Yes)
|
|
arguments.append("--tuple-file-origins"sv);
|
|
|
|
arguments.append("--type"sv);
|
|
switch (type) {
|
|
case Web::Bindings::AgentType::DedicatedWorker:
|
|
arguments.append("dedicated"sv);
|
|
break;
|
|
case Web::Bindings::AgentType::SharedWorker:
|
|
arguments.append("shared"sv);
|
|
break;
|
|
case Web::Bindings::AgentType::ServiceWorker:
|
|
arguments.append("service"sv);
|
|
break;
|
|
default:
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
if (auto server = mach_server_name(); server.has_value()) {
|
|
arguments.append("--mach-server-name"sv);
|
|
arguments.append(server.value());
|
|
}
|
|
|
|
return launch_server_process<WebWorkerClient>("WebWorker"sv, move(arguments), agent_id);
|
|
}
|
|
|
|
ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process()
|
|
{
|
|
auto const& browser_options = Application::browser_options();
|
|
auto const& request_server_options = Application::request_server_options();
|
|
|
|
Vector<ByteString> arguments;
|
|
|
|
if (browser_options.disable_sandbox == DisableSandbox::Yes)
|
|
arguments.append("--disable-sandbox"sv);
|
|
for (auto const& certificate : request_server_options.certificates)
|
|
arguments.append(ByteString::formatted("--certificate={}", certificate));
|
|
|
|
arguments.append("--http-disk-cache-mode"sv);
|
|
|
|
switch (request_server_options.http_disk_cache_mode) {
|
|
case HTTPDiskCacheMode::Disabled:
|
|
arguments.append("disabled"sv);
|
|
break;
|
|
case HTTPDiskCacheMode::Enabled:
|
|
arguments.append("enabled"sv);
|
|
break;
|
|
case HTTPDiskCacheMode::Partitioned:
|
|
arguments.append("partitioned"sv);
|
|
break;
|
|
case HTTPDiskCacheMode::Testing:
|
|
arguments.append("testing"sv);
|
|
break;
|
|
}
|
|
|
|
if (auto server = mach_server_name(); server.has_value()) {
|
|
arguments.append("--mach-server-name"sv);
|
|
arguments.append(server.value());
|
|
}
|
|
|
|
if (request_server_options.resource_substitution_map_path.has_value())
|
|
arguments.append(ByteString::formatted("--resource-map={}", *request_server_options.resource_substitution_map_path));
|
|
|
|
auto client = TRY(launch_server_process<Requests::RequestClient>("RequestServer"sv, move(arguments)));
|
|
|
|
auto const& browsing_data_settings = Application::settings().browsing_data_settings();
|
|
client->async_set_disk_cache_settings(browsing_data_settings.disk_cache_settings);
|
|
|
|
Application::settings().dns_settings().visit(
|
|
[](SystemDNS) {},
|
|
[&](DNSOverTLS const& dns_over_tls) {
|
|
dbgln("Setting DNS server to {}:{} with TLS ({} local dnssec)", dns_over_tls.server_address, dns_over_tls.port, dns_over_tls.validate_dnssec_locally ? "with" : "without");
|
|
client->async_set_dns_server(dns_over_tls.server_address, dns_over_tls.port, true, dns_over_tls.validate_dnssec_locally);
|
|
},
|
|
[&](DNSOverUDP const& dns_over_udp) {
|
|
dbgln("Setting DNS server to {}:{} ({} local dnssec)", dns_over_udp.server_address, dns_over_udp.port, dns_over_udp.validate_dnssec_locally ? "with" : "without");
|
|
client->async_set_dns_server(dns_over_udp.server_address, dns_over_udp.port, false, dns_over_udp.validate_dnssec_locally);
|
|
});
|
|
|
|
return client;
|
|
}
|
|
|
|
ErrorOr<IPC::TransportHandle> connect_new_request_server_client()
|
|
{
|
|
auto response = Application::request_server_client().send_sync_but_allow_failure<Messages::RequestServer::ConnectNewClient>();
|
|
if (!response)
|
|
return Error::from_string_literal("Failed to connect to RequestServer");
|
|
return response->take_handle();
|
|
}
|
|
|
|
ErrorOr<IPC::TransportHandle> connect_new_image_decoder_client()
|
|
{
|
|
auto response = Application::image_decoder_client().send_sync_but_allow_failure<Messages::ImageDecoderServer::ConnectNewClients>(1);
|
|
if (!response)
|
|
return Error::from_string_literal("Failed to connect to ImageDecoder");
|
|
|
|
auto handles = response->take_handles();
|
|
if (handles.size() != 1)
|
|
return Error::from_string_literal("Failed to connect to ImageDecoder");
|
|
return handles.take_last();
|
|
}
|
|
|
|
}
|