From aca4385daf6a967f6b60f9ff90e3c6c14f36a941 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sun, 9 Mar 2025 18:01:25 -0400 Subject: [PATCH] LibWebView: Create a spare WebContent process Since cross-site navigation is a pretty frequent task, creating a spare process is commonplace in other browsers to reduce the overhead of directing the target site to a new process. We store this process on the WebView application. If it is unavailable, we queue a task to create it later. --- Libraries/LibWebView/Application.cpp | 46 +++++++++++++++++++++ Libraries/LibWebView/Application.h | 5 +++ Libraries/LibWebView/Forward.h | 1 + Libraries/LibWebView/HelperProcess.cpp | 24 +++++++++-- Libraries/LibWebView/HelperProcess.h | 4 ++ Libraries/LibWebView/ViewImplementation.cpp | 7 +--- Libraries/LibWebView/WebContentClient.cpp | 16 +++++++ Libraries/LibWebView/WebContentClient.h | 4 ++ 8 files changed, 98 insertions(+), 9 deletions(-) diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 08349bfbe12..adf1ffdd628 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -191,6 +191,52 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_ } } +static ErrorOr> create_web_content_client(Optional view) +{ + auto request_server_socket = TRY(connect_new_request_server_client()); + auto image_decoder_socket = TRY(connect_new_image_decoder_client()); + + if (view.has_value()) + return WebView::launch_web_content_process(*view, move(image_decoder_socket), move(request_server_socket)); + return WebView::launch_spare_web_content_process(move(image_decoder_socket), move(request_server_socket)); +} + +ErrorOr> Application::launch_web_content_process(ViewImplementation& view) +{ + if (m_spare_web_content_process) { + auto web_content_client = m_spare_web_content_process.release_nonnull(); + launch_spare_web_content_process(); + + web_content_client->assign_view({}, view); + return web_content_client; + } + + launch_spare_web_content_process(); + return create_web_content_client(view); +} + +void Application::launch_spare_web_content_process() +{ + if (m_has_queued_task_to_launch_spare_web_content_process) + return; + m_has_queued_task_to_launch_spare_web_content_process = true; + + Core::deferred_invoke([this]() { + m_has_queued_task_to_launch_spare_web_content_process = false; + + auto web_content_client = create_web_content_client({}); + if (web_content_client.is_error()) { + dbgln("Unable to create spare web content client: {}", web_content_client.error()); + return; + } + + m_spare_web_content_process = web_content_client.release_value(); + + if (auto process = find_process(m_spare_web_content_process->pid()); process.has_value()) + process->set_title("(spare)"_string); + }); +} + ErrorOr Application::launch_services() { TRY(launch_request_server()); diff --git a/Libraries/LibWebView/Application.h b/Libraries/LibWebView/Application.h index dd8930e160b..1595f8a7d42 100644 --- a/Libraries/LibWebView/Application.h +++ b/Libraries/LibWebView/Application.h @@ -45,6 +45,7 @@ public: Core::EventLoop& event_loop() { return m_event_loop; } + ErrorOr> launch_web_content_process(ViewImplementation&); ErrorOr launch_services(); void add_child_process(Process&&); @@ -85,6 +86,7 @@ protected: private: void initialize(Main::Arguments const& arguments, URL::URL new_tab_page_url); + void launch_spare_web_content_process(); ErrorOr launch_request_server(); ErrorOr launch_image_decoder_server(); ErrorOr launch_devtools_server(); @@ -118,6 +120,9 @@ private: RefPtr m_request_server_client; RefPtr m_image_decoder_client; + RefPtr m_spare_web_content_process; + bool m_has_queued_task_to_launch_spare_web_content_process { false }; + RefPtr m_database; OwnPtr m_cookie_jar; diff --git a/Libraries/LibWebView/Forward.h b/Libraries/LibWebView/Forward.h index b1a15ab19fc..242796bcfc7 100644 --- a/Libraries/LibWebView/Forward.h +++ b/Libraries/LibWebView/Forward.h @@ -10,6 +10,7 @@ namespace WebView { +class Application; class CookieJar; class Database; class InspectorClient; diff --git a/Libraries/LibWebView/HelperProcess.cpp b/Libraries/LibWebView/HelperProcess.cpp index b131adb9d25..d5b2124bb49 100644 --- a/Libraries/LibWebView/HelperProcess.cpp +++ b/Libraries/LibWebView/HelperProcess.cpp @@ -79,10 +79,11 @@ static ErrorOr> launch_server_process( VERIFY_NOT_REACHED(); } -ErrorOr> launch_web_content_process( - WebView::ViewImplementation& view, +template +static ErrorOr> launch_web_content_process_impl( IPC::File image_decoder_socket, - Optional request_server_socket) + Optional request_server_socket, + ClientArguments&&... client_arguments) { auto const& chrome_options = WebView::Application::chrome_options(); auto const& web_content_options = WebView::Application::web_content_options(); @@ -138,7 +139,22 @@ ErrorOr> launch_web_content_process( arguments.append("--image-decoder-socket"sv); arguments.append(ByteString::number(image_decoder_socket.fd())); - return launch_server_process("WebContent"sv, move(arguments), view); + return launch_server_process("WebContent"sv, move(arguments), forward(client_arguments)...); +} + +ErrorOr> launch_web_content_process( + WebView::ViewImplementation& view, + IPC::File image_decoder_socket, + Optional request_server_socket) +{ + return launch_web_content_process_impl(move(image_decoder_socket), move(request_server_socket), view); +} + +ErrorOr> launch_spare_web_content_process( + IPC::File image_decoder_socket, + Optional request_server_socket) +{ + return launch_web_content_process_impl(move(image_decoder_socket), move(request_server_socket)); } ErrorOr> launch_image_decoder_process() diff --git a/Libraries/LibWebView/HelperProcess.h b/Libraries/LibWebView/HelperProcess.h index a0a66a0eb34..b36211f647d 100644 --- a/Libraries/LibWebView/HelperProcess.h +++ b/Libraries/LibWebView/HelperProcess.h @@ -21,6 +21,10 @@ ErrorOr> launch_web_content_process( IPC::File image_decoder_socket, Optional request_server_socket = {}); +ErrorOr> launch_spare_web_content_process( + IPC::File image_decoder_socket, + Optional request_server_socket = {}); + ErrorOr> launch_image_decoder_process(); ErrorOr> launch_web_worker_process(); ErrorOr> launch_request_server_process(); diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp index 3358847f5da..3ae345ebe32 100644 --- a/Libraries/LibWebView/ViewImplementation.cpp +++ b/Libraries/LibWebView/ViewImplementation.cpp @@ -568,11 +568,8 @@ void ViewImplementation::initialize_client(CreateNewClient create_new_client) if (create_new_client == CreateNewClient::Yes) { m_client_state = {}; - // FIXME: Fail to open the tab, rather than crashing the whole application if these fail. - auto request_server_socket = connect_new_request_server_client().release_value_but_fixme_should_propagate_errors(); - auto image_decoder_socket = connect_new_image_decoder_client().release_value_but_fixme_should_propagate_errors(); - - m_client_state.client = launch_web_content_process(*this, AK::move(image_decoder_socket), AK::move(request_server_socket)).release_value_but_fixme_should_propagate_errors(); + // FIXME: Fail to open the tab, rather than crashing the whole application if this fails. + m_client_state.client = Application::the().launch_web_content_process(*this).release_value_but_fixme_should_propagate_errors(); } else { m_client_state.client->register_view(m_client_state.page_index, *this); } diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index 29d664096d9..be244be42ee 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -31,6 +31,12 @@ WebContentClient::WebContentClient(IPC::Transport transport, ViewImplementation& m_views.set(0, &view); } +WebContentClient::WebContentClient(IPC::Transport transport) + : IPC::ConnectionToServer(*this, move(transport)) +{ + s_clients.set(this); +} + WebContentClient::~WebContentClient() { s_clients.remove(this); @@ -41,6 +47,12 @@ void WebContentClient::die() // Intentionally empty. Restart is handled at another level. } +void WebContentClient::assign_view(Badge, ViewImplementation& view) +{ + VERIFY(m_views.is_empty()); + m_views.set(0, &view); +} + void WebContentClient::register_view(u64 page_id, ViewImplementation& view) { VERIFY(page_id > 0); @@ -768,6 +780,10 @@ void WebContentClient::did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSh Optional WebContentClient::view_for_page_id(u64 page_id, SourceLocation location) { + // Don't bother logging anything for the spare WebContent process. It will only receive a load notification for about:blank. + if (m_views.is_empty()) + return {}; + if (auto view = m_views.get(page_id); view.has_value()) return *view.value(); diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h index 28dd0d71376..05d9ddecb6a 100644 --- a/Libraries/LibWebView/WebContentClient.h +++ b/Libraries/LibWebView/WebContentClient.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -38,14 +39,17 @@ public: static size_t client_count() { return s_clients.size(); } + explicit WebContentClient(IPC::Transport); WebContentClient(IPC::Transport, ViewImplementation&); ~WebContentClient(); + void assign_view(Badge, ViewImplementation&); void register_view(u64 page_id, ViewImplementation&); void unregister_view(u64 page_id); Function on_web_content_process_crash; + pid_t pid() const { return m_process_handle.pid; } void set_pid(pid_t pid) { m_process_handle.pid = pid; } private: