2022-11-08 10:03:07 -05:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
|
|
|
|
|
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
2025-02-05 11:36:19 -05:00
|
|
|
* Copyright (c) 2022-2025, Tim Flynn <trflynn89@ladybird.org>
|
2022-11-08 10:03:07 -05:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2023-12-16 17:49:34 +03:30
|
|
|
#include <AK/ByteString.h>
|
2023-03-05 15:31:49 -05:00
|
|
|
#include <AK/String.h>
|
2024-12-26 14:32:52 +01:00
|
|
|
#include <LibGC/RootVector.h>
|
2023-02-25 10:43:09 -07:00
|
|
|
#include <LibGfx/Rect.h>
|
2022-11-08 10:03:07 -05:00
|
|
|
#include <LibIPC/ConnectionToServer.h>
|
2024-10-22 15:47:33 -06:00
|
|
|
#include <LibIPC/Transport.h>
|
2022-11-10 20:55:17 -05:00
|
|
|
#include <LibJS/Forward.h>
|
2022-11-09 15:02:38 -05:00
|
|
|
#include <LibWeb/Forward.h>
|
2024-10-28 23:45:18 -04:00
|
|
|
#include <LibWeb/HTML/VisibilityState.h>
|
2022-11-09 15:02:38 -05:00
|
|
|
#include <LibWeb/WebDriver/ElementLocationStrategies.h>
|
2024-10-31 20:23:36 -04:00
|
|
|
#include <LibWeb/WebDriver/ExecuteScript.h>
|
2022-11-08 10:03:07 -05:00
|
|
|
#include <LibWeb/WebDriver/Response.h>
|
2022-11-11 14:28:57 -05:00
|
|
|
#include <LibWeb/WebDriver/TimeoutsConfiguration.h>
|
2022-11-08 10:03:07 -05:00
|
|
|
#include <WebContent/Forward.h>
|
|
|
|
|
#include <WebContent/WebDriverClientEndpoint.h>
|
|
|
|
|
#include <WebContent/WebDriverServerEndpoint.h>
|
|
|
|
|
|
|
|
|
|
namespace WebContent {
|
|
|
|
|
|
2024-10-30 15:26:04 -04:00
|
|
|
class ElementLocator;
|
|
|
|
|
|
2022-11-08 10:03:07 -05:00
|
|
|
class WebDriverConnection final
|
|
|
|
|
: public IPC::ConnectionToServer<WebDriverClientEndpoint, WebDriverServerEndpoint> {
|
2022-12-15 08:44:10 -05:00
|
|
|
C_OBJECT_ABSTRACT(WebDriverConnection)
|
2022-11-08 10:03:07 -05:00
|
|
|
|
|
|
|
|
public:
|
2023-12-16 17:49:34 +03:30
|
|
|
static ErrorOr<NonnullRefPtr<WebDriverConnection>> connect(Web::PageClient& page_client, ByteString const& webdriver_ipc_path);
|
2022-11-08 10:03:07 -05:00
|
|
|
virtual ~WebDriverConnection() = default;
|
|
|
|
|
|
2024-10-17 18:50:36 -04:00
|
|
|
void visit_edges(JS::Cell::Visitor&);
|
|
|
|
|
|
2024-10-25 10:28:29 -04:00
|
|
|
void page_did_open_dialog(Badge<PageClient>);
|
|
|
|
|
|
2022-11-08 10:03:07 -05:00
|
|
|
private:
|
2024-10-22 15:47:33 -06:00
|
|
|
WebDriverConnection(IPC::Transport transport, Web::PageClient& page_client);
|
2022-11-08 10:03:07 -05:00
|
|
|
|
|
|
|
|
virtual void die() override { }
|
2022-11-08 14:14:29 -05:00
|
|
|
|
|
|
|
|
virtual void close_session() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual void set_page_load_strategy(Web::WebDriver::PageLoadStrategy page_load_strategy) override;
|
|
|
|
|
virtual void set_user_prompt_handler(Web::WebDriver::UserPromptHandler user_prompt_handler) override;
|
2022-11-17 16:27:32 -05:00
|
|
|
virtual void set_strict_file_interactability(bool strict_file_interactability) override;
|
2022-11-08 10:42:12 -05:00
|
|
|
virtual void set_is_webdriver_active(bool) override;
|
2022-11-11 14:28:57 -05:00
|
|
|
virtual Messages::WebDriverClient::GetTimeoutsResponse get_timeouts() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::SetTimeoutsResponse set_timeouts(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::NavigateToResponse navigate_to(JsonValue payload) override;
|
2022-11-08 13:06:22 -05:00
|
|
|
virtual Messages::WebDriverClient::GetCurrentUrlResponse get_current_url() override;
|
2022-11-11 13:46:22 -05:00
|
|
|
virtual Messages::WebDriverClient::BackResponse back() override;
|
|
|
|
|
virtual Messages::WebDriverClient::ForwardResponse forward() override;
|
|
|
|
|
virtual Messages::WebDriverClient::RefreshResponse refresh() override;
|
2022-11-11 13:50:43 -05:00
|
|
|
virtual Messages::WebDriverClient::GetTitleResponse get_title() override;
|
2023-03-13 17:45:34 +03:00
|
|
|
virtual Messages::WebDriverClient::GetWindowHandleResponse get_window_handle() override;
|
2022-11-14 11:55:10 -05:00
|
|
|
virtual Messages::WebDriverClient::CloseWindowResponse close_window() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::SwitchToWindowResponse switch_to_window(String handle) override;
|
|
|
|
|
virtual Messages::WebDriverClient::NewWindowResponse new_window(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::SwitchToFrameResponse switch_to_frame(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::SwitchToParentFrameResponse switch_to_parent_frame(JsonValue payload) override;
|
2022-11-09 09:56:20 -05:00
|
|
|
virtual Messages::WebDriverClient::GetWindowRectResponse get_window_rect() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::SetWindowRectResponse set_window_rect(JsonValue payload) override;
|
2022-11-09 11:00:53 -05:00
|
|
|
virtual Messages::WebDriverClient::MaximizeWindowResponse maximize_window() override;
|
|
|
|
|
virtual Messages::WebDriverClient::MinimizeWindowResponse minimize_window() override;
|
2022-11-09 19:09:17 +01:00
|
|
|
virtual Messages::WebDriverClient::FullscreenWindowResponse fullscreen_window() override;
|
2024-05-29 11:14:42 -06:00
|
|
|
virtual Messages::WebDriverClient::ConsumeUserActivationResponse consume_user_activation() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::FindElementResponse find_element(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::FindElementsResponse find_elements(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::FindElementFromElementResponse find_element_from_element(JsonValue payload, String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::FindElementsFromElementResponse find_elements_from_element(JsonValue payload, String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::FindElementFromShadowRootResponse find_element_from_shadow_root(JsonValue payload, String shadow_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::FindElementsFromShadowRootResponse find_elements_from_shadow_root(JsonValue payload, String shadow_id) override;
|
2022-11-14 19:02:23 -05:00
|
|
|
virtual Messages::WebDriverClient::GetActiveElementResponse get_active_element() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::GetElementShadowRootResponse get_element_shadow_root(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::IsElementSelectedResponse is_element_selected(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetElementAttributeResponse get_element_attribute(String element_id, String name) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetElementPropertyResponse get_element_property(String element_id, String name) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetElementCssValueResponse get_element_css_value(String element_id, String name) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetElementTextResponse get_element_text(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetElementTagNameResponse get_element_tag_name(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetElementRectResponse get_element_rect(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::IsElementEnabledResponse is_element_enabled(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetComputedRoleResponse get_computed_role(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::GetComputedLabelResponse get_computed_label(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::ElementClickResponse element_click(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::ElementClearResponse element_clear(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::ElementSendKeysResponse element_send_keys(String element_id, JsonValue payload) override;
|
2022-11-10 18:00:01 -05:00
|
|
|
virtual Messages::WebDriverClient::GetSourceResponse get_source() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::ExecuteScriptResponse execute_script(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::ExecuteAsyncScriptResponse execute_async_script(JsonValue payload) override;
|
2022-11-11 09:24:07 -05:00
|
|
|
virtual Messages::WebDriverClient::GetAllCookiesResponse get_all_cookies() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::GetNamedCookieResponse get_named_cookie(String name) override;
|
|
|
|
|
virtual Messages::WebDriverClient::AddCookieResponse add_cookie(JsonValue payload) override;
|
|
|
|
|
virtual Messages::WebDriverClient::DeleteCookieResponse delete_cookie(String name) override;
|
2022-11-11 11:27:08 -05:00
|
|
|
virtual Messages::WebDriverClient::DeleteAllCookiesResponse delete_all_cookies() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::PerformActionsResponse perform_actions(JsonValue payload) override;
|
2023-06-17 17:36:00 +02:00
|
|
|
virtual Messages::WebDriverClient::ReleaseActionsResponse release_actions() override;
|
2022-11-16 07:15:57 -05:00
|
|
|
virtual Messages::WebDriverClient::DismissAlertResponse dismiss_alert() override;
|
2022-11-16 07:24:22 -05:00
|
|
|
virtual Messages::WebDriverClient::AcceptAlertResponse accept_alert() override;
|
2022-11-16 08:26:48 -05:00
|
|
|
virtual Messages::WebDriverClient::GetAlertTextResponse get_alert_text() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::SendAlertTextResponse send_alert_text(JsonValue payload) override;
|
2022-11-10 13:20:44 -05:00
|
|
|
virtual Messages::WebDriverClient::TakeScreenshotResponse take_screenshot() override;
|
2025-03-08 12:22:39 -05:00
|
|
|
virtual Messages::WebDriverClient::TakeElementScreenshotResponse take_element_screenshot(String element_id) override;
|
|
|
|
|
virtual Messages::WebDriverClient::PrintPageResponse print_page(JsonValue payload) override;
|
2023-03-07 11:29:14 -05:00
|
|
|
virtual Messages::WebDriverClient::EnsureTopLevelBrowsingContextIsOpenResponse ensure_top_level_browsing_context_is_open() override;
|
2024-09-13 17:18:23 -04:00
|
|
|
|
2024-10-01 13:04:37 -04:00
|
|
|
void set_current_browsing_context(Web::HTML::BrowsingContext&);
|
2024-09-13 17:18:23 -04:00
|
|
|
Web::HTML::BrowsingContext& current_browsing_context() { return *m_current_browsing_context; }
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Web::HTML::BrowsingContext> current_parent_browsing_context() { return m_current_parent_browsing_context; }
|
2024-10-01 13:04:37 -04:00
|
|
|
|
|
|
|
|
void set_current_top_level_browsing_context(Web::HTML::BrowsingContext&);
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Web::HTML::BrowsingContext> current_top_level_browsing_context() { return m_current_top_level_browsing_context; }
|
2024-09-13 17:18:23 -04:00
|
|
|
|
|
|
|
|
ErrorOr<void, Web::WebDriver::Error> ensure_current_browsing_context_is_open();
|
|
|
|
|
ErrorOr<void, Web::WebDriver::Error> ensure_current_top_level_browsing_context_is_open();
|
2023-03-07 11:29:14 -05:00
|
|
|
|
2025-03-08 12:22:39 -05:00
|
|
|
Web::WebDriver::Response element_click_impl(StringView element_id);
|
|
|
|
|
Web::WebDriver::Response element_clear_impl(StringView element_id);
|
|
|
|
|
Web::WebDriver::Response element_send_keys_impl(StringView element_id, String const& text);
|
2024-11-03 09:39:18 -05:00
|
|
|
Web::WebDriver::Response add_cookie_impl(JsonObject const&);
|
2024-11-01 08:20:14 -04:00
|
|
|
|
2025-02-05 11:36:19 -05:00
|
|
|
Web::WebDriver::PromptHandlerConfiguration get_the_prompt_handler(Web::WebDriver::PromptType type) const;
|
2024-11-01 08:20:14 -04:00
|
|
|
void handle_any_user_prompts(Function<void()> on_dialog_closed);
|
2024-10-28 23:45:18 -04:00
|
|
|
|
|
|
|
|
void maximize_the_window();
|
2024-11-15 04:01:23 +13:00
|
|
|
void iconify_the_window(GC::Ref<GC::Function<void()>>);
|
|
|
|
|
void restore_the_window(GC::Ref<GC::Function<void()>>);
|
|
|
|
|
void wait_for_visibility_state(GC::Ref<GC::Function<void()>>, Web::HTML::VisibilityState);
|
2022-11-21 09:51:46 -05:00
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
using OnNavigationComplete = GC::Ref<GC::Function<void(Web::WebDriver::Response)>>;
|
2024-10-25 10:28:29 -04:00
|
|
|
void wait_for_navigation_to_complete(OnNavigationComplete);
|
2023-03-08 02:04:17 +03:00
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
Gfx::IntPoint calculate_absolute_position_of_element(GC::Ref<Web::Geometry::DOMRect> rect);
|
2024-09-13 17:18:23 -04:00
|
|
|
Gfx::IntRect calculate_absolute_rect_of_element(Web::DOM::Element const& element);
|
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
using GetStartNode = GC::Ref<GC::Function<ErrorOr<GC::Ref<Web::DOM::ParentNode>, Web::WebDriver::Error>()>>;
|
|
|
|
|
using OnFindComplete = GC::Ref<GC::Function<void(Web::WebDriver::Response)>>;
|
2025-02-17 13:58:21 -05:00
|
|
|
void find(Web::WebDriver::LocationStrategy, String, GetStartNode, OnFindComplete);
|
2022-11-08 10:03:07 -05:00
|
|
|
|
2022-11-10 20:55:17 -05:00
|
|
|
struct ScriptArguments {
|
2025-02-17 13:58:21 -05:00
|
|
|
String script;
|
2024-12-26 14:32:52 +01:00
|
|
|
GC::RootVector<JS::Value> arguments;
|
2022-11-10 20:55:17 -05:00
|
|
|
};
|
2024-09-27 17:42:07 -04:00
|
|
|
ErrorOr<ScriptArguments, Web::WebDriver::Error> extract_the_script_arguments_from_a_request(JS::VM&, JsonValue const& payload);
|
WebContent: Track the ongoing script execution with an ID
Executing scripts via WebDriver has a bit of awkwardness around dealing
with user dialogs that open during script execution. When this happens,
we must return control back to the client immediately with a null
response, while allowing the script to continue executing. When the
script completes, we must then ignore its result.
We've previously handled this by tracking a boolean for the ongoing
script execution, set to true when the script begins and false when it
ends (either via normal script completion or the above dialog handling).
However, this failed to handle the following scenario, running two
scripts in a row:
execute_script("alert('hi'); return 1;")
execute_script("return 2;")
The first script would execute and open a dialog, and thus return a null
response to the client while the script continued and the dialog remains
open. The second script would "handle any user prompts", which closes
the dialog. This would end the execution of the first script. But since
we're now executing a script again, the boolean flag is true, and we'd
return the result of the first script back to the client. The client
would then think this is the result of the second script.
So we now track script execution with a simple ID. If a script completes
whose execution ID is not the ID of the currently executing script, we
drop the result.
2025-02-07 07:13:56 -05:00
|
|
|
void handle_script_response(Web::WebDriver::ExecutionResult, size_t script_execution_id);
|
2024-10-31 20:23:36 -04:00
|
|
|
|
2022-11-11 11:21:00 -05:00
|
|
|
void delete_cookies(Optional<StringView> const& name = {});
|
2022-11-10 20:55:17 -05:00
|
|
|
|
2022-11-17 16:27:32 -05:00
|
|
|
// https://w3c.github.io/webdriver/#dfn-page-load-strategy
|
|
|
|
|
Web::WebDriver::PageLoadStrategy m_page_load_strategy { Web::WebDriver::PageLoadStrategy::Normal };
|
|
|
|
|
|
|
|
|
|
// https://w3c.github.io/webdriver/#dfn-strict-file-interactability
|
|
|
|
|
bool m_strict_file_interactability { false };
|
|
|
|
|
|
2022-11-11 14:28:57 -05:00
|
|
|
// https://w3c.github.io/webdriver/#dfn-session-script-timeout
|
|
|
|
|
Web::WebDriver::TimeoutsConfiguration m_timeouts_configuration;
|
2024-09-13 17:18:23 -04:00
|
|
|
|
|
|
|
|
// https://w3c.github.io/webdriver/#dfn-current-browsing-context
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Web::HTML::BrowsingContext> m_current_browsing_context;
|
2024-09-29 09:40:17 -04:00
|
|
|
|
2024-10-01 13:04:37 -04:00
|
|
|
// https://w3c.github.io/webdriver/#dfn-current-parent-browsing-context
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Web::HTML::BrowsingContext> m_current_parent_browsing_context;
|
2024-10-01 13:04:37 -04:00
|
|
|
|
|
|
|
|
// https://w3c.github.io/webdriver/#dfn-current-top-level-browsing-context
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Web::HTML::BrowsingContext> m_current_top_level_browsing_context;
|
2024-10-01 13:04:37 -04:00
|
|
|
|
2024-10-28 23:37:11 -04:00
|
|
|
size_t m_pending_window_rect_requests { 0 };
|
WebContent: Track the ongoing script execution with an ID
Executing scripts via WebDriver has a bit of awkwardness around dealing
with user dialogs that open during script execution. When this happens,
we must return control back to the client immediately with a null
response, while allowing the script to continue executing. When the
script completes, we must then ignore its result.
We've previously handled this by tracking a boolean for the ongoing
script execution, set to true when the script begins and false when it
ends (either via normal script completion or the above dialog handling).
However, this failed to handle the following scenario, running two
scripts in a row:
execute_script("alert('hi'); return 1;")
execute_script("return 2;")
The first script would execute and open a dialog, and thus return a null
response to the client while the script continued and the dialog remains
open. The second script would "handle any user prompts", which closes
the dialog. This would end the execution of the first script. But since
we're now executing a script again, the boolean flag is true, and we'd
return the result of the first script back to the client. The client
would then think this is the result of the second script.
So we now track script execution with a simple ID. If a script completes
whose execution ID is not the ID of the currently executing script, we
drop the result.
2025-02-07 07:13:56 -05:00
|
|
|
|
|
|
|
|
size_t m_script_execution_id_counter { 0 };
|
|
|
|
|
Optional<size_t> m_current_script_execution_id;
|
2024-10-28 23:37:11 -04:00
|
|
|
|
2024-10-30 15:26:04 -04:00
|
|
|
friend class ElementLocator;
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<ElementLocator> m_element_locator;
|
2024-10-30 15:26:04 -04:00
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<JS::Cell> m_action_executor;
|
2024-10-25 10:28:29 -04:00
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Web::DOM::DocumentObserver> m_document_observer;
|
|
|
|
|
GC::Ptr<Web::HTML::NavigationObserver> m_navigation_observer;
|
|
|
|
|
GC::Ptr<Web::WebDriver::HeapTimer> m_navigation_timer;
|
2022-11-08 10:03:07 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|