diff --git a/Ladybird/AppKit/Application/Application.h b/Ladybird/AppKit/Application/Application.h index f8de23dc8b..dc4c561af5 100644 --- a/Ladybird/AppKit/Application/Application.h +++ b/Ladybird/AppKit/Application/Application.h @@ -6,8 +6,23 @@ #pragma once +#include +#include +#include +#include + #import +namespace Ladybird { +class WebViewBridge; +} + @interface Application : NSApplication +- (instancetype)init; + +- (ErrorOr)launchRequestServer:(Vector const&)certificates; +- (ErrorOr>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge; +- (ErrorOr)launchWebWorker; + @end diff --git a/Ladybird/AppKit/Application/Application.mm b/Ladybird/AppKit/Application/Application.mm index c8091a8556..3682ed82a2 100644 --- a/Ladybird/AppKit/Application/Application.mm +++ b/Ladybird/AppKit/Application/Application.mm @@ -4,7 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include +#include #import @@ -13,10 +16,42 @@ #endif @interface Application () +{ + OwnPtr m_application_bridge; +} + @end @implementation Application +- (instancetype)init +{ + if (self = [super init]) { + m_application_bridge = make(); + } + + return self; +} + +#pragma mark - Public methods + +- (ErrorOr)launchRequestServer:(Vector const&)certificates +{ + return m_application_bridge->launch_request_server(certificates); +} + +- (ErrorOr>)launchWebContent:(Ladybird::WebViewBridge&)web_view_bridge +{ + return m_application_bridge->launch_web_content(web_view_bridge); +} + +- (ErrorOr)launchWebWorker +{ + return m_application_bridge->launch_web_worker(); +} + +#pragma mark - NSApplication + - (void)terminate:(id)sender { Core::EventLoop::current().quit(0); diff --git a/Ladybird/AppKit/Application/ApplicationBridge.cpp b/Ladybird/AppKit/Application/ApplicationBridge.cpp new file mode 100644 index 0000000000..d9f092ed3d --- /dev/null +++ b/Ladybird/AppKit/Application/ApplicationBridge.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Ladybird { + +// Unfortunately, the Protocol namespace conflicts hard with a @Protocol interface defined by Objective-C. And the #define +// trick we use for e.g. Duration does not work for Protocol. So here, we make sure that any use of the Protocol namespace +// is limited to .cpp files (i.e. not .h files that an Objective-C file can include). +struct ApplicationBridgeImpl { + RefPtr request_server_client; +}; + +ApplicationBridge::ApplicationBridge() + : m_impl(make()) +{ +} + +ApplicationBridge::~ApplicationBridge() = default; + +ErrorOr ApplicationBridge::launch_request_server(Vector const& certificates) +{ + 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)); + + m_impl->request_server_client = move(protocol_client); + 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_sockets = TRY(connect_new_request_server_client(*m_impl->request_server_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_sockets))); + + return web_content; +} + +ErrorOr ApplicationBridge::launch_web_worker() +{ + auto web_worker_paths = TRY(get_paths_for_helper_process("WebWorker"sv)); + auto worker_client = TRY(launch_web_worker_process(web_worker_paths, *m_impl->request_server_client)); + + return worker_client->dup_sockets(); +} + +} diff --git a/Ladybird/AppKit/Application/ApplicationBridge.h b/Ladybird/AppKit/Application/ApplicationBridge.h new file mode 100644 index 0000000000..4ce44b0b5c --- /dev/null +++ b/Ladybird/AppKit/Application/ApplicationBridge.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Ladybird { + +struct ApplicationBridgeImpl; +class WebViewBridge; + +class ApplicationBridge { +public: + ApplicationBridge(); + ~ApplicationBridge(); + + ErrorOr launch_request_server(Vector const& certificates); + ErrorOr> launch_web_content(WebViewBridge&); + ErrorOr launch_web_worker(); + +private: + NonnullOwnPtr m_impl; +}; + +} diff --git a/Ladybird/AppKit/UI/LadybirdWebView.mm b/Ladybird/AppKit/UI/LadybirdWebView.mm index 8d2dff143b..c5e1867b3c 100644 --- a/Ladybird/AppKit/UI/LadybirdWebView.mm +++ b/Ladybird/AppKit/UI/LadybirdWebView.mm @@ -15,6 +15,7 @@ #include #include +#import #import #import #import @@ -106,6 +107,8 @@ struct HideCursor { m_web_view_bridge = MUST(Ladybird::WebViewBridge::create(move(screen_rects), device_pixel_ratio, [delegate webContentOptions], [delegate webdriverContentIPCPath], [delegate preferredColorScheme])); [self setWebViewCallbacks]; + m_web_view_bridge->initialize_client(); + auto* area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect | NSTrackingMouseMoved owner:self @@ -277,6 +280,16 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_ return [self.observer onCreateNewTab:"about:blank"sv activateTab:activate_tab]; }; + m_web_view_bridge->on_request_web_content = [self]() { + Application* application = NSApp; + return [application launchWebContent:*m_web_view_bridge].release_value_but_fixme_should_propagate_errors(); + }; + + m_web_view_bridge->on_request_worker_agent = []() { + Application* application = NSApp; + return [application launchWebWorker].release_value_but_fixme_should_propagate_errors(); + }; + m_web_view_bridge->on_activate_tab = [self]() { [[self window] orderFront:nil]; }; diff --git a/Ladybird/AppKit/UI/LadybirdWebViewBridge.cpp b/Ladybird/AppKit/UI/LadybirdWebViewBridge.cpp index 2797d65302..c7361929f6 100644 --- a/Ladybird/AppKit/UI/LadybirdWebViewBridge.cpp +++ b/Ladybird/AppKit/UI/LadybirdWebViewBridge.cpp @@ -36,8 +36,6 @@ WebViewBridge::WebViewBridge(Vector screen_rects, float de { m_device_pixel_ratio = device_pixel_ratio; - initialize_client(CreateNewClient::Yes); - on_scroll_by_delta = [this](auto x_delta, auto y_delta) { auto position = m_viewport_rect.location(); position.set_x(position.x() + x_delta); @@ -51,11 +49,6 @@ WebViewBridge::WebViewBridge(Vector screen_rects, float de if (on_scroll) on_scroll(to_widget_position(position)); }; - - on_request_worker_agent = [this]() { - auto worker_client = MUST(launch_web_worker_process(MUST(get_paths_for_helper_process("WebWorker"sv)), m_web_content_options.certificates)); - return worker_client->dup_sockets(); - }; } WebViewBridge::~WebViewBridge() = default; @@ -150,14 +143,13 @@ Gfx::IntPoint WebViewBridge::to_widget_position(Gfx::IntPoint content_position) void WebViewBridge::initialize_client(CreateNewClient) { + VERIFY(on_request_web_content); + // FIXME: Don't create a new process when CreateNewClient is false // We should create a new tab/window in the UI instead, and re-use the existing WebContentClient object. m_client_state = {}; - auto candidate_web_content_paths = MUST(get_paths_for_helper_process("WebContent"sv)); - auto new_client = MUST(launch_web_content_process(*this, candidate_web_content_paths, m_web_content_options)); - - m_client_state.client = new_client; + m_client_state.client = on_request_web_content(); m_client_state.client->on_web_content_process_crash = [this] { Core::deferred_invoke([this] { handle_web_content_process_crash(); diff --git a/Ladybird/AppKit/UI/LadybirdWebViewBridge.h b/Ladybird/AppKit/UI/LadybirdWebViewBridge.h index 46f0125d0d..5bcc2eb75f 100644 --- a/Ladybird/AppKit/UI/LadybirdWebViewBridge.h +++ b/Ladybird/AppKit/UI/LadybirdWebViewBridge.h @@ -23,6 +23,10 @@ public: static ErrorOr> create(Vector screen_rects, float device_pixel_ratio, WebContentOptions const&, Optional webdriver_content_ipc_path, Web::CSS::PreferredColorScheme); virtual ~WebViewBridge() override; + virtual void initialize_client(CreateNewClient = CreateNewClient::Yes) override; + + WebContentOptions const& web_content_options() const { return m_web_content_options; } + float device_pixel_ratio() const { return m_device_pixel_ratio; } void set_device_pixel_ratio(float device_pixel_ratio); float inverse_device_pixel_ratio() const { return 1.0f / m_device_pixel_ratio; } @@ -47,6 +51,7 @@ public: }; Optional paintable(); + Function()> on_request_web_content; Function on_zoom_level_changed; Function on_scroll; @@ -58,8 +63,6 @@ private: virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const override; virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const override; - virtual void initialize_client(CreateNewClient) override; - Vector m_screen_rects; Gfx::IntRect m_viewport_rect; diff --git a/Ladybird/AppKit/main.mm b/Ladybird/AppKit/main.mm index ba8de34f84..a25ed505c9 100644 --- a/Ladybird/AppKit/main.mm +++ b/Ladybird/AppKit/main.mm @@ -31,7 +31,7 @@ ErrorOr serenity_main(Main::Arguments arguments) { AK::set_rich_debug_enabled(true); - [Application sharedApplication]; + Application* application = [Application sharedApplication]; Core::EventLoopManager::install(*new Ladybird::CFEventLoopManager); Core::EventLoop event_loop; @@ -71,6 +71,9 @@ ErrorOr serenity_main(Main::Arguments arguments) auto database = TRY(WebView::Database::create(move(sql_server_paths))); auto cookie_jar = TRY(WebView::CookieJar::create(*database)); + // 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]); + URL::URL new_tab_page_url = Browser::default_new_tab_url; Vector initial_urls; @@ -87,7 +90,6 @@ ErrorOr serenity_main(Main::Arguments arguments) Ladybird::WebContentOptions web_content_options { .command_line = MUST(command_line_builder.to_string()), .executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))), - .certificates = move(certificates), .enable_gpu_painting = use_gpu_painting ? Ladybird::EnableGPUPainting::Yes : Ladybird::EnableGPUPainting::No, .use_lagom_networking = Ladybird::UseLagomNetworking::Yes, .wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No, diff --git a/Ladybird/CMakeLists.txt b/Ladybird/CMakeLists.txt index d768f5d8b7..21153519c2 100644 --- a/Ladybird/CMakeLists.txt +++ b/Ladybird/CMakeLists.txt @@ -139,6 +139,7 @@ elseif (APPLE) ${SOURCES} AppKit/main.mm AppKit/Application/Application.mm + AppKit/Application/ApplicationBridge.cpp AppKit/Application/ApplicationDelegate.mm AppKit/Application/EventLoopImplementation.mm AppKit/UI/Event.mm diff --git a/Ladybird/HelperProcess.cpp b/Ladybird/HelperProcess.cpp index d05d7a7349..08cd69d9f9 100644 --- a/Ladybird/HelperProcess.cpp +++ b/Ladybird/HelperProcess.cpp @@ -12,7 +12,8 @@ ErrorOr> launch_web_content_process( WebView::ViewImplementation& view, ReadonlySpan candidate_web_content_paths, - Ladybird::WebContentOptions const& web_content_options) + Ladybird::WebContentOptions const& web_content_options, + Optional request_server_sockets) { int socket_fds[2] {}; TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds)); @@ -73,10 +74,14 @@ ErrorOr> launch_web_content_process( arguments.append("--mach-server-name"sv); arguments.append(server.value()); } - Vector certificate_args; - for (auto const& certificate : web_content_options.certificates) { - certificate_args.append(ByteString::formatted("--certificate={}", certificate)); - arguments.append(certificate_args.last().view()); + Vector fd_strings; + if (request_server_sockets.has_value()) { + arguments.append("--request-server-socket"sv); + fd_strings.append(MUST(String::number(request_server_sockets->socket.fd()))); + arguments.append(fd_strings.last()); + arguments.append("--request-server-fd-passing-socket"sv); + fd_strings.append(MUST(String::number(request_server_sockets->fd_passing_socket.fd()))); + arguments.append(fd_strings.last()); } result = Core::System::exec(arguments[0], arguments.span(), Core::System::SearchInPath::Yes); @@ -109,7 +114,7 @@ ErrorOr> launch_web_content_process( } template -ErrorOr> launch_generic_server_process(ReadonlySpan candidate_server_paths, StringView serenity_resource_root, Vector const& certificates, StringView server_name) +ErrorOr> launch_generic_server_process(ReadonlySpan candidate_server_paths, StringView server_name, Vector extra_arguments = {}) { int socket_fds[2] {}; TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds)); @@ -139,20 +144,14 @@ ErrorOr> launch_generic_server_process(ReadonlySpan { + auto arguments = Vector { path.view(), "--fd-passing-socket"sv, fd_passing_socket_string, }; - if (!serenity_resource_root.is_empty()) { - arguments.append("--serenity-resource-root"sv); - arguments.append(serenity_resource_root); - } - Vector certificate_args; - for (auto const& certificate : certificates) { - certificate_args.append(ByteString::formatted("--certificate={}", certificate)); - arguments.append(certificate_args.last().view()); - } + + if (!extra_arguments.is_empty()) + arguments.extend(extra_arguments); result = Core::System::exec(arguments[0], arguments.span(), Core::System::SearchInPath::Yes); if (!result.is_error()) @@ -180,15 +179,59 @@ ErrorOr> launch_generic_server_process(ReadonlySpan> launch_image_decoder_process(ReadonlySpan candidate_image_decoder_paths) { - return launch_generic_server_process(candidate_image_decoder_paths, ""sv, {}, "ImageDecoder"sv); + return launch_generic_server_process(candidate_image_decoder_paths, "ImageDecoder"sv); } -ErrorOr> launch_web_worker_process(ReadonlySpan candidate_web_worker_paths, Vector const& certificates) +ErrorOr> launch_web_worker_process(ReadonlySpan candidate_web_worker_paths, NonnullRefPtr request_client) { - return launch_generic_server_process(candidate_web_worker_paths, ""sv, certificates, "WebWorker"sv); + auto request_server_sockets = TRY(connect_new_request_server_client(move(request_client))); + + Vector arguments; + Vector fd_strings; + + arguments.append("--request-server-socket"sv); + fd_strings.append(MUST(String::number(request_server_sockets.socket.fd()))); + arguments.append(fd_strings.last()); + arguments.append("--request-server-fd-passing-socket"sv); + fd_strings.append(MUST(String::number(request_server_sockets.fd_passing_socket.fd()))); + arguments.append(fd_strings.last()); + + return launch_generic_server_process(candidate_web_worker_paths, "WebWorker"sv, move(arguments)); } ErrorOr> launch_request_server_process(ReadonlySpan candidate_request_server_paths, StringView serenity_resource_root, Vector const& certificates) { - return launch_generic_server_process(candidate_request_server_paths, serenity_resource_root, certificates, "RequestServer"sv); + Vector arguments; + if (!serenity_resource_root.is_empty()) { + arguments.append("--serenity-resource-root"sv); + arguments.append(serenity_resource_root); + } + Vector certificate_args; + for (auto const& certificate : certificates) { + certificate_args.append(ByteString::formatted("--certificate={}", certificate)); + arguments.append(certificate_args.last().view()); + } + + return launch_generic_server_process(candidate_request_server_paths, "RequestServer"sv, move(arguments)); +} + +ErrorOr connect_new_request_server_client(Protocol::RequestClient& client) +{ + auto new_sockets = client.send_sync_but_allow_failure(); + if (!new_sockets) + return Error::from_string_literal("Failed to connect to RequestServer"); + + auto socket = new_sockets->take_client_socket(); + auto fd_passing_socket = new_sockets->take_client_fd_passing_socket(); + + // FIXME: IPC::Files transferred over the wire are always set O_CLOEXEC during decoding. + // Perhaps we should add an option to IPC::File to allow the receiver to decide whether to + // make it O_CLOEXEC or not. Or an attribute in the .ipc file? + for (auto fd : { socket.fd(), fd_passing_socket.fd() }) { + auto fd_flags = MUST(Core::System::fcntl(fd, F_GETFD)); + fd_flags &= ~FD_CLOEXEC; + MUST(Core::System::fcntl(fd, F_SETFD, fd_flags)); + } + + return WebView::SocketPair { move(socket), move(fd_passing_socket) }; } diff --git a/Ladybird/HelperProcess.h b/Ladybird/HelperProcess.h index 2722545d24..890d71ab5b 100644 --- a/Ladybird/HelperProcess.h +++ b/Ladybird/HelperProcess.h @@ -8,19 +8,24 @@ #include "Types.h" #include +#include #include #include #include #include #include +#include #include #include ErrorOr> launch_web_content_process( WebView::ViewImplementation& view, ReadonlySpan candidate_web_content_paths, - Ladybird::WebContentOptions const&); + Ladybird::WebContentOptions const&, + Optional request_server_sockets = {}); ErrorOr> launch_image_decoder_process(ReadonlySpan candidate_image_decoder_paths); -ErrorOr> launch_web_worker_process(ReadonlySpan candidate_web_worker_paths, Vector const& certificates); +ErrorOr> launch_web_worker_process(ReadonlySpan candidate_web_worker_paths, NonnullRefPtr); 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&); diff --git a/Ladybird/Qt/Application.cpp b/Ladybird/Qt/Application.cpp index fc42597837..d305cfd03d 100644 --- a/Ladybird/Qt/Application.cpp +++ b/Ladybird/Qt/Application.cpp @@ -28,8 +28,8 @@ bool Application::event(QEvent* event) if (auto file_url = WebView::sanitize_url(file); file_url.has_value()) on_open_file(file_url.release_value()); + break; } - default: break; } diff --git a/Ladybird/Qt/Application.h b/Ladybird/Qt/Application.h index 57d8a15d8e..d0abd5dfae 100644 --- a/Ladybird/Qt/Application.h +++ b/Ladybird/Qt/Application.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -21,6 +22,7 @@ public: virtual bool event(QEvent* event) override; Function on_open_file; + RefPtr request_server_client; }; } diff --git a/Ladybird/Qt/WebContentView.cpp b/Ladybird/Qt/WebContentView.cpp index 4771bea2b6..a007d8af45 100644 --- a/Ladybird/Qt/WebContentView.cpp +++ b/Ladybird/Qt/WebContentView.cpp @@ -6,22 +6,19 @@ */ #include "WebContentView.h" +#include "Application.h" #include "StringUtils.h" #include #include #include -#include #include #include -#include #include #include #include #include -#include #include #include -#include #include #include #include @@ -30,10 +27,9 @@ #include #include #include -#include #include -#include #include +#include #include #include #include @@ -126,8 +122,9 @@ WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_con finish_handling_key_event(event); }; - on_request_worker_agent = [this]() { - auto worker_client = MUST(launch_web_worker_process(MUST(get_paths_for_helper_process("WebWorker"sv)), m_web_content_options.certificates)); + on_request_worker_agent = []() { + auto& request_server_client = static_cast(QApplication::instance())->request_server_client; + auto worker_client = MUST(launch_web_worker_process(MUST(get_paths_for_helper_process("WebWorker"sv)), *request_server_client)); return worker_client->dup_sockets(); }; } @@ -537,8 +534,17 @@ void WebContentView::initialize_client(WebView::ViewImplementation::CreateNewCli if (create_new_client == CreateNewClient::Yes) { m_client_state = {}; + Optional request_server_sockets; + if (m_web_content_options.use_lagom_networking == UseLagomNetworking::Yes) { + auto& protocol = static_cast(QApplication::instance())->request_server_client; + + // FIXME: Fail to open the tab, rather than crashing the whole application if this fails + auto sockets = connect_new_request_server_client(*protocol).release_value_but_fixme_should_propagate_errors(); + request_server_sockets = AK::move(sockets); + } + 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).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_sockets)).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 ed48a75f1e..0c0ab3f8c5 100644 --- a/Ladybird/Qt/main.cpp +++ b/Ladybird/Qt/main.cpp @@ -132,12 +132,17 @@ ErrorOr serenity_main(Main::Arguments arguments) initial_urls.append(ak_string_from_qstring(new_tab_page)); } + // NOTE: WebWorker *always* needs a request server connection, even if WebContent uses Qt Networking + // 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; + StringBuilder command_line_builder; command_line_builder.join(' ', arguments.strings); Ladybird::WebContentOptions web_content_options { .command_line = MUST(command_line_builder.to_string()), .executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))), - .certificates = move(certificates), .enable_callgrind_profiling = enable_callgrind_profiling ? Ladybird::EnableCallgrindProfiling::Yes : Ladybird::EnableCallgrindProfiling::No, .enable_gpu_painting = use_gpu_painting ? Ladybird::EnableGPUPainting::Yes : Ladybird::EnableGPUPainting::No, .use_lagom_networking = enable_qt_networking ? Ladybird::UseLagomNetworking::No : Ladybird::UseLagomNetworking::Yes, diff --git a/Ladybird/Types.h b/Ladybird/Types.h index b9911e7974..476c735197 100644 --- a/Ladybird/Types.h +++ b/Ladybird/Types.h @@ -48,7 +48,6 @@ enum class EnableIDLTracing { struct WebContentOptions { String command_line; String executable_path; - Vector certificates; EnableCallgrindProfiling enable_callgrind_profiling { EnableCallgrindProfiling::No }; EnableGPUPainting enable_gpu_painting { EnableGPUPainting::No }; IsLayoutTestMode is_layout_test_mode { IsLayoutTestMode::No }; diff --git a/Ladybird/WebContent/main.cpp b/Ladybird/WebContent/main.cpp index 2f1a5263d9..77f5f45b64 100644 --- a/Ladybird/WebContent/main.cpp +++ b/Ladybird/WebContent/main.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -20,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +51,7 @@ static ErrorOr load_content_filters(); static ErrorOr load_autoplay_allowlist(); -static ErrorOr initialize_lagom_networking(Vector const& certificates); +static ErrorOr initialize_lagom_networking(int request_server_socket, int request_server_fd_passing_socket); namespace JS { extern bool g_log_all_js_exceptions; @@ -93,6 +93,8 @@ ErrorOr serenity_main(Main::Arguments arguments) StringView mach_server_name {}; Vector certificates; int webcontent_fd_passing_socket { -1 }; + int request_server_socket { -1 }; + int request_server_fd_passing_socket { -1 }; bool is_layout_test_mode = false; bool use_lagom_networking = false; bool use_gpu_painting = false; @@ -103,8 +105,9 @@ ErrorOr serenity_main(Main::Arguments arguments) Core::ArgsParser args_parser; 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(certificates, "Path to a certificate file", "certificate", 'C', "certificate"); args_parser.add_option(webcontent_fd_passing_socket, "File descriptor of the passing socket for the WebContent connection", "webcontent-fd-passing-socket", 'c', "webcontent_fd_passing_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(request_server_fd_passing_socket, "File descriptor of the fd passing socket for the RequestServer connection", "request-server-fd-passing-socket", 'f', "request_server_fd_passing_socket"); args_parser.add_option(is_layout_test_mode, "Is layout test mode", "layout-test-mode", 0); args_parser.add_option(use_lagom_networking, "Enable Lagom servers for networking", "use-lagom-networking", 0); args_parser.add_option(use_gpu_painting, "Enable GPU painting", "use-gpu-painting", 0); @@ -136,7 +139,7 @@ ErrorOr serenity_main(Main::Arguments arguments) Web::ResourceLoader::initialize(Ladybird::RequestManagerQt::create()); else #endif - TRY(initialize_lagom_networking(certificates)); + TRY(initialize_lagom_networking(request_server_socket, request_server_fd_passing_socket)); Web::HTML::Window::set_internals_object_exposed(is_layout_test_mode); @@ -217,11 +220,14 @@ static ErrorOr load_autoplay_allowlist() return {}; } -static ErrorOr initialize_lagom_networking(Vector const& certificates) +ErrorOr initialize_lagom_networking(int request_server_socket, int request_server_fd_passing_socket) { - auto candidate_request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv)); - auto request_server_client = TRY(launch_request_server_process(candidate_request_server_paths, s_serenity_resource_root, certificates)); - Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(request_server_client)))); + auto socket = TRY(Core::LocalSocket::adopt_fd(request_server_socket)); + TRY(socket->set_blocking(true)); + auto new_client = TRY(try_make_ref_counted(move(socket))); + new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(request_server_fd_passing_socket))); + + Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(new_client)))); return {}; } diff --git a/Ladybird/WebWorker/main.cpp b/Ladybird/WebWorker/main.cpp index 95f58dde49..059bdc5e5c 100644 --- a/Ladybird/WebWorker/main.cpp +++ b/Ladybird/WebWorker/main.cpp @@ -26,20 +26,22 @@ #include #include -static ErrorOr initialize_lagom_networking(Vector const& certificates); +static ErrorOr initialize_lagom_networking(int request_server_socket, int request_server_fd_passing_socket); ErrorOr serenity_main(Main::Arguments arguments) { AK::set_rich_debug_enabled(true); int fd_passing_socket { -1 }; + int request_server_socket { -1 }; + int request_server_fd_passing_socket { -1 }; StringView serenity_resource_root; - Vector certificates; Core::ArgsParser args_parser; args_parser.add_option(fd_passing_socket, "File descriptor of the fd passing socket", "fd-passing-socket", 'c', "fd-passing-socket"); + args_parser.add_option(request_server_socket, "File descriptor of the request server socket", "request-server-socket", 's', "request-server-socket"); + args_parser.add_option(request_server_fd_passing_socket, "File descriptor of the request server fd passing socket", "request-server-fd-passing-socket", 'f', "request-server-fd-passing-socket"); args_parser.add_option(serenity_resource_root, "Absolute path to directory for serenity resources", "serenity-resource-root", 'r', "serenity-resource-root"); - args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate"); args_parser.parse(arguments); platform_init(); @@ -49,7 +51,7 @@ ErrorOr serenity_main(Main::Arguments arguments) Web::Platform::FontPlugin::install(*new Web::Platform::FontPluginSerenity); - TRY(initialize_lagom_networking(certificates)); + TRY(initialize_lagom_networking(request_server_socket, request_server_fd_passing_socket)); VERIFY(fd_passing_socket >= 0); @@ -61,11 +63,15 @@ ErrorOr serenity_main(Main::Arguments arguments) return event_loop.exec(); } -static ErrorOr initialize_lagom_networking(Vector const& certificates) +static ErrorOr initialize_lagom_networking(int request_server_socket, int request_server_fd_passing_socket) { - auto candidate_request_server_paths = TRY(get_paths_for_helper_process("RequestServer"sv)); - auto request_server_client = TRY(launch_request_server_process(candidate_request_server_paths, s_serenity_resource_root, certificates)); - Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(request_server_client)))); + auto socket = TRY(Core::LocalSocket::adopt_fd(request_server_socket)); + TRY(socket->set_blocking(true)); + + auto new_client = TRY(try_make_ref_counted(move(socket))); + new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(request_server_fd_passing_socket))); + + Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create(move(new_client)))); return {}; } diff --git a/Meta/gn/secondary/Ladybird/BUILD.gn b/Meta/gn/secondary/Ladybird/BUILD.gn index 6fdee57288..226aea3af8 100644 --- a/Meta/gn/secondary/Ladybird/BUILD.gn +++ b/Meta/gn/secondary/Ladybird/BUILD.gn @@ -115,6 +115,7 @@ executable("ladybird_executable") { } else { sources += [ "AppKit/Application/Application.mm", + "AppKit/Application/ApplicationBridge.cpp", "AppKit/Application/ApplicationDelegate.mm", "AppKit/Application/EventLoopImplementation.mm", "AppKit/UI/Event.mm", diff --git a/Userland/Utilities/headless-browser.cpp b/Userland/Utilities/headless-browser.cpp index db637e952b..66ef637b33 100644 --- a/Userland/Utilities/headless-browser.cpp +++ b/Userland/Utilities/headless-browser.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -64,34 +65,41 @@ static StringView s_current_test_path; class HeadlessWebContentView final : public WebView::ViewImplementation { 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 = {}) + 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; + #if defined(AK_OS_SERENITY) auto database = TRY(WebView::Database::create()); + (void)resources_folder; + (void)certificates; #else auto sql_server_paths = TRY(get_paths_for_helper_process("SQLServer"sv)); auto database = TRY(WebView::Database::create(move(sql_server_paths))); + + 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)); #endif auto cookie_jar = TRY(WebView::CookieJar::create(*database)); - auto view = TRY(adopt_nonnull_own_or_enomem(new (nothrow) HeadlessWebContentView(move(database), move(cookie_jar), certificates))); + auto view = TRY(adopt_nonnull_own_or_enomem(new (nothrow) HeadlessWebContentView(move(database), move(cookie_jar), request_client))); #if defined(AK_OS_SERENITY) view->m_client_state.client = TRY(WebView::WebContentClient::try_create(*view)); (void)command_line; - (void)certificates; (void)is_layout_test_mode; #else Ladybird::WebContentOptions web_content_options { .command_line = command_line, .executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))), - .certificates = certificates, .is_layout_test_mode = is_layout_test_mode, }; + auto request_server_sockets = TRY(connect_new_request_server_client(*request_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)); + view->m_client_state.client = TRY(launch_web_content_process(*view, candidate_web_content_paths, web_content_options, move(request_server_sockets))); #endif view->client().async_update_system_theme(0, move(theme)); @@ -155,10 +163,10 @@ public: } private: - HeadlessWebContentView(NonnullRefPtr database, WebView::CookieJar cookie_jar, Vector certificates) + HeadlessWebContentView(NonnullRefPtr database, WebView::CookieJar cookie_jar, RefPtr request_client = nullptr) : m_database(move(database)) , m_cookie_jar(move(cookie_jar)) - , m_certificates(move(certificates)) + , m_request_client(move(request_client)) { on_scroll_to_point = [this](auto position) { m_viewport_rect.set_location(position); @@ -186,7 +194,7 @@ private: auto worker_client = MUST(Web::HTML::WebWorkerClient::try_create()); (void)this; #else - auto worker_client = MUST(launch_web_worker_process(MUST(get_paths_for_helper_process("WebWorker"sv)), m_certificates)); + auto worker_client = MUST(launch_web_worker_process(MUST(get_paths_for_helper_process("WebWorker"sv)), *m_request_client)); #endif return worker_client->dup_sockets(); }; @@ -205,7 +213,7 @@ private: NonnullRefPtr m_database; WebView::CookieJar m_cookie_jar; - Vector m_certificates; + RefPtr m_request_client; }; static ErrorOr> load_page_for_screenshot_and_exit(Core::EventLoop& event_loop, HeadlessWebContentView& view, URL::URL url, int screenshot_timeout) @@ -690,7 +698,7 @@ ErrorOr serenity_main(Main::Arguments arguments) StringBuilder command_line_builder; command_line_builder.join(' ', arguments.strings); - auto view = TRY(HeadlessWebContentView::create(move(theme), window_size, MUST(command_line_builder.to_string()), web_driver_ipc_path, is_layout_test_mode ? Ladybird::IsLayoutTestMode::Yes : Ladybird::IsLayoutTestMode::No, certificates)); + auto view = TRY(HeadlessWebContentView::create(move(theme), window_size, MUST(command_line_builder.to_string()), web_driver_ipc_path, is_layout_test_mode ? Ladybird::IsLayoutTestMode::Yes : Ladybird::IsLayoutTestMode::No, certificates, resources_folder)); if (!test_root_path.is_empty()) { test_glob = ByteString::formatted("*{}*", test_glob);