summaryrefslogtreecommitdiff
path: root/Userland/Services
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2022-11-02 18:11:04 +0000
committerLinus Groh <mail@linusgroh.de>2022-11-02 23:46:30 +0000
commitf88a0c51a329435f6a706c65a25c9cd959e26168 (patch)
treee5f88861f8007286ee923d0d02e2b50b978a02bd /Userland/Services
parent6e1131e6dee6d0c8f87d835f8e97524b410c731a (diff)
downloadserenity-f88a0c51a329435f6a706c65a25c9cd959e26168.zip
WebDriver: Implement `POST /session/{session id}/execute/sync` endpoint
Diffstat (limited to 'Userland/Services')
-rw-r--r--Userland/Services/WebDriver/Client.cpp11
-rw-r--r--Userland/Services/WebDriver/Client.h1
-rw-r--r--Userland/Services/WebDriver/Session.cpp74
-rw-r--r--Userland/Services/WebDriver/Session.h1
4 files changed, 87 insertions, 0 deletions
diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp
index d6f477b3be..ea20088864 100644
--- a/Userland/Services/WebDriver/Client.cpp
+++ b/Userland/Services/WebDriver/Client.cpp
@@ -49,6 +49,7 @@ Vector<Client::Route> Client::s_routes = {
{ 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::POST, { "session", ":session_id", "execute", "sync" }, &Client::handle_execute_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 },
@@ -685,6 +686,16 @@ ErrorOr<JsonValue, WebDriverError> Client::handle_get_element_tag_name(Vector<St
return make_json_value(result);
}
+// 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
+// POST /session/{session id}/execute/sync
+ErrorOr<JsonValue, WebDriverError> 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]));
+ auto result = TRY(session->execute_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
ErrorOr<JsonValue, WebDriverError> Client::handle_get_all_cookies(Vector<StringView> const& parameters, JsonValue const&)
diff --git a/Userland/Services/WebDriver/Client.h b/Userland/Services/WebDriver/Client.h
index 195f59323a..33239087e1 100644
--- a/Userland/Services/WebDriver/Client.h
+++ b/Userland/Services/WebDriver/Client.h
@@ -74,6 +74,7 @@ private:
ErrorOr<JsonValue, WebDriverError> handle_get_element_css_value(Vector<StringView> const&, JsonValue const& payload);
ErrorOr<JsonValue, WebDriverError> handle_get_element_text(Vector<StringView> const&, JsonValue const& payload);
ErrorOr<JsonValue, WebDriverError> handle_get_element_tag_name(Vector<StringView> const&, JsonValue const& payload);
+ ErrorOr<JsonValue, WebDriverError> handle_execute_script(Vector<StringView> const&, JsonValue const& payload);
ErrorOr<JsonValue, WebDriverError> handle_get_all_cookies(Vector<StringView> const&, JsonValue const& payload);
ErrorOr<JsonValue, WebDriverError> handle_get_named_cookie(Vector<StringView> const&, JsonValue const& payload);
ErrorOr<JsonValue, WebDriverError> handle_add_cookie(Vector<StringView> const&, JsonValue const& payload);
diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp
index 197a61f77b..53ab8bdbd2 100644
--- a/Userland/Services/WebDriver/Session.cpp
+++ b/Userland/Services/WebDriver/Session.cpp
@@ -23,6 +23,7 @@
#include <LibGfx/Size.h>
#include <LibWeb/Cookie/Cookie.h>
#include <LibWeb/Cookie/ParsedCookie.h>
+#include <LibWeb/WebDriver/ExecuteScript.h>
#include <unistd.h>
namespace WebDriver {
@@ -899,6 +900,79 @@ ErrorOr<JsonValue, WebDriverError> Session::get_element_tag_name(JsonValue const
return JsonValue(qualified_name);
}
+struct ScriptArguments {
+ String script;
+ JsonArray const& arguments;
+};
+
+// https://w3c.github.io/webdriver/#dfn-extract-the-script-arguments-from-a-request
+static ErrorOr<ScriptArguments, WebDriverError> extract_the_script_arguments_from_a_request(JsonValue const& payload)
+{
+ if (!payload.is_object())
+ return WebDriverError::from_code(ErrorCode::InvalidArgument, "Payload is not a JSON object");
+
+ auto const& properties = payload.as_object();
+
+ // 1. Let script be the result of getting a property named script from the parameters.
+ // 2. If script is not a String, return error with error code invalid argument.
+ if (!properties.has_string("script"sv))
+ return WebDriverError::from_code(ErrorCode::InvalidArgument, "Payload doesn't have a 'script' string property");
+ auto script = properties.get("script"sv).as_string();
+
+ // 3. Let args be the result of getting a property named args from the parameters.
+ // 4. If args is not an Array return error with error code invalid argument.
+ if (!properties.has_array("args"sv))
+ return WebDriverError::from_code(ErrorCode::InvalidArgument, "Payload doesn't have an 'args' string property");
+ auto const& args = properties.get("args"sv).as_array();
+
+ // 5. Let arguments be the result of calling the JSON deserialize algorithm with arguments args.
+ // NOTE: We forward the JSON array to the Browser and then WebContent process over IPC, so this is not necessary.
+
+ // 6. Return success with data script and arguments.
+ return ScriptArguments { script, args };
+}
+
+// 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
+ErrorOr<JsonValue, WebDriverError> Session::execute_script(JsonValue const& payload)
+{
+ // 1. Let body and arguments be the result of trying to extract the script arguments from a request with argument parameters.
+ auto const& [body, arguments] = TRY(extract_the_script_arguments_from_a_request(payload));
+
+ // 2. If the current browsing context is no longer open, return error with error code no such window.
+ TRY(check_for_open_top_level_browsing_context_or_return_error());
+
+ // FIXME: 3. Handle any user prompts, and return its value if it is an error.
+
+ // 4., 5.1-5.3.
+ Vector<String> json_arguments;
+ arguments.for_each([&](JsonValue const& json_value) {
+ // NOTE: serialized() instead of to_string() ensures proper quoting.
+ json_arguments.append(json_value.serialized<StringBuilder>());
+ });
+
+ dbgln("Executing script with 'args': [{}] / 'body':\n{}", String::join(", "sv, json_arguments), body);
+ auto execute_script_response = m_browser_connection->execute_script(body, json_arguments, m_timeouts_configuration.script_timeout, false);
+ dbgln("Executing script returned: {}", execute_script_response.json_result());
+
+ // NOTE: This is assumed to be a valid JSON value.
+ auto result = MUST(JsonValue::from_string(execute_script_response.json_result()));
+
+ switch (execute_script_response.result_type()) {
+ // 6. If promise is still pending and the session script timeout is reached, return error with error code script timeout.
+ case Web::WebDriver::ExecuteScriptResultType::Timeout:
+ return WebDriverError::from_code(ErrorCode::ScriptTimeoutError, "Script timed out");
+ // 7. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
+ case Web::WebDriver::ExecuteScriptResultType::PromiseResolved:
+ return result;
+ // 8. Upon rejection of promise with reason r, let result be a JSON clone of r, and return error with error code javascript error and data result.
+ case Web::WebDriver::ExecuteScriptResultType::PromiseRejected:
+ case Web::WebDriver::ExecuteScriptResultType::JavaScriptError:
+ return WebDriverError::from_code(ErrorCode::JavascriptError, "Script returned an error", move(result));
+ default:
+ VERIFY_NOT_REACHED();
+ }
+}
+
// https://w3c.github.io/webdriver/#dfn-serialized-cookie
static JsonObject serialize_cookie(Web::Cookie::Cookie const& cookie)
{
diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h
index b0982e9ea4..9dc8943d59 100644
--- a/Userland/Services/WebDriver/Session.h
+++ b/Userland/Services/WebDriver/Session.h
@@ -63,6 +63,7 @@ public:
ErrorOr<JsonValue, WebDriverError> get_element_css_value(JsonValue const& payload, StringView element_id, StringView property_name);
ErrorOr<JsonValue, WebDriverError> get_element_text(JsonValue const& payload, StringView element_id);
ErrorOr<JsonValue, WebDriverError> get_element_tag_name(JsonValue const& payload, StringView element_id);
+ ErrorOr<JsonValue, WebDriverError> execute_script(JsonValue const& payload);
ErrorOr<JsonValue, WebDriverError> get_all_cookies();
ErrorOr<JsonValue, WebDriverError> get_named_cookie(String const& name);
ErrorOr<JsonValue, WebDriverError> add_cookie(JsonValue const& payload);