From 8944c6db3f84ed72f5f2f18bf39b103492a21493 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 26 Sep 2024 11:35:28 -0400 Subject: [PATCH] LibWeb+WebContent: Move WebDriver property extraction to a helper file This will be needed outside of WebContent. --- .../Libraries/LibWeb/WebDriver/Properties.h | 54 ++++++++++++ .../WebContent/WebDriverConnection.cpp | 88 ++++++------------- 2 files changed, 80 insertions(+), 62 deletions(-) create mode 100644 Userland/Libraries/LibWeb/WebDriver/Properties.h diff --git a/Userland/Libraries/LibWeb/WebDriver/Properties.h b/Userland/Libraries/LibWeb/WebDriver/Properties.h new file mode 100644 index 00000000000..4f25c1851dc --- /dev/null +++ b/Userland/Libraries/LibWeb/WebDriver/Properties.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web::WebDriver { + +template +static ErrorOr 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) { + 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) { + 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) { + 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) { + 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) { + 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, "get_property invoked with unknown property type"); + VERIFY_NOT_REACHED(); + } +} + +} diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index 071c14a6539..a96d1fdac72 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -3,7 +3,7 @@ * Copyright (c) 2022-2023, Sam Atkins * Copyright (c) 2022, Tobias Christiansen * Copyright (c) 2022, Linus Groh - * Copyright (c) 2022-2024, Tim Flynn + * Copyright (c) 2022-2024, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -115,43 +116,6 @@ static ErrorOr scroll_element_into_view(Web::DOM::Element& element) return {}; } -template -static ErrorOr 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) { - 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) { - 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) { - 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) { - 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) { - 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, "get_property invoked with unknown property type"); - VERIFY_NOT_REACHED(); - } -} - // https://w3c.github.io/webdriver/#dfn-container static Optional 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(payload, "cookie"sv)); + auto const& data = *TRY(Web::WebDriver::get_property(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 document’s 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 context’s active document’s 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(data, "secure"sv)); + cookie.secure_attribute_present = TRY(Web::WebDriver::get_property(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(data, "httpOnly"sv)); + cookie.http_only_attribute_present = TRY(Web::WebDriver::get_property(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(data, "expiry"sv)); + auto expiry = TRY(Web::WebDriver::get_property(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 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(payload, "args"sv)); + 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() };