LibWeb+WebContent+WebDriver: Implement the Perform Actions endpoint
Similar to script execution, this spins the WebDriver process until the action is complete (rather than spinning the WebContent process, which we've seen result in deadlocks).
This commit is contained in:
parent
8000837f78
commit
709deeb482
Notes:
github-actions[bot]
2024-10-01 09:03:18 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/709deeb482a Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1566
10 changed files with 83 additions and 16 deletions
|
@ -77,6 +77,18 @@ bool represents_a_web_element(JsonValue const& value)
|
|||
return value.as_object().has_string(web_element_identifier);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-get-a-webelement-origin
|
||||
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_element_origin(StringView origin)
|
||||
{
|
||||
// 1. Assert: browsing context is the current browsing context.
|
||||
|
||||
// 2. Let element be equal to the result of trying to get a known element with session and origin.
|
||||
auto* element = TRY(get_known_connected_element(origin));
|
||||
|
||||
// 3. Return success with data element.
|
||||
return *element;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-get-a-known-element
|
||||
ErrorOr<Web::DOM::Element*, Web::WebDriver::Error> get_known_connected_element(StringView element_id)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,7 @@ JsonObject web_element_reference_object(Web::DOM::Node const& element);
|
|||
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, WebDriver::Error> deserialize_web_element(JsonObject const&);
|
||||
ByteString extract_web_element_reference(JsonObject const&);
|
||||
bool represents_a_web_element(JsonValue const& value);
|
||||
ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_web_element_origin(StringView origin);
|
||||
ErrorOr<Web::DOM::Element*, Web::WebDriver::Error> get_known_connected_element(StringView element_id);
|
||||
bool is_element_stale(Web::DOM::Node const& element);
|
||||
|
||||
|
|
|
@ -44,8 +44,10 @@
|
|||
#include <LibWeb/Platform/Timer.h>
|
||||
#include <LibWeb/UIEvents/EventNames.h>
|
||||
#include <LibWeb/UIEvents/MouseEvent.h>
|
||||
#include <LibWeb/WebDriver/Actions.h>
|
||||
#include <LibWeb/WebDriver/ElementReference.h>
|
||||
#include <LibWeb/WebDriver/ExecuteScript.h>
|
||||
#include <LibWeb/WebDriver/InputState.h>
|
||||
#include <LibWeb/WebDriver/Properties.h>
|
||||
#include <LibWeb/WebDriver/Screenshot.h>
|
||||
#include <WebContent/WebDriverConnection.h>
|
||||
|
@ -1887,17 +1889,38 @@ Messages::WebDriverClient::DeleteAllCookiesResponse WebDriverConnection::delete_
|
|||
// 15.7 Perform Actions, https://w3c.github.io/webdriver/#perform-actions
|
||||
Messages::WebDriverClient::PerformActionsResponse WebDriverConnection::perform_actions(JsonValue const& payload)
|
||||
{
|
||||
dbgln("FIXME: WebDriverConnection::perform_actions({})", payload);
|
||||
// 4. If session's current browsing context is no longer open, return error with error code no such window.
|
||||
// NOTE: We do this first so we can assume the current top-level browsing context below is non-null.
|
||||
TRY(ensure_current_browsing_context_is_open());
|
||||
|
||||
// FIXME: 1. Let input state be the result of get the input state with session and session's current top-level browsing context.
|
||||
// FIXME: 2. Let actions options be a new actions options with the is element origin steps set to represents a web element, and the get element origin steps set to get a WebElement origin.
|
||||
// FIXME: 3. Let actions by tick be the result of trying to extract an action sequence with input state, parameters, and actions options.
|
||||
// FIXME: 4. If session's current browsing context is no longer open, return error with error code no such window.
|
||||
// FIXME: 5. Try to handle any user prompts with session.
|
||||
// FIXME: 6. Dispatch actions with input state, actions by tick, current browsing context, and actions options. If this results in an error return that error.
|
||||
// FIXME: 7. Return success with data null.
|
||||
// 1. Let input state be the result of get the input state with session and session's current top-level browsing context.
|
||||
auto& input_state = Web::WebDriver::get_input_state(*current_top_level_browsing_context());
|
||||
|
||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::UnsupportedOperation, "perform actions not implemented"sv);
|
||||
// 2. Let actions options be a new actions options with the is element origin steps set to represents a web element,
|
||||
// and the get element origin steps set to get a WebElement origin.
|
||||
Web::WebDriver::ActionsOptions actions_options {
|
||||
.is_element_origin = &Web::WebDriver::represents_a_web_element,
|
||||
.get_element_origin = &Web::WebDriver::get_web_element_origin,
|
||||
};
|
||||
|
||||
// 3. Let actions by tick be the result of trying to extract an action sequence with input state, parameters, and
|
||||
// actions options.
|
||||
auto actions_by_tick = TRY(Web::WebDriver::extract_an_action_sequence(input_state, payload, actions_options));
|
||||
|
||||
// 5. Try to handle any user prompts with session.
|
||||
TRY(handle_any_user_prompts());
|
||||
|
||||
// 6. Dispatch actions with input state, actions by tick, current browsing context, and actions options. If this
|
||||
// results in an error return that error.
|
||||
auto on_complete = JS::create_heap_function(current_browsing_context().heap(), [this](Web::WebDriver::Response result) {
|
||||
m_action_executor = nullptr;
|
||||
async_actions_performed(move(result));
|
||||
});
|
||||
|
||||
m_action_executor = Web::WebDriver::dispatch_actions(input_state, move(actions_by_tick), current_browsing_context(), move(actions_options), on_complete);
|
||||
|
||||
// 7. Return success with data null.
|
||||
return JsonValue {};
|
||||
}
|
||||
|
||||
// 15.8 Release Actions, https://w3c.github.io/webdriver/#release-actions
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -145,6 +145,8 @@ private:
|
|||
|
||||
// https://w3c.github.io/webdriver/#dfn-current-browsing-context
|
||||
JS::Handle<Web::HTML::BrowsingContext> m_current_browsing_context;
|
||||
|
||||
JS::Handle<JS::Cell> m_action_executor;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
|
||||
endpoint WebDriverServer {
|
||||
script_executed(Web::WebDriver::Response response) =|
|
||||
actions_performed(Web::WebDriver::Response response) =|
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022-2023, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -687,7 +687,7 @@ Web::WebDriver::Response Client::perform_actions(Web::WebDriver::Parameters para
|
|||
{
|
||||
dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/actions");
|
||||
auto session = TRY(find_session_with_id(parameters[0]));
|
||||
return session->web_content_connection().perform_actions(move(payload));
|
||||
return session->perform_actions(move(payload));
|
||||
}
|
||||
|
||||
// 15.8 Release Actions, https://w3c.github.io/webdriver/#release-actions
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -201,4 +201,22 @@ Web::WebDriver::Response Session::execute_script(JsonValue payload, ScriptMode m
|
|||
return response.release_value();
|
||||
}
|
||||
|
||||
Web::WebDriver::Response Session::perform_actions(JsonValue payload) const
|
||||
{
|
||||
ScopeGuard guard { [&]() { web_content_connection().on_actions_performed = nullptr; } };
|
||||
|
||||
Optional<Web::WebDriver::Response> response;
|
||||
web_content_connection().on_actions_performed = [&](auto result) {
|
||||
response = move(result);
|
||||
};
|
||||
|
||||
TRY(web_content_connection().perform_actions(move(payload)));
|
||||
|
||||
Core::EventLoop::current().spin_until([&]() {
|
||||
return response.has_value();
|
||||
});
|
||||
|
||||
return response.release_value();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Florent Castelli <florent.castelli@gmail.com>
|
||||
* Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -61,6 +61,8 @@ public:
|
|||
};
|
||||
Web::WebDriver::Response execute_script(JsonValue, ScriptMode) const;
|
||||
|
||||
Web::WebDriver::Response perform_actions(JsonValue) const;
|
||||
|
||||
private:
|
||||
using ServerPromise = Core::Promise<ErrorOr<void>>;
|
||||
ErrorOr<NonnullRefPtr<Core::LocalServer>> create_server(NonnullRefPtr<ServerPromise> promise);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -26,4 +26,10 @@ void WebContentConnection::script_executed(Web::WebDriver::Response const& respo
|
|||
on_script_executed(response);
|
||||
}
|
||||
|
||||
void WebContentConnection::actions_performed(Web::WebDriver::Response const& response)
|
||||
{
|
||||
if (on_actions_performed)
|
||||
on_actions_performed(response);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2022-2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -22,11 +22,13 @@ public:
|
|||
|
||||
Function<void()> on_close;
|
||||
Function<void(Web::WebDriver::Response)> on_script_executed;
|
||||
Function<void(Web::WebDriver::Response)> on_actions_performed;
|
||||
|
||||
private:
|
||||
virtual void die() override;
|
||||
|
||||
virtual void script_executed(Web::WebDriver::Response const&) override;
|
||||
virtual void actions_performed(Web::WebDriver::Response const&) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue