Serenity Operating System
at master 184 lines 7.9 kB view raw
1/* 2 * Copyright (c) 2022, Linus Groh <linusg@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Function.h> 8#include <AK/TypeCasts.h> 9#include <LibJS/Runtime/PromiseCapability.h> 10#include <LibJS/Runtime/PromiseConstructor.h> 11#include <LibJS/Runtime/Realm.h> 12#include <LibWeb/Bindings/ExceptionOrUtils.h> 13#include <LibWeb/Bindings/HostDefined.h> 14#include <LibWeb/WebIDL/ExceptionOr.h> 15#include <LibWeb/WebIDL/Promise.h> 16 17namespace Web::WebIDL { 18 19// https://webidl.spec.whatwg.org/#a-new-promise 20JS::NonnullGCPtr<Promise> create_promise(JS::Realm& realm) 21{ 22 auto& vm = realm.vm(); 23 24 // 1. Let constructor be realm.[[Intrinsics]].[[%Promise%]]. 25 auto* constructor = realm.intrinsics().promise_constructor(); 26 27 // Return ? NewPromiseCapability(constructor). 28 // NOTE: When called with %Promise%, NewPromiseCapability can't throw. 29 return MUST(JS::new_promise_capability(vm, constructor)); 30} 31 32// https://webidl.spec.whatwg.org/#a-promise-resolved-with 33JS::NonnullGCPtr<Promise> create_resolved_promise(JS::Realm& realm, JS::Value value) 34{ 35 auto& vm = realm.vm(); 36 37 // 1. Let value be the result of converting x to an ECMAScript value. 38 39 // 2. Let constructor be realm.[[Intrinsics]].[[%Promise%]]. 40 auto* constructor = realm.intrinsics().promise_constructor(); 41 42 // 3. Let promiseCapability be ? NewPromiseCapability(constructor). 43 // NOTE: When called with %Promise%, NewPromiseCapability can't throw. 44 auto promise_capability = MUST(JS::new_promise_capability(vm, constructor)); 45 46 // 4. Perform ! Call(promiseCapability.[[Resolve]], undefined, « value »). 47 MUST(JS::call(vm, *promise_capability->resolve(), JS::js_undefined(), value)); 48 49 // 5. Return promiseCapability. 50 return promise_capability; 51} 52 53// https://webidl.spec.whatwg.org/#a-promise-rejected-with 54JS::NonnullGCPtr<Promise> create_rejected_promise(JS::Realm& realm, JS::Value reason) 55{ 56 auto& vm = realm.vm(); 57 58 // 1. Let constructor be realm.[[Intrinsics]].[[%Promise%]]. 59 auto* constructor = realm.intrinsics().promise_constructor(); 60 61 // 2. Let promiseCapability be ? NewPromiseCapability(constructor). 62 // NOTE: When called with %Promise%, NewPromiseCapability can't throw. 63 auto promise_capability = MUST(JS::new_promise_capability(vm, constructor)); 64 65 // 3. Perform ! Call(promiseCapability.[[Reject]], undefined, « r »). 66 MUST(JS::call(vm, *promise_capability->reject(), JS::js_undefined(), reason)); 67 68 // 4. Return promiseCapability. 69 return promise_capability; 70} 71 72// https://webidl.spec.whatwg.org/#resolve 73void resolve_promise(JS::Realm& realm, Promise const& promise, JS::Value value) 74{ 75 auto& vm = realm.vm(); 76 77 // 1. If x is not given, then let it be the undefined value. 78 // NOTE: This is done via the default argument. 79 80 // 2. Let value be the result of converting x to an ECMAScript value. 81 // 3. Perform ! Call(p.[[Resolve]], undefined, « value »). 82 MUST(JS::call(vm, *promise.resolve(), JS::js_undefined(), value)); 83} 84 85// https://webidl.spec.whatwg.org/#reject 86void reject_promise(JS::Realm& realm, Promise const& promise, JS::Value reason) 87{ 88 auto& vm = realm.vm(); 89 90 // 1. Perform ! Call(p.[[Reject]], undefined, « r »). 91 MUST(JS::call(vm, *promise.reject(), JS::js_undefined(), reason)); 92} 93 94// https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled 95JS::NonnullGCPtr<JS::Promise> react_to_promise(Promise const& promise, Optional<ReactionSteps> on_fulfilled_callback, Optional<ReactionSteps> on_rejected_callback) 96{ 97 auto& realm = promise.promise()->shape().realm(); 98 auto& vm = realm.vm(); 99 100 // 1. Let onFulfilledSteps be the following steps given argument V: 101 auto on_fulfilled_steps = [on_fulfilled_callback = move(on_fulfilled_callback)](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> { 102 // 1. Let value be the result of converting V to an IDL value of type T. 103 auto value = vm.argument(0); 104 105 // 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. 106 auto result = on_fulfilled_callback.has_value() 107 ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return (*on_fulfilled_callback)(value); })) 108 : value; 109 110 // 3. Return result, converted to an ECMAScript value. 111 return result; 112 }; 113 114 // 2. Let onFulfilled be CreateBuiltinFunction(onFulfilledSteps, « »): 115 auto on_fulfilled = JS::NativeFunction::create(realm, move(on_fulfilled_steps), 1, ""); 116 117 // 3. Let onRejectedSteps be the following steps given argument R: 118 auto on_rejected_steps = [&realm, on_rejected_callback = move(on_rejected_callback)](JS::VM& vm) -> JS::ThrowCompletionOr<JS::Value> { 119 // 1. Let reason be the result of converting R to an IDL value of type any. 120 auto reason = vm.argument(0); 121 122 // 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. 123 auto result = on_rejected_callback.has_value() 124 ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return (*on_rejected_callback)(reason); })) 125 : WebIDL::create_rejected_promise(realm, reason)->promise(); 126 127 // 3. Return result, converted to an ECMAScript value. 128 return result; 129 }; 130 131 // 4. Let onRejected be CreateBuiltinFunction(onRejectedSteps, « »): 132 auto on_rejected = JS::NativeFunction::create(realm, move(on_rejected_steps), 1, ""); 133 134 // 5. Let constructor be promise.[[Promise]].[[Realm]].[[Intrinsics]].[[%Promise%]]. 135 auto* constructor = realm.intrinsics().promise_constructor(); 136 137 // 6. Let newCapability be ? NewPromiseCapability(constructor). 138 // NOTE: When called with %Promise%, NewPromiseCapability can't throw. 139 auto new_capability = MUST(JS::new_promise_capability(vm, constructor)); 140 141 // 7. Return PerformPromiseThen(promise.[[Promise]], onFulfilled, onRejected, newCapability). 142 auto promise_object = verify_cast<JS::Promise>(promise.promise().ptr()); 143 auto value = promise_object->perform_then(on_fulfilled, on_rejected, new_capability); 144 return verify_cast<JS::Promise>(value.as_object()); 145} 146 147// https://webidl.spec.whatwg.org/#upon-fulfillment 148JS::NonnullGCPtr<JS::Promise> upon_fulfillment(Promise const& promise, ReactionSteps steps) 149{ 150 // 1. Return the result of reacting to promise: 151 return react_to_promise(promise, 152 // - If promise was fulfilled with value v, then: 153 [steps = move(steps)](auto value) { 154 // 1. Perform steps with v. 155 // NOTE: The `return` is not immediately obvious, but `steps` may be something like 156 // "Return the result of ...", which we also need to do _here_. 157 return steps(value); 158 }, 159 {}); 160} 161 162// https://webidl.spec.whatwg.org/#upon-rejection 163JS::NonnullGCPtr<JS::Promise> upon_rejection(Promise const& promise, ReactionSteps steps) 164{ 165 // 1. Return the result of reacting to promise: 166 return react_to_promise(promise, {}, 167 // - If promise was rejected with reason r, then: 168 [steps = move(steps)](auto reason) { 169 // 1. Perform steps with r. 170 // NOTE: The `return` is not immediately obvious, but `steps` may be something like 171 // "Return the result of ...", which we also need to do _here_. 172 return steps(reason); 173 }); 174} 175 176// https://webidl.spec.whatwg.org/#mark-a-promise-as-handled 177void mark_promise_as_handled(Promise const& promise) 178{ 179 // To mark as handled a Promise<T> promise, set promise.[[Promise]].[[PromiseIsHandled]] to true. 180 auto promise_object = verify_cast<JS::Promise>(promise.promise().ptr()); 181 promise_object->set_is_handled(); 182} 183 184}