From 956fa84f1258dbd9bfb9fcdb934f069423e875f3 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 15 Dec 2022 08:46:01 -0500 Subject: [PATCH] WebDriver: Specify callbacks for clients to launch browser windows This moves the actual launching of browser windows to the WebDriver main file. This will allow Ladybird to specify its own callback and re-use Serenity's Session class. --- Userland/Services/WebDriver/Client.cpp | 12 +++++++---- Userland/Services/WebDriver/Client.h | 12 +++++++++-- Userland/Services/WebDriver/Session.cpp | 26 +++++------------------- Userland/Services/WebDriver/Session.h | 4 +++- Userland/Services/WebDriver/main.cpp | 27 ++++++++++++++++++++++++- 5 files changed, 52 insertions(+), 29 deletions(-) diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp index 829c1cc8f5..85bcd53aa9 100644 --- a/Userland/Services/WebDriver/Client.cpp +++ b/Userland/Services/WebDriver/Client.cpp @@ -20,14 +20,18 @@ namespace WebDriver { Atomic Client::s_next_session_id; NonnullOwnPtrVector Client::s_sessions; -ErrorOr> Client::try_create(NonnullOwnPtr socket, Core::Object* parent) +ErrorOr> Client::try_create(NonnullOwnPtr socket, LaunchBrowserCallbacks callbacks, Core::Object* parent) { + if (!callbacks.launch_browser || !callbacks.launch_headless_browser) + return Error::from_string_view("All callbacks to launch a browser must be provided"sv); + TRY(socket->set_blocking(true)); - return adopt_nonnull_ref_or_enomem(new (nothrow) Client(move(socket), parent)); + return adopt_nonnull_ref_or_enomem(new (nothrow) Client(move(socket), move(callbacks), parent)); } -Client::Client(NonnullOwnPtr socket, Core::Object* parent) +Client::Client(NonnullOwnPtr socket, LaunchBrowserCallbacks callbacks, Core::Object* parent) : Web::WebDriver::Client(move(socket), parent) + , m_callbacks(move(callbacks)) { } @@ -154,7 +158,7 @@ Web::WebDriver::Response Client::new_session(Web::WebDriver::Parameters, JsonVal Web::WebDriver::LadybirdOptions options { capabilities.as_object() }; auto session = make(session_id, *this, move(options)); - if (auto start_result = session->start(); start_result.is_error()) + if (auto start_result = session->start(m_callbacks); start_result.is_error()) return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::SessionNotCreated, DeprecatedString::formatted("Failed to start session: {}", start_result.error().string_literal())); auto& web_content_connection = session->web_content_connection(); diff --git a/Userland/Services/WebDriver/Client.h b/Userland/Services/WebDriver/Client.h index d590ba87ff..bfc1728c42 100644 --- a/Userland/Services/WebDriver/Client.h +++ b/Userland/Services/WebDriver/Client.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -18,17 +19,22 @@ namespace WebDriver { +struct LaunchBrowserCallbacks { + Function(DeprecatedString const&)> launch_browser; + Function(DeprecatedString const&)> launch_headless_browser; +}; + class Client final : public Web::WebDriver::Client { C_OBJECT_ABSTRACT(Client); public: - static ErrorOr> try_create(NonnullOwnPtr, Core::Object* parent); + static ErrorOr> try_create(NonnullOwnPtr, LaunchBrowserCallbacks, Core::Object* parent); virtual ~Client() override; void close_session(unsigned session_id); private: - Client(NonnullOwnPtr, Core::Object* parent); + Client(NonnullOwnPtr, LaunchBrowserCallbacks, Core::Object* parent); ErrorOr find_session_with_id(StringView session_id); ErrorOr, Web::WebDriver::Error> take_session_with_id(StringView session_id); @@ -87,6 +93,8 @@ private: static NonnullOwnPtrVector s_sessions; static Atomic s_next_session_id; + + LaunchBrowserCallbacks m_callbacks; }; } diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index 46692dc359..08d4deb4fa 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -58,33 +58,17 @@ ErrorOr> Session::create_server(DeprecatedStrin return server; } -ErrorOr Session::start() +ErrorOr Session::start(LaunchBrowserCallbacks const& callbacks) { auto promise = TRY(ServerPromise::try_create()); auto web_content_socket_path = DeprecatedString::formatted("{}/webdriver/session_{}_{}", TRY(Core::StandardPaths::runtime_directory()), getpid(), m_id); auto web_content_server = TRY(create_server(web_content_socket_path, promise)); - if (m_options.headless) { - char const* argv[] = { - "/bin/headless-browser", - "--webdriver-ipc-path", - web_content_socket_path.characters(), - "about:blank", - nullptr, - }; - - m_browser_pid = TRY(Core::System::posix_spawn("/bin/headless-browser"sv, nullptr, nullptr, const_cast(argv), environ)); - } else { - char const* argv[] = { - "/bin/Browser", - "--webdriver-content-path", - web_content_socket_path.characters(), - nullptr, - }; - - m_browser_pid = TRY(Core::System::posix_spawn("/bin/Browser"sv, nullptr, nullptr, const_cast(argv), environ)); - } + if (m_options.headless) + m_browser_pid = TRY(callbacks.launch_headless_browser(web_content_socket_path)); + else + m_browser_pid = TRY(callbacks.launch_browser(web_content_socket_path)); // FIXME: Allow this to be more asynchronous. For now, this at least allows us to propagate // errors received while accepting the Browser and WebContent sockets. diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index 31e55a7be7..426df466bd 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -19,6 +19,8 @@ namespace WebDriver { +struct LaunchBrowserCallbacks; + class Session { public: Session(unsigned session_id, NonnullRefPtr client, Web::WebDriver::LadybirdOptions options); @@ -32,7 +34,7 @@ public: return *m_web_content_connection; } - ErrorOr start(); + ErrorOr start(LaunchBrowserCallbacks const&); Web::WebDriver::Response stop(); private: diff --git a/Userland/Services/WebDriver/main.cpp b/Userland/Services/WebDriver/main.cpp index bebf3f7577..29a4eeb242 100644 --- a/Userland/Services/WebDriver/main.cpp +++ b/Userland/Services/WebDriver/main.cpp @@ -13,6 +13,31 @@ #include #include +static ErrorOr launch_browser(DeprecatedString const& socket_path) +{ + char const* argv[] = { + "/bin/Browser", + "--webdriver-content-path", + socket_path.characters(), + nullptr, + }; + + return Core::System::posix_spawn("/bin/Browser"sv, nullptr, nullptr, const_cast(argv), environ); +} + +static ErrorOr launch_headless_browser(DeprecatedString const& socket_path) +{ + char const* argv[] = { + "/bin/headless-browser", + "--webdriver-ipc-path", + socket_path.characters(), + "about:blank", + nullptr, + }; + + return Core::System::posix_spawn("/bin/headless-browser"sv, nullptr, nullptr, const_cast(argv), environ); +} + ErrorOr serenity_main(Main::Arguments arguments) { DeprecatedString default_listen_address = "0.0.0.0"; @@ -62,7 +87,7 @@ ErrorOr serenity_main(Main::Arguments arguments) return; } - auto maybe_client = WebDriver::Client::try_create(maybe_buffered_socket.release_value(), server); + auto maybe_client = WebDriver::Client::try_create(maybe_buffered_socket.release_value(), { launch_browser, launch_headless_browser }, server); if (maybe_client.is_error()) { warnln("Could not create a WebDriver client: {}", maybe_client.error()); return;