diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2022-10-12 10:50:33 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-10-12 23:07:42 +0200 |
commit | 8c0f1da9f75ff5790d32ad58250a914178d16a17 (patch) | |
tree | 8e216eb389f6dc3e1c77a0ce1a520388967e23ca /Userland | |
parent | ec9c11667f5773de18047525cfc6613f875b8ab9 (diff) | |
download | serenity-8c0f1da9f75ff5790d32ad58250a914178d16a17.zip |
Browser: Add a basic WebDriver API
This adds a new option "--webdriver" that opens a local unix socket
in /tmp/browser_{pid} which the WebDriver server can use to send
commands to the Browser instance.
Co-authored-by: Florent Castelli <florent.castelli@gmail.com>
Diffstat (limited to 'Userland')
6 files changed, 129 insertions, 1 deletions
diff --git a/Userland/Applications/Browser/CMakeLists.txt b/Userland/Applications/Browser/CMakeLists.txt index e5956a02e9..caa300669a 100644 --- a/Userland/Applications/Browser/CMakeLists.txt +++ b/Userland/Applications/Browser/CMakeLists.txt @@ -5,6 +5,9 @@ serenity_component( DEPENDS BrowserSettings ImageDecoder RequestServer WebContent WebSocket ) +compile_ipc(WebDriverSessionServer.ipc WebDriverSessionServerEndpoint.h) +compile_ipc(WebDriverSessionClient.ipc WebDriverSessionClientEndpoint.h) + compile_gml(BrowserWindow.gml BrowserWindowGML.h browser_window_gml) compile_gml(EditBookmark.gml EditBookmarkGML.h edit_bookmark_gml) compile_gml(StorageWidget.gml StorageWidgetGML.h storage_widget_gml) @@ -24,6 +27,7 @@ set(SOURCES StorageModel.cpp StorageWidget.cpp Tab.cpp + WebDriverConnection.cpp WindowActions.cpp main.cpp ) @@ -33,6 +37,8 @@ set(GENERATED_SOURCES EditBookmarkGML.h StorageWidgetGML.h TabGML.h + WebDriverSessionClientEndpoint.h + WebDriverSessionServerEndpoint.h ) serenity_app(Browser ICON app-browser) diff --git a/Userland/Applications/Browser/WebDriverConnection.cpp b/Userland/Applications/Browser/WebDriverConnection.cpp new file mode 100644 index 0000000000..9d4960fd80 --- /dev/null +++ b/Userland/Applications/Browser/WebDriverConnection.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com> + * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "WebDriverConnection.h" +#include "BrowserWindow.h" + +namespace Browser { + +WebDriverConnection::WebDriverConnection(NonnullOwnPtr<Core::Stream::LocalSocket> socket, NonnullRefPtr<BrowserWindow> browser_window) + : IPC::ConnectionToServer<WebDriverSessionClientEndpoint, WebDriverSessionServerEndpoint>(*this, move(socket)) + , m_browser_window(move(browser_window)) +{ +} + +void WebDriverConnection::quit() +{ + dbgln("WebDriverConnection: quit"); + if (auto browser_window = m_browser_window.strong_ref()) + browser_window->close(); +} + +Messages::WebDriverSessionClient::GetUrlResponse WebDriverConnection::get_url() +{ + dbgln("WebDriverConnection: get_url"); + if (auto browser_window = m_browser_window.strong_ref()) + return { browser_window->active_tab().url() }; + return { URL("") }; +} + +void WebDriverConnection::set_url(AK::URL const& url) +{ + dbgln("WebDriverConnection: set_url {}", url); + if (auto browser_window = m_browser_window.strong_ref()) + browser_window->active_tab().load(url); +} + +Messages::WebDriverSessionClient::GetTitleResponse WebDriverConnection::get_title() +{ + dbgln("WebDriverConnection: get_title"); + if (auto browser_window = m_browser_window.strong_ref()) + return { browser_window->active_tab().title() }; + return { "" }; +} + +} diff --git a/Userland/Applications/Browser/WebDriverConnection.h b/Userland/Applications/Browser/WebDriverConnection.h new file mode 100644 index 0000000000..c887ce30c4 --- /dev/null +++ b/Userland/Applications/Browser/WebDriverConnection.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com> + * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "BrowserWindow.h" +#include <AK/Error.h> +#include <AK/String.h> +#include <Applications/Browser/WebDriverSessionClientEndpoint.h> +#include <Applications/Browser/WebDriverSessionServerEndpoint.h> +#include <LibCore/LocalServer.h> +#include <LibGUI/Application.h> +#include <LibIPC/ConnectionToServer.h> +#include <unistd.h> + +namespace Browser { + +class WebDriverConnection final + : public IPC::ConnectionToServer<WebDriverSessionClientEndpoint, WebDriverSessionServerEndpoint> { + C_OBJECT_ABSTRACT(WebDriverConnection) +public: + static ErrorOr<NonnullRefPtr<WebDriverConnection>> connect_to_webdriver(NonnullRefPtr<BrowserWindow> browser_window, String path) + { + dbgln("Trying to connect to {}", path); + auto result = TRY(Core::Stream::LocalSocket::connect(path)); + dbgln("Connected to WebDriver"); + return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) WebDriverConnection(move(result), browser_window))); + } + + virtual ~WebDriverConnection() = default; + + virtual void die() override { } + + virtual void quit() override; + virtual Messages::WebDriverSessionClient::GetUrlResponse get_url() override; + virtual void set_url(AK::URL const& url) override; + virtual Messages::WebDriverSessionClient::GetTitleResponse get_title() override; + +private: + WebDriverConnection(NonnullOwnPtr<Core::Stream::LocalSocket> socket, NonnullRefPtr<BrowserWindow> browser_window); + + WeakPtr<BrowserWindow> m_browser_window; +}; + +} diff --git a/Userland/Applications/Browser/WebDriverSessionClient.ipc b/Userland/Applications/Browser/WebDriverSessionClient.ipc new file mode 100644 index 0000000000..361a634add --- /dev/null +++ b/Userland/Applications/Browser/WebDriverSessionClient.ipc @@ -0,0 +1,9 @@ +#include <AK/URL.h> + +endpoint WebDriverSessionClient { + quit() =| + + get_url() => (URL url) + set_url(URL url) =| + get_title() => (String title) +} diff --git a/Userland/Applications/Browser/WebDriverSessionServer.ipc b/Userland/Applications/Browser/WebDriverSessionServer.ipc new file mode 100644 index 0000000000..0f8e2e3342 --- /dev/null +++ b/Userland/Applications/Browser/WebDriverSessionServer.ipc @@ -0,0 +1,2 @@ +endpoint WebDriverSessionServer { +} diff --git a/Userland/Applications/Browser/main.cpp b/Userland/Applications/Browser/main.cpp index 393c45359e..f8d83a05b6 100644 --- a/Userland/Applications/Browser/main.cpp +++ b/Userland/Applications/Browser/main.cpp @@ -11,6 +11,7 @@ #include <Applications/Browser/BrowserWindow.h> #include <Applications/Browser/CookieJar.h> #include <Applications/Browser/Tab.h> +#include <Applications/Browser/WebDriverConnection.h> #include <Applications/Browser/WindowActions.h> #include <LibConfig/Client.h> #include <LibCore/ArgsParser.h> @@ -62,12 +63,15 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) return 1; } - TRY(Core::System::pledge("stdio recvfd sendfd unix cpath rpath wpath proc exec")); + TRY(Core::System::pledge("stdio recvfd sendfd unix fattr cpath rpath wpath proc exec")); Vector<String> specified_urls; + String webdriver_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.parse(arguments); auto app = TRY(GUI::Application::try_create(arguments)); @@ -81,6 +85,11 @@ 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()) { + specified_urls.empend("about:blank"); + TRY(Core::System::unveil(webdriver_ipc_path.view(), "rw"sv)); + } + TRY(Core::System::unveil("/proc/all", "r")); TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); @@ -135,6 +144,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) Browser::CookieJar cookie_jar; auto window = Browser::BrowserWindow::construct(cookie_jar, first_url); + RefPtr<Browser::WebDriverConnection> web_driver_connection; + + if (!webdriver_ipc_path.is_empty()) + web_driver_connection = TRY(Browser::WebDriverConnection::connect_to_webdriver(window, webdriver_ipc_path)); auto content_filters_watcher = TRY(Core::FileWatcher::create()); content_filters_watcher->on_change = [&](Core::FileWatcherEvent const&) { |