diff --git a/Userland/Services/WebContent/WebDriverClient.ipc b/Userland/Services/WebContent/WebDriverClient.ipc index d330dc902d..c0922a3095 100644 --- a/Userland/Services/WebContent/WebDriverClient.ipc +++ b/Userland/Services/WebContent/WebDriverClient.ipc @@ -11,6 +11,9 @@ endpoint WebDriverClient { forward() => (Web::WebDriver::Response response) refresh() => (Web::WebDriver::Response response) get_title() => (Web::WebDriver::Response response) + get_window_handle() => (Web::WebDriver::Response response) + close_window() => (Web::WebDriver::Response response) + get_window_handles() => (Web::WebDriver::Response response) get_window_rect() => (Web::WebDriver::Response response) set_window_rect(JsonValue payload) => (Web::WebDriver::Response response) maximize_window() => (Web::WebDriver::Response response) diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index 0bfa776900..932d7434ca 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -216,7 +216,9 @@ WebDriverConnection::WebDriverConnection(NonnullOwnPtr(*this, move(socket)) , m_web_content_client(web_content_client) , m_page_host(page_host) + , m_current_window_handle("main"sv) { + m_windows.set(m_current_window_handle, { m_current_window_handle, true }); } // https://w3c.github.io/webdriver/#dfn-close-the-session @@ -226,7 +228,8 @@ void WebDriverConnection::close_session() set_is_webdriver_active(false); // 2. An endpoint node must close any top-level browsing contexts associated with the session, without prompting to unload. - m_page_host.page().top_level_browsing_context().close(); + if (!m_page_host.page().top_level_browsing_context().has_been_discarded()) + m_page_host.page().top_level_browsing_context().close(); } void WebDriverConnection::set_is_webdriver_active(bool is_webdriver_active) @@ -377,6 +380,50 @@ Messages::WebDriverClient::GetTitleResponse WebDriverConnection::get_title() return title; } +// 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()); + + // FIXME: 2. Handle any user prompts and return its value if it is an error. + + // 3. Close the current top-level browsing context. + m_page_host.page().top_level_browsing_context().close(); + 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(); +} + +// 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; +} + // 11.8.1 Get Window Rect, https://w3c.github.io/webdriver/#dfn-get-window-rect Messages::WebDriverClient::GetWindowRectResponse WebDriverConnection::get_window_rect() { diff --git a/Userland/Services/WebContent/WebDriverConnection.h b/Userland/Services/WebContent/WebDriverConnection.h index dbea45c7bc..64e4e2bd87 100644 --- a/Userland/Services/WebContent/WebDriverConnection.h +++ b/Userland/Services/WebContent/WebDriverConnection.h @@ -8,6 +8,8 @@ #pragma once +#include +#include #include #include #include @@ -44,6 +46,9 @@ private: virtual Messages::WebDriverClient::ForwardResponse forward() override; virtual Messages::WebDriverClient::RefreshResponse refresh() override; virtual Messages::WebDriverClient::GetTitleResponse get_title() override; + virtual Messages::WebDriverClient::GetWindowHandleResponse get_window_handle() override; + virtual Messages::WebDriverClient::CloseWindowResponse close_window() override; + virtual Messages::WebDriverClient::GetWindowHandlesResponse get_window_handles() override; virtual Messages::WebDriverClient::GetWindowRectResponse get_window_rect() override; virtual Messages::WebDriverClient::SetWindowRectResponse set_window_rect(JsonValue const& payload) override; virtual Messages::WebDriverClient::MaximizeWindowResponse maximize_window() override; @@ -90,6 +95,13 @@ private: // https://w3c.github.io/webdriver/#dfn-session-script-timeout Web::WebDriver::TimeoutsConfiguration m_timeouts_configuration; + + struct Window { + String handle; + bool is_open { false }; + }; + HashMap m_windows; + String m_current_window_handle; }; } diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp index 987639a200..3d4810eb0c 100644 --- a/Userland/Services/WebDriver/Client.cpp +++ b/Userland/Services/WebDriver/Client.cpp @@ -252,7 +252,7 @@ Web::WebDriver::Response Client::get_window_handle(Web::WebDriver::Parameters pa { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//window"); auto* session = TRY(find_session_with_id(parameters[0])); - return session->get_window_handle(); + return session->web_content_connection().get_window_handle(); } // 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window @@ -261,7 +261,12 @@ Web::WebDriver::Response Client::close_window(Web::WebDriver::Parameters paramet { dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session//window"); auto* session = TRY(find_session_with_id(parameters[0])); - return session->close_window(); + + auto open_windows = TRY(session->web_content_connection().close_window()); + if (open_windows.is_array() && open_windows.as_array().is_empty()) + TRY(session->stop()); + + return open_windows; } // 11.4 Get Window Handles, https://w3c.github.io/webdriver/#dfn-get-window-handles @@ -270,7 +275,7 @@ Web::WebDriver::Response Client::get_window_handles(Web::WebDriver::Parameters p { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//window/handles"); auto* session = TRY(find_session_with_id(parameters[0])); - return session->get_window_handles(); + return session->web_content_connection().get_window_handles(); } // 11.8.1 Get Window Rect, https://w3c.github.io/webdriver/#dfn-get-window-rect diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index 323ba5fde5..7510425cb6 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -10,16 +10,9 @@ #include "Session.h" #include "Client.h" -#include -#include -#include -#include #include #include #include -#include -#include -#include #include namespace WebDriver { @@ -32,26 +25,8 @@ Session::Session(unsigned session_id, NonnullRefPtr client) Session::~Session() { - if (m_started) { - auto error = stop(); - if (error.is_error()) { - warnln("Failed to stop session {}: {}", m_id, error.error()); - } - } -} - -ErrorOr Session::current_window() -{ - auto window = m_windows.get(m_current_window_handle); - if (!window.has_value()) - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchWindow, "Window not found"); - return window.release_value(); -} - -ErrorOr Session::check_for_open_top_level_browsing_context_or_return_error() -{ - (void)TRY(current_window()); - return {}; + if (auto error = stop(); error.is_error()) + warnln("Failed to stop session {}: {}", m_id, error.error()); } ErrorOr> Session::create_server(String const& socket_path, NonnullRefPtr promise) @@ -102,19 +77,18 @@ ErrorOr Session::start() TRY(promise->await()); m_started = true; - m_windows.set("main", make("main", true)); - m_current_window_handle = "main"; - return {}; } // https://w3c.github.io/webdriver/#dfn-close-the-session Web::WebDriver::Response Session::stop() { + if (!m_started) + return JsonValue {}; + // 1. Perform the following substeps based on the remote end’s type: // NOTE: We perform the "Remote end is an endpoint node" steps in the WebContent process. m_web_content_connection->close_session(); - m_web_content_connection = nullptr; // 2. Remove the current session from active sessions. // NOTE: Handled by WebDriver::Client. @@ -131,47 +105,4 @@ Web::WebDriver::Response Session::stop() return JsonValue {}; } -// 11.1 Get Window Handle, https://w3c.github.io/webdriver/#get-window-handle -Web::WebDriver::Response Session::get_window_handle() -{ - // 1. If the current top-level browsing context is no longer open, return error with error code no such window. - TRY(check_for_open_top_level_browsing_context_or_return_error()); - - // 2. Return success with data being the window handle associated with the current top-level browsing context. - return JsonValue { m_current_window_handle }; -} - -// 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window -Web::WebDriver::Response Session::close_window() -{ - // 1. If the current top-level browsing context is no longer open, return error with error code no such window. - TRY(check_for_open_top_level_browsing_context_or_return_error()); - - // FIXME: 2. Handle any user prompts and return its value if it is an error. - - // 3. Close the current top-level browsing context. - 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()) - TRY(stop()); - - // 5. Return the result of running the remote end steps for the Get Window Handles command. - return get_window_handles(); -} - -// 11.4 Get Window Handles, https://w3c.github.io/webdriver/#dfn-get-window-handles -Web::WebDriver::Response Session::get_window_handles() const -{ - // 1. Let handles be a JSON List. - auto handles = JsonArray {}; - - // 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 JsonValue { handles }; -} - } diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index 4385765e47..f039edbdf6 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -9,7 +9,6 @@ #pragma once #include -#include #include #include #include @@ -26,19 +25,6 @@ public: unsigned session_id() const { return m_id; } - struct Window { - String handle; - bool is_open; - }; - - struct LocalElement { - i32 id; - }; - - ErrorOr current_window(); - ErrorOr check_for_open_top_level_browsing_context_or_return_error(); - String const& current_window_handle() { return m_current_window_handle; } - WebContentConnection& web_content_connection() { VERIFY(m_web_content_connection); @@ -47,10 +33,6 @@ public: ErrorOr start(); Web::WebDriver::Response stop(); - Web::WebDriver::Response get_window_handle(); - Web::WebDriver::Response close_window(); - Web::WebDriver::Response get_window_handles() const; - Web::WebDriver::Response take_element_screenshot(StringView element_id); private: using ServerPromise = Core::Promise>; @@ -59,8 +41,6 @@ private: NonnullRefPtr m_client; bool m_started { false }; unsigned m_id { 0 }; - HashMap> m_windows; - String m_current_window_handle; RefPtr m_web_content_connection; Optional m_browser_pid; };