diff --git a/Userland/Libraries/LibWeb/WebDriver/JSON.cpp b/Userland/Libraries/LibWeb/WebDriver/JSON.cpp index efda7594e9f..c10d3624a08 100644 --- a/Userland/Libraries/LibWeb/WebDriver/JSON.cpp +++ b/Userland/Libraries/LibWeb/WebDriver/JSON.cpp @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -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 +static ErrorOr 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; + + 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 { // -> 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::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(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 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>(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 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); +} + } diff --git a/Userland/Libraries/LibWeb/WebDriver/JSON.h b/Userland/Libraries/LibWeb/WebDriver/JSON.h index ccc7a8125a5..5cf354c2611 100644 --- a/Userland/Libraries/LibWeb/WebDriver/JSON.h +++ b/Userland/Libraries/LibWeb/WebDriver/JSON.h @@ -14,5 +14,6 @@ namespace Web::WebDriver { Response json_clone(HTML::BrowsingContext const&, JS::Value); +ErrorOr json_deserialize(HTML::BrowsingContext const&, JsonValue const&); } diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index 4d23af3ff8c..d5dd4147946 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -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 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::extract_the_script_arguments_from_a_request(JS::VM& vm, JsonValue const& payload) { @@ -2866,10 +2830,13 @@ ErrorOr WebDriverCo auto const& args = *TRY(Web::WebDriver::get_property(payload, "args"sv)); // 5. Let arguments be the result of calling the JSON deserialize algorithm with arguments args. - auto arguments = JS::MarkedVector { vm.heap() }; + JS::MarkedVector arguments { vm.heap() }; + auto& browsing_context = current_browsing_context(); + + TRY(args.try_for_each([&](JsonValue const& arg) -> ErrorOr { + auto deserialized = TRY(Web::WebDriver::json_deserialize(browsing_context, arg)); + arguments.append(deserialized); - TRY(args.try_for_each([&](auto const& arg) -> ErrorOr { - arguments.append(TRY(json_deserialize(vm, current_browsing_context(), arg))); return {}; }));