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.
This commit is contained in:
Andrew Kaster 2024-06-26 13:44:42 -06:00 committed by Andrew Kaster
parent 343a3a0d7e
commit 4b5541e1b7
Notes: sideshowbarker 2024-07-17 00:59:43 +09:00
21 changed files with 250 additions and 31 deletions

View file

@ -22,6 +22,7 @@ class WebViewBridge;
- (instancetype)init; - (instancetype)init;
- (ErrorOr<void>)launchRequestServer:(Vector<ByteString> const&)certificates; - (ErrorOr<void>)launchRequestServer:(Vector<ByteString> const&)certificates;
- (ErrorOr<void>)launchImageDecoder;
- (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge; - (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge;
- (ErrorOr<IPC::File>)launchWebWorker; - (ErrorOr<IPC::File>)launchWebWorker;

View file

@ -41,6 +41,11 @@
return m_application_bridge->launch_request_server(certificates); return m_application_bridge->launch_request_server(certificates);
} }
- (ErrorOr<void>)launchImageDecoder
{
return m_application_bridge->launch_image_decoder();
}
- (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge - (ErrorOr<NonnullRefPtr<WebView::WebContentClient>>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge
{ {
return m_application_bridge->launch_web_content(web_view_bridge); return m_application_bridge->launch_web_content(web_view_bridge);

View file

@ -9,6 +9,7 @@
#include <Ladybird/AppKit/UI/LadybirdWebViewBridge.h> #include <Ladybird/AppKit/UI/LadybirdWebViewBridge.h>
#include <Ladybird/HelperProcess.h> #include <Ladybird/HelperProcess.h>
#include <Ladybird/Utilities.h> #include <Ladybird/Utilities.h>
#include <LibImageDecoderClient/Client.h>
#include <LibProtocol/RequestClient.h> #include <LibProtocol/RequestClient.h>
#include <LibWebView/WebContentClient.h> #include <LibWebView/WebContentClient.h>
@ -19,6 +20,7 @@ namespace Ladybird {
// is limited to .cpp files (i.e. not .h files that an Objective-C file can include). // is limited to .cpp files (i.e. not .h files that an Objective-C file can include).
struct ApplicationBridgeImpl { struct ApplicationBridgeImpl {
RefPtr<Protocol::RequestClient> request_server_client; RefPtr<Protocol::RequestClient> request_server_client;
RefPtr<ImageDecoderClient::Client> image_decoder_client;
}; };
ApplicationBridge::ApplicationBridge() ApplicationBridge::ApplicationBridge()
@ -37,13 +39,47 @@ ErrorOr<void> ApplicationBridge::launch_request_server(Vector<ByteString> const&
return {}; return {};
} }
static ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> 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<void> 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<Messages::ImageDecoderServer::ConnectNewClients>(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<NonnullRefPtr<WebView::WebContentClient>> ApplicationBridge::launch_web_content(WebViewBridge& web_view_bridge) ErrorOr<NonnullRefPtr<WebView::WebContentClient>> ApplicationBridge::launch_web_content(WebViewBridge& web_view_bridge)
{ {
// FIXME: Fail to open the tab, rather than crashing the whole application if this fails // 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 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_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; return web_content;
} }

View file

@ -22,6 +22,7 @@ public:
~ApplicationBridge(); ~ApplicationBridge();
ErrorOr<void> launch_request_server(Vector<ByteString> const& certificates); ErrorOr<void> launch_request_server(Vector<ByteString> const& certificates);
ErrorOr<void> launch_image_decoder();
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content(WebViewBridge&); ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content(WebViewBridge&);
ErrorOr<IPC::File> launch_web_worker(); ErrorOr<IPC::File> launch_web_worker();

View file

@ -136,6 +136,8 @@ ErrorOr<int> 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 // 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 launchRequestServer:certificates]);
TRY([application launchImageDecoder]);
StringBuilder command_line_builder; StringBuilder command_line_builder;
command_line_builder.join(' ', arguments.strings); command_line_builder.join(' ', arguments.strings);
Ladybird::WebContentOptions web_content_options { Ladybird::WebContentOptions web_content_options {

View file

@ -77,6 +77,7 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
WebView::ViewImplementation& view, WebView::ViewImplementation& view,
ReadonlySpan<ByteString> candidate_web_content_paths, ReadonlySpan<ByteString> candidate_web_content_paths,
Ladybird::WebContentOptions const& web_content_options, Ladybird::WebContentOptions const& web_content_options,
IPC::File image_decoder_socket,
Optional<IPC::File> request_server_socket) Optional<IPC::File> request_server_socket)
{ {
Vector<ByteString> arguments { Vector<ByteString> arguments {
@ -113,6 +114,9 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
arguments.append(ByteString::number(request_server_socket->fd())); 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<WebView::WebContentClient>("WebContent"sv, candidate_web_content_paths, move(arguments), RegisterWithProcessManager::No, web_content_options.enable_callgrind_profiling, view); return launch_server_process<WebView::WebContentClient>("WebContent"sv, candidate_web_content_paths, move(arguments), RegisterWithProcessManager::No, web_content_options.enable_callgrind_profiling, view);
} }
@ -164,3 +168,19 @@ ErrorOr<IPC::File> connect_new_request_server_client(Protocol::RequestClient& cl
return socket; return socket;
} }
ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client& client)
{
auto new_socket = client.send_sync_but_allow_failure<Messages::ImageDecoderServer::ConnectNewClients>(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;
}

View file

@ -21,6 +21,7 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
WebView::ViewImplementation& view, WebView::ViewImplementation& view,
ReadonlySpan<ByteString> candidate_web_content_paths, ReadonlySpan<ByteString> candidate_web_content_paths,
Ladybird::WebContentOptions const&, Ladybird::WebContentOptions const&,
IPC::File image_decoder_socket,
Optional<IPC::File> request_server_socket = {}); Optional<IPC::File> request_server_socket = {});
ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths); ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths);
@ -28,3 +29,4 @@ ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(Rea
ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root, Vector<ByteString> const& certificates); ErrorOr<NonnullRefPtr<Protocol::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root, Vector<ByteString> const& certificates);
ErrorOr<IPC::File> connect_new_request_server_client(Protocol::RequestClient&); ErrorOr<IPC::File> connect_new_request_server_client(Protocol::RequestClient&);
ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client&);

