LibWeb: Do not create a RootVector to invoke IDL callbacks

These callbacks are evaluated synchronously via JS::Call. We do not need
to construct an expensive RootVector container just to immediately
invoke the callbacks.

Stylistically, this also helps indicate where the actual arguments start
at the call sites, by wrapping the arguments in braces.
This commit is contained in:
Timothy Flynn 2025-04-15 20:56:03 -04:00 committed by Tim Flynn
commit 6dd2a4c945
Notes: github-actions[bot] 2025-04-16 11:32:56 +00:00
20 changed files with 60 additions and 119 deletions

View file

@ -713,7 +713,7 @@ void queue_mutation_observer_microtask(DOM::Document const& document)
MUST(wrapped_records->create_data_property(property_index, record.ptr())); MUST(wrapped_records->create_data_property(property_index, record.ptr()));
} }
(void)WebIDL::invoke_callback(callback, mutation_observer, WebIDL::ExceptionBehavior::Report, wrapped_records, mutation_observer); (void)WebIDL::invoke_callback(callback, mutation_observer, WebIDL::ExceptionBehavior::Report, { { wrapped_records, mutation_observer } });
} }
} }

View file

@ -4569,7 +4569,7 @@ void Document::queue_intersection_observer_task()
// 5. Invoke callback with queue as the first argument, observer as the second argument, and observer as the callback this value. If this throws an exception, report the exception. // 5. Invoke callback with queue as the first argument, observer as the second argument, and observer as the callback this value. If this throws an exception, report the exception.
// NOTE: This does not follow the spec as written precisely, but this is the same thing we do elsewhere and there is a WPT test that relies on this. // NOTE: This does not follow the spec as written precisely, but this is the same thing we do elsewhere and there is a WPT test that relies on this.
(void)WebIDL::invoke_callback(callback, observer.ptr(), WebIDL::ExceptionBehavior::Report, wrapped_queue, observer.ptr()); (void)WebIDL::invoke_callback(callback, observer.ptr(), WebIDL::ExceptionBehavior::Report, { { wrapped_queue, observer.ptr() } });
} }
})); }));
} }

View file

