From 61d0b66bfb9dc22b65a802b31aa69f40c5ffa3d7 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Wed, 2 Nov 2022 20:26:06 -0400 Subject: [PATCH] WebDriver+Browser: Implement `GET /session/{id}/source` --- Userland/Applications/Browser/BrowserWindow.cpp | 4 ++++ .../Browser/WebDriverConnection.cpp | 12 ++++++++++++ .../Applications/Browser/WebDriverConnection.h | 1 + .../Applications/Browser/WebDriverEndpoints.h | 1 + .../Browser/WebDriverSessionClient.ipc | 1 + Userland/Services/WebDriver/Client.cpp | 11 +++++++++++ Userland/Services/WebDriver/Client.h | 1 + Userland/Services/WebDriver/Session.cpp | 17 +++++++++++++++++ Userland/Services/WebDriver/Session.h | 1 + 9 files changed, 49 insertions(+) diff --git a/Userland/Applications/Browser/BrowserWindow.cpp b/Userland/Applications/Browser/BrowserWindow.cpp index 7f43d69b38..8f2ec40d0c 100644 --- a/Userland/Applications/Browser/BrowserWindow.cpp +++ b/Userland/Applications/Browser/BrowserWindow.cpp @@ -631,6 +631,10 @@ void BrowserWindow::create_new_tab(URL url, bool activate) return active_tab().view().get_element_tag_name(element_id); }; + new_tab.webdriver_endpoints().on_serialize_source = [this]() { + return active_tab().view().serialize_source(); + }; + new_tab.webdriver_endpoints().on_execute_script = [this](String const& body, Vector const& json_arguments, Optional const& timeout, bool async) { return active_tab().view().webdriver_execute_script(body, json_arguments, timeout, async); }; diff --git a/Userland/Applications/Browser/WebDriverConnection.cpp b/Userland/Applications/Browser/WebDriverConnection.cpp index a4dbec53aa..aa4f6ba990 100644 --- a/Userland/Applications/Browser/WebDriverConnection.cpp +++ b/Userland/Applications/Browser/WebDriverConnection.cpp @@ -117,6 +117,18 @@ void WebDriverConnection::minimize_window() browser_window->set_minimized(true); } +Messages::WebDriverSessionClient::SerializeSourceResponse WebDriverConnection::serialize_source() +{ + dbgln_if(WEBDRIVER_DEBUG, "WebDriverConnection: serialize_source"); + if (auto browser_window = m_browser_window.strong_ref()) { + auto& tab = browser_window->active_tab(); + if (tab.webdriver_endpoints().on_serialize_source) + return { tab.webdriver_endpoints().on_serialize_source() }; + } + + return { {} }; +} + Messages::WebDriverSessionClient::ExecuteScriptResponse WebDriverConnection::execute_script(String const& body, Vector const& json_arguments, Optional const& timeout, bool async) { dbgln("WebDriverConnection: execute_script"); diff --git a/Userland/Applications/Browser/WebDriverConnection.h b/Userland/Applications/Browser/WebDriverConnection.h index 6a8ddde2b7..6e1af64154 100644 --- a/Userland/Applications/Browser/WebDriverConnection.h +++ b/Userland/Applications/Browser/WebDriverConnection.h @@ -49,6 +49,7 @@ public: virtual void set_window_position(Gfx::IntPoint const&) override; virtual void maximize_window() override; virtual void minimize_window() override; + virtual Messages::WebDriverSessionClient::SerializeSourceResponse serialize_source() override; virtual Messages::WebDriverSessionClient::ExecuteScriptResponse execute_script(String const& body, Vector const& json_arguments, Optional const& timeout, bool async) override; virtual Messages::WebDriverSessionClient::GetAllCookiesResponse get_all_cookies() override; virtual Messages::WebDriverSessionClient::GetNamedCookieResponse get_named_cookie(String const& name) override; diff --git a/Userland/Applications/Browser/WebDriverEndpoints.h b/Userland/Applications/Browser/WebDriverEndpoints.h index d10138e10d..81f6c4f990 100644 --- a/Userland/Applications/Browser/WebDriverEndpoints.h +++ b/Userland/Applications/Browser/WebDriverEndpoints.h @@ -29,6 +29,7 @@ public: Function on_get_computed_value_for_element; Function on_get_element_text; Function on_get_element_tag_name; + Function on_serialize_source; Function const& json_arguments, Optional const& timeout, bool async)> on_execute_script; }; diff --git a/Userland/Applications/Browser/WebDriverSessionClient.ipc b/Userland/Applications/Browser/WebDriverSessionClient.ipc index 2a4885255d..53e67e269d 100644 --- a/Userland/Applications/Browser/WebDriverSessionClient.ipc +++ b/Userland/Applications/Browser/WebDriverSessionClient.ipc @@ -23,6 +23,7 @@ endpoint WebDriverSessionClient { set_window_position(Gfx::IntPoint position) =| maximize_window() =| minimize_window() =| + serialize_source() => (String source) execute_script(String body, Vector json_arguments, Optional timeout, bool async) => (Web::WebDriver::ExecuteScriptResultType result_type, String json_result) get_all_cookies() => (Vector cookies) get_named_cookie(String name) => (Optional cookie) diff --git a/Userland/Services/WebDriver/Client.cpp b/Userland/Services/WebDriver/Client.cpp index 0e1a4e81d6..a681d201d9 100644 --- a/Userland/Services/WebDriver/Client.cpp +++ b/Userland/Services/WebDriver/Client.cpp @@ -49,6 +49,7 @@ Vector Client::s_routes = { { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "css", ":property_name" }, &Client::handle_get_element_css_value }, { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "text" }, &Client::handle_get_element_text }, { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "element", ":element_id", "name" }, &Client::handle_get_element_tag_name }, + { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "source" }, &Client::handle_get_source }, { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "execute", "sync" }, &Client::handle_execute_script }, { HTTP::HttpRequest::Method::POST, { "session", ":session_id", "execute", "async" }, &Client::handle_execute_async_script }, { HTTP::HttpRequest::Method::GET, { "session", ":session_id", "cookie" }, &Client::handle_get_all_cookies }, @@ -687,6 +688,16 @@ ErrorOr Client::handle_get_element_tag_name(Vector Client::handle_get_source(Vector const& parameters, JsonValue const&) +{ + dbgln_if(WEBDRIVER_DEBUG, "Handling GET /session//source"); + auto* session = TRY(find_session_with_id(parameters[0])); + auto result = TRY(session->get_source()); + return make_json_value(result); +} + // 13.2.1 Execute Script, https://w3c.github.io/webdriver/#dfn-execute-script // POST /session/{session id}/execute/sync ErrorOr Client::handle_execute_script(Vector const& parameters, JsonValue const& payload) diff --git a/Userland/Services/WebDriver/Client.h b/Userland/Services/WebDriver/Client.h index 1550b2dfdd..ffe42120df 100644 --- a/Userland/Services/WebDriver/Client.h +++ b/Userland/Services/WebDriver/Client.h @@ -74,6 +74,7 @@ private: ErrorOr handle_get_element_css_value(Vector const&, JsonValue const& payload); ErrorOr handle_get_element_text(Vector const&, JsonValue const& payload); ErrorOr handle_get_element_tag_name(Vector const&, JsonValue const& payload); + ErrorOr handle_get_source(Vector const&, JsonValue const& payload); ErrorOr handle_execute_script(Vector const&, JsonValue const& payload); ErrorOr handle_execute_async_script(Vector const&, JsonValue const& payload); ErrorOr handle_get_all_cookies(Vector const&, JsonValue const& payload); diff --git a/Userland/Services/WebDriver/Session.cpp b/Userland/Services/WebDriver/Session.cpp index ba31b82b70..8b49973dde 100644 --- a/Userland/Services/WebDriver/Session.cpp +++ b/Userland/Services/WebDriver/Session.cpp @@ -900,6 +900,23 @@ ErrorOr Session::get_element_tag_name(JsonValue const return JsonValue(qualified_name); } +// 13.1 Get Page Source, https://w3c.github.io/webdriver/#dfn-get-page-source +ErrorOr Session::get_source() +{ + // 1. If the current 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. Let source be the result of invoking the fragment serializing algorithm on a fictional node whose only child is the document element providing true for the require well-formed flag. If this causes an exception to be thrown, let source be null. + // 4. Let source be the result of serializing to string the current browsing context active document, if source is null. + // NOTE: Both of the above cases are handled in the remote WebContent process. + auto source = m_browser_connection->serialize_source(); + + // 5. Return success with data source. + return source; +} + struct ScriptArguments { String script; JsonArray const& arguments; diff --git a/Userland/Services/WebDriver/Session.h b/Userland/Services/WebDriver/Session.h index fad69e44ba..1b40f99ed4 100644 --- a/Userland/Services/WebDriver/Session.h +++ b/Userland/Services/WebDriver/Session.h @@ -63,6 +63,7 @@ public: ErrorOr get_element_css_value(JsonValue const& payload, StringView element_id, StringView property_name); ErrorOr get_element_text(JsonValue const& payload, StringView element_id); ErrorOr get_element_tag_name(JsonValue const& payload, StringView element_id); + ErrorOr get_source(); ErrorOr execute_script(JsonValue const& payload); ErrorOr execute_async_script(JsonValue const& payload); ErrorOr get_all_cookies();