LibWeb: Implement WebDriver shadow references according to the spec

Similar to commit a9c858fc78, this patch
implements the WebDriver spec concept of shadow references grouped
according to their browsing context groups.
This commit is contained in:
Timothy Flynn 2024-10-30 16:57:56 -04:00 committed by Tim Ledbetter
parent ad9d623664
commit e8cd3749c8
Notes: github-actions[bot] 2024-10-31 00:43:26 +00:00
2 changed files with 37 additions and 25 deletions

View file

@ -184,18 +184,18 @@ ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_known_el
// 1. If not node reference is known with session, session's current browsing context, and reference return error // 1. If not node reference is known with session, session's current browsing context, and reference return error
// with error code no such element. // with error code no such element.
if (!node_reference_is_known(browsing_context, reference)) 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. // 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); 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. // 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()) 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. // 4. If node is null or node is stale return error with error code stale element reference.
if (!node || is_element_stale(*node)) 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. // 5. Return success with data node.
return static_cast<Web::DOM::Element&>(*node); return static_cast<Web::DOM::Element&>(*node);
@ -376,24 +376,22 @@ JS::MarkedVector<JS::NonnullGCPtr<Web::DOM::Element>> pointer_interactable_tree(
} }
// https://w3c.github.io/webdriver/#dfn-get-or-create-a-shadow-root-reference // 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 contexts list of known shadow roots: // 1. Assert: element implements ShadowRoot.
// FIXME: 1. If known shadow root equals shadow root, return success with known shadow roots shadow root reference. // 2. Return the result of trying to get or create a node reference with session, session's current browsing context,
// FIXME: 2. Add shadow to the list of known shadow roots of the current browsing context. // and element.
// FIXME: 3. Return success with the shadows shadow root reference. return get_or_create_a_node_reference(browsing_context, shadow_root);
return ByteString::number(shadow_root.unique_id().value());
} }
// https://w3c.github.io/webdriver/#dfn-shadow-root-reference-object // 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. // 1. Let identifier be the shadow root identifier.
auto identifier = 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. // 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(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. // 3. Return a JSON Object initialized with a property with name identifier and value reference.
JsonObject object; 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 // https://w3c.github.io/webdriver/#dfn-get-a-known-shadow-root
ErrorOr<JS::NonnullGCPtr<Web::DOM::ShadowRoot>, Web::WebDriver::Error> get_known_shadow_root(StringView shadow_id) ErrorOr<JS::NonnullGCPtr<Web::DOM::ShadowRoot>, 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(). // 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.
// For now the shadow root is only represented by its ID. if (!node_reference_is_known(browsing_context, reference))
auto shadow_root = shadow_id.to_number<i64>(); return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchShadowRoot, ByteString::formatted("Shadow root reference '{}' is not known", reference));
if (!shadow_root.has_value())
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Shadow ID is not an integer");
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()) // 3. If node is not null and node does not implement ShadowRoot return error with error code no such shadow root.
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find shadow root with ID: {}", shadow_id)); 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<Web::DOM::ShadowRoot&>(*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<Web::DOM::ShadowRoot&>(*node); return static_cast<Web::DOM::ShadowRoot&>(*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 // https://w3c.github.io/webdriver/#dfn-center-point
CSSPixelPoint in_view_center_point(DOM::Element const& element, CSSPixelRect viewport) CSSPixelPoint in_view_center_point(DOM::Element const& element, CSSPixelRect viewport)
{ {

View file

@ -42,9 +42,10 @@ bool is_element_in_view(ReadonlySpan<JS::NonnullGCPtr<Web::DOM::Element>> paint_
bool is_element_obscured(ReadonlySpan<JS::NonnullGCPtr<Web::DOM::Element>> paint_tree, Web::DOM::Element&); bool is_element_obscured(ReadonlySpan<JS::NonnullGCPtr<Web::DOM::Element>> paint_tree, Web::DOM::Element&);
JS::MarkedVector<JS::NonnullGCPtr<Web::DOM::Element>> pointer_interactable_tree(Web::HTML::BrowsingContext&, Web::DOM::Element&); JS::MarkedVector<JS::NonnullGCPtr<Web::DOM::Element>> pointer_interactable_tree(Web::HTML::BrowsingContext&, Web::DOM::Element&);
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&, Web::DOM::ShadowRoot const&);
JsonObject shadow_root_reference_object(Web::DOM::ShadowRoot const& shadow_root); JsonObject shadow_root_reference_object(HTML::BrowsingContext const&, Web::DOM::ShadowRoot const&);
ErrorOr<JS::NonnullGCPtr<Web::DOM::ShadowRoot>, Web::WebDriver::Error> get_known_shadow_root(StringView shadow_id); ErrorOr<JS::NonnullGCPtr<Web::DOM::ShadowRoot>, 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); CSSPixelPoint in_view_center_point(DOM::Element const& element, CSSPixelRect viewport);