LibWeb: Implement Blob::bytes()

Implements https://w3c.github.io/FileAPI/#dom-blob-bytes.
This commit is contained in:
Kemal Zebari 2024-07-23 23:48:01 -07:00 committed by Andreas Kling
parent 1ce9bbdd6d
commit c5f1e47883
Notes: github-actions[bot] 2024-07-26 08:22:29 +00:00
12 changed files with 52 additions and 10 deletions

View file

@ -0,0 +1,13 @@
<script src="../include.js"></script>
<script>
asyncTest(async (done) => {
const blob = new Blob(["This is some data to be read! 🦬"]);
const uint8Array = await blob.bytes();
const string = new TextDecoder().decode(uint8Array);
if (string === "This is some data to be read! 🦬")
println("PASS");
else
println("FAIL");
done();
});
</script>

View file

@ -111,7 +111,7 @@ static void write_blobs_and_option_to_clipboard(JS::Realm& realm, ReadonlySpan<J
// 3. Let payload be the result of UTF-8 decoding items underlying byte sequence.
auto decoder = TextCodec::decoder_for("UTF-8"sv);
auto payload = MUST(TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, item->bytes()));
auto payload = MUST(TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(*decoder, item->raw_bytes()));
// 4. Insert payload and presentationStyle into the system clipboard using formatString as the native clipboard format.
window.page().client().page_did_insert_clipboard_entry(move(payload), move(presentation_style), move(format_string));

View file

@ -611,7 +611,7 @@ URL::URL parse(StringView input, Optional<URL::URL> const& base_url)
if (blob_url_entry.has_value()) {
url.set_blob_url_entry(URL::BlobURLEntry {
.type = blob_url_entry->object->type(),
.byte_buffer = MUST(ByteBuffer::copy(blob_url_entry->object->bytes())),
.byte_buffer = MUST(ByteBuffer::copy(blob_url_entry->object->raw_bytes())),
});
}

View file

@ -841,7 +841,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> scheme_fetch(JS::Realm& r
// 8. If requests header list does not contain `Range`:
if (!request->header_list()->contains("Range"sv.bytes())) {
// 1. Let bodyWithType be the result of safely extracting blob.
auto body_with_type = TRY(safely_extract_body(realm, blob->bytes()));
auto body_with_type = TRY(safely_extract_body(realm, blob->raw_bytes()));
// 2. Set responses status message to `OK`.
response->set_status_message(MUST(ByteBuffer::copy("OK"sv.bytes())));
@ -2150,7 +2150,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> nonstandard_resource_load
return {};
},
[&](JS::Handle<FileAPI::Blob> const& blob_handle) -> WebIDL::ExceptionOr<void> {
load_request.set_body(TRY_OR_THROW_OOM(vm, ByteBuffer::copy(blob_handle->bytes())));
load_request.set_body(TRY_OR_THROW_OOM(vm, ByteBuffer::copy(blob_handle->raw_bytes())));
return {};
},
[](Empty) -> WebIDL::ExceptionOr<void> {

View file

@ -96,7 +96,7 @@ void Body::fully_read(JS::Realm& realm, Web::Fetch::Infrastructure::Body::Proces
error_steps(WebIDL::UnknownError::create(realm, "Out-of-memory"_fly_string));
},
[&](JS::Handle<FileAPI::Blob> const& blob) {
if (auto result = success_steps(blob->bytes()); result.is_error())
if (auto result = success_steps(blob->raw_bytes()); result.is_error())
error_steps(WebIDL::UnknownError::create(realm, "Out-of-memory"_fly_string));
},
[&](Empty) {

View file

@ -107,7 +107,7 @@ ErrorOr<ByteBuffer> process_blob_parts(Vector<BlobPart> const& blob_parts, Optio
},
// 3. If element is a Blob, append the bytes it represents to bytes.
[&](JS::Handle<Blob> const& blob) -> ErrorOr<void> {
return bytes.try_append(blob->bytes());
return bytes.try_append(blob->raw_bytes());
}));
}
// 3. Return bytes.
@ -413,4 +413,30 @@ JS::NonnullGCPtr<JS::Promise> Blob::array_buffer()
}));
}
// https://w3c.github.io/FileAPI/#dom-blob-bytes
JS::NonnullGCPtr<JS::Promise> Blob::bytes()
{
auto& realm = this->realm();
// 1. Let stream be the result of calling get stream on this.
auto stream = get_stream();
// 2. Let reader be the result of getting a reader from stream. If that threw an exception, return a new promise rejected with that exception.
auto reader_or_exception = acquire_readable_stream_default_reader(*stream);
if (reader_or_exception.is_exception())
return WebIDL::create_rejected_promise_from_exception(realm, reader_or_exception.release_error());
auto reader = reader_or_exception.release_value();
// 3. Let promise be the result of reading all bytes from stream with reader.
auto promise = reader->read_all_bytes_deprecated();
// 4. Return the result of transforming promise by a fulfillment handler that returns a new Uint8Array wrapping an ArrayBuffer containing its first argument.
return WebIDL::upon_fulfillment(*promise, JS::create_heap_function(heap(), [&realm](JS::Value first_argument) -> WebIDL::ExceptionOr<JS::Value> {
auto& object = first_argument.as_object();
VERIFY(is<JS::ArrayBuffer>(object));
auto& array_buffer = static_cast<JS::ArrayBuffer&>(object);
return JS::Uint8Array::create(realm, array_buffer.byte_length(), array_buffer);
}));
}
}

