mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 02:27:43 +00:00
Ladybird/WebDriver: Move to using local socket files for WebDriver IPC
This allows us to use standard Serenity IPC infrastructure rather than manually creating FD-passing sockets. This also lets us use Serenity's WebDriver Session class, removing the copy previously used in Ladybird. This ensures any changes to Session in the future will be picked up by Ladybird for free.
This commit is contained in:
parent
ea26e45594
commit
e5192073d9
12 changed files with 95 additions and 231 deletions
|
@ -2,9 +2,9 @@ set(WEBDRIVER_SOURCE_DIR ${SERENITY_SOURCE_DIR}/Userland/Services/WebDriver)
|
|||
|
||||
set(SOURCES
|
||||
${WEBDRIVER_SOURCE_DIR}/Client.cpp
|
||||
${WEBDRIVER_SOURCE_DIR}/Session.cpp
|
||||
${WEBDRIVER_SOURCE_DIR}/WebContentConnection.cpp
|
||||
../Utilities.cpp
|
||||
Session.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#define AK_DONT_REPLACE_STD
|
||||
|
||||
#include "Session.h"
|
||||
#include "../Utilities.h"
|
||||
#include <LibCore/Stream.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <WebDriver/Client.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace WebDriver {
|
||||
|
||||
Session::Session(unsigned session_id, NonnullRefPtr<Client> client, Web::WebDriver::LadybirdOptions options)
|
||||
: m_client(move(client))
|
||||
, m_options(move(options))
|
||||
, m_id(session_id)
|
||||
{
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
if (auto error = stop(); error.is_error())
|
||||
warnln("Failed to stop session {}: {}", m_id, error.error());
|
||||
}
|
||||
|
||||
ErrorOr<void> Session::start()
|
||||
{
|
||||
int socket_fds[2] {};
|
||||
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
|
||||
auto [webdriver_fd, webcontent_fd] = socket_fds;
|
||||
|
||||
int fd_passing_socket_fds[2] {};
|
||||
TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds));
|
||||
auto [webdriver_fd_passing_fd, webcontent_fd_passing_fd] = fd_passing_socket_fds;
|
||||
|
||||
m_browser_pid = TRY(Core::System::fork());
|
||||
|
||||
if (m_browser_pid == 0) {
|
||||
TRY(Core::System::close(webdriver_fd_passing_fd));
|
||||
TRY(Core::System::close(webdriver_fd));
|
||||
|
||||
auto takeover_string = DeprecatedString::formatted("WebDriver:{}", webcontent_fd);
|
||||
TRY(Core::System::setenv("SOCKET_TAKEOVER"sv, takeover_string, true));
|
||||
|
||||
auto fd_passing_socket_string = DeprecatedString::number(webcontent_fd_passing_fd);
|
||||
|
||||
if (m_options.headless) {
|
||||
auto resouces = DeprecatedString::formatted("{}/res", s_serenity_resource_root);
|
||||
auto error_page = DeprecatedString::formatted("{}/res/html/error.html", s_serenity_resource_root);
|
||||
auto certs = DeprecatedString::formatted("{}/etc/ca_certs.ini", s_serenity_resource_root);
|
||||
|
||||
char const* argv[] = {
|
||||
"headless-browser",
|
||||
"--resources",
|
||||
resouces.characters(),
|
||||
"--error-page",
|
||||
error_page.characters(),
|
||||
"--certs",
|
||||
certs.characters(),
|
||||
"--webdriver-fd-passing-socket",
|
||||
fd_passing_socket_string.characters(),
|
||||
"about:blank",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
if (execvp("./_deps/lagom-build/headless-browser", const_cast<char**>(argv)) < 0)
|
||||
perror("execvp");
|
||||
} else {
|
||||
char const* argv[] = {
|
||||
"ladybird",
|
||||
"--webdriver-fd-passing-socket",
|
||||
fd_passing_socket_string.characters(),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
if (execvp("./ladybird", const_cast<char**>(argv)) < 0)
|
||||
perror("execvp");
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
TRY(Core::System::close(webcontent_fd_passing_fd));
|
||||
TRY(Core::System::close(webcontent_fd));
|
||||
|
||||
auto socket = TRY(Core::Stream::LocalSocket::adopt_fd(webdriver_fd));
|
||||
TRY(socket->set_blocking(true));
|
||||
|
||||
m_web_content_connection = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) WebContentConnection(move(socket), m_client, session_id())));
|
||||
m_web_content_connection->set_fd_passing_socket(TRY(Core::Stream::LocalSocket::adopt_fd(webdriver_fd_passing_fd)));
|
||||
|
||||
m_started = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-close-the-session
|
||||
Web::WebDriver::Response Session::stop()
|
||||
{
|
||||
if (!m_started)
|
||||
return JsonValue {};
|
||||
|
||||
// 1. Perform the following substeps based on the remote end’s type:
|
||||
// NOTE: We perform the "Remote end is an endpoint node" steps in the WebContent process.
|
||||
m_web_content_connection->close_session();
|
||||
|
||||
// 2. Remove the current session from active sessions.
|
||||
// NOTE: Handled by WebDriver::Client.
|
||||
|
||||
// 3. Perform any implementation-specific cleanup steps.
|
||||
if (m_browser_pid.has_value()) {
|
||||
MUST(Core::System::kill(*m_browser_pid, SIGTERM));
|
||||
m_browser_pid = {};
|
||||
}
|
||||
|
||||
m_started = false;
|
||||
|
||||
// 4. If an error has occurred in any of the steps above, return the error, otherwise return success with data null.
|
||||
return JsonValue {};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibWeb/WebDriver/Error.h>
|
||||
#include <LibWeb/WebDriver/Response.h>
|
||||
#include <WebDriver/WebContentConnection.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace WebDriver {
|
||||
|
||||
class Session {
|
||||
public:
|
||||
Session(unsigned session_id, NonnullRefPtr<Client> client, Web::WebDriver::LadybirdOptions options);
|
||||
~Session();
|
||||
|
||||
unsigned session_id() const { return m_id; }
|
||||
|
||||
WebContentConnection& web_content_connection()
|
||||
{
|
||||
VERIFY(m_web_content_connection);
|
||||
return *m_web_content_connection;
|
||||
}
|
||||
|
||||
ErrorOr<void> start();
|
||||
Web::WebDriver::Response stop();
|
||||
|
||||
private:
|
||||
NonnullRefPtr<Client> m_client;
|
||||
Web::WebDriver::LadybirdOptions m_options;
|
||||
|
||||
bool m_started { false };
|
||||
unsigned m_id { 0 };
|
||||
|
||||
RefPtr<WebContentConnection> m_web_content_connection;
|
||||
Optional<pid_t> m_browser_pid;
|
||||
};
|
||||
|
||||
}
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
#include "../Utilities.h"
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibCore/TCPServer.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
@ -16,6 +18,41 @@
|
|||
|
||||
extern DeprecatedString s_serenity_resource_root;
|
||||
|
||||
static ErrorOr<pid_t> launch_browser(DeprecatedString const& socket_path)
|
||||
{
|
||||
char const* argv[] = {
|
||||
"ladybird",
|
||||
"--webdriver-content-path",
|
||||
socket_path.characters(),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
return Core::System::posix_spawn("./ladybird"sv, nullptr, nullptr, const_cast<char**>(argv), environ);
|
||||
}
|
||||
|
||||
static ErrorOr<pid_t> launch_headless_browser(DeprecatedString const& socket_path)
|
||||
{
|
||||
auto resouces = DeprecatedString::formatted("{}/res", s_serenity_resource_root);
|
||||
auto error_page = DeprecatedString::formatted("{}/res/html/error.html", s_serenity_resource_root);
|
||||
auto certs = DeprecatedString::formatted("{}/etc/ca_certs.ini", s_serenity_resource_root);
|
||||
|
||||
char const* argv[] = {
|
||||
"headless-browser",
|
||||
"--resources",
|
||||
resouces.characters(),
|
||||
"--error-page",
|
||||
error_page.characters(),
|
||||
"--certs",
|
||||
certs.characters(),
|
||||
"--webdriver-ipc-path",
|
||||
socket_path.characters(),
|
||||
"about:blank",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
return Core::System::posix_spawn("./_deps/lagom-build/headless-browser"sv, nullptr, nullptr, const_cast<char**>(argv), environ);
|
||||
}
|
||||
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
auto listen_address = "0.0.0.0"sv;
|
||||
|
@ -39,6 +76,9 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
|
||||
platform_init();
|
||||
|
||||
auto webdriver_socket_path = DeprecatedString::formatted("{}/webdriver", TRY(Core::StandardPaths::runtime_directory()));
|
||||
TRY(Core::Directory::create(webdriver_socket_path, Core::Directory::CreateDirectories::Yes));
|
||||
|
||||
Core::EventLoop loop;
|
||||
auto server = TRY(Core::TCPServer::try_create());
|
||||
|
||||
|
@ -56,7 +96,7 @@ ErrorOr<int> 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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue