Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibWeb/Streams: Fully implement BYOB operations for ReadableStream #3014

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Libraries/LibWeb/Streams/ReadableByteStreamController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ void ReadableByteStreamController::pull_steps(GC::Ref<ReadRequest> read_request)
readable_byte_stream_controller_call_pull_if_needed(*this);
}

// https://streams.spec.whatwg.org/#rbs-controller-private-pull
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontroller-releasesteps
void ReadableByteStreamController::release_steps()
{
// 1. If this.[[pendingPullIntos]] is not empty,
Expand Down
69 changes: 52 additions & 17 deletions Libraries/LibWeb/Streams/ReadableStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
#include <LibWeb/Streams/ReadableByteStreamController.h>
#include <LibWeb/Streams/ReadableStream.h>
#include <LibWeb/Streams/ReadableStreamBYOBReader.h>
#include <LibWeb/Streams/ReadableStreamBYOBRequest.h>
#include <LibWeb/Streams/ReadableStreamDefaultController.h>
#include <LibWeb/Streams/ReadableStreamDefaultReader.h>
#include <LibWeb/Streams/UnderlyingSource.h>
#include <LibWeb/Streams/WritableStream.h>
#include <LibWeb/WebIDL/Buffers.h>
#include <LibWeb/WebIDL/ExceptionOr.h>

namespace Web::Streams {
Expand Down Expand Up @@ -273,8 +276,10 @@ WebIDL::ExceptionOr<void> ReadableStream::pull_from_bytes(ByteBuffer bytes)
// 3. Let desiredSize be available.
auto desired_size = available;

// FIXME: 4. If stream’s current BYOB request view is non-null, then set desiredSize to stream’s current BYOB request
// view's byte length.
// 4. If stream’s current BYOB request view is non-null, then set desiredSize to stream’s current BYOB request
// view's byte length.
if (auto byob_view = current_byob_request_view())
desired_size = byob_view->byte_length();

// 5. Let pullSize be the smaller value of available and desiredSize.
auto pull_size = min(available, desired_size);
Expand All @@ -286,11 +291,16 @@ WebIDL::ExceptionOr<void> ReadableStream::pull_from_bytes(ByteBuffer bytes)
if (pull_size != available)
bytes = MUST(bytes.slice(pull_size, available - pull_size));

// FIXME: 8. If stream’s current BYOB request view is non-null, then:
// 1. Write pulled into stream’s current BYOB request view.
// 2. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize).
// 8. If stream’s current BYOB request view is non-null, then:
if (auto byob_view = current_byob_request_view()) {
// 1. Write pulled into stream’s current BYOB request view.
byob_view->write(pulled);

// 2. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize).
TRY(readable_byte_stream_controller_respond(controller, pull_size));
}
// 9. Otherwise,
{
else {
// 1. Set view to the result of creating a Uint8Array from pulled in stream’s relevant Realm.
auto array_buffer = JS::ArrayBuffer::create(realm, move(pulled));
auto view = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer);
Expand All @@ -302,6 +312,23 @@ WebIDL::ExceptionOr<void> ReadableStream::pull_from_bytes(ByteBuffer bytes)
return {};
}

// https://streams.spec.whatwg.org/#readablestream-current-byob-request-view
GC::Ptr<WebIDL::ArrayBufferView> ReadableStream::current_byob_request_view()
{
// 1. Assert: stream.[[controller]] implements ReadableByteStreamController.
VERIFY(m_controller->has<GC::Ref<ReadableByteStreamController>>());

// 2. Let byobRequest be ! ReadableByteStreamControllerGetBYOBRequest(stream.[[controller]]).
auto byob_request = readable_byte_stream_controller_get_byob_request(m_controller->get<GC::Ref<ReadableByteStreamController>>());

// 3. If byobRequest is null, then return null.
if (!byob_request)
return {};

// 4. Return byobRequest.[[view]].
return byob_request->view();
}

// https://streams.spec.whatwg.org/#readablestream-enqueue
WebIDL::ExceptionOr<void> ReadableStream::enqueue(JS::Value chunk)
{
Expand All @@ -310,31 +337,39 @@ WebIDL::ExceptionOr<void> ReadableStream::enqueue(JS::Value chunk)
// 1. If stream.[[controller]] implements ReadableStreamDefaultController,
if (m_controller->has<GC::Ref<ReadableStreamDefaultController>>()) {
// 1. Perform ! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], chunk).
return readable_stream_default_controller_enqueue(m_controller->get<GC::Ref<ReadableStreamDefaultController>>(), chunk);
MUST(readable_stream_default_controller_enqueue(m_controller->get<GC::Ref<ReadableStreamDefaultController>>(), chunk));
}
// 2. Otherwise,
else {
// 1. Assert: stream.[[controller]] implements ReadableByteStreamController.
VERIFY(m_controller->has<GC::Ref<ReadableByteStreamController>>());
auto readable_byte_controller = m_controller->get<GC::Ref<ReadableByteStreamController>>();

// FIXME: 2. Assert: chunk is an ArrayBufferView.
// 2. Assert: chunk is an ArrayBufferView.
VERIFY(chunk.is_object());
auto chunk_view = heap().allocate<WebIDL::ArrayBufferView>(chunk.as_object());

// 3. Let byobView be the current BYOB request view for stream.
// FIXME: This is not what the spec means by 'current BYOB request view'
auto byob_view = readable_byte_controller->raw_byob_request();
auto byob_view = current_byob_request_view();

// 4. If byobView is non-null, and chunk.[[ViewedArrayBuffer]] is byobView.[[ViewedArrayBuffer]], then:
if (byob_view) {
// FIXME: 1. Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]].
// FIXME: 2. Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]].
// FIXME: 3. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]]).
TODO();
}
if (byob_view && chunk_view->viewed_array_buffer() == byob_view->viewed_array_buffer()) {
// 1. Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]].
VERIFY(chunk_view->byte_offset() == byob_view->byte_offset());

// 2. Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]].
VERIFY(chunk_view->byte_length() <= byob_view->byte_length());

// 3. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]]).
TRY(readable_byte_stream_controller_respond(readable_byte_controller, chunk_view->byte_length()));
}
// 5. Otherwise, perform ? ReadableByteStreamControllerEnqueue(stream.[[controller]], chunk).
return readable_byte_stream_controller_enqueue(readable_byte_controller, chunk);
else {
TRY(readable_byte_stream_controller_enqueue(readable_byte_controller, chunk));
}
}

return {};
}

// https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Streams/ReadableStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class ReadableStream final : public Bindings::PlatformObject {
WebIDL::ExceptionOr<void> enqueue(JS::Value chunk);
void set_up_with_byte_reading_support(GC::Ptr<PullAlgorithm> = {}, GC::Ptr<CancelAlgorithm> = {}, double high_water_mark = 0);

GC::Ptr<WebIDL::ArrayBufferView> current_byob_request_view();

private:
explicit ReadableStream(JS::Realm&);

Expand Down
20 changes: 20 additions & 0 deletions Libraries/LibWeb/WebIDL/Buffers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,26 @@ u32 ArrayBufferView::byte_offset() const
[](auto& view) -> u32 { return static_cast<u32>(view->byte_offset()); });
}