View file

@ -6,11 +6,6 @@
*/ */
#include "ImageCodecPlugin.h" #include "ImageCodecPlugin.h"
#ifdef AK_OS_ANDROID
# include <Ladybird/Android/src/main/cpp/WebContentService.h>
#else
# include "HelperProcess.h"
#endif
#include "Utilities.h" #include "Utilities.h"
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
#include <LibGfx/ImageFormats/ImageDecoder.h> #include <LibGfx/ImageFormats/ImageDecoder.h>
@ -18,28 +13,37 @@
namespace Ladybird { namespace Ladybird {
ImageCodecPlugin::ImageCodecPlugin(NonnullRefPtr<ImageDecoderClient::Client> client)
: m_client(move(client))
{
m_client->on_death = [this] {
m_client = nullptr;
};
}
void ImageCodecPlugin::set_client(NonnullRefPtr<ImageDecoderClient::Client> client)
{
m_client = move(client);
m_client->on_death = [this] {
m_client = nullptr;
};
}
ImageCodecPlugin::~ImageCodecPlugin() = default; ImageCodecPlugin::~ImageCodecPlugin() = default;
NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> ImageCodecPlugin::decode_image(ReadonlyBytes bytes, Function<ErrorOr<void>(Web::Platform::DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected) NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> ImageCodecPlugin::decode_image(ReadonlyBytes bytes, Function<ErrorOr<void>(Web::Platform::DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected)
{ {
if (!m_client) {
#ifdef AK_OS_ANDROID
m_client = MUST(bind_service<ImageDecoderClient::Client>(&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<Web::Platform::DecodedImage>::construct(); auto promise = Core::Promise<Web::Platform::DecodedImage>::construct();
if (on_resolved) if (on_resolved)
promise->on_resolution = move(on_resolved); promise->on_resolution = move(on_resolved);
if (on_rejected) if (on_rejected)
promise->on_rejection = move(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( auto image_decoder_promise = m_client->decode_image(
bytes, bytes,
[promise](ImageDecoderClient::DecodedImage& result) -> ErrorOr<void> { [promise](ImageDecoderClient::DecodedImage& result) -> ErrorOr<void> {

View file

@ -14,11 +14,13 @@ namespace Ladybird {
class ImageCodecPlugin final : public Web::Platform::ImageCodecPlugin { class ImageCodecPlugin final : public Web::Platform::ImageCodecPlugin {
public: public:
ImageCodecPlugin() = default; explicit ImageCodecPlugin(NonnullRefPtr<ImageDecoderClient::Client>);
virtual ~ImageCodecPlugin() override; virtual ~ImageCodecPlugin() override;
virtual NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> decode_image(ReadonlyBytes, Function<ErrorOr<void>(Web::Platform::DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected) override; virtual NonnullRefPtr<Core::Promise<Web::Platform::DecodedImage>> decode_image(ReadonlyBytes, Function<ErrorOr<void>(Web::Platform::DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected) override;
void set_client(NonnullRefPtr<ImageDecoderClient::Client>);
private: private:
RefPtr<ImageDecoderClient::Client> m_client; RefPtr<ImageDecoderClient::Client> m_client;
}; };

View file

@ -7,6 +7,8 @@
#include "Application.h" #include "Application.h"
#include "StringUtils.h" #include "StringUtils.h"
#include "TaskManagerWindow.h" #include "TaskManagerWindow.h"
#include <Ladybird/HelperProcess.h>
#include <Ladybird/Utilities.h>
#include <LibWebView/URL.h> #include <LibWebView/URL.h>
#include <QFileOpenEvent> #include <QFileOpenEvent>
@ -43,6 +45,38 @@ bool Application::event(QEvent* event)
return QApplication::event(event); return QApplication::event(event);
} }
static ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_new_image_decoder()
{
auto paths = TRY(get_paths_for_helper_process("ImageDecoder"sv));
return launch_image_decoder_process(paths);
}
ErrorOr<void> 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<Messages::ImageDecoderServer::ConnectNewClients>(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() void Application::show_task_manager_window()
{ {
if (!m_task_manager_window) { if (!m_task_manager_window) {

View file

@ -9,6 +9,7 @@
#include <AK/Function.h> #include <AK/Function.h>
#include <AK/HashTable.h> #include <AK/HashTable.h>
#include <Ladybird/Qt/BrowserWindow.h> #include <Ladybird/Qt/BrowserWindow.h>
#include <LibImageDecoderClient/Client.h>
#include <LibProtocol/RequestClient.h> #include <LibProtocol/RequestClient.h>
#include <LibURL/URL.h> #include <LibURL/URL.h>
#include <QApplication> #include <QApplication>
@ -27,6 +28,9 @@ public:
Function<void(URL::URL)> on_open_file; Function<void(URL::URL)> on_open_file;
RefPtr<Protocol::RequestClient> request_server_client; RefPtr<Protocol::RequestClient> request_server_client;
NonnullRefPtr<ImageDecoderClient::Client> image_decoder_client() const { return *m_image_decoder_client; }
ErrorOr<void> initialize_image_decoder();
BrowserWindow& new_window(Vector<URL::URL> const& initial_urls, WebView::CookieJar&, WebContentOptions const&, StringView webdriver_content_ipc_path, bool allow_popups, Tab* parent_tab = nullptr, Optional<u64> page_index = {}); BrowserWindow& new_window(Vector<URL::URL> const& initial_urls, WebView::CookieJar&, WebContentOptions const&, StringView webdriver_content_ipc_path, bool allow_popups, Tab* parent_tab = nullptr, Optional<u64> page_index = {});
void show_task_manager_window(); void show_task_manager_window();
@ -38,6 +42,8 @@ public:
private: private:
TaskManagerWindow* m_task_manager_window { nullptr }; TaskManagerWindow* m_task_manager_window { nullptr };
BrowserWindow* m_active_window { nullptr }; BrowserWindow* m_active_window { nullptr };
RefPtr<ImageDecoderClient::Client> m_image_decoder_client;
}; };
} }

View file

@ -549,8 +549,11 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli
request_server_socket = AK::move(socket); request_server_socket = AK::move(socket);
} }
auto image_decoder = static_cast<Ladybird::Application*>(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 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; m_client_state.client = new_client;
} else { } else {

View file

@ -166,7 +166,9 @@ ErrorOr<int> 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 // 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 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)); 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; StringBuilder command_line_builder;
command_line_builder.join(' ', arguments.strings); command_line_builder.join(' ', arguments.strings);

View file

@ -14,7 +14,6 @@
#include <LibCore/LocalServer.h> #include <LibCore/LocalServer.h>
#include <LibCore/Process.h> #include <LibCore/Process.h>
#include <LibCore/Resource.h> #include <LibCore/Resource.h>
#include <LibCore/System.h>
#include <LibCore/SystemServerTakeover.h> #include <LibCore/SystemServerTakeover.h>
#include <LibIPC/ConnectionFromClient.h> #include <LibIPC/ConnectionFromClient.h>
#include <LibJS/Bytecode/Interpreter.h> #include <LibJS/Bytecode/Interpreter.h>
@ -28,9 +27,7 @@
#include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h> #include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
#include <LibWeb/Platform/AudioCodecPluginAgnostic.h> #include <LibWeb/Platform/AudioCodecPluginAgnostic.h>
#include <LibWeb/Platform/EventLoopPluginSerenity.h> #include <LibWeb/Platform/EventLoopPluginSerenity.h>
#include <LibWeb/WebSockets/WebSocket.h>
#include <LibWebView/RequestServerAdapter.h> #include <LibWebView/RequestServerAdapter.h>
#include <LibWebView/WebSocketClientAdapter.h>
#include <WebContent/ConnectionFromClient.h> #include <WebContent/ConnectionFromClient.h>
#include <WebContent/PageClient.h> #include <WebContent/PageClient.h>
#include <WebContent/WebDriverConnection.h> #include <WebContent/WebDriverConnection.h>
@ -52,6 +49,8 @@
static ErrorOr<void> load_content_filters(); static ErrorOr<void> load_content_filters();
static ErrorOr<void> load_autoplay_allowlist(); static ErrorOr<void> load_autoplay_allowlist();
static ErrorOr<void> initialize_lagom_networking(int request_server_socket); static ErrorOr<void> initialize_lagom_networking(int request_server_socket);
static ErrorOr<void> initialize_image_decoder(int image_decoder_socket);
static ErrorOr<void> reinitialize_image_decoder(IPC::File const& image_decoder_socket);
namespace JS { namespace JS {
extern bool g_log_all_js_exceptions; extern bool g_log_all_js_exceptions;
@ -79,7 +78,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
platform_init(); platform_init();
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity); Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity);
Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPlugin);
Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) { Web::Platform::AudioCodecPlugin::install_creation_hook([](auto loader) {
#if defined(HAVE_QT_MULTIMEDIA) #if defined(HAVE_QT_MULTIMEDIA)
@ -97,6 +95,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
StringView mach_server_name {}; StringView mach_server_name {};
Vector<ByteString> certificates; Vector<ByteString> certificates;
int request_server_socket { -1 }; int request_server_socket { -1 };
int image_decoder_socket { -1 };
bool is_layout_test_mode = false; bool is_layout_test_mode = false;
bool expose_internals_object = false; bool expose_internals_object = false;
bool use_lagom_networking = false; bool use_lagom_networking = false;
@ -111,6 +110,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
args_parser.add_option(command_line, "Chrome process command line", "command-line", 0, "command_line"); 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(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(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(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(expose_internals_object, "Expose internals object", "expose-internals-object");
args_parser.add_option(use_lagom_networking, "Enable Lagom servers for networking", "use-lagom-networking"); args_parser.add_option(use_lagom_networking, "Enable Lagom servers for networking", "use-lagom-networking");
@ -160,6 +160,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
#endif #endif
TRY(initialize_lagom_networking(request_server_socket)); 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::HTML::Window::set_internals_object_exposed(expose_internals_object);
Web::Platform::FontPlugin::install(*new Ladybird::FontPlugin(is_layout_test_mode)); Web::Platform::FontPlugin::install(*new Ladybird::FontPlugin(is_layout_test_mode));
@ -185,6 +187,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto webcontent_socket = TRY(Core::take_over_socket_from_system_server("WebContent"sv)); auto webcontent_socket = TRY(Core::take_over_socket_from_system_server("WebContent"sv));
auto webcontent_client = TRY(WebContent::ConnectionFromClient::try_create(move(webcontent_socket))); 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(); return event_loop.exec();
} }
@ -246,3 +254,27 @@ ErrorOr<void> initialize_lagom_networking(int request_server_socket)
Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(new_client)))); Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(new_client))));
return {}; return {};
} }
ErrorOr<void> 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<ImageDecoderClient::Client>(move(socket)));
Web::Platform::ImageCodecPlugin::install(*new Ladybird::ImageCodecPlugin(move(new_client)));
return {};
}
ErrorOr<void> 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<ImageDecoderClient::Client>(move(socket)));
static_cast<Ladybird::ImageCodecPlugin&>(Web::Platform::ImageCodecPlugin::the()).set_client(move(new_client));
return {};
}

View file

@ -5,6 +5,7 @@
*/ */
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/IDAllocator.h>
#include <ImageDecoder/ConnectionFromClient.h> #include <ImageDecoder/ConnectionFromClient.h>
#include <ImageDecoder/ImageDecoderClientEndpoint.h> #include <ImageDecoder/ImageDecoderClientEndpoint.h>
#include <LibGfx/Bitmap.h> #include <LibGfx/Bitmap.h>
@ -13,9 +14,13 @@
namespace ImageDecoder { namespace ImageDecoder {
static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
static IDAllocator s_client_ids;
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket) ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket)
: IPC::ConnectionFromClient<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint>(*this, move(socket), 1) : IPC::ConnectionFromClient<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint>(*this, move(socket), s_client_ids.allocate())
{ {
s_connections.set(client_id(), *this);
} }
void ConnectionFromClient::die() void ConnectionFromClient::die()
@ -25,8 +30,49 @@ void ConnectionFromClient::die()
} }
m_pending_jobs.clear(); m_pending_jobs.clear();
Threading::quit_background_thread(); auto client_id = this->client_id();
Core::EventLoop::current().quit(0); 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<IPC::File> 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<IPC::File> 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<IPC::File> {};
}
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<Gfx::IntSize> ideal_size, Vector<Gfx::ShareableBitmap>& bitmaps, Vector<u32>& durations) static void decode_image_to_bitmaps_and_durations_with_decoder(Gfx::ImageDecoder const& decoder, Optional<Gfx::IntSize> ideal_size, Vector<Gfx::ShareableBitmap>& bitmaps, Vector<u32>& durations)

View file

@ -39,6 +39,9 @@ private:
virtual Messages::ImageDecoderServer::DecodeImageResponse decode_image(Core::AnonymousBuffer const&, Optional<Gfx::IntSize> const& ideal_size, Optional<ByteString> const& mime_type) override; virtual Messages::ImageDecoderServer::DecodeImageResponse decode_image(Core::AnonymousBuffer const&, Optional<Gfx::IntSize> const& ideal_size, Optional<ByteString> const& mime_type) override;
virtual void cancel_decoding(i64 image_id) override; virtual void cancel_decoding(i64 image_id) override;
virtual Messages::ImageDecoderServer::ConnectNewClientsResponse connect_new_clients(size_t count) override;
ErrorOr<IPC::File> connect_new_client();
NonnullRefPtr<Job> make_decode_image_job(i64 image_id, Core::AnonymousBuffer, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type); NonnullRefPtr<Job> make_decode_image_job(i64 image_id, Core::AnonymousBuffer, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type);

View file

@ -4,4 +4,6 @@ endpoint ImageDecoderServer
{ {
decode_image(Core::AnonymousBuffer data, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type) => (i64 image_id) decode_image(Core::AnonymousBuffer data, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type) => (i64 image_id)
cancel_decoding(i64 image_id) =| cancel_decoding(i64 image_id) =|
connect_new_clients(size_t count) => (Vector<IPC::File> sockets)
} }

View file

@ -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) void ConnectionFromClient::update_system_theme(u64 page_id, Core::AnonymousBuffer const& theme_buffer)
{ {
auto page = this->page(page_id); auto page = this->page(page_id);

View file

@ -45,6 +45,8 @@ public:
PageHost& page_host() { return *m_page_host; } PageHost& page_host() { return *m_page_host; }
PageHost const& page_host() const { return *m_page_host; } PageHost const& page_host() const { return *m_page_host; }
Function<void(IPC::File const&)> on_image_decoder_connection;
private: private:
explicit ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>); explicit ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>);
@ -54,6 +56,7 @@ private:
virtual Messages::WebContentServer::GetWindowHandleResponse get_window_handle(u64 page_id) override; 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 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_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_system_theme(u64 page_id, Core::AnonymousBuffer const&) override;
virtual void update_screen_rects(u64 page_id, Vector<Web::DevicePixelRect> const&, u32) override; virtual void update_screen_rects(u64 page_id, Vector<Web::DevicePixelRect> const&, u32) override;
virtual void load_url(u64 page_id, URL::URL const&) override; virtual void load_url(u64 page_id, URL::URL const&) override;