@ -2517,7 +2517,7 @@ JS::ThrowCompletionOr<void> Element::upgrade_element(GC::Ref<HTML::CustomElement
set_custom_element_state(CustomElementState::Precustomized); set_custom_element_state(CustomElementState::Precustomized);
// 3. Let constructResult be the result of constructing C, with no arguments. // 3. Let constructResult be the result of constructing C, with no arguments.
auto construct_result = TRY(WebIDL::construct(constructor)); auto construct_result = TRY(WebIDL::construct(constructor, {}));
// 4. If SameValue(constructResult, element) is false, then throw a TypeError. // 4. If SameValue(constructResult, element) is false, then throw a TypeError.
if (!JS::same_value(construct_result, this)) if (!JS::same_value(construct_result, this))

View file

@ -578,7 +578,7 @@ WebIDL::ExceptionOr<GC::Ref<Element>> create_element(Document& document, FlyStri
auto& constructor = definition->constructor(); auto& constructor = definition->constructor();
// 2. Set result to the result of constructing C, with no arguments. // 2. Set result to the result of constructing C, with no arguments.
auto result = TRY(WebIDL::construct(constructor)); auto result = TRY(WebIDL::construct(constructor, {}));
// NOTE: IDL does not currently convert the object for us, so we will have to do it here. // NOTE: IDL does not currently convert the object for us, so we will have to do it here.
if (!result.is_object() || !is<HTML::HTMLElement>(result.as_object())) if (!result.is_object() || !is<HTML::HTMLElement>(result.as_object()))

View file

@ -91,7 +91,7 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<GC::Root<DOM::DOMEventLi
// FIXME: These should be wrapped for us in call_user_object_operation, but it currently doesn't do that. // FIXME: These should be wrapped for us in call_user_object_operation, but it currently doesn't do that.
auto* this_value = event.current_target().ptr(); auto* this_value = event.current_target().ptr();
auto* wrapped_event = &event; auto* wrapped_event = &event;
auto result = WebIDL::call_user_object_operation(callback, "handleEvent"_string, this_value, wrapped_event); auto result = WebIDL::call_user_object_operation(callback, "handleEvent"_string, this_value, { { wrapped_event } });
// If this throws an exception, then: // If this throws an exception, then:
if (result.is_error()) { if (result.is_error()) {

View file

@ -698,7 +698,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// calls directly into the callback without considering things such as proxies, it is a waste. However, if it observable, then we must reuse the this_value that was given to the callback. // calls directly into the callback without considering things such as proxies, it is a waste. However, if it observable, then we must reuse the this_value that was given to the callback.
auto* this_value = error_event.current_target().ptr(); auto* this_value = error_event.current_target().ptr();
return_value_or_error = WebIDL::invoke_callback(*callback, this_value, wrapped_message, wrapped_filename, wrapped_lineno, wrapped_colno, error_event.error()); return_value_or_error = WebIDL::invoke_callback(*callback, this_value, { { wrapped_message, wrapped_filename, wrapped_lineno, wrapped_colno, error_event.error() } });
} else { } else {
// -> Otherwise // -> Otherwise
// Invoke callback with one argument, the value of which is the Event object event, with the callback this value set to event's currentTarget. Let return value be the callback's return value. [WEBIDL] // Invoke callback with one argument, the value of which is the Event object event, with the callback this value set to event's currentTarget. Let return value be the callback's return value. [WEBIDL]
@ -709,7 +709,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// FIXME: The comments about this in the special_error_event_handling path also apply here. // FIXME: The comments about this in the special_error_event_handling path also apply here.
auto* this_value = event.current_target().ptr(); auto* this_value = event.current_target().ptr();
return_value_or_error = WebIDL::invoke_callback(*callback, this_value, wrapped_event); return_value_or_error = WebIDL::invoke_callback(*callback, this_value, { { wrapped_event } });
} }
// If an exception gets thrown by the callback, end these steps and allow the exception to propagate. (It will propagate to the DOM event dispatch logic, which will then report the exception.) // If an exception gets thrown by the callback, end these steps and allow the exception to propagate. (It will propagate to the DOM event dispatch logic, which will then report the exception.)

View file

@ -169,7 +169,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
// 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ». // 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ».
// If this throws an exception, then unset traversers active flag and rethrow the exception. // If this throws an exception, then unset traversers active flag and rethrow the exception.
auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, &node); auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, { { &node } });
if (result.is_abrupt()) { if (result.is_abrupt()) {
m_active = false; m_active = false;
return result; return result;

View file

@ -274,7 +274,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
// 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ». // 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ».
// If this throws an exception, then unset traversers active flag and rethrow the exception. // If this throws an exception, then unset traversers active flag and rethrow the exception.
auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, &node); auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, { { &node } });
if (result.is_abrupt()) { if (result.is_abrupt()) {
m_active = false; m_active = false;
return result; return result;

View file

@ -117,7 +117,7 @@ void DataTransferItem::get_as_string(GC::Ptr<WebIDL::CallbackType> callback) con
HTML::queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr, HTML::queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr,
GC::Function<void()>::create(realm.heap(), [callback, data]() { GC::Function<void()>::create(realm.heap(), [callback, data]() {
(void)WebIDL::invoke_callback(*callback, {}, data); (void)WebIDL::invoke_callback(*callback, {}, { { data } });
})); }));
} }

View file

@ -397,7 +397,7 @@ WebIDL::ExceptionOr<void> HTMLCanvasElement::to_blob(GC::Ref<WebIDL::CallbackTyp
blob_result = FileAPI::Blob::create(realm(), file_result->buffer, TRY_OR_THROW_OOM(vm(), String::from_utf8(file_result->mime_type))); blob_result = FileAPI::Blob::create(realm(), file_result->buffer, TRY_OR_THROW_OOM(vm(), String::from_utf8(file_result->mime_type)));
// 2. Invoke callback with « result » and "report". // 2. Invoke callback with « result » and "report".
TRY(WebIDL::invoke_callback(*callback, {}, WebIDL::ExceptionBehavior::Report, move(blob_result))); TRY(WebIDL::invoke_callback(*callback, {}, WebIDL::ExceptionBehavior::Report, { { blob_result } }));
return {}; return {};
}); });
if (maybe_error.is_throw_completion()) if (maybe_error.is_throw_completion())

View file

