From d95be7d88c37b5ac013f29e94bb255bb388ea784 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 7 Feb 2025 09:04:45 -0500 Subject: [PATCH] WebDriver: Move session management to the Session class Session management is a bit awkward right now in that the list of active sessions is managed by Client, resulting in operations like closing a session being split between several functions in Client and Session. This patch moves all session management to the Session class. Closing a session is now entirely in Session::close(). This will make managing a separate HTTP session list a bit simpler. --- Services/WebDriver/Client.cpp | 175 ++++++++++++--------------------- Services/WebDriver/Client.h | 12 +-- Services/WebDriver/Session.cpp | 72 ++++++++++---- Services/WebDriver/Session.h | 18 ++-- 4 files changed, 126 insertions(+), 151 deletions(-) diff --git a/Services/WebDriver/Client.cpp b/Services/WebDriver/Client.cpp index 83d476fccee..678ed325295 100644 --- a/Services/WebDriver/Client.cpp +++ b/Services/WebDriver/Client.cpp @@ -14,13 +14,13 @@ #include #include #include +#include #include #include +#include namespace WebDriver { -HashMap> Client::s_sessions; - ErrorOr> Client::try_create(NonnullOwnPtr socket, LaunchBrowserCallbacks callbacks, Core::EventReceiver* parent) { if (!callbacks.launch_browser || !callbacks.launch_headless_browser) @@ -38,54 +38,6 @@ Client::Client(NonnullOwnPtr socket, LaunchBrowserCallb Client::~Client() = default; -ErrorOr, Web::WebDriver::Error> Client::find_session_with_id(StringView session_id, AllowInvalidWindowHandle allow_invalid_window_handle) -{ - if (auto session = s_sessions.get(session_id); session.has_value()) { - if (allow_invalid_window_handle == AllowInvalidWindowHandle::No) - TRY(session.value()->ensure_current_window_handle_is_valid()); - - return *session.release_value(); - } - - return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSessionId, "Invalid session id"); -} - -// https://w3c.github.io/webdriver/#dfn-close-the-session -void Client::close_session(String const& session_id) -{ - // FIXME: 1. If session's HTTP flag is set, remove session from active HTTP sessions. - - // 2. Remove session from active sessions. - if (s_sessions.remove(session_id)) - dbgln_if(WEBDRIVER_DEBUG, "Shut down session {}", session_id); - else - dbgln_if(WEBDRIVER_DEBUG, "Unable to shut down session {}: Not found", session_id); - - // 3. Perform the following substeps based on the remote end's type: - // -> Remote end is an endpoint node - // 1. If the list of active sessions is empty: - if (s_sessions.is_empty()) { - // 1. Set the webdriver-active flag to false - // NOTE: This is handled by the WebContent process. - - // 2. Set the user prompt handler to null. - Web::WebDriver::set_user_prompt_handler({}); - - // FIXME: 3. Unset the accept insecure TLS flag. - // FIXME: 4. Reset the has proxy configuration flag to its default value. - - // 5. Optionally, close all top-level browsing contexts, without prompting to unload. - // NOTE: This is handled by the WebContent process. - } - // -> Remote end is an intermediary node - // 1. Close the associated session. If this causes an error to occur, complete the remainder of this algorithm - // before returning the error. - - // 4. Perform any implementation-specific cleanup steps. - - // 5. If an error has occurred in any of the steps above, return the error, otherwise return success with data null. -} - // 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) @@ -117,7 +69,6 @@ Web::WebDriver::Response Client::new_session(Web::WebDriver::Parameters, JsonVal return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::SessionNotCreated, ByteString::formatted("Failed to start session: {}", maybe_session.error())); auto session = maybe_session.release_value(); - s_sessions.set(session->session_id(), session); // 7. Let body be a JSON Object initialized with: JsonObject body; @@ -146,8 +97,8 @@ Web::WebDriver::Response Client::delete_session(Web::WebDriver::Parameters param dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session/"); // 1. If the current session is an active session, try to close the session. - if (auto session = find_session_with_id(parameters[0], AllowInvalidWindowHandle::Yes); !session.is_error()) - close_session(session.value()->session_id()); + if (auto session = Session::find_session(parameters[0], Session::AllowInvalidWindowHandle::Yes); !session.is_error()) + session.value()->close(); // 2. Return success with data null. return JsonValue {}; @@ -178,7 +129,7 @@ Web::WebDriver::Response Client::get_status(Web::WebDriver::Parameters, JsonValu Web::WebDriver::Response Client::get_timeouts(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//timeouts"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->web_content_connection().get_timeouts(); } @@ -187,7 +138,7 @@ Web::WebDriver::Response Client::get_timeouts(Web::WebDriver::Parameters paramet Web::WebDriver::Response Client::set_timeouts(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//timeouts"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->set_timeouts(move(payload)); } @@ -196,7 +147,7 @@ Web::WebDriver::Response Client::set_timeouts(Web::WebDriver::Parameters paramet Web::WebDriver::Response Client::navigate_to(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//url"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.navigate_to(move(payload)); @@ -208,7 +159,7 @@ Web::WebDriver::Response Client::navigate_to(Web::WebDriver::Parameters paramete Web::WebDriver::Response Client::get_current_url(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//url"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_current_url(); @@ -220,7 +171,7 @@ Web::WebDriver::Response Client::get_current_url(Web::WebDriver::Parameters para Web::WebDriver::Response Client::back(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//back"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.back(); @@ -232,7 +183,7 @@ Web::WebDriver::Response Client::back(Web::WebDriver::Parameters parameters, Jso Web::WebDriver::Response Client::forward(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//forward"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.forward(); @@ -244,7 +195,7 @@ Web::WebDriver::Response Client::forward(Web::WebDriver::Parameters parameters, Web::WebDriver::Response Client::refresh(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//refresh"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.refresh(); @@ -256,7 +207,7 @@ Web::WebDriver::Response Client::refresh(Web::WebDriver::Parameters parameters, Web::WebDriver::Response Client::get_title(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//title"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_title(); @@ -268,7 +219,7 @@ Web::WebDriver::Response Client::get_title(Web::WebDriver::Parameters parameters Web::WebDriver::Response Client::get_window_handle(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//window"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); // 1. If the current top-level browsing context is no longer open, return error with error code no such window. TRY(session->web_content_connection().ensure_top_level_browsing_context_is_open()); @@ -282,7 +233,7 @@ Web::WebDriver::Response Client::get_window_handle(Web::WebDriver::Parameters pa Web::WebDriver::Response Client::close_window(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session//window"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->close_window(); } @@ -291,7 +242,7 @@ Web::WebDriver::Response Client::close_window(Web::WebDriver::Parameters paramet Web::WebDriver::Response Client::switch_to_window(Web::WebDriver::Parameters parameters, AK::JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window"); - auto session = TRY(find_session_with_id(parameters[0], AllowInvalidWindowHandle::Yes)); + auto session = TRY(Session::find_session(parameters[0], Session::AllowInvalidWindowHandle::Yes)); if (!payload.is_object()) return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object"); @@ -311,7 +262,7 @@ Web::WebDriver::Response Client::switch_to_window(Web::WebDriver::Parameters par Web::WebDriver::Response Client::get_window_handles(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//window/handles"); - auto session = TRY(find_session_with_id(parameters[0], AllowInvalidWindowHandle::Yes)); + auto session = TRY(Session::find_session(parameters[0], Session::AllowInvalidWindowHandle::Yes)); return session->get_window_handles(); } @@ -320,7 +271,7 @@ Web::WebDriver::Response Client::get_window_handles(Web::WebDriver::Parameters p Web::WebDriver::Response Client::new_window(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window/new"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); auto handle = TRY(session->perform_async_action([&](auto& connection) { return connection.new_window(move(payload)); @@ -346,7 +297,7 @@ Web::WebDriver::Response Client::new_window(Web::WebDriver::Parameters parameter Web::WebDriver::Response Client::switch_to_frame(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//frame"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.switch_to_frame(move(payload)); @@ -358,7 +309,7 @@ Web::WebDriver::Response Client::switch_to_frame(Web::WebDriver::Parameters para Web::WebDriver::Response Client::switch_to_parent_frame(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//frame/parent"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.switch_to_parent_frame(move(payload)); @@ -370,7 +321,7 @@ Web::WebDriver::Response Client::switch_to_parent_frame(Web::WebDriver::Paramete Web::WebDriver::Response Client::get_window_rect(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//window/rect"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_window_rect(); @@ -382,7 +333,7 @@ Web::WebDriver::Response Client::get_window_rect(Web::WebDriver::Parameters para Web::WebDriver::Response Client::set_window_rect(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window/rect"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.set_window_rect(move(payload)); @@ -394,7 +345,7 @@ Web::WebDriver::Response Client::set_window_rect(Web::WebDriver::Parameters para Web::WebDriver::Response Client::maximize_window(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window/maximize"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.maximize_window(); @@ -406,7 +357,7 @@ Web::WebDriver::Response Client::maximize_window(Web::WebDriver::Parameters para Web::WebDriver::Response Client::minimize_window(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window/minimize"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.minimize_window(); @@ -418,7 +369,7 @@ Web::WebDriver::Response Client::minimize_window(Web::WebDriver::Parameters para Web::WebDriver::Response Client::fullscreen_window(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window/fullscreen"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.fullscreen_window(); @@ -430,7 +381,7 @@ Web::WebDriver::Response Client::fullscreen_window(Web::WebDriver::Parameters pa Web::WebDriver::Response Client::consume_user_activation(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//window/consume-user-activation"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->web_content_connection().consume_user_activation(); } @@ -439,7 +390,7 @@ Web::WebDriver::Response Client::consume_user_activation(Web::WebDriver::Paramet Web::WebDriver::Response Client::find_element(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//element"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.find_element(move(payload)); @@ -451,7 +402,7 @@ Web::WebDriver::Response Client::find_element(Web::WebDriver::Parameters paramet Web::WebDriver::Response Client::find_elements(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//elements"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.find_elements(move(payload)); @@ -463,7 +414,7 @@ Web::WebDriver::Response Client::find_elements(Web::WebDriver::Parameters parame Web::WebDriver::Response Client::find_element_from_element(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//element//element"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.find_element_from_element(move(payload), move(parameters[1])); @@ -475,7 +426,7 @@ Web::WebDriver::Response Client::find_element_from_element(Web::WebDriver::Param Web::WebDriver::Response Client::find_elements_from_element(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//element//elements"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.find_elements_from_element(move(payload), move(parameters[1])); @@ -487,7 +438,7 @@ Web::WebDriver::Response Client::find_elements_from_element(Web::WebDriver::Para Web::WebDriver::Response Client::find_element_from_shadow_root(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//shadow//element"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.find_element_from_shadow_root(move(payload), move(parameters[1])); @@ -499,7 +450,7 @@ Web::WebDriver::Response Client::find_element_from_shadow_root(Web::WebDriver::P Web::WebDriver::Response Client::find_elements_from_shadow_root(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//shadow//elements"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.find_elements_from_shadow_root(move(payload), move(parameters[1])); @@ -511,7 +462,7 @@ Web::WebDriver::Response Client::find_elements_from_shadow_root(Web::WebDriver:: Web::WebDriver::Response Client::get_active_element(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element/active"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_active_element(); @@ -523,7 +474,7 @@ Web::WebDriver::Response Client::get_active_element(Web::WebDriver::Parameters p Web::WebDriver::Response Client::get_element_shadow_root(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//shadow"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_shadow_root(move(parameters[1])); @@ -535,7 +486,7 @@ Web::WebDriver::Response Client::get_element_shadow_root(Web::WebDriver::Paramet Web::WebDriver::Response Client::is_element_selected(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//selected"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.is_element_selected(move(parameters[1])); @@ -547,7 +498,7 @@ Web::WebDriver::Response Client::is_element_selected(Web::WebDriver::Parameters Web::WebDriver::Response Client::get_element_attribute(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//attribute/"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_attribute(move(parameters[1]), move(parameters[2])); @@ -559,7 +510,7 @@ Web::WebDriver::Response Client::get_element_attribute(Web::WebDriver::Parameter Web::WebDriver::Response Client::get_element_property(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//property/"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_property(move(parameters[1]), move(parameters[2])); @@ -571,7 +522,7 @@ Web::WebDriver::Response Client::get_element_property(Web::WebDriver::Parameters Web::WebDriver::Response Client::get_element_css_value(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//css/"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_css_value(move(parameters[1]), move(parameters[2])); @@ -583,7 +534,7 @@ Web::WebDriver::Response Client::get_element_css_value(Web::WebDriver::Parameter Web::WebDriver::Response Client::get_element_text(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//text"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_text(move(parameters[1])); @@ -595,7 +546,7 @@ Web::WebDriver::Response Client::get_element_text(Web::WebDriver::Parameters par Web::WebDriver::Response Client::get_element_tag_name(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//name"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_tag_name(move(parameters[1])); @@ -607,7 +558,7 @@ Web::WebDriver::Response Client::get_element_tag_name(Web::WebDriver::Parameters Web::WebDriver::Response Client::get_element_rect(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//rect"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_element_rect(move(parameters[1])); @@ -619,7 +570,7 @@ Web::WebDriver::Response Client::get_element_rect(Web::WebDriver::Parameters par Web::WebDriver::Response Client::is_element_enabled(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//enabled"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.is_element_enabled(move(parameters[1])); @@ -631,7 +582,7 @@ Web::WebDriver::Response Client::is_element_enabled(Web::WebDriver::Parameters p Web::WebDriver::Response Client::get_computed_role(Web::WebDriver::Parameters parameters, AK::JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//computedrole"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_computed_role(move(parameters[1])); @@ -643,7 +594,7 @@ Web::WebDriver::Response Client::get_computed_role(Web::WebDriver::Parameters pa Web::WebDriver::Response Client::get_computed_label(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//computedlabel"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_computed_label(move(parameters[1])); @@ -655,7 +606,7 @@ Web::WebDriver::Response Client::get_computed_label(Web::WebDriver::Parameters p Web::WebDriver::Response Client::element_click(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//element//click"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.element_click(move(parameters[1])); @@ -667,7 +618,7 @@ Web::WebDriver::Response Client::element_click(Web::WebDriver::Parameters parame Web::WebDriver::Response Client::element_clear(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//element//clear"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.element_clear(move(parameters[1])); @@ -679,7 +630,7 @@ Web::WebDriver::Response Client::element_clear(Web::WebDriver::Parameters parame Web::WebDriver::Response Client::element_send_keys(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//element//value"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.element_send_keys(move(parameters[1]), move(payload)); @@ -691,7 +642,7 @@ Web::WebDriver::Response Client::element_send_keys(Web::WebDriver::Parameters pa Web::WebDriver::Response Client::get_source(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//source"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_source(); @@ -703,7 +654,7 @@ Web::WebDriver::Response Client::get_source(Web::WebDriver::Parameters parameter Web::WebDriver::Response Client::execute_script(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//execute/sync"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.execute_script(move(payload)); @@ -715,7 +666,7 @@ Web::WebDriver::Response Client::execute_script(Web::WebDriver::Parameters param Web::WebDriver::Response Client::execute_async_script(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//execute/async"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.execute_async_script(move(payload)); @@ -727,7 +678,7 @@ Web::WebDriver::Response Client::execute_async_script(Web::WebDriver::Parameters Web::WebDriver::Response Client::get_all_cookies(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//cookie"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_all_cookies(); @@ -739,7 +690,7 @@ Web::WebDriver::Response Client::get_all_cookies(Web::WebDriver::Parameters para Web::WebDriver::Response Client::get_named_cookie(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//cookie/"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.get_named_cookie(move(parameters[1])); @@ -751,7 +702,7 @@ Web::WebDriver::Response Client::get_named_cookie(Web::WebDriver::Parameters par Web::WebDriver::Response Client::add_cookie(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//cookie"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.add_cookie(move(payload)); @@ -763,7 +714,7 @@ Web::WebDriver::Response Client::add_cookie(Web::WebDriver::Parameters parameter Web::WebDriver::Response Client::delete_cookie(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session//cookie/"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.delete_cookie(move(parameters[1])); @@ -775,7 +726,7 @@ Web::WebDriver::Response Client::delete_cookie(Web::WebDriver::Parameters parame Web::WebDriver::Response Client::delete_all_cookies(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session//cookie"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.delete_all_cookies(); @@ -787,7 +738,7 @@ Web::WebDriver::Response Client::delete_all_cookies(Web::WebDriver::Parameters p Web::WebDriver::Response Client::perform_actions(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//actions"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.perform_actions(move(payload)); @@ -799,7 +750,7 @@ Web::WebDriver::Response Client::perform_actions(Web::WebDriver::Parameters para Web::WebDriver::Response Client::release_actions(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling DELETE /session//actions"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.release_actions(); @@ -811,7 +762,7 @@ Web::WebDriver::Response Client::release_actions(Web::WebDriver::Parameters para Web::WebDriver::Response Client::dismiss_alert(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//alert/dismiss"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.dismiss_alert(); @@ -823,7 +774,7 @@ Web::WebDriver::Response Client::dismiss_alert(Web::WebDriver::Parameters parame Web::WebDriver::Response Client::accept_alert(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//alert/accept"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.accept_alert(); @@ -835,7 +786,7 @@ Web::WebDriver::Response Client::accept_alert(Web::WebDriver::Parameters paramet Web::WebDriver::Response Client::get_alert_text(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//alert/text"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->web_content_connection().get_alert_text(); } @@ -844,7 +795,7 @@ Web::WebDriver::Response Client::get_alert_text(Web::WebDriver::Parameters param Web::WebDriver::Response Client::send_alert_text(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//alert/text"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->web_content_connection().send_alert_text(payload); } @@ -853,7 +804,7 @@ Web::WebDriver::Response Client::send_alert_text(Web::WebDriver::Parameters para Web::WebDriver::Response Client::take_screenshot(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//screenshot"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.take_screenshot(); @@ -865,7 +816,7 @@ Web::WebDriver::Response Client::take_screenshot(Web::WebDriver::Parameters para Web::WebDriver::Response Client::take_element_screenshot(Web::WebDriver::Parameters parameters, JsonValue) { dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//element//screenshot"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->perform_async_action([&](auto& connection) { return connection.take_element_screenshot(move(parameters[1])); @@ -877,7 +828,7 @@ Web::WebDriver::Response Client::take_element_screenshot(Web::WebDriver::Paramet Web::WebDriver::Response Client::print_page(Web::WebDriver::Parameters parameters, JsonValue payload) { dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session//print"); - auto session = TRY(find_session_with_id(parameters[0])); + auto session = TRY(Session::find_session(parameters[0])); return session->web_content_connection().print_page(move(payload)); } diff --git a/Services/WebDriver/Client.h b/Services/WebDriver/Client.h index bb86cf9e9bd..3faa63e9842 100644 --- a/Services/WebDriver/Client.h +++ b/Services/WebDriver/Client.h @@ -11,10 +11,9 @@ #include #include #include +#include #include -#include #include -#include namespace WebDriver { @@ -31,17 +30,10 @@ public: virtual ~Client() override; LaunchBrowserCallbacks const& launch_browser_callbacks() const { return m_callbacks; } - void close_session(String const& session_id); private: Client(NonnullOwnPtr, LaunchBrowserCallbacks, Core::EventReceiver* parent); - enum class AllowInvalidWindowHandle { - No, - Yes, - }; - ErrorOr, Web::WebDriver::Error> find_session_with_id(StringView session_id, AllowInvalidWindowHandle = AllowInvalidWindowHandle::No); - virtual Web::WebDriver::Response new_session(Web::WebDriver::Parameters parameters, JsonValue payload) override; virtual Web::WebDriver::Response delete_session(Web::WebDriver::Parameters parameters, JsonValue payload) override; virtual Web::WebDriver::Response get_status(Web::WebDriver::Parameters parameters, JsonValue payload) override; @@ -105,8 +97,6 @@ private: virtual Web::WebDriver::Response take_element_screenshot(Web::WebDriver::Parameters parameters, JsonValue payload) override; virtual Web::WebDriver::Response print_page(Web::WebDriver::Parameters parameters, JsonValue payload) override; - static HashMap> s_sessions; - LaunchBrowserCallbacks m_callbacks; }; diff --git a/Services/WebDriver/Session.cpp b/Services/WebDriver/Session.cpp index 7c154f4d7cd..9aec7ac512d 100644 --- a/Services/WebDriver/Session.cpp +++ b/Services/WebDriver/Session.cpp @@ -10,6 +10,7 @@ #include "Session.h" #include "Client.h" +#include #include #include #include @@ -21,6 +22,8 @@ namespace WebDriver { +static HashMap> s_sessions; + // https://w3c.github.io/webdriver/#dfn-create-a-session ErrorOr> Session::create(NonnullRefPtr client, JsonObject& capabilities, ReadonlySpan flags) { @@ -93,8 +96,9 @@ ErrorOr> Session::create(NonnullRefPtr client, Js // FIXME: 11. Run any WebDriver new session algorithm defined in external specifications, with arguments session, capabilities, and flags. // 12. Append session to active sessions. - // 13. If flags contains "http", append session to active HTTP sessions. - // NOTE: These steps are handled by WebDriver::Client. + s_sessions.set(session->session_id(), session); + + // FIXME: 13. If flags contains "http", append session to active HTTP sessions. // 14. Set the webdriver-active flag to true. session->web_content_connection().async_set_is_webdriver_active(true); @@ -105,28 +109,55 @@ ErrorOr> Session::create(NonnullRefPtr client, Js Session::Session(NonnullRefPtr client, JsonObject const& capabilities, String session_id, bool http) : m_client(move(client)) , m_options(capabilities) - , m_id(move(session_id)) + , m_session_id(move(session_id)) , m_http(http) { } -// https://w3c.github.io/webdriver/#dfn-close-the-session -Session::~Session() -{ - if (!m_started) - return; +Session::~Session() = default; - // 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. - for (auto& it : m_windows) { - it.value.web_content_connection->close_session(); +ErrorOr, Web::WebDriver::Error> Session::find_session(StringView session_id, AllowInvalidWindowHandle allow_invalid_window_handle) +{ + if (auto session = s_sessions.get(session_id); session.has_value()) { + if (allow_invalid_window_handle == AllowInvalidWindowHandle::No) + TRY(session.value()->ensure_current_window_handle_is_valid()); + + return *session.release_value(); } - // 2. Remove the current session from active sessions. - // NOTE: We are in a session destruction which means it is already removed - // from active sessions + return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidSessionId, "Invalid session id"); +} - // 3. Perform any implementation-specific cleanup steps. +// https://w3c.github.io/webdriver/#dfn-close-the-session +void Session::close() +{ + // FIXME: 1. If session's HTTP flag is set, remove session from active HTTP sessions. + + // 2. Remove session from active sessions. + s_sessions.remove(m_session_id); + + // 3. Perform the following substeps based on the remote end's type: + // -> Remote end is an endpoint node + // 1. If the list of active sessions is empty: + if (s_sessions.is_empty()) { + // 1. Set the webdriver-active flag to false + // NOTE: This is handled by the WebContent process. + + // 2. Set the user prompt handler to null. + Web::WebDriver::set_user_prompt_handler({}); + + // FIXME: 3. Unset the accept insecure TLS flag. + // FIXME: 4. Reset the has proxy configuration flag to its default value. + + // 5. Optionally, close all top-level browsing contexts, without prompting to unload. + for (auto& it : m_windows) + it.value.web_content_connection->close_session(); + } + // -> Remote end is an intermediary node + // 1. Close the associated session. If this causes an error to occur, complete the remainder of this algorithm + // before returning the error. + + // 4. Perform any implementation-specific cleanup steps. if (m_browser_process.has_value()) MUST(Core::System::kill(m_browser_process->pid(), SIGTERM)); @@ -134,6 +165,8 @@ Session::~Session() MUST(Core::System::unlink(*m_web_content_socket_path)); m_web_content_socket_path = {}; } + + // 5. If an error has occurred in any of the steps above, return the error, otherwise return success with data null. } ErrorOr> Session::create_server(NonnullRefPtr promise) @@ -169,7 +202,7 @@ ErrorOr> Session::create_server(NonnullRefPtrclose_session(session_id()); + close(); }; web_content_connection->async_set_page_load_strategy(m_page_load_strategy); @@ -197,7 +230,7 @@ ErrorOr Session::start(LaunchBrowserCallbacks const& callbacks) { auto promise = TRY(ServerPromise::try_create()); - m_web_content_socket_path = ByteString::formatted("{}/webdriver/session_{}_{}", TRY(Core::StandardPaths::runtime_directory()), getpid(), m_id); + m_web_content_socket_path = ByteString::formatted("{}/webdriver/session_{}_{}", TRY(Core::StandardPaths::runtime_directory()), getpid(), m_session_id); m_web_content_server = TRY(create_server(promise)); if (m_options.headless) @@ -209,7 +242,6 @@ ErrorOr Session::start(LaunchBrowserCallbacks const& callbacks) // errors received while accepting the Browser and WebContent sockets. TRY(TRY(promise->await())); - m_started = true; return {}; } @@ -233,7 +265,7 @@ Web::WebDriver::Response Session::close_window() // 4. If there are no more open top-level browsing contexts, then close the session. if (m_windows.size() == 1) - m_client->close_session(session_id()); + close(); } // 5. Return the result of running the remote end steps for the Get Window Handles command. diff --git a/Services/WebDriver/Session.h b/Services/WebDriver/Session.h index 73aefc0d72b..c18aa25104b 100644 --- a/Services/WebDriver/Session.h +++ b/Services/WebDriver/Session.h @@ -32,7 +32,11 @@ public: static ErrorOr> create(NonnullRefPtr client, JsonObject& capabilities, ReadonlySpan flags); ~Session(); - String session_id() const { return m_id; } + enum class AllowInvalidWindowHandle { + No, + Yes, + }; + static ErrorOr, Web::WebDriver::Error> find_session(StringView session_id, AllowInvalidWindowHandle = AllowInvalidWindowHandle::No); struct Window { String handle; @@ -47,10 +51,10 @@ public: return current_window->web_content_connection; } - String const& current_window_handle() const - { - return m_current_window_handle; - } + void close(); + + String session_id() const { return m_session_id; } + String const& current_window_handle() const { return m_current_window_handle; } bool has_window_handle(StringView handle) const { return m_windows.contains(handle); } @@ -89,9 +93,7 @@ private: NonnullRefPtr m_client; Web::WebDriver::LadybirdOptions m_options; - bool m_started { false }; - - String m_id; + String m_session_id; bool m_http { false }; HashMap m_windows;