Browse Source

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

This will be needed outside of WebContent.
Timothy Flynn 9 tháng trước cách đây
mục cha
commit
8944c6db3f

+ 54 - 0
Userland/Libraries/LibWeb/WebDriver/Properties.h

@@ -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();
+    }
+}
+
+}

+ 26 - 62
Userland/Services/WebContent/WebDriverConnection.cpp

@@ -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 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<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() };