1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 14:27:35 +00:00

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.
This commit is contained in:
Timothy Flynn 2022-11-08 10:18:11 -05:00 committed by Tim Flynn
parent 6b014489d4
commit 50ae1ad18a
11 changed files with 151 additions and 35 deletions

View file

@ -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)

View file

@ -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);
TRY(Core::System::bind(listen_socket, (const struct sockaddr*)&addr, sizeof(struct sockaddr_un)));
TRY(Core::System::listen(listen_socket, 1));
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;
}
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));

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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 };
};
}

View file

@ -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"));