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>
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-02-28 17:45:49 +00:00
# include <LibWeb/Bindings/HostDefined.h>
2022-09-22 23:36:09 +01:00
# include <LibWeb/WebIDL/ExceptionOr.h>
# include <LibWeb/WebIDL/Promise.h>
namespace Web : : WebIDL {
// https://webidl.spec.whatwg.org/#a-new-promise
2023-02-13 11:58:39 +01:00
JS : : NonnullGCPtr < 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
2023-02-13 11:58:39 +01:00
JS : : NonnullGCPtr < 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
2023-02-13 11:58:39 +01:00
JS : : NonnullGCPtr < 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
2023-02-13 11:58:39 +01:00
JS : : NonnullGCPtr < JS : : Promise > react_to_promise ( Promise const & promise , Optional < ReactionSteps > on_fulfilled_callback , Optional < 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.
auto result = on_fulfilled_callback . has_value ( )
? TRY ( Bindings : : throw_dom_exception_if_needed ( vm , [ & ] { return ( * on_fulfilled_callback ) ( value ) ; } ) )
: value ;
// 3. Return result, converted to an ECMAScript value.
return result ;
} ;
// 2. Let onFulfilled be CreateBuiltinFunction(onFulfilledSteps, « »):
2022-12-13 20:49:50 +00: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.
auto result = on_rejected_callback . has_value ( )
? TRY ( Bindings : : throw_dom_exception_if_needed ( vm , [ & ] { return ( * on_rejected_callback ) ( 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, « »):
2022-12-13 20:49:50 +00: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).
2023-02-13 11:58:39 +01:00
auto promise_object = verify_cast < JS : : Promise > ( promise . promise ( ) . ptr ( ) ) ;
auto value = promise_object - > perform_then ( on_fulfilled , on_rejected , new_capability ) ;
2022-09-22 23:36:09 +01:00
return verify_cast < JS : : Promise > ( value . as_object ( ) ) ;
}
// https://webidl.spec.whatwg.org/#upon-fulfillment
2023-02-13 11:58:39 +01:00
JS : : NonnullGCPtr < JS : : Promise > upon_fulfillment ( Promise const & promise , 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:
[ steps = move ( steps ) ] ( auto value ) {
// 1. Perform steps with v.
// NOTE: The `return` is not immediately obvious, but `steps` may be something like
// "Return the result of ...", which we also need to do _here_.
return steps ( value ) ;
} ,
{ } ) ;
}
// https://webidl.spec.whatwg.org/#upon-rejection
2023-02-13 11:58:39 +01:00
JS : : NonnullGCPtr < JS : : Promise > upon_rejection ( Promise const & promise , 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:
[ steps = move ( steps ) ] ( auto reason ) {
// 1. Perform steps with r.
// NOTE: The `return` is not immediately obvious, but `steps` may be something like
// "Return the result of ...", which we also need to do _here_.
return steps ( reason ) ;
} ) ;
}
// 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.
2023-02-13 11:58:39 +01:00
auto promise_object = verify_cast < JS : : Promise > ( promise . promise ( ) . ptr ( ) ) ;
promise_object - > set_is_handled ( ) ;
2022-09-22 23:36:09 +01:00
}
}