mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 22:47:47 +00:00
LibWeb/WebDriver: Support resolve with callback in execute_async_script
Before execute_async_script supported returning value from script to /execute/async caller only if script returns promise that resolves into returned value. This change fixes execute_async_script to also support returning value to caller using callback that is always passed to script as a last argument.
This commit is contained in:
parent
33815d550b
commit
857ceb215c
1 changed files with 38 additions and 25 deletions
|
@ -331,19 +331,21 @@ ExecuteScriptResultSerialized execute_async_script(Web::Page& page, DeprecatedSt
|
||||||
auto& vm = window->vm();
|
auto& vm = window->vm();
|
||||||
auto start = MonotonicTime::now();
|
auto start = MonotonicTime::now();
|
||||||
|
|
||||||
// 4. Let promise be a new Promise.
|
// NOTE: We need to push an execution context in order to make create_resolving_functions() succeed.
|
||||||
auto promise = JS::Promise::create(realm);
|
vm.push_execution_context(settings_object.realm_execution_context());
|
||||||
|
ScopeGuard pop_guard = [&] {
|
||||||
// FIXME: 5 Run the following substeps in parallel:
|
|
||||||
auto result = [&] {
|
|
||||||
// NOTE: We need to push an execution context in order to make create_resolving_functions() succeed.
|
|
||||||
vm.push_execution_context(settings_object.realm_execution_context());
|
|
||||||
|
|
||||||
// 1. Let resolvingFunctions be CreateResolvingFunctions(promise).
|
|
||||||
auto resolving_functions = promise->create_resolving_functions();
|
|
||||||
|
|
||||||
VERIFY(&settings_object.realm_execution_context() == &vm.running_execution_context());
|
VERIFY(&settings_object.realm_execution_context() == &vm.running_execution_context());
|
||||||
vm.pop_execution_context();
|
vm.pop_execution_context();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Let promise be a new Promise.
|
||||||
|
auto promise_capability = WebIDL::create_promise(realm);
|
||||||
|
JS::NonnullGCPtr promise { verify_cast<JS::Promise>(*promise_capability->promise()) };
|
||||||
|
|
||||||
|
// FIXME: 5 Run the following substeps in parallel:
|
||||||
|
[&] {
|
||||||
|
// 1. Let resolvingFunctions be CreateResolvingFunctions(promise).
|
||||||
|
auto resolving_functions = promise->create_resolving_functions();
|
||||||
|
|
||||||
// 2. Append resolvingFunctions.[[Resolve]] to arguments.
|
// 2. Append resolvingFunctions.[[Resolve]] to arguments.
|
||||||
arguments.append(resolving_functions.resolve);
|
arguments.append(resolving_functions.resolve);
|
||||||
|
@ -357,27 +359,27 @@ ExecuteScriptResultSerialized execute_async_script(Web::Page& page, DeprecatedSt
|
||||||
// In order to preserve legacy behavior, the return value only influences the command if it is a
|
// In order to preserve legacy behavior, the return value only influences the command if it is a
|
||||||
// "thenable" object or if determining this produces an exception.
|
// "thenable" object or if determining this produces an exception.
|
||||||
if (script_result.is_throw_completion())
|
if (script_result.is_throw_completion())
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseRejected, *script_result.throw_completion().value() };
|
return;
|
||||||
|
|
||||||
// 5. If Type(scriptResult.[[Value]]) is not Object, then abort these steps.
|
// 5. If Type(scriptResult.[[Value]]) is not Object, then abort these steps.
|
||||||
if (!script_result.value().is_object())
|
if (!script_result.value().is_object())
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseResolved, JS::js_null() };
|
return;
|
||||||
|
|
||||||
// 6. Let then be Get(scriptResult.[[Value]], "then").
|
// 6. Let then be Get(scriptResult.[[Value]], "then").
|
||||||
auto then = script_result.value().as_object().get(vm.names.then);
|
auto then = script_result.value().as_object().get(vm.names.then);
|
||||||
|
|
||||||
// 7. If then.[[Type]] is not normal, then reject promise with value then.[[Value]], and abort these steps.
|
// 7. If then.[[Type]] is not normal, then reject promise with value then.[[Value]], and abort these steps.
|
||||||
if (then.is_throw_completion())
|
if (then.is_throw_completion())
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseRejected, *then.throw_completion().value() };
|
return;
|
||||||
|
|
||||||
// 8. If IsCallable(then.[[Type]]) is false, then abort these steps.
|
// 8. If IsCallable(then.[[Type]]) is false, then abort these steps.
|
||||||
if (!then.value().is_function())
|
if (!then.value().is_function())
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseResolved, JS::js_null() };
|
return;
|
||||||
|
|
||||||
// 9. Let scriptPromise be PromiseResolve(Promise, scriptResult.[[Value]]).
|
// 9. Let scriptPromise be PromiseResolve(Promise, scriptResult.[[Value]]).
|
||||||
auto script_promise_or_error = JS::promise_resolve(vm, realm.intrinsics().promise_constructor(), script_result.value());
|
auto script_promise_or_error = JS::promise_resolve(vm, realm.intrinsics().promise_constructor(), script_result.value());
|
||||||
if (script_promise_or_error.is_throw_completion())
|
if (script_promise_or_error.is_throw_completion())
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseRejected, *script_promise_or_error.throw_completion().value() };
|
return;
|
||||||
auto& script_promise = static_cast<JS::Promise&>(*script_promise_or_error.value());
|
auto& script_promise = static_cast<JS::Promise&>(*script_promise_or_error.value());
|
||||||
|
|
||||||
vm.custom_data()->spin_event_loop_until([&] {
|
vm.custom_data()->spin_event_loop_until([&] {
|
||||||
|
@ -390,26 +392,37 @@ ExecuteScriptResultSerialized execute_async_script(Web::Page& page, DeprecatedSt
|
||||||
|
|
||||||
// 10. Upon fulfillment of scriptPromise with value v, resolve promise with value v.
|
// 10. Upon fulfillment of scriptPromise with value v, resolve promise with value v.
|
||||||
if (script_promise.state() == JS::Promise::State::Fulfilled)
|
if (script_promise.state() == JS::Promise::State::Fulfilled)
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseResolved, script_promise.result() };
|
WebIDL::resolve_promise(realm, promise_capability, script_promise.result());
|
||||||
|
|
||||||
// 11. Upon rejection of scriptPromise with value r, reject promise with value r.
|
// 11. Upon rejection of scriptPromise with value r, reject promise with value r.
|
||||||
if (script_promise.state() == JS::Promise::State::Rejected)
|
if (script_promise.state() == JS::Promise::State::Rejected)
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::PromiseRejected, script_promise.result() };
|
WebIDL::reject_promise(realm, promise_capability, script_promise.result());
|
||||||
|
|
||||||
return ExecuteScriptResult { ExecuteScriptResultType::Timeout, script_promise.result() };
|
|
||||||
}();
|
}();
|
||||||
|
|
||||||
// 6. If promise is still pending and session script timeout milliseconds is reached, return error with error code script timeout.
|
// FIXME: 6. If promise is still pending and session script timeout milliseconds is reached, return error with error code script timeout.
|
||||||
// 7. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
|
|
||||||
// 8. 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.
|
vm.custom_data()->spin_event_loop_until([&] {
|
||||||
auto json_value_or_error = json_clone(realm, result.value);
|
return promise->state() != JS::Promise::State::Pending;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto json_value_or_error = json_clone(realm, promise->result());
|
||||||
if (json_value_or_error.is_error()) {
|
if (json_value_or_error.is_error()) {
|
||||||
auto error_object = JsonObject {};
|
auto error_object = JsonObject {};
|
||||||
error_object.set("name", "Error");
|
error_object.set("name", "Error");
|
||||||
error_object.set("message", "Could not clone result value");
|
error_object.set("message", "Could not clone result value");
|
||||||
return { ExecuteScriptResultType::JavaScriptError, move(error_object) };
|
return { ExecuteScriptResultType::JavaScriptError, move(error_object) };
|
||||||
}
|
}
|
||||||
return { result.type, json_value_or_error.release_value() };
|
|
||||||
|
// 7. Upon fulfillment of promise with value v, let result be a JSON clone of v, and return success with data result.
|
||||||
|
if (promise->state() == JS::Promise::State::Fulfilled) {
|
||||||
|
return { ExecuteScriptResultType::PromiseResolved, json_value_or_error.release_value() };
|
||||||
|
}
|
||||||
|
// 8. 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.
|
||||||
|
if (promise->state() == JS::Promise::State::Rejected) {
|
||||||
|
return { ExecuteScriptResultType::PromiseRejected, json_value_or_error.release_value() };
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue