diff options
author | Linus Groh <mail@linusgroh.de> | 2022-11-02 18:11:04 +0000 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-11-02 23:46:30 +0000 |
commit | f88a0c51a329435f6a706c65a25c9cd959e26168 (patch) | |
tree | e5f88861f8007286ee923d0d02e2b50b978a02bd /Userland/Services | |
parent | 6e1131e6dee6d0c8f87d835f8e97524b410c731a (diff) | |
download | serenity-f88a0c51a329435f6a706c65a25c9cd959e26168.zip |
WebDriver: Implement `POST /session/{session id}/execute/sync` endpoint
Diffstat (limited to 'Userland/Services')
-rw-r--r-- | Userland/Services/WebDriver/Client.cpp | 11 | ||||
-rw-r--r-- | Userland/Services/WebDriver/Client.h | 1 | ||||
-rw-r--r-- | Userland/Services/WebDriver/Session.cpp | 74 | ||||
-rw-r--r-- | Userland/Services/WebDriver/Session.h | 1 |
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); |