2022-11-02 18:02:19 +00:00
/*
2023-04-13 00:47:15 +02:00
* Copyright ( c ) 2022 - 2023 , Linus Groh < linusg @ serenityos . org >
2022-11-02 18:02:19 +00:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/JsonArray.h>
# include <AK/JsonObject.h>
# include <AK/JsonValue.h>
# include <AK/NumericLimits.h>
# include <AK/ScopeGuard.h>
# include <AK/Time.h>
# include <AK/Variant.h>
2022-11-23 13:28:01 +01:00
# include <LibJS/Parser.h>
2022-11-02 18:02:19 +00:00
# include <LibJS/Runtime/Array.h>
# include <LibJS/Runtime/ECMAScriptFunctionObject.h>
# include <LibJS/Runtime/GlobalEnvironment.h>
# include <LibJS/Runtime/JSONObject.h>
# include <LibJS/Runtime/Promise.h>
# include <LibJS/Runtime/PromiseConstructor.h>
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/HTMLCollection.h>
# include <LibWeb/DOM/NodeList.h>
# include <LibWeb/FileAPI/FileList.h>
# include <LibWeb/HTML/BrowsingContext.h>
# include <LibWeb/HTML/HTMLOptionsCollection.h>
# include <LibWeb/HTML/Scripting/Environments.h>
2023-07-06 07:43:23 -04:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2022-11-02 18:02:19 +00:00
# include <LibWeb/HTML/Window.h>
2024-02-04 03:44:19 -07:00
# include <LibWeb/HTML/WindowProxy.h>
2022-11-02 18:02:19 +00:00
# include <LibWeb/Page/Page.h>
2024-08-30 22:36:46 +01:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2023-04-20 17:41:32 +01:00
# include <LibWeb/WebDriver/Contexts.h>
2022-11-02 18:02:19 +00:00
# include <LibWeb/WebDriver/ExecuteScript.h>
namespace Web : : WebDriver {
2023-01-14 16:34:44 -07:00
# define TRY_OR_JS_ERROR(expression) \
( { \
2023-02-09 13:27:43 -05:00
auto & & _temporary_result = ( expression ) ; \
2023-01-14 16:34:44 -07:00
if ( _temporary_result . is_error ( ) ) [[unlikely]] \
return ExecuteScriptResultType : : JavaScriptError ; \
static_assert ( ! : : AK : : Detail : : IsLvalueReference < decltype ( _temporary_result . release_value ( ) ) > , \
" Do not return a reference from a fallible expression " ) ; \
_temporary_result . release_value ( ) ; \
2022-11-02 18:02:19 +00:00
} )
static ErrorOr < JsonValue , ExecuteScriptResultType > internal_json_clone_algorithm ( JS : : Realm & , JS : : Value , HashTable < JS : : Object * > & seen ) ;
static ErrorOr < JsonValue , ExecuteScriptResultType > clone_an_object ( JS : : Realm & , JS : : Object & , HashTable < JS : : Object * > & seen , auto const & clone_algorithm ) ;
// https://w3c.github.io/webdriver/#dfn-collection
static bool is_collection ( JS : : Object const & value )
{
// A collection is an Object that implements the Iterable interface, and whose:
return (
// - initial value of the toString own property is "Arguments"
value . has_parameter_map ( )
// - instance of Array
| | is < JS : : Array > ( value )
// - instance of FileList
| | is < FileAPI : : FileList > ( value )
// - instance of HTMLAllCollection
| | false // FIXME
// - instance of HTMLCollection
| | is < DOM : : HTMLCollection > ( value )
// - instance of HTMLFormControlsCollection
| | false // FIXME
// - instance of HTMLOptionsCollection
| | is < HTML : : HTMLOptionsCollection > ( value )
// - instance of NodeList
| | is < DOM : : NodeList > ( value ) ) ;
}
// https://w3c.github.io/webdriver/#dfn-json-clone
static ErrorOr < JsonValue , ExecuteScriptResultType > json_clone ( JS : : Realm & realm , JS : : Value value )
{
// To perform a JSON clone return the result of calling the internal JSON clone algorithm with arguments value and an empty List.
auto seen = HashTable < JS : : Object * > { } ;
return internal_json_clone_algorithm ( realm , value , seen ) ;
}
// https://w3c.github.io/webdriver/#dfn-internal-json-clone-algorithm
static ErrorOr < JsonValue , ExecuteScriptResultType > internal_json_clone_algorithm ( JS : : Realm & realm , JS : : Value value , HashTable < JS : : Object * > & seen )
{
auto & vm = realm . vm ( ) ;
// When required to run the internal JSON clone algorithm with arguments value and seen, a remote end must return the value of the first matching statement, matching on value:
// -> undefined
// -> null
if ( value . is_nullish ( ) ) {
// Success with data null.
return JsonValue { } ;
}
// -> type Boolean
// -> type Number
// -> type String
// Success with data value.
if ( value . is_boolean ( ) )
return JsonValue { value . as_bool ( ) } ;
if ( value . is_number ( ) )
return JsonValue { value . as_double ( ) } ;
if ( value . is_string ( ) )
2023-12-16 17:49:34 +03:30
return JsonValue { value . as_string ( ) . byte_string ( ) } ;
2022-11-02 18:02:19 +00:00
// NOTE: BigInt and Symbol not mentioned anywhere in the WebDriver spec, as it references ES5.
// It assumes that all primitives are handled above, and the value is an object for the remaining steps.
if ( value . is_bigint ( ) | | value . is_symbol ( ) )
return ExecuteScriptResultType : : JavaScriptError ;
2023-04-20 17:41:32 +01:00
// FIXME: -> a collection
// FIXME: -> instance of element
// FIXME: -> instance of shadow root
// -> a WindowProxy object
if ( is < HTML : : WindowProxy > ( value . as_object ( ) ) ) {
auto const & window_proxy = static_cast < HTML : : WindowProxy & > ( value . as_object ( ) ) ;
2024-02-05 12:34:29 -05:00
// If the associated browsing context of the WindowProxy object in value has been destroyed, return error with
2023-04-20 17:41:32 +01:00
// error code stale element reference.
2024-02-05 12:34:29 -05:00
if ( window_proxy . associated_browsing_context ( ) - > has_navigable_been_destroyed ( ) )
2023-04-20 17:41:32 +01:00
return ExecuteScriptResultType : : BrowsingContextDiscarded ;
// Otherwise return success with data set to WindowProxy reference object for value.
return window_proxy_reference_object ( window_proxy ) ;
}
2022-11-02 18:02:19 +00:00
// -> has an own property named "toJSON" that is a Function
auto to_json = value . as_object ( ) . get_without_side_effects ( vm . names . toJSON ) ;
if ( to_json . is_function ( ) ) {
// Return success with the value returned by Function.[[Call]](toJSON) with value as the this value.
auto to_json_result = TRY_OR_JS_ERROR ( to_json . as_function ( ) . internal_call ( value , JS : : MarkedVector < JS : : Value > { vm . heap ( ) } ) ) ;
if ( ! to_json_result . is_string ( ) )
return ExecuteScriptResultType : : JavaScriptError ;
2023-12-16 17:49:34 +03:30
return to_json_result . as_string ( ) . byte_string ( ) ;
2022-11-02 18:02:19 +00:00
}
// -> Otherwise
// 1. If value is in seen, return error with error code javascript error.
if ( seen . contains ( & value . as_object ( ) ) )
return ExecuteScriptResultType : : JavaScriptError ;
// 2. Append value to seen.
seen . set ( & value . as_object ( ) ) ;
ScopeGuard remove_seen { [ & ] {
// 4. Remove the last element of seen.
seen . remove ( & value . as_object ( ) ) ;
} } ;
// 3. Let result be the value of running the clone an object algorithm with arguments value and seen, and the internal JSON clone algorithm as the clone algorithm.
auto result = TRY ( clone_an_object ( realm , value . as_object ( ) , seen , internal_json_clone_algorithm ) ) ;
// 5. Return result.
return result ;
}
// https://w3c.github.io/webdriver/#dfn-clone-an-object
static ErrorOr < JsonValue , ExecuteScriptResultType > clone_an_object ( JS : : Realm & realm , JS : : Object & value , HashTable < JS : : Object * > & seen , auto const & clone_algorithm )
{
auto & vm = realm . vm ( ) ;
// 1. Let result be the value of the first matching statement, matching on value:
auto get_result = [ & ] ( ) - > ErrorOr < Variant < JsonArray , JsonObject > , ExecuteScriptResultType > {
// -> a collection
if ( is_collection ( value ) ) {
// A new Array which length property is equal to the result of getting the property length of value.
auto length_property = TRY_OR_JS_ERROR ( value . internal_get_own_property ( vm . names . length ) ) ;
if ( ! length_property - > value . has_value ( ) )
return ExecuteScriptResultType : : JavaScriptError ;
auto length = TRY_OR_JS_ERROR ( length_property - > value - > to_length ( vm ) ) ;
if ( length > NumericLimits < u32 > : : max ( ) )
return ExecuteScriptResultType : : JavaScriptError ;
auto array = JsonArray { } ;
for ( size_t i = 0 ; i < length ; + + i )
2023-04-17 15:38:27 +10:00
array . must_append ( JsonValue { } ) ;
2022-11-02 18:02:19 +00:00
return array ;
}
// -> Otherwise
else {
// A new Object.
return JsonObject { } ;
}
} ;
auto result = TRY ( get_result ( ) ) ;
// 2. For each enumerable own property in value, run the following substeps:
for ( auto & key : MUST ( value . Object : : internal_own_property_keys ( ) ) ) {
// 1. Let name be the name of the property.
auto name = MUST ( JS : : PropertyKey : : from_value ( vm , key ) ) ;
if ( ! value . storage_get ( name ) - > attributes . is_enumerable ( ) )
continue ;
// 2. Let source property value be the result of getting a property named name from value. If doing so causes script to be run and that script throws an error, return error with error code javascript error.
auto source_property_value = TRY_OR_JS_ERROR ( value . internal_get_own_property ( name ) ) ;
if ( ! source_property_value . has_value ( ) | | ! source_property_value - > value . has_value ( ) )
continue ;
// 3. Let cloned property result be the result of calling the clone algorithm with arguments source property value and seen.
auto cloned_property_result = clone_algorithm ( realm , * source_property_value - > value , seen ) ;
// 4. If cloned property result is a success, set a property of result with name name and value equal to cloned property result’ s data.
if ( ! cloned_property_result . is_error ( ) ) {
result . visit (
[ & ] ( JsonArray & array ) {
// NOTE: If this was a JS array, only indexed properties would be serialized anyway.
if ( name . is_number ( ) )
2023-05-08 13:53:29 -07:00
array . set ( name . as_number ( ) , cloned_property_result . value ( ) ) ;
2022-11-02 18:02:19 +00:00
} ,
[ & ] ( JsonObject & object ) {
object . set ( name . to_string ( ) , cloned_property_result . value ( ) ) ;
} ) ;
}
// 5. Otherwise, return cloned property result.
else {
return cloned_property_result ;
}
}
return result . visit ( [ & ] ( auto const & value ) - > JsonValue { return value ; } ) ;
}
// https://w3c.github.io/webdriver/#dfn-execute-a-function-body
2023-12-16 17:49:34 +03:30
static JS : : ThrowCompletionOr < JS : : Value > execute_a_function_body ( Web : : Page & page , ByteString const & body , JS : : MarkedVector < JS : : Value > parameters )
2022-11-02 18:02:19 +00:00
{
// 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.
// 1. Let window be the associated window of the current browsing context’ s active document.
// FIXME: This will need adjusting when WebDriver supports frames.
2024-03-10 08:41:18 +01:00
auto window = page . top_level_browsing_context ( ) . active_document ( ) - > window ( ) ;
2022-11-02 18:02:19 +00:00
// 2. Let environment settings be the environment settings object for window.
2024-03-10 08:41:18 +01:00
auto & environment_settings = Web : : HTML : : relevant_settings_object ( * window ) ;
2022-11-02 18:02:19 +00:00
// 3. Let global scope be environment settings realm’ s global environment.
auto & global_scope = environment_settings . realm ( ) . global_environment ( ) ;
2024-03-10 08:41:18 +01:00
auto & realm = window - > realm ( ) ;
2022-11-02 18:02:19 +00:00
2023-12-16 17:49:34 +03:30
auto source_text = ByteString : : formatted ( " function() {{ {} }} " , body ) ;
2022-11-02 18:02:19 +00:00
auto parser = JS : : Parser { JS : : Lexer { source_text } } ;
auto function_expression = parser . parse_function_node < JS : : FunctionExpression > ( ) ;
// 4. If body is not parsable as a FunctionBody or if parsing detects an early error, return Completion { [[Type]]: normal, [[Value]]: null, [[Target]]: empty }.
if ( parser . has_errors ( ) )
return JS : : js_null ( ) ;
// 5. If body begins with a directive prologue that contains a use strict directive then let strict be true, otherwise let strict be false.
// NOTE: Handled in step 8 below.
// 6. Prepare to run a script with environment settings.
environment_settings . prepare_to_run_script ( ) ;
// 7. Prepare to run a callback with environment settings.
environment_settings . prepare_to_run_callback ( ) ;
// 8. Let function be the result of calling FunctionCreate, with arguments:
// kind
// Normal.
// list
// An empty List.
// body
// The result of parsing body above.
// global scope
// The result of parsing global scope above.
// strict
// The result of parsing strict above.
2024-05-22 11:04:50 +01:00
auto function = JS : : ECMAScriptFunctionObject : : create ( realm , " " , move ( source_text ) , function_expression - > body ( ) , function_expression - > parameters ( ) , function_expression - > function_length ( ) , function_expression - > local_variables_names ( ) , & global_scope , nullptr , function_expression - > kind ( ) , function_expression - > is_strict_mode ( ) , function_expression - > parsing_insights ( ) ) ;
2022-11-02 18:02:19 +00:00
// 9. Let completion be Function.[[Call]](window, parameters) with function as the this value.
// NOTE: This is not entirely clear, but I don't think they mean actually passing `function` as
// the this value argument, but using it as the object [[Call]] is executed on.
2024-03-10 08:41:18 +01:00
auto completion = function - > internal_call ( window , move ( parameters ) ) ;
2022-11-02 18:02:19 +00:00
// 10. Clean up after running a callback with environment settings.
environment_settings . clean_up_after_running_callback ( ) ;
// 11. Clean up after running a script with environment settings.
environment_settings . clean_up_after_running_script ( ) ;
// 12. Return completion.
return completion ;
}
2024-08-30 22:36:46 +01:00
ExecuteScriptResultSerialized execute_script ( Web : : Page & page , ByteString const & body , JS : : MarkedVector < JS : : Value > arguments , Optional < u64 > const & timeout_ms )
2022-11-02 18:02:19 +00:00
{
2024-08-30 22:36:46 +01:00
auto * document = page . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-02 18:02:19 +00:00
auto * window = page . top_level_browsing_context ( ) . active_window ( ) ;
auto & realm = window - > realm ( ) ;
2024-08-30 22:36:46 +01:00
auto & vm = window - > vm ( ) ;
2022-11-02 18:02:19 +00:00
2024-08-30 22:36:46 +01:00
// 5. Let timer be a new timer.
auto timeout_flag = false ;
auto timer = Core : : Timer : : create ( ) ;
// 6. If timeout is not null:
if ( timeout_ms . has_value ( ) ) {
// 1. Start the timer with timer and timeout.
timer - > on_timeout = [ & ] ( ) {
timeout_flag = true ;
} ;
timer - > set_interval ( timeout_ms . value ( ) ) ;
timer - > set_single_shot ( true ) ;
}
2022-11-02 18:02:19 +00:00
2024-08-30 22:36:46 +01:00
// AD-HOC: An execution context is required for Promise creation hooks.
HTML : : TemporaryExecutionContext execution_context { document - > relevant_settings_object ( ) } ;
// 7. Let promise be a new Promise.
auto promise_capability = WebIDL : : create_promise ( realm ) ;
JS : : NonnullGCPtr promise { verify_cast < JS : : Promise > ( * promise_capability - > promise ( ) ) } ;
// 8. Run the following substeps in parallel:
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( [ & realm , & page , promise_capability , promise , body = move ( body ) , arguments = move ( arguments ) ] ( ) mutable {
2022-11-02 18:02:19 +00:00
// 1. Let scriptPromise be the result of promise-calling execute a function body, with arguments body and arguments.
2024-08-30 22:36:46 +01:00
auto script_result = execute_a_function_body ( page , body , move ( arguments ) ) ;
2022-11-02 18:02:19 +00:00
// 2. Upon fulfillment of scriptPromise with value v, resolve promise with value v.
2024-08-30 22:36:46 +01:00
if ( script_result . has_value ( ) ) {
WebIDL : : resolve_promise ( realm , promise_capability , script_result . release_value ( ) ) ;
}
2022-11-02 18:02:19 +00:00
// 3. Upon rejection of scriptPromise with value r, reject promise with value r.
2024-08-30 22:36:46 +01:00
if ( script_result . is_throw_completion ( ) ) {
promise - > reject ( * script_result . throw_completion ( ) . value ( ) ) ;
}
} ) ;
// 9. Wait until promise is resolved, or timer's timeout fired flag is set, whichever occurs first.
vm . custom_data ( ) - > spin_event_loop_until ( [ & ] {
return timeout_flag | | promise - > state ( ) ! = JS : : Promise : : State : : Pending ;
} ) ;
// 10. If promise is still pending and timer's timeout fired flag is set, return error with error code script timeout.
if ( timeout_flag & & promise - > state ( ) = = JS : : Promise : : State : : Pending ) {
auto error_object = JsonObject { } ;
error_object . set ( " name " , " Error " ) ;
error_object . set ( " message " , " Script Timeout " ) ;
return { ExecuteScriptResultType : : Timeout , move ( error_object ) } ;
}
auto json_value_or_error = json_clone ( realm , promise - > result ( ) ) ;
2022-11-02 18:02:19 +00:00
if ( json_value_or_error . is_error ( ) ) {
auto error_object = JsonObject { } ;
error_object . set ( " name " , " Error " ) ;
error_object . set ( " message " , " Could not clone result value " ) ;
return { ExecuteScriptResultType : : JavaScriptError , move ( error_object ) } ;
}
2024-08-30 22:36:46 +01:00
// 11. If promise is fulfilled with value v, let result be JSON clone with session and v, and return success with data result.
if ( promise - > state ( ) = = JS : : Promise : : State : : Fulfilled ) {
return { ExecuteScriptResultType : : PromiseResolved , json_value_or_error . release_value ( ) } ;
}
// 12. If promise is rejected with reason r, let result be JSON clone with session and 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 ( ) ;
2022-11-02 18:02:19 +00:00
}
2024-08-30 22:36:46 +01:00
ExecuteScriptResultSerialized execute_async_script ( Web : : Page & page , ByteString const & body , JS : : MarkedVector < JS : : Value > arguments , Optional < u64 > const & timeout_ms )
2022-11-02 18:02:19 +00:00
{
2023-03-11 05:47:16 +03:00
auto * document = page . top_level_browsing_context ( ) . active_document ( ) ;
2022-11-02 18:02:19 +00:00
auto * window = page . top_level_browsing_context ( ) . active_window ( ) ;
auto & realm = window - > realm ( ) ;
auto & vm = window - > vm ( ) ;
2024-08-30 22:36:46 +01:00
// 5. Let timer be a new timer.
IGNORE_USE_IN_ESCAPING_LAMBDA auto timeout_flag = false ;
auto timer = Core : : Timer : : create ( ) ;
// 6. If timeout is not null:
if ( timeout_ms . has_value ( ) ) {
// 1. Start the timer with timer and timeout.
timer - > on_timeout = [ & ] ( ) {
timeout_flag = true ;
} ;
timer - > set_interval ( timeout_ms . value ( ) ) ;
timer - > set_single_shot ( true ) ;
}
2023-06-17 18:07:27 +02:00
2023-07-06 07:43:23 -04:00
// AD-HOC: An execution context is required for Promise creation hooks.
HTML : : TemporaryExecutionContext execution_context { document - > relevant_settings_object ( ) } ;
2023-05-30 15:23:46 +03:00
2024-08-30 22:36:46 +01:00
// 7. Let promise be a new Promise.
2023-05-30 15:23:46 +03:00
auto promise_capability = WebIDL : : create_promise ( realm ) ;
JS : : NonnullGCPtr promise { verify_cast < JS : : Promise > ( * promise_capability - > promise ( ) ) } ;
2022-11-02 18:02:19 +00:00
2024-08-30 22:36:46 +01:00
// 8. Run the following substeps in parallel:
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( [ & vm , & realm , & page , & timeout_flag , promise_capability , promise , body = move ( body ) , arguments = move ( arguments ) ] ( ) mutable {
2022-11-02 18:02:19 +00:00
// 1. Let resolvingFunctions be CreateResolvingFunctions(promise).
auto resolving_functions = promise - > create_resolving_functions ( ) ;
// 2. Append resolvingFunctions.[[Resolve]] to arguments.
2023-02-26 16:09:02 -07:00
arguments . append ( resolving_functions . resolve ) ;
2022-11-02 18:02:19 +00:00
// 3. Let result be the result of calling execute a function body, with arguments body and arguments.
// FIXME: 'result' -> 'scriptResult' (spec issue)
auto script_result = execute_a_function_body ( page , body , move ( arguments ) ) ;
2024-08-30 22:36:46 +01:00
// 4. If scriptResult.[[Type]] is not normal, then reject promise with value scriptResult.[[Value]], and abort these steps.
2022-11-02 18:02:19 +00:00
// NOTE: Prior revisions of this specification did not recognize the return value of the provided script.
// 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.
2023-06-17 18:17:05 +02:00
if ( script_result . is_throw_completion ( ) ) {
promise - > reject ( * script_result . throw_completion ( ) . value ( ) ) ;
2023-05-30 15:23:46 +03:00
return ;
2023-06-17 18:17:05 +02:00
}
2022-11-02 18:02:19 +00:00
// 5. If Type(scriptResult.[[Value]]) is not Object, then abort these steps.
if ( ! script_result . value ( ) . is_object ( ) )
2023-05-30 15:23:46 +03:00
return ;
2022-11-02 18:02:19 +00:00
// 6. Let then be Get(scriptResult.[[Value]], "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.
2023-06-17 18:17:05 +02:00
if ( then . is_throw_completion ( ) ) {
promise - > reject ( * then . throw_completion ( ) . value ( ) ) ;
2023-05-30 15:23:46 +03:00
return ;
2023-06-17 18:17:05 +02:00
}
2022-11-02 18:02:19 +00:00
// 8. If IsCallable(then.[[Type]]) is false, then abort these steps.
if ( ! then . value ( ) . is_function ( ) )
2023-05-30 15:23:46 +03:00
return ;
2022-11-02 18:02:19 +00:00
// 9. Let scriptPromise be PromiseResolve(Promise, scriptResult.[[Value]]).
2023-04-13 00:47:15 +02:00
auto script_promise_or_error = JS : : promise_resolve ( vm , realm . intrinsics ( ) . promise_constructor ( ) , script_result . value ( ) ) ;
2022-11-02 18:02:19 +00:00
if ( script_promise_or_error . is_throw_completion ( ) )
2023-05-30 15:23:46 +03:00
return ;
2022-11-02 18:02:19 +00:00
auto & script_promise = static_cast < JS : : Promise & > ( * script_promise_or_error . value ( ) ) ;
vm . custom_data ( ) - > spin_event_loop_until ( [ & ] {
2024-08-30 22:36:46 +01:00
return timeout_flag | | script_promise . state ( ) ! = JS : : Promise : : State : : Pending ;
2022-11-02 18:02:19 +00:00
} ) ;
// 10. Upon fulfillment of scriptPromise with value v, resolve promise with value v.
if ( script_promise . state ( ) = = JS : : Promise : : State : : Fulfilled )
2023-05-30 15:23:46 +03:00
WebIDL : : resolve_promise ( realm , promise_capability , script_promise . result ( ) ) ;
2022-11-02 18:02:19 +00:00
// 11. Upon rejection of scriptPromise with value r, reject promise with value r.
if ( script_promise . state ( ) = = JS : : Promise : : State : : Rejected )
2023-05-30 15:23:46 +03:00
WebIDL : : reject_promise ( realm , promise_capability , script_promise . result ( ) ) ;
2024-08-30 22:36:46 +01:00
} ) ;
2022-11-02 18:02:19 +00:00
2024-08-30 22:36:46 +01:00
// 9. Wait until promise is resolved, or timer's timeout fired flag is set, whichever occurs first.
2023-05-30 15:23:46 +03:00
vm . custom_data ( ) - > spin_event_loop_until ( [ & ] {
2024-08-30 22:36:46 +01:00
return timeout_flag | | promise - > state ( ) ! = JS : : Promise : : State : : Pending ;
2023-05-30 15:23:46 +03:00
} ) ;
2024-08-30 22:36:46 +01:00
// 10. If promise is still pending and timer's timeout fired flag is set, return error with error code script
if ( timeout_flag & & promise - > state ( ) = = JS : : Promise : : State : : Pending ) {
2023-06-17 18:07:27 +02:00
auto error_object = JsonObject { } ;
error_object . set ( " name " , " Error " ) ;
2024-08-30 22:36:46 +01:00
error_object . set ( " message " , " Script Timeout " ) ;
2023-06-17 18:07:27 +02:00
return { ExecuteScriptResultType : : Timeout , move ( error_object ) } ;
}
2023-05-30 15:23:46 +03:00
auto json_value_or_error = json_clone ( realm , promise - > result ( ) ) ;
2022-11-02 18:02:19 +00:00
if ( json_value_or_error . is_error ( ) ) {
auto error_object = JsonObject { } ;
error_object . set ( " name " , " Error " ) ;
error_object . set ( " message " , " Could not clone result value " ) ;
return { ExecuteScriptResultType : : JavaScriptError , move ( error_object ) } ;
}
2023-05-30 15:23:46 +03:00
2024-08-30 22:36:46 +01:00
// 11. If promise is fulfilled with value v, let result be JSON clone with session and v, and return success with data result.
2023-05-30 15:23:46 +03:00
if ( promise - > state ( ) = = JS : : Promise : : State : : Fulfilled ) {
return { ExecuteScriptResultType : : PromiseResolved , json_value_or_error . release_value ( ) } ;
}
2024-08-30 22:36:46 +01:00
// 12. If promise is rejected with reason r, let result be JSON clone with session and r, and return error with error code javascript error and data result.
2023-05-30 15:23:46 +03:00
if ( promise - > state ( ) = = JS : : Promise : : State : : Rejected ) {
return { ExecuteScriptResultType : : PromiseRejected , json_value_or_error . release_value ( ) } ;
}
VERIFY_NOT_REACHED ( ) ;
2022-11-02 18:02:19 +00:00
}
}