diff --git a/Userland/Services/WebContent/WebDriverClient.ipc b/Userland/Services/WebContent/WebDriverClient.ipc index 8838beed36..ecd0591b6d 100644 --- a/Userland/Services/WebContent/WebDriverClient.ipc +++ b/Userland/Services/WebContent/WebDriverClient.ipc @@ -11,4 +11,5 @@ endpoint WebDriverClient { minimize_window() => (Web::WebDriver::Response response) find_element(JsonValue payload) => (Web::WebDriver::Response response) find_elements(JsonValue payload) => (Web::WebDriver::Response response) + find_element_from_element(JsonValue payload, String element_id) => (Web::WebDriver::Response response) } diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index ffe3e5266a..70148d1f31 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -80,6 +80,25 @@ static JsonObject web_element_reference_object(Web::DOM::Node const& element) return object; } +// https://w3c.github.io/webdriver/#dfn-get-a-known-connected-element +static ErrorOr get_known_connected_element(StringView element_id) +{ + // NOTE: The whole concept of "connected elements" is not implemented yet. See get_or_create_a_web_element_reference(). + // For now the element is only represented by its ID. + auto element = element_id.to_int(); + if (!element.has_value()) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Element ID is not an integer"); + + auto* node = Web::DOM::Node::from_id(*element); + + if (!node) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, String::formatted("Could not find element with ID: {}", element_id)); + if (!is(node)) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, String::formatted("Element with ID is not a parent node: {}", element_id)); + + return static_cast(node); +} + static ErrorOr get_property(JsonValue const& payload, StringView key) { if (!payload.is_object()) @@ -375,6 +394,39 @@ Messages::WebDriverClient::FindElementsResponse WebDriverConnection::find_elemen return make_success_response(move(result)); } +// 12.3.4 Find Element From Element, https://w3c.github.io/webdriver/#dfn-find-element-from-element +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 = 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. + if (!location_strategy.has_value()) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, String::formatted("Location strategy '{}' is invalid", location_strategy_string)); + + // 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)); + + // 5. If the current browsing context is no longer open, return error with error code no such window. + TRY(ensure_open_top_level_browsing_context()); + + // FIXME: 6. Handle any user prompts and return its value if it is an error. + + // 7. Let start node be the result of trying to get a known connected element with url variable element id. + auto* start_node = TRY(get_known_connected_element(element_id)); + + // 8. Let result be the value of trying to Find with start node, location strategy, and selector. + auto result = TRY(find(*start_node, *location_strategy, selector)); + + // 9. If result is empty, return error with error code no such element. Otherwise, return the first element of result. + if (result.is_empty()) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The requested element does not exist"sv); + + return make_success_response(result.at(0)); +} + // https://w3c.github.io/webdriver/#dfn-no-longer-open ErrorOr WebDriverConnection::ensure_open_top_level_browsing_context() { diff --git a/Userland/Services/WebContent/WebDriverConnection.h b/Userland/Services/WebContent/WebDriverConnection.h index 889b0beacd..dd76deb68d 100644 --- a/Userland/Services/WebContent/WebDriverConnection.h +++ b/Userland/Services/WebContent/WebDriverConnection.h @@ -41,6 +41,7 @@ private: virtual Messages::WebDriverClient::MinimizeWindowResponse minimize_window() override; virtual Messages::WebDriverClient::FindElementResponse find_element(JsonValue const& payload) override; virtual Messages::WebDriverClient::FindElementsResponse find_elements(JsonValue const& payload) override; + virtual Messages::WebDriverClient::FindElementFromElementResponse find_element_from_element(JsonValue const& payload, String const& element_id) override; ErrorOr ensure_open_top_level_browsing_context(); void restore_the_window(); diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp index ede08154c6..110157b00c 100644 --- a/Userland/Services/WebDriver/Client.cpp +++ b/Userland/Services/WebDriver/Client.cpp @@ -636,8 +636,7 @@ Web::WebDriver::Response Client::handle_find_element_from_element(Vector/element//element"); auto* session = TRY(find_session_with_id(parameters[0])); - auto result = TRY(session->find_element_from_element(payload, parameters[1])); - return make_json_value(result); + return session->web_content_connection().find_element_from_element(payload, parameters[1]); } // 12.3.5 Find Elements From Element, https://w3c.github.io/webdriver/#dfn-find-elements-from-element diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index aa79b3edd5..3a5b89465f 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -447,55 +447,6 @@ ErrorOr, Web::WebDriver::Error> Session::locator_s return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnsupportedOperation, "Not implemented: locator strategy XPath"); } -// 12.3.4 Find Element From Element, https://w3c.github.io/webdriver/#dfn-find-element-from-element -Web::WebDriver::Response Session::find_element_from_element(JsonValue const& payload, StringView parameter_element_id) -{ - if (!payload.is_object()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object"); - - auto const& properties = payload.as_object(); - // 1. Let location strategy be the result of getting a property called "using". - if (!properties.has("using"sv)) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "No property called 'using' present"); - auto const& maybe_location_strategy = properties.get("using"sv); - if (!maybe_location_strategy.is_string()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Property 'using' is not a String"); - - auto location_strategy = maybe_location_strategy.to_string(); - - // 2. If location strategy is not present as a keyword in the table of location strategies, return error with error code invalid argument. - if (!s_locator_strategies.first_matching([&](LocatorStrategy const& match) { return match.name == location_strategy; }).has_value()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "No valid location strategy"); - - // 3. Let selector be the result of getting a property called "value". - // 4. If selector is undefined, return error with error code invalid argument. - if (!properties.has("value"sv)) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "No property called 'value' present"); - auto const& maybe_selector = properties.get("value"sv); - if (!maybe_selector.is_string()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Property 'value' is not a String"); - - auto selector = maybe_selector.to_string(); - - // 5. If the current browsing context is no longer open, return error with error code no such window. - TRY(check_for_open_top_level_browsing_context_or_return_error()); - - // FIXME: 6. Handle any user prompts and return its value if it is an error. - - // 7. Let start node be the result of trying to get a known connected element with url variable element id. - auto element_id = TRY(get_known_connected_element(parameter_element_id)); - LocalElement start_node = { element_id }; - - // 8. Let result be the value of trying to Find with start node, location strategy, and selector. - auto result = TRY(find(start_node, location_strategy, selector)); - - // 9. If result is empty, return error with error code no such element. Otherwise, return the first element of result. - if (result.is_empty()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The requested element does not exist"); - - return JsonValue(result.at(0)); -} - // 12.3.5 Find Elements From Element, https://w3c.github.io/webdriver/#dfn-find-elements-from-element Web::WebDriver::Response Session::find_elements_from_element(JsonValue const& payload, StringView parameter_element_id) { diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index 735404b0cf..263bdc567f 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -58,7 +58,6 @@ public: Web::WebDriver::Response get_window_handle(); ErrorOr> close_window(); Web::WebDriver::Response get_window_handles() const; - Web::WebDriver::Response find_element_from_element(JsonValue const& payload, StringView parameter_element_id); Web::WebDriver::Response find_elements_from_element(JsonValue const& payload, StringView parameter_element_id); Web::WebDriver::Response is_element_selected(StringView element_id); Web::WebDriver::Response get_element_attribute(JsonValue const& payload, StringView element_id, StringView name);