// https://webidl.spec.whatwg.org/#arraybufferview-write
void ArrayBufferView::write(ReadonlyBytes bytes, u32 starting_offset)
{
// 1. Let jsView be the result of converting view to a JavaScript value.
// 2. Assert: bytes’s length ≤ jsView.[[ByteLength]] − startingOffset.
VERIFY(bytes.size() <= byte_length() - starting_offset);

// 3. Assert: if view is not a DataView, then bytes’s length modulo the element size of view’s type is 0.
if (!m_bufferable_object.has<GC::Ref<JS::DataView>>()) {
auto element_size = m_bufferable_object.get<GC::Ref<JS::TypedArrayBase>>()->element_size();
VERIFY(bytes.size() % element_size == 0);
}

// 4. Let arrayBuffer be the result of converting jsView.[[ViewedArrayBuffer]] to an IDL value of type ArrayBuffer.
auto array_buffer = viewed_array_buffer();

// 5. Write bytes into arrayBuffer with startingOffset set to jsView.[[ByteOffset]] + startingOffset.
array_buffer->buffer().overwrite(byte_offset() + starting_offset, bytes.data(), bytes.size());
}

BufferSource::~BufferSource() = default;

}
1 change: 1 addition & 0 deletions Libraries/LibWeb/WebIDL/Buffers.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ArrayBufferView : public BufferableObjectBase {
using BufferableObjectBase::is_typed_array_base;

u32 byte_offset() const;
void write(ReadonlyBytes, u32 starting_offset = 0);
};

// https://webidl.spec.whatwg.org/#BufferSource
Expand Down
Loading