View file

@ -50,8 +50,9 @@ public:
JS::NonnullGCPtr<Streams::ReadableStream> stream();
JS::NonnullGCPtr<JS::Promise> text();
JS::NonnullGCPtr<JS::Promise> array_buffer();
JS::NonnullGCPtr<JS::Promise> bytes();
ReadonlyBytes bytes() const { return m_byte_buffer.bytes(); }
ReadonlyBytes raw_bytes() const { return m_byte_buffer.bytes(); }
JS::NonnullGCPtr<Streams::ReadableStream> get_stream();

View file

@ -15,6 +15,7 @@ interface Blob {
[NewObject] ReadableStream stream();
[NewObject] Promise<USVString> text();
[NewObject] Promise<ArrayBuffer> arrayBuffer();
[NewObject] Promise<Uint8Array> bytes();
};
enum EndingType { "transparent", "native" };

View file

@ -277,7 +277,7 @@ ErrorOr<SerializedFormData> serialize_to_multipart_form_data(Vector<XHR::FormDat
TRY(builder.try_append(TRY(String::formatted("Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n", escaped_name, escaped_filename))));
// The parts of the generated multipart/form-data resource that correspond to file fields must have a `Content-Type` header specified.
TRY(builder.try_append(TRY(String::formatted("Content-Type: {}\r\n\r\n", file->type()))));
TRY(builder.try_append(file->bytes()));
TRY(builder.try_append(file->raw_bytes()));
TRY(builder.try_append("\r\n"sv));
return {};
},

View file

@ -210,7 +210,7 @@ JS::NonnullGCPtr<JS::Promise> WindowOrWorkerGlobalScopeMixin::create_image_bitma
// 1. Let imageData be the result of reading image's data. If an error occurs during reading of the
// object, then reject p with an "InvalidStateError" DOMException and abort these steps.
// FIXME: I guess this is always fine for us as the data is already read.
auto const image_data = blob->bytes();
auto const image_data = blob->raw_bytes();
// FIXME:
// 2. Apply the image sniffing rules to determine the file format of imageData, with MIME type of

View file

@ -233,7 +233,7 @@ WebIDL::ExceptionOr<void> WebSocket::send(Variant<JS::Handle<WebIDL::BufferSourc
return {};
},
[this](JS::Handle<FileAPI::Blob> const& blob) -> ErrorOr<void> {
auto byte_buffer = TRY(ByteBuffer::copy(blob->bytes()));
auto byte_buffer = TRY(ByteBuffer::copy(blob->raw_bytes()));
m_websocket->send(byte_buffer, false);
return {};
}));