diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-11-08 10:18:11 -0500 |
---|---|---|
committer | Tim Flynn <trflynn89@pm.me> | 2022-11-08 19:58:34 -0500 |
commit | 50ae1ad18a0fa0efa6fed6f10ab6c78e852c9b0a (patch) | |
tree | b1fb850f34b20ba163737901d5aa586dc535dbd0 | |
parent | 6b014489d406c31a727e88ca304e60513f832021 (diff) | |
download | serenity-50ae1ad18a0fa0efa6fed6f10ab6c78e852c9b0a.zip |
Browser+LibWebView+WebDriver: Connect WebDriver to WebContent
First, this moves the WebDriver socket to the /tmp/websocket/ directory,
as WebDriver now creates multiple sockets per session. Those sockets are
now created with Core::LocalServer rather than manually setting up the
listening sockets (this was an existing FIXME which resolved some issues
I was hitting with creating a second listening socket).
WebDriver passes both socket paths to Browser via command line. Browser
continues to connect itself via one socket path, then forwards the other
socket path to the WebContent process created by the OOPWV. WebContent
then connects to WebDriver over this path.
WebContent will temporarily set the navigator.webdriver flag to true
after connecting to WebDriver. This will soon be moved to its own IPC to
be sent by WebDriver.
-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")); |