ladybird/Userland/Services/WebDriver/Client.cpp

819 lines
38 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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
*/
#include <AK/Debug.h>
#include <AK/JsonParser.h>
#include <LibCore/DateTime.h>
#include <LibCore/MemoryStream.h>
#include <LibHTTP/HttpRequest.h>
#include <LibHTTP/HttpResponse.h>
#include <WebDriver/Client.h>
#include <WebDriver/Session.h>
#include <WebDriver/TimeoutsConfiguration.h>
namespace WebDriver {
Atomic<unsigned> Client::s_next_session_id;
NonnullOwnPtrVector<Session> Client::s_sessions;
Vector<Client::Route> Client::s_routes = {
{ HTTP::HttpRequest::Method::POST, { "session" }, &Client::handle_new_session },
{ HTTP::HttpRequest::Method::DELETE, { "session", ":session_id" }, &Client::handle_delete_session },
{ HTTP::HttpRequest::Method::GET, { "status" }, &Client::handle_get_status },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "timeouts" }, &Client::handle_get_timeouts },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "timeouts" }, &Client::handle_set_timeouts },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "url" }, &Client::handle_navigate_to },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "url" }, &Client::handle_get_current_url },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "back" }, &Client::handle_back },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "forward" }, &Client::handle_forward },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "refresh" }, &Client::handle_refresh },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "title" }, &Client::handle_get_title },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "window" }, &Client::handle_get_window_handle },
{ HTTP::HttpRequest::Method::DELETE, { "session", ":session_id", "window" }, &Client::handle_close_window },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "window", "handles" }, &Client::handle_get_window_handles },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "window", "rect" }, &Client::handle_get_window_rect },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "window", "rect" }, &Client::handle_set_window_rect },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "window", "maximize" }, &Client::handle_maximize_window },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "window", "minimize" }, &Client::handle_minimize_window },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "element" }, &Client::handle_find_element },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "elements" }, &Client::handle_find_elements },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "element", ":element_id", "element" }, &Client::handle_find_element_from_element },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "element", ":element_id", "elements" }, &Client::handle_find_elements_from_element },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "selected" }, &Client::handle_is_element_selected },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "attribute", ":name" }, &Client::handle_get_element_attribute },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "property", ":name" }, &Client::handle_get_element_property },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "css", ":property_name" }, &Client::handle_get_element_css_value },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "text" }, &Client::handle_get_element_text },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "name" }, &Client::handle_get_element_tag_name },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "rect" }, &Client::handle_get_element_rect },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "enabled" }, &Client::handle_is_element_enabled },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "source" }, &Client::handle_get_source },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "execute", "sync" }, &Client::handle_execute_script },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "execute", "async" }, &Client::handle_execute_async_script },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "cookie" }, &Client::handle_get_all_cookies },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "cookie", ":name" }, &Client::handle_get_named_cookie },
{ HTTP::HttpRequest::Method::POST, { "session", ":session_id", "cookie" }, &Client::handle_add_cookie },
{ HTTP::HttpRequest::Method::DELETE, { "session", ":session_id", "cookie", ":name" }, &Client::handle_delete_cookie },
{ HTTP::HttpRequest::Method::DELETE, { "session", ":session_id", "cookie" }, &Client::handle_delete_all_cookies },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "screenshot" }, &Client::handle_take_screenshot },
{ HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "screenshot" }, &Client::handle_take_element_screenshot },
};
Client::Client(NonnullOwnPtr<Core::Stream::BufferedTCPSocket> socket, Core::Object* parent)
: Core::Object(parent)
, m_socket(move(socket))
{
}
void Client::die()
{
m_socket->close();
deferred_invoke([this] { remove_from_parent(); });
}
void Client::start()
{
m_socket->on_ready_to_read = [this] {
StringBuilder builder;
// FIXME: All this should be moved to LibHTTP and be made spec compliant
auto maybe_buffer = ByteBuffer::create_uninitialized(m_socket->buffer_size());
if (maybe_buffer.is_error()) {
warnln("Could not create buffer for client: {}", maybe_buffer.error());
die();
return;
}
auto buffer = maybe_buffer.release_value();
for (;;) {
auto maybe_can_read = m_socket->can_read_without_blocking();
if (maybe_can_read.is_error()) {
warnln("Failed to get the blocking status for the socket: {}", maybe_can_read.error());
die();
return;
}
if (!maybe_can_read.value())
break;
auto maybe_data = m_socket->read(buffer);
if (maybe_data.is_error()) {
warnln("Failed to read data from the request: {}", maybe_data.error());
die();
return;
}
if (m_socket->is_eof()) {
die();
break;
}
builder.append(StringView(maybe_data.value()));
}
auto request = builder.to_byte_buffer();
auto http_request_or_error = HTTP::HttpRequest::from_raw_request(request);
if (!http_request_or_error.has_value())
return;
auto http_request = http_request_or_error.release_value();
auto body_or_error = read_body_as_json(http_request);
if (body_or_error.is_error()) {
warnln("Failed to read the request body: {}", body_or_error.error());
die();
return;
}
auto maybe_did_handle = handle_request(http_request, body_or_error.value());
if (maybe_did_handle.is_error()) {
warnln("Failed to handle the request: {}", maybe_did_handle.error());
}
die();
};
}
ErrorOr<JsonValue> Client::read_body_as_json(HTTP::HttpRequest const& request)
{
// If we received a multipart body here, this would fail badly.
unsigned content_length = 0;
for (auto const& header : request.headers()) {
if (header.name.equals_ignoring_case("Content-Length"sv)) {
content_length = header.value.to_int(TrimWhitespace::Yes).value_or(0);
break;
}
}
if (!content_length)
return JsonValue();
// FIXME: Check the Content-Type is actually application/json
JsonParser json_parser(request.body());
return json_parser.parse();
}
ErrorOr<bool> Client::handle_request(HTTP::HttpRequest const& request, JsonValue const& body)
{
if constexpr (WEBDRIVER_DEBUG) {
dbgln("Got HTTP request: {} {}", request.method_name(), request.resource());
if (!body.is_null())
dbgln("Body: {}", body.to_string());
}
auto routing_result_match = match_route(request.method(), request.resource());
if (routing_result_match.is_error()) {
auto error = routing_result_match.release_error();
dbgln_if(WEBDRIVER_DEBUG, "Failed to match route: {}", error);
TRY(send_error_response(error, request));
return false;
}
auto routing_result = routing_result_match.release_value();
auto result = (this->*routing_result.handler)(routing_result.parameters, body);
if (result.is_error()) {
dbgln_if(WEBDRIVER_DEBUG, "Error in calling route handler: {}", result.error());
TRY(send_error_response(result.release_error(), request));
return false;
}
auto object = result.release_value();
TRY(send_response(object.to_string(), request));
return true;
}
// https://w3c.github.io/webdriver/#dfn-send-a-response
ErrorOr<void> Client::send_response(StringView content, HTTP::HttpRequest const& request)
{
// FIXME: Implement to spec.
StringBuilder builder;
builder.append("HTTP/1.0 200 OK\r\n"sv);
builder.append("Server: WebDriver (SerenityOS)\r\n"sv);
builder.append("X-Frame-Options: SAMEORIGIN\r\n"sv);
builder.append("X-Content-Type-Options: nosniff\r\n"sv);
builder.append("Pragma: no-cache\r\n"sv);
builder.append("Content-Type: application/json; charset=utf-8\r\n"sv);
builder.appendff("Content-Length: {}\r\n", content.length());
builder.append("\r\n"sv);
auto builder_contents = builder.to_byte_buffer();
TRY(m_socket->write(builder_contents));
while (!content.is_empty()) {
auto bytes_sent = TRY(m_socket->write(content.bytes()));
content = content.substring_view(bytes_sent);
}
log_response(200, request);
auto keep_alive = false;
if (auto it = request.headers().find_if([](auto& header) { return header.name.equals_ignoring_case("Connection"sv); }); !it.is_end()) {
if (it->value.trim_whitespace().equals_ignoring_case("keep-alive"sv))
keep_alive = true;
}
if (!keep_alive)
m_socket->close();
return {};
}
// https://w3c.github.io/webdriver/#dfn-send-an-error
ErrorOr<void> Client::send_error_response(Web::WebDriver::Error const& error, HTTP::HttpRequest const& request)
{
// FIXME: Implement to spec.
dbgln("send_error_response: {} {}: {}", error.http_status, error.error, error.message);
auto reason_phrase = HTTP::HttpResponse::reason_phrase_for_code(error.http_status);
auto result = JsonObject();
result.set("error", error.error);
result.set("message", error.message);
result.set("stacktrace", "");
if (error.data.has_value())
result.set("data", *error.data);
StringBuilder content_builder;
result.serialize(content_builder);
StringBuilder header_builder;
header_builder.appendff("HTTP/1.0 {} ", error.http_status);
header_builder.append(reason_phrase);
header_builder.append("\r\n"sv);
header_builder.append("Content-Type: application/json; charset=UTF-8\r\n"sv);
header_builder.appendff("Content-Length: {}\r\n", content_builder.length());
header_builder.append("\r\n"sv);
TRY(m_socket->write(header_builder.to_byte_buffer()));
TRY(m_socket->write(content_builder.to_byte_buffer()));
log_response(error.http_status, request);
return {};
}
void Client::log_response(unsigned code, HTTP::HttpRequest const& request)
{
outln("{} :: {:03d} :: {} {}", Core::DateTime::now().to_string(), code, request.method_name(), request.resource());
}
// https://w3c.github.io/webdriver/#dfn-match-a-request
ErrorOr<Client::RoutingResult, Web::WebDriver::Error> Client::match_route(HTTP::HttpRequest::Method method, String const& resource)
{
// FIXME: Implement to spec.
dbgln_if(WEBDRIVER_DEBUG, "match_route({}, {})", HTTP::to_string(method), resource);
// https://w3c.github.io/webdriver/webdriver-spec.html#routing-requests
if (!resource.starts_with(m_prefix))
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnknownCommand, "The resource doesn't start with the prefix.");
Vector<StringView> resource_split = resource.substring_view(m_prefix.length()).split_view('/', SplitBehavior::KeepEmpty);
Vector<StringView> parameters;
bool matched_path = false;
for (auto const& route : Client::s_routes) {
dbgln_if(WEBDRIVER_DEBUG, "- Checking {} {}", HTTP::to_string(route.method), String::join("/"sv, route.path));
if (resource_split.size() != route.path.size()) {
dbgln_if(WEBDRIVER_DEBUG, "-> Discarding: Wrong length");
continue;
}
bool match = true;
for (size_t i = 0; i < route.path.size(); ++i) {
if (route.path[i].starts_with(':')) {
parameters.append(resource_split[i]);
continue;
}
if (route.path[i] != resource_split[i]) {
match = false;
parameters.clear();
dbgln_if(WEBDRIVER_DEBUG, "-> Discarding: Part `{}` does not match `{}`", route.path[i], resource_split[i]);
break;
}
}
if (match && route.method == method) {
dbgln_if(WEBDRIVER_DEBUG, "-> Matched! :^)");
return RoutingResult { route.handler, parameters };
}
matched_path = true;
}
// Matched a path, but didn't match a known method
if (matched_path) {
dbgln_if(WEBDRIVER_DEBUG, "- A path matched, but method didn't. :^(");
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnknownMethod, "The command matched a known URL but did not match a method for that URL.");
}
// Didn't have any match
dbgln_if(WEBDRIVER_DEBUG, "- No matches. :^(");
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnknownCommand, "The command was not recognized.");
}
ErrorOr<Session*, Web::WebDriver::Error> Client::find_session_with_id(StringView session_id)
{
auto session_id_or_error = session_id.to_uint<>();
if (!session_id_or_error.has_value())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSessionId, "Invalid session id");
for (auto& session : Client::s_sessions) {
if (session.session_id() == session_id_or_error.value())
return &session;
}
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSessionId, "Invalid session id");
}
ErrorOr<NonnullOwnPtr<Session>, Web::WebDriver::Error> Client::take_session_with_id(StringView session_id)
{
auto session_id_or_error = session_id.to_uint<>();
if (!session_id_or_error.has_value())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSessionId, "Invalid session id");
for (size_t i = 0; i < Client::s_sessions.size(); ++i) {
if (Client::s_sessions[i].session_id() == session_id_or_error.value()) {
return Client::s_sessions.take(i);
}
}
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSessionId, "Invalid session id");
}
void Client::close_session(unsigned session_id)
{
bool found = Client::s_sessions.remove_first_matching([&](auto const& it) {
return it->session_id() == session_id;
});
if (found)
dbgln_if(WEBDRIVER_DEBUG, "Shut down session {}", session_id);
else
dbgln_if(WEBDRIVER_DEBUG, "Unable to shut down session {}: Not found", session_id);
}
JsonValue Client::make_json_value(JsonValue const& value)
{
JsonObject result;
result.set("value", value);
return result;
}
// 8.1 New Session, https://w3c.github.io/webdriver/#dfn-new-sessions
// POST /session
Web::WebDriver::Response Client::handle_new_session(Vector<StringView> const&, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session");
// FIXME: 1. If the maximum active sessions is equal to the length of the list of active sessions,
// return error with error code session not created.
// FIXME: 2. If the remote end is an intermediary node, take implementation-defined steps that either
// result in returning an error with error code session not created, or in returning a
// success with data that is isomorphic to that returned by remote ends according to the
// rest of this algorithm. If an error is not returned, the intermediary node must retain a
// reference to the session created on the upstream node as the associated session such
// that commands may be forwarded to this associated session on subsequent commands.
// FIXME: 3. If the maximum active sessions is equal to the length of the list of active sessions,
// return error with error code session not created.
// FIXME: 4. Let capabilities be the result of trying to process capabilities with parameters as an argument.
auto capabilities = JsonObject {};
// FIXME: 5. If capabilitiess is null, return error with error code session not created.
// 6. Let session id be the result of generating a UUID.
// FIXME: Actually create a UUID.
auto session_id = Client::s_next_session_id++;
// 7. Let session be a new session with the session ID of session id.
NonnullOwnPtr<Session> session = make<Session>(session_id, *this);
auto start_result = session->start();
if (start_result.is_error()) {
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::SessionNotCreated, String::formatted("Failed to start session: {}", start_result.error().string_literal()));
}
auto& web_content_connection = session->web_content_connection();
// FIXME: 8. Set the current session to session.
// FIXME: 9. Run any WebDriver new session algorithm defined in external specifications,
// with arguments session and capabilities.
// 10. Append session to active sessions.
Client::s_sessions.append(move(session));
// 11. Let body be a JSON Object initialized with:
JsonObject body;
// "sessionId"
// session id
body.set("sessionId", String::number(session_id));
// "capabilities"
// capabilities
body.set("capabilities", move(capabilities));
// FIXME: 12. Initialize the following from capabilities:
// NOTE: See spec for steps
// 13. Set the webdriver-active flag to true.
web_content_connection.async_set_is_webdriver_active(true);
// FIXME: 14. Set the current top-level browsing context for session with the top-level browsing context
// of the UAs current browsing context.
// FIXME: 15. Set the request queue to a new queue.
// 16. Return success with data body.
return make_json_value(body);
}
// 8.2 Delete Session, https://w3c.github.io/webdriver/#dfn-delete-session
// DELETE /session/{session id}
Web::WebDriver::Response Client::handle_delete_session(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>");
// 1. If the current session is an active session, try to close the session.
auto session = TRY(take_session_with_id(parameters[0]));
TRY(session->stop());
// 2. Return success with data null.
return make_json_value(JsonValue());
}
// 8.3 Status, https://w3c.github.io/webdriver/#dfn-status
// GET /status
Web::WebDriver::Response Client::handle_get_status(Vector<StringView> const&, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /status");
// 1. Let body be a new JSON Object with the following properties:
// "ready"
// The remote ends readiness state.
// "message"
// An implementation-defined string explaining the remote ends readiness state.
// FIXME: Report if we are somehow not ready.
JsonObject body;
body.set("ready", true);
body.set("message", "Ready to start some sessions!");
// 2. Return success with data body.
return JsonValue { body };
}
// 9.1 Get Timeouts, https://w3c.github.io/webdriver/#dfn-get-timeouts
// GET /session/{session id}/timeouts
Web::WebDriver::Response Client::handle_get_timeouts(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session id>/timeouts");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = session->get_timeouts();
return make_json_value(result);
}
// 9.2 Set Timeouts, https://w3c.github.io/webdriver/#dfn-set-timeouts
// POST /session/{session id}/timeouts
Web::WebDriver::Response Client::handle_set_timeouts(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session id>/timeouts");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->set_timeouts(payload));
return make_json_value(result);
}
// 10.1 Navigate To, https://w3c.github.io/webdriver/#dfn-navigate-to
// POST /session/{session id}/url
Web::WebDriver::Response Client::handle_navigate_to(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/url");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().navigate_to(payload);
}
// 10.2 Get Current URL, https://w3c.github.io/webdriver/#dfn-get-current-url
// GET /session/{session id}/url
Web::WebDriver::Response Client::handle_get_current_url(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/url");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_current_url();
}
// 10.3 Back, https://w3c.github.io/webdriver/#dfn-back
// POST /session/{session id}/back
Web::WebDriver::Response Client::handle_back(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/back");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->back());
return make_json_value(result);
}
// 10.4 Forward, https://w3c.github.io/webdriver/#dfn-forward
// POST /session/{session id}/forward
Web::WebDriver::Response Client::handle_forward(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/forward");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->forward());
return make_json_value(result);
}
// 10.5 Refresh, https://w3c.github.io/webdriver/#dfn-refresh
// POST /session/{session id}/refresh
Web::WebDriver::Response Client::handle_refresh(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/refresh");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->refresh());
return make_json_value(result);
}
// 10.6 Get Title, https://w3c.github.io/webdriver/#dfn-get-title
// GET /session/{session id}/title
Web::WebDriver::Response Client::handle_get_title(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/title");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->get_title());
return make_json_value(result);
}
// 11.1 Get Window Handle, https://w3c.github.io/webdriver/#get-window-handle
// GET /session/{session id}/window
Web::WebDriver::Response Client::handle_get_window_handle(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/window");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->get_window_handle());
return make_json_value(result);
}
// 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window
// DELETE /session/{session id}/window
Web::WebDriver::Response Client::handle_close_window(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>/window");
auto* session = TRY(find_session_with_id(parameters[0]));
TRY(unwrap_result(session->close_window()));
return make_json_value(JsonValue());
}
// 11.4 Get Window Handles, https://w3c.github.io/webdriver/#dfn-get-window-handles
// GET /session/{session id}/window/handles
Web::WebDriver::Response Client::handle_get_window_handles(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/window/handles");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->get_window_handles());
return make_json_value(result);
}
// 11.8.1 Get Window Rect, https://w3c.github.io/webdriver/#dfn-get-window-rect
// GET /session/{session id}/window/rect
Web::WebDriver::Response Client::handle_get_window_rect(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/window/rect");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_window_rect();
}
// 11.8.2 Set Window Rect, https://w3c.github.io/webdriver/#dfn-set-window-rect
// POST /session/{session id}/window/rect
Web::WebDriver::Response Client::handle_set_window_rect(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/rect");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().set_window_rect(payload);
}
// 11.8.3 Maximize Window, https://w3c.github.io/webdriver/#dfn-maximize-window
// POST /session/{session id}/window/maximize
Web::WebDriver::Response Client::handle_maximize_window(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/maximize");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().maximize_window();
}
// 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
// POST /session/{session id}/window/minimize
Web::WebDriver::Response Client::handle_minimize_window(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/minimize");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().minimize_window();
}
// 12.3.2 Find Element, https://w3c.github.io/webdriver/#dfn-find-element
// POST /session/{session id}/element
Web::WebDriver::Response Client::handle_find_element(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/element");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().find_element(payload);
}
// 12.3.3 Find Elements, https://w3c.github.io/webdriver/#dfn-find-elements
// POST /session/{session id}/elements
Web::WebDriver::Response Client::handle_find_elements(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/elements");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().find_elements(payload);
}
// 12.3.4 Find Element From Element, https://w3c.github.io/webdriver/#dfn-find-element-from-element
// POST /session/{session id}/element/{element id}/element
Web::WebDriver::Response Client::handle_find_element_from_element(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/element/<element_id>/element");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().find_element_from_element(payload, parameters[1]);
}
// 12.3.5 Find Elements From Element, https://w3c.github.io/webdriver/#dfn-find-elements-from-element
// POST /session/{session id}/element/{element id}/elements
Web::WebDriver::Response Client::handle_find_elements_from_element(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/element/<element_id>/elements");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().find_elements_from_element(payload, parameters[1]);
}
// 12.4.1 Is Element Selected, https://w3c.github.io/webdriver/#dfn-is-element-selected
// GET /session/{session id}/element/{element id}/selected
Web::WebDriver::Response Client::handle_is_element_selected(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/selected");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().is_element_selected(parameters[1]);
}
// 12.4.2 Get Element Attribute, https://w3c.github.io/webdriver/#dfn-get-element-attribute
// GET /session/{session id}/element/{element id}/attribute/{name}
Web::WebDriver::Response Client::handle_get_element_attribute(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/attribute/<name>");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_element_attribute(parameters[1], parameters[2]);
}
// 12.4.3 Get Element Property, https://w3c.github.io/webdriver/#dfn-get-element-property
// GET /session/{session id}/element/{element id}/property/{name}
Web::WebDriver::Response Client::handle_get_element_property(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/property/<name>");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_element_property(parameters[1], parameters[2]);
}
// 12.4.4 Get Element CSS Value, https://w3c.github.io/webdriver/#dfn-get-element-css-value
// GET /session/{session id}/element/{element id}/css/{property name}
Web::WebDriver::Response Client::handle_get_element_css_value(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/css/<property_name>");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_element_css_value(parameters[1], parameters[2]);
}
// 12.4.5 Get Element Text, https://w3c.github.io/webdriver/#dfn-get-element-text
// GET /session/{session id}/element/{element id}/text
Web::WebDriver::Response Client::handle_get_element_text(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/text");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_element_text(parameters[1]);
}
// 12.4.6 Get Element Tag Name, https://w3c.github.io/webdriver/#dfn-get-element-tag-name
// GET /session/{session id}/element/{element id}/name
Web::WebDriver::Response Client::handle_get_element_tag_name(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/name");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_element_tag_name(parameters[1]);
}
// 12.4.7 Get Element Rect, https://w3c.github.io/webdriver/#dfn-get-element-rect
// GET /session/{session id}/element/{element id}/rect
Web::WebDriver::Response Client::handle_get_element_rect(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/rect");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_element_rect(parameters[1]);
}
// 12.4.8 Is Element Enabled, https://w3c.github.io/webdriver/#dfn-is-element-enabled
// GET /session/{session id}/element/{element id}/enabled
Web::WebDriver::Response Client::handle_is_element_enabled(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/enabled");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().is_element_enabled(parameters[1]);
}
// 13.1 Get Page Source, https://w3c.github.io/webdriver/#dfn-get-page-source
// GET /session/{session id}/source
Web::WebDriver::Response Client::handle_get_source(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/source");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().get_source();
}
// 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
// POST /session/{session id}/execute/sync
Web::WebDriver::Response Client::handle_execute_script(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/execute/sync");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().execute_script(payload);
}
// 13.2.2 Execute Async Script, https://w3c.github.io/webdriver/#dfn-execute-async-script
// POST /session/{session id}/execute/async
Web::WebDriver::Response Client::handle_execute_async_script(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/execute/async");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->execute_async_script(payload));
return make_json_value(result);
}
// 14.1 Get All Cookies, https://w3c.github.io/webdriver/#dfn-get-all-cookies
// GET /session/{session id}/cookie
Web::WebDriver::Response Client::handle_get_all_cookies(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/cookie");
auto* session = TRY(find_session_with_id(parameters[0]));
auto cookies = TRY(session->get_all_cookies());
return make_json_value(cookies);
}
// 14.2 Get Named Cookie, https://w3c.github.io/webdriver/#dfn-get-named-cookie
// GET /session/{session id}/cookie/{name}
Web::WebDriver::Response Client::handle_get_named_cookie(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/cookie/<name>");
auto* session = TRY(find_session_with_id(parameters[0]));
auto cookies = TRY(session->get_named_cookie(parameters[1]));
return make_json_value(cookies);
}
// 14.3 Add Cookie, https://w3c.github.io/webdriver/#dfn-adding-a-cookie
// POST /session/{session id}/cookie
Web::WebDriver::Response Client::handle_add_cookie(Vector<StringView> const& parameters, JsonValue const& payload)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/cookie");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->add_cookie(payload));
return make_json_value(result);
}
// 14.4 Delete Cookie, https://w3c.github.io/webdriver/#dfn-delete-cookie
// DELETE /session/{session id}/cookie/{name}
Web::WebDriver::Response Client::handle_delete_cookie(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>/cookie/<name>");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->delete_cookie(parameters[1]));
return make_json_value(result);
}
// 14.5 Delete All Cookies, https://w3c.github.io/webdriver/#dfn-delete-all-cookies
// DELETE /session/{session id}/cookie
Web::WebDriver::Response Client::handle_delete_all_cookies(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>/cookie");
auto* session = TRY(find_session_with_id(parameters[0]));
auto result = TRY(session->delete_all_cookies());
return make_json_value(result);
}
// 17.1 Take Screenshot, https://w3c.github.io/webdriver/#take-screenshot
// GET /session/{session id}/screenshot
Web::WebDriver::Response Client::handle_take_screenshot(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/screenshot");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().take_screenshot();
}
// 17.2 Take Element Screenshot, https://w3c.github.io/webdriver/#dfn-take-element-screenshot
// GET /session/{session id}/element/{element id}/screenshot
Web::WebDriver::Response Client::handle_take_element_screenshot(Vector<StringView> const& parameters, JsonValue const&)
{
dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/element/<element_id>/screenshot");
auto* session = TRY(find_session_with_id(parameters[0]));
return session->web_content_connection().take_element_screenshot(parameters[1]);
}
}