1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-08 05:27:14 +09:00
ladybird/Libraries/LibWeb/WebIDL/AsyncIterator.h
Timothy Flynn c0ead1b01a LibIDL+LibWeb: Begin support for async iterator in IDL
This adds support for async iterators of the form:

    async iterable<value_type>;
    async iterable<value_type>(/* arguments... */);

It does not yet support the value pairs of the form:

    async iterable<key_type, value_type>;
    async iterable<key_type, value_type>(/* arguments... */);

Async iterators have an optional `return` data property. There's not a
particularly good way to know what interfaces implement this property.
So this adds a new extended attribute, DefinesAsyncIteratorReturn, which
interfaces can use to declare their support.
2025-04-14 17:43:11 -04:00

116 lines
4.7 KiB
C++

/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/StringView.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/PromiseCapability.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/VM.h>
#include <LibWeb/WebIDL/Promise.h>
namespace Web::WebIDL {
class AsyncIterator : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(AsyncIterator, Bindings::PlatformObject);
GC_DECLARE_ALLOCATOR(AsyncIterator);
public:
virtual ~AsyncIterator() override;
// https://webidl.spec.whatwg.org/#ref-for-dfn-asynchronous-iterator-prototype-object%E2%91%A2
template<typename AsyncIteratorInterface>
static JS::ThrowCompletionOr<GC::Ptr<JS::Object>> next(JS::Realm& realm, StringView interface_name)
{
auto validation_result = validate_this<AsyncIteratorInterface>(realm, interface_name);
return validation_result.visit(
[](GC::Ref<AsyncIteratorInterface> iterator) {
return iterator->iterator_next_impl();
},
[](JS::ThrowCompletionOr<GC::Ptr<JS::Object>> result) {
return result;
});
}
// https://webidl.spec.whatwg.org/#ref-for-asynchronous-iterator-return
template<typename AsyncIteratorInterface>
static JS::ThrowCompletionOr<GC::Ptr<JS::Object>> return_(JS::Realm& realm, StringView interface_name, JS::Value value)
{
auto return_promise_capability = WebIDL::create_promise(realm);
auto validation_result = validate_this<AsyncIteratorInterface>(realm, interface_name, return_promise_capability);
return validation_result.visit(
[&](GC::Ref<AsyncIteratorInterface> iterator) {
return iterator->iterator_return_impl(return_promise_capability, value);
},
[](JS::ThrowCompletionOr<GC::Ptr<JS::Object>> result) {
return result;
});
}
protected:
AsyncIterator(JS::Realm&, JS::Object::PropertyKind);
virtual void visit_edges(Cell::Visitor&) override;
virtual GC::Ref<WebIDL::Promise> next_iteration_result(JS::Realm&) = 0;
virtual GC::Ref<WebIDL::Promise> iterator_return(JS::Realm&, JS::Value);
private:
template<typename AsyncIteratorInterface>
static Variant<JS::Completion, GC::Ref<JS::Object>, GC::Ref<AsyncIteratorInterface>> validate_this(JS::Realm& realm, StringView interface_name, GC::Ptr<WebIDL::Promise> this_validation_promise_capability = {})
{
// NOTE: This defines the steps to validate `this` that are common between "next" and "return".
auto& vm = realm.vm();
// 1. Let interface be the interface for which the asynchronous iterator prototype object exists.
// 2. Let thisValidationPromiseCapability be ! NewPromiseCapability(%Promise%).
if (!this_validation_promise_capability)
this_validation_promise_capability = WebIDL::create_promise(realm);
// 3. Let thisValue be the this value.
auto this_value = vm.this_value();
// 4. Let object be Completion(ToObject(thisValue)).
// 5. IfAbruptRejectPromise(object, thisValidationPromiseCapability).
auto object = TRY_OR_REJECT(vm, this_validation_promise_capability, this_value.to_object(vm));
// FIXME: 6. If object is a platform object, then perform a security check, passing:
// * the platform object object,
// * the identifier "next", and
// * the type "method".
//
// If this threw an exception e, then:
// Perform ! Call(thisValidationPromiseCapability.[[Reject]], undefined, « e »).
// Return thisValidationPromiseCapability.[[Promise]].
// 7. If object is not a default asynchronous iterator object for interface, then:
auto* iterator = as_if<AsyncIteratorInterface>(*object);
if (!iterator) {
// 1. Let error be a new TypeError.
auto error = vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, interface_name);
// 2. Perform ! Call(thisValidationPromiseCapability.[[Reject]], undefined, « error »).
// 3. Return thisValidationPromiseCapability.[[Promise]].
TRY_OR_MUST_REJECT(vm, this_validation_promise_capability, error);
VERIFY_NOT_REACHED();
}
return GC::Ref { *iterator };
}
JS::ThrowCompletionOr<GC::Ptr<JS::Object>> iterator_next_impl();
JS::ThrowCompletionOr<GC::Ptr<JS::Object>> iterator_return_impl(GC::Ref<WebIDL::Promise> return_promise_capability, JS::Value);
JS::Object::PropertyKind m_kind { JS::Object::PropertyKind::Value };
GC::Ptr<JS::Promise> m_ongoing_promise;
bool m_is_finished { false };
};
}