@ -1141,7 +1141,7 @@ bool Navigation::inner_navigate_event_firing_algorithm(
// 2. For each handler of event's navigation handler list: // 2. For each handler of event's navigation handler list:
for (auto const& handler : event->navigation_handler_list()) { for (auto const& handler : event->navigation_handler_list()) {
// 1. Append the result of invoking handler with an empty arguments list to promisesList. // 1. Append the result of invoking handler with an empty arguments list to promisesList.
auto result = WebIDL::invoke_callback(handler, {}); auto result = WebIDL::invoke_callback(handler, {}, {});
// This *should* be equivalent to converting a promise to a promise capability // This *should* be equivalent to converting a promise to a promise capability
promises_list.append(WebIDL::create_resolved_promise(realm, result.value())); promises_list.append(WebIDL::create_resolved_promise(realm, result.value()));
} }

View file

@ -86,7 +86,7 @@ void UniversalGlobalScopeMixin::queue_microtask(WebIDL::CallbackType& callback)
// The queueMicrotask(callback) method must queue a microtask to invoke callback with « » and "report". // The queueMicrotask(callback) method must queue a microtask to invoke callback with « » and "report".
HTML::queue_a_microtask(document, GC::create_function(realm.heap(), [&callback] { HTML::queue_a_microtask(document, GC::create_function(realm.heap(), [&callback] {
(void)WebIDL::invoke_callback(callback, {}, WebIDL::ExceptionBehavior::Report); (void)WebIDL::invoke_callback(callback, {}, WebIDL::ExceptionBehavior::Report, {});
})); }));
} }

View file

@ -1611,7 +1611,7 @@ WebIDL::UnsignedLong Window::request_animation_frame(GC::Ref<WebIDL::CallbackTyp
// FIXME: Make this fully spec compliant. Currently implements a mix of 'requestAnimationFrame()' and 'run the animation frame callbacks'. // FIXME: Make this fully spec compliant. Currently implements a mix of 'requestAnimationFrame()' and 'run the animation frame callbacks'.
return animation_frame_callback_driver().add(GC::create_function(heap(), [this, callback](double now) { return animation_frame_callback_driver().add(GC::create_function(heap(), [this, callback](double now) {
// 3. Invoke callback, passing now as the only argument, and if an exception is thrown, report the exception. // 3. Invoke callback, passing now as the only argument, and if an exception is thrown, report the exception.
auto result = WebIDL::invoke_callback(*callback, {}, JS::Value(now)); auto result = WebIDL::invoke_callback(*callback, {}, { { JS::Value(now) } });
if (result.is_error()) if (result.is_error())
report_exception(result, realm()); report_exception(result, realm());
})); }));
@ -1655,7 +1655,7 @@ u32 Window::request_idle_callback(WebIDL::CallbackType& callback, RequestIdleCal
// 4. Push callback to the end of window's list of idle request callbacks, associated with handle. // 4. Push callback to the end of window's list of idle request callbacks, associated with handle.
auto handler = [callback = GC::make_root(callback)](GC::Ref<RequestIdleCallback::IdleDeadline> deadline) -> JS::Completion { auto handler = [callback = GC::make_root(callback)](GC::Ref<RequestIdleCallback::IdleDeadline> deadline) -> JS::Completion {
return WebIDL::invoke_callback(*callback, {}, deadline.ptr()); return WebIDL::invoke_callback(*callback, {}, { { deadline } });
}; };
m_idle_request_callbacks.append(adopt_ref(*new IdleCallback(move(handler), handle))); m_idle_request_callbacks.append(adopt_ref(*new IdleCallback(move(handler), handle)));

View file

@ -639,7 +639,7 @@ void WindowOrWorkerGlobalScopeMixin::queue_the_performance_observer_task()
// 9. Call pos observer callback with observerEntryList as the first argument, with po as the second // 9. Call pos observer callback with observerEntryList as the first argument, with po as the second
// argument and as callback this value, and with callbackOptions as the third argument. // argument and as callback this value, and with callbackOptions as the third argument.
// If this throws an exception, report the exception. // If this throws an exception, report the exception.
auto completion = WebIDL::invoke_callback(registered_observer->callback(), registered_observer, observer_entry_list, registered_observer, callback_options); auto completion = WebIDL::invoke_callback(registered_observer->callback(), registered_observer, { { observer_entry_list, registered_observer, callback_options } });
if (completion.is_abrupt()) if (completion.is_abrupt())
HTML::report_exception(completion, realm); HTML::report_exception(completion, realm);
} }

View file

@ -109,7 +109,7 @@ void ResizeObserver::invoke_callback(ReadonlySpan<GC::Ref<ResizeObserverEntry>>
MUST(wrapped_records->create_data_property(property_index, record.ptr())); MUST(wrapped_records->create_data_property(property_index, record.ptr()));
} }
(void)WebIDL::invoke_callback(callback, JS::js_undefined(), WebIDL::ExceptionBehavior::Report, wrapped_records); (void)WebIDL::invoke_callback(callback, JS::js_undefined(), WebIDL::ExceptionBehavior::Report, { { wrapped_records } });
} }
} }

View file

@ -1762,7 +1762,8 @@ GC::Ref<SizeAlgorithm> extract_size_algorithm(JS::VM& vm, QueuingStrategy const&
// 2. Return an algorithm that performs the following steps, taking a chunk argument: // 2. Return an algorithm that performs the following steps, taking a chunk argument:
return GC::create_function(vm.heap(), [size = strategy.size](JS::Value chunk) { return GC::create_function(vm.heap(), [size = strategy.size](JS::Value chunk) {
return WebIDL::invoke_callback(*size, JS::js_undefined(), chunk); // 1. Return the result of invoking strategy["size"] with argument list « chunk ».
return WebIDL::invoke_callback(*size, {}, { { chunk } });
}); });
} }
@ -3145,7 +3146,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
// invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource. // invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source.start) { if (underlying_source.start) {
start_algorithm = GC::create_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.start]() -> WebIDL::ExceptionOr<JS::Value> { start_algorithm = GC::create_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.start]() -> WebIDL::ExceptionOr<JS::Value> {
return TRY(WebIDL::invoke_callback(*callback, underlying_source_value, controller)); return TRY(WebIDL::invoke_callback(*callback, underlying_source_value, { { controller } }));
}); });
} }
@ -3153,7 +3154,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
// invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource. // invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source.pull) { if (underlying_source.pull) {
pull_algorithm = GC::create_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.pull]() { pull_algorithm = GC::create_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.pull]() {
return WebIDL::invoke_promise_callback(*callback, underlying_source_value, controller); return WebIDL::invoke_promise_callback(*callback, underlying_source_value, { { controller } });
}); });
} }
@ -3162,7 +3163,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
// callback this value underlyingSource. // callback this value underlyingSource.
if (underlying_source.cancel) { if (underlying_source.cancel) {
cancel_algorithm = GC::create_function(realm.heap(), [underlying_source_value, callback = underlying_source.cancel](JS::Value reason) { cancel_algorithm = GC::create_function(realm.heap(), [underlying_source_value, callback = underlying_source.cancel](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, underlying_source_value, reason); return WebIDL::invoke_promise_callback(*callback, underlying_source_value, { { reason } });
}); });
} }
@ -4846,7 +4847,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// callback this value underlyingSink. // callback this value underlyingSink.
if (underlying_sink.start) { if (underlying_sink.start) {
start_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.start]() -> WebIDL::ExceptionOr<JS::Value> { start_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.start]() -> WebIDL::ExceptionOr<JS::Value> {
return TRY(WebIDL::invoke_callback(*callback, underlying_sink_value, WebIDL::ExceptionBehavior::Rethrow, controller)); return TRY(WebIDL::invoke_callback(*callback, underlying_sink_value, WebIDL::ExceptionBehavior::Rethrow, { { controller } }));
}); });
} }
@ -4855,7 +4856,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// callback this value underlyingSink. // callback this value underlyingSink.
if (underlying_sink.write) { if (underlying_sink.write) {
write_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.write](JS::Value chunk) { write_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.write](JS::Value chunk) {
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, chunk, controller); return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, { { chunk, controller } });
}); });
} }
@ -4863,7 +4864,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// invoking underlyingSinkDict["close"] with argument list «» and callback this value underlyingSink. // invoking underlyingSinkDict["close"] with argument list «» and callback this value underlyingSink.
if (underlying_sink.close) { if (underlying_sink.close) {
close_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.close]() { close_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.close]() {
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value); return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, {});
}); });
} }
@ -4872,7 +4873,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// value underlyingSink. // value underlyingSink.
if (underlying_sink.abort) { if (underlying_sink.abort) {
abort_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.abort](JS::Value reason) { abort_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.abort](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, reason); return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, { { reason } });
}); });
} }
@ -5275,7 +5276,7 @@ void set_up_transform_stream_default_controller_from_transformer(TransformStream
// callback this value transformer. // callback this value transformer.
if (transformer_dict.transform) { if (transformer_dict.transform) {
transform_algorithm = GC::create_function(realm.heap(), [controller, transformer, callback = transformer_dict.transform](JS::Value chunk) { transform_algorithm = GC::create_function(realm.heap(), [controller, transformer, callback = transformer_dict.transform](JS::Value chunk) {
return WebIDL::invoke_promise_callback(*callback, transformer, chunk, controller); return WebIDL::invoke_promise_callback(*callback, transformer, { { chunk, controller } });
}); });
} }
@ -5283,7 +5284,7 @@ void set_up_transform_stream_default_controller_from_transformer(TransformStream
// transformerDict["flush"] with argument list « controller » and callback this value transformer. // transformerDict["flush"] with argument list « controller » and callback this value transformer.
if (transformer_dict.flush) { if (transformer_dict.flush) {
flush_algorithm = GC::create_function(realm.heap(), [transformer, callback = transformer_dict.flush, controller]() { flush_algorithm = GC::create_function(realm.heap(), [transformer, callback = transformer_dict.flush, controller]() {
return WebIDL::invoke_promise_callback(*callback, transformer, controller); return WebIDL::invoke_promise_callback(*callback, transformer, { { controller } });
}); });
} }
@ -5291,7 +5292,7 @@ void set_up_transform_stream_default_controller_from_transformer(TransformStream
// the result of invoking transformerDict["cancel"] with argument list « reason » and callback this value transformer. // the result of invoking transformerDict["cancel"] with argument list « reason » and callback this value transformer.
if (transformer_dict.cancel) { if (transformer_dict.cancel) {
cancel_algorithm = GC::create_function(realm.heap(), [transformer, callback = transformer_dict.cancel](JS::Value reason) { cancel_algorithm = GC::create_function(realm.heap(), [transformer, callback = transformer_dict.cancel](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, transformer, reason); return WebIDL::invoke_promise_callback(*callback, transformer, { { reason } });
}); });
} }
@ -5816,7 +5817,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
// invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource. // invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source_dict.start) { if (underlying_source_dict.start) {
start_algorithm = GC::create_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.start]() -> WebIDL::ExceptionOr<JS::Value> { start_algorithm = GC::create_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.start]() -> WebIDL::ExceptionOr<JS::Value> {
return TRY(WebIDL::invoke_callback(*callback, underlying_source, controller)); return TRY(WebIDL::invoke_callback(*callback, underlying_source, { { controller } }));
}); });
} }
@ -5824,7 +5825,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
// invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource. // invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source_dict.pull) { if (underlying_source_dict.pull) {
pull_algorithm = GC::create_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.pull]() { pull_algorithm = GC::create_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.pull]() {
return WebIDL::invoke_promise_callback(*callback, underlying_source, controller); return WebIDL::invoke_promise_callback(*callback, underlying_source, { { controller } });
}); });
} }
@ -5833,7 +5834,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
// callback this value underlyingSource. // callback this value underlyingSource.
if (underlying_source_dict.cancel) { if (underlying_source_dict.cancel) {
cancel_algorithm = GC::create_function(realm.heap(), [underlying_source, callback = underlying_source_dict.cancel](JS::Value reason) { cancel_algorithm = GC::create_function(realm.heap(), [underlying_source, callback = underlying_source_dict.cancel](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, underlying_source, reason); return WebIDL::invoke_promise_callback(*callback, underlying_source, { { reason } });
}); });
} }

