From e8cd3749c861bde41a046db4e103fe12f1f8a97e Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 30 Oct 2024 16:57:56 -0400 Subject: [PATCH] LibWeb: Implement WebDriver shadow references according to the spec Similar to commit a9c858fc78df266145bc0dca4450a7e0d98f1113, this patch implements the WebDriver spec concept of shadow references grouped according to their browsing context groups. --- .../LibWeb/WebDriver/ElementReference.cpp | 55 +++++++++++-------- .../LibWeb/WebDriver/ElementReference.h | 7 ++- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/Userland/Libraries/LibWeb/WebDriver/ElementReference.cpp b/Userland/Libraries/LibWeb/WebDriver/ElementReference.cpp index 8c21a4f5b8a..d5c1a56f294 100644 --- a/Userland/Libraries/LibWeb/WebDriver/ElementReference.cpp +++ b/Userland/Libraries/LibWeb/WebDriver/ElementReference.cpp @@ -184,18 +184,18 @@ ErrorOr, Web::WebDriver::Error> get_known_el // 1. If not node reference is known with session, session's current browsing context, and reference return error // with error code no such element. if (!node_reference_is_known(browsing_context, reference)) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "Element ID is not an integer"); + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Element reference '{}' is not known", reference)); // 2. Let node be the result of get a node with session, session's current browsing context, and reference. auto node = get_node(browsing_context, reference); // 3. If node is not null and node does not implement Element return error with error code no such element. if (node && !node->is_element()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find element with node reference: {}", reference)); + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find element with reference '{}'", reference)); // 4. If node is null or node is stale return error with error code stale element reference. if (!node || is_element_stale(*node)) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, ByteString::formatted("Element reference {} is stale", reference)); + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, ByteString::formatted("Element reference '{}' is stale", reference)); // 5. Return success with data node. return static_cast(*node); @@ -376,24 +376,22 @@ JS::MarkedVector> pointer_interactable_tree( } // https://w3c.github.io/webdriver/#dfn-get-or-create-a-shadow-root-reference -ByteString get_or_create_a_shadow_root_reference(Web::DOM::ShadowRoot const& shadow_root) +ByteString get_or_create_a_shadow_root_reference(HTML::BrowsingContext const& browsing_context, Web::DOM::ShadowRoot const& shadow_root) { - // FIXME: 1. For each known shadow root of the current browsing context’s list of known shadow roots: - // FIXME: 1. If known shadow root equals shadow root, return success with known shadow root’s shadow root reference. - // FIXME: 2. Add shadow to the list of known shadow roots of the current browsing context. - // FIXME: 3. Return success with the shadow’s shadow root reference. - - return ByteString::number(shadow_root.unique_id().value()); + // 1. Assert: element implements ShadowRoot. + // 2. Return the result of trying to get or create a node reference with session, session's current browsing context, + // and element. + return get_or_create_a_node_reference(browsing_context, shadow_root); } // https://w3c.github.io/webdriver/#dfn-shadow-root-reference-object -JsonObject shadow_root_reference_object(Web::DOM::ShadowRoot const& shadow_root) +JsonObject shadow_root_reference_object(HTML::BrowsingContext const& browsing_context, Web::DOM::ShadowRoot const& shadow_root) { // 1. Let identifier be the shadow root identifier. auto identifier = shadow_root_identifier; - // 2. Let reference be the result of get or create a shadow root reference given shadow root. - auto reference = get_or_create_a_shadow_root_reference(shadow_root); + // 2. Let reference be the result of get or create a shadow root reference with session and shadow root. + auto reference = get_or_create_a_shadow_root_reference(browsing_context, shadow_root); // 3. Return a JSON Object initialized with a property with name identifier and value reference. JsonObject object; @@ -402,22 +400,35 @@ JsonObject shadow_root_reference_object(Web::DOM::ShadowRoot const& shadow_root) } // https://w3c.github.io/webdriver/#dfn-get-a-known-shadow-root -ErrorOr, Web::WebDriver::Error> get_known_shadow_root(StringView shadow_id) +ErrorOr, Web::WebDriver::Error> get_known_shadow_root(HTML::BrowsingContext const& browsing_context, StringView reference) { - // NOTE: The whole concept of "known shadow roots" is not implemented yet. See get_or_create_a_shadow_root_reference(). - // For now the shadow root is only represented by its ID. - auto shadow_root = shadow_id.to_number(); - if (!shadow_root.has_value()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Shadow ID is not an integer"); + // 1. If not node reference is known with session, session's current browsing context, and reference return error with error code no such shadow root. + if (!node_reference_is_known(browsing_context, reference)) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchShadowRoot, ByteString::formatted("Shadow root reference '{}' is not known", reference)); - auto* node = Web::DOM::Node::from_unique_id(UniqueNodeID(*shadow_root)); + // 2. Let node be the result of get a node with session, session's current browsing context, and reference. + auto node = get_node(browsing_context, reference); - if (!node || !node->is_shadow_root()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find shadow root with ID: {}", shadow_id)); + // 3. If node is not null and node does not implement ShadowRoot return error with error code no such shadow root. + if (node && !node->is_shadow_root()) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchShadowRoot, ByteString::formatted("Could not find shadow root with reference '{}'", reference)); + // 4. If node is null or node is detached return error with error code detached shadow root. + if (!node || is_shadow_root_detached(static_cast(*node))) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::DetachedShadowRoot, ByteString::formatted("Element reference '{}' is stale", reference)); + + // 5. Return success with data node. return static_cast(*node); } +// https://w3c.github.io/webdriver/#dfn-is-detached +bool is_shadow_root_detached(Web::DOM::ShadowRoot const& shadow_root) +{ + // A shadow root is detached if its node document is not the active document or if the element node referred to as + // its host is stale. + return !shadow_root.document().is_active() || !shadow_root.host() || is_element_stale(*shadow_root.host()); +} + // https://w3c.github.io/webdriver/#dfn-center-point CSSPixelPoint in_view_center_point(DOM::Element const& element, CSSPixelRect viewport) { diff --git a/Userland/Libraries/LibWeb/WebDriver/ElementReference.h b/Userland/Libraries/LibWeb/WebDriver/ElementReference.h index 09ba532cb21..301bd975024 100644 --- a/Userland/Libraries/LibWeb/WebDriver/ElementReference.h +++ b/Userland/Libraries/LibWeb/WebDriver/ElementReference.h @@ -42,9 +42,10 @@ bool is_element_in_view(ReadonlySpan> paint_ bool is_element_obscured(ReadonlySpan> paint_tree, Web::DOM::Element&); JS::MarkedVector> pointer_interactable_tree(Web::HTML::BrowsingContext&, Web::DOM::Element&); -ByteString get_or_create_a_shadow_root_reference(Web::DOM::ShadowRoot const& shadow_root); -JsonObject shadow_root_reference_object(Web::DOM::ShadowRoot const& shadow_root); -ErrorOr, Web::WebDriver::Error> get_known_shadow_root(StringView shadow_id); +ByteString get_or_create_a_shadow_root_reference(HTML::BrowsingContext const&, Web::DOM::ShadowRoot const&); +JsonObject shadow_root_reference_object(HTML::BrowsingContext const&, Web::DOM::ShadowRoot const&); +ErrorOr, Web::WebDriver::Error> get_known_shadow_root(HTML::BrowsingContext const&, StringView reference); +bool is_shadow_root_detached(Web::DOM::ShadowRoot const&); CSSPixelPoint in_view_center_point(DOM::Element const& element, CSSPixelRect viewport);