LibWeb+WebContent: Handle user prompts that open during script execution
If a dialog is opened while a script is executing, we must give control back to the WebDriver client. The script must also continue executing though, so once it completes, we ignore its result.
This commit is contained in:
parent
3cc7118bf4
commit
1be67faab7
Notes:
github-actions[bot]
2024-11-02 10:10:50 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/1be67faab75 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2100
3 changed files with 38 additions and 43 deletions
|
@ -254,8 +254,6 @@ JS::ThrowCompletionOr<JS::Value> execute_a_function_body(HTML::BrowsingContext c
|
|||
// https://w3c.github.io/webdriver/#dfn-execute-a-function-body
|
||||
JS::ThrowCompletionOr<JS::Value> execute_a_function_body(HTML::Window const& window, ByteString const& body, ReadonlySpan<JS::Value> parameters, JS::GCPtr<JS::Object> environment_override_object)
|
||||
{
|
||||
// FIXME: If at any point during the algorithm a user prompt appears, immediately return Completion { [[Type]]: normal, [[Value]]: null, [[Target]]: empty }, but continue to run the other steps of this algorithm in parallel.
|
||||
|
||||
auto& realm = window.realm();
|
||||
|
||||
// 2. Let environment settings be the environment settings object for window.
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
#include <LibWeb/UIEvents/MouseEvent.h>
|
||||
#include <LibWeb/WebDriver/Actions.h>
|
||||
#include <LibWeb/WebDriver/ElementReference.h>
|
||||
#include <LibWeb/WebDriver/ExecuteScript.h>
|
||||
#include <LibWeb/WebDriver/HeapTimer.h>
|
||||
#include <LibWeb/WebDriver/InputState.h>
|
||||
#include <LibWeb/WebDriver/Properties.h>
|
||||
|
@ -1861,35 +1860,12 @@ Messages::WebDriverClient::ExecuteScriptResponse WebDriverConnection::execute_sc
|
|||
auto timeout_ms = m_timeouts_configuration.script_timeout;
|
||||
|
||||
// This handles steps 5 to 9 and produces the appropriate result type for the following steps.
|
||||
Web::WebDriver::execute_script(current_browsing_context(), move(body), move(arguments), timeout_ms, JS::create_heap_function(vm.heap(), [&](Web::WebDriver::ExecuteScriptResultSerialized result) {
|
||||
Web::WebDriver::execute_script(current_browsing_context(), move(body), move(arguments), timeout_ms, JS::create_heap_function(vm.heap(), [this](Web::WebDriver::ExecuteScriptResultSerialized result) {
|
||||
dbgln_if(WEBDRIVER_DEBUG, "Executing script returned: {}", result.value);
|
||||
Web::WebDriver::Response response;
|
||||
|
||||
switch (result.type) {
|
||||
// 10. If promise is still pending and the session script timeout is reached, return error with error code script timeout.
|
||||
case Web::WebDriver::ExecuteScriptResultType::Timeout:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ScriptTimeoutError, "Script timed out");
|
||||
break;
|
||||
// 11. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
|
||||
case Web::WebDriver::ExecuteScriptResultType::PromiseResolved:
|
||||
response = move(result.value);
|
||||
break;
|
||||
// 12. Upon rejection of promise with reason r, let result be a JSON clone of r, and return error with error code javascript error and data result.
|
||||
case Web::WebDriver::ExecuteScriptResultType::PromiseRejected:
|
||||
case Web::WebDriver::ExecuteScriptResultType::JavaScriptError:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::JavascriptError, "Script returned an error", move(result.value));
|
||||
break;
|
||||
case Web::WebDriver::ExecuteScriptResultType::BrowsingContextDiscarded:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, "Browsing context has been discarded", move(result.value));
|
||||
break;
|
||||
case Web::WebDriver::ExecuteScriptResultType::StaleElement:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, "Referenced element has become stale", move(result.value));
|
||||
break;
|
||||
}
|
||||
|
||||
async_script_executed(move(response));
|
||||
handle_script_response(move(result));
|
||||
}));
|
||||
|
||||
m_has_pending_script_execution = true;
|
||||
return JsonValue {};
|
||||
}
|
||||
|
||||
|
@ -1914,34 +1890,43 @@ Messages::WebDriverClient::ExecuteAsyncScriptResponse WebDriverConnection::execu
|
|||
// This handles steps 5 to 9 and produces the appropriate result type for the following steps.
|
||||
Web::WebDriver::execute_async_script(current_browsing_context(), move(body), move(arguments), timeout_ms, JS::create_heap_function(vm.heap(), [&](Web::WebDriver::ExecuteScriptResultSerialized result) {
|
||||
dbgln_if(WEBDRIVER_DEBUG, "Executing async script returned: {}", result.value);
|
||||
Web::WebDriver::Response response;
|
||||
handle_script_response(move(result));
|
||||
}));
|
||||
|
||||
m_has_pending_script_execution = true;
|
||||
return JsonValue {};
|
||||
}
|
||||
|
||||
void WebDriverConnection::handle_script_response(Web::WebDriver::ExecuteScriptResultSerialized result)
|
||||
{
|
||||
if (!m_has_pending_script_execution)
|
||||
return;
|
||||
m_has_pending_script_execution = false;
|
||||
|
||||
auto response = [&]() -> Web::WebDriver::Response {
|
||||
switch (result.type) {
|
||||
// 10. If promise is still pending and the session script timeout is reached, return error with error code script timeout.
|
||||
case Web::WebDriver::ExecuteScriptResultType::Timeout:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ScriptTimeoutError, "Script timed out");
|
||||
break;
|
||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ScriptTimeoutError, "Script timed out");
|
||||
|
||||
// 11. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
|
||||
case Web::WebDriver::ExecuteScriptResultType::PromiseResolved:
|
||||
response = move(result.value);
|
||||
break;
|
||||
return move(result.value);
|
||||
|
||||
// 12. Upon rejection of promise with reason r, let result be a JSON clone of r, and return error with error code javascript error and data result.
|
||||
case Web::WebDriver::ExecuteScriptResultType::PromiseRejected:
|
||||
case Web::WebDriver::ExecuteScriptResultType::JavaScriptError:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::JavascriptError, "Script returned an error", move(result.value));
|
||||
break;
|
||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::JavascriptError, "Script returned an error", move(result.value));
|
||||
case Web::WebDriver::ExecuteScriptResultType::BrowsingContextDiscarded:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, "Browsing context has been discarded", move(result.value));
|
||||
break;
|
||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, "Browsing context has been discarded", move(result.value));
|
||||
case Web::WebDriver::ExecuteScriptResultType::StaleElement:
|
||||
response = Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, "Referenced element has become stale", move(result.value));
|
||||
break;
|
||||
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::StaleElementReference, "Referenced element has become stale", move(result.value));
|
||||
}
|
||||
|
||||
async_script_executed(move(response));
|
||||
}));
|
||||
VERIFY_NOT_REACHED();
|
||||
}();
|
||||
|
||||
return JsonValue {};
|
||||
async_script_executed(move(response));
|
||||
}
|
||||
|
||||
// 14.1 Get All Cookies, https://w3c.github.io/webdriver/#dfn-get-all-cookies
|
||||
|
@ -2562,6 +2547,14 @@ void WebDriverConnection::page_did_open_dialog(Badge<PageClient>)
|
|||
// seems to match how other browsers behave.
|
||||
if (m_navigation_timer)
|
||||
m_navigation_timer->stop_and_fire_timeout_handler();
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-execute-a-function-body
|
||||
// If at any point during the algorithm a user prompt appears, immediately return Completion { [[Type]]: normal,
|
||||
// [[Value]]: null, [[Target]]: empty }, but continue to run the other steps of this algorithm in parallel.
|
||||
if (m_has_pending_script_execution) {
|
||||
m_has_pending_script_execution = false;
|
||||
async_script_executed(JsonValue {});
|
||||
}
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-maximize-the-window
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/VisibilityState.h>
|
||||
#include <LibWeb/WebDriver/ElementLocationStrategies.h>
|
||||
#include <LibWeb/WebDriver/ExecuteScript.h>
|
||||
#include <LibWeb/WebDriver/Response.h>
|
||||
#include <LibWeb/WebDriver/TimeoutsConfiguration.h>
|
||||
#include <WebContent/Forward.h>
|
||||
|
@ -143,6 +144,8 @@ private:
|
|||
JS::MarkedVector<JS::Value> arguments;
|
||||
};
|
||||
ErrorOr<ScriptArguments, Web::WebDriver::Error> extract_the_script_arguments_from_a_request(JS::VM&, JsonValue const& payload);
|
||||
void handle_script_response(Web::WebDriver::ExecuteScriptResultSerialized);
|
||||
|
||||
void delete_cookies(Optional<StringView> const& name = {});
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-page-load-strategy
|
||||
|
@ -167,6 +170,7 @@ private:
|
|||
JS::GCPtr<Web::HTML::BrowsingContext> m_current_top_level_browsing_context;
|
||||
|
||||
size_t m_pending_window_rect_requests { 0 };
|
||||
bool m_has_pending_script_execution { false };
|
||||
|
||||
friend class ElementLocator;
|
||||
JS::GCPtr<ElementLocator> m_element_locator;
|
||||
|
|
Loading…
Add table
Reference in a new issue