View file

@ -63,7 +63,7 @@ WebIDL::ExceptionOr<GC::Ref<TransformStream>> TransformStream::construct_impl(JS
// 12. If transformerDict["start"] exists, then resolve startPromise with the result of invoking // 12. If transformerDict["start"] exists, then resolve startPromise with the result of invoking
// transformerDict["start"] with argument list « this.[[controller]] » and callback this value transformer. // transformerDict["start"] with argument list « this.[[controller]] » and callback this value transformer.
if (transformer_dict.start) { if (transformer_dict.start) {
auto result = TRY(WebIDL::invoke_callback(*transformer_dict.start, transformer, stream->controller())); auto result = TRY(WebIDL::invoke_callback(*transformer_dict.start, transformer, { { stream->controller() } }));
WebIDL::resolve_promise(realm, start_promise, result); WebIDL::resolve_promise(realm, start_promise, result);
} }
// 13. Otherwise, resolve startPromise with undefined. // 13. Otherwise, resolve startPromise with undefined.

View file

@ -243,7 +243,7 @@ GC::Ref<WebIDL::Promise> BaseAudioContext::decode_audio_data(GC::Root<WebIDL::Bu
// 4.3. Queue a media element task to invoke errorCallback with error. // 4.3. Queue a media element task to invoke errorCallback with error.
if (error_callback) { if (error_callback) {
queue_a_media_element_task(GC::create_function(heap(), [&realm, error_callback, error] { queue_a_media_element_task(GC::create_function(heap(), [&realm, error_callback, error] {
auto completion = WebIDL::invoke_callback(*error_callback, {}, error); auto completion = WebIDL::invoke_callback(*error_callback, {}, { { error } });
if (completion.is_abrupt()) if (completion.is_abrupt())
HTML::report_exception(completion, realm); HTML::report_exception(completion, realm);
})); }));
@ -293,7 +293,7 @@ void BaseAudioContext::queue_a_decoding_operation(GC::Ref<JS::PromiseCapability>
// 4.2. If errorCallback is not missing, invoke errorCallback with error. // 4.2. If errorCallback is not missing, invoke errorCallback with error.
if (error_callback) { if (error_callback) {
auto completion = WebIDL::invoke_callback(*error_callback, {}, error); auto completion = WebIDL::invoke_callback(*error_callback, {}, { { error } });
if (completion.is_abrupt()) if (completion.is_abrupt())
HTML::report_exception(completion, realm); HTML::report_exception(completion, realm);
} }
@ -317,7 +317,7 @@ void BaseAudioContext::queue_a_decoding_operation(GC::Ref<JS::PromiseCapability>
// 5.2.3. If successCallback is not missing, invoke successCallback with buffer. // 5.2.3. If successCallback is not missing, invoke successCallback with buffer.
if (success_callback) { if (success_callback) {
auto completion = WebIDL::invoke_callback(*success_callback, {}, buffer); auto completion = WebIDL::invoke_callback(*success_callback, {}, { { buffer } });
if (completion.is_abrupt()) if (completion.is_abrupt())
HTML::report_exception(completion, realm); HTML::report_exception(completion, realm);
} }

View file

@ -8,17 +8,17 @@
#include <AK/ByteBuffer.h> #include <AK/ByteBuffer.h>
#include <AK/Enumerate.h> #include <AK/Enumerate.h>
#include <AK/Math.h>
#include <AK/NumericLimits.h> #include <AK/NumericLimits.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ArrayBuffer.h> #include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DataView.h> #include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/PropertyKey.h> #include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/PlatformObject.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Window.h> #include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
#include <LibWeb/WebIDL/AbstractOperations.h> #include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/CallbackType.h>
#include <LibWeb/WebIDL/Promise.h> #include <LibWeb/WebIDL/Promise.h>
#include <LibWeb/WebIDL/Types.h> #include <LibWeb/WebIDL/Types.h>
@ -141,7 +141,7 @@ inline JS::Completion clean_up_on_return(JS::Realm& stored_realm, JS::Realm& rel
// https://webidl.spec.whatwg.org/#call-a-user-objects-operation // https://webidl.spec.whatwg.org/#call-a-user-objects-operation
// https://whatpr.org/webidl/1437.html#call-a-user-objects-operation // https://whatpr.org/webidl/1437.html#call-a-user-objects-operation
JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args) JS::Completion call_user_object_operation(CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args)
{ {
// 1. Let completion be an uninitialized variable. // 1. Let completion be an uninitialized variable.
JS::Completion completion; JS::Completion completion;
@ -197,9 +197,7 @@ JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String
// For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to. // For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to.
// 11. Let callResult be Call(X, thisArg, esArgs). // 11. Let callResult be Call(X, thisArg, esArgs).
VERIFY(actual_function_object); auto call_result = JS::call(object->vm(), as<JS::FunctionObject>(*actual_function_object), this_argument.value(), args);
auto& vm = object->vm();
auto call_result = JS::call(vm, as<JS::FunctionObject>(*actual_function_object), this_argument.value(), args.span());
// 12. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return. // 12. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) { if (call_result.is_throw_completion()) {
@ -244,7 +242,7 @@ JS::ThrowCompletionOr<String> to_usv_string(JS::VM& vm, JS::Value value)
// https://webidl.spec.whatwg.org/#invoke-a-callback-function // https://webidl.spec.whatwg.org/#invoke-a-callback-function
// https://whatpr.org/webidl/1437.html#invoke-a-callback-function // https://whatpr.org/webidl/1437.html#invoke-a-callback-function
template<typename ReturnSteps> template<typename ReturnSteps>
static auto invoke_callback_impl(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args, ReturnSteps&& return_steps) static auto invoke_callback_impl(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args, ReturnSteps&& return_steps)
{ {
// 1. Let completion be an uninitialized variable. // 1. Let completion be an uninitialized variable.
@ -279,8 +277,7 @@ static auto invoke_callback_impl(WebIDL::CallbackType& callback, Optional<JS::Va
// If this throws an exception, set completion to the completion value representing the thrown exception and jump to the step labeled return. // If this throws an exception, set completion to the completion value representing the thrown exception and jump to the step labeled return.
// 10. Let callResult be Call(F, thisArg, jsArgs). // 10. Let callResult be Call(F, thisArg, jsArgs).
auto& vm = function_object->vm(); auto call_result = JS::call(function_object->vm(), as<JS::FunctionObject>(*function_object), this_argument.value(), args);
auto call_result = JS::call(vm, as<JS::FunctionObject>(*function_object), this_argument.value(), args.span());
// 11. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return. // 11. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
// 12. Set completion to the result of converting callResult.[[Value]] to an IDL value of the same type as callables // 12. Set completion to the result of converting callResult.[[Value]] to an IDL value of the same type as callables
@ -298,7 +295,7 @@ static auto invoke_callback_impl(WebIDL::CallbackType& callback, Optional<JS::Va
} }
} }
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, GC::RootVector<JS::Value> args) JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, ReadonlySpan<JS::Value> args)
{ {
// https://webidl.spec.whatwg.org/#js-invoking-callback-functions // https://webidl.spec.whatwg.org/#js-invoking-callback-functions
// The exceptionBehavior argument must be supplied if, and only if, callables return type is not a promise type. If callables return type is neither undefined nor any, it must be "rethrow". // The exceptionBehavior argument must be supplied if, and only if, callables return type is not a promise type. If callables return type is neither undefined nor any, it must be "rethrow".
@ -308,7 +305,7 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
VERIFY(exception_behavior == ExceptionBehavior::NotSpecified || callback.operation_returns_promise == OperationReturnsPromise::No); VERIFY(exception_behavior == ExceptionBehavior::NotSpecified || callback.operation_returns_promise == OperationReturnsPromise::No);
return invoke_callback_impl(callback, move(this_argument), move(args), [&](JS::Realm& relevant_realm, JS::Completion completion) -> JS::Completion { return invoke_callback_impl(callback, move(this_argument), args, [&](JS::Realm& relevant_realm, JS::Completion completion) -> JS::Completion {
// 3. If completion is an IDL value, return completion. // 3. If completion is an IDL value, return completion.
if (!completion.is_abrupt()) if (!completion.is_abrupt())
return completion; return completion;
@ -344,21 +341,21 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
}); });
} }
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args) JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args)
{ {
return invoke_callback(callback, move(this_argument), ExceptionBehavior::NotSpecified, move(args)); return invoke_callback(callback, move(this_argument), ExceptionBehavior::NotSpecified, args);
} }
// AD-HOC: This may be used as an alternative to WebIDL::invoke_callback when you know the callback returns a promise, // AD-HOC: This may be used as an alternative to WebIDL::invoke_callback when you know the callback returns a promise,
// and the caller needs a WebIDL::Promise rather than a JS::Promise. // and the caller needs a WebIDL::Promise rather than a JS::Promise.
GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args) GC::Ref<Promise> invoke_promise_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args)
{ {
VERIFY(callback.operation_returns_promise == OperationReturnsPromise::Yes); VERIFY(callback.operation_returns_promise == OperationReturnsPromise::Yes);
return invoke_callback_impl(callback, move(this_argument), move(args), [&](JS::Realm& relevant_realm, JS::Completion completion) -> GC::Ref<WebIDL::Promise> { return invoke_callback_impl(callback, move(this_argument), args, [&](JS::Realm& relevant_realm, JS::Completion completion) {
// 3. If completion is an IDL value, return completion. // 3. If completion is an IDL value, return completion.
if (!completion.is_abrupt()) if (!completion.is_abrupt())
return WebIDL::create_resolved_promise(relevant_realm, completion.release_value()); return create_resolved_promise(relevant_realm, completion.release_value());
// 4. Assert: completion is an abrupt completion. // 4. Assert: completion is an abrupt completion.
VERIFY(completion.is_abrupt()); VERIFY(completion.is_abrupt());
@ -371,7 +368,7 @@ GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback,
}); });
} }
JS::Completion construct(WebIDL::CallbackType& callback, GC::RootVector<JS::Value> args) JS::Completion construct(CallbackType& callback, ReadonlySpan<JS::Value> args)
{ {
// 1. Let completion be an uninitialized variable. // 1. Let completion be an uninitialized variable.
JS::Completion completion; JS::Completion completion;
@ -399,8 +396,7 @@ JS::Completion construct(WebIDL::CallbackType& callback, GC::RootVector<JS::Valu
// For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to. // For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to.
// 8. Let callResult be Completion(Construct(F, esArgs)). // 8. Let callResult be Completion(Construct(F, esArgs)).
auto& vm = function_object->vm(); auto call_result = JS::construct(function_object->vm(), as<JS::FunctionObject>(*function_object), args);
auto call_result = JS::construct(vm, as<JS::FunctionObject>(*function_object), args.span());
// 9. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return. // 9. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) { if (call_result.is_throw_completion()) {

View file

@ -9,11 +9,9 @@
#pragma once #pragma once
#include <AK/Forward.h> #include <AK/Forward.h>
#include <LibGC/Ptr.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Runtime/AbstractOperations.h> #include <LibWeb/Forward.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/WebIDL/CallbackType.h>
namespace Web::WebIDL { namespace Web::WebIDL {
@ -21,77 +19,23 @@ bool is_buffer_source_type(JS::Value);
GC::Ptr<JS::ArrayBuffer> underlying_buffer_source(JS::Object& buffer_source); GC::Ptr<JS::ArrayBuffer> underlying_buffer_source(JS::Object& buffer_source);
ErrorOr<ByteBuffer> get_buffer_source_copy(JS::Object const& buffer_source); ErrorOr<ByteBuffer> get_buffer_source_copy(JS::Object const& buffer_source);
JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args); JS::Completion call_user_object_operation(CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args);
JS::ThrowCompletionOr<String> to_string(JS::VM&, JS::Value); JS::ThrowCompletionOr<String> to_string(JS::VM&, JS::Value);
JS::ThrowCompletionOr<String> to_usv_string(JS::VM&, JS::Value); JS::ThrowCompletionOr<String> to_usv_string(JS::VM&, JS::Value);
JS::ThrowCompletionOr<String> to_byte_string(JS::VM&, JS::Value); JS::ThrowCompletionOr<String> to_byte_string(JS::VM&, JS::Value);
// https://webidl.spec.whatwg.org/#call-a-user-objects-operation
template<typename... Args>
JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return call_user_object_operation(callback, operation_name, move(this_argument), move(arguments_list));
}
enum class ExceptionBehavior { enum class ExceptionBehavior {
NotSpecified, NotSpecified,
Report, Report,
Rethrow, Rethrow,
}; };
JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, ReadonlySpan<JS::Value> args);
JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args);
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, GC::RootVector<JS::Value> args); GC::Ref<Promise> invoke_promise_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args);
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args);
// https://webidl.spec.whatwg.org/#invoke-a-callback-function JS::Completion construct(CallbackType& callback, ReadonlySpan<JS::Value> args);
template<typename... Args>
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return invoke_callback(callback, move(this_argument), exception_behavior, move(arguments_list));
}
template<typename... Args>
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, Args&&... args)
{
return invoke_callback(callback, move(this_argument), ExceptionBehavior::NotSpecified, forward<Args>(args)...);
}
GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args);
template<typename... Args>
GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return invoke_promise_callback(callback, move(this_argument), move(arguments_list));
}
JS::Completion construct(WebIDL::CallbackType& callback, GC::RootVector<JS::Value> args);
// https://webidl.spec.whatwg.org/#construct-a-callback-function
template<typename... Args>
JS::Completion construct(WebIDL::CallbackType& callback, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return construct(callback, move(arguments_list));
}
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart // https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
double integer_part(double n); double integer_part(double n);