From d66b54fec5e64d9bc796b503540c5e7df2fc6006 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 4 Nov 2024 08:37:16 -0500 Subject: [PATCH] WebContent+WebDriver: Transfer WebDriver capability init to Session Capabilities are configured on a per-session basis, but we were only applying these options to the first WebContent process created. We will need to pass these options to new windows created from that process. This patch re-organizes capability processing so that our session can remember them for new windows. --- .../WebContent/WebDriverConnection.cpp | 3 +- Userland/Services/WebDriver/Client.cpp | 54 +--------------- Userland/Services/WebDriver/Session.cpp | 62 ++++++++++++++++++- Userland/Services/WebDriver/Session.h | 11 ++++ 4 files changed, 77 insertions(+), 53 deletions(-) diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index 43c23592c94..6f1672e10c7 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -279,7 +279,8 @@ Messages::WebDriverClient::SetTimeoutsResponse WebDriverConnection::set_timeouts // 2. Make the session timeouts the new timeouts. // 3. Return success with data null. - return JsonValue {}; + // NOTE: We return the current timeouts configuration so the client may store them for new sessions. + return Web::WebDriver::timeouts_object(m_timeouts_configuration); } // 10.1 Navigate To, https://w3c.github.io/webdriver/#navigate-to diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp index 37752b77262..5db5bc0f32f 100644 --- a/Userland/Services/WebDriver/Client.cpp +++ b/Userland/Services/WebDriver/Client.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include namespace WebDriver { @@ -63,53 +62,6 @@ void Client::close_session(unsigned session_id) dbgln_if(WEBDRIVER_DEBUG, "Unable to shut down session {}: Not found", session_id); } -// Step 12 of https://w3c.github.io/webdriver/#dfn-new-sessions -static void initialize_session_from_capabilities(WebContentConnection& web_content_connection, JsonObject& capabilities) -{ - // 1. Let strategy be the result of getting property "pageLoadStrategy" from capabilities. - auto strategy = capabilities.get_byte_string("pageLoadStrategy"sv); - - // 2. If strategy is a string, set the current session’s page loading strategy to strategy. Otherwise, set the page loading strategy to normal and set a property of capabilities with name "pageLoadStrategy" and value "normal". - if (strategy.has_value()) - web_content_connection.async_set_page_load_strategy(Web::WebDriver::page_load_strategy_from_string(*strategy)); - else - capabilities.set("pageLoadStrategy"sv, "normal"sv); - - // 3. Let strictFileInteractability be the result of getting property "strictFileInteractability" from capabilities. - auto strict_file_interactiblity = capabilities.get_bool("strictFileInteractability"sv); - - // 4. If strictFileInteractability is a boolean, set the current session’s strict file interactability to strictFileInteractability. Otherwise set the current session’s strict file interactability to false. - if (strict_file_interactiblity.has_value()) - web_content_connection.async_set_strict_file_interactability(*strict_file_interactiblity); - else - capabilities.set("strictFileInteractability"sv, false); - - // FIXME: 5. Let proxy be the result of getting property "proxy" from capabilities and run the substeps of the first matching statement: - // FIXME: proxy is a proxy configuration object - // FIXME: Take implementation-defined steps to set the user agent proxy using the extracted proxy configuration. If the defined proxy cannot be configured return error with error code session not created. - // FIXME: Otherwise - // FIXME: Set a property of capabilities with name "proxy" and a value that is a new JSON Object. - - // 6. If capabilities has a property with the key "timeouts": - if (auto timeouts = capabilities.get_object("timeouts"sv); timeouts.has_value()) { - // a. Let timeouts be the result of trying to JSON deserialize as a timeouts configuration the value of the "timeouts" property. - // NOTE: This happens on the remote end. - - // b. Make the session timeouts the new timeouts. - MUST(web_content_connection.set_timeouts(*timeouts)); - } else { - // 7. Set a property on capabilities with name "timeouts" and value that of the JSON deserialization of the session timeouts. - capabilities.set("timeouts"sv, Web::WebDriver::timeouts_object({})); - } - - // 8. Apply changes to the user agent for any implementation-defined capabilities selected during the capabilities processing step. - auto behavior = capabilities.get_byte_string("unhandledPromptBehavior"sv); - if (behavior.has_value()) - web_content_connection.async_set_unhandled_prompt_behavior(Web::WebDriver::unhandled_prompt_behavior_from_string(*behavior)); - else - capabilities.set("unhandledPromptBehavior"sv, "dismiss and notify"sv); -} - // 8.1 New Session, https://w3c.github.io/webdriver/#dfn-new-sessions // POST /session Web::WebDriver::Response Client::new_session(Web::WebDriver::Parameters, JsonValue payload) @@ -155,12 +107,12 @@ Web::WebDriver::Response Client::new_session(Web::WebDriver::Parameters, JsonVal // with arguments session and capabilities. // 10. Append session to active sessions. - s_sessions.set(session_id, move(session)); + s_sessions.set(session_id, session); // NOTE: We do step 12 before 11 because step 12 mutates the capabilities we set in step 11. // 12. Initialize the following from capabilities: - initialize_session_from_capabilities(web_content_connection, capabilities.as_object()); + session->initialize_from_capabilities(capabilities.as_object()); // 11. Let body be a JSON Object initialized with: JsonObject body; @@ -232,7 +184,7 @@ Web::WebDriver::Response Client::set_timeouts(Web::WebDriver::Parameters paramet { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//timeouts"); auto session = TRY(find_session_with_id(parameters[0])); - return session->web_content_connection().set_timeouts(payload); + return session->set_timeouts(move(payload)); } // 10.1 Navigate To, https://w3c.github.io/webdriver/#dfn-navigate-to diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index 615472bc24a..5fb77e41dca 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -11,10 +11,10 @@ #include "Session.h" #include "Client.h" #include -#include #include #include #include +#include #include namespace WebDriver { @@ -53,6 +53,60 @@ Session::~Session() } } +// Step 12 of https://w3c.github.io/webdriver/#dfn-new-sessions +void Session::initialize_from_capabilities(JsonObject& capabilities) +{ + auto& connection = web_content_connection(); + + // 1. Let strategy be the result of getting property "pageLoadStrategy" from capabilities. + auto strategy = capabilities.get_byte_string("pageLoadStrategy"sv); + + // 2. If strategy is a string, set the current session’s page loading strategy to strategy. Otherwise, set the page loading strategy to normal and set a property of capabilities with name "pageLoadStrategy" and value "normal". + if (strategy.has_value()) { + m_page_load_strategy = Web::WebDriver::page_load_strategy_from_string(*strategy); + connection.async_set_page_load_strategy(m_page_load_strategy); + } else { + capabilities.set("pageLoadStrategy"sv, "normal"sv); + } + + // 3. Let strictFileInteractability be the result of getting property "strictFileInteractability" from capabilities. + auto strict_file_interactiblity = capabilities.get_bool("strictFileInteractability"sv); + + // 4. If strictFileInteractability is a boolean, set the current session’s strict file interactability to strictFileInteractability. Otherwise set the current session’s strict file interactability to false. + if (strict_file_interactiblity.has_value()) { + m_strict_file_interactiblity = *strict_file_interactiblity; + connection.async_set_strict_file_interactability(m_strict_file_interactiblity); + } else { + capabilities.set("strictFileInteractability"sv, false); + } + + // FIXME: 5. Let proxy be the result of getting property "proxy" from capabilities and run the substeps of the first matching statement: + // FIXME: proxy is a proxy configuration object + // FIXME: Take implementation-defined steps to set the user agent proxy using the extracted proxy configuration. If the defined proxy cannot be configured return error with error code session not created. + // FIXME: Otherwise + // FIXME: Set a property of capabilities with name "proxy" and a value that is a new JSON Object. + + // 6. If capabilities has a property with the key "timeouts": + if (auto timeouts = capabilities.get_object("timeouts"sv); timeouts.has_value()) { + // a. Let timeouts be the result of trying to JSON deserialize as a timeouts configuration the value of the "timeouts" property. + // NOTE: This happens on the remote end. + + // b. Make the session timeouts the new timeouts. + MUST(set_timeouts(*timeouts)); + } else { + // 7. Set a property on capabilities with name "timeouts" and value that of the JSON deserialization of the session timeouts. + capabilities.set("timeouts"sv, Web::WebDriver::timeouts_object({})); + } + + // 8. Apply changes to the user agent for any implementation-defined capabilities selected during the capabilities processing step. + if (auto behavior = capabilities.get_byte_string("unhandledPromptBehavior"sv); behavior.has_value()) { + m_unhandled_prompt_behavior = Web::WebDriver::unhandled_prompt_behavior_from_string(*behavior); + connection.async_set_unhandled_prompt_behavior(m_unhandled_prompt_behavior); + } else { + capabilities.set("unhandledPromptBehavior"sv, "dismiss and notify"sv); + } +} + ErrorOr> Session::create_server(NonnullRefPtr promise) { static_assert(IsSame, "Need to handle other IPC transports here"); @@ -123,6 +177,12 @@ ErrorOr Session::start(LaunchBrowserCallbacks const& callbacks) return {}; } +Web::WebDriver::Response Session::set_timeouts(JsonValue payload) +{ + m_timeouts_configuration = TRY(web_content_connection().set_timeouts(move(payload))); + return JsonValue {}; +} + // 11.2 Close Window, https://w3c.github.io/webdriver/#dfn-close-window Web::WebDriver::Response Session::close_window() { diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index c0b00d80f64..6c8bd05e212 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -9,12 +9,14 @@ #pragma once #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -29,6 +31,8 @@ public: Session(unsigned session_id, NonnullRefPtr client, Web::WebDriver::LadybirdOptions options); ~Session(); + void initialize_from_capabilities(JsonObject&); + unsigned session_id() const { return m_id; } struct Window { @@ -52,6 +56,8 @@ public: bool has_window_handle(StringView handle) const { return m_windows.contains(handle); } ErrorOr start(LaunchBrowserCallbacks const&); + + Web::WebDriver::Response set_timeouts(JsonValue); Web::WebDriver::Response close_window(); Web::WebDriver::Response switch_to_window(StringView); Web::WebDriver::Response get_window_handles() const; @@ -92,6 +98,11 @@ private: Optional m_browser_pid; RefPtr m_web_content_server; + + Web::WebDriver::PageLoadStrategy m_page_load_strategy { Web::WebDriver::PageLoadStrategy::Normal }; + Web::WebDriver::UnhandledPromptBehavior m_unhandled_prompt_behavior { Web::WebDriver::UnhandledPromptBehavior::DismissAndNotify }; + Optional m_timeouts_configuration; + bool m_strict_file_interactiblity { false }; }; }