diff --git a/Userland/Services/WebContent/WebDriverClient.ipc b/Userland/Services/WebContent/WebDriverClient.ipc index 411845071c..28e3ac0096 100644 --- a/Userland/Services/WebContent/WebDriverClient.ipc +++ b/Userland/Services/WebContent/WebDriverClient.ipc @@ -15,10 +15,7 @@ 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) - switch_to_window(JsonValue payload) => (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 bb45dfd80f..ae62835614 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -331,9 +331,7 @@ ErrorOr> WebDriverConnection::connect(Web::Pa WebDriverConnection::WebDriverConnection(NonnullOwnPtr socket, Web::PageClient& page_client) : IPC::ConnectionToServer(*this, move(socket)) , m_page_client(page_client) - , 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 @@ -518,16 +516,6 @@ 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() { @@ -539,60 +527,9 @@ Messages::WebDriverClient::CloseWindowResponse WebDriverConnection::close_window // 3. Close the current top-level browsing context. m_page_client.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.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 {}; } -// 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 2e80d7cede..7b64828fbb 100644 --- a/Userland/Services/WebContent/WebDriverConnection.h +++ b/Userland/Services/WebContent/WebDriverConnection.h @@ -52,10 +52,7 @@ 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::SwitchToWindowResponse switch_to_window(JsonValue const& payload) 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; @@ -125,13 +122,6 @@ private: // https://w3c.github.io/webdriver/#dfn-session-script-timeout Web::WebDriver::TimeoutsConfiguration m_timeouts_configuration; - - struct Window { - DeprecatedString handle; - bool is_open { false }; - }; - HashMap m_windows; - DeprecatedString m_current_window_handle; }; } diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp index 2bd4c15bb3..c6af656ab5 100644 --- a/Userland/Services/WebDriver/Client.cpp +++ b/Userland/Services/WebDriver/Client.cpp @@ -309,7 +309,11 @@ 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->web_content_connection().get_window_handle(); + + // FIXME: 1. If the current top-level browsing context is no longer open, return error with error code no such window. + + // 2. Return success with data being the window handle associated with the current top-level browsing context. + return JsonValue { session->current_window_handle() }; } // 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window @@ -318,12 +322,7 @@ 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])); - - 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; + return session->close_window(); } // 11.3 Switch to Window, https://w3c.github.io/webdriver/#dfn-switch-to-window @@ -332,7 +331,18 @@ Web::WebDriver::Response Client::switch_to_window(Web::WebDriver::Parameters par { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window"); auto* session = TRY(find_session_with_id(parameters[0])); - return session->web_content_connection().switch_to_window(payload); + + if (!payload.is_object()) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object"); + + // 1. Let handle be the result of getting the property "handle" from the parameters argument. + auto handle = payload.as_object().get("handle"sv); + + // 2. If handle is undefined, return error with error code invalid argument. + if (!handle.has_value()) + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "No property called 'handle' present"); + + return session->switch_to_window(handle->as_string()); } // 11.4 Get Window Handles, https://w3c.github.io/webdriver/#dfn-get-window-handles @@ -341,7 +351,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->web_content_connection().get_window_handles(); + return session->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 8e56c08192..3de3394790 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -10,6 +10,7 @@ #include "Session.h" #include "Client.h" +#include #include #include #include @@ -45,7 +46,14 @@ ErrorOr> Session::create_server(NonnullRefPtrresolve({}); }; @@ -85,7 +93,7 @@ Web::WebDriver::Response Session::stop() // 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(); + web_content_connection().close_session(); // 2. Remove the current session from active sessions. // NOTE: Handled by WebDriver::Client. @@ -106,4 +114,53 @@ Web::WebDriver::Response Session::stop() return JsonValue {}; } +// 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window +Web::WebDriver::Response Session::close_window() +{ + // 3. Close the current top-level browsing context. + TRY(web_content_connection().close_window()); + 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()) + stop(); + + // 5. Return the result of running the remote end steps for the Get Window Handles command. + return get_window_handles(); +} + +// 11.3 Switch to Window, https://w3c.github.io/webdriver/#dfn-switch-to-window +Web::WebDriver::Response Session::switch_to_window(StringView handle) +{ + // 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. + if (auto it = m_windows.find(handle); it != m_windows.end()) + m_current_window_handle = it->key; + else + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchWindow, "Window not found"); + + // 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 {}; +} + +// 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. + 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(JsonValue(window_handle)); + } + + // 3. Return success with data handles. + return JsonValue { move(handles) }; +} + } diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index 8c56ebc09f..2af79c8f50 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -28,14 +29,27 @@ public: unsigned session_id() const { return m_id; } - WebContentConnection& web_content_connection() + struct Window { + String handle; + NonnullRefPtr web_content_connection; + }; + + WebContentConnection& web_content_connection() const { - VERIFY(m_web_content_connection); - return *m_web_content_connection; + auto const& current_window = m_windows.get(m_current_window_handle).value(); + return current_window.web_content_connection; + } + + String const& current_window_handle() const + { + return m_current_window_handle; } ErrorOr start(LaunchBrowserCallbacks const&); Web::WebDriver::Response stop(); + Web::WebDriver::Response close_window(); + Web::WebDriver::Response switch_to_window(StringView); + Web::WebDriver::Response get_window_handles() const; private: using ServerPromise = Core::Promise>; @@ -47,7 +61,10 @@ private: bool m_started { false }; unsigned m_id { 0 }; - RefPtr m_web_content_connection; + unsigned m_next_handle_id = 0; + HashMap m_windows; + String m_current_window_handle; + Optional m_web_content_socket_path; Optional m_browser_pid; };