1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 05:57:44 +00:00

Ladybird: Implement WebDriver for Ladybird :^)

This adds a WebDriver binary for Ladybird to make use of Serenity's
WebDriver implementation. This has to use the same IPC socket handling
that was used to make WebContent work out-of-process. Besides that, we
are able to reuse almost everything from Serenity.
This commit is contained in:
Timothy Flynn 2022-11-14 12:09:14 -05:00 committed by Andrew Kaster
parent 54321f49ad
commit 9e0db602ca
6 changed files with 238 additions and 2 deletions

View file

@ -0,0 +1,104 @@
/*
* 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 <LibCore/Stream.h>
#include <LibCore/System.h>
#include <WebDriver/Client.h>
#include <unistd.h>
namespace WebDriver {
Session::Session(unsigned session_id, NonnullRefPtr<Client> client)
: m_client(move(client))
, 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 = String::formatted("WebDriver:{}", webcontent_fd);
TRY(Core::System::setenv("SOCKET_TAKEOVER"sv, takeover_string, true));
auto fd_passing_socket_string = String::number(webcontent_fd_passing_fd);
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 ends 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 {};
}
}