LibWeb+WebContent: Move WebDriver property extraction to a helper file

This will be needed outside of WebContent.
This commit is contained in:
Timothy Flynn 2024-09-26 11:35:28 -04:00 committed by Andreas Kling
parent 5e99715377
commit 8944c6db3f
Notes: github-actions[bot] 2024-10-01 09:04:02 +00:00
2 changed files with 80 additions and 62 deletions

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <LibWeb/WebDriver/Error.h>
namespace Web::WebDriver {
template<typename PropertyType = ByteString>
static ErrorOr<PropertyType, WebDriver::Error> get_property(JsonValue const& payload, StringView key)
{
if (!payload.is_object())
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, "Payload is not a JSON object");
auto property = payload.as_object().get(key);
if (!property.has_value())
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, ByteString::formatted("No property called '{}' present", key));
if constexpr (IsSame<PropertyType, ByteString>) {
if (!property->is_string())
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not a String", key));
return property->as_string();
} else if constexpr (IsSame<PropertyType, bool>) {
if (!property->is_bool())
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not a Boolean", key));
return property->as_bool();
} else if constexpr (IsSame<PropertyType, u32>) {
if (auto maybe_u32 = property->get_u32(); maybe_u32.has_value())
return *maybe_u32;
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not a Number", key));
} else if constexpr (IsSame<PropertyType, JsonArray const*>) {
if (!property->is_array())
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not an Array", key));
return &property->as_array();
} else if constexpr (IsSame<PropertyType, JsonObject const*>) {
if (!property->is_object())
return WebDriver::Error::from_code(ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not an Object", key));
return &property->as_object();
} else {
static_assert(DependentFalse<PropertyType>, "get_property invoked with unknown property type");
VERIFY_NOT_REACHED();
}
}
}

View file

@ -3,7 +3,7 @@
* Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -46,6 +46,7 @@
#include <LibWeb/UIEvents/MouseEvent.h>
#include <LibWeb/WebDriver/ElementReference.h>
#include <LibWeb/WebDriver/ExecuteScript.h>
#include <LibWeb/WebDriver/Properties.h>
#include <LibWeb/WebDriver/Screenshot.h>
#include <WebContent/WebDriverConnection.h>
@ -115,43 +116,6 @@ static ErrorOr<void> scroll_element_into_view(Web::DOM::Element& element)
return {};
}
template<typename PropertyType = ByteString>
static ErrorOr<PropertyType, Web::WebDriver::Error> get_property(JsonValue const& payload, StringView key)
{
if (!payload.is_object())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
auto property = payload.as_object().get(key);
if (!property.has_value())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, ByteString::formatted("No property called '{}' present", key));
if constexpr (IsSame<PropertyType, ByteString>) {
if (!property->is_string())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not a String", key));
return property->as_string();
} else if constexpr (IsSame<PropertyType, bool>) {
if (!property->is_bool())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not a Boolean", key));
return property->as_bool();
} else if constexpr (IsSame<PropertyType, u32>) {
if (auto maybe_u32 = property->get_u32(); maybe_u32.has_value())
return *maybe_u32;
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not a Number", key));
} else if constexpr (IsSame<PropertyType, JsonArray const*>) {
if (!property->is_array())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not an Array", key));
return &property->as_array();
} else if constexpr (IsSame<PropertyType, JsonObject const*>) {
if (!property->is_object())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, ByteString::formatted("Property '{}' is not an Object", key));
return &property->as_object();
} else {
static_assert(DependentFalse<PropertyType>, "get_property invoked with unknown property type");
VERIFY_NOT_REACHED();
}
}
// https://w3c.github.io/webdriver/#dfn-container
static Optional<Web::DOM::Element&> container_for_element(Web::DOM::Element& element)
{
@ -800,7 +764,7 @@ Messages::WebDriverClient::ConsumeUserActivationResponse WebDriverConnection::co
Messages::WebDriverClient::FindElementResponse WebDriverConnection::find_element(JsonValue const& payload)
{
// 1. Let location strategy be the result of getting a property called "using".
auto location_strategy_string = TRY(get_property(payload, "using"sv));
auto location_strategy_string = TRY(Web::WebDriver::get_property(payload, "using"sv));
auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
// 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
@ -809,7 +773,7 @@ Messages::WebDriverClient::FindElementResponse WebDriverConnection::find_element
// 3. Let selector be the result of getting a property called "value".
// 4. If selector is undefined, return error with error code invalid argument.
auto selector = TRY(get_property(payload, "value"sv));
auto selector = TRY(Web::WebDriver::get_property(payload, "value"sv));
// 5. If the current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open());
@ -842,7 +806,7 @@ Messages::WebDriverClient::FindElementResponse WebDriverConnection::find_element
Messages::WebDriverClient::FindElementsResponse WebDriverConnection::find_elements(JsonValue const& payload)
{
// 1. Let location strategy be the result of getting a property called "using".
auto location_strategy_string = TRY(get_property(payload, "using"sv));
auto location_strategy_string = TRY(Web::WebDriver::get_property(payload, "using"sv));
auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
// 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
@ -851,7 +815,7 @@ Messages::WebDriverClient::FindElementsResponse WebDriverConnection::find_elemen
// 3. Let selector be the result of getting a property called "value".
// 4. If selector is undefined, return error with error code invalid argument.
auto selector = TRY(get_property(payload, "value"sv));
auto selector = TRY(Web::WebDriver::get_property(payload, "value"sv));
// 5. If the current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open());
@ -878,7 +842,7 @@ Messages::WebDriverClient::FindElementsResponse WebDriverConnection::find_elemen
Messages::WebDriverClient::FindElementFromElementResponse WebDriverConnection::find_element_from_element(JsonValue const& payload, String const& element_id)
{
// 1. Let location strategy be the result of getting a property called "using".
auto location_strategy_string = TRY(get_property(payload, "using"sv));
auto location_strategy_string = TRY(Web::WebDriver::get_property(payload, "using"sv));
auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
// 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
@ -887,7 +851,7 @@ Messages::WebDriverClient::FindElementFromElementResponse WebDriverConnection::f
// 3. Let selector be the result of getting a property called "value".
// 4. If selector is undefined, return error with error code invalid argument.
auto selector = TRY(get_property(payload, "value"sv));
auto selector = TRY(Web::WebDriver::get_property(payload, "value"sv));
// 5. If the current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open());
@ -914,7 +878,7 @@ Messages::WebDriverClient::FindElementFromElementResponse WebDriverConnection::f
Messages::WebDriverClient::FindElementsFromElementResponse WebDriverConnection::find_elements_from_element(JsonValue const& payload, String const& element_id)
{
// 1. Let location strategy be the result of getting a property called "using".
auto location_strategy_string = TRY(get_property(payload, "using"sv));
auto location_strategy_string = TRY(Web::WebDriver::get_property(payload, "using"sv));
auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
// 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
@ -923,7 +887,7 @@ Messages::WebDriverClient::FindElementsFromElementResponse WebDriverConnection::
// 3. Let selector be the result of getting a property called "value".
// 4. If selector is undefined, return error with error code invalid argument.
auto selector = TRY(get_property(payload, "value"sv));
auto selector = TRY(Web::WebDriver::get_property(payload, "value"sv));
// 5. If the current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open());
@ -944,7 +908,7 @@ Messages::WebDriverClient::FindElementsFromElementResponse WebDriverConnection::
Messages::WebDriverClient::FindElementFromShadowRootResponse WebDriverConnection::find_element_from_shadow_root(JsonValue const& payload, String const& shadow_id)
{
// 1. Let location strategy be the result of getting a property called "using".
auto location_strategy_string = TRY(get_property(payload, "using"sv));
auto location_strategy_string = TRY(Web::WebDriver::get_property(payload, "using"sv));
auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
// 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
@ -953,7 +917,7 @@ Messages::WebDriverClient::FindElementFromShadowRootResponse WebDriverConnection
// 3. Let selector be the result of getting a property called "value".
// 4. If selector is undefined, return error with error code invalid argument.
auto selector = TRY(get_property(payload, "value"sv));
auto selector = TRY(Web::WebDriver::get_property(payload, "value"sv));
// 5. If the current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open());
@ -980,7 +944,7 @@ Messages::WebDriverClient::FindElementFromShadowRootResponse WebDriverConnection
Messages::WebDriverClient::FindElementsFromShadowRootResponse WebDriverConnection::find_elements_from_shadow_root(JsonValue const& payload, String const& shadow_id)
{
// 1. Let location strategy be the result of getting a property called "using".
auto location_strategy_string = TRY(get_property(payload, "using"sv));
auto location_strategy_string = TRY(Web::WebDriver::get_property(payload, "using"sv));
auto location_strategy = Web::WebDriver::location_strategy_from_string(location_strategy_string);
// 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument.
@ -989,7 +953,7 @@ Messages::WebDriverClient::FindElementsFromShadowRootResponse WebDriverConnectio
// 3. Let selector be the result of getting a property called "value".
// 4. If selector is undefined, return error with error code invalid argument.
auto selector = TRY(get_property(payload, "value"sv));
auto selector = TRY(Web::WebDriver::get_property(payload, "value"sv));
// 5. If the current browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_browsing_context_is_open());
@ -1819,7 +1783,7 @@ Messages::WebDriverClient::GetNamedCookieResponse WebDriverConnection::get_named
Messages::WebDriverClient::AddCookieResponse WebDriverConnection::add_cookie(JsonValue const& payload)
{
// 1. Let data be the result of getting a property named cookie from the parameters argument.
auto const& data = *TRY(get_property<JsonObject const*>(payload, "cookie"sv));
auto const& data = *TRY(Web::WebDriver::get_property<JsonObject const*>(payload, "cookie"sv));
// 2. If data is not a JSON Object with all the required (non-optional) JSON keys listed in the table for cookie conversion, return error with error code invalid argument.
// NOTE: This validation is performed in subsequent steps.
@ -1837,13 +1801,13 @@ Messages::WebDriverClient::AddCookieResponse WebDriverConnection::add_cookie(Jso
// 7. Create a cookie in the cookie store associated with the active documents address using cookie name name, cookie value value, and an attribute-value list of the following cookie concepts listed in the table for cookie conversion from data:
Web::Cookie::ParsedCookie cookie {};
cookie.name = MUST(String::from_byte_string(TRY(get_property(data, "name"sv))));
cookie.value = MUST(String::from_byte_string(TRY(get_property(data, "value"sv))));
cookie.name = MUST(String::from_byte_string(TRY(Web::WebDriver::get_property(data, "name"sv))));
cookie.value = MUST(String::from_byte_string(TRY(Web::WebDriver::get_property(data, "value"sv))));
// Cookie path
// The value if the entry exists, otherwise "/".
if (data.has("path"sv))
cookie.path = MUST(String::from_byte_string(TRY(get_property(data, "path"sv))));
cookie.path = MUST(String::from_byte_string(TRY(Web::WebDriver::get_property(data, "path"sv))));
else
cookie.path = "/"_string;
@ -1851,30 +1815,30 @@ Messages::WebDriverClient::AddCookieResponse WebDriverConnection::add_cookie(Jso
// The value if the entry exists, otherwise the current browsing contexts active documents URL domain.
// NOTE: The otherwise case is handled by the CookieJar
if (data.has("domain"sv))
cookie.domain = MUST(String::from_byte_string(TRY(get_property(data, "domain"sv))));
cookie.domain = MUST(String::from_byte_string(TRY(Web::WebDriver::get_property(data, "domain"sv))));
// Cookie secure only
// The value if the entry exists, otherwise false.
if (data.has("secure"sv))
cookie.secure_attribute_present = TRY(get_property<bool>(data, "secure"sv));
cookie.secure_attribute_present = TRY(Web::WebDriver::get_property<bool>(data, "secure"sv));
// Cookie HTTP only
// The value if the entry exists, otherwise false.
if (data.has("httpOnly"sv))
cookie.http_only_attribute_present = TRY(get_property<bool>(data, "httpOnly"sv));
cookie.http_only_attribute_present = TRY(Web::WebDriver::get_property<bool>(data, "httpOnly"sv));
// Cookie expiry time
// The value if the entry exists, otherwise leave unset to indicate that this is a session cookie.
if (data.has("expiry"sv)) {
// NOTE: less than 0 or greater than safe integer are handled by the JSON parser
auto expiry = TRY(get_property<u32>(data, "expiry"sv));
auto expiry = TRY(Web::WebDriver::get_property<u32>(data, "expiry"sv));
cookie.expiry_time_from_expires_attribute = UnixDateTime::from_seconds_since_epoch(expiry);
}
// Cookie same site
// The value if the entry exists, otherwise leave unset to indicate that no same site policy is defined.
if (data.has("sameSite"sv)) {
auto same_site = TRY(get_property(data, "sameSite"sv));
auto same_site = TRY(Web::WebDriver::get_property(data, "sameSite"sv));
cookie.same_site_attribute = Web::Cookie::same_site_from_string(same_site);
}
@ -2012,7 +1976,7 @@ Messages::WebDriverClient::SendAlertTextResponse WebDriverConnection::send_alert
{
// 1. Let text be the result of getting the property "text" from parameters.
// 2. If text is not a String, return error with error code invalid argument.
auto text = TRY(get_property(payload, "text"sv));
auto text = TRY(Web::WebDriver::get_property(payload, "text"sv));
// 3. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY(ensure_current_top_level_browsing_context_is_open());
@ -2356,11 +2320,11 @@ ErrorOr<WebDriverConnection::ScriptArguments, Web::WebDriver::Error> WebDriverCo
// 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));
auto script = TRY(Web::WebDriver::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));
auto const& args = *TRY(Web::WebDriver::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() };