View file

@ -17,6 +17,7 @@ endpoint WebContentServer
set_window_handle(u64 page_id, String handle) =| set_window_handle(u64 page_id, String handle) =|
connect_to_webdriver(u64 page_id, ByteString webdriver_ipc_path) =| 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_system_theme(u64 page_id, Core::AnonymousBuffer theme_buffer) =|
update_screen_rects(u64 page_id, Vector<Web::DevicePixelRect> rects, u32 main_screen_index) =| update_screen_rects(u64 page_id, Vector<Web::DevicePixelRect> rects, u32 main_screen_index) =|

View file

@ -43,6 +43,7 @@
#include <LibGfx/StandardCursor.h> #include <LibGfx/StandardCursor.h>
#include <LibGfx/SystemTheme.h> #include <LibGfx/SystemTheme.h>
#include <LibIPC/File.h> #include <LibIPC/File.h>
#include <LibImageDecoderClient/Client.h>
#include <LibProtocol/RequestClient.h> #include <LibProtocol/RequestClient.h>
#include <LibURL/URL.h> #include <LibURL/URL.h>
#include <LibWeb/Cookie/Cookie.h> #include <LibWeb/Cookie/Cookie.h>
@ -65,14 +66,18 @@ public:
static ErrorOr<NonnullOwnPtr<HeadlessWebContentView>> 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<ByteString> const& certificates = {}, StringView resources_folder = {}) static ErrorOr<NonnullOwnPtr<HeadlessWebContentView>> 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<ByteString> const& certificates = {}, StringView resources_folder = {})
{ {
RefPtr<Protocol::RequestClient> request_client; RefPtr<Protocol::RequestClient> request_client;
RefPtr<ImageDecoderClient::Client> image_decoder_client;
auto request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv)); 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)); 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 database = TRY(WebView::Database::create());
auto cookie_jar = TRY(WebView::CookieJar::create(*database)); 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 { Ladybird::WebContentOptions web_content_options {
.command_line = command_line, .command_line = command_line,
@ -81,9 +86,10 @@ public:
}; };
auto request_server_socket = TRY(connect_new_request_server_client(*request_client)); 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)); 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)); view->client().async_update_system_theme(0, move(theme));
@ -145,10 +151,11 @@ public:
} }
private: private:
HeadlessWebContentView(NonnullRefPtr<WebView::Database> database, NonnullOwnPtr<WebView::CookieJar> cookie_jar, RefPtr<Protocol::RequestClient> request_client = nullptr) HeadlessWebContentView(NonnullRefPtr<WebView::Database> database, NonnullOwnPtr<WebView::CookieJar> cookie_jar, RefPtr<ImageDecoderClient::Client> image_decoder_client, RefPtr<Protocol::RequestClient> request_client)
: m_database(move(database)) : m_database(move(database))
, m_cookie_jar(move(cookie_jar)) , m_cookie_jar(move(cookie_jar))
, m_request_client(move(request_client)) , m_request_client(move(request_client))
, m_image_decoder_client(move(image_decoder_client))
{ {
on_get_cookie = [this](auto const& url, auto source) { on_get_cookie = [this](auto const& url, auto source) {
return m_cookie_jar->get_cookie(url, source); return m_cookie_jar->get_cookie(url, source);
@ -178,6 +185,7 @@ private:
NonnullRefPtr<WebView::Database> m_database; NonnullRefPtr<WebView::Database> m_database;
NonnullOwnPtr<WebView::CookieJar> m_cookie_jar; NonnullOwnPtr<WebView::CookieJar> m_cookie_jar;
RefPtr<Protocol::RequestClient> m_request_client; RefPtr<Protocol::RequestClient> m_request_client;
RefPtr<ImageDecoderClient::Client> m_image_decoder_client;
}; };
static ErrorOr<NonnullRefPtr<Core::Timer>> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, URL::URL url, int screenshot_timeout) static ErrorOr<NonnullRefPtr<Core::Timer>> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, URL::URL url, int screenshot_timeout)