ladybird/Libraries/LibWebView/HelperProcess.cpp
Aliaksandr Kalenik 9eee1f4488 LibWebView+WebContent: Enable async scrolling by default
Async scrolling is now correct enough to run by default, so keep it
enabled in normal browsing and test coverage to catch the remaining
issues.
2026-05-15 01:54:15 +02:00

277 lines
12 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/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); } && !IsSame<ClientType, Web::HTML::WebWorkerClient>) {
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();
}
template<typename... ClientArguments>
static ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process_impl(ClientArguments&&... client_arguments)
{
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 (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), forward<ClientArguments>(client_arguments)...);
}
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(WebView::ViewImplementation& view)
{
return launch_web_content_process_impl(view);
}
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_spare_web_content_process()
{
return launch_web_content_process_impl();
}
ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process()
{
Vector<ByteString> arguments;
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<Web::HTML::WebWorkerClient>> launch_web_worker_process(Web::Bindings::AgentType type)
{
auto const& web_content_options = WebView::Application::web_content_options();
Vector<ByteString> arguments;
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<Web::HTML::WebWorkerClient>("WebWorker"sv, move(arguments));
}
ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process()
{
auto const& request_server_options = Application::request_server_options();
Vector<ByteString> arguments;
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();
}
}