From ffac32d20e6e47381df6c73003cc32ede2ebf19b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 3 Apr 2024 16:26:59 +0200 Subject: [PATCH] LibWeb: Use JS::HeapFunction for WebIDL promise reaction steps Switching away from SafeFunction immediately backfired here, as we're dealing with two layers of captures, not one. Let's do the correct fix, which is to use HeapFunction. This makes the API and its behavior explicit, and keeps captures alive as long as the HeapFunction is alive. Fixes #23819. --- Userland/Libraries/LibWeb/FileAPI/Blob.cpp | 8 +- .../LibWeb/HTML/Scripting/Fetching.cpp | 8 +- .../LibWeb/Streams/AbstractOperations.cpp | 90 +++++++++---------- Userland/Libraries/LibWeb/WebIDL/Promise.cpp | 30 +++---- Userland/Libraries/LibWeb/WebIDL/Promise.h | 8 +- 5 files changed, 68 insertions(+), 76 deletions(-) diff --git a/Userland/Libraries/LibWeb/FileAPI/Blob.cpp b/Userland/Libraries/LibWeb/FileAPI/Blob.cpp index fadcedadff8..8943955be22 100644 --- a/Userland/Libraries/LibWeb/FileAPI/Blob.cpp +++ b/Userland/Libraries/LibWeb/FileAPI/Blob.cpp @@ -375,7 +375,7 @@ WebIDL::ExceptionOr> Blob::text() auto promise = TRY(reader->read_all_bytes_deprecated()); // 4. Return the result of transforming promise by a fulfillment handler that returns the result of running UTF-8 decode on its first argument. - return WebIDL::upon_fulfillment(*promise, [&](auto const& first_argument) -> WebIDL::ExceptionOr { + return WebIDL::upon_fulfillment(*promise, JS::create_heap_function(heap(), [&vm](JS::Value first_argument) -> WebIDL::ExceptionOr { auto const& object = first_argument.as_object(); VERIFY(is(object)); auto const& buffer = static_cast(object).buffer(); @@ -383,7 +383,7 @@ WebIDL::ExceptionOr> Blob::text() auto decoder = TextCodec::decoder_for("UTF-8"sv); auto utf8_text = TRY_OR_THROW_OOM(vm, TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, buffer)); return JS::PrimitiveString::create(vm, move(utf8_text)); - }); + })); } // https://w3c.github.io/FileAPI/#dom-blob-arraybuffer @@ -404,13 +404,13 @@ WebIDL::ExceptionOr> Blob::array_buffer() auto promise = TRY(reader->read_all_bytes_deprecated()); // 4. Return the result of transforming promise by a fulfillment handler that returns a new ArrayBuffer whose contents are its first argument. - return WebIDL::upon_fulfillment(*promise, [&](auto const& first_argument) -> WebIDL::ExceptionOr { + return WebIDL::upon_fulfillment(*promise, JS::create_heap_function(heap(), [&realm](JS::Value first_argument) -> WebIDL::ExceptionOr { auto const& object = first_argument.as_object(); VERIFY(is(object)); auto const& buffer = static_cast(object).buffer(); return JS::ArrayBuffer::create(realm, buffer); - }); + })); } } diff --git a/Userland/Libraries/LibWeb/HTML/Scripting/Fetching.cpp b/Userland/Libraries/LibWeb/HTML/Scripting/Fetching.cpp index 94d680a217b..7b4433096f7 100644 --- a/Userland/Libraries/LibWeb/HTML/Scripting/Fetching.cpp +++ b/Userland/Libraries/LibWeb/HTML/Scripting/Fetching.cpp @@ -828,7 +828,7 @@ void fetch_descendants_of_and_link_a_module_script(JS::Realm& realm, auto& loading_promise = record->load_requested_modules(state); // 6. Upon fulfillment of loadingPromise, run the following steps: - WebIDL::upon_fulfillment(loading_promise, [&realm, record, &module_script, on_complete](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(loading_promise, JS::create_heap_function(realm.heap(), [&realm, record, &module_script, on_complete](JS::Value) -> WebIDL::ExceptionOr { // 1. Perform record.Link(). auto linking_result = record->link(realm.vm()); @@ -840,10 +840,10 @@ void fetch_descendants_of_and_link_a_module_script(JS::Realm& realm, on_complete->function()(module_script); return JS::js_undefined(); - }); + })); // 7. Upon rejection of loadingPromise, run the following steps: - WebIDL::upon_rejection(loading_promise, [state, &module_script, on_complete](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(loading_promise, JS::create_heap_function(realm.heap(), [state, &module_script, on_complete](JS::Value) -> WebIDL::ExceptionOr { // 1. If state.[[ParseError]] is not null, set moduleScript's error to rethrow to state.[[ParseError]] and run // onComplete given moduleScript. if (!state->parse_error.is_null()) { @@ -857,7 +857,7 @@ void fetch_descendants_of_and_link_a_module_script(JS::Realm& realm, } return JS::js_undefined(); - }); + })); fetch_client.clean_up_after_running_callback(); realm.vm().pop_execution_context(); diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp index 4e05a8cb7ec..2f500322d2d 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -127,7 +127,7 @@ WebIDL::ExceptionOr> readable_stream_cancel(Re // 8. Return the result of reacting to sourceCancelPromise with a fulfillment step that returns undefined. auto react_result = WebIDL::react_to_promise(*source_cancel_promise, - [](auto const&) -> WebIDL::ExceptionOr { return JS::js_undefined(); }, + JS::create_heap_function(stream.heap(), [](JS::Value) -> WebIDL::ExceptionOr { return JS::js_undefined(); }), {}); return WebIDL::create_resolved_promise(realm, react_result); @@ -535,7 +535,7 @@ WebIDL::ExceptionOr readable_stream_default_tee(JS::Realm& r params->branch2 = MUST(create_readable_stream(realm, start_algorithm, pull_algorithm, cancel2_algorithm)); // 19. Upon rejection of reader.[[closedPromise]] with reason r, - WebIDL::upon_rejection(*reader->closed_promise_capability(), [&realm, params, cancel_promise](auto reason) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*reader->closed_promise_capability(), JS::create_heap_function(realm.heap(), [&realm, params, cancel_promise](JS::Value reason) -> WebIDL::ExceptionOr { auto controller1 = params->branch1->controller()->get>(); auto controller2 = params->branch2->controller()->get>(); @@ -551,7 +551,7 @@ WebIDL::ExceptionOr readable_stream_default_tee(JS::Realm& r } return JS::js_undefined(); - }); + })); // 20. Return « branch1, branch2 ». return ReadableStreamPair { *params->branch1, *params->branch2 }; @@ -964,7 +964,7 @@ WebIDL::ExceptionOr readable_byte_stream_tee(JS::Realm& real // 1. Upon rejection of thisReader.[[closedPromise]] with reason r, auto closed_promise = this_reader.visit([](auto const& underlying_reader) { return underlying_reader->closed_promise_capability(); }); - WebIDL::upon_rejection(*closed_promise, [&realm, this_reader, params, cancel_promise](auto reason) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*closed_promise, JS::create_heap_function(realm.heap(), [&realm, this_reader, params, cancel_promise](JS::Value reason) -> WebIDL::ExceptionOr { auto controller1 = params->branch1->controller()->get>(); auto controller2 = params->branch2->controller()->get>(); @@ -985,7 +985,7 @@ WebIDL::ExceptionOr readable_byte_stream_tee(JS::Realm& real } return JS::js_undefined(); - }); + })); }); // 15. Let pullWithDefaultReader be the following steps: @@ -1938,7 +1938,7 @@ WebIDL::ExceptionOr readable_stream_default_controller_can_pull_if_needed( auto pull_promise = TRY(controller.pull_algorithm()->function()()); // 7. Upon fulfillment of pullPromise, - WebIDL::upon_fulfillment(*pull_promise, [&](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr { // 1. Set controller.[[pulling]] to false. controller.set_pulling(false); @@ -1952,15 +1952,15 @@ WebIDL::ExceptionOr readable_stream_default_controller_can_pull_if_needed( } return JS::js_undefined(); - }); + })); // 8. Upon rejection of pullPromise with reason e, - WebIDL::upon_rejection(*pull_promise, [&](auto const& e) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value e) -> WebIDL::ExceptionOr { // 1. Perform ! ReadableStreamDefaultControllerError(controller, e). readable_stream_default_controller_error(controller, e); return JS::js_undefined(); - }); + })); return {}; } @@ -2345,7 +2345,7 @@ WebIDL::ExceptionOr set_up_readable_stream_default_controller(ReadableStre auto start_promise = WebIDL::create_resolved_promise(realm, start_result); // 11. Upon fulfillment of startPromise, - WebIDL::upon_fulfillment(start_promise, [&](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr { // 1. Set controller.[[started]] to true. controller.set_started(true); @@ -2359,15 +2359,15 @@ WebIDL::ExceptionOr set_up_readable_stream_default_controller(ReadableStre TRY(readable_stream_default_controller_can_pull_if_needed(controller)); return JS::js_undefined(); - }); + })); // 12. Upon rejection of startPromise with reason r, - WebIDL::upon_rejection(start_promise, [&](auto const& r) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value r) -> WebIDL::ExceptionOr { // 1. Perform ! ReadableStreamDefaultControllerError(controller, r). readable_stream_default_controller_error(controller, r); return JS::js_undefined(); - }); + })); return {}; } @@ -2454,7 +2454,7 @@ WebIDL::ExceptionOr readable_byte_stream_controller_call_pull_if_needed(Re auto pull_promise = TRY(controller.pull_algorithm()->function()()); // 7. Upon fulfillment of pullPromise, - WebIDL::upon_fulfillment(*pull_promise, [&](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr { // 1. Set controller.[[pulling]] to false. controller.set_pulling(false); @@ -2468,15 +2468,15 @@ WebIDL::ExceptionOr readable_byte_stream_controller_call_pull_if_needed(Re } return JS::js_undefined(); - }); + })); // 8. Upon rejection of pullPromise with reason e, - WebIDL::upon_rejection(*pull_promise, [&](auto const& error) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*pull_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value error) -> WebIDL::ExceptionOr { // 1. Perform ! ReadableByteStreamControllerError(controller, e). readable_byte_stream_controller_error(controller, error); return JS::js_undefined(); - }); + })); return {}; } @@ -2967,7 +2967,7 @@ WebIDL::ExceptionOr set_up_readable_byte_stream_controller(ReadableStream& auto start_promise = WebIDL::create_resolved_promise(realm, start_result); // 16. Upon fulfillment of startPromise, - WebIDL::upon_fulfillment(start_promise, [&](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value) -> WebIDL::ExceptionOr { // 1. Set controller.[[started]] to true. controller.set_started(true); @@ -2981,15 +2981,15 @@ WebIDL::ExceptionOr set_up_readable_byte_stream_controller(ReadableStream& TRY(readable_byte_stream_controller_call_pull_if_needed(controller)); return JS::js_undefined(); - }); + })); // 17. Upon rejection of startPromise with reason r, - WebIDL::upon_rejection(start_promise, [&](auto const& r) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(start_promise, JS::create_heap_function(controller.heap(), [&controller](JS::Value r) -> WebIDL::ExceptionOr { // 1. Perform ! ReadableByteStreamControllerError(controller, r). readable_byte_stream_controller_error(controller, r); return JS::js_undefined(); - }); + })); return {}; } @@ -3577,7 +3577,7 @@ WebIDL::ExceptionOr writable_stream_finish_erroring(WritableStream& stream auto promise = TRY(stream.controller()->abort_steps(abort_request.reason)); // 13. Upon fulfillment of promise, - WebIDL::upon_fulfillment(*promise, [&, abort_promise = abort_request.promise](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(*promise, JS::create_heap_function(realm.heap(), [&realm, &stream, abort_promise = abort_request.promise](JS::Value) -> WebIDL::ExceptionOr { // 1. Resolve abortRequest’s promise with undefined. WebIDL::resolve_promise(realm, abort_promise, JS::js_undefined()); @@ -3585,10 +3585,10 @@ WebIDL::ExceptionOr writable_stream_finish_erroring(WritableStream& stream writable_stream_reject_close_and_closed_promise_if_needed(stream); return JS::js_undefined(); - }); + })); // 14. Upon rejection of promise with reason reason, - WebIDL::upon_rejection(*promise, [&, abort_promise = abort_request.promise](auto const& reason) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*promise, JS::create_heap_function(realm.heap(), [&realm, &stream, abort_promise = abort_request.promise](JS::Value reason) -> WebIDL::ExceptionOr { // 1. Reject abortRequest’s promise with reason. WebIDL::reject_promise(realm, abort_promise, reason); @@ -3596,7 +3596,7 @@ WebIDL::ExceptionOr writable_stream_finish_erroring(WritableStream& stream writable_stream_reject_close_and_closed_promise_if_needed(stream); return JS::js_undefined(); - }); + })); return {}; } @@ -4085,7 +4085,7 @@ WebIDL::ExceptionOr set_up_writable_stream_default_controller(WritableStre auto start_promise = WebIDL::create_resolved_promise(realm, start_result); // 17. Upon fulfillment of startPromise, - WebIDL::upon_fulfillment(*start_promise, [&](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(*start_promise, JS::create_heap_function(realm.heap(), [&controller, &stream](JS::Value) -> WebIDL::ExceptionOr { // 1. Assert: stream.[[state]] is "writable" or "erroring". auto state = stream.state(); VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring); @@ -4097,10 +4097,10 @@ WebIDL::ExceptionOr set_up_writable_stream_default_controller(WritableStre TRY(writable_stream_default_controller_advance_queue_if_needed(controller)); return JS::js_undefined(); - }); + })); // 18. Upon rejection of startPromise with reason r, - WebIDL::upon_rejection(*start_promise, [&](JS::Value reason) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*start_promise, JS::create_heap_function(realm.heap(), [&stream, &controller](JS::Value reason) -> WebIDL::ExceptionOr { // 1. Assert: stream.[[state]] is "writable" or "erroring". auto state = stream.state(); VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring); @@ -4112,7 +4112,7 @@ WebIDL::ExceptionOr set_up_writable_stream_default_controller(WritableStre TRY(writable_stream_deal_with_rejection(stream, reason)); return JS::js_undefined(); - }); + })); return {}; } @@ -4342,20 +4342,20 @@ WebIDL::ExceptionOr writable_stream_default_controller_process_close(Writa writable_stream_default_controller_clear_algorithms(controller); // 7. Upon fulfillment of sinkClosePromise, - WebIDL::upon_fulfillment(*sink_close_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(*sink_close_promise, JS::create_heap_function(controller.heap(), [stream](JS::Value) -> WebIDL::ExceptionOr { // 1. Perform ! WritableStreamFinishInFlightClose(stream). writable_stream_finish_in_flight_close(*stream); return JS::js_undefined(); - }); + })); // 8. Upon rejection of sinkClosePromise with reason reason, - WebIDL::upon_rejection(*sink_close_promise, [&, stream = stream](auto const& reason) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*sink_close_promise, JS::create_heap_function(controller.heap(), [stream = stream](JS::Value reason) -> WebIDL::ExceptionOr { // 1. Perform ! WritableStreamFinishInFlightCloseWithError(stream, reason). TRY(writable_stream_finish_in_flight_close_with_error(*stream, reason)); return JS::js_undefined(); - }); + })); return {}; } @@ -4373,7 +4373,7 @@ WebIDL::ExceptionOr writable_stream_default_controller_process_write(Writa auto sink_write_promise = TRY(controller.write_algorithm()->function()(chunk)); // 4. Upon fulfillment of sinkWritePromise, - WebIDL::upon_fulfillment(*sink_write_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr { + WebIDL::upon_fulfillment(*sink_write_promise, JS::create_heap_function(controller.heap(), [&controller, stream](JS::Value) -> WebIDL::ExceptionOr { // 1. Perform ! WritableStreamFinishInFlightWrite(stream). writable_stream_finish_in_flight_write(*stream); @@ -4399,10 +4399,10 @@ WebIDL::ExceptionOr writable_stream_default_controller_process_write(Writa TRY(writable_stream_default_controller_advance_queue_if_needed(controller)); return JS::js_undefined(); - }); + })); // 5. Upon rejection of sinkWritePromise with reason, - WebIDL::upon_rejection(*sink_write_promise, [&, stream = stream](auto const& reason) -> WebIDL::ExceptionOr { + WebIDL::upon_rejection(*sink_write_promise, JS::create_heap_function(controller.heap(), [&controller, stream](JS::Value reason) -> WebIDL::ExceptionOr { // 1. If stream.[[state]] is "writable", perform ! WritableStreamDefaultControllerClearAlgorithms(controller). if (stream->state() == WritableStream::State::Writable) writable_stream_default_controller_clear_algorithms(controller); @@ -4411,7 +4411,7 @@ WebIDL::ExceptionOr writable_stream_default_controller_process_write(Writa TRY(writable_stream_finish_in_flight_write_with_error(*stream, reason)); return JS::js_undefined(); - }); + })); return {}; } @@ -4700,13 +4700,13 @@ WebIDL::ExceptionOr> transform_stream_default_ // 2. Return the result of reacting to transformPromise with the following rejection steps given the argument r: auto react_result = WebIDL::react_to_promise(*transform_promise, {}, - [&](auto const& reason) -> WebIDL::ExceptionOr { + JS::create_heap_function(realm.heap(), [&controller](JS::Value reason) -> WebIDL::ExceptionOr { // 1. Perform ! TransformStreamError(controller.[[stream]], r). TRY(transform_stream_error(*controller.stream(), reason)); // 2. Throw r. return JS::throw_completion(reason); - }); + })); return WebIDL::create_resolved_promise(realm, react_result); } @@ -4744,7 +4744,7 @@ WebIDL::ExceptionOr> transform_stream_default_ auto react_result = WebIDL::react_to_promise( *flush_promise, // 1. If flushPromise was fulfilled, then: - [readable](auto const&) -> WebIDL::ExceptionOr { + JS::create_heap_function(realm.heap(), [readable](JS::Value) -> WebIDL::ExceptionOr { // 1. If readable.[[state]] is "errored", throw readable.[[storedError]]. if (readable->state() == ReadableStream::State::Errored) return JS::throw_completion(readable->stored_error()); @@ -4754,15 +4754,15 @@ WebIDL::ExceptionOr> transform_stream_default_ readable_stream_default_controller_close(readable->controller().value().get>()); return JS::js_undefined(); - }, + }), // 2. If flushPromise was rejected with reason r, then: - [&stream, readable](auto const& reason) -> WebIDL::ExceptionOr { + JS::create_heap_function(realm.heap(), [&stream, readable](JS::Value reason) -> WebIDL::ExceptionOr { // 1. Perform ! TransformStreamError(stream, r). TRY(transform_stream_error(stream, reason)); // 2. Throw readable.[[storedError]]. return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, readable->stored_error().as_string().utf8_string() }; - }); + })); return WebIDL::create_resolved_promise(realm, react_result); } @@ -4788,7 +4788,7 @@ WebIDL::ExceptionOr> transform_stream_default_ // 3. Return the result of reacting to backpressureChangePromise with the following fulfillment steps: auto react_result = WebIDL::react_to_promise(*backpressure_change_promise, - [&stream, controller, chunk](auto const&) -> WebIDL::ExceptionOr { + JS::create_heap_function(realm.heap(), [&stream, controller, chunk](JS::Value) -> WebIDL::ExceptionOr { // 1. Let writable be stream.[[writable]]. auto writable = stream.writable(); @@ -4804,7 +4804,7 @@ WebIDL::ExceptionOr> transform_stream_default_ // 5. Return ! TransformStreamDefaultControllerPerformTransform(controller, chunk). return TRY(transform_stream_default_controller_perform_transform(*controller, chunk))->promise(); - }, + }), {}); return WebIDL::create_resolved_promise(realm, react_result); diff --git a/Userland/Libraries/LibWeb/WebIDL/Promise.cpp b/Userland/Libraries/LibWeb/WebIDL/Promise.cpp index d2e58a81285..3b7bda2e4c4 100644 --- a/Userland/Libraries/LibWeb/WebIDL/Promise.cpp +++ b/Userland/Libraries/LibWeb/WebIDL/Promise.cpp @@ -93,7 +93,7 @@ void reject_promise(JS::Realm& realm, Promise const& promise, JS::Value reason) } // https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled -JS::NonnullGCPtr react_to_promise(Promise const& promise, Optional on_fulfilled_callback, Optional on_rejected_callback) +JS::NonnullGCPtr react_to_promise(Promise const& promise, JS::GCPtr on_fulfilled_callback, JS::GCPtr on_rejected_callback) { auto& realm = promise.promise()->shape().realm(); auto& vm = realm.vm(); @@ -104,8 +104,8 @@ JS::NonnullGCPtr react_to_promise(Promise const& promise, Optional< 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); })) + auto result = on_fulfilled_callback + ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return on_fulfilled_callback->function()(value); })) : value; // 3. Return result, converted to an ECMAScript value. @@ -121,8 +121,8 @@ JS::NonnullGCPtr react_to_promise(Promise const& promise, Optional< 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); })) + auto result = on_rejected_callback + ? TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return on_rejected_callback->function()(reason); })) : WebIDL::create_rejected_promise(realm, reason)->promise(); // 3. Return result, converted to an ECMAScript value. @@ -146,32 +146,24 @@ JS::NonnullGCPtr react_to_promise(Promise const& promise, Optional< } // https://webidl.spec.whatwg.org/#upon-fulfillment -JS::NonnullGCPtr upon_fulfillment(Promise const& promise, ReactionSteps steps) +JS::NonnullGCPtr upon_fulfillment(Promise const& promise, JS::NonnullGCPtr steps) { // 1. Return the result of reacting to promise: return react_to_promise(promise, // - 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); - }, + // 1. Perform steps with v. + steps, {}); } // https://webidl.spec.whatwg.org/#upon-rejection -JS::NonnullGCPtr upon_rejection(Promise const& promise, ReactionSteps steps) +JS::NonnullGCPtr upon_rejection(Promise const& promise, JS::NonnullGCPtr steps) { // 1. Return the result of reacting to promise: return react_to_promise(promise, {}, // - 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); - }); + // 1. Perform steps with r. + steps); } // https://webidl.spec.whatwg.org/#mark-a-promise-as-handled diff --git a/Userland/Libraries/LibWeb/WebIDL/Promise.h b/Userland/Libraries/LibWeb/WebIDL/Promise.h index d7a42c12153..a2d91c268a1 100644 --- a/Userland/Libraries/LibWeb/WebIDL/Promise.h +++ b/Userland/Libraries/LibWeb/WebIDL/Promise.h @@ -17,7 +17,7 @@ namespace Web::WebIDL { // NOTE: This is Function, not SafeFunction, because they get stored in a NativeFunction anyway, which will protect captures. -using ReactionSteps = Function(JS::Value)>; +using ReactionSteps = JS::HeapFunction(JS::Value)>; // https://webidl.spec.whatwg.org/#es-promise using Promise = JS::PromiseCapability; @@ -27,9 +27,9 @@ JS::NonnullGCPtr create_resolved_promise(JS::Realm&, JS::Value); JS::NonnullGCPtr create_rejected_promise(JS::Realm&, JS::Value); void resolve_promise(JS::Realm&, Promise const&, JS::Value = JS::js_undefined()); void reject_promise(JS::Realm&, Promise const&, JS::Value); -JS::NonnullGCPtr react_to_promise(Promise const&, Optional on_fulfilled_callback, Optional on_rejected_callback); -JS::NonnullGCPtr upon_fulfillment(Promise const&, ReactionSteps); -JS::NonnullGCPtr upon_rejection(Promise const&, ReactionSteps); +JS::NonnullGCPtr react_to_promise(Promise const&, JS::GCPtr on_fulfilled_callback, JS::GCPtr on_rejected_callback); +JS::NonnullGCPtr upon_fulfillment(Promise const&, JS::NonnullGCPtr); +JS::NonnullGCPtr upon_rejection(Promise const&, JS::NonnullGCPtr); void mark_promise_as_handled(Promise const&); void wait_for_all(JS::Realm&, Vector> const& promises, Function const&)> success_steps, Function failure_steps);