LibWeb: Implement WebDriver element references according to the spec

Our currently ad-hoc method of tracking element references is basically
a process-wide map, rather than grouping elements according to their
browsing context groups. This prevents us from recognizing when an
element reference is invalid due to its browsing context having been
closed.

This implements the WebDriver spec concept of element references grouped
according to their browsing context groups.

This patch is a bit noisy because we now need to plumb the current BC
through to the element reference AOs.
This commit is contained in:
Timothy Flynn 2024-10-24 13:23:38 -04:00 committed by Tim Flynn
commit a9c858fc78
Notes: github-actions[bot] 2024-10-24 23:01:12 +00:00
8 changed files with 155 additions and 67 deletions

View file

@ -397,6 +397,11 @@ BrowsingContextGroup* BrowsingContext::group()
return m_group; return m_group;
} }
BrowsingContextGroup const* BrowsingContext::group() const
{
return m_group;
}
void BrowsingContext::set_group(BrowsingContextGroup* group) void BrowsingContext::set_group(BrowsingContextGroup* group)
{ {
m_group = group; m_group = group;

View file

@ -117,6 +117,7 @@ public:
JS::GCPtr<BrowsingContext> top_level_browsing_context() const; JS::GCPtr<BrowsingContext> top_level_browsing_context() const;
BrowsingContextGroup* group(); BrowsingContextGroup* group();
BrowsingContextGroup const* group() const;
void set_group(BrowsingContextGroup*); void set_group(BrowsingContextGroup*);
// https://html.spec.whatwg.org/multipage/browsers.html#bcg-remove // https://html.spec.whatwg.org/multipage/browsers.html#bcg-remove

View file

@ -200,7 +200,7 @@ static ErrorOr<CSSPixelPoint, WebDriver::Error> get_coordinates_relative_to_orig
// 1. Let element be the result of trying to run actions options' get element origin steps with origin and // 1. Let element be the result of trying to run actions options' get element origin steps with origin and
// browsing context. // browsing context.
// 2. If element is null, return error with error code no such element. // 2. If element is null, return error with error code no such element.
auto element = TRY(actions_options.get_element_origin(origin)); auto element = TRY(actions_options.get_element_origin(browsing_context, origin));
// 3. Let x element and y element be the result of calculating the in-view center point of element. // 3. Let x element and y element be the result of calculating the in-view center point of element.
auto position = in_view_center_point(element, viewport); auto position = in_view_center_point(element, viewport);

View file

@ -119,7 +119,7 @@ struct ActionObject {
// https://w3c.github.io/webdriver/#dfn-actions-options // https://w3c.github.io/webdriver/#dfn-actions-options
struct ActionsOptions { struct ActionsOptions {
Function<bool(JsonObject const&)> is_element_origin; Function<bool(JsonObject const&)> is_element_origin;
Function<ErrorOr<JS::NonnullGCPtr<DOM::Element>, WebDriver::Error>(StringView)> get_element_origin; Function<ErrorOr<JS::NonnullGCPtr<DOM::Element>, WebDriver::Error>(HTML::BrowsingContext const&, StringView)> get_element_origin;
}; };
using OnActionsComplete = JS::NonnullGCPtr<JS::HeapFunction<void(Web::WebDriver::Response)>>; using OnActionsComplete = JS::NonnullGCPtr<JS::HeapFunction<void(Web::WebDriver::Response)>>;

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/HashMap.h>
#include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h> #include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/Node.h> #include <LibWeb/DOM/Node.h>
@ -11,6 +12,7 @@
#include <LibWeb/Geometry/DOMRect.h> #include <LibWeb/Geometry/DOMRect.h>
#include <LibWeb/Geometry/DOMRectList.h> #include <LibWeb/Geometry/DOMRectList.h>
#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/BrowsingContextGroup.h>
#include <LibWeb/HTML/HTMLBodyElement.h> #include <LibWeb/HTML/HTMLBodyElement.h>
#include <LibWeb/HTML/HTMLInputElement.h> #include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/HTML/HTMLTextAreaElement.h> #include <LibWeb/HTML/HTMLTextAreaElement.h>
@ -27,25 +29,105 @@ static ByteString const web_element_identifier = "element-6066-11e4-a52e-4f73546
// https://w3c.github.io/webdriver/#dfn-shadow-root-identifier // https://w3c.github.io/webdriver/#dfn-shadow-root-identifier
static ByteString const shadow_root_identifier = "shadow-6066-11e4-a52e-4f735466cecf"sv; static ByteString const shadow_root_identifier = "shadow-6066-11e4-a52e-4f735466cecf"sv;
// https://w3c.github.io/webdriver/#dfn-get-or-create-a-web-element-reference // https://w3c.github.io/webdriver/#dfn-browsing-context-group-node-map
ByteString get_or_create_a_web_element_reference(Web::DOM::Node const& element) static HashMap<JS::RawGCPtr<HTML::BrowsingContextGroup const>, HashTable<ByteString>> browsing_context_group_node_map;
{
// FIXME: 1. For each known element of the current browsing contexts list of known elements:
// FIXME: 1. If known element equals element, return success with known elements web element reference.
// FIXME: 2. Add element to the list of known elements of the current browsing context.
// FIXME: 3. Return success with the elements web element reference.
return ByteString::number(element.unique_id().value()); // https://w3c.github.io/webdriver/#dfn-navigable-seen-nodes-map
static HashMap<JS::RawGCPtr<HTML::Navigable>, HashTable<ByteString>> navigable_seen_nodes_map;
// https://w3c.github.io/webdriver/#dfn-get-a-node
JS::GCPtr<Web::DOM::Node> get_node(HTML::BrowsingContext const& browsing_context, StringView reference)
{
// 1. Let browsing context group node map be session's browsing context group node map.
// 2. Let browsing context group be browsing context's browsing context group.
auto const* browsing_context_group = browsing_context.group();
// 3. If browsing context group node map does not contain browsing context group, return null.
// 4. Let node id map be browsing context group node map[browsing context group].
auto node_id_map = browsing_context_group_node_map.get(browsing_context_group);
if (!node_id_map.has_value())
return nullptr;
// 5. Let node be the entry in node id map whose value is reference, if such an entry exists, or null otherwise.
JS::GCPtr<Web::DOM::Node> node;
if (node_id_map->contains(reference)) {
auto node_id = reference.to_number<i64>().value();
node = Web::DOM::Node::from_unique_id(UniqueNodeID(node_id));
}
// 6. Return node.
return node;
}
// https://w3c.github.io/webdriver/#dfn-get-or-create-a-node-reference
ByteString get_or_create_a_node_reference(HTML::BrowsingContext const& browsing_context, Web::DOM::Node const& node)
{
// 1. Let browsing context group node map be session's browsing context group node map.
// 2. Let browsing context group be browsing context's browsing context group.
auto const* browsing_context_group = browsing_context.group();
// 3. If browsing context group node map does not contain browsing context group, set browsing context group node
// map[browsing context group] to a new weak map.
// 4. Let node id map be browsing context group node map[browsing context group].
auto& node_id_map = browsing_context_group_node_map.ensure(browsing_context_group);
auto node_id = ByteString::number(node.unique_id().value());
// 5. If node id map does not contain node:
if (!node_id_map.contains(node_id)) {
// 1. Let node id be a new globally unique string.
// 2. Set node id map[node] to node id.
node_id_map.set(node_id);
// 3. Let navigable be browsing context's active document's node navigable.
auto navigable = browsing_context.active_document()->navigable();
// 4. Let navigable seen nodes map be session's navigable seen nodes map.
// 5. If navigable seen nodes map does not contain navigable, set navigable seen nodes map[navigable] to an empty set.
// 6. Append node id to navigable seen nodes map[navigable].
navigable_seen_nodes_map.ensure(navigable).set(node_id);
}
// 6. Return node id map[node].
return node_id;
}
// https://w3c.github.io/webdriver/#dfn-node-reference-is-known
bool node_reference_is_known(HTML::BrowsingContext const& browsing_context, StringView reference)
{
// 1. Let navigable be browsing context's active document's node navigable.
auto navigable = browsing_context.active_document()->navigable();
if (!navigable)
return false;
// 2. Let navigable seen nodes map be session's navigable seen nodes map.
// 3. If navigable seen nodes map contains navigable and navigable seen nodes map[navigable] contains reference,
// return true, otherwise return false.
if (auto map = navigable_seen_nodes_map.get(navigable); map.has_value())
return map->contains(reference);
return false;
}
// https://w3c.github.io/webdriver/#dfn-get-or-create-a-web-element-reference
ByteString get_or_create_a_web_element_reference(HTML::BrowsingContext const& browsing_context, Web::DOM::Node const& element)
{
// 1. Assert: element implements Element.
VERIFY(element.is_element());
// 2. Return the result of trying to get or create a node reference given session, session's current browsing
// context, and element.
return get_or_create_a_node_reference(browsing_context, element);
} }
// https://w3c.github.io/webdriver/#dfn-web-element-reference-object // https://w3c.github.io/webdriver/#dfn-web-element-reference-object
JsonObject web_element_reference_object(Web::DOM::Node const& element) JsonObject web_element_reference_object(HTML::BrowsingContext const& browsing_context, Web::DOM::Node const& element)
{ {
// 1. Let identifier be the web element identifier. // 1. Let identifier be the web element identifier.
auto identifier = web_element_identifier; auto identifier = web_element_identifier;
// 2. Let reference be the result of get or create a web element reference given element. // 2. Let reference be the result of get or create a web element reference given element.
auto reference = get_or_create_a_web_element_reference(element); auto reference = get_or_create_a_web_element_reference(browsing_context, element);
// 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;
@ -54,7 +136,7 @@ JsonObject web_element_reference_object(Web::DOM::Node const& element)
} }
// https://w3c.github.io/webdriver/#dfn-deserialize-a-web-element // https://w3c.github.io/webdriver/#dfn-deserialize-a-web-element
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, WebDriver::Error> deserialize_web_element(JsonObject const& object) ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, WebDriver::Error> deserialize_web_element(Web::HTML::BrowsingContext const& browsing_context, JsonObject const& object)
{ {
// 1. If object has no own property web element identifier, return error with error code invalid argument. // 1. If object has no own property web element identifier, return error with error code invalid argument.
if (!object.has_string(web_element_identifier)) if (!object.has_string(web_element_identifier))
@ -64,10 +146,10 @@ ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, WebDriver::Error> deserialize_web_e
auto reference = extract_web_element_reference(object); auto reference = extract_web_element_reference(object);
// 3. Let element be the result of trying to get a known element with session and reference. // 3. Let element be the result of trying to get a known element with session and reference.
auto element = TRY(get_known_element(reference)); auto element = TRY(get_known_element(browsing_context, reference));
// 4. Return success with data element. // 4. Return success with data element.
return *element; return element;
} }
ByteString extract_web_element_reference(JsonObject const& object) ByteString extract_web_element_reference(JsonObject const& object)
@ -85,39 +167,35 @@ bool represents_a_web_element(JsonValue const& value)
} }
// https://w3c.github.io/webdriver/#dfn-get-a-webelement-origin // https://w3c.github.io/webdriver/#dfn-get-a-webelement-origin
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_element_origin(StringView origin) ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_element_origin(Web::HTML::BrowsingContext const& browsing_context, StringView origin)
{ {
// 1. Assert: browsing context is the current browsing context. // 1. Assert: browsing context is the current browsing context.
// 2. Let element be equal to the result of trying to get a known element with session and origin. // 2. Let element be equal to the result of trying to get a known element with session and origin.
auto element = TRY(get_known_element(origin)); auto element = TRY(get_known_element(browsing_context, origin));
// 3. Return success with data element. // 3. Return success with data element.
return element; return element;
} }
// https://w3c.github.io/webdriver/#dfn-get-a-known-element // https://w3c.github.io/webdriver/#dfn-get-a-known-element
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_known_element(StringView element_id) ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_known_element(Web::HTML::BrowsingContext const& browsing_context, StringView reference)
{ {
// 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.
// 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.
auto element = element_id.to_number<i64>(); if (!node_reference_is_known(browsing_context, reference))
if (!element.has_value())
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, "Element ID is not an integer");
// 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 = Web::DOM::Node::from_unique_id(UniqueNodeID(*element)); 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 ID: {}", element_id)); return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find element with node 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 with ID: {} is stale", element_id)); 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);

View file

@ -16,13 +16,17 @@
namespace Web::WebDriver { namespace Web::WebDriver {
ByteString get_or_create_a_web_element_reference(Web::DOM::Node const& element); JS::GCPtr<Web::DOM::Node> get_node(HTML::BrowsingContext const&, StringView reference);
JsonObject web_element_reference_object(Web::DOM::Node const& element); ByteString get_or_create_a_node_reference(HTML::BrowsingContext const&, Web::DOM::Node const&);
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, WebDriver::Error> deserialize_web_element(JsonObject const&); bool node_reference_is_known(HTML::BrowsingContext const&, StringView reference);
ByteString get_or_create_a_web_element_reference(HTML::BrowsingContext const&, Web::DOM::Node const& element);
JsonObject web_element_reference_object(HTML::BrowsingContext const&, Web::DOM::Node const& element);
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, WebDriver::Error> deserialize_web_element(Web::HTML::BrowsingContext const&, JsonObject const&);
ByteString extract_web_element_reference(JsonObject const&); ByteString extract_web_element_reference(JsonObject const&);
bool represents_a_web_element(JsonValue const& value); bool represents_a_web_element(JsonValue const& value);
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_element_origin(StringView origin); ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_element_origin(Web::HTML::BrowsingContext const&, StringView origin);
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_known_element(StringView element_id); ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_known_element(Web::HTML::BrowsingContext const&, StringView reference);
bool is_element_stale(Web::DOM::Node const& element); bool is_element_stale(Web::DOM::Node const& element);
bool is_element_interactable(Web::HTML::BrowsingContext const&, Web::DOM::Element const&); bool is_element_interactable(Web::HTML::BrowsingContext const&, Web::DOM::Element const&);

View file

@ -46,8 +46,8 @@ namespace Web::WebDriver {
_temporary_result.release_value(); \ _temporary_result.release_value(); \
}) })
static ErrorOr<JsonValue, ExecuteScriptResultType> internal_json_clone_algorithm(JS::Realm&, JS::Value, HashTable<JS::Object*>& seen); static ErrorOr<JsonValue, ExecuteScriptResultType> internal_json_clone_algorithm(JS::Realm&, HTML::BrowsingContext const&, JS::Value, HashTable<JS::Object*>& seen);
static ErrorOr<JsonValue, ExecuteScriptResultType> clone_an_object(JS::Realm&, JS::Object&, HashTable<JS::Object*>& seen, auto const& clone_algorithm); static ErrorOr<JsonValue, ExecuteScriptResultType> clone_an_object(JS::Realm&, HTML::BrowsingContext const&, JS::Object&, HashTable<JS::Object*>& seen, auto const& clone_algorithm);
// https://w3c.github.io/webdriver/#dfn-collection // https://w3c.github.io/webdriver/#dfn-collection
static bool is_collection(JS::Object const& value) static bool is_collection(JS::Object const& value)
@ -73,15 +73,15 @@ static bool is_collection(JS::Object const& value)
} }
// https://w3c.github.io/webdriver/#dfn-json-clone // https://w3c.github.io/webdriver/#dfn-json-clone
static ErrorOr<JsonValue, ExecuteScriptResultType> json_clone(JS::Realm& realm, JS::Value value) static ErrorOr<JsonValue, ExecuteScriptResultType> json_clone(JS::Realm& realm, HTML::BrowsingContext const& browsing_context, JS::Value value)
{ {
// To perform a JSON clone return the result of calling the internal JSON clone algorithm with arguments value and an empty List. // To perform a JSON clone return the result of calling the internal JSON clone algorithm with arguments value and an empty List.
auto seen = HashTable<JS::Object*> {}; auto seen = HashTable<JS::Object*> {};
return internal_json_clone_algorithm(realm, value, seen); return internal_json_clone_algorithm(realm, browsing_context, value, seen);
} }
// https://w3c.github.io/webdriver/#dfn-internal-json-clone-algorithm // https://w3c.github.io/webdriver/#dfn-internal-json-clone-algorithm
static ErrorOr<JsonValue, ExecuteScriptResultType> internal_json_clone_algorithm(JS::Realm& realm, JS::Value value, HashTable<JS::Object*>& seen) static ErrorOr<JsonValue, ExecuteScriptResultType> internal_json_clone_algorithm(JS::Realm& realm, HTML::BrowsingContext const& browsing_context, JS::Value value, HashTable<JS::Object*>& seen)
{ {
auto& vm = realm.vm(); auto& vm = realm.vm();
@ -122,7 +122,7 @@ static ErrorOr<JsonValue, ExecuteScriptResultType> internal_json_clone_algorithm
// Otherwise: // Otherwise:
else { else {
// 1. Let reference be the web element reference object for session and value. // 1. Let reference be the web element reference object for session and value.
auto reference = web_element_reference_object(element); auto reference = web_element_reference_object(browsing_context, element);
// 2. Return success with data reference. // 2. Return success with data reference.
return reference; return reference;
@ -168,14 +168,14 @@ static ErrorOr<JsonValue, ExecuteScriptResultType> internal_json_clone_algorithm
} }; } };
// 3. Let result be the value of running the clone an object algorithm with arguments value and seen, and the internal JSON clone algorithm as the clone algorithm. // 3. Let result be the value of running the clone an object algorithm with arguments value and seen, and the internal JSON clone algorithm as the clone algorithm.
auto result = TRY(clone_an_object(realm, value.as_object(), seen, internal_json_clone_algorithm)); auto result = TRY(clone_an_object(realm, browsing_context, value.as_object(), seen, internal_json_clone_algorithm));
// 5. Return result. // 5. Return result.
return result; return result;
} }
// https://w3c.github.io/webdriver/#dfn-clone-an-object // https://w3c.github.io/webdriver/#dfn-clone-an-object
static ErrorOr<JsonValue, ExecuteScriptResultType> clone_an_object(JS::Realm& realm, JS::Object& value, HashTable<JS::Object*>& seen, auto const& clone_algorithm) static ErrorOr<JsonValue, ExecuteScriptResultType> clone_an_object(JS::Realm& realm, HTML::BrowsingContext const& browsing_context, JS::Object& value, HashTable<JS::Object*>& seen, auto const& clone_algorithm)
{ {
auto& vm = realm.vm(); auto& vm = realm.vm();
@ -217,7 +217,7 @@ static ErrorOr<JsonValue, ExecuteScriptResultType> clone_an_object(JS::Realm& re
continue; continue;
// 3. Let cloned property result be the result of calling the clone algorithm with arguments source property value and seen. // 3. Let cloned property result be the result of calling the clone algorithm with arguments source property value and seen.
auto cloned_property_result = clone_algorithm(realm, *source_property_value->value, seen); auto cloned_property_result = clone_algorithm(realm, browsing_context, *source_property_value->value, seen);
// 4. If cloned property result is a success, set a property of result with name name and value equal to cloned property results data. // 4. If cloned property result is a success, set a property of result with name name and value equal to cloned property results data.
if (!cloned_property_result.is_error()) { if (!cloned_property_result.is_error()) {
@ -398,12 +398,12 @@ void execute_script(HTML::BrowsingContext const& browsing_context, ByteString bo
}); });
// 9. Wait until promise is resolved, or timer's timeout fired flag is set, whichever occurs first. // 9. Wait until promise is resolved, or timer's timeout fired flag is set, whichever occurs first.
auto reaction_steps = JS::create_heap_function(vm.heap(), [&realm, promise, timer, on_complete](JS::Value) -> WebIDL::ExceptionOr<JS::Value> { auto reaction_steps = JS::create_heap_function(vm.heap(), [&realm, &browsing_context, promise, timer, on_complete](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
if (timer->is_timed_out()) if (timer->is_timed_out())
return JS::js_undefined(); return JS::js_undefined();
timer->stop(); timer->stop();
auto json_value_or_error = json_clone(realm, promise->result()); auto json_value_or_error = json_clone(realm, browsing_context, promise->result());
if (json_value_or_error.is_error()) { if (json_value_or_error.is_error()) {
auto error_object = JsonObject {}; auto error_object = JsonObject {};
error_object.set("name", "Error"); error_object.set("name", "Error");
@ -513,12 +513,12 @@ void execute_async_script(HTML::BrowsingContext const& browsing_context, ByteStr
}); });
// 9. Wait until promise is resolved, or timer's timeout fired flag is set, whichever occurs first. // 9. Wait until promise is resolved, or timer's timeout fired flag is set, whichever occurs first.
auto reaction_steps = JS::create_heap_function(vm.heap(), [&realm, promise, timer, on_complete](JS::Value) -> WebIDL::ExceptionOr<JS::Value> { auto reaction_steps = JS::create_heap_function(vm.heap(), [&realm, &browsing_context, promise, timer, on_complete](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
if (timer->is_timed_out()) if (timer->is_timed_out())
return JS::js_undefined(); return JS::js_undefined();
timer->stop(); timer->stop();
auto json_value_or_error = json_clone(realm, promise->result()); auto json_value_or_error = json_clone(realm, browsing_context, promise->result());
if (json_value_or_error.is_error()) { if (json_value_or_error.is_error()) {
auto error_object = JsonObject {}; auto error_object = JsonObject {};
error_object.set("name", "Error"); error_object.set("name", "Error");

View file

@ -565,7 +565,7 @@ Messages::WebDriverClient::SwitchToFrameResponse WebDriverConnection::switch_to_
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known element with session and id. // 3. Let element be the result of trying to get a known element with session and id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. If element is not a frame or iframe element, return error with error code no such frame. // 4. If element is not a frame or iframe element, return error with error code no such frame.
bool is_frame = is<Web::HTML::HTMLFrameElement>(*element); bool is_frame = is<Web::HTML::HTMLFrameElement>(*element);
@ -902,7 +902,7 @@ Messages::WebDriverClient::FindElementFromElementResponse WebDriverConnection::f
auto start_node_getter = [&]() -> StartNodeGetter::ReturnType { auto start_node_getter = [&]() -> StartNodeGetter::ReturnType {
// 7. Let start node be the result of trying to get a known connected element with url variable element id. // 7. Let start node be the result of trying to get a known connected element with url variable element id.
return TRY(Web::WebDriver::get_known_element(element_id)); return TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
}; };
// 8. Let result be the value of trying to Find with start node, location strategy, and selector. // 8. Let result be the value of trying to Find with start node, location strategy, and selector.
@ -938,7 +938,7 @@ Messages::WebDriverClient::FindElementsFromElementResponse WebDriverConnection::
auto start_node_getter = [&]() -> StartNodeGetter::ReturnType { auto start_node_getter = [&]() -> StartNodeGetter::ReturnType {
// 7. Let start node be the result of trying to get a known connected element with url variable element id. // 7. Let start node be the result of trying to get a known connected element with url variable element id.
return TRY(Web::WebDriver::get_known_element(element_id)); return TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
}; };
// 8. Return the result of trying to Find with start node, location strategy, and selector. // 8. Return the result of trying to Find with start node, location strategy, and selector.
@ -1041,7 +1041,7 @@ Messages::WebDriverClient::GetElementShadowRootResponse WebDriverConnection::get
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let shadow root be element's shadow root. // 4. Let shadow root be element's shadow root.
auto shadow_root = element->shadow_root(); auto shadow_root = element->shadow_root();
@ -1067,7 +1067,7 @@ Messages::WebDriverClient::IsElementSelectedResponse WebDriverConnection::is_ele
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let selected be the value corresponding to the first matching statement: // 4. Let selected be the value corresponding to the first matching statement:
bool selected = false; bool selected = false;
@ -1103,7 +1103,7 @@ Messages::WebDriverClient::GetElementAttributeResponse WebDriverConnection::get_
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let result be the result of the first matching condition: // 4. Let result be the result of the first matching condition:
Optional<ByteString> result; Optional<ByteString> result;
@ -1137,7 +1137,7 @@ Messages::WebDriverClient::GetElementPropertyResponse WebDriverConnection::get_e
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
Optional<ByteString> result; Optional<ByteString> result;
@ -1170,7 +1170,7 @@ Messages::WebDriverClient::GetElementCssValueResponse WebDriverConnection::get_e
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let computed value be the result of the first matching condition: // 4. Let computed value be the result of the first matching condition:
ByteString computed_value; ByteString computed_value;
@ -1203,7 +1203,7 @@ Messages::WebDriverClient::GetElementTextResponse WebDriverConnection::get_eleme
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let rendered text be the result of performing implementation-specific steps whose result is exactly the same as the result of a Function.[[Call]](null, element) with bot.dom.getVisibleText as the this value. // 4. Let rendered text be the result of performing implementation-specific steps whose result is exactly the same as the result of a Function.[[Call]](null, element) with bot.dom.getVisibleText as the this value.
auto rendered_text = element->text_content(); auto rendered_text = element->text_content();
@ -1222,7 +1222,7 @@ Messages::WebDriverClient::GetElementTagNameResponse WebDriverConnection::get_el
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let qualified name be the result of getting elements tagName IDL attribute. // 4. Let qualified name be the result of getting elements tagName IDL attribute.
auto qualified_name = element->tag_name(); auto qualified_name = element->tag_name();
@ -1241,7 +1241,7 @@ Messages::WebDriverClient::GetElementRectResponse WebDriverConnection::get_eleme
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Calculate the absolute position of element and let it be coordinates. // 4. Calculate the absolute position of element and let it be coordinates.
// 5. Let rect be elements bounding rectangle. // 5. Let rect be elements bounding rectangle.
@ -1272,7 +1272,7 @@ Messages::WebDriverClient::IsElementEnabledResponse WebDriverConnection::is_elem
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let enabled be a boolean initially set to true if the current browsing contexts active documents type is not "xml". // 4. Let enabled be a boolean initially set to true if the current browsing contexts active documents type is not "xml".
// 5. Otherwise, let enabled to false and jump to the last step of this algorithm. // 5. Otherwise, let enabled to false and jump to the last step of this algorithm.
@ -1298,7 +1298,7 @@ Messages::WebDriverClient::GetComputedRoleResponse WebDriverConnection::get_comp
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let role be the result of computing the WAI-ARIA role of element. // 4. Let role be the result of computing the WAI-ARIA role of element.
auto role = element->role_or_default(); auto role = element->role_or_default();
@ -1319,7 +1319,7 @@ Messages::WebDriverClient::GetComputedLabelResponse WebDriverConnection::get_com
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known element with url variable element id. // 3. Let element be the result of trying to get a known element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Let label be the result of a Accessible Name and Description Computation for the Accessible Name of the element. // 4. Let label be the result of a Accessible Name and Description Computation for the Accessible Name of the element.
auto label = element->accessible_name(element->document()).release_value_but_fixme_should_propagate_errors(); auto label = element->accessible_name(element->document()).release_value_but_fixme_should_propagate_errors();
@ -1338,7 +1338,7 @@ Messages::WebDriverClient::ElementClickResponse WebDriverConnection::element_cli
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known element with element id. // 3. Let element be the result of trying to get a known element with element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. If the element is an input element in the file upload state return error with error code invalid argument. // 4. If the element is an input element in the file upload state return error with error code invalid argument.
if (is<Web::HTML::HTMLInputElement>(*element)) { if (is<Web::HTML::HTMLInputElement>(*element)) {
@ -1467,7 +1467,7 @@ Messages::WebDriverClient::ElementClickResponse WebDriverConnection::element_cli
pointer_move_action.pointer_move_fields().position = { 0, 0 }; pointer_move_action.pointer_move_fields().position = { 0, 0 };
// 10. Set a property origin to element on pointer move action. // 10. Set a property origin to element on pointer move action.
auto origin = Web::WebDriver::get_or_create_a_web_element_reference(*element); auto origin = Web::WebDriver::get_or_create_a_web_element_reference(current_browsing_context(), *element);
pointer_move_action.pointer_move_fields().origin = MUST(String::from_byte_string(origin)); pointer_move_action.pointer_move_fields().origin = MUST(String::from_byte_string(origin));
// 11. Let pointer down action be an action object constructed with arguments input id, "pointer", and "pointerDown". // 11. Let pointer down action be an action object constructed with arguments input id, "pointer", and "pointerDown".
@ -1561,7 +1561,7 @@ Messages::WebDriverClient::ElementClearResponse WebDriverConnection::element_cle
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known element with session and element id. // 3. Let element be the result of trying to get a known element with session and element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. If element is not editable, return an error with error code invalid element state. // 4. If element is not editable, return an error with error code invalid element state.
if (!Web::WebDriver::is_element_editable(*element)) if (!Web::WebDriver::is_element_editable(*element))
@ -1617,7 +1617,7 @@ Messages::WebDriverClient::ElementSendKeysResponse WebDriverConnection::element_
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 5. Let element be the result of trying to get a known element with session and URL variables[element id]. // 5. Let element be the result of trying to get a known element with session and URL variables[element id].
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 6. Let file be true if element is input element in the file upload state, or false otherwise. // 6. Let file be true if element is input element in the file upload state, or false otherwise.
auto file = is<Web::HTML::HTMLInputElement>(*element) && static_cast<Web::HTML::HTMLInputElement&>(*element).type_state() == Web::HTML::HTMLInputElement::TypeAttributeState::FileUpload; auto file = is<Web::HTML::HTMLInputElement>(*element) && static_cast<Web::HTML::HTMLInputElement&>(*element).type_state() == Web::HTML::HTMLInputElement::TypeAttributeState::FileUpload;
@ -2273,7 +2273,7 @@ Messages::WebDriverClient::TakeElementScreenshotResponse WebDriverConnection::ta
TRY(handle_any_user_prompts()); TRY(handle_any_user_prompts());
// 3. Let element be the result of trying to get a known connected element with url variable element id. // 3. Let element be the result of trying to get a known connected element with url variable element id.
auto element = TRY(Web::WebDriver::get_known_element(element_id)); auto element = TRY(Web::WebDriver::get_known_element(current_browsing_context(), element_id));
// 4. Scroll into view the element. // 4. Scroll into view the element.
(void)scroll_element_into_view(*element); (void)scroll_element_into_view(*element);
@ -2545,14 +2545,14 @@ ErrorOr<JsonArray, Web::WebDriver::Error> WebDriverConnection::find(StartNodeGet
// 8. For each element in elements returned, append the web element reference object for element, to result. // 8. For each element in elements returned, append the web element reference object for element, to result.
for (size_t i = 0; i < elements->length(); ++i) for (size_t i = 0; i < elements->length(); ++i)
TRY(result.append(Web::WebDriver::web_element_reference_object(*elements->item(i)))); TRY(result.append(Web::WebDriver::web_element_reference_object(current_browsing_context(), *elements->item(i))));
// 9. Return success with data result. // 9. Return success with data result.
return result; return result;
} }
// https://w3c.github.io/webdriver/#dfn-json-deserialize // https://w3c.github.io/webdriver/#dfn-json-deserialize
static ErrorOr<JS::Value, Web::WebDriver::Error> json_deserialize(JS::VM& vm, JsonValue const& value) static ErrorOr<JS::Value, Web::WebDriver::Error> json_deserialize(JS::VM& vm, Web::HTML::BrowsingContext const& browsing_context, JsonValue const& value)
{ {
// 1. If seen is not provided, let seen be an empty List. // 1. If seen is not provided, let seen be an empty List.
// 2. Jump to the first appropriate step below: // 2. Jump to the first appropriate step below:
@ -2570,7 +2570,7 @@ static ErrorOr<JS::Value, Web::WebDriver::Error> json_deserialize(JS::VM& vm, Js
// -> Object that represents a web element // -> Object that represents a web element
if (Web::WebDriver::represents_a_web_element(value)) { if (Web::WebDriver::represents_a_web_element(value)) {
// Return the deserialized web element of value. // Return the deserialized web element of value.
return Web::WebDriver::deserialize_web_element(value.as_object()); return Web::WebDriver::deserialize_web_element(browsing_context, value.as_object());
} }
// FIXME: -> Object that represents a shadow root // FIXME: -> Object that represents a shadow root
@ -2604,7 +2604,7 @@ ErrorOr<WebDriverConnection::ScriptArguments, Web::WebDriver::Error> WebDriverCo
auto arguments = JS::MarkedVector<JS::Value> { vm.heap() }; auto arguments = JS::MarkedVector<JS::Value> { vm.heap() };
TRY(args.try_for_each([&](auto const& arg) -> ErrorOr<void, Web::WebDriver::Error> { TRY(args.try_for_each([&](auto const& arg) -> ErrorOr<void, Web::WebDriver::Error> {
arguments.append(TRY(json_deserialize(vm, arg))); arguments.append(TRY(json_deserialize(vm, current_browsing_context(), arg)));
return {}; return {};
})); }));