From 4b5541e1b7b1ea84239dd0c999706d592a768488 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Wed, 26 Jun 2024 13:44:42 -0600 Subject: [PATCH] Everywhere: Transition ImageDecoder to be single-instance, owned by UI This is the same behavior as RequestServer, with the added benefit that we know how to gracefully reconnect ImageDecoder to all WebContent processes on restart. --- Ladybird/AppKit/Application/Application.h | 1 + Ladybird/AppKit/Application/Application.mm | 5 ++ .../AppKit/Application/ApplicationBridge.cpp | 38 +++++++++++++- .../AppKit/Application/ApplicationBridge.h | 1 + Ladybird/AppKit/main.mm | 2 + Ladybird/HelperProcess.cpp | 20 +++++++ Ladybird/HelperProcess.h | 2 + Ladybird/ImageCodecPlugin.cpp | 38 ++++++++------ Ladybird/ImageCodecPlugin.h | 4 +- Ladybird/Qt/Application.cpp | 34 ++++++++++++ Ladybird/Qt/Application.h | 6 +++ Ladybird/Qt/WebContentView.cpp | 5 +- Ladybird/Qt/main.cpp | 4 +- Ladybird/WebContent/main.cpp | 40 ++++++++++++-- .../ImageDecoder/ConnectionFromClient.cpp | 52 +++++++++++++++++-- .../ImageDecoder/ConnectionFromClient.h | 3 ++ .../ImageDecoder/ImageDecoderServer.ipc | 2 + .../WebContent/ConnectionFromClient.cpp | 6 +++ .../WebContent/ConnectionFromClient.h | 3 ++ .../Services/WebContent/WebContentServer.ipc | 1 + Userland/Utilities/headless-browser.cpp | 14 +++-- 21 files changed, 250 insertions(+), 31 deletions(-) diff --git a/Ladybird/AppKit/Application/Application.h b/Ladybird/AppKit/Application/Application.h index 111eeb6997a..edd2112184d 100644 --- a/Ladybird/AppKit/Application/Application.h +++ b/Ladybird/AppKit/Application/Application.h @@ -22,6 +22,7 @@ class WebViewBridge; - (instancetype)init; - (ErrorOr)launchRequestServer:(Vector const&)certificates; +- (ErrorOr)launchImageDecoder; - (ErrorOr>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge; - (ErrorOr)launchWebWorker; diff --git a/Ladybird/AppKit/Application/Application.mm b/Ladybird/AppKit/Application/Application.mm index 472ad5f8f09..6a0d378daa3 100644 --- a/Ladybird/AppKit/Application/Application.mm +++ b/Ladybird/AppKit/Application/Application.mm @@ -41,6 +41,11 @@ return m_application_bridge->launch_request_server(certificates); } +- (ErrorOr)launchImageDecoder +{ + return m_application_bridge->launch_image_decoder(); +} + - (ErrorOr>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge { return m_application_bridge->launch_web_content(web_view_bridge); diff --git a/Ladybird/AppKit/Application/ApplicationBridge.cpp b/Ladybird/AppKit/Application/ApplicationBridge.cpp index 1b248692fd9..cadc17d7e80 100644 --- a/Ladybird/AppKit/Application/ApplicationBridge.cpp +++ b/Ladybird/AppKit/Application/ApplicationBridge.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,7 @@ namespace Ladybird { // is limited to .cpp files (i.e. not .h files that an Objective-C file can include). struct ApplicationBridgeImpl { RefPtr request_server_client; + RefPtr image_decoder_client; }; ApplicationBridge::ApplicationBridge() @@ -37,13 +39,47 @@ ErrorOr ApplicationBridge::launch_request_server(Vector const& return {}; } +static ErrorOr> launch_new_image_decoder() +{ + auto image_decoder_paths = TRY(get_paths_for_helper_process("ImageDecoder"sv)); + return launch_image_decoder_process(image_decoder_paths); +} + +ErrorOr ApplicationBridge::launch_image_decoder() +{ + m_impl->image_decoder_client = TRY(launch_new_image_decoder()); + + m_impl->image_decoder_client->on_death = [this] { + m_impl->image_decoder_client = nullptr; + if (auto err = this->launch_image_decoder(); err.is_error()) { + dbgln("Failed to restart image decoder: {}", err.error()); + VERIFY_NOT_REACHED(); + } + + auto num_clients = WebView::WebContentClient::client_count(); + auto new_sockets = m_impl->image_decoder_client->send_sync_but_allow_failure(num_clients); + if (!new_sockets || new_sockets->sockets().size() == 0) { + dbgln("Failed to connect {} new clients to ImageDecoder", num_clients); + VERIFY_NOT_REACHED(); + } + + WebView::WebContentClient::for_each_client([sockets = new_sockets->take_sockets()](WebView::WebContentClient& client) mutable { + client.async_connect_to_image_decoder(sockets.take_last()); + return IterationDecision::Continue; + }); + }; + + return {}; +} + ErrorOr> ApplicationBridge::launch_web_content(WebViewBridge& web_view_bridge) { // FIXME: Fail to open the tab, rather than crashing the whole application if this fails auto request_server_socket = TRY(connect_new_request_server_client(*m_impl->request_server_client)); + auto image_decoder_socket = TRY(connect_new_image_decoder_client(*m_impl->image_decoder_client)); auto web_content_paths = TRY(get_paths_for_helper_process("WebContent"sv)); - auto web_content = TRY(launch_web_content_process(web_view_bridge, web_content_paths, web_view_bridge.web_content_options(), move(request_server_socket))); + auto web_content = TRY(launch_web_content_process(web_view_bridge, web_content_paths, web_view_bridge.web_content_options(), move(image_decoder_socket), move(request_server_socket))); return web_content; } diff --git a/Ladybird/AppKit/Application/ApplicationBridge.h b/Ladybird/AppKit/Application/ApplicationBridge.h index 7978343272c..ae83e4e8100 100644 --- a/Ladybird/AppKit/Application/ApplicationBridge.h +++ b/Ladybird/AppKit/Application/ApplicationBridge.h @@ -22,6 +22,7 @@ public: ~ApplicationBridge(); ErrorOr launch_request_server(Vector const& certificates); + ErrorOr launch_image_decoder(); ErrorOr> launch_web_content(WebViewBridge&); ErrorOr launch_web_worker(); diff --git a/Ladybird/AppKit/main.mm b/Ladybird/AppKit/main.mm index e98d3028c5b..9bf170dcc8d 100644 --- a/Ladybird/AppKit/main.mm +++ b/Ladybird/AppKit/main.mm @@ -136,6 +136,8 @@ ErrorOr serenity_main(Main::Arguments arguments) // FIXME: Create an abstraction to re-spawn the RequestServer and re-hook up its client hooks to each tab on crash TRY([application launchRequestServer:certificates]); + TRY([application launchImageDecoder]); + StringBuilder command_line_builder; command_line_builder.join(' ', arguments.strings); Ladybird::WebContentOptions web_content_options { diff --git a/Ladybird/HelperProcess.cpp b/Ladybird/HelperProcess.cpp index 624249be342..6c96be90dcc 100644 --- a/Ladybird/HelperProcess.cpp +++ b/Ladybird/HelperProcess.cpp @@ -77,6 +77,7 @@ ErrorOr> launch_web_content_process( WebView::ViewImplementation& view, ReadonlySpan candidate_web_content_paths, Ladybird::WebContentOptions const& web_content_options, + IPC::File image_decoder_socket, Optional request_server_socket) { Vector arguments { @@ -113,6 +114,9 @@ ErrorOr> launch_web_content_process( arguments.append(ByteString::number(request_server_socket->fd())); } + arguments.append("--image-decoder-socket"sv); + arguments.append(ByteString::number(image_decoder_socket.fd())); + return launch_server_process("WebContent"sv, candidate_web_content_paths, move(arguments), RegisterWithProcessManager::No, web_content_options.enable_callgrind_profiling, view); } @@ -164,3 +168,19 @@ ErrorOr connect_new_request_server_client(Protocol::RequestClient& cl return socket; } + +ErrorOr connect_new_image_decoder_client(ImageDecoderClient::Client& client) +{ + auto new_socket = client.send_sync_but_allow_failure(1); + if (!new_socket) + return Error::from_string_literal("Failed to connect to ImageDecoder"); + + auto sockets = new_socket->take_sockets(); + if (sockets.size() != 1) + return Error::from_string_literal("Failed to connect to ImageDecoder"); + + auto socket = sockets.take_last(); + TRY(socket.clear_close_on_exec()); + + return socket; +} diff --git a/Ladybird/HelperProcess.h b/Ladybird/HelperProcess.h index bce225c9b48..55061f1c0b3 100644 --- a/Ladybird/HelperProcess.h +++ b/Ladybird/HelperProcess.h @@ -21,6 +21,7 @@ ErrorOr> launch_web_content_process( WebView::ViewImplementation& view, ReadonlySpan candidate_web_content_paths, Ladybird::WebContentOptions const&, + IPC::File image_decoder_socket, Optional request_server_socket = {}); ErrorOr> launch_image_decoder_process(ReadonlySpan candidate_image_decoder_paths); @@ -28,3 +29,4 @@ ErrorOr> launch_web_worker_process(Rea ErrorOr> launch_request_server_process(ReadonlySpan candidate_request_server_paths, StringView serenity_resource_root, Vector const& certificates); ErrorOr connect_new_request_server_client(Protocol::RequestClient&); +ErrorOr connect_new_image_decoder_client(ImageDecoderClient::Client&); diff --git a/Ladybird/ImageCodecPlugin.cpp b/Ladybird/ImageCodecPlugin.cpp index 1678a4a7dd0..8a9d71fe950 100644 --- a/Ladybird/ImageCodecPlugin.cpp +++ b/Ladybird/ImageCodecPlugin.cpp @@ -6,11 +6,6 @@ */ #include "ImageCodecPlugin.h" -#ifdef AK_OS_ANDROID -# include -#else -# include "HelperProcess.h" -#endif #include "Utilities.h" #include #include @@ -18,28 +13,37 @@ namespace Ladybird { +ImageCodecPlugin::ImageCodecPlugin(NonnullRefPtr client) + : m_client(move(client)) +{ + m_client->on_death = [this] { + m_client = nullptr; + }; +} + +void ImageCodecPlugin::set_client(NonnullRefPtr client) +{ + m_client = move(client); + m_client->on_death = [this] { + m_client = nullptr; + }; +} + ImageCodecPlugin::~ImageCodecPlugin() = default; NonnullRefPtr> ImageCodecPlugin::decode_image(ReadonlyBytes bytes, Function(Web::Platform::DecodedImage&)> on_resolved, Function on_rejected) { - if (!m_client) { -#ifdef AK_OS_ANDROID - m_client = MUST(bind_service(&bind_image_decoder_java)); -#else - auto candidate_image_decoder_paths = get_paths_for_helper_process("ImageDecoder"sv).release_value_but_fixme_should_propagate_errors(); - m_client = launch_image_decoder_process(candidate_image_decoder_paths).release_value_but_fixme_should_propagate_errors(); -#endif - m_client->on_death = [&] { - m_client = nullptr; - }; - } - auto promise = Core::Promise::construct(); if (on_resolved) promise->on_resolution = move(on_resolved); if (on_rejected) promise->on_rejection = move(on_rejected); + if (!m_client) { + promise->reject(Error::from_string_literal("ImageDecoderClient is disconnected")); + return promise; + } + auto image_decoder_promise = m_client->decode_image( bytes, [promise](ImageDecoderClient::DecodedImage& result) -> ErrorOr { diff --git a/Ladybird/ImageCodecPlugin.h b/Ladybird/ImageCodecPlugin.h index e9cd93fadf5..5ddebcc40d6 100644 --- a/Ladybird/ImageCodecPlugin.h +++ b/Ladybird/ImageCodecPlugin.h @@ -14,11 +14,13 @@ namespace Ladybird { class ImageCodecPlugin final : public Web::Platform::ImageCodecPlugin { public: - ImageCodecPlugin() = default; + explicit ImageCodecPlugin(NonnullRefPtr); virtual ~ImageCodecPlugin() override; virtual NonnullRefPtr> decode_image(ReadonlyBytes, Function(Web::Platform::DecodedImage&)> on_resolved, Function on_rejected) override; + void set_client(NonnullRefPtr); + private: RefPtr m_client; }; diff --git a/Ladybird/Qt/Application.cpp b/Ladybird/Qt/Application.cpp index 69036a91dfb..41d0249ee17 100644 --- a/Ladybird/Qt/Application.cpp +++ b/Ladybird/Qt/Application.cpp @@ -7,6 +7,8 @@ #include "Application.h" #include "StringUtils.h" #include "TaskManagerWindow.h" +#include +#include #include #include @@ -43,6 +45,38 @@ bool Application::event(QEvent* event) return QApplication::event(event); } +static ErrorOr> launch_new_image_decoder() +{ + auto paths = TRY(get_paths_for_helper_process("ImageDecoder"sv)); + return launch_image_decoder_process(paths); +} + +ErrorOr Application::initialize_image_decoder() +{ + m_image_decoder_client = TRY(launch_new_image_decoder()); + + m_image_decoder_client->on_death = [this] { + m_image_decoder_client = nullptr; + if (auto err = this->initialize_image_decoder(); err.is_error()) { + dbgln("Failed to restart image decoder: {}", err.error()); + VERIFY_NOT_REACHED(); + } + + auto num_clients = WebView::WebContentClient::client_count(); + auto new_sockets = m_image_decoder_client->send_sync_but_allow_failure(num_clients); + if (!new_sockets || new_sockets->sockets().size() == 0) { + dbgln("Failed to connect {} new clients to ImageDecoder", num_clients); + VERIFY_NOT_REACHED(); + } + + WebView::WebContentClient::for_each_client([sockets = new_sockets->take_sockets()](WebView::WebContentClient& client) mutable { + client.async_connect_to_image_decoder(sockets.take_last()); + return IterationDecision::Continue; + }); + }; + return {}; +} + void Application::show_task_manager_window() { if (!m_task_manager_window) { diff --git a/Ladybird/Qt/Application.h b/Ladybird/Qt/Application.h index 80c72b50cb3..c0abc89af7d 100644 --- a/Ladybird/Qt/Application.h +++ b/Ladybird/Qt/Application.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,9 @@ public: Function on_open_file; RefPtr request_server_client; + NonnullRefPtr image_decoder_client() const { return *m_image_decoder_client; } + ErrorOr initialize_image_decoder(); + BrowserWindow& new_window(Vector const& initial_urls, WebView::CookieJar&, WebContentOptions const&, StringView webdriver_content_ipc_path, bool allow_popups, Tab* parent_tab = nullptr, Optional page_index = {}); void show_task_manager_window(); @@ -38,6 +42,8 @@ public: private: TaskManagerWindow* m_task_manager_window { nullptr }; BrowserWindow* m_active_window { nullptr }; + + RefPtr m_image_decoder_client; }; } diff --git a/Ladybird/Qt/WebContentView.cpp b/Ladybird/Qt/WebContentView.cpp index 424407980b0..55313a16605 100644 --- a/Ladybird/Qt/WebContentView.cpp +++ b/Ladybird/Qt/WebContentView.cpp @@ -549,8 +549,11 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli request_server_socket = AK::move(socket); } + auto image_decoder = static_cast(QApplication::instance())->image_decoder_client(); + auto image_decoder_socket = connect_new_image_decoder_client(*image_decoder).release_value_but_fixme_should_propagate_errors(); + auto candidate_web_content_paths = get_paths_for_helper_process("WebContent"sv).release_value_but_fixme_should_propagate_errors(); - auto new_client = launch_web_content_process(*this, candidate_web_content_paths, m_web_content_options, AK::move(request_server_socket)).release_value_but_fixme_should_propagate_errors(); + auto new_client = launch_web_content_process(*this, candidate_web_content_paths, m_web_content_options, AK::move(image_decoder_socket), AK::move(request_server_socket)).release_value_but_fixme_should_propagate_errors(); m_client_state.client = new_client; } else { diff --git a/Ladybird/Qt/main.cpp b/Ladybird/Qt/main.cpp index 93321b0e56d..1f0becd0af3 100644 --- a/Ladybird/Qt/main.cpp +++ b/Ladybird/Qt/main.cpp @@ -166,7 +166,9 @@ ErrorOr serenity_main(Main::Arguments arguments) // FIXME: Create an abstraction to re-spawn the RequestServer and re-hook up its client hooks to each tab on crash auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv)); auto protocol_client = TRY(launch_request_server_process(request_server_paths, s_serenity_resource_root, certificates)); - app.request_server_client = protocol_client; + app.request_server_client = move(protocol_client); + + TRY(app.initialize_image_decoder()); StringBuilder command_line_builder; command_line_builder.join(' ', arguments.strings); diff --git a/Ladybird/WebContent/main.cpp b/Ladybird/WebContent/main.cpp index a9715bf6aa3..5af59932166 100644 --- a/Ladybird/WebContent/main.cpp +++ b/Ladybird/WebContent/main.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -28,9 +27,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -52,6 +49,8 @@ static ErrorOr load_content_filters(); static ErrorOr load_autoplay_allowlist(); static ErrorOr initialize_lagom_networking(int request_server_socket); +static ErrorOr initialize_image_decoder(int image_decoder_socket); +static ErrorOr reinitialize_image_decoder(IPC::File const& image_decoder_socket); namespace JS { extern bool g_log_all_js_exceptions; @@ -79,7 +78,6 @@ ErrorOr serenity_main(Main::Arguments arguments) platform_init(); Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity); - Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPlugin); Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) { #if defined(HAVE_QT_MULTIMEDIA) @@ -97,6 +95,7 @@ ErrorOr serenity_main(Main::Arguments arguments) StringView mach_server_name {}; Vector certificates; int request_server_socket { -1 }; + int image_decoder_socket { -1 }; bool is_layout_test_mode = false; bool expose_internals_object = false; bool use_lagom_networking = false; @@ -111,6 +110,7 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_option(command_line, "Chrome process command line", "command-line", 0, "command_line"); args_parser.add_option(executable_path, "Chrome process executable path", "executable-path", 0, "executable_path"); args_parser.add_option(request_server_socket, "File descriptor of the socket for the RequestServer connection", "request-server-socket", 'r', "request_server_socket"); + args_parser.add_option(image_decoder_socket, "File descriptor of the socket for the ImageDecoder connection", "image-decoder-socket", 'i', "image_decoder_socket"); args_parser.add_option(is_layout_test_mode, "Is layout test mode", "layout-test-mode"); args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object"); args_parser.add_option(use_lagom_networking, "Enable Lagom servers for networking", "use-lagom-networking"); @@ -160,6 +160,8 @@ ErrorOr serenity_main(Main::Arguments arguments) #endif TRY(initialize_lagom_networking(request_server_socket)); + TRY(initialize_image_decoder(image_decoder_socket)); + Web::HTML::Window::set_internals_object_exposed(expose_internals_object); Web::Platform::FontPlugin::install(*new Ladybird::FontPlugin(is_layout_test_mode)); @@ -185,6 +187,12 @@ ErrorOr serenity_main(Main::Arguments arguments) auto webcontent_socket = TRY(Core::take_over_socket_from_system_server("WebContent"sv)); auto webcontent_client = TRY(WebContent::ConnectionFromClient::try_create(move(webcontent_socket))); + webcontent_client->on_image_decoder_connection = [&](auto& socket_file) { + auto maybe_error = reinitialize_image_decoder(socket_file); + if (maybe_error.is_error()) + dbgln("Failed to reinitialize image decoder: {}", maybe_error.error()); + }; + return event_loop.exec(); } @@ -246,3 +254,27 @@ ErrorOr initialize_lagom_networking(int request_server_socket) Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(new_client)))); return {}; } + +ErrorOr initialize_image_decoder(int image_decoder_socket) +{ + auto socket = TRY(Core::LocalSocket::adopt_fd(image_decoder_socket)); + TRY(socket->set_blocking(true)); + + auto new_client = TRY(try_make_ref_counted(move(socket))); + + Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPlugin(move(new_client))); + + return {}; +} + +ErrorOr reinitialize_image_decoder(IPC::File const& image_decoder_socket) +{ + auto socket = TRY(Core::LocalSocket::adopt_fd(image_decoder_socket.take_fd())); + TRY(socket->set_blocking(true)); + + auto new_client = TRY(try_make_ref_counted(move(socket))); + + static_cast(Web::Platform::ImageCodecPlugin::the()).set_client(move(new_client)); + + return {}; +} diff --git a/Userland/Services/ImageDecoder/ConnectionFromClient.cpp b/Userland/Services/ImageDecoder/ConnectionFromClient.cpp index db53702e5e8..f51ee99e624 100644 --- a/Userland/Services/ImageDecoder/ConnectionFromClient.cpp +++ b/Userland/Services/ImageDecoder/ConnectionFromClient.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -13,9 +14,13 @@ namespace ImageDecoder { +static HashMap> s_connections; +static IDAllocator s_client_ids; + ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr socket) - : IPC::ConnectionFromClient(*this, move(socket), 1) + : IPC::ConnectionFromClient(*this, move(socket), s_client_ids.allocate()) { + s_connections.set(client_id(), *this); } void ConnectionFromClient::die() @@ -25,8 +30,49 @@ void ConnectionFromClient::die() } m_pending_jobs.clear(); - Threading::quit_background_thread(); - Core::EventLoop::current().quit(0); + auto client_id = this->client_id(); + s_connections.remove(client_id); + s_client_ids.deallocate(client_id); + + if (s_connections.is_empty()) { + Threading::quit_background_thread(); + Core::EventLoop::current().quit(0); + } +} + +ErrorOr ConnectionFromClient::connect_new_client() +{ + int socket_fds[2] {}; + if (auto err = Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds); err.is_error()) + return err.release_error(); + + auto client_socket_or_error = Core::LocalSocket::adopt_fd(socket_fds[0]); + if (client_socket_or_error.is_error()) { + close(socket_fds[0]); + close(socket_fds[1]); + return client_socket_or_error.release_error(); + } + + auto client_socket = client_socket_or_error.release_value(); + // Note: A ref is stored in the static s_connections map + auto client = adopt_ref(*new ConnectionFromClient(move(client_socket))); + + return IPC::File::adopt_fd(socket_fds[1]); +} + +Messages::ImageDecoderServer::ConnectNewClientsResponse ConnectionFromClient::connect_new_clients(size_t count) +{ + Vector files; + files.ensure_capacity(count); + for (size_t i = 0; i < count; ++i) { + auto file_or_error = connect_new_client(); + if (file_or_error.is_error()) { + dbgln("Failed to connect new client: {}", file_or_error.error()); + return Vector {}; + } + files.unchecked_append(file_or_error.release_value()); + } + return files; } static void decode_image_to_bitmaps_and_durations_with_decoder(Gfx::ImageDecoder const& decoder, Optional ideal_size, Vector& bitmaps, Vector& durations) diff --git a/Userland/Services/ImageDecoder/ConnectionFromClient.h b/Userland/Services/ImageDecoder/ConnectionFromClient.h index 025013bd163..baacecd877e 100644 --- a/Userland/Services/ImageDecoder/ConnectionFromClient.h +++ b/Userland/Services/ImageDecoder/ConnectionFromClient.h @@ -39,6 +39,9 @@ private: virtual Messages::ImageDecoderServer::DecodeImageResponse decode_image(Core::AnonymousBuffer const&, Optional const& ideal_size, Optional const& mime_type) override; virtual void cancel_decoding(i64 image_id) override; + virtual Messages::ImageDecoderServer::ConnectNewClientsResponse connect_new_clients(size_t count) override; + + ErrorOr connect_new_client(); NonnullRefPtr make_decode_image_job(i64 image_id, Core::AnonymousBuffer, Optional ideal_size, Optional mime_type); diff --git a/Userland/Services/ImageDecoder/ImageDecoderServer.ipc b/Userland/Services/ImageDecoder/ImageDecoderServer.ipc index 93db39a5776..cd6408ee59f 100644 --- a/Userland/Services/ImageDecoder/ImageDecoderServer.ipc +++ b/Userland/Services/ImageDecoder/ImageDecoderServer.ipc @@ -4,4 +4,6 @@ endpoint ImageDecoderServer { decode_image(Core::AnonymousBuffer data, Optional ideal_size, Optional mime_type) => (i64 image_id) cancel_decoding(i64 image_id) =| + + connect_new_clients(size_t count) => (Vector sockets) } diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index 9a89c0ebcb1..3818423e6cf 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -104,6 +104,12 @@ void ConnectionFromClient::connect_to_webdriver(u64 page_id, ByteString const& w } } +void ConnectionFromClient::connect_to_image_decoder(IPC::File const& image_decoder_socket) +{ + if (on_image_decoder_connection) + on_image_decoder_connection(image_decoder_socket); +} + void ConnectionFromClient::update_system_theme(u64 page_id, Core::AnonymousBuffer const& theme_buffer) { auto page = this->page(page_id); diff --git a/Userland/Services/WebContent/ConnectionFromClient.h b/Userland/Services/WebContent/ConnectionFromClient.h index 3f9ac2be6e4..404d0c6d32e 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.h +++ b/Userland/Services/WebContent/ConnectionFromClient.h @@ -45,6 +45,8 @@ public: PageHost& page_host() { return *m_page_host; } PageHost const& page_host() const { return *m_page_host; } + Function on_image_decoder_connection; + private: explicit ConnectionFromClient(NonnullOwnPtr); @@ -54,6 +56,7 @@ private: virtual Messages::WebContentServer::GetWindowHandleResponse get_window_handle(u64 page_id) override; virtual void set_window_handle(u64 page_id, String const& handle) override; virtual void connect_to_webdriver(u64 page_id, ByteString const& webdriver_ipc_path) override; + virtual void connect_to_image_decoder(IPC::File const& image_decoder_socket) override; virtual void update_system_theme(u64 page_id, Core::AnonymousBuffer const&) override; virtual void update_screen_rects(u64 page_id, Vector const&, u32) override; virtual void load_url(u64 page_id, URL::URL const&) override; diff --git a/Userland/Services/WebContent/WebContentServer.ipc b/Userland/Services/WebContent/WebContentServer.ipc index 540e98de7d2..94666e7c1ef 100644 --- a/Userland/Services/WebContent/WebContentServer.ipc +++ b/Userland/Services/WebContent/WebContentServer.ipc @@ -17,6 +17,7 @@ endpoint WebContentServer set_window_handle(u64 page_id, String handle) =| connect_to_webdriver(u64 page_id, ByteString webdriver_ipc_path) =| + connect_to_image_decoder(IPC::File socket_fd) =| update_system_theme(u64 page_id, Core::AnonymousBuffer theme_buffer) =| update_screen_rects(u64 page_id, Vector rects, u32 main_screen_index) =| diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index 98648e5dc48..bb1ccaff2a7 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -65,14 +66,18 @@ public: static ErrorOr> create(Core::AnonymousBuffer theme, Gfx::IntSize const& window_size, String const& command_line, StringView web_driver_ipc_path, Ladybird::IsLayoutTestMode is_layout_test_mode = Ladybird::IsLayoutTestMode::No, Vector const& certificates = {}, StringView resources_folder = {}) { RefPtr request_client; + RefPtr image_decoder_client; auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv)); request_client = TRY(launch_request_server_process(request_server_paths, resources_folder, certificates)); + auto image_decoder_paths = TRY(get_paths_for_helper_process("ImageDecoder"sv)); + image_decoder_client = TRY(launch_image_decoder_process(image_decoder_paths)); + auto database = TRY(WebView::Database::create()); auto cookie_jar = TRY(WebView::CookieJar::create(*database)); - auto view = TRY(adopt_nonnull_own_or_enomem(new (nothrow) HeadlessWebContentView(move(database), move(cookie_jar), request_client))); + auto view = TRY(adopt_nonnull_own_or_enomem(new (nothrow) HeadlessWebContentView(move(database), move(cookie_jar), image_decoder_client, request_client))); Ladybird::WebContentOptions web_content_options { .command_line = command_line, @@ -81,9 +86,10 @@ public: }; auto request_server_socket = TRY(connect_new_request_server_client(*request_client)); + auto image_decoder_socket = TRY(connect_new_image_decoder_client(*image_decoder_client)); auto candidate_web_content_paths = TRY(get_paths_for_helper_process("WebContent"sv)); - view->m_client_state.client = TRY(launch_web_content_process(*view, candidate_web_content_paths, web_content_options, move(request_server_socket))); + view->m_client_state.client = TRY(launch_web_content_process(*view, candidate_web_content_paths, web_content_options, move(image_decoder_socket), move(request_server_socket))); view->client().async_update_system_theme(0, move(theme)); @@ -145,10 +151,11 @@ public: } private: - HeadlessWebContentView(NonnullRefPtr database, NonnullOwnPtr cookie_jar, RefPtr request_client = nullptr) + HeadlessWebContentView(NonnullRefPtr database, NonnullOwnPtr cookie_jar, RefPtr image_decoder_client, RefPtr request_client) : m_database(move(database)) , m_cookie_jar(move(cookie_jar)) , m_request_client(move(request_client)) + , m_image_decoder_client(move(image_decoder_client)) { on_get_cookie = [this](auto const& url, auto source) { return m_cookie_jar->get_cookie(url, source); @@ -178,6 +185,7 @@ private: NonnullRefPtr m_database; NonnullOwnPtr m_cookie_jar; RefPtr m_request_client; + RefPtr m_image_decoder_client; }; static ErrorOr> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, URL::URL url, int screenshot_timeout)