| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | /*
 | 
					
						
							|  |  |  |  |  * Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com> | 
					
						
							|  |  |  |  |  * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-10-15 14:08:07 +02:00
										 |  |  |  |  * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-10-18 22:19:03 +02:00
										 |  |  |  |  * Copyright (c) 2022, Linus Groh <linusg@serenityos.org> | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  |  * | 
					
						
							|  |  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include "Client.h"
 | 
					
						
							|  |  |  |  | #include "Session.h"
 | 
					
						
							|  |  |  |  | #include <AK/Debug.h>
 | 
					
						
							|  |  |  |  | #include <AK/JsonParser.h>
 | 
					
						
							|  |  |  |  | #include <LibCore/DateTime.h>
 | 
					
						
							|  |  |  |  | #include <LibCore/MemoryStream.h>
 | 
					
						
							|  |  |  |  | #include <LibHTTP/HttpRequest.h>
 | 
					
						
							|  |  |  |  | #include <LibHTTP/HttpResponse.h>
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | namespace WebDriver { | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | Atomic<unsigned> Client::s_next_session_id; | 
					
						
							|  |  |  |  | NonnullOwnPtrVector<Session> Client::s_sessions; | 
					
						
							|  |  |  |  | Vector<Client::Route> Client::s_routes = { | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session" }, &Client::handle_new_session }, | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  |     { HTTP::HttpRequest::Method::DELETE, { "session", ":session_id" }, &Client::handle_delete_session }, | 
					
						
							|  |  |  |  |     { HTTP::HttpRequest::Method::GET, { "status" }, &Client::handle_get_status }, | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  |     { 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 }, | 
					
						
							| 
									
										
										
										
											2022-10-15 18:15:38 +01:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "back" }, &Client::handle_back }, | 
					
						
							| 
									
										
										
										
											2022-10-15 18:18:35 +01:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "forward" }, &Client::handle_forward }, | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "refresh" }, &Client::handle_refresh }, | 
					
						
							|  |  |  |  |     { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "title" }, &Client::handle_get_title }, | 
					
						
							| 
									
										
										
										
											2022-10-18 22:19:03 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "window" }, &Client::handle_get_window_handle }, | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::DELETE, { "session", ":session_id", "window" }, &Client::handle_close_window }, | 
					
						
							| 
									
										
										
										
											2022-10-18 12:47:19 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "element" }, &Client::handle_find_element }, | 
					
						
							| 
									
										
										
										
											2022-10-19 13:21:38 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "elements" }, &Client::handle_find_elements }, | 
					
						
							| 
									
										
										
										
											2022-10-15 14:08:07 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "cookie" }, &Client::handle_get_all_cookies }, | 
					
						
							| 
									
										
										
										
											2022-10-15 19:22:20 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "cookie", ":name" }, &Client::handle_get_named_cookie }, | 
					
						
							| 
									
										
										
										
											2022-10-17 13:20:57 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "cookie" }, &Client::handle_add_cookie }, | 
					
						
							| 
									
										
										
										
											2022-10-16 19:55:47 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::DELETE, { "session", ":session_id", "cookie", ":name" }, &Client::handle_delete_cookie }, | 
					
						
							| 
									
										
										
										
											2022-10-16 19:55:01 +02:00
										 |  |  |  |     { HTTP::HttpRequest::Method::DELETE, { "session", ":session_id", "cookie" }, &Client::handle_delete_all_cookies }, | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 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)); | 
					
						
							|  |  |  |  |     TRY(m_socket->write(content.bytes())); | 
					
						
							|  |  |  |  |     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(HttpError 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", ""); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     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
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<Client::RoutingResult, HttpError> Client::match_route(HTTP::HttpRequest::Method method, String const& resource) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     // 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 HttpError { 404, "unknown command", "The resource doesn't start with the prefix." }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     Vector<StringView> resource_split = resource.substring_view(m_prefix.length()).split_view('/', true); | 
					
						
							|  |  |  |  |     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 HttpError { 405, "unknown method", "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 HttpError { 404, "unknown command", "The command was not recognized." }; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ErrorOr<Session*, HttpError> 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 HttpError { 404, "invalid session id", "Invalid session id" }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (auto& session : Client::s_sessions) { | 
					
						
							|  |  |  |  |         if (session.session_id() == session_id_or_error.value()) | 
					
						
							|  |  |  |  |             return &session; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     return HttpError { 404, "invalid session id", "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; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 8.1 New Session, https://w3c.github.io/webdriver/#dfn-new-sessions
 | 
					
						
							|  |  |  |  | // POST /session
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_new_session(Vector<StringView> const&, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     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.
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:29:18 +02:00
										 |  |  |  |     auto capabilities = JsonObject {}; | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // FIXME: 5. If capabilities’s 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 HttpError { 500, "Failed to start session", start_result.error().string_literal() }; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 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; | 
					
						
							| 
									
										
										
										
											2022-10-18 22:29:18 +02:00
										 |  |  |  |     // "sessionId"
 | 
					
						
							|  |  |  |  |     //     session id
 | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  |     body.set("sessionId", String::number(session_id)); | 
					
						
							| 
									
										
										
										
											2022-10-18 22:29:18 +02:00
										 |  |  |  |     // "capabilities"
 | 
					
						
							|  |  |  |  |     //     capabilities
 | 
					
						
							|  |  |  |  |     body.set("capabilities", move(capabilities)); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // FIXME: 12. Initialize the following from capabilities:
 | 
					
						
							|  |  |  |  |     //            NOTE: See spec for steps
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // FIXME: 13. Set the webdriver-active flag to true.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // FIXME: 14. Set the current top-level browsing context for session with the top-level browsing context
 | 
					
						
							|  |  |  |  |     //            of the UA’s current browsing context.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // FIXME: 15. Set the request queue to a new queue.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 16. Return success with data body.
 | 
					
						
							|  |  |  |  |     return make_json_value(body); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 8.2 Delete Session, https://w3c.github.io/webdriver/#dfn-delete-session
 | 
					
						
							|  |  |  |  | // DELETE /session/{session id}
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_delete_session(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 1. If the current session is an active session, try to close the session.
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     auto stop_result = session->stop(); | 
					
						
							|  |  |  |  |     if (stop_result.is_error()) { | 
					
						
							|  |  |  |  |         return HttpError { 500, "unsupported operation", stop_result.error().string_literal() }; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 2. Return success with data null.
 | 
					
						
							|  |  |  |  |     return make_json_value(JsonValue()); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 8.3 Status, https://w3c.github.io/webdriver/#dfn-status
 | 
					
						
							|  |  |  |  | // GET /status
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_get_status(Vector<StringView> const&, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling GET /status"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-07 17:17:21 +01:00
										 |  |  |  |     // 1. Let body be a new JSON Object with the following properties:
 | 
					
						
							|  |  |  |  |     //    "ready"
 | 
					
						
							|  |  |  |  |     //        The remote end’s readiness state.
 | 
					
						
							|  |  |  |  |     //    "message"
 | 
					
						
							|  |  |  |  |     //        An implementation-defined string explaining the remote end’s 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 body; | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 10.1 Navigate To, https://w3c.github.io/webdriver/#dfn-navigate-to
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/url
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_navigate_to(Vector<StringView> const& parameters, JsonValue const& payload) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/url"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  |     // NOTE: Spec steps handled in Session::navigate_to().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->navigate_to(payload)); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 10.2 Get Current URL, https://w3c.github.io/webdriver/#dfn-get-current-url
 | 
					
						
							|  |  |  |  | // GET /session/{session id}/url
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_get_current_url(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/url"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  |     // NOTE: Spec steps handled in Session::get_current_url().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->get_current_url()); | 
					
						
							| 
									
										
										
										
											2022-10-10 11:45:21 +01:00
										 |  |  |  |     return make_json_value(result); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  | // 10.3 Back, https://w3c.github.io/webdriver/#dfn-back
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/back
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_back(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/back"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     // NOTE: Spec steps handled in Session::back().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->back()); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  | // 10.4 Forward, https://w3c.github.io/webdriver/#dfn-forward
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/forward
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_forward(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/forward"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     // NOTE: Spec steps handled in Session::forward().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->forward()); | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 10.5 Refresh, https://w3c.github.io/webdriver/#dfn-refresh
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/refresh
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_refresh(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-14 18:09:33 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/refresh"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-14 18:09:33 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::refresh().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->refresh()); | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  | // 10.6 Get Title, https://w3c.github.io/webdriver/#dfn-get-title
 | 
					
						
							|  |  |  |  | // GET /session/{session id}/title
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_get_title(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-15 18:15:38 +01:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/title"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-15 18:15:38 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     // NOTE: Spec steps handled in Session::get_title().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->get_title()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-15 18:15:38 +01:00
										 |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:19:03 +02:00
										 |  |  |  | // 11.1 Get Window Handle, https://w3c.github.io/webdriver/#get-window-handle
 | 
					
						
							|  |  |  |  | // GET /session/{session id}/window
 | 
					
						
							|  |  |  |  | ErrorOr<JsonValue, HttpError> 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])); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 1. If the current top-level browsing context is no longer open, return error with error code no such window.
 | 
					
						
							|  |  |  |  |     auto current_window = session->get_window_object(); | 
					
						
							|  |  |  |  |     if (!current_window.has_value()) | 
					
						
							|  |  |  |  |         return HttpError { 404, "no such window", "Window not found" }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 2. Return success with data being the window handle associated with the current top-level browsing context.
 | 
					
						
							|  |  |  |  |     return make_json_value(session->current_window_handle()); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  | // 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window
 | 
					
						
							|  |  |  |  | // DELETE /session/{session id}/window
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_close_window(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-15 18:18:35 +01:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>/window"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-15 18:18:35 +01:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 22:27:06 +02:00
										 |  |  |  |     // NOTE: Spec steps handled in Session::close_window().
 | 
					
						
							|  |  |  |  |     TRY(unwrap_result(session->close_window())); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:19:45 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     return make_json_value(JsonValue()); | 
					
						
							| 
									
										
										
										
											2022-10-15 18:18:35 +01:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-18 12:47:19 +02:00
										 |  |  |  | // 12.3.2 Find Element, https://w3c.github.io/webdriver/#dfn-find-element
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/element
 | 
					
						
							|  |  |  |  | ErrorOr<JsonValue, HttpError> 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])); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::find_element().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->find_element(payload)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 13:21:38 +02:00
										 |  |  |  | // 12.3.3 Find Elements, https://w3c.github.io/webdriver/#dfn-find-elements
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/elements
 | 
					
						
							|  |  |  |  | ErrorOr<JsonValue, HttpError> 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])); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::find_elements().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->find_elements(payload)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 14.1 Get All Cookies, https://w3c.github.io/webdriver/#dfn-get-all-cookies
 | 
					
						
							|  |  |  |  | // GET /session/{session id}/cookie
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_get_all_cookies(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-15 14:08:07 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/cookie"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-15 14:08:07 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::get_all_cookies().
 | 
					
						
							|  |  |  |  |     auto cookies = TRY(session->get_all_cookies()); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return make_json_value(cookies); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 14.2 Get Named Cookie, https://w3c.github.io/webdriver/#dfn-get-named-cookie
 | 
					
						
							|  |  |  |  | // GET /session/{session id}/cookie/{name}
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_get_named_cookie(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-15 19:22:20 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session/<session_id>/cookie/<name>"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-15 19:22:20 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::get_all_cookies().
 | 
					
						
							|  |  |  |  |     auto cookies = TRY(session->get_named_cookie(parameters[1])); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return make_json_value(cookies); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 14.3 Add Cookie, https://w3c.github.io/webdriver/#dfn-adding-a-cookie
 | 
					
						
							|  |  |  |  | // POST /session/{session id}/cookie
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_add_cookie(Vector<StringView> const& parameters, JsonValue const& payload) | 
					
						
							| 
									
										
										
										
											2022-10-17 13:20:57 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/cookie"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-17 13:20:57 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::add_cookie().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->add_cookie(payload)); | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 14.4 Delete Cookie, https://w3c.github.io/webdriver/#dfn-delete-cookie
 | 
					
						
							|  |  |  |  | // DELETE /session/{session id}/cookie/{name}
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_delete_cookie(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-16 19:55:47 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>/cookie/<name>"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-16 19:55:47 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::delete_cookie().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->delete_cookie(parameters[1])); | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:12:20 +02:00
										 |  |  |  | // 14.5 Delete All Cookies, https://w3c.github.io/webdriver/#dfn-delete-all-cookies
 | 
					
						
							|  |  |  |  | // DELETE /session/{session id}/cookie
 | 
					
						
							| 
									
										
										
										
											2022-10-17 16:22:59 +02:00
										 |  |  |  | ErrorOr<JsonValue, HttpError> Client::handle_delete_all_cookies(Vector<StringView> const& parameters, JsonValue const&) | 
					
						
							| 
									
										
										
										
											2022-10-16 19:55:01 +02:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |     dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/<session_id>/cookie"); | 
					
						
							| 
									
										
										
										
											2022-10-17 16:25:23 +02:00
										 |  |  |  |     auto* session = TRY(find_session_with_id(parameters[0])); | 
					
						
							| 
									
										
										
										
											2022-10-16 19:55:01 +02:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // NOTE: Spec steps handled in Session::delete_all_cookies().
 | 
					
						
							|  |  |  |  |     auto result = TRY(session->delete_all_cookies()); | 
					
						
							|  |  |  |  |     return make_json_value(result); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-12 11:14:59 +01:00
										 |  |  |  | } |