WebContent+WebDriver: Move Execute Script to WebContent
This commit is contained in:
parent
88dcdf681f
commit
0b9803dc93
Notes:
sideshowbarker
2024-07-17 06:35:23 +09:00
Author: https://github.com/trflynn89 Commit: https://github.com/SerenityOS/serenity/commit/0b9803dc93 Pull-request: https://github.com/SerenityOS/serenity/pull/16019 Reviewed-by: https://github.com/linusg ✅
6 changed files with 83 additions and 48 deletions
|
@ -22,6 +22,7 @@ endpoint WebDriverClient {
|
||||||
get_element_rect(String element_id) => (Web::WebDriver::Response response)
|
get_element_rect(String element_id) => (Web::WebDriver::Response response)
|
||||||
is_element_enabled(String element_id) => (Web::WebDriver::Response response)
|
is_element_enabled(String element_id) => (Web::WebDriver::Response response)
|
||||||
get_source() => (Web::WebDriver::Response response)
|
get_source() => (Web::WebDriver::Response response)
|
||||||
|
execute_script(JsonValue payload) => (Web::WebDriver::Response response)
|
||||||
take_screenshot() => (Web::WebDriver::Response response)
|
take_screenshot() => (Web::WebDriver::Response response)
|
||||||
take_element_screenshot(String element_id) => (Web::WebDriver::Response response)
|
take_element_screenshot(String element_id) => (Web::WebDriver::Response response)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <AK/JsonObject.h>
|
#include <AK/JsonObject.h>
|
||||||
#include <AK/JsonValue.h>
|
#include <AK/JsonValue.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibJS/Runtime/JSONObject.h>
|
||||||
#include <LibJS/Runtime/Value.h>
|
#include <LibJS/Runtime/Value.h>
|
||||||
#include <LibWeb/CSS/PropertyID.h>
|
#include <LibWeb/CSS/PropertyID.h>
|
||||||
#include <LibWeb/CSS/StyleProperties.h>
|
#include <LibWeb/CSS/StyleProperties.h>
|
||||||
|
@ -155,7 +156,8 @@ static void scroll_element_into_view(Web::DOM::Element& element)
|
||||||
element.scroll_into_view(options);
|
element.scroll_into_view(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ErrorOr<String, Web::WebDriver::Error> get_property(JsonValue const& payload, StringView key)
|
template<typename PropertyType = String>
|
||||||
|
static ErrorOr<PropertyType, Web::WebDriver::Error> get_property(JsonValue const& payload, StringView key)
|
||||||
{
|
{
|
||||||
if (!payload.is_object())
|
if (!payload.is_object())
|
||||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
|
||||||
|
@ -164,10 +166,19 @@ static ErrorOr<String, Web::WebDriver::Error> get_property(JsonValue const& payl
|
||||||
|
|
||||||
if (!property)
|
if (!property)
|
||||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("No property called '{}' present", key));
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("No property called '{}' present", key));
|
||||||
if (!property->is_string())
|
|
||||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not a String", key));
|
|
||||||
|
|
||||||
return property->as_string();
|
if constexpr (IsSame<PropertyType, String>) {
|
||||||
|
if (!property->is_string())
|
||||||
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not a String", key));
|
||||||
|
return property->as_string();
|
||||||
|
} else if constexpr (IsSame<PropertyType, JsonArray const*>) {
|
||||||
|
if (!property->is_array())
|
||||||
|
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Property '{}' is not an Array", key));
|
||||||
|
return &property->as_array();
|
||||||
|
} else {
|
||||||
|
static_assert(DependentFalse<PropertyType>, "get_property invoked with unknown property type");
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<NonnullRefPtr<WebDriverConnection>> WebDriverConnection::connect(ConnectionFromClient& web_content_client, PageHost& page_host, String const& webdriver_ipc_path)
|
ErrorOr<NonnullRefPtr<WebDriverConnection>> WebDriverConnection::connect(ConnectionFromClient& web_content_client, PageHost& page_host, String const& webdriver_ipc_path)
|
||||||
|
@ -754,6 +765,38 @@ Messages::WebDriverClient::GetSourceResponse WebDriverConnection::get_source()
|
||||||
return make_success_response(source.release_value());
|
return make_success_response(source.release_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
|
||||||
|
Messages::WebDriverClient::ExecuteScriptResponse WebDriverConnection::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(ensure_open_top_level_browsing_context());
|
||||||
|
|
||||||
|
// FIXME: 3. Handle any user prompts, and return its value if it is an error.
|
||||||
|
|
||||||
|
// 4., 5.1-5.3.
|
||||||
|
// FIXME: Move timeouts from WebDriver to WebContent and pass the script timeout through here.
|
||||||
|
auto result = Web::WebDriver::execute_script(m_page_host.page(), body, move(arguments), {});
|
||||||
|
dbgln_if(WEBDRIVER_DEBUG, "Executing script returned: {}", result.value);
|
||||||
|
|
||||||
|
switch (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 Web::WebDriver::Error::from_code(Web::WebDriver::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 make_success_response(move(result.value));
|
||||||
|
// 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 Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::JavascriptError, "Script returned an error", move(result.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
// 17.1 Take Screenshot, https://w3c.github.io/webdriver/#take-screenshot
|
// 17.1 Take Screenshot, https://w3c.github.io/webdriver/#take-screenshot
|
||||||
Messages::WebDriverClient::TakeScreenshotResponse WebDriverConnection::take_screenshot()
|
Messages::WebDriverClient::TakeScreenshotResponse WebDriverConnection::take_screenshot()
|
||||||
{
|
{
|
||||||
|
@ -892,4 +935,29 @@ ErrorOr<JsonArray, Web::WebDriver::Error> WebDriverConnection::find(Web::DOM::Pa
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://w3c.github.io/webdriver/#dfn-extract-the-script-arguments-from-a-request
|
||||||
|
ErrorOr<WebDriverConnection::ScriptArguments, Web::WebDriver::Error> WebDriverConnection::extract_the_script_arguments_from_a_request(JsonValue const& payload)
|
||||||
|
{
|
||||||
|
auto* window = m_page_host.page().top_level_browsing_context().active_window();
|
||||||
|
auto& vm = window->vm();
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
auto script = TRY(get_property(payload, "script"sv));
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
auto const& args = *TRY(get_property<JsonArray const*>(payload, "args"sv));
|
||||||
|
|
||||||
|
// 5. Let arguments be the result of calling the JSON deserialize algorithm with arguments args.
|
||||||
|
auto arguments = JS::MarkedVector<JS::Value> { vm.heap() };
|
||||||
|
|
||||||
|
args.for_each([&](auto const& arg) {
|
||||||
|
arguments.append(JS::JSONObject::parse_json_value(vm, arg));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 6. Return success with data script and arguments.
|
||||||
|
return ScriptArguments { move(script), move(arguments) };
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <LibIPC/ConnectionToServer.h>
|
#include <LibIPC/ConnectionToServer.h>
|
||||||
|
#include <LibJS/Forward.h>
|
||||||
|
#include <LibJS/Heap/MarkedVector.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
#include <LibWeb/WebDriver/ElementLocationStrategies.h>
|
#include <LibWeb/WebDriver/ElementLocationStrategies.h>
|
||||||
#include <LibWeb/WebDriver/Response.h>
|
#include <LibWeb/WebDriver/Response.h>
|
||||||
|
@ -52,6 +54,7 @@ private:
|
||||||
virtual Messages::WebDriverClient::GetElementRectResponse get_element_rect(String const& element_id) override;
|
virtual Messages::WebDriverClient::GetElementRectResponse get_element_rect(String const& element_id) override;
|
||||||
virtual Messages::WebDriverClient::IsElementEnabledResponse is_element_enabled(String const& element_id) override;
|
virtual Messages::WebDriverClient::IsElementEnabledResponse is_element_enabled(String const& element_id) override;
|
||||||
virtual Messages::WebDriverClient::GetSourceResponse get_source() override;
|
virtual Messages::WebDriverClient::GetSourceResponse get_source() override;
|
||||||
|
virtual Messages::WebDriverClient::ExecuteScriptResponse execute_script(JsonValue const& payload) override;
|
||||||
virtual Messages::WebDriverClient::TakeScreenshotResponse take_screenshot() override;
|
virtual Messages::WebDriverClient::TakeScreenshotResponse take_screenshot() override;
|
||||||
virtual Messages::WebDriverClient::TakeElementScreenshotResponse take_element_screenshot(String const& element_id) override;
|
virtual Messages::WebDriverClient::TakeElementScreenshotResponse take_element_screenshot(String const& element_id) override;
|
||||||
|
|
||||||
|
@ -61,6 +64,12 @@ private:
|
||||||
Gfx::IntRect iconify_the_window();
|
Gfx::IntRect iconify_the_window();
|
||||||
ErrorOr<JsonArray, Web::WebDriver::Error> find(Web::DOM::ParentNode& start_node, Web::WebDriver::LocationStrategy using_, StringView value);
|
ErrorOr<JsonArray, Web::WebDriver::Error> find(Web::DOM::ParentNode& start_node, Web::WebDriver::LocationStrategy using_, StringView value);
|
||||||
|
|
||||||
|
struct ScriptArguments {
|
||||||
|
String script;
|
||||||
|
JS::MarkedVector<JS::Value> arguments;
|
||||||
|
};
|
||||||
|
ErrorOr<ScriptArguments, Web::WebDriver::Error> extract_the_script_arguments_from_a_request(JsonValue const& payload);
|
||||||
|
|
||||||
ConnectionFromClient& m_web_content_client;
|
ConnectionFromClient& m_web_content_client;
|
||||||
PageHost& m_page_host;
|
PageHost& m_page_host;
|
||||||
};
|
};
|
||||||
|
|
|
@ -735,8 +735,7 @@ Web::WebDriver::Response Client::handle_execute_script(Vector<StringView> const&
|
||||||
{
|
{
|
||||||
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/execute/sync");
|
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/execute/sync");
|
||||||
auto* session = TRY(find_session_with_id(parameters[0]));
|
auto* session = TRY(find_session_with_id(parameters[0]));
|
||||||
auto result = TRY(session->execute_script(payload));
|
return session->web_content_connection().execute_script(payload);
|
||||||
return make_json_value(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.2.2 Execute Async Script, https://w3c.github.io/webdriver/#dfn-execute-async-script
|
// 13.2.2 Execute Async Script, https://w3c.github.io/webdriver/#dfn-execute-async-script
|
||||||
|
|
|
@ -329,47 +329,6 @@ static ErrorOr<ScriptArguments, Web::WebDriver::Error> extract_the_script_argume
|
||||||
return ScriptArguments { script, args };
|
return ScriptArguments { script, args };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
|
|
||||||
Web::WebDriver::Response 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 Web::WebDriver::Error::from_code(Web::WebDriver::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 Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::JavascriptError, "Script returned an error", move(result));
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 13.2.2 Execute Async Script, https://w3c.github.io/webdriver/#dfn-execute-async-script
|
// 13.2.2 Execute Async Script, https://w3c.github.io/webdriver/#dfn-execute-async-script
|
||||||
Web::WebDriver::Response Session::execute_async_script(JsonValue const& parameters)
|
Web::WebDriver::Response Session::execute_async_script(JsonValue const& parameters)
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,7 +58,6 @@ public:
|
||||||
Web::WebDriver::Response get_window_handle();
|
Web::WebDriver::Response get_window_handle();
|
||||||
ErrorOr<void, Variant<Web::WebDriver::Error, Error>> close_window();
|
ErrorOr<void, Variant<Web::WebDriver::Error, Error>> close_window();
|
||||||
Web::WebDriver::Response get_window_handles() const;
|
Web::WebDriver::Response get_window_handles() const;
|
||||||
Web::WebDriver::Response execute_script(JsonValue const& payload);
|
|
||||||
Web::WebDriver::Response execute_async_script(JsonValue const& payload);
|
Web::WebDriver::Response execute_async_script(JsonValue const& payload);
|
||||||
Web::WebDriver::Response get_all_cookies();
|
Web::WebDriver::Response get_all_cookies();
|
||||||
Web::WebDriver::Response get_named_cookie(String const& name);
|
Web::WebDriver::Response get_named_cookie(String const& name);
|
||||||
|
|
Loading…
Add table
Reference in a new issue