LibWeb+WebContent: Fully implement WebDriver JSON deserialization

This commit is contained in:
Timothy Flynn 2024-11-03 07:24:32 -05:00 committed by Andreas Kling
parent 7e1caf30a7
commit fd3f8b7645
Notes: github-actions[bot] 2024-11-03 17:08:21 +00:00
3 changed files with 93 additions and 49 deletions

View file

@ -12,7 +12,9 @@
#include <AK/NumericLimits.h>
#include <AK/Variant.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/JSONObject.h>
#include <LibWeb/DOM/DOMTokenList.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/HTMLCollection.h>
#include <LibWeb/DOM/NodeList.h>
#include <LibWeb/DOM/ShadowRoot.h>
@ -66,9 +68,13 @@ static bool is_collection(JS::Object const& value)
}
// https://w3c.github.io/webdriver/#dfn-clone-an-object
static Response clone_an_object(HTML::BrowsingContext const& browsing_context, JS::Object const& value, SeenMap& seen, auto const& clone_algorithm)
template<typename ResultType, typename CloneAlgorithm>
static ErrorOr<ResultType, WebDriver::Error> clone_an_object(HTML::BrowsingContext const& browsing_context, JS::Object const& value, SeenMap& seen, CloneAlgorithm const& clone_algorithm)
{
auto& vm = browsing_context.vm();
static constexpr bool is_json_value = IsSame<ResultType, JsonValue>;
auto& realm = browsing_context.active_document()->realm();
auto& vm = realm.vm();
// 1. If value is in seen, return error with error code javascript error.
if (seen.contains(value))
@ -78,7 +84,7 @@ static Response clone_an_object(HTML::BrowsingContext const& browsing_context, J
seen.set(value);
// 3. Let result be the value of the first matching statement, matching on value:
auto result = TRY(([&]() -> Response {
auto result = TRY(([&]() -> ErrorOr<ResultType, WebDriver::Error> {
// -> a collection
if (is_collection(value)) {
// A new Array which length property is equal to the result of getting the property length of value.
@ -88,12 +94,18 @@ static Response clone_an_object(HTML::BrowsingContext const& browsing_context, J
if (length > NumericLimits<u32>::max())
return WebDriver::Error::from_code(ErrorCode::JavascriptError, "Length of Object too large"sv);
return JsonValue { JsonArray { length } };
if constexpr (is_json_value)
return JsonArray { length };
else
return TRY_OR_JS_ERROR(JS::Array::create(realm, length));
}
// -> Otherwise
else {
// A new Object.
return JsonValue { JsonObject {} };
if constexpr (is_json_value)
return JsonObject {};
else
return JS::Object::create(realm, realm.intrinsics().object_prototype());
}
}()));
@ -119,10 +131,14 @@ static Response clone_an_object(HTML::BrowsingContext const& browsing_context, J
// 4. If cloned property result is a success, set a property of result with name name and value equal to cloned
// property result's data.
if (!cloned_property_result.is_error()) {
if (result.is_array() && name.is_number())
result.as_array().set(name.as_number(), cloned_property_result.value());
else if (result.is_object())
result.as_object().set(name.to_string(), cloned_property_result.value());
if constexpr (is_json_value) {
if (result.is_array() && name.is_number())
result.as_array().set(name.as_number(), cloned_property_result.value());
else if (result.is_object())
result.as_object().set(name.to_string(), cloned_property_result.value());
} else {
(void)result->set(name, cloned_property_result.value(), JS::Object::ShouldThrowExceptions::No);
}
}
// 5. Otherwise, return cloned property result.
else {
@ -246,7 +262,7 @@ static Response internal_json_clone(HTML::BrowsingContext const& browsing_contex
// -> Otherwise
// 1. Let result be clone an object with session value and seen, and internal JSON clone as the clone algorithm.
auto result = TRY(clone_an_object(browsing_context, object, seen, internal_json_clone));
auto result = TRY(clone_an_object<JsonValue>(browsing_context, object, seen, internal_json_clone));
// 2. Return success with data result.
return result;
@ -261,4 +277,64 @@ Response json_clone(HTML::BrowsingContext const& browsing_context, JS::Value val
return internal_json_clone(browsing_context, value, seen);
}
// https://w3c.github.io/webdriver/#dfn-json-deserialize
static ErrorOr<JS::Value, WebDriver::Error> internal_json_deserialize(HTML::BrowsingContext const& browsing_context, JS::Value value, SeenMap& seen)
{
// 1. If seen is not provided, let seen be an empty List.
// 2. Jump to the first appropriate step below:
// 3. Matching on value:
// -> undefined
// -> null
// -> type Boolean
// -> type Number
// -> type String
if (value.is_nullish() || value.is_boolean() || value.is_number() || value.is_string()) {
// Return success with data value.
return value;
}
// -> Object that represents a web element
if (represents_a_web_element(value)) {
// Return the deserialized web element of value.
return deserialize_web_element(browsing_context, value.as_object());
}
// -> Object that represents a shadow root
if (represents_a_shadow_root(value)) {
// Return the deserialized shadow root of value.
return deserialize_shadow_root(browsing_context, value.as_object());
}
// -> Object that represents a web frame
if (represents_a_web_frame(value)) {
// Return the deserialized web frame of value.
return deserialize_web_frame(value.as_object());
}
// -> Object that represents a web window
if (represents_a_web_window(value)) {
// Return the deserialized web window of value.
return deserialize_web_window(value.as_object());
}
// -> instance of Array
// -> instance of Object
if (value.is_object()) {
// Return clone an object algorithm with session, value and seen, and the JSON deserialize algorithm as the
// clone algorithm.
return clone_an_object<JS::NonnullGCPtr<JS::Object>>(browsing_context, value.as_object(), seen, internal_json_deserialize);
}
return WebDriver::Error::from_code(ErrorCode::JavascriptError, "Unrecognized value type"sv);
}
// https://w3c.github.io/webdriver/#dfn-json-deserialize
ErrorOr<JS::Value, WebDriver::Error> json_deserialize(HTML::BrowsingContext const& browsing_context, JsonValue const& value)
{
auto& vm = browsing_context.vm();
SeenMap seen;
return internal_json_deserialize(browsing_context, JS::JSONObject::parse_json_value(vm, value), seen);
}
}

View file

@ -14,5 +14,6 @@
namespace Web::WebDriver {
Response json_clone(HTML::BrowsingContext const&, JS::Value);
ErrorOr<JS::Value, WebDriver::Error> json_deserialize(HTML::BrowsingContext const&, JsonValue const&);
}

View file

@ -14,7 +14,6 @@
#include <AK/Time.h>
#include <AK/Vector.h>
#include <LibCore/File.h>
#include <LibJS/Runtime/JSONObject.h>
#include <LibJS/Runtime/Value.h>
#include <LibWeb/CSS/CSSStyleValue.h>
#include <LibWeb/CSS/PropertyID.h>
@ -2816,41 +2815,6 @@ void WebDriverConnection::find(Web::WebDriver::LocationStrategy location_strateg
m_element_locator->search_for_element();
}
// https://w3c.github.io/webdriver/#dfn-json-deserialize
static ErrorOr<JS::Value, Web::WebDriver::Error> json_deserialize(JS::VM& vm, Web::HTML::BrowsingContext const& browsing_context, JsonValue const& value)
{
// 1. If seen is not provided, let seen be an empty List.
// 2. Jump to the first appropriate step below:
// 3. Matching on value:
// -> undefined
// -> null
// -> type Boolean
// -> type Number
// -> type String
if (value.is_null() || value.is_bool() || value.is_number() || value.is_string()) {
// Return success with data value.
return JS::JSONObject::parse_json_value(vm, value);
}
// -> Object that represents a web element
if (Web::WebDriver::represents_a_web_element(value)) {
// Return the deserialized web element of value.
return Web::WebDriver::deserialize_web_element(browsing_context, value.as_object());
}
// FIXME: -> Object that represents a shadow root
// Return the deserialized shadow root of value.
// FIXME: -> Object that represents a web frame
// Return the deserialized web frame of value.
// FIXME: -> Object that represents a web window
// Return the deserialized web window of value.
// FIXME: -> instance of Array
// FIXME: -> instance of Object
// Return clone an object algorithm with session, value and seen, and the JSON deserialize algorithm as the clone algorithm.
dbgln("FIXME: Implement JSON deserialize for: {}", value);
return JS::JSONObject::parse_json_value(vm, value);
}
// 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(JS::VM& vm, JsonValue const& payload)
{
@ -2866,10 +2830,13 @@ ErrorOr<WebDriverConnection::ScriptArguments, Web::WebDriver::Error> WebDriverCo
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() };
JS::MarkedVector<JS::Value> arguments { vm.heap() };
auto& browsing_context = current_browsing_context();
TRY(args.try_for_each([&](JsonValue const& arg) -> ErrorOr<void, Web::WebDriver::Error> {
auto deserialized = TRY(Web::WebDriver::json_deserialize(browsing_context, arg));
arguments.append(deserialized);
TRY(args.try_for_each([&](auto const& arg) -> ErrorOr<void, Web::WebDriver::Error> {
arguments.append(TRY(json_deserialize(vm, current_browsing_context(), arg)));
return {};
}));