Use the snapshot from the previous commit to let CompositorThread apply experimental viewport wheel deltas when async scrolling is enabled. The event handler first performs synchronous admission on the main thread, then enqueues a compositor scroll command instead of mutating live document scroll state directly. Rasterize accepted scrolls through the same compositor presentation path added earlier. The compositor stores the newest async viewport offset so the next main-thread display-list recording can adopt it before repainting, preventing older paints from snapping the visible position backward. Keep DOM wheel dispatch on the main thread. When the compositor already performed the default action, dispatch the wheel as non-cancelable and suppress a second default scroll. Non-viewport targets, nested scrollers, and pages with blocking wheel listeners stay synchronous.
277 lines
12 KiB
C++
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::Yes)
|
|
arguments.append("--enable-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();
|
|
}
|
|
|
|
}
|