2022-11-08 10:03:07 -05:00
/*
* Copyright ( c ) 2022 , Florent Castelli < florent . castelli @ gmail . com >
2023-01-27 13:25:34 +00:00
* Copyright ( c ) 2022 - 2023 , Sam Atkins < atkinssj @ serenityos . org >
2022-11-08 10:03:07 -05:00
* Copyright ( c ) 2022 , Tobias Christiansen < tobyase @ serenityos . org >
* Copyright ( c ) 2022 , Linus Groh < linusg @ serenityos . org >
* Copyright ( c ) 2022 , Tim Flynn < trflynn89 @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/JsonObject.h>
# include <AK/JsonValue.h>
# include <AK/Vector.h>
2022-11-10 20:55:17 -05:00
# include <LibJS/Runtime/JSONObject.h>
2022-11-10 09:25:53 -05:00
# include <LibJS/Runtime/Value.h>
2022-11-10 10:11:04 -05:00
# include <LibWeb/CSS/PropertyID.h>
# include <LibWeb/CSS/StyleProperties.h>
# include <LibWeb/CSS/StyleValue.h>
2022-11-11 09:24:07 -05:00
# include <LibWeb/Cookie/Cookie.h>
2022-11-21 16:10:57 -05:00
# include <LibWeb/Cookie/ParsedCookie.h>
2022-11-08 13:06:22 -05:00
# include <LibWeb/DOM/Document.h>
2022-11-10 09:29:10 -05:00
# include <LibWeb/DOM/Element.h>
2023-01-26 17:40:32 +00:00
# include <LibWeb/DOM/Event.h>
2023-01-27 13:25:34 +00:00
# include <LibWeb/DOM/NodeFilter.h>
# include <LibWeb/DOM/NodeIterator.h>
2022-11-14 19:27:11 -05:00
# include <LibWeb/DOM/ShadowRoot.h>
2022-11-10 10:33:29 -05:00
# include <LibWeb/Geometry/DOMRect.h>
2022-11-10 08:57:37 -05:00
# include <LibWeb/HTML/AttributeNames.h>
2022-11-08 10:03:07 -05:00
# include <LibWeb/HTML/BrowsingContext.h>
2023-01-26 17:40:32 +00:00
# include <LibWeb/HTML/Focus.h>
2022-11-10 10:39:09 -05:00
# include <LibWeb/HTML/FormAssociatedElement.h>
2023-01-27 13:25:34 +00:00
# include <LibWeb/HTML/HTMLDataListElement.h>
2022-11-10 08:15:39 -05:00
# include <LibWeb/HTML/HTMLInputElement.h>
2023-01-27 13:25:34 +00:00
# include <LibWeb/HTML/HTMLOptGroupElement.h>
2022-11-10 08:15:39 -05:00
# include <LibWeb/HTML/HTMLOptionElement.h>
2023-01-27 13:25:34 +00:00
# include <LibWeb/HTML/HTMLSelectElement.h>
2022-11-08 10:03:07 -05:00
# include <LibWeb/Page/Page.h>
2022-11-09 09:56:20 -05:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2022-11-08 10:03:07 -05:00
# include <LibWeb/Platform/Timer.h>
2023-01-26 17:40:32 +00:00
# include <LibWeb/UIEvents/MouseEvent.h>
2022-11-21 16:10:57 -05:00
# include <LibWeb/WebDriver/ExecuteScript.h>
2022-11-10 13:20:44 -05:00
# include <LibWeb/WebDriver/Screenshot.h>
2022-11-08 10:03:07 -05:00
# include <WebContent/WebDriverConnection.h>
namespace WebContent {
2022-11-11 09:24:07 -05:00
// https://w3c.github.io/webdriver/#dfn-serialized-cookie
static JsonValue serialize_cookie ( Web : : Cookie : : Cookie const & cookie )
{
JsonObject serialized_cookie ;
serialized_cookie . set ( " name " sv , cookie . name ) ;
serialized_cookie . set ( " value " sv , cookie . value ) ;
serialized_cookie . set ( " path " sv , cookie . path ) ;
serialized_cookie . set ( " domain " sv , cookie . domain ) ;
serialized_cookie . set ( " secure " sv , cookie . secure ) ;
serialized_cookie . set ( " httpOnly " sv , cookie . http_only ) ;
serialized_cookie . set ( " expiry " sv , cookie . expiry_time . timestamp ( ) ) ;
serialized_cookie . set ( " sameSite " sv , Web : : Cookie : : same_site_to_string ( cookie . same_site ) ) ;
return serialized_cookie ;
}
2022-11-09 09:56:20 -05:00
static JsonValue serialize_rect ( Gfx : : IntRect const & rect )
{
JsonObject serialized_rect = { } ;
serialized_rect . set ( " x " , rect . x ( ) ) ;
serialized_rect . set ( " y " , rect . y ( ) ) ;
serialized_rect . set ( " width " , rect . width ( ) ) ;
serialized_rect . set ( " height " , rect . height ( ) ) ;
2022-11-13 00:52:44 -05:00
return serialized_rect ;
2022-11-09 09:56:20 -05:00
}
static Gfx : : IntRect compute_window_rect ( Web : : Page const & page )
{
return {
page . window_position ( ) . x ( ) ,
page . window_position ( ) . y ( ) ,
page . window_size ( ) . width ( ) ,
page . window_size ( ) . height ( )
} ;
}
2022-11-10 10:33:29 -05:00
// https://w3c.github.io/webdriver/#dfn-calculate-the-absolute-position
static Gfx : : IntPoint calculate_absolute_position_of_element ( Web : : Page const & page , JS : : NonnullGCPtr < Web : : Geometry : : DOMRect > rect )
{
// 1. Let rect be the value returned by calling getBoundingClientRect().
// 2. Let window be the associated window of current top-level browsing context.
auto const * window = page . top_level_browsing_context ( ) . active_window ( ) ;
// 3. Let x be (scrollX of window + rect’ s x coordinate).
auto x = ( window ? static_cast < int > ( window - > scroll_x ( ) ) : 0 ) + static_cast < int > ( rect - > x ( ) ) ;
// 4. Let y be (scrollY of window + rect’ s y coordinate).
auto y = ( window ? static_cast < int > ( window - > scroll_y ( ) ) : 0 ) + static_cast < int > ( rect - > y ( ) ) ;
// 5. Return a pair of (x, y).
return { x , y } ;
}
static Gfx : : IntRect calculate_absolute_rect_of_element ( Web : : Page const & page , Web : : DOM : : Element const & element )
{
auto bounding_rect = element . get_bounding_client_rect ( ) ;
auto coordinates = calculate_absolute_position_of_element ( page , bounding_rect ) ;
return {
coordinates . x ( ) ,
coordinates . y ( ) ,
static_cast < int > ( bounding_rect - > width ( ) ) ,
static_cast < int > ( bounding_rect - > height ( ) )
} ;
}
2022-11-09 15:02:38 -05:00
// https://w3c.github.io/webdriver/#dfn-get-or-create-a-web-element-reference
2022-12-04 18:02:33 +00:00
static DeprecatedString get_or_create_a_web_element_reference ( Web : : DOM : : Node const & element )
2022-11-09 15:02:38 -05:00
{
// FIXME: 1. For each known element of the current browsing context’ s list of known elements:
// FIXME: 1. If known element equals element, return success with known element’ s web element reference.
// FIXME: 2. Add element to the list of known elements of the current browsing context.
// FIXME: 3. Return success with the element’ s web element reference.
2022-12-04 18:02:33 +00:00
return DeprecatedString : : number ( element . id ( ) ) ;
2022-11-09 15:02:38 -05:00
}
// https://w3c.github.io/webdriver/#dfn-web-element-reference-object
static JsonObject web_element_reference_object ( Web : : DOM : : Node const & element )
{
// https://w3c.github.io/webdriver/#dfn-web-element-identifier
2022-12-04 18:02:33 +00:00
static DeprecatedString const web_element_identifier = " element-6066-11e4-a52e-4f735466cecf " sv ;
2022-11-09 15:02:38 -05:00
// 1. Let identifier be the web element identifier.
auto identifier = web_element_identifier ;
// 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 ) ;
// 3. Return a JSON Object initialized with a property with name identifier and value reference.
JsonObject object ;
object . set ( " name " sv , identifier ) ;
object . set ( " value " sv , reference ) ;
return object ;
}
2022-11-09 15:18:28 -05:00
// https://w3c.github.io/webdriver/#dfn-get-a-known-connected-element
2022-11-10 09:29:10 -05:00
static ErrorOr < Web : : DOM : : Element * , Web : : WebDriver : : Error > get_known_connected_element ( StringView element_id )
2022-11-09 15:18:28 -05:00
{
// 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 ) ;
2022-11-10 09:29:10 -05:00
if ( ! node | | ! node - > is_element ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchElement , DeprecatedString : : formatted ( " Could not find element with ID: {} " , element_id ) ) ;
2022-11-09 15:18:28 -05:00
2022-11-10 09:29:10 -05:00
return static_cast < Web : : DOM : : Element * > ( node ) ;
2022-11-09 15:18:28 -05:00
}
2022-11-14 19:27:11 -05:00
// https://w3c.github.io/webdriver/#dfn-get-or-create-a-shadow-root-reference
2022-12-04 18:02:33 +00:00
static DeprecatedString get_or_create_a_shadow_root_reference ( Web : : DOM : : ShadowRoot const & shadow_root )
2022-11-14 19:27:11 -05:00
{
// 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.
2022-12-04 18:02:33 +00:00
return DeprecatedString : : number ( shadow_root . id ( ) ) ;
2022-11-14 19:27:11 -05:00
}
// https://w3c.github.io/webdriver/#dfn-shadow-root-reference-object
static JsonObject shadow_root_reference_object ( Web : : DOM : : ShadowRoot const & shadow_root )
{
// https://w3c.github.io/webdriver/#dfn-shadow-root-identifier
2022-12-04 18:02:33 +00:00
static DeprecatedString const shadow_root_identifier = " shadow-6066-11e4-a52e-4f735466cecf " sv ;
2022-11-14 19:27:11 -05:00
// 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 ) ;
// 3. Return a JSON Object initialized with a property with name identifier and value reference.
JsonObject object ;
object . set ( " name " sv , move ( identifier ) ) ;
object . set ( " value " sv , move ( reference ) ) ;
return object ;
}
2022-11-14 19:48:48 -05:00
// https://w3c.github.io/webdriver/#dfn-get-a-known-shadow-root
static ErrorOr < Web : : DOM : : ShadowRoot * , Web : : WebDriver : : Error > get_known_shadow_root ( StringView shadow_id )
{
// 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_int ( ) ;
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_id ( * shadow_root ) ;
if ( ! node | | ! node - > is_shadow_root ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchElement , DeprecatedString : : formatted ( " Could not find shadow root with ID: {} " , shadow_id ) ) ;
2022-11-14 19:48:48 -05:00
return static_cast < Web : : DOM : : ShadowRoot * > ( node ) ;
}
2022-11-10 13:33:18 -05:00
// https://w3c.github.io/webdriver/#dfn-scrolls-into-view
2022-12-24 13:15:12 +01:00
static ErrorOr < void > scroll_element_into_view ( Web : : DOM : : Element & element )
2022-11-10 13:33:18 -05:00
{
// 1. Let options be the following ScrollIntoViewOptions:
Web : : DOM : : ScrollIntoViewOptions options { } ;
// Logical scroll position "block"
// "end"
options . block = Web : : Bindings : : ScrollLogicalPosition : : End ;
// Logical scroll position "inline"
// "nearest"
options . inline_ = Web : : Bindings : : ScrollLogicalPosition : : Nearest ;
// 2. Run Function.[[Call]](scrollIntoView, options) with element as the this value.
2022-12-24 13:15:12 +01:00
TRY ( element . scroll_into_view ( options ) ) ;
return { } ;
2022-11-10 13:33:18 -05:00
}
2022-12-04 18:02:33 +00:00
template < typename PropertyType = DeprecatedString >
2022-11-10 20:55:17 -05:00
static ErrorOr < PropertyType , Web : : WebDriver : : Error > get_property ( JsonValue const & payload , StringView key )
2022-11-09 15:02:38 -05:00
{
if ( ! payload . is_object ( ) )
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , " Payload is not a JSON object " ) ;
2022-12-21 20:56:50 +00:00
auto property = payload . as_object ( ) . get ( key ) ;
2022-11-09 15:02:38 -05:00
2022-12-21 20:56:50 +00:00
if ( ! property . has_value ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " No property called '{}' present " , key ) ) ;
2022-11-09 15:02:38 -05:00
2022-12-04 18:02:33 +00:00
if constexpr ( IsSame < PropertyType , DeprecatedString > ) {
2022-11-10 20:55:17 -05:00
if ( ! property - > is_string ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' is not a String " , key ) ) ;
2022-11-10 20:55:17 -05:00
return property - > as_string ( ) ;
2022-11-11 11:00:45 -05:00
} else if constexpr ( IsSame < PropertyType , bool > ) {
if ( ! property - > is_bool ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' is not a Boolean " , key ) ) ;
2022-11-11 11:00:45 -05:00
return property - > as_bool ( ) ;
} else if constexpr ( IsSame < PropertyType , u32 > ) {
if ( ! property - > is_u32 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' is not a Number " , key ) ) ;
2022-11-11 11:00:45 -05:00
return property - > as_u32 ( ) ;
2022-11-10 20:55:17 -05:00
} else if constexpr ( IsSame < PropertyType , JsonArray const * > ) {
if ( ! property - > is_array ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' is not an Array " , key ) ) ;
2022-11-10 20:55:17 -05:00
return & property - > as_array ( ) ;
2022-11-11 11:00:45 -05:00
} else if constexpr ( IsSame < PropertyType , JsonObject const * > ) {
if ( ! property - > is_object ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' is not an Object " , key ) ) ;
2022-11-11 11:00:45 -05:00
return & property - > as_object ( ) ;
2022-11-10 20:55:17 -05:00
} else {
static_assert ( DependentFalse < PropertyType > , " get_property invoked with unknown property type " ) ;
VERIFY_NOT_REACHED ( ) ;
}
2022-11-09 15:02:38 -05:00
}
2023-01-27 13:25:34 +00:00
// https://w3c.github.io/webdriver/#dfn-container
static Optional < Web : : DOM : : Element & > container_for_element ( Web : : DOM : : Element & element )
{
auto first_element_reached_by_traversing_the_tree_in_reverse_order = [ ] ( Web : : DOM : : Element & element , auto filter ) - > Optional < Web : : DOM : : Element & > {
auto node_iterator = element . document ( ) . create_node_iterator ( element , to_underlying ( Web : : DOM : : NodeFilter : : WhatToShow : : SHOW_ALL ) , nullptr ) ;
auto current_node = node_iterator - > previous_node ( ) ;
while ( current_node . has_value ( ) & & current_node . value ( ) ! = nullptr & & current_node . value ( ) - > is_element ( ) ) {
if ( filter ( current_node . value ( ) ) )
return static_cast < Web : : DOM : : Element & > ( * current_node . release_value ( ) ) ;
}
return { } ;
} ;
// An element’ s container is:
// -> option element in a valid element context
// -> optgroup element in a valid element context
// FIXME: Determine if the element is in a valid element context. (https://html.spec.whatwg.org/#concept-element-contexts)
if ( is < Web : : HTML : : HTMLOptionElement > ( element ) | | is < Web : : HTML : : HTMLOptGroupElement > ( element ) ) {
// The element’ s element context, which is determined by:
// 1. Let datalist parent be the first datalist element reached by traversing the tree in reverse order from element, or undefined if the root of the tree is reached.
auto datalist_parent = first_element_reached_by_traversing_the_tree_in_reverse_order ( element , [ ] ( auto & node ) { return is < Web : : HTML : : HTMLDataListElement > ( * node ) ; } ) ;
// 2. Let select parent be the first select element reached by traversing the tree in reverse order from element, or undefined if the root of the tree is reached.
auto select_parent = first_element_reached_by_traversing_the_tree_in_reverse_order ( element , [ ] ( auto & node ) { return is < Web : : HTML : : HTMLSelectElement > ( * node ) ; } ) ;
// 3. If datalist parent is undefined, the element context is select parent. Otherwise, the element context is datalist parent.
if ( ! datalist_parent . has_value ( ) )
return select_parent ;
return datalist_parent ;
}
// -> option element in an invalid element context
else if ( is < Web : : HTML : : HTMLOptionElement > ( element ) ) {
// The element does not have a container.
return { } ;
}
// -> Otherwise
else {
// The container is the element itself.
return element ;
}
}
2023-01-26 17:40:32 +00:00
template < typename T >
static bool fire_an_event ( DeprecatedString name , Optional < Web : : DOM : : Element & > target )
{
// FIXME: This is supposed to call the https://dom.spec.whatwg.org/#concept-event-fire DOM algorithm,
// but that doesn't seem to be implemented elsewhere. So, we'll ad-hack it for now. :^)
if ( ! target . has_value ( ) )
return false ;
auto event = T : : create ( target - > realm ( ) , name ) ;
return target - > dispatch_event ( * event ) ;
}
2022-12-04 18:02:33 +00:00
ErrorOr < NonnullRefPtr < WebDriverConnection > > WebDriverConnection : : connect ( Web : : PageClient & page_client , DeprecatedString const & webdriver_ipc_path )
2022-11-08 10:03:07 -05:00
{
dbgln_if ( WEBDRIVER_DEBUG , " Trying to connect to {} " , webdriver_ipc_path ) ;
auto socket = TRY ( Core : : Stream : : LocalSocket : : connect ( webdriver_ipc_path ) ) ;
dbgln_if ( WEBDRIVER_DEBUG , " Connected to WebDriver " ) ;
2022-12-15 08:44:10 -05:00
return adopt_nonnull_ref_or_enomem ( new ( nothrow ) WebDriverConnection ( move ( socket ) , page_client ) ) ;
2022-11-08 10:03:07 -05:00
}
2022-11-21 16:13:37 -05:00
WebDriverConnection : : WebDriverConnection ( NonnullOwnPtr < Core : : Stream : : LocalSocket > socket , Web : : PageClient & page_client )
2022-11-08 10:03:07 -05:00
: IPC : : ConnectionToServer < WebDriverClientEndpoint , WebDriverServerEndpoint > ( * this , move ( socket ) )
2022-11-21 15:25:21 -05:00
, m_page_client ( page_client )
2022-11-14 11:55:10 -05:00
, m_current_window_handle ( " main " sv )
2022-11-08 10:03:07 -05:00
{
2022-11-14 11:55:10 -05:00
m_windows . set ( m_current_window_handle , { m_current_window_handle , true } ) ;
2022-11-08 10:03:07 -05:00
}
2022-11-08 14:14:29 -05:00
// https://w3c.github.io/webdriver/#dfn-close-the-session
void WebDriverConnection : : close_session ( )
{
// 1. Set the webdriver-active flag to false.
set_is_webdriver_active ( false ) ;
// 2. An endpoint node must close any top-level browsing contexts associated with the session, without prompting to unload.
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . top_level_browsing_context ( ) . has_been_discarded ( ) )
m_page_client . page ( ) . top_level_browsing_context ( ) . close ( ) ;
2022-11-08 14:14:29 -05:00
}
2022-11-17 16:27:32 -05:00
void WebDriverConnection : : set_page_load_strategy ( Web : : WebDriver : : PageLoadStrategy const & page_load_strategy )
{
m_page_load_strategy = page_load_strategy ;
}
void WebDriverConnection : : set_unhandled_prompt_behavior ( Web : : WebDriver : : UnhandledPromptBehavior const & unhandled_prompt_behavior )
{
m_unhandled_prompt_behavior = unhandled_prompt_behavior ;
}
void WebDriverConnection : : set_strict_file_interactability ( bool strict_file_interactability )
{
m_strict_file_interactability = strict_file_interactability ;
}
2022-11-08 10:42:12 -05:00
void WebDriverConnection : : set_is_webdriver_active ( bool is_webdriver_active )
{
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . set_is_webdriver_active ( is_webdriver_active ) ;
2022-11-08 10:42:12 -05:00
}
2022-11-11 14:28:57 -05:00
// 9.1 Get Timeouts, https://w3c.github.io/webdriver/#dfn-get-timeouts
Messages : : WebDriverClient : : GetTimeoutsResponse WebDriverConnection : : get_timeouts ( )
{
// 1. Let timeouts be the timeouts object for session’ s timeouts configuration
auto timeouts = Web : : WebDriver : : timeouts_object ( m_timeouts_configuration ) ;
// 2. Return success with data timeouts.
return timeouts ;
}
// 9.2 Set Timeouts, https://w3c.github.io/webdriver/#dfn-set-timeouts
Messages : : WebDriverClient : : SetTimeoutsResponse WebDriverConnection : : set_timeouts ( JsonValue const & payload )
{
// 1. Let timeouts be the result of trying to JSON deserialize as a timeouts configuration the request’ s parameters.
auto timeouts = TRY ( Web : : WebDriver : : json_deserialize_as_a_timeouts_configuration ( payload ) ) ;
// 2. Make the session timeouts the new timeouts.
m_timeouts_configuration = move ( timeouts ) ;
// 3. Return success with data null.
return JsonValue { } ;
}
2022-11-08 12:58:24 -05:00
// 10.1 Navigate To, https://w3c.github.io/webdriver/#navigate-to
Messages : : WebDriverClient : : NavigateToResponse WebDriverConnection : : navigate_to ( JsonValue const & payload )
{
dbgln_if ( WEBDRIVER_DEBUG , " WebDriverConnection::navigate_to {} " , payload ) ;
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
2022-11-08 20:13:00 -05:00
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-08 12:58:24 -05:00
// 2. Let url be the result of getting the property url from the parameters argument.
if ( ! payload . is_object ( ) | | ! payload . as_object ( ) . has_string ( " url " sv ) )
2022-11-08 20:13:00 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , " Payload doesn't have a string `url` " sv ) ;
2022-12-21 20:56:50 +00:00
URL url ( payload . as_object ( ) . get_deprecated_string ( " url " sv ) . value ( ) ) ;
2022-11-08 12:58:24 -05:00
// FIXME: 3. If url is not an absolute URL or is not an absolute URL with fragment or not a local scheme, return error with error code invalid argument.
2022-11-16 06:58:14 -05:00
// 4. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-08 12:58:24 -05:00
// FIXME: 5. Let current URL be the current top-level browsing context’ s active document’ s URL.
// FIXME: 6. If current URL and url do not have the same absolute URL:
// FIXME: a. If timer has not been started, start a timer. If this algorithm has not completed before timer reaches the session’ s session page load timeout in milliseconds, return an error with error code timeout.
// 7. Navigate the current top-level browsing context to url.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . load ( url ) ;
2022-11-08 12:58:24 -05:00
// FIXME: 8. If url is special except for file and current URL and URL do not have the same absolute URL:
// FIXME: a. Try to wait for navigation to complete.
// FIXME: b. Try to run the post-navigation checks.
// FIXME: 9. Set the current browsing context with the current top-level browsing context.
// FIXME: 10. If the current top-level browsing context contains a refresh state pragma directive of time 1 second or less, wait until the refresh timeout has elapsed, a new navigate has begun, and return to the first step of this algorithm.
// 11. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-08 12:58:24 -05:00
}
2022-11-08 13:06:22 -05:00
// 10.2 Get Current URL, https://w3c.github.io/webdriver/#get-current-url
Messages : : WebDriverClient : : GetCurrentUrlResponse WebDriverConnection : : get_current_url ( )
{
dbgln_if ( WEBDRIVER_DEBUG , " WebDriverConnection::get_current_url " ) ;
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
2022-11-08 20:13:00 -05:00
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-08 13:06:22 -05:00
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-08 13:06:22 -05:00
// 3. Let url be the serialization of the current top-level browsing context’ s active document’ s document URL.
2022-12-06 01:12:49 +00:00
auto url = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) - > url ( ) . to_deprecated_string ( ) ;
2022-11-08 13:06:22 -05:00
// 4. Return success with data url.
2022-11-13 00:52:44 -05:00
return url ;
2022-11-08 13:06:22 -05:00
}
2022-11-11 13:46:22 -05:00
// 10.3 Back, https://w3c.github.io/webdriver/#dfn-back
Messages : : WebDriverClient : : BackResponse WebDriverConnection : : back ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 13:46:22 -05:00
// 3. Traverse the history by a delta – 1 for the current browsing context.
2022-11-21 16:10:57 -05:00
m_page_client . page_did_request_navigate_back ( ) ;
2022-11-11 13:46:22 -05:00
// FIXME: 4. If the previous step completed results in a pageHide event firing, wait until pageShow event fires or for the session page load timeout milliseconds to pass, whichever occurs sooner.
// FIXME: 5. If the previous step completed by the session page load timeout being reached, and user prompts have been handled, return error with error code timeout.
// 6. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-11 13:46:22 -05:00
}
// 10.4 Forward, https://w3c.github.io/webdriver/#dfn-forward
Messages : : WebDriverClient : : ForwardResponse WebDriverConnection : : forward ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 13:46:22 -05:00
// 3. Traverse the history by a delta 1 for the current browsing context.
2022-11-21 16:10:57 -05:00
m_page_client . page_did_request_navigate_forward ( ) ;
2022-11-11 13:46:22 -05:00
// FIXME: 4. If the previous step completed results in a pageHide event firing, wait until pageShow event fires or for the session page load timeout milliseconds to pass, whichever occurs sooner.
// FIXME: 5. If the previous step completed by the session page load timeout being reached, and user prompts have been handled, return error with error code timeout.
// 6. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-11 13:46:22 -05:00
}
// 10.5 Refresh, https://w3c.github.io/webdriver/#dfn-refresh
Messages : : WebDriverClient : : RefreshResponse WebDriverConnection : : refresh ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 13:46:22 -05:00
// 3. Initiate an overridden reload of the current top-level browsing context’ s active document.
2022-11-21 16:10:57 -05:00
m_page_client . page_did_request_refresh ( ) ;
2022-11-11 13:46:22 -05:00
// FIXME: 4. If url is special except for file:
// FIXME: 1. Try to wait for navigation to complete.
// FIXME: 2. Try to run the post-navigation checks.
// FIXME: 5. Set the current browsing context with current top-level browsing context.
// 6. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-11 13:46:22 -05:00
}
2022-11-11 13:50:43 -05:00
// 10.6 Get Title, https://w3c.github.io/webdriver/#dfn-get-title
Messages : : WebDriverClient : : GetTitleResponse WebDriverConnection : : get_title ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 13:50:43 -05:00
// 3. Let title be the initial value of the title IDL attribute of the current top-level browsing context's active document.
2022-11-21 15:25:21 -05:00
auto title = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) - > title ( ) ;
2022-11-11 13:50:43 -05:00
// 4. Return success with data title.
2022-11-13 00:52:44 -05:00
return title ;
2022-11-11 13:50:43 -05:00
}
2022-11-14 11:55:10 -05:00
// 11.1 Get Window Handle, https://w3c.github.io/webdriver/#get-window-handle
Messages : : WebDriverClient : : GetWindowHandleResponse WebDriverConnection : : get_window_handle ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. Return success with data being the window handle associated with the current top-level browsing context.
return m_current_window_handle ;
}
// 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window
Messages : : WebDriverClient : : CloseWindowResponse WebDriverConnection : : close_window ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-14 11:55:10 -05:00
// 3. Close the current top-level browsing context.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . top_level_browsing_context ( ) . close ( ) ;
2022-11-14 11:55:10 -05:00
m_windows . remove ( m_current_window_handle ) ;
// 4. If there are no more open top-level browsing contexts, then close the session.
if ( m_windows . is_empty ( ) )
close_session ( ) ;
// 5. Return the result of running the remote end steps for the Get Window Handles command.
return get_window_handles ( ) . take_response ( ) ;
}
2022-11-24 20:32:53 -06:00
// 11.3 Switch to Window, https://w3c.github.io/webdriver/#dfn-switch-to-window
Messages : : WebDriverClient : : SwitchToWindowResponse WebDriverConnection : : switch_to_window ( JsonValue const & payload )
{
// 1. Let handle be the result of getting the property "handle" from the parameters argument.
// 2. If handle is undefined, return error with error code invalid argument.
auto handle = TRY ( get_property ( payload , " handle " sv ) ) ;
// 3. If there is an active user prompt, that prevents the focusing of another top-level browsing
// context, return error with error code unexpected alert open.
if ( m_page_client . page ( ) . has_pending_dialog ( ) )
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnexpectedAlertOpen , " A user dialog is open " sv ) ;
// 4. If handle is equal to the associated window handle for some top-level browsing context in the
// current session, let context be the that browsing context, and set the current top-level
// browsing context with context.
// Otherwise, return error with error code no such window.
auto const & maybe_window = m_windows . get ( handle ) ;
if ( ! maybe_window . has_value ( ) )
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchWindow , " Window not found " ) ;
m_current_window_handle = handle ;
// FIXME: 5. Update any implementation-specific state that would result from the user selecting the current
// browsing context for interaction, without altering OS-level focus.
// 6. Return success with data null.
return JsonValue { } ;
}
2022-11-14 11:55:10 -05:00
// 11.4 Get Window Handles, https://w3c.github.io/webdriver/#dfn-get-window-handles
Messages : : WebDriverClient : : GetWindowHandlesResponse WebDriverConnection : : get_window_handles ( )
{
// 1. Let handles be a JSON List.
JsonArray handles { } ;
// 2. For each top-level browsing context in the remote end, push the associated window handle onto handles.
for ( auto const & window_handle : m_windows . keys ( ) )
handles . append ( window_handle ) ;
// 3. Return success with data handles.
return handles ;
}
2022-11-09 09:56:20 -05:00
// 11.8.1 Get Window Rect, https://w3c.github.io/webdriver/#dfn-get-window-rect
Messages : : WebDriverClient : : GetWindowRectResponse WebDriverConnection : : get_window_rect ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 09:56:20 -05:00
// 3. Return success with data set to the WindowRect object for the current top-level browsing context.
2022-11-21 15:25:21 -05:00
return serialize_rect ( compute_window_rect ( m_page_client . page ( ) ) ) ;
2022-11-09 09:56:20 -05:00
}
// 11.8.2 Set Window Rect, https://w3c.github.io/webdriver/#dfn-set-window-rect
Messages : : WebDriverClient : : SetWindowRectResponse WebDriverConnection : : set_window_rect ( JsonValue const & payload )
{
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 ( ) ;
2022-12-21 20:56:50 +00:00
auto resolve_property = [ ] ( auto name , auto const & property , auto min , auto max ) - > ErrorOr < Optional < i32 > , Web : : WebDriver : : Error > {
if ( property . is_null ( ) )
2022-11-09 09:56:20 -05:00
return Optional < i32 > { } ;
2022-12-21 20:56:50 +00:00
if ( ! property . is_number ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' is not a Number " , name ) ) ;
2022-11-09 09:56:20 -05:00
2022-12-21 20:56:50 +00:00
auto number = property . template to_number < i64 > ( ) ;
2022-11-09 09:56:20 -05:00
if ( number < min )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' value {} exceeds the minimum allowed value {} " , name , number , min ) ) ;
2022-11-09 09:56:20 -05:00
if ( number > max )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Property '{}' value {} exceeds the maximum allowed value {} " , name , number , max ) ) ;
2022-11-09 09:56:20 -05:00
return static_cast < i32 > ( number ) ;
} ;
// 1. Let width be the result of getting a property named width from the parameters argument, else let it be null.
2022-12-21 20:56:50 +00:00
auto width_property = properties . get ( " width " sv ) . value_or ( JsonValue ( ) ) ;
2022-11-09 09:56:20 -05:00
// 2. Let height be the result of getting a property named height from the parameters argument, else let it be null.
2022-12-21 20:56:50 +00:00
auto height_property = properties . get ( " height " sv ) . value_or ( JsonValue ( ) ) ;
2022-11-09 09:56:20 -05:00
// 3. Let x be the result of getting a property named x from the parameters argument, else let it be null.
2022-12-21 20:56:50 +00:00
auto x_property = properties . get ( " x " sv ) . value_or ( JsonValue ( ) ) ;
2022-11-09 09:56:20 -05:00
// 4. Let y be the result of getting a property named y from the parameters argument, else let it be null.
2022-12-21 20:56:50 +00:00
auto y_property = properties . get ( " y " sv ) . value_or ( JsonValue ( ) ) ;
2022-11-09 09:56:20 -05:00
// 5. If width or height is neither null nor a Number from 0 to 2^31 − 1, return error with error code invalid argument.
auto width = TRY ( resolve_property ( " width " sv , width_property , 0 , NumericLimits < i32 > : : max ( ) ) ) ;
auto height = TRY ( resolve_property ( " height " sv , height_property , 0 , NumericLimits < i32 > : : max ( ) ) ) ;
// 6. If x or y is neither null nor a Number from − (2^31) to 2^31 − 1, return error with error code invalid argument.
auto x = TRY ( resolve_property ( " x " sv , x_property , NumericLimits < i32 > : : min ( ) , NumericLimits < i32 > : : max ( ) ) ) ;
auto y = TRY ( resolve_property ( " y " sv , y_property , NumericLimits < i32 > : : min ( ) , NumericLimits < i32 > : : max ( ) ) ) ;
// 7. If the remote end does not support the Set Window Rect command for the current top-level browsing context for any reason, return error with error code unsupported operation.
// 8. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 9. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 09:56:20 -05:00
// FIXME: 10. Fully exit fullscreen.
// 11. Restore the window.
restore_the_window ( ) ;
Gfx : : IntRect window_rect ;
// 11. If width and height are not null:
if ( width . has_value ( ) & & height . has_value ( ) ) {
// a. Set the width, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to width.
// b. Set the height, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to height.
2022-11-21 16:10:57 -05:00
auto size = m_page_client . page_did_request_resize_window ( { * width , * height } ) ;
2022-11-09 09:56:20 -05:00
window_rect . set_size ( size ) ;
} else {
2022-11-03 12:49:54 +00:00
window_rect . set_size ( m_page_client . page ( ) . window_size ( ) . to_type < int > ( ) ) ;
2022-11-09 09:56:20 -05:00
}
// 12. If x and y are not null:
if ( x . has_value ( ) & & y . has_value ( ) ) {
// a. Run the implementation-specific steps to set the position of the operating system level window containing the current top-level browsing context to the position given by the x and y coordinates.
2022-11-21 16:10:57 -05:00
auto position = m_page_client . page_did_request_reposition_window ( { * x , * y } ) ;
2022-11-09 09:56:20 -05:00
window_rect . set_location ( position ) ;
} else {
2022-11-03 12:49:54 +00:00
window_rect . set_location ( m_page_client . page ( ) . window_position ( ) . to_type < int > ( ) ) ;
2022-11-09 09:56:20 -05:00
}
// 14. Return success with data set to the WindowRect object for the current top-level browsing context.
return serialize_rect ( window_rect ) ;
}
2022-11-09 11:00:53 -05:00
// 11.8.3 Maximize Window, https://w3c.github.io/webdriver/#dfn-maximize-window
Messages : : WebDriverClient : : MaximizeWindowResponse WebDriverConnection : : maximize_window ( )
{
// 1. If the remote end does not support the Maximize Window command for the current top-level browsing context for any reason, return error with error code unsupported operation.
// 2. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 3. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 11:00:53 -05:00
// FIXME: 4. Fully exit fullscreen.
// 5. Restore the window.
restore_the_window ( ) ;
// 6. Maximize the window of the current top-level browsing context.
auto window_rect = maximize_the_window ( ) ;
// 7. Return success with data set to the WindowRect object for the current top-level browsing context.
return serialize_rect ( window_rect ) ;
}
// 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
Messages : : WebDriverClient : : MinimizeWindowResponse WebDriverConnection : : minimize_window ( )
{
// 1. If the remote end does not support the Minimize Window command for the current top-level browsing context for any reason, return error with error code unsupported operation.
// 2. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 3. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 11:00:53 -05:00
// FIXME: 4. Fully exit fullscreen.
// 5. Iconify the window.
auto window_rect = iconify_the_window ( ) ;
// 6. Return success with data set to the WindowRect object for the current top-level browsing context.
return serialize_rect ( window_rect ) ;
}
2022-11-09 19:09:17 +01:00
// 11.8.5 Fullscreen Window, https://w3c.github.io/webdriver/#dfn-fullscreen-window
Messages : : WebDriverClient : : FullscreenWindowResponse WebDriverConnection : : fullscreen_window ( )
{
// 1. If the remote end does not support fullscreen return error with error code unsupported operation.
// 2. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 3. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 19:09:17 +01:00
// 4. Restore the window.
restore_the_window ( ) ;
// 5. FIXME: Call fullscreen an element with the current top-level browsing context’ s active document’ s document element.
// As described in https://fullscreen.spec.whatwg.org/#fullscreen-an-element
// NOTE: What we do here is basically `requestFullscreen(options)` with options["navigationUI"]="show"
2022-11-21 16:10:57 -05:00
auto rect = m_page_client . page_did_request_fullscreen_window ( ) ;
2022-11-09 19:09:17 +01:00
// 6. Return success with data set to the WindowRect object for the current top-level browsing context.
return serialize_rect ( rect ) ;
}
2022-11-09 15:02:38 -05:00
// 12.3.2 Find Element, https://w3c.github.io/webdriver/#dfn-find-element
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 = 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 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Location strategy '{}' is invalid " , location_strategy_string ) ) ;
2022-11-09 15:02:38 -05:00
// 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 ( ) ) ;
2022-11-16 06:58:14 -05:00
// 6. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 15:02:38 -05:00
2022-11-21 09:51:46 -05:00
auto start_node_getter = [ this ] ( ) - > StartNodeGetter : : ReturnType {
// 7. Let start node be the current browsing context’ s document element.
2022-11-21 15:25:21 -05:00
auto * start_node = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-09 15:02:38 -05:00
2022-11-21 09:51:46 -05:00
// 8. If start node is null, return error with error code no such element.
if ( ! start_node )
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchElement , " document element does not exist " sv ) ;
return start_node ;
} ;
2022-11-09 15:02:38 -05:00
// 9. Let result be the result of trying to Find with start node, location strategy, and selector.
2022-11-21 09:51:46 -05:00
auto result = TRY ( find ( move ( start_node_getter ) , * location_strategy , selector ) ) ;
2022-11-09 15:02:38 -05:00
// 10. 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 ) ;
2022-11-13 00:52:44 -05:00
return result . take ( 0 ) ;
2022-11-09 15:02:38 -05:00
}
2022-11-09 15:08:49 -05:00
// 12.3.3 Find Elements, https://w3c.github.io/webdriver/#dfn-find-elements
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 = 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 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Location strategy '{}' is invalid " , location_strategy_string ) ) ;
2022-11-09 15:08:49 -05:00
// 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 ( ) ) ;
2022-11-16 06:58:14 -05:00
// 6. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 15:08:49 -05:00
2022-11-21 09:51:46 -05:00
auto start_node_getter = [ this ] ( ) - > StartNodeGetter : : ReturnType {
// 7. Let start node be the current browsing context’ s document element.
2022-11-21 15:25:21 -05:00
auto * start_node = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-09 15:08:49 -05:00
2022-11-21 09:51:46 -05:00
// 8. If start node is null, return error with error code no such element.
if ( ! start_node )
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchElement , " document element does not exist " sv ) ;
return start_node ;
} ;
2022-11-09 15:08:49 -05:00
// 9. Return the result of trying to Find with start node, location strategy, and selector.
2022-11-21 09:51:46 -05:00
return TRY ( find ( move ( start_node_getter ) , * location_strategy , selector ) ) ;
2022-11-09 15:08:49 -05:00
}
2022-11-09 15:18:28 -05:00
// 12.3.4 Find Element From Element, https://w3c.github.io/webdriver/#dfn-find-element-from-element
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : FindElementFromElementResponse WebDriverConnection : : find_element_from_element ( JsonValue const & payload , DeprecatedString const & element_id )
2022-11-09 15:18:28 -05:00
{
// 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 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Location strategy '{}' is invalid " , location_strategy_string ) ) ;
2022-11-09 15:18:28 -05:00
// 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 ( ) ) ;
2022-11-16 06:58:14 -05:00
// 6. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 15:18:28 -05:00
2022-11-21 09:51:46 -05:00
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.
return TRY ( get_known_connected_element ( element_id ) ) ;
} ;
2022-11-09 15:18:28 -05:00
// 8. Let result be the value of trying to Find with start node, location strategy, and selector.
2022-11-21 09:51:46 -05:00
auto result = TRY ( find ( move ( start_node_getter ) , * location_strategy , selector ) ) ;
2022-11-09 15:18:28 -05:00
// 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 ) ;
2022-11-13 00:52:44 -05:00
return result . take ( 0 ) ;
2022-11-09 15:18:28 -05:00
}
2022-11-09 15:25:23 -05:00
// 12.3.5 Find Elements From Element, https://w3c.github.io/webdriver/#dfn-find-elements-from-element
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : FindElementsFromElementResponse WebDriverConnection : : find_elements_from_element ( JsonValue const & payload , DeprecatedString const & element_id )
2022-11-09 15:25:23 -05:00
{
// 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 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Location strategy '{}' is invalid " , location_strategy_string ) ) ;
2022-11-09 15:25:23 -05:00
// 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 ( ) ) ;
2022-11-16 06:58:14 -05:00
// 6. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-09 15:25:23 -05:00
2022-11-21 09:51:46 -05:00
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.
return TRY ( get_known_connected_element ( element_id ) ) ;
} ;
2022-11-09 15:25:23 -05:00
// 8. Return the result of trying to Find with start node, location strategy, and selector.
2022-11-21 09:51:46 -05:00
return TRY ( find ( move ( start_node_getter ) , * location_strategy , selector ) ) ;
2022-11-09 15:25:23 -05:00
}
2022-11-14 19:48:48 -05:00
// 12.3.6 Find Element From Shadow Root, https://w3c.github.io/webdriver/#find-element-from-shadow-root
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : FindElementFromShadowRootResponse WebDriverConnection : : find_element_from_shadow_root ( JsonValue const & payload , DeprecatedString const & shadow_id )
2022-11-14 19:48:48 -05:00
{
// 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 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Location strategy '{}' is invalid " , location_strategy_string ) ) ;
2022-11-14 19:48:48 -05:00
// 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 ( ) ) ;
2022-11-16 06:58:14 -05:00
// 6. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-14 19:48:48 -05:00
2022-11-21 09:51:46 -05:00
auto start_node_getter = [ & ] ( ) - > StartNodeGetter : : ReturnType {
// 7. Let start node be the result of trying to get a known shadow root with url variable shadow id.
return TRY ( get_known_shadow_root ( shadow_id ) ) ;
} ;
2022-11-14 19:48:48 -05:00
// 8. Let result be the value of trying to Find with start node, location strategy, and selector.
2022-11-21 09:51:46 -05:00
auto result = TRY ( find ( move ( start_node_getter ) , * location_strategy , selector ) ) ;
2022-11-14 19:48:48 -05:00
// 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 result . take ( 0 ) ;
}
2022-11-14 19:54:17 -05:00
// 12.3.7 Find Elements From Shadow Root, https://w3c.github.io/webdriver/#find-elements-from-shadow-root
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : FindElementsFromShadowRootResponse WebDriverConnection : : find_elements_from_shadow_root ( JsonValue const & payload , DeprecatedString const & shadow_id )
2022-11-14 19:54:17 -05:00
{
// 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 ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , DeprecatedString : : formatted ( " Location strategy '{}' is invalid " , location_strategy_string ) ) ;
2022-11-14 19:54:17 -05:00
// 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 ( ) ) ;
2022-11-16 06:58:14 -05:00
// 6. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-14 19:54:17 -05:00
2022-11-21 09:51:46 -05:00
auto start_node_getter = [ & ] ( ) - > StartNodeGetter : : ReturnType {
// 7. Let start node be the result of trying to get a known shadow root with url variable shadow id.
return TRY ( get_known_shadow_root ( shadow_id ) ) ;
} ;
2022-11-14 19:54:17 -05:00
// 8. Return the result of trying to Find with start node, location strategy, and selector.
2022-11-21 09:51:46 -05:00
return TRY ( find ( move ( start_node_getter ) , * location_strategy , selector ) ) ;
2022-11-14 19:54:17 -05:00
}
2022-11-14 19:02:23 -05:00
// 12.3.8 Get Active Element, https://w3c.github.io/webdriver/#get-active-element
Messages : : WebDriverClient : : GetActiveElementResponse WebDriverConnection : : get_active_element ( )
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-14 19:02:23 -05:00
// 3. Let active element be the active element of the current browsing context’ s document element.
2022-11-21 15:25:21 -05:00
auto * active_element = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) - > active_element ( ) ;
2022-11-14 19:02:23 -05:00
// 4. If active element is a non-null element, return success with data set to web element reference object for active element.
// Otherwise, return error with error code no such element.
if ( active_element )
2022-12-04 18:02:33 +00:00
return DeprecatedString : : number ( active_element - > id ( ) ) ;
2022-11-14 19:02:23 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchElement , " The current document does not have an active element " sv ) ;
}
2022-11-14 19:27:11 -05:00
// 12.3.9 Get Element Shadow Root, https://w3c.github.io/webdriver/#get-element-shadow-root
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementShadowRootResponse WebDriverConnection : : get_element_shadow_root ( DeprecatedString const & element_id )
2022-11-14 19:27:11 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-14 19:27:11 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let shadow root be element's shadow root.
auto * shadow_root = element - > shadow_root ( ) ;
// 5. If shadow root is null, return error with error code no such shadow root.
if ( ! shadow_root )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchShadowRoot , DeprecatedString : : formatted ( " Element with ID '{}' does not have a shadow root " , element_id ) ) ;
2022-11-14 19:27:11 -05:00
// 6. Let serialized be the shadow root reference object for shadow root.
auto serialized = shadow_root_reference_object ( * shadow_root ) ;
// 7. Return success with data serialized.
return serialized ;
}
2022-11-10 08:15:39 -05:00
// 12.4.1 Is Element Selected, https://w3c.github.io/webdriver/#dfn-is-element-selected
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : IsElementSelectedResponse WebDriverConnection : : is_element_selected ( DeprecatedString const & element_id )
2022-11-10 08:15:39 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 08:15:39 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let selected be the value corresponding to the first matching statement:
bool selected = false ;
// element is an input element with a type attribute in the Checkbox- or Radio Button state
if ( is < Web : : HTML : : HTMLInputElement > ( * element ) ) {
// -> The result of element’ s checkedness.
auto & input = static_cast < Web : : HTML : : HTMLInputElement & > ( * element ) ;
using enum Web : : HTML : : HTMLInputElement : : TypeAttributeState ;
if ( input . type_state ( ) = = Checkbox | | input . type_state ( ) = = RadioButton )
selected = input . checked ( ) ;
}
// element is an option element
else if ( is < Web : : HTML : : HTMLOptionElement > ( * element ) ) {
// -> The result of element’ s selectedness.
selected = static_cast < Web : : HTML : : HTMLOptionElement & > ( * element ) . selected ( ) ;
}
// Otherwise
// -> False.
// 5. Return success with data selected.
2022-11-13 00:52:44 -05:00
return selected ;
2022-11-10 08:15:39 -05:00
}
2022-11-10 08:57:37 -05:00
// 12.4.2 Get Element Attribute, https://w3c.github.io/webdriver/#dfn-get-element-attribute
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementAttributeResponse WebDriverConnection : : get_element_attribute ( DeprecatedString const & element_id , DeprecatedString const & name )
2022-11-10 08:57:37 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 08:57:37 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let result be the result of the first matching condition:
2022-12-04 18:02:33 +00:00
Optional < DeprecatedString > result ;
2022-11-10 08:57:37 -05:00
// -> If name is a boolean attribute
if ( Web : : HTML : : is_boolean_attribute ( name ) ) {
// "true" (string) if the element has the attribute, otherwise null.
if ( element - > has_attribute ( name ) )
result = " true " sv ;
}
// -> Otherwise
else {
// The result of getting an attribute by name name.
result = element - > get_attribute ( name ) ;
}
// 5. Return success with data result.
if ( result . has_value ( ) )
2022-11-13 00:52:44 -05:00
return result . release_value ( ) ;
return JsonValue { } ;
2022-11-10 08:57:37 -05:00
}
2022-11-10 09:25:53 -05:00
// 12.4.3 Get Element Property, https://w3c.github.io/webdriver/#dfn-get-element-property
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementPropertyResponse WebDriverConnection : : get_element_property ( DeprecatedString const & element_id , DeprecatedString const & name )
2022-11-10 09:25:53 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 09:25:53 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
2022-12-04 18:02:33 +00:00
Optional < DeprecatedString > result ;
2022-11-10 09:25:53 -05:00
// 4. Let property be the result of calling the Object.[[GetProperty]](name) on element.
if ( auto property_or_error = element - > get ( name ) ; ! property_or_error . is_throw_completion ( ) ) {
auto property = property_or_error . release_value ( ) ;
// 5. Let result be the value of property if not undefined, or null.
if ( ! property . is_undefined ( ) ) {
2023-01-13 10:29:02 -05:00
if ( auto string_or_error = property . to_deprecated_string ( element - > vm ( ) ) ; ! string_or_error . is_error ( ) )
2022-11-10 09:25:53 -05:00
result = string_or_error . release_value ( ) ;
}
}
// 6. Return success with data result.
if ( result . has_value ( ) )
2022-11-13 00:52:44 -05:00
return result . release_value ( ) ;
return JsonValue { } ;
2022-11-10 09:25:53 -05:00
}
2022-11-10 10:11:04 -05:00
// 12.4.4 Get Element CSS Value, https://w3c.github.io/webdriver/#dfn-get-element-css-value
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementCssValueResponse WebDriverConnection : : get_element_css_value ( DeprecatedString const & element_id , DeprecatedString const & name )
2022-11-10 10:11:04 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 10:11:04 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let computed value be the result of the first matching condition:
2022-12-04 18:02:33 +00:00
DeprecatedString computed_value ;
2022-11-10 10:11:04 -05:00
// -> current browsing context’ s active document’ s type is not "xml"
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) - > is_xml_document ( ) ) {
2022-11-10 10:11:04 -05:00
// computed value of parameter property name from element’ s style declarations. property name is obtained from url variables.
auto property = Web : : CSS : : property_id_from_string ( name ) ;
if ( auto * computed_values = element - > computed_css_values ( ) )
2023-01-06 19:02:26 +01:00
computed_value = computed_values - > property ( property ) - > to_string ( ) . release_value_but_fixme_should_propagate_errors ( ) . to_deprecated_string ( ) ;
2022-11-10 10:11:04 -05:00
}
// -> Otherwise
else {
// "" (empty string)
2022-12-04 18:02:33 +00:00
computed_value = DeprecatedString : : empty ( ) ;
2022-11-10 10:11:04 -05:00
}
// 5. Return success with data computed value.
2022-11-13 00:52:44 -05:00
return computed_value ;
2022-11-10 10:11:04 -05:00
}
2022-11-10 10:21:03 -05:00
// 12.4.5 Get Element Text, https://w3c.github.io/webdriver/#dfn-get-element-text
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementTextResponse WebDriverConnection : : get_element_text ( DeprecatedString const & element_id )
2022-11-10 10:21:03 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 10:21:03 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( 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.
auto rendered_text = element - > text_content ( ) ;
// 5. Return success with data rendered text.
2022-11-13 00:52:44 -05:00
return rendered_text ;
2022-11-10 10:21:03 -05:00
}
2022-11-10 10:24:49 -05:00
// 12.4.6 Get Element Tag Name, https://w3c.github.io/webdriver/#dfn-get-element-tag-name
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementTagNameResponse WebDriverConnection : : get_element_tag_name ( DeprecatedString const & element_id )
2022-11-10 10:24:49 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 10:24:49 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let qualified name be the result of getting element’ s tagName IDL attribute.
auto qualified_name = element - > tag_name ( ) ;
// 5. Return success with data qualified name.
2022-11-13 00:52:44 -05:00
return qualified_name ;
2022-11-10 10:24:49 -05:00
}
2022-11-10 10:33:29 -05:00
// 12.4.7 Get Element Rect, https://w3c.github.io/webdriver/#dfn-get-element-rect
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetElementRectResponse WebDriverConnection : : get_element_rect ( DeprecatedString const & element_id )
2022-11-10 10:33:29 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 10:33:29 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Calculate the absolute position of element and let it be coordinates.
// 5. Let rect be element’ s bounding rectangle.
2022-11-21 15:25:21 -05:00
auto rect = calculate_absolute_rect_of_element ( m_page_client . page ( ) , * element ) ;
2022-11-10 10:33:29 -05:00
// 6. Let body be a new JSON Object initialized with:
// "x"
// The first value of coordinates.
// "y"
// The second value of coordinates.
// "width"
// Value of rect’ s width dimension.
// "height"
// Value of rect’ s height dimension.
auto body = serialize_rect ( rect ) ;
// 7. Return success with data body.
return body ;
}
2022-11-10 10:39:09 -05:00
// 12.4.8 Is Element Enabled, https://w3c.github.io/webdriver/#dfn-is-element-enabled
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : IsElementEnabledResponse WebDriverConnection : : is_element_enabled ( DeprecatedString const & element_id )
2022-11-10 10:39:09 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 10:39:09 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let enabled be a boolean initially set to true if the current browsing context’ s active document’ s type is not "xml".
// 5. Otherwise, let enabled to false and jump to the last step of this algorithm.
2022-11-21 15:25:21 -05:00
bool enabled = ! m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) - > is_xml_document ( ) ;
2022-11-10 10:39:09 -05:00
// 6. Set enabled to false if a form control is disabled.
if ( enabled & & is < Web : : HTML : : FormAssociatedElement > ( * element ) ) {
auto & form_associated_element = dynamic_cast < Web : : HTML : : FormAssociatedElement & > ( * element ) ;
enabled = form_associated_element . enabled ( ) ;
}
// 7. Return success with data enabled.
2022-11-13 00:52:44 -05:00
return enabled ;
2022-11-10 10:39:09 -05:00
}
2023-01-15 10:26:08 -06:00
// 12.4.9 Get Computed Role, https://w3c.github.io/webdriver/#dfn-get-computed-role
Messages : : WebDriverClient : : GetComputedRoleResponse WebDriverConnection : : get_computed_role ( DeprecatedString const & element_id )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Let role be the result of computing the WAI-ARIA role of element.
auto role = element - > role_or_default ( ) ;
// 5. Return success with data role.
return DeprecatedString { role } ;
}
2022-12-24 02:17:06 +01:00
// 12.5.1 Element Click, https://w3c.github.io/webdriver/#element-click
2023-01-26 14:11:54 +00:00
Messages : : WebDriverClient : : ElementClickResponse WebDriverConnection : : element_click ( DeprecatedString const & element_id )
2022-12-24 02:17:06 +01:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
// 3. Let element be the result of trying to get a known element with element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 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 ) ) {
// -> The result of element’ s checkedness.
auto & input = static_cast < Web : : HTML : : HTMLInputElement & > ( * element ) ;
using enum Web : : HTML : : HTMLInputElement : : TypeAttributeState ;
if ( input . type_state ( ) = = FileUpload )
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidArgument , " Clicking on an input element in the file upload state is not supported " sv ) ;
}
// 5. Scroll into view the element’ s container.
2023-01-27 13:25:34 +00:00
auto element_container = container_for_element ( * element ) ;
auto scroll_or_error = scroll_element_into_view ( * element_container ) ;
2022-12-24 02:17:06 +01:00
if ( scroll_or_error . is_error ( ) )
2023-01-27 13:25:34 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnknownError , scroll_or_error . error ( ) . string_literal ( ) ) ;
// FIXME: 6. If element’ s container is still not in view, return error with error code element not interactable.
2022-12-24 02:17:06 +01:00
// FIXME: 7. If element’ s container is obscured by another element, return error with error code element click intercepted.
// 8. Matching on element:
2023-01-26 17:40:32 +00:00
// -> option element
if ( is < Web : : HTML : : HTMLOptionElement > ( * element ) ) {
auto & option_element = static_cast < Web : : HTML : : HTMLOptionElement & > ( * element ) ;
// 1. Let parent node be the element’ s container.
auto parent_node = element_container ;
// 2. Fire a mouseOver event at parent node.
fire_an_event < Web : : UIEvents : : MouseEvent > ( " mouseOver " , parent_node ) ;
// 3. Fire a mouseMove event at parent node.
fire_an_event < Web : : UIEvents : : MouseEvent > ( " mouseMove " , parent_node ) ;
// 4. Fire a mouseDown event at parent node.
fire_an_event < Web : : UIEvents : : MouseEvent > ( " mouseDown " , parent_node ) ;
// 5. Run the focusing steps on parent node.
Web : : HTML : : run_focusing_steps ( parent_node . has_value ( ) ? & * parent_node : nullptr ) ;
// 6. If element is not disabled:
if ( ! option_element . is_actually_disabled ( ) ) {
// 1. Fire an input event at parent node.
fire_an_event < Web : : DOM : : Event > ( " input " , parent_node ) ;
// 2. Let previous selectedness be equal to element selectedness.
auto previous_selectedness = option_element . selected ( ) ;
// 3. If element’ s container has the multiple attribute, toggle the element’ s selectedness state
// by setting it to the opposite value of its current selectedness.
if ( parent_node . has_value ( ) & & parent_node - > has_attribute ( " multiple " ) ) {
option_element . set_selected ( ! option_element . selected ( ) ) ;
}
// Otherwise, set the element’ s selectedness state to true.
else {
option_element . set_selected ( true ) ;
}
// 4. If previous selectedness is false, fire a change event at parent node.
if ( ! previous_selectedness ) {
fire_an_event < Web : : DOM : : Event > ( " change " , parent_node ) ;
}
}
// 7. Fire a mouseUp event at parent node.
fire_an_event < Web : : UIEvents : : MouseEvent > ( " mouseUp " , parent_node ) ;
// 8. Fire a click event at parent node.
fire_an_event < Web : : UIEvents : : MouseEvent > ( " click " , parent_node ) ;
}
// -> Otherwise
else {
// FIXME: 1. Let input state be the result of get the input state given current session and current top-level browsing context.
// FIXME: 2. Let actions options be a new actions options with the is element origin steps set to represents a web element, and the get element origin steps set to get a WebElement origin.
// FIXME: 3. Let input id be a the result of generating a UUID.
// FIXME: 4. Let source be the result of create an input source with input state, and "pointer".
// FIXME: 5. Add an input source with input state, input id and source.
// FIXME: 6. Let click point be the element’ s in-view center point.
// FIXME: 7. Let pointer move action be an action object constructed with arguments input id, "pointer", and "pointerMove".
// FIXME: 8. Set a property x to 0 on pointer move action.
// FIXME: 9. Set a property y to 0 on pointer move action.
// FIXME: 10. Set a property origin to element on pointer move action.
// FIXME: 11. Let pointer down action be an action object constructed with arguments input id, "pointer", and "pointerDown".
// FIXME: 12. Set a property button to 0 on pointer down action.
// FIXME: 13. Let pointer up action be an action object constructed with arguments input id, "mouse", and "pointerUp" as arguments.
// FIXME: 14. Set a property button to 0 on pointer up action.
// FIXME: 15. Let actions be the list «pointer move action, pointer down action, pointer move action».
// FIXME: 16. Dispatch a list of actions with input state, actions, current browsing context, and actions options.
// FIXME: 17. Remove an input source with input state and input id.
}
2022-12-24 02:17:06 +01:00
// FIXME: 9. Wait until the user agent event loop has spun enough times to process the DOM events generated by the previous step.
// FIXME: 10. Perform implementation-defined steps to allow any navigations triggered by the click to start.
// FIXME: 11. Try to wait for navigation to complete.
// FIXME: 12. Try to run the post-navigation checks.
// FIXME: 13. Return success with data null.
2023-01-27 13:25:34 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnsupportedOperation , " Click not implemented " sv ) ;
2022-12-24 02:17:06 +01:00
}
2022-11-10 18:00:01 -05:00
// 13.1 Get Page Source, https://w3c.github.io/webdriver/#dfn-get-page-source
Messages : : WebDriverClient : : GetSourceResponse WebDriverConnection : : get_source ( )
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 18:00:01 -05:00
2022-11-21 15:25:21 -05:00
auto * document = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-12-04 18:02:33 +00:00
Optional < DeprecatedString > source ;
2022-11-10 18:00:01 -05:00
// 3. Let source be the result of invoking the fragment serializing algorithm on a fictional node whose only child is the document element providing true for the require well-formed flag. If this causes an exception to be thrown, let source be null.
if ( auto result = document - > serialize_fragment ( Web : : DOMParsing : : RequireWellFormed : : Yes ) ; ! result . is_error ( ) )
source = result . release_value ( ) ;
// 4. Let source be the result of serializing to string the current browsing context active document, if source is null.
if ( ! source . has_value ( ) )
source = MUST ( document - > serialize_fragment ( Web : : DOMParsing : : RequireWellFormed : : No ) ) ;
// 5. Return success with data source.
2022-11-13 00:52:44 -05:00
return source . release_value ( ) ;
2022-11-10 18:00:01 -05:00
}
2022-11-10 20:55:17 -05:00
// 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script
Messages : : WebDriverClient : : ExecuteScriptResponse WebDriverConnection : : execute_script ( JsonValue const & payload )
{
// 1. Let body and arguments be the result of trying to extract the script arguments from a request with argument parameters.
auto const & [ body , arguments ] = TRY ( extract_the_script_arguments_from_a_request ( payload ) ) ;
// 2. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 3. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 20:55:17 -05:00
// 4., 5.1-5.3.
2022-11-21 15:25:21 -05:00
auto result = Web : : WebDriver : : execute_script ( m_page_client . page ( ) , body , move ( arguments ) , m_timeouts_configuration . script_timeout ) ;
2022-11-10 20:55:17 -05:00
dbgln_if ( WEBDRIVER_DEBUG , " Executing script returned: {} " , result . value ) ;
2022-11-10 21:00:27 -05:00
switch ( result . type ) {
// 6. If promise is still pending and the session script timeout is reached, return error with error code script timeout.
case Web : : WebDriver : : ExecuteScriptResultType : : Timeout :
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : ScriptTimeoutError , " Script timed out " ) ;
// 7. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
case Web : : WebDriver : : ExecuteScriptResultType : : PromiseResolved :
2022-11-13 00:52:44 -05:00
return move ( result . value ) ;
2022-11-10 21:00:27 -05:00
// 8. Upon rejection of promise with reason r, let result be a JSON clone of r, and return error with error code javascript error and data result.
case Web : : WebDriver : : ExecuteScriptResultType : : PromiseRejected :
case Web : : WebDriver : : ExecuteScriptResultType : : JavaScriptError :
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : JavascriptError , " Script returned an error " , move ( result . value ) ) ;
}
VERIFY_NOT_REACHED ( ) ;
}
// 13.2.2 Execute Async Script, https://w3c.github.io/webdriver/#dfn-execute-async-script
Messages : : WebDriverClient : : ExecuteAsyncScriptResponse WebDriverConnection : : execute_async_script ( JsonValue const & payload )
{
// 1. Let body and arguments by the result of trying to extract the script arguments from a request with argument parameters.
auto const & [ body , arguments ] = TRY ( extract_the_script_arguments_from_a_request ( payload ) ) ;
// 2. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 3. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 21:00:27 -05:00
// 4., 5.1-5.11.
2022-11-21 15:25:21 -05:00
auto result = Web : : WebDriver : : execute_async_script ( m_page_client . page ( ) , body , move ( arguments ) , m_timeouts_configuration . script_timeout ) ;
2022-11-10 21:00:27 -05:00
dbgln_if ( WEBDRIVER_DEBUG , " Executing async script returned: {} " , result . value ) ;
2022-11-10 20:55:17 -05:00
switch ( result . type ) {
// 6. If promise is still pending and the session script timeout is reached, return error with error code script timeout.
case Web : : WebDriver : : ExecuteScriptResultType : : Timeout :
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : ScriptTimeoutError , " Script timed out " ) ;
// 7. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
case Web : : WebDriver : : ExecuteScriptResultType : : PromiseResolved :
2022-11-13 00:52:44 -05:00
return move ( result . value ) ;
2022-11-10 20:55:17 -05:00
// 8. Upon rejection of promise with reason r, let result be a JSON clone of r, and return error with error code javascript error and data result.
case Web : : WebDriver : : ExecuteScriptResultType : : PromiseRejected :
case Web : : WebDriver : : ExecuteScriptResultType : : JavaScriptError :
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : JavascriptError , " Script returned an error " , move ( result . value ) ) ;
}
VERIFY_NOT_REACHED ( ) ;
}
2022-11-11 09:24:07 -05:00
// 14.1 Get All Cookies, https://w3c.github.io/webdriver/#dfn-get-all-cookies
Messages : : WebDriverClient : : GetAllCookiesResponse WebDriverConnection : : get_all_cookies ( )
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 2. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 09:24:07 -05:00
// 3. Let cookies be a new JSON List.
JsonArray cookies ;
// 4. For each cookie in all associated cookies of the current browsing context’ s active document:
2022-11-21 15:25:21 -05:00
auto * document = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-11 09:24:07 -05:00
2022-11-21 16:10:57 -05:00
for ( auto const & cookie : m_page_client . page_did_request_all_cookies ( document - > url ( ) ) ) {
2022-11-11 09:24:07 -05:00
// 1. Let serialized cookie be the result of serializing cookie.
auto serialized_cookie = serialize_cookie ( cookie ) ;
// 2. Append serialized cookie to cookies
cookies . append ( move ( serialized_cookie ) ) ;
}
// 5. Return success with data cookies.
2022-11-13 00:52:44 -05:00
return cookies ;
2022-11-11 09:24:07 -05:00
}
2022-11-11 09:55:11 -05:00
// 14.2 Get Named Cookie, https://w3c.github.io/webdriver/#dfn-get-named-cookie
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : GetNamedCookieResponse WebDriverConnection : : get_named_cookie ( DeprecatedString const & name )
2022-11-11 09:55:11 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 2. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 09:55:11 -05:00
// 3. If the url variable name is equal to a cookie’ s cookie name amongst all associated cookies of the current browsing context’ s active document, return success with the serialized cookie as data.
2022-11-21 15:25:21 -05:00
auto * document = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-11 09:55:11 -05:00
2022-11-21 16:10:57 -05:00
if ( auto cookie = m_page_client . page_did_request_named_cookie ( document - > url ( ) , name ) ; cookie . has_value ( ) ) {
2022-11-11 09:55:11 -05:00
auto serialized_cookie = serialize_cookie ( * cookie ) ;
2022-11-13 00:52:44 -05:00
return serialized_cookie ;
2022-11-11 09:55:11 -05:00
}
// 4. Otherwise, return error with error code no such cookie.
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchCookie , DeprecatedString : : formatted ( " Cookie '{}' not found " , name ) ) ;
2022-11-11 09:55:11 -05:00
}
2022-11-11 11:00:45 -05:00
// 14.3 Add Cookie, https://w3c.github.io/webdriver/#dfn-adding-a-cookie
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 ) ) ;
// 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.
// 3. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 4. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 11:00:45 -05:00
// FIXME: 5. If the current browsing context’ s document element is a cookie-averse Document object, return error with error code invalid cookie domain.
// 6. If cookie name or cookie value is null, cookie domain is not equal to the current browsing context’ s active document’ s domain, cookie secure only or cookie HTTP only are not boolean types, or cookie expiry time is not an integer type, or it less than 0 or greater than the maximum safe integer, return error with error code invalid argument.
// NOTE: This validation is either performed in subsequent steps, or is performed by the CookieJar (namely domain matching).
// 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 = TRY ( get_property ( data , " name " sv ) ) ;
cookie . value = TRY ( get_property ( data , " value " sv ) ) ;
// Cookie path
// The value if the entry exists, otherwise "/".
if ( data . has ( " path " sv ) )
cookie . path = TRY ( get_property ( data , " path " sv ) ) ;
else
cookie . path = " / " ;
// Cookie domain
// 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 = TRY ( 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 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 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 ) ) ;
cookie . expiry_time_from_expires_attribute = Core : : DateTime : : from_timestamp ( 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 ) ) ;
cookie . same_site_attribute = Web : : Cookie : : same_site_from_string ( same_site ) ;
}
2022-11-21 15:25:21 -05:00
auto * document = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-21 16:10:57 -05:00
m_page_client . page_did_set_cookie ( document - > url ( ) , cookie , Web : : Cookie : : Source : : Http ) ;
2022-11-11 11:00:45 -05:00
// If there is an error during this step, return error with error code unable to set cookie.
// NOTE: This probably should only apply to the actual setting of the cookie in the Browser, which cannot fail in our case.
// 8. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-11 11:00:45 -05:00
}
2022-11-11 11:21:00 -05:00
// 14.4 Delete Cookie, https://w3c.github.io/webdriver/#dfn-delete-cookie
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : DeleteCookieResponse WebDriverConnection : : delete_cookie ( DeprecatedString const & name )
2022-11-11 11:21:00 -05:00
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 2. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 11:21:00 -05:00
// 3. Delete cookies using the url variable name parameter as the filter argument.
delete_cookies ( name ) ;
// 4. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-11 11:21:00 -05:00
}
2022-11-11 11:27:08 -05:00
// 14.5 Delete All Cookies, https://w3c.github.io/webdriver/#dfn-delete-all-cookies
Messages : : WebDriverClient : : DeleteAllCookiesResponse WebDriverConnection : : delete_all_cookies ( )
{
// 1. If the current browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-18 07:10:04 -05:00
// 2. Handle any user prompts, and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-11 11:27:08 -05:00
// 3. Delete cookies, giving no filtering argument.
delete_cookies ( ) ;
// 4. Return success with data null.
2022-11-13 00:52:44 -05:00
return JsonValue { } ;
2022-11-11 11:27:08 -05:00
}
2022-11-16 07:15:57 -05:00
// 16.1 Dismiss Alert, https://w3c.github.io/webdriver/#dismiss-alert
Messages : : WebDriverClient : : DismissAlertResponse WebDriverConnection : : dismiss_alert ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. If there is no current user prompt, return error with error code no such alert.
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . has_pending_dialog ( ) )
2022-11-16 07:15:57 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchAlert , " No user dialog is currently open " sv ) ;
// 3. Dismiss the current user prompt.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . dismiss_dialog ( ) ;
2022-11-16 07:15:57 -05:00
// 4. Return success with data null.
return JsonValue { } ;
}
2022-11-16 07:24:22 -05:00
// 16.2 Accept Alert, https://w3c.github.io/webdriver/#accept-alert
Messages : : WebDriverClient : : AcceptAlertResponse WebDriverConnection : : accept_alert ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. If there is no current user prompt, return error with error code no such alert.
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . has_pending_dialog ( ) )
2022-11-16 07:24:22 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchAlert , " No user dialog is currently open " sv ) ;
// 3. Accept the current user prompt.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . accept_dialog ( ) ;
2022-11-16 07:24:22 -05:00
// 4. Return success with data null.
return JsonValue { } ;
}
2022-11-16 08:26:48 -05:00
// 16.3 Get Alert Text, https://w3c.github.io/webdriver/#get-alert-text
Messages : : WebDriverClient : : GetAlertTextResponse WebDriverConnection : : get_alert_text ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. If there is no current user prompt, return error with error code no such alert.
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . has_pending_dialog ( ) )
2022-11-16 08:26:48 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchAlert , " No user dialog is currently open " sv ) ;
// 3. Let message be the text message associated with the current user prompt, or otherwise be null.
2022-11-21 15:25:21 -05:00
auto const & message = m_page_client . page ( ) . pending_dialog_text ( ) ;
2022-11-16 08:26:48 -05:00
// 4. Return success with data message.
if ( message . has_value ( ) )
return * message ;
return JsonValue { } ;
}
2022-11-16 08:57:05 -05:00
// 16.4 Send Alert Text, https://w3c.github.io/webdriver/#send-alert-text
Messages : : WebDriverClient : : SendAlertTextResponse WebDriverConnection : : send_alert_text ( JsonValue const & payload )
{
// 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 ) ) ;
// 3. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 4. If there is no current user prompt, return error with error code no such alert.
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . has_pending_dialog ( ) )
2022-11-16 08:57:05 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchAlert , " No user dialog is currently open " sv ) ;
// 5. Run the substeps of the first matching current user prompt:
2022-11-21 15:25:21 -05:00
switch ( m_page_client . page ( ) . pending_dialog ( ) ) {
2022-11-16 08:57:05 -05:00
// -> alert
// -> confirm
2022-11-21 15:18:42 -05:00
case Web : : Page : : PendingDialog : : Alert :
case Web : : Page : : PendingDialog : : Confirm :
2022-11-16 08:57:05 -05:00
// Return error with error code element not interactable.
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : ElementNotInteractable , " Only prompt dialogs may receive text " sv ) ;
// -> prompt
2022-11-21 15:18:42 -05:00
case Web : : Page : : PendingDialog : : Prompt :
2022-11-16 08:57:05 -05:00
// Do nothing.
break ;
// -> Otherwise
default :
// Return error with error code unsupported operation.
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnsupportedOperation , " Unknown dialog type " sv ) ;
}
// 6. Perform user agent dependent steps to set the value of current user prompt’ s text field to text.
2022-11-21 16:10:57 -05:00
m_page_client . page_did_request_set_prompt_text ( move ( text ) ) ;
2022-11-16 08:57:05 -05:00
// 7. Return success with data null.
return JsonValue { } ;
}
2022-11-10 13:20:44 -05:00
// 17.1 Take Screenshot, https://w3c.github.io/webdriver/#take-screenshot
Messages : : WebDriverClient : : TakeScreenshotResponse WebDriverConnection : : take_screenshot ( )
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
// 2. When the user agent is next to run the animation frame callbacks:
// a. Let root rect be the current top-level browsing context’ s document element’ s rectangle.
// b. Let screenshot result be the result of trying to call draw a bounding box from the framebuffer, given root rect as an argument.
// c. Let canvas be a canvas element of screenshot result’ s data.
// d. Let encoding result be the result of trying encoding a canvas as Base64 canvas.
// e. Let encoded string be encoding result’ s data.
2022-11-21 15:25:21 -05:00
auto * document = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
auto root_rect = calculate_absolute_rect_of_element ( m_page_client . page ( ) , * document - > document_element ( ) ) ;
2022-11-10 13:20:44 -05:00
auto encoded_string = TRY ( Web : : WebDriver : : capture_element_screenshot (
2022-11-25 17:07:19 +00:00
[ & ] ( auto const & rect , auto & bitmap ) { m_page_client . paint ( rect . template to_type < Web : : DevicePixels > ( ) , bitmap ) ; } ,
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) ,
2022-11-10 13:20:44 -05:00
* document - > document_element ( ) ,
root_rect ) ) ;
// 3. Return success with data encoded string.
2022-11-13 00:52:44 -05:00
return encoded_string ;
2022-11-10 13:20:44 -05:00
}
2022-11-10 13:33:18 -05:00
// 17.2 Take Element Screenshot, https://w3c.github.io/webdriver/#dfn-take-element-screenshot
2022-12-04 18:02:33 +00:00
Messages : : WebDriverClient : : TakeElementScreenshotResponse WebDriverConnection : : take_element_screenshot ( DeprecatedString const & element_id )
2022-11-10 13:33:18 -05:00
{
// 1. If the current top-level browsing context is no longer open, return error with error code no such window.
TRY ( ensure_open_top_level_browsing_context ( ) ) ;
2022-11-16 06:58:14 -05:00
// 2. Handle any user prompts and return its value if it is an error.
TRY ( handle_any_user_prompts ( ) ) ;
2022-11-10 13:33:18 -05:00
// 3. Let element be the result of trying to get a known connected element with url variable element id.
auto * element = TRY ( get_known_connected_element ( element_id ) ) ;
// 4. Scroll into view the element.
2022-12-24 13:15:12 +01:00
( void ) scroll_element_into_view ( * element ) ;
2022-11-10 13:33:18 -05:00
// 5. When the user agent is next to run the animation frame callbacks:
// a. Let element rect be element’ s rectangle.
// b. Let screenshot result be the result of trying to call draw a bounding box from the framebuffer, given element rect as an argument.
// c. Let canvas be a canvas element of screenshot result’ s data.
// d. Let encoding result be the result of trying encoding a canvas as Base64 canvas.
// e. Let encoded string be encoding result’ s data.
2022-11-21 15:25:21 -05:00
auto element_rect = calculate_absolute_rect_of_element ( m_page_client . page ( ) , * element ) ;
2022-11-10 13:33:18 -05:00
auto encoded_string = TRY ( Web : : WebDriver : : capture_element_screenshot (
2022-11-25 17:07:19 +00:00
[ & ] ( auto const & rect , auto & bitmap ) { m_page_client . paint ( rect . template to_type < Web : : DevicePixels > ( ) , bitmap ) ; } ,
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) ,
2022-11-10 13:33:18 -05:00
* element ,
element_rect ) ) ;
// 6. Return success with data encoded string.
2022-11-13 00:52:44 -05:00
return encoded_string ;
2022-11-10 13:33:18 -05:00
}
2022-11-27 00:30:03 +01:00
// 18.1 Print Page, https://w3c.github.io/webdriver/#dfn-print-page
Messages : : WebDriverClient : : PrintPageResponse WebDriverConnection : : print_page ( )
{
// FIXME: Actually implement this :^)
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnsupportedOperation , " Print not implemented " sv ) ;
}
2022-11-08 12:58:24 -05:00
// https://w3c.github.io/webdriver/#dfn-no-longer-open
ErrorOr < void , Web : : WebDriver : : Error > WebDriverConnection : : ensure_open_top_level_browsing_context ( )
{
// A browsing context is said to be no longer open if it has been discarded.
2022-11-21 15:25:21 -05:00
if ( m_page_client . page ( ) . top_level_browsing_context ( ) . has_been_discarded ( ) )
2022-11-08 12:58:24 -05:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : NoSuchWindow , " Window not found " sv ) ;
return { } ;
}
2022-11-16 06:58:14 -05:00
// https://w3c.github.io/webdriver/#dfn-handle-any-user-prompts
ErrorOr < void , Web : : WebDriver : : Error > WebDriverConnection : : handle_any_user_prompts ( )
{
// 1. If there is no current user prompt, abort these steps and return success.
2022-11-21 15:25:21 -05:00
if ( ! m_page_client . page ( ) . has_pending_dialog ( ) )
2022-11-16 06:58:14 -05:00
return { } ;
// 2. Perform the following substeps based on the current session’ s user prompt handler:
2022-11-17 16:45:19 -05:00
switch ( m_unhandled_prompt_behavior ) {
2022-11-16 06:58:14 -05:00
// -> dismiss state
2022-11-17 16:45:19 -05:00
case Web : : WebDriver : : UnhandledPromptBehavior : : Dismiss :
// Dismiss the current user prompt.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . dismiss_dialog ( ) ;
2022-11-17 16:45:19 -05:00
break ;
2022-11-16 06:58:14 -05:00
// -> accept state
2022-11-17 16:45:19 -05:00
case Web : : WebDriver : : UnhandledPromptBehavior : : Accept :
// Accept the current user prompt.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . accept_dialog ( ) ;
2022-11-17 16:45:19 -05:00
break ;
2022-11-16 06:58:14 -05:00
// -> dismiss and notify state
2022-11-17 16:45:19 -05:00
case Web : : WebDriver : : UnhandledPromptBehavior : : DismissAndNotify :
2022-11-16 06:58:14 -05:00
// Dismiss the current user prompt.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . dismiss_dialog ( ) ;
2022-11-16 06:58:14 -05:00
// Return an annotated unexpected alert open error.
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnexpectedAlertOpen , " A user dialog is open " sv ) ;
2022-11-17 16:45:19 -05:00
2022-11-16 06:58:14 -05:00
// -> accept and notify state
2022-11-17 16:45:19 -05:00
case Web : : WebDriver : : UnhandledPromptBehavior : : AcceptAndNotify :
// Accept the current user prompt.
2022-11-21 15:25:21 -05:00
m_page_client . page ( ) . accept_dialog ( ) ;
2022-11-17 16:45:19 -05:00
// Return an annotated unexpected alert open error.
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnexpectedAlertOpen , " A user dialog is open " sv ) ;
2022-11-16 06:58:14 -05:00
// -> ignore state
2022-11-17 16:45:19 -05:00
case Web : : WebDriver : : UnhandledPromptBehavior : : Ignore :
// Return an annotated unexpected alert open error.
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : UnexpectedAlertOpen , " A user dialog is open " sv ) ;
}
2022-11-16 06:58:14 -05:00
// 3. Return success.
return { } ;
}
2022-11-09 09:56:20 -05:00
// https://w3c.github.io/webdriver/#dfn-restore-the-window
void WebDriverConnection : : restore_the_window ( )
{
// To restore the window, given an operating system level window with an associated top-level browsing context, run implementation-specific steps to restore or unhide the window to the visible screen.
2022-11-21 16:10:57 -05:00
m_page_client . page_did_request_restore_window ( ) ;
2022-11-09 09:56:20 -05:00
// Do not return from this operation until the visibility state of the top-level browsing context’ s active document has reached the visible state, or until the operation times out.
// FIXME: Implement timeouts.
Web : : Platform : : EventLoopPlugin : : the ( ) . spin_until ( [ this ] ( ) {
2022-11-21 15:25:21 -05:00
auto state = m_page_client . page ( ) . top_level_browsing_context ( ) . system_visibility_state ( ) ;
2022-11-09 11:00:53 -05:00
return state = = Web : : HTML : : VisibilityState : : Visible ;
} ) ;
}
// https://w3c.github.io/webdriver/#dfn-maximize-the-window
Gfx : : IntRect WebDriverConnection : : maximize_the_window ( )
{
// To maximize the window, given an operating system level window with an associated top-level browsing context, run the implementation-specific steps to transition the operating system level window into the maximized window state.
2022-11-21 16:10:57 -05:00
auto rect = m_page_client . page_did_request_maximize_window ( ) ;
2022-11-09 11:00:53 -05:00
// Return when the window has completed the transition, or within an implementation-defined timeout.
return rect ;
}
// https://w3c.github.io/webdriver/#dfn-iconify-the-window
Gfx : : IntRect WebDriverConnection : : iconify_the_window ( )
{
// To iconify the window, given an operating system level window with an associated top-level browsing context, run implementation-specific steps to iconify, minimize, or hide the window from the visible screen.
2022-11-21 16:10:57 -05:00
auto rect = m_page_client . page_did_request_minimize_window ( ) ;
2022-11-09 11:00:53 -05:00
// Do not return from this operation until the visibility state of the top-level browsing context’ s active document has reached the hidden state, or until the operation times out.
// FIXME: Implement timeouts.
Web : : Platform : : EventLoopPlugin : : the ( ) . spin_until ( [ this ] ( ) {
2022-11-21 15:25:21 -05:00
auto state = m_page_client . page ( ) . top_level_browsing_context ( ) . system_visibility_state ( ) ;
2022-11-09 11:00:53 -05:00
return state = = Web : : HTML : : VisibilityState : : Hidden ;
2022-11-09 09:56:20 -05:00
} ) ;
2022-11-09 11:00:53 -05:00
return rect ;
2022-11-09 09:56:20 -05:00
}
2022-11-09 15:02:38 -05:00
// https://w3c.github.io/webdriver/#dfn-find
2022-11-21 09:51:46 -05:00
ErrorOr < JsonArray , Web : : WebDriver : : Error > WebDriverConnection : : find ( StartNodeGetter & & start_node_getter , Web : : WebDriver : : LocationStrategy using_ , StringView value )
2022-11-09 15:02:38 -05:00
{
2022-11-21 11:19:28 -05:00
// 1. Let end time be the current time plus the session implicit wait timeout.
auto end_time = Time : : now_monotonic ( ) + Time : : from_milliseconds ( static_cast < i64 > ( m_timeouts_configuration . implicit_wait_timeout ) ) ;
2022-11-09 15:02:38 -05:00
// 2. Let location strategy be equal to using.
auto location_strategy = using_ ;
// 3. Let selector be equal to value.
auto selector = value ;
2022-11-21 11:19:28 -05:00
ErrorOr < JS : : GCPtr < Web : : DOM : : NodeList > , Web : : WebDriver : : Error > maybe_elements { nullptr } ;
2022-11-09 15:02:38 -05:00
2022-11-21 11:19:28 -05:00
auto try_to_find_element = [ & ] ( ) - > decltype ( maybe_elements ) {
// 4. Let elements returned be the result of trying to call the relevant element location strategy with arguments start node, and selector.
auto elements = Web : : WebDriver : : invoke_location_strategy ( location_strategy , * TRY ( start_node_getter ( ) ) , selector ) ;
2022-11-09 15:02:38 -05:00
2022-11-21 11:19:28 -05:00
// 5. If a DOMException, SyntaxError, XPathException, or other error occurs during the execution of the element location strategy, return error invalid selector.
if ( elements . is_error ( ) )
2022-12-04 18:02:33 +00:00
return Web : : WebDriver : : Error : : from_code ( Web : : WebDriver : : ErrorCode : : InvalidSelector , DeprecatedString : : formatted ( " The location strategy could not finish: {} " , elements . error ( ) . message ) ) ;
2022-11-21 11:19:28 -05:00
return elements . release_value ( ) ;
} ;
Web : : Platform : : EventLoopPlugin : : the ( ) . spin_until ( [ & ] ( ) {
maybe_elements = try_to_find_element ( ) ;
if ( maybe_elements . is_error ( ) )
return true ;
// 6. If elements returned is empty and the current time is less than end time return to step 4. Otherwise, continue to the next step.
return maybe_elements . value ( ) - > length ( ) ! = 0 | | Time : : now_monotonic ( ) > = end_time ;
} ) ;
auto elements = TRY ( maybe_elements ) ;
VERIFY ( elements ) ;
2022-11-09 15:02:38 -05:00
// 7. Let result be an empty JSON List.
JsonArray result ;
2022-11-21 11:19:28 -05:00
result . ensure_capacity ( elements - > length ( ) ) ;
2022-11-09 15:02:38 -05:00
// 8. For each element in elements returned, append the web element reference object for element, to result.
2022-11-21 11:19:28 -05:00
for ( size_t i = 0 ; i < elements - > length ( ) ; + + i )
result . append ( web_element_reference_object ( * elements - > item ( i ) ) ) ;
2022-11-09 15:02:38 -05:00
// 9. Return success with data result.
return result ;
}
2022-11-10 20:55:17 -05:00
// https://w3c.github.io/webdriver/#dfn-extract-the-script-arguments-from-a-request
ErrorOr < WebDriverConnection : : ScriptArguments , Web : : WebDriver : : Error > WebDriverConnection : : extract_the_script_arguments_from_a_request ( JsonValue const & payload )
{
2022-11-21 15:25:21 -05:00
auto * window = m_page_client . page ( ) . top_level_browsing_context ( ) . active_window ( ) ;
2022-11-10 20:55:17 -05:00
auto & vm = window - > vm ( ) ;
// 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 ) ) ;
// 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 ) ) ;
// 5. Let arguments be the result of calling the JSON deserialize algorithm with arguments args.
auto arguments = JS : : MarkedVector < JS : : Value > { vm . heap ( ) } ;
args . for_each ( [ & ] ( auto const & arg ) {
arguments . append ( JS : : JSONObject : : parse_json_value ( vm , arg ) ) ;
} ) ;
// 6. Return success with data script and arguments.
return ScriptArguments { move ( script ) , move ( arguments ) } ;
}
2022-11-11 11:21:00 -05:00
// https://w3c.github.io/webdriver/#dfn-delete-cookies
void WebDriverConnection : : delete_cookies ( Optional < StringView > const & name )
{
// For each cookie among all associated cookies of the current browsing context’ s active document, un the substeps of the first matching condition:
2022-11-21 15:25:21 -05:00
auto * document = m_page_client . page ( ) . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-11 11:21:00 -05:00
2022-11-21 16:10:57 -05:00
for ( auto & cookie : m_page_client . page_did_request_all_cookies ( document - > url ( ) ) ) {
2022-11-11 11:21:00 -05:00
// -> name is undefined
// -> name is equal to cookie name
if ( ! name . has_value ( ) | | name . value ( ) = = cookie . name ) {
// Set the cookie expiry time to a Unix timestamp in the past.
cookie . expiry_time = Core : : DateTime : : from_timestamp ( 0 ) ;
2022-11-28 11:24:04 -05:00
m_page_client . page_did_update_cookie ( move ( cookie ) ) ;
2022-11-11 11:21:00 -05:00
}
// -> Otherwise
// Do nothing.
}
}
2022-11-08 10:03:07 -05:00
}