summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/Streams
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2023-03-28 18:30:22 -0700
committerLinus Groh <mail@linusgroh.de>2023-04-01 23:43:07 +0100
commit222e3c32cdd7e1d58146d2cea5e7cb93c672a780 (patch)
tree9b45251c8a3f02a4c8ea8d854be265a141e41893 /Userland/Libraries/LibWeb/Streams
parent7ff657ef57eddf7506f086f890e6114b56c94e66 (diff)
downloadserenity-222e3c32cdd7e1d58146d2cea5e7cb93c672a780.zip
LibWeb: Add ReadableStreamDefaultReader
Diffstat (limited to 'Userland/Libraries/LibWeb/Streams')
-rw-r--r--Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp167
-rw-r--r--Userland/Libraries/LibWeb/Streams/AbstractOperations.h7
-rw-r--r--Userland/Libraries/LibWeb/Streams/ReadableStream.cpp2
-rw-r--r--Userland/Libraries/LibWeb/Streams/ReadableStream.h10
-rw-r--r--Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp122
-rw-r--r--Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h60
-rw-r--r--Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.idl17
-rw-r--r--Userland/Libraries/LibWeb/Streams/ReadableStreamGenericReader.h4
8 files changed, 381 insertions, 8 deletions
diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp
index 3f12814e09..fc9bc45020 100644
--- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp
+++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp
@@ -7,8 +7,10 @@
#include <LibWeb/Streams/AbstractOperations.h>
#include <LibWeb/Streams/ReadableStream.h>
+#include <LibWeb/Streams/ReadableStreamDefaultReader.h>
#include <LibWeb/Streams/ReadableStreamGenericReader.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
+#include <LibWeb/WebIDL/Promise.h>
namespace Web::Streams {
@@ -16,7 +18,7 @@ namespace Web::Streams {
bool is_readable_stream_locked(ReadableStream const& stream)
{
// 1. If stream.[[reader]] is undefined, return false.
- if (stream.reader() == nullptr)
+ if (!stream.reader())
return false;
// 2. Return true.
@@ -80,12 +82,18 @@ void readable_stream_close(ReadableStream& stream)
// 5. Resolve reader.[[closedPromise]] with undefined.
WebIDL::resolve_promise(realm, *reader->closed_promise_capability());
- // FIXME:
// 6. If reader implements ReadableStreamDefaultReader,
- // 1. Let readRequests be reader.[[readRequests]].
- // 2. Set reader.[[readRequests]] to an empty list.
- // 3. For each readRequest of readRequests,
- // 1. Perform readRequest’s close steps.
+ if (reader->is_default_reader()) {
+ // 1. Let readRequests be reader.[[readRequests]].
+ // 2. Set reader.[[readRequests]] to an empty list.
+ auto read_requests = move(reader->read_requests());
+
+ // 3. For each readRequest of readRequests,
+ for (auto& read_request : read_requests) {
+ // 1. Perform readRequest’s close steps.
+ read_request->on_close();
+ }
+ }
}
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel
@@ -101,4 +109,151 @@ JS::NonnullGCPtr<WebIDL::Promise> readable_stream_reader_generic_cancel(Readable
return MUST(readable_stream_cancel(*stream, reason));
}
+// https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize
+void readable_stream_reader_generic_initialize(ReadableStreamGenericReaderMixin& reader, ReadableStream& stream)
+{
+ auto& realm = stream.realm();
+
+ // 1. Set reader.[[stream]] to stream.
+ reader.set_stream(stream);
+
+ // 2. Set stream.[[reader]] to reader.
+ if (reader.is_default_reader()) {
+ stream.set_reader(static_cast<ReadableStreamDefaultReader&>(reader));
+ } else {
+ // FIXME: Handle other descendents of ReadableStreamGenericReaderMixin (i.e. BYOBReader)
+ TODO();
+ }
+
+ // 3. If stream.[[state]] is "readable",
+ if (stream.is_readable()) {
+ // 1. Set reader.[[closedPromise]] to a new promise.
+ reader.set_closed_promise_capability(WebIDL::create_promise(realm));
+ }
+ // 4. Otherwise, if stream.[[state]] is "closed",
+ else if (stream.is_closed()) {
+ // 1. Set reader.[[closedPromise]] to a promise resolved with undefined.
+ reader.set_closed_promise_capability(WebIDL::create_resolved_promise(realm, JS::js_undefined()));
+ }
+ // 5. Otherwise,
+ else {
+ // 1. Assert: stream.[[state]] is "errored".
+ VERIFY(stream.is_errored());
+
+ // 2. Set reader.[[closedPromise]] to a promise rejected with stream.[[storedError]].
+ reader.set_closed_promise_capability(WebIDL::create_rejected_promise(realm, stream.stored_error()));
+
+ // 3. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
+ WebIDL::mark_promise_as_handled(*reader.closed_promise_capability());
+ }
+}
+
+// https://streams.spec.whatwg.org/#readable-stream-reader-generic-release
+WebIDL::ExceptionOr<void> readable_stream_reader_generic_release(ReadableStreamGenericReaderMixin& reader)
+{
+ // 1. Let stream be reader.[[stream]].
+ auto stream = reader.stream();
+
+ // 2. Assert: stream is not undefined.
+ VERIFY(stream);
+
+ // 3. Assert: stream.[[reader]] is reader.
+ VERIFY(stream->reader().ptr() == &reader);
+
+ // 4. If stream.[[state]] is "readable", reject reader.[[closedPromise]] with a TypeError exception.
+ auto exception = TRY(JS::TypeError::create(stream->realm(), "Released readable stream"sv));
+ if (stream->is_readable()) {
+ WebIDL::reject_promise(stream->realm(), *reader.closed_promise_capability(), exception);
+ }
+ // 5. Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception.
+ else {
+ reader.set_closed_promise_capability(WebIDL::create_rejected_promise(stream->realm(), exception));
+ }
+
+ // 6. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
+ WebIDL::mark_promise_as_handled(*reader.closed_promise_capability());
+
+ // FIXME: 7. Perform ! stream.[[controller]].[[ReleaseSteps]]().
+
+ // 8. Set stream.[[reader]] to undefined.
+ stream->set_reader({});
+
+ // 9. Set reader.[[stream]] to undefined.
+ reader.set_stream({});
+
+ return {};
+}
+
+// https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreadererrorreadrequests
+void readable_stream_default_reader_error_read_requests(ReadableStreamDefaultReader& reader, JS::Value error)
+{
+ // 1. Let readRequests be reader.[[readRequests]].
+ // 2. Set reader.[[readRequests]] to a new empty list.
+ auto read_requests = move(reader.read_requests());
+
+ // 3. For each readRequest of readRequests,
+ for (auto& read_request : read_requests) {
+ // 1. Perform readRequest’s error steps, given e.
+ read_request->on_error(error);
+ }
+}
+
+// https://streams.spec.whatwg.org/#readable-stream-default-reader-read
+void readable_stream_default_reader_read(ReadableStreamDefaultReader& reader, ReadRequest& read_request)
+{
+ // 1. Let stream be reader.[[stream]].
+ auto stream = reader.stream();
+
+ // 2. Assert: stream is not undefined.
+ VERIFY(stream);
+
+ // 3. Set stream.[[disturbed]] to true.
+ stream->set_disturbed(true);
+
+ // 4. If stream.[[state]] is "closed", perform readRequest’s close steps.
+ if (stream->is_closed()) {
+ read_request.on_close();
+ }
+ // 5. Otherwise, if stream.[[state]] is "errored", perform readRequest’s error steps given stream.[[storedError]].
+ else if (stream->is_errored()) {
+ read_request.on_error(stream->stored_error());
+ }
+ // 6. Otherwise,
+ else {
+ // 1. Assert: stream.[[state]] is "readable".
+ VERIFY(stream->is_readable());
+
+ // FIXME: 2. Perform ! stream.[[controller]].[[PullSteps]](readRequest).
+ }
+}
+
+// https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease
+WebIDL::ExceptionOr<void> readable_stream_default_reader_release(ReadableStreamDefaultReader& reader)
+{
+ // 1. Perform ! ReadableStreamReaderGenericRelease(reader).
+ TRY(readable_stream_reader_generic_release(reader));
+
+ // 2. Let e be a new TypeError exception.
+ auto e = TRY(JS::TypeError::create(reader.realm(), "Reader has been released"sv));
+
+ // 3. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
+ readable_stream_default_reader_error_read_requests(reader, e);
+
+ return {};
+}
+
+// https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
+WebIDL::ExceptionOr<void> set_up_readable_stream_default_reader(ReadableStreamDefaultReader& reader, ReadableStream& stream)
+{
+ // 1. If ! IsReadableStreamLocked(stream) is true, throw a TypeError exception.
+ if (is_readable_stream_locked(stream))
+ return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot create stream reader for a locked stream"sv };
+
+ // 2. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
+ // 3. Set reader.[[readRequests]] to a new empty list.
+ readable_stream_reader_generic_initialize(reader, stream);
+
+ return {};
+}
+
}
diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h
index 2d83d34492..e419bc2403 100644
--- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h
+++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h
@@ -20,5 +20,12 @@ void readable_stream_close(ReadableStream&);
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> readable_stream_cancel(ReadableStream&, JS::Value reason);
JS::NonnullGCPtr<WebIDL::Promise> readable_stream_reader_generic_cancel(ReadableStreamGenericReaderMixin&, JS::Value reason);
+void readable_stream_reader_generic_initialize(ReadableStreamGenericReaderMixin&, ReadableStream&);
+WebIDL::ExceptionOr<void> readable_stream_reader_generic_release(ReadableStreamGenericReaderMixin&);
+
+void readable_stream_default_reader_error_read_requests(ReadableStreamDefaultReader&, JS::Value error);
+void readable_stream_default_reader_read(ReadableStreamDefaultReader&, ReadRequest&);
+WebIDL::ExceptionOr<void> readable_stream_default_reader_release(ReadableStreamDefaultReader&);
+WebIDL::ExceptionOr<void> set_up_readable_stream_default_reader(ReadableStreamDefaultReader&, ReadableStream&);
}
diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp b/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp
index e931193f3a..6e96ec2c2e 100644
--- a/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp
+++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp
@@ -7,6 +7,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Streams/AbstractOperations.h>
#include <LibWeb/Streams/ReadableStream.h>
+#include <LibWeb/Streams/ReadableStreamDefaultReader.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::Streams {
@@ -37,6 +38,7 @@ void ReadableStream::visit_edges(Cell::Visitor& visitor)
Base::visit_edges(visitor);
visitor.visit(m_controller);
visitor.visit(m_stored_error);
+ visitor.visit(m_reader);
}
// https://streams.spec.whatwg.org/#readablestream-locked
diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.h b/Userland/Libraries/LibWeb/Streams/ReadableStream.h
index cb24384335..174a790f74 100644
--- a/Userland/Libraries/LibWeb/Streams/ReadableStream.h
+++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.h
@@ -13,6 +13,10 @@
namespace Web::Streams {
+// FIXME: Variant<DefaultReader, ByteStreamReader>
+// https://streams.spec.whatwg.org/#typedefdef-readablestreamreader
+using ReadableStreamReader = JS::GCPtr<ReadableStreamDefaultReader>;
+
// https://streams.spec.whatwg.org/#readablestream
class ReadableStream final : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(ReadableStream, Bindings::PlatformObject);
@@ -29,9 +33,11 @@ public:
virtual ~ReadableStream() override;
JS::GCPtr<JS::Object> controller() const { return m_controller; }
- ReadableStreamGenericReaderMixin* reader() const { return m_reader; }
JS::Value stored_error() const { return m_stored_error; }
+ ReadableStreamReader reader() const { return m_reader; }
+ void set_reader(ReadableStreamReader value) { m_reader = value; }
+
bool is_readable() const;
bool is_closed() const;
bool is_errored() const;
@@ -61,7 +67,7 @@ private:
// https://streams.spec.whatwg.org/#readablestream-reader
// A ReadableStreamDefaultReader or ReadableStreamBYOBReader instance, if the stream is locked to a reader, or undefined if it is not
- ReadableStreamGenericReaderMixin* m_reader;
+ ReadableStreamReader m_reader;
// https://streams.spec.whatwg.org/#readablestream-state
// A string containing the stream’s current state, used internally; one of "readable", "closed", or "errored"
diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp
new file mode 100644
index 0000000000..271314db6e
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+#include <LibJS/Runtime/PromiseCapability.h>
+#include <LibJS/Runtime/Realm.h>
+#include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/Bindings/ReadableStreamDefaultReaderPrototype.h>
+#include <LibWeb/Streams/AbstractOperations.h>
+#include <LibWeb/Streams/ReadableStream.h>
+#include <LibWeb/Streams/ReadableStreamDefaultReader.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
+#include <LibWeb/WebIDL/Promise.h>
+
+namespace Web::Streams {
+
+// https://streams.spec.whatwg.org/#default-reader-constructor
+WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStreamDefaultReader>> ReadableStreamDefaultReader::construct_impl(JS::Realm& realm, JS::NonnullGCPtr<ReadableStream> stream)
+{
+ auto reader = TRY(realm.heap().allocate<ReadableStreamDefaultReader>(realm, realm));
+
+ // 1. Perform ? SetUpReadableStreamDefaultReader(this, stream);
+ TRY(set_up_readable_stream_default_reader(reader, *stream));
+
+ return reader;
+}
+
+ReadableStreamDefaultReader::ReadableStreamDefaultReader(JS::Realm& realm)
+ : Bindings::PlatformObject(realm)
+{
+}
+
+JS::ThrowCompletionOr<void> ReadableStreamDefaultReader::initialize(JS::Realm& realm)
+{
+ MUST_OR_THROW_OOM(Base::initialize(realm));
+ set_prototype(&Bindings::ensure_web_prototype<Bindings::ReadableStreamDefaultReaderPrototype>(realm, "ReadableStreamDefaultReader"));
+
+ return {};
+}
+
+void ReadableStreamDefaultReader::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ ReadableStreamGenericReaderMixin::visit_edges(visitor);
+}
+
+class DefaultReaderReadRequest : public ReadRequest {
+public:
+ DefaultReaderReadRequest(JS::Realm& realm, WebIDL::Promise& promise)
+ : m_realm(realm)
+ , m_promise(promise)
+ {
+ }
+
+ virtual void on_chunk(JS::Value chunk) override
+ {
+ WebIDL::resolve_promise(m_realm, m_promise, JS::create_iterator_result_object(m_realm.vm(), chunk, false));
+ }
+
+ virtual void on_close() override
+ {
+ WebIDL::resolve_promise(m_realm, m_promise, JS::create_iterator_result_object(m_realm.vm(), JS::js_undefined(), true));
+ }
+
+ virtual void on_error(JS::Value error) override
+ {
+ WebIDL::reject_promise(m_realm, m_promise, error);
+ }
+
+private:
+ JS::Realm& m_realm;
+ WebIDL::Promise& m_promise;
+};
+
+// https://streams.spec.whatwg.org/#default-reader-read
+WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> ReadableStreamDefaultReader::read()
+{
+ auto& realm = this->realm();
+
+ // 1. If this.[[stream]] is undefined, return a promise rejected with a TypeError exception.
+ if (!m_stream) {
+ auto exception = TRY(JS::TypeError::create(realm, "Cannot read from an empty stream"sv));
+ auto promise_capability = WebIDL::create_rejected_promise(realm, exception);
+ return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+ }
+
+ // 2. Let promise be a new promise.
+ auto promise_capability = WebIDL::create_promise(realm);
+
+ // 3. Let readRequest be a new read request with the following items:
+ // chunk steps, given chunk
+ // Resolve promise with «[ "value" → chunk, "done" → false ]».
+ // close steps
+ // Resolve promise with «[ "value" → undefined, "done" → true ]».
+ // error steps, given e
+ // Reject promise with e.
+ auto read_request = adopt_ref(*new DefaultReaderReadRequest(realm, promise_capability));
+
+ // 4. Perform ! ReadableStreamDefaultReaderRead(this, readRequest).
+ readable_stream_default_reader_read(*this, read_request);
+
+ // 5. Return promise.
+ return JS::NonnullGCPtr { verify_cast<JS::Promise>(*promise_capability->promise()) };
+}
+
+// https://streams.spec.whatwg.org/#default-reader-release-lock
+WebIDL::ExceptionOr<void> ReadableStreamDefaultReader::release_lock()
+{
+ // 1. If this.[[stream]] is undefined, return.
+ if (!m_stream)
+ return {};
+
+ // 2. Perform ! ReadableStreamDefaultReaderRelease(this).
+ return readable_stream_default_reader_release(*this);
+}
+
+}
diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h
new file mode 100644
index 0000000000..c67bab2541
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/SinglyLinkedList.h>
+#include <LibJS/Forward.h>
+#include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/Forward.h>
+#include <LibWeb/Streams/ReadableStreamGenericReader.h>
+
+namespace Web::Streams {
+
+struct ReadableStreamReadResult {
+ JS::Value value;
+ bool done;
+};
+
+class ReadRequest : public RefCounted<ReadRequest> {
+public:
+ virtual ~ReadRequest() = default;
+
+ virtual void on_chunk(JS::Value chunk) = 0;
+ virtual void on_close() = 0;
+ virtual void on_error(JS::Value error) = 0;
+};
+
+// https://streams.spec.whatwg.org/#readablestreamdefaultreader
+class ReadableStreamDefaultReader final
+ : public Bindings::PlatformObject
+ , public ReadableStreamGenericReaderMixin {
+ WEB_PLATFORM_OBJECT(ReadableStreamDefaultReader, Bindings::PlatformObject);
+
+public:
+ static WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStreamDefaultReader>> construct_impl(JS::Realm&, JS::NonnullGCPtr<ReadableStream>);
+
+ virtual ~ReadableStreamDefaultReader() override = default;
+
+ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> read();
+ WebIDL::ExceptionOr<void> release_lock();
+
+ SinglyLinkedList<NonnullRefPtr<ReadRequest>>& read_requests() { return m_read_requests; }
+
+ virtual bool is_default_reader() const override { return true; }
+
+private:
+ explicit ReadableStreamDefaultReader(JS::Realm&);
+
+ virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
+
+ virtual void visit_edges(Cell::Visitor&) override;
+
+ SinglyLinkedList<NonnullRefPtr<ReadRequest>> m_read_requests;
+};
+
+}
diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.idl b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.idl
new file mode 100644
index 0000000000..1507aa3fa8
--- /dev/null
+++ b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.idl
@@ -0,0 +1,17 @@
+#import <Streams/ReadableStream.idl>
+#import <Streams/ReadableStreamGenericReader.idl>
+
+// https://streams.spec.whatwg.org/#readablestreamdefaultreader
+[Exposed=*]
+interface ReadableStreamDefaultReader {
+ constructor(ReadableStream stream);
+
+ Promise<ReadableStreamReadResult> read();
+ undefined releaseLock();
+};
+ReadableStreamDefaultReader includes ReadableStreamGenericReader;
+
+dictionary ReadableStreamReadResult {
+ any value;
+ boolean done;
+};
diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStreamGenericReader.h b/Userland/Libraries/LibWeb/Streams/ReadableStreamGenericReader.h
index 3bec0d62f3..2934c18724 100644
--- a/Userland/Libraries/LibWeb/Streams/ReadableStreamGenericReader.h
+++ b/Userland/Libraries/LibWeb/Streams/ReadableStreamGenericReader.h
@@ -17,6 +17,8 @@ namespace Web::Streams {
// https://streams.spec.whatwg.org/#readablestreamgenericreader
class ReadableStreamGenericReaderMixin {
public:
+ virtual ~ReadableStreamGenericReaderMixin() = default;
+
WebIDL::ExceptionOr<JS::GCPtr<JS::Promise>> closed();
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> cancel(JS::Value reason);
@@ -27,6 +29,8 @@ public:
JS::GCPtr<WebIDL::Promise> closed_promise_capability() { return m_closed_promise; }
void set_closed_promise_capability(JS::GCPtr<WebIDL::Promise> promise) { m_closed_promise = promise; }
+ virtual bool is_default_reader() const { return false; }
+
protected:
void visit_edges(JS::Cell::Visitor&);