2022-09-22 23:36:09 +01:00
/*
2023-04-13 00:47:15 +02:00
* Copyright ( c ) 2022 - 2023 , Linus Groh < linusg @ serenityos . org >
2022-09-22 23:36:09 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/Function.h>
2022-11-23 13:41:50 +01:00
# include <AK/TypeCasts.h>
2024-11-15 04:01:23 +13:00
# include <LibGC/Function.h>
2022-10-02 10:59:22 +01:00
# include <LibJS/Runtime/PromiseCapability.h>
2022-09-22 23:36:09 +01:00
# include <LibJS/Runtime/PromiseConstructor.h>
# include <LibJS/Runtime/Realm.h>
# include <LibWeb/Bindings/ExceptionOrUtils.h>
2023-09-22 18:16:14 -06:00
# include <LibWeb/HTML/Scripting/ExceptionReporter.h>
2022-09-22 23:36:09 +01:00
# include <LibWeb/WebIDL/Promise.h>
namespace Web : : WebIDL {
// https://webidl.spec.whatwg.org/#a-new-promise
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > create_promise ( JS : : Realm & realm )
2022-09-22 23:36:09 +01:00
{
auto & vm = realm . vm ( ) ;
// 1. Let constructor be realm.[[Intrinsics]].[[%Promise%]].
2023-04-13 00:47:15 +02:00
auto constructor = realm . intrinsics ( ) . promise_constructor ( ) ;
2022-09-22 23:36:09 +01:00
// Return ? NewPromiseCapability(constructor).
// NOTE: When called with %Promise%, NewPromiseCapability can't throw.
return MUST ( JS : : new_promise_capability ( vm , constructor ) ) ;
}
// https://webidl.spec.whatwg.org/#a-promise-resolved-with
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > create_resolved_promise ( JS : : Realm & realm , JS : : Value value )
2022-09-22 23:36:09 +01:00
{
auto & vm = realm . vm ( ) ;
// 1. Let value be the result of converting x to an ECMAScript value.
// 2. Let constructor be realm.[[Intrinsics]].[[%Promise%]].
2023-04-13 00:47:15 +02:00
auto constructor = realm . intrinsics ( ) . promise_constructor ( ) ;
2022-09-22 23:36:09 +01:00
// 3. Let promiseCapability be ? NewPromiseCapability(constructor).
// NOTE: When called with %Promise%, NewPromiseCapability can't throw.
auto promise_capability = MUST ( JS : : new_promise_capability ( vm , constructor ) ) ;
// 4. Perform ! Call(promiseCapability.[[Resolve]], undefined, « value »).
2022-10-02 12:11:30 +01:00
MUST ( JS : : call ( vm , * promise_capability - > resolve ( ) , JS : : js_undefined ( ) , value ) ) ;
2022-09-22 23:36:09 +01:00
// 5. Return promiseCapability.
return promise_capability ;
}
// https://webidl.spec.whatwg.org/#a-promise-rejected-with
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > create_rejected_promise ( JS : : Realm & realm , JS : : Value reason )
2022-09-22 23:36:09 +01:00
{
auto & vm = realm . vm ( ) ;
// 1. Let constructor be realm.[[Intrinsics]].[[%Promise%]].
2023-04-13 00:47:15 +02:00
auto constructor = realm . intrinsics ( ) . promise_constructor ( ) ;
2022-09-22 23:36:09 +01:00
// 2. Let promiseCapability be ? NewPromiseCapability(constructor).
// NOTE: When called with %Promise%, NewPromiseCapability can't throw.
auto promise_capability = MUST ( JS : : new_promise_capability ( vm , constructor ) ) ;
// 3. Perform ! Call(promiseCapability.[[Reject]], undefined, « r »).
2022-10-02 12:11:30 +01:00
MUST ( JS : : call ( vm , * promise_capability - > reject ( ) , JS : : js_undefined ( ) , reason ) ) ;
2022-09-22 23:36:09 +01:00
// 4. Return promiseCapability.
return promise_capability ;
}
// https://webidl.spec.whatwg.org/#resolve
2023-02-28 17:45:49 +00:00
void resolve_promise ( JS : : Realm & realm , Promise const & promise , JS : : Value value )
2022-09-22 23:36:09 +01:00
{
2023-02-28 17:45:49 +00:00
auto & vm = realm . vm ( ) ;
2022-09-22 23:36:09 +01:00
// 1. If x is not given, then let it be the undefined value.
// NOTE: This is done via the default argument.
// 2. Let value be the result of converting x to an ECMAScript value.
// 3. Perform ! Call(p.[[Resolve]], undefined, « value »).
2023-02-13 11:58:39 +01:00
MUST ( JS : : call ( vm , * promise . resolve ( ) , JS : : js_undefined ( ) , value ) ) ;
2022-09-22 23:36:09 +01:00
}
// https://webidl.spec.whatwg.org/#reject
2023-02-28 17:45:49 +00:00
void reject_promise ( JS : : Realm & realm , Promise const & promise , JS : : Value reason )
2022-09-22 23:36:09 +01:00
{
2023-02-28 17:45:49 +00:00
auto & vm = realm . vm ( ) ;
2022-09-22 23:36:09 +01:00
// 1. Perform ! Call(p.[[Reject]], undefined, « r »).
2023-02-13 11:58:39 +01:00
MUST ( JS : : call ( vm , * promise . reject ( ) , JS : : js_undefined ( ) , reason ) ) ;
2022-09-22 23:36:09 +01:00
}
// https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > react_to_promise ( Promise const & promise , GC : : Ptr < ReactionSteps > on_fulfilled_callback , GC : : Ptr < ReactionSteps > on_rejected_callback )
2022-09-22 23:36:09 +01:00
{
2023-02-13 11:58:39 +01:00
auto & realm = promise . promise ( ) - > shape ( ) . realm ( ) ;
2022-09-22 23:36:09 +01:00
auto & vm = realm . vm ( ) ;
// 1. Let onFulfilledSteps be the following steps given argument V:
auto on_fulfilled_steps = [ on_fulfilled_callback = move ( on_fulfilled_callback ) ] ( JS : : VM & vm ) - > JS : : ThrowCompletionOr < JS : : Value > {
// 1. Let value be the result of converting V to an IDL value of type T.
auto value = vm . argument ( 0 ) ;
// 2. If there is a set of steps to be run if the promise was fulfilled, then let result be the result of performing them, given value if T is not undefined. Otherwise, let result be value.
2024-04-03 16:26:59 +02:00
auto result = on_fulfilled_callback
? TRY ( Bindings : : throw_dom_exception_if_needed ( vm , [ & ] { return on_fulfilled_callback - > function ( ) ( value ) ; } ) )
2022-09-22 23:36:09 +01:00
: value ;
// 3. Return result, converted to an ECMAScript value.
return result ;
} ;
// 2. Let onFulfilled be CreateBuiltinFunction(onFulfilledSteps, « »):
2025-03-16 21:25:29 -05:00
auto on_fulfilled = JS : : NativeFunction : : create ( realm , move ( on_fulfilled_steps ) , 1 ) ;
2022-09-22 23:36:09 +01:00
// 3. Let onRejectedSteps be the following steps given argument R:
auto on_rejected_steps = [ & realm , on_rejected_callback = move ( on_rejected_callback ) ] ( JS : : VM & vm ) - > JS : : ThrowCompletionOr < JS : : Value > {
// 1. Let reason be the result of converting R to an IDL value of type any.
auto reason = vm . argument ( 0 ) ;
// 2. If there is a set of steps to be run if the promise was rejected, then let result be the result of performing them, given reason. Otherwise, let result be a promise rejected with reason.
2024-04-03 16:26:59 +02:00
auto result = on_rejected_callback
? TRY ( Bindings : : throw_dom_exception_if_needed ( vm , [ & ] { return on_rejected_callback - > function ( ) ( reason ) ; } ) )
2022-10-02 12:11:30 +01:00
: WebIDL : : create_rejected_promise ( realm , reason ) - > promise ( ) ;
2022-09-22 23:36:09 +01:00
// 3. Return result, converted to an ECMAScript value.
return result ;
} ;
// 4. Let onRejected be CreateBuiltinFunction(onRejectedSteps, « »):
2025-03-16 21:25:29 -05:00
auto on_rejected = JS : : NativeFunction : : create ( realm , move ( on_rejected_steps ) , 1 ) ;
2022-09-22 23:36:09 +01:00
// 5. Let constructor be promise.[[Promise]].[[Realm]].[[Intrinsics]].[[%Promise%]].
2023-04-13 00:47:15 +02:00
auto constructor = realm . intrinsics ( ) . promise_constructor ( ) ;
2022-09-22 23:36:09 +01:00
// 6. Let newCapability be ? NewPromiseCapability(constructor).
// NOTE: When called with %Promise%, NewPromiseCapability can't throw.
auto new_capability = MUST ( JS : : new_promise_capability ( vm , constructor ) ) ;
// 7. Return PerformPromiseThen(promise.[[Promise]], onFulfilled, onRejected, newCapability).
2024-10-25 12:38:19 -06:00
// FIXME: https://github.com/whatwg/webidl/issues/1443
// Returning newCapability instead of newCapability.[[Promise].
2025-01-21 09:12:05 -05:00
auto promise_object = as < JS : : Promise > ( promise . promise ( ) . ptr ( ) ) ;
2023-02-13 11:58:39 +01:00
auto value = promise_object - > perform_then ( on_fulfilled , on_rejected , new_capability ) ;
2024-10-25 12:38:19 -06:00
VERIFY ( value = = new_capability - > promise ( ) ) ;
return new_capability ;
2022-09-22 23:36:09 +01:00
}
// https://webidl.spec.whatwg.org/#upon-fulfillment
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > upon_fulfillment ( Promise const & promise , GC : : Ref < ReactionSteps > steps )
2022-09-22 23:36:09 +01:00
{
// 1. Return the result of reacting to promise:
2023-02-13 11:58:39 +01:00
return react_to_promise ( promise ,
2022-09-22 23:36:09 +01:00
// - If promise was fulfilled with value v, then:
2024-04-03 16:26:59 +02:00
// 1. Perform steps with v.
steps ,
2022-09-22 23:36:09 +01:00
{ } ) ;
}
// https://webidl.spec.whatwg.org/#upon-rejection
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > upon_rejection ( Promise const & promise , GC : : Ref < ReactionSteps > steps )
2022-09-22 23:36:09 +01:00
{
// 1. Return the result of reacting to promise:
2023-02-13 11:58:39 +01:00
return react_to_promise ( promise , { } ,
2022-09-22 23:36:09 +01:00
// - If promise was rejected with reason r, then:
2024-04-03 16:26:59 +02:00
// 1. Perform steps with r.
steps ) ;
2022-09-22 23:36:09 +01:00
}
// https://webidl.spec.whatwg.org/#mark-a-promise-as-handled
2023-02-13 11:58:39 +01:00
void mark_promise_as_handled ( Promise const & promise )
2022-09-22 23:36:09 +01:00
{
// To mark as handled a Promise<T> promise, set promise.[[Promise]].[[PromiseIsHandled]] to true.
2025-01-21 09:12:05 -05:00
auto promise_object = as < JS : : Promise > ( promise . promise ( ) . ptr ( ) ) ;
2023-02-13 11:58:39 +01:00
promise_object - > set_is_handled ( ) ;
2022-09-22 23:36:09 +01:00
}
2023-11-03 18:54:12 -06:00
struct WaitForAllResults : JS : : Cell {
2024-11-15 04:01:23 +13:00
GC_CELL ( WaitForAllResults , JS : : Cell ) ;
GC_DECLARE_ALLOCATOR ( WaitForAllResults ) ;
2023-11-03 18:54:12 -06:00
2024-11-15 04:01:23 +13:00
WaitForAllResults ( GC : : Ref < GC : : Function < void ( Vector < JS : : Value > const & ) > > s , size_t t )
2023-11-03 18:54:12 -06:00
: success_steps ( s )
, total ( t )
{
// 8. Let result be a list containing total null values.
result . ensure_capacity ( total ) ;
for ( size_t i = 0 ; i < total ; + + i )
result . unchecked_append ( JS : : js_null ( ) ) ;
}
virtual void visit_edges ( JS : : Cell : : Visitor & visitor ) override
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( success_steps ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( result ) ;
2023-11-03 18:54:12 -06:00
}
2024-11-15 04:01:23 +13:00
GC : : Ref < GC : : Function < void ( Vector < JS : : Value > const & ) > > success_steps ;
2023-11-03 18:54:12 -06:00
Vector < JS : : Value > result ;
size_t total = 0 ;
size_t fulfilled_count = 0 ;
} ;
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( WaitForAllResults ) ;
2024-04-06 10:16:04 -07:00
2023-09-22 18:16:14 -06:00
// https://webidl.spec.whatwg.org/#wait-for-all
2024-11-15 04:01:23 +13:00
void wait_for_all ( JS : : Realm & realm , Vector < GC : : Ref < Promise > > const & promises , Function < void ( Vector < JS : : Value > const & ) > success_steps , Function < void ( JS : : Value ) > failure_steps )
2023-09-22 18:16:14 -06:00
{
// FIXME: Fix spec typo, fullfilled --> fulfilled
// 1. Let fullfilledCount be 0.
2023-11-03 18:54:12 -06:00
// Handled later in WaitForAllResults
2023-09-22 18:16:14 -06:00
// 2. Let rejected be false.
auto rejected = false ;
// 3. Let rejectionHandlerSteps be the following steps given arg:
2024-11-15 04:01:23 +13:00
auto rejection_handler_steps = [ rejected , failure_steps = GC : : create_function ( realm . heap ( ) , move ( failure_steps ) ) ] ( JS : : VM & vm ) mutable - > JS : : ThrowCompletionOr < JS : : Value > {
2023-09-22 18:16:14 -06:00
// 1. If rejected is true, abort these steps.
if ( rejected )
return JS : : js_undefined ( ) ;
// 2. Set rejected to true.
rejected = true ;
// 3. Perform failureSteps given arg.
2023-11-03 18:54:12 -06:00
failure_steps - > function ( ) ( vm . argument ( 0 ) ) ;
2023-09-22 18:16:14 -06:00
return JS : : js_undefined ( ) ;
} ;
// 4. Let rejectionHandler be CreateBuiltinFunction(rejectionHandlerSteps, « »):
2025-03-16 21:25:29 -05:00
auto rejection_handler = JS : : NativeFunction : : create ( realm , move ( rejection_handler_steps ) , 1 ) ;
2023-09-22 18:16:14 -06:00
// 5. Let total be promises’ s size.
auto total = promises . size ( ) ;
// 6. If total is 0, then:
if ( total = = 0 ) {
// 1. Queue a microtask to perform successSteps given « ».
2024-11-15 04:01:23 +13:00
HTML : : queue_a_microtask ( nullptr , GC : : create_function ( realm . heap ( ) , [ success_steps = GC : : create_function ( realm . heap ( ) , move ( success_steps ) ) ] {
2023-11-03 18:54:12 -06:00
success_steps - > function ( ) ( { } ) ;
2024-04-13 16:32:39 +02:00
} ) ) ;
2023-09-22 18:16:14 -06:00
// 2. Return.
return ;
}
// 7. Let index be 0.
auto index = 0 ;
// 8. Let result be a list containing total null values.
2023-11-03 18:54:12 -06:00
// Handled in WaitForAllResults
2024-11-15 04:01:23 +13:00
auto results = realm . create < WaitForAllResults > ( GC : : create_function ( realm . heap ( ) , move ( success_steps ) ) , total ) ;
2023-09-22 18:16:14 -06:00
// 9. For each promise of promises:
for ( auto const & promise : promises ) {
// 1. Let promiseIndex be index.
auto promise_index = index ;
// FIXME: This should be fulfillmentHandlerSteps
// 2. Let fulfillmentHandler be the following steps given arg:
2023-11-03 18:54:12 -06:00
auto fulfillment_handler_steps = [ results , promise_index ] ( JS : : VM & vm ) - > JS : : ThrowCompletionOr < JS : : Value > {
2023-09-22 18:16:14 -06:00
auto arg = vm . argument ( 0 ) ;
// 1. Set result[promiseIndex] to arg.
2023-11-03 18:54:12 -06:00
results - > result [ promise_index ] = arg ;
2023-09-22 18:16:14 -06:00
// 2. Set fullfilledCount to fullfilledCount + 1.
2023-11-03 18:54:12 -06:00
+ + results - > fulfilled_count ;
2023-09-22 18:16:14 -06:00
// 3. If fullfilledCount equals total, then perform successSteps given result.
2023-11-03 18:54:12 -06:00
if ( results - > fulfilled_count = = results - > total )
results - > success_steps - > function ( ) ( results - > result ) ;
2023-09-22 18:16:14 -06:00
return JS : : js_undefined ( ) ;
} ;
// 3. Let fulfillmentHandler be CreateBuiltinFunction(fulfillmentHandler, « »):
2025-03-16 21:25:29 -05:00
auto fulfillment_handler = JS : : NativeFunction : : create ( realm , move ( fulfillment_handler_steps ) , 1 ) ;
2023-09-22 18:16:14 -06:00
// 4. Perform PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler).
static_cast < JS : : Promise & > ( * promise - > promise ( ) ) . perform_then ( fulfillment_handler , rejection_handler , nullptr ) ;
// 5. Set index to index + 1.
+ + index ;
}
}
2024-11-15 04:01:23 +13:00
GC : : Ref < Promise > create_rejected_promise_from_exception ( JS : : Realm & realm , Exception exception )
2024-03-08 15:37:28 +00:00
{
2024-11-04 14:37:27 +01:00
auto throw_completion = Bindings : : exception_to_throw_completion ( realm . vm ( ) , move ( exception ) ) ;
2024-10-25 12:38:19 -06:00
return WebIDL : : create_rejected_promise ( realm , * throw_completion . value ( ) ) ;
2024-03-08 15:37:28 +00:00
}
2022-09-22 23:36:09 +01:00
}