diff options
-rw-r--r-- | Userland/Applications/Browser/Browser.h | 1 | ||||
-rw-r--r-- | Userland/Applications/Browser/Tab.cpp | 5 | ||||
-rw-r--r-- | Userland/Applications/Browser/main.cpp | 19 | ||||
-rw-r--r-- | Userland/Libraries/LibWebView/OutOfProcessWebView.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibWebView/OutOfProcessWebView.h | 2 | ||||
-rw-r--r-- | Userland/Services/WebDriver/CMakeLists.txt | 4 | ||||
-rw-r--r-- | Userland/Services/WebDriver/Session.cpp | 78 | ||||
-rw-r--r-- | Userland/Services/WebDriver/Session.h | 11 | ||||
-rw-r--r-- | Userland/Services/WebDriver/WebContentConnection.cpp | 25 | ||||
-rw-r--r-- | Userland/Services/WebDriver/WebContentConnection.h | 31 | ||||
-rw-r--r-- | Userland/Services/WebDriver/main.cpp | 6 |
11 files changed, 151 insertions, 35 deletions
diff --git a/Userland/Applications/Browser/Browser.h b/Userland/Applications/Browser/Browser.h index f637073014..ad901076c3 100644 --- a/Userland/Applications/Browser/Browser.h +++ b/Userland/Applications/Browser/Browser.h @@ -21,5 +21,6 @@ extern HashMap<String, size_t> g_proxy_mappings; extern bool g_content_filters_enabled; extern IconBag g_icon_bag; extern RefPtr<Browser::WebDriverConnection> g_web_driver_connection; +extern String g_webdriver_content_ipc_path; } diff --git a/Userland/Applications/Browser/Tab.cpp b/Userland/Applications/Browser/Tab.cpp index 8141f1c0d8..4559f7a3ad 100644 --- a/Userland/Applications/Browser/Tab.cpp +++ b/Userland/Applications/Browser/Tab.cpp @@ -126,8 +126,7 @@ Tab::Tab(BrowserWindow& window) m_web_content_view->set_content_filters({}); m_web_content_view->set_proxy_mappings(g_proxies, g_proxy_mappings); - - if (!g_web_driver_connection.is_null()) + if (!g_webdriver_content_ipc_path.is_empty()) enable_webdriver_mode(); auto& go_back_button = toolbar.add_action(window.go_back_action()); @@ -668,7 +667,7 @@ void Tab::hide_event(GUI::HideEvent&) void Tab::enable_webdriver_mode() { - m_web_content_view->set_is_webdriver_active(true); + m_web_content_view->connect_to_webdriver(Browser::g_webdriver_content_ipc_path); auto& webdriver_banner = *find_descendant_of_type_named<GUI::Widget>("webdriver_banner"); webdriver_banner.set_visible(true); } diff --git a/Userland/Applications/Browser/main.cpp b/Userland/Applications/Browser/main.cpp index 85126f7a3d..aee870bdf6 100644 --- a/Userland/Applications/Browser/main.cpp +++ b/Userland/Applications/Browser/main.cpp @@ -41,6 +41,7 @@ Vector<String> g_proxies; HashMap<String, size_t> g_proxy_mappings; IconBag g_icon_bag; RefPtr<Browser::WebDriverConnection> g_web_driver_connection; +String g_webdriver_content_ipc_path; } @@ -68,11 +69,12 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) TRY(Core::System::pledge("stdio recvfd sendfd unix fattr cpath rpath wpath proc exec")); Vector<String> specified_urls; - String webdriver_ipc_path; + String webdriver_browser_ipc_path; Core::ArgsParser args_parser; args_parser.add_positional_argument(specified_urls, "URLs to open", "url", Core::ArgsParser::Required::No); - args_parser.add_option(webdriver_ipc_path, "Path to WebDriver IPC", "webdriver", 0, "path"); + args_parser.add_option(webdriver_browser_ipc_path, "Path to WebDriver IPC file for Browser", "webdriver-browser-path", 0, "path"); + args_parser.add_option(Browser::g_webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path"); args_parser.parse(arguments); @@ -87,9 +89,9 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) TRY(Desktop::Launcher::add_allowed_url(URL::create_with_file_scheme(Core::StandardPaths::downloads_directory()))); TRY(Desktop::Launcher::seal_allowlist()); - if (!webdriver_ipc_path.is_empty()) { + if (!webdriver_browser_ipc_path.is_empty()) { specified_urls.empend("about:blank"); - TRY(Core::System::unveil(webdriver_ipc_path.view(), "rw"sv)); + TRY(Core::System::unveil(webdriver_browser_ipc_path, "rw"sv)); } TRY(Core::System::unveil("/sys/kernel/processes", "r")); @@ -147,13 +149,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) Browser::CookieJar cookie_jar; auto window = Browser::BrowserWindow::construct(cookie_jar, first_url); - if (!webdriver_ipc_path.is_empty()) { - Browser::g_web_driver_connection = TRY(Browser::WebDriverConnection::connect_to_webdriver(window, webdriver_ipc_path)); - - // The first tab is created with the BrowserWindow above, so we have to do this - // manually once after establishing the connection. - window->active_tab().enable_webdriver_mode(); - } + if (!webdriver_browser_ipc_path.is_empty()) + Browser::g_web_driver_connection = TRY(Browser::WebDriverConnection::connect_to_webdriver(window, webdriver_browser_ipc_path)); auto content_filters_watcher = TRY(Core::FileWatcher::create()); content_filters_watcher->on_change = [&](Core::FileWatcherEvent const&) { diff --git a/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp b/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp index d04df78f99..a354601df4 100644 --- a/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp +++ b/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp @@ -595,9 +595,9 @@ void OutOfProcessWebView::set_preferred_color_scheme(Web::CSS::PreferredColorSch client().async_set_preferred_color_scheme(color_scheme); } -void OutOfProcessWebView::set_is_webdriver_active(bool is_webdriver_enabled) +void OutOfProcessWebView::connect_to_webdriver(String const& webdriver_ipc_path) { - client().async_set_is_webdriver_active(is_webdriver_enabled); + client().async_connect_to_webdriver(webdriver_ipc_path); } void OutOfProcessWebView::set_window_position(Gfx::IntPoint const& position) diff --git a/Userland/Libraries/LibWebView/OutOfProcessWebView.h b/Userland/Libraries/LibWebView/OutOfProcessWebView.h index abf43cc71e..a2d24d455b 100644 --- a/Userland/Libraries/LibWebView/OutOfProcessWebView.h +++ b/Userland/Libraries/LibWebView/OutOfProcessWebView.h @@ -79,7 +79,7 @@ public: void set_content_filters(Vector<String>); void set_proxy_mappings(Vector<String> proxies, HashMap<String, size_t> mappings); void set_preferred_color_scheme(Web::CSS::PreferredColorScheme); - void set_is_webdriver_active(bool); + void connect_to_webdriver(String const& webdriver_ipc_path); void set_window_position(Gfx::IntPoint const&); void set_window_size(Gfx::IntSize const&); diff --git a/Userland/Services/WebDriver/CMakeLists.txt b/Userland/Services/WebDriver/CMakeLists.txt index 61bda04597..2a2787f145 100644 --- a/Userland/Services/WebDriver/CMakeLists.txt +++ b/Userland/Services/WebDriver/CMakeLists.txt @@ -1,6 +1,7 @@ serenity_component( WebDriver TARGETS WebDriver + DEPENDS WebContent ) set(SOURCES @@ -8,12 +9,15 @@ set(SOURCES Client.cpp Session.cpp TimeoutsConfiguration.cpp + WebContentConnection.cpp main.cpp ) set(GENERATED_SOURCES ../../Applications/Browser/WebDriverSessionClientEndpoint.h ../../Applications/Browser/WebDriverSessionServerEndpoint.h + ../../Services/WebContent/WebDriverClientEndpoint.h + ../../Services/WebContent/WebDriverServerEndpoint.h ) serenity_bin(WebDriver) diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index a36801ed7a..6046a69394 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -59,29 +59,75 @@ ErrorOr<void, Web::WebDriver::Error> Session::check_for_open_top_level_browsing_ return {}; } -ErrorOr<void> Session::start() +ErrorOr<NonnullRefPtr<Core::LocalServer>> Session::create_server(String const& socket_path, ServerType type, NonnullRefPtr<ServerPromise> promise) { - auto socket_path = String::formatted("/tmp/browser_webdriver_{}_{}", getpid(), m_id); dbgln("Listening for WebDriver connection on {}", socket_path); - // FIXME: Use Core::LocalServer - struct sockaddr_un addr; - int listen_socket = TRY(Core::System::socket(AF_UNIX, SOCK_STREAM, 0)); - ::memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - ::strncpy(addr.sun_path, socket_path.characters(), sizeof(addr.sun_path) - 1); + auto server = TRY(Core::LocalServer::try_create()); + server->listen(socket_path); + + server->on_accept = [this, type, promise](auto client_socket) mutable { + switch (type) { + case ServerType::Browser: { + auto maybe_connection = adopt_nonnull_ref_or_enomem(new (nothrow) BrowserConnection(move(client_socket), m_client, session_id())); + if (maybe_connection.is_error()) { + promise->resolve(maybe_connection.release_error()); + return; + } + + dbgln("WebDriver is connected to Browser socket"); + m_browser_connection = maybe_connection.release_value(); + break; + } + + case ServerType::WebContent: { + auto maybe_connection = adopt_nonnull_ref_or_enomem(new (nothrow) WebContentConnection(move(client_socket), m_client, session_id())); + if (maybe_connection.is_error()) { + promise->resolve(maybe_connection.release_error()); + return; + } + + dbgln("WebDriver is connected to WebContent socket"); + m_web_content_connection = maybe_connection.release_value(); + break; + } + } + + if (m_browser_connection && m_web_content_connection) + promise->resolve({}); + }; + + server->on_accept_error = [promise](auto error) mutable { + promise->resolve(move(error)); + }; + + return server; +} - TRY(Core::System::bind(listen_socket, (const struct sockaddr*)&addr, sizeof(struct sockaddr_un))); - TRY(Core::System::listen(listen_socket, 1)); +ErrorOr<void> Session::start() +{ + auto promise = TRY(ServerPromise::try_create()); + + auto browser_socket_path = String::formatted("/tmp/webdriver/browser_{}_{}", getpid(), m_id); + auto browser_server = TRY(create_server(browser_socket_path, ServerType::Browser, promise)); + + auto web_content_socket_path = String::formatted("/tmp/webdriver/content_{}_{}", getpid(), m_id); + auto web_content_server = TRY(create_server(web_content_socket_path, ServerType::WebContent, promise)); + + char const* argv[] = { + "/bin/Browser", + "--webdriver-browser-path", + browser_socket_path.characters(), + "--webdriver-content-path", + web_content_socket_path.characters(), + nullptr, + }; - char const* argv[] = { "/bin/Browser", "--webdriver", socket_path.characters(), nullptr }; TRY(Core::System::posix_spawn("/bin/Browser"sv, nullptr, nullptr, const_cast<char**>(argv), environ)); - int data_socket = TRY(Core::System::accept(listen_socket, nullptr, nullptr)); - auto socket = TRY(Core::Stream::LocalSocket::adopt_fd(data_socket)); - TRY(socket->set_blocking(true)); - m_browser_connection = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) BrowserConnection(move(socket), m_client, session_id()))); - dbgln("Browser is connected"); + // FIXME: Allow this to be more asynchronous. For now, this at least allows us to propogate + // errors received while accepting the Browser and WebContent sockets. + TRY(promise->await()); m_started = true; m_windows.set("main", make<Session::Window>("main", true)); diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index 7a1d33bf91..8e7885fdfd 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -11,10 +11,12 @@ #include <AK/Error.h> #include <AK/JsonValue.h> #include <AK/RefPtr.h> +#include <LibCore/Promise.h> #include <LibWeb/WebDriver/Error.h> #include <LibWeb/WebDriver/Response.h> #include <WebDriver/BrowserConnection.h> #include <WebDriver/TimeoutsConfiguration.h> +#include <WebDriver/WebContentConnection.h> #include <unistd.h> namespace WebDriver { @@ -97,13 +99,20 @@ private: ErrorOr<Vector<LocalElement>, Web::WebDriver::Error> locator_strategy_tag_name(LocalElement const&, StringView); ErrorOr<Vector<LocalElement>, Web::WebDriver::Error> locator_strategy_x_path(LocalElement const&, StringView); + enum class ServerType { + Browser, + WebContent, + }; + using ServerPromise = Core::Promise<ErrorOr<void>>; + ErrorOr<NonnullRefPtr<Core::LocalServer>> create_server(String const& socket_path, ServerType type, NonnullRefPtr<ServerPromise> promise); + NonnullRefPtr<Client> m_client; bool m_started { false }; unsigned m_id { 0 }; HashMap<String, NonnullOwnPtr<Window>> m_windows; String m_current_window_handle; - RefPtr<Core::LocalServer> m_local_server; RefPtr<BrowserConnection> m_browser_connection; + RefPtr<WebContentConnection> m_web_content_connection; // https://w3c.github.io/webdriver/#dfn-session-script-timeout TimeoutsConfiguration m_timeouts_configuration; diff --git a/Userland/Services/WebDriver/WebContentConnection.cpp b/Userland/Services/WebDriver/WebContentConnection.cpp new file mode 100644 index 0000000000..4c5560819e --- /dev/null +++ b/Userland/Services/WebDriver/WebContentConnection.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <Services/WebDriver/Client.h> +#include <Services/WebDriver/WebContentConnection.h> + +namespace WebDriver { + +WebContentConnection::WebContentConnection(NonnullOwnPtr<Core::Stream::LocalSocket> socket, NonnullRefPtr<Client> client, unsigned session_id) + : IPC::ConnectionFromClient<WebDriverClientEndpoint, WebDriverServerEndpoint>(*this, move(socket), 1) + , m_client(move(client)) + , m_session_id(session_id) +{ +} + +void WebContentConnection::die() +{ + dbgln_if(WEBDRIVER_DEBUG, "Session {} was closed remotely. Shutting down...", m_session_id); + m_client->close_session(m_session_id); +} + +} diff --git a/Userland/Services/WebDriver/WebContentConnection.h b/Userland/Services/WebDriver/WebContentConnection.h new file mode 100644 index 0000000000..b65d4b3f47 --- /dev/null +++ b/Userland/Services/WebDriver/WebContentConnection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibCore/Stream.h> +#include <LibIPC/ConnectionFromClient.h> +#include <Services/WebContent/WebDriverClientEndpoint.h> +#include <Services/WebContent/WebDriverServerEndpoint.h> + +namespace WebDriver { + +class Client; + +class WebContentConnection + : public IPC::ConnectionFromClient<WebDriverClientEndpoint, WebDriverServerEndpoint> { + C_OBJECT_ABSTRACT(WebContentConnection) +public: + WebContentConnection(NonnullOwnPtr<Core::Stream::LocalSocket> socket, NonnullRefPtr<Client> client, unsigned session_id); + + virtual void die() override; + +private: + NonnullRefPtr<Client> m_client; + unsigned m_session_id { 0 }; +}; + +} diff --git a/Userland/Services/WebDriver/main.cpp b/Userland/Services/WebDriver/main.cpp index 01965aaeb7..50adadc772 100644 --- a/Userland/Services/WebDriver/main.cpp +++ b/Userland/Services/WebDriver/main.cpp @@ -5,6 +5,7 @@ */ #include <LibCore/ArgsParser.h> +#include <LibCore/Directory.h> #include <LibCore/EventLoop.h> #include <LibCore/System.h> #include <LibCore/TCPServer.h> @@ -35,6 +36,9 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) return 1; } + TRY(Core::System::pledge("stdio accept cpath rpath recvfd inet unix proc exec fattr")); + + TRY(Core::Directory::create("/tmp/webdriver"sv, Core::Directory::CreateDirectories::Yes)); TRY(Core::System::pledge("stdio accept rpath recvfd inet unix proc exec fattr")); Core::EventLoop loop; @@ -67,7 +71,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) TRY(Core::System::unveil("/bin/Browser", "rx")); TRY(Core::System::unveil("/etc/timezone", "r")); TRY(Core::System::unveil("/res/icons", "r")); - TRY(Core::System::unveil("/tmp", "rwc")); + TRY(Core::System::unveil("/tmp/webdriver", "rwc")); TRY(Core::System::unveil(nullptr, nullptr)); TRY(Core::System::pledge("stdio accept rpath recvfd unix proc exec fattr")); |