Browse Source

LibWeb: Implement Blob::bytes()

Implements https://w3c.github.io/FileAPI/#dom-blob-bytes.
Kemal Zebari 11 months ago
parent
commit
c5f1e47883

+ 1 - 0
Tests/LibWeb/Text/expected/FileAPI/Blob-intialized-with-strings-read-from-bytes.txt

@@ -0,0 +1 @@
+PASS

+ 13 - 0
Tests/LibWeb/Text/input/FileAPI/Blob-intialized-with-strings-read-from-bytes.html

@@ -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>

+ 1 - 1
Userland/Libraries/LibWeb/Clipboard/Clipboard.cpp

@@ -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 item’s underlying byte sequence.
         // 3. Let payload be the result of UTF-8 decoding item’s underlying byte sequence.
         auto decoder = TextCodec::decoder_for("UTF-8"sv);
         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.
         // 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));
         window.page().client().page_did_insert_clipboard_entry(move(payload), move(presentation_style), move(format_string));

+ 1 - 1
Userland/Libraries/LibWeb/DOMURL/DOMURL.cpp

@@ -611,7 +611,7 @@ URL::URL parse(StringView input, Optional<URL::URL> const& base_url)
     if (blob_url_entry.has_value()) {
     if (blob_url_entry.has_value()) {
         url.set_blob_url_entry(URL::BlobURLEntry {
         url.set_blob_url_entry(URL::BlobURLEntry {
             .type = blob_url_entry->object->type(),
             .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())),
         });
         });
     }
     }
 
 

+ 2 - 2
Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp

@@ -841,7 +841,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> scheme_fetch(JS::Realm& r
         // 8. If request’s header list does not contain `Range`:
         // 8. If request’s header list does not contain `Range`:
         if (!request->header_list()->contains("Range"sv.bytes())) {
         if (!request->header_list()->contains("Range"sv.bytes())) {
             // 1. Let bodyWithType be the result of safely extracting blob.
             // 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 response’s status message to `OK`.
             // 2. Set response’s status message to `OK`.
             response->set_status_message(MUST(ByteBuffer::copy("OK"sv.bytes())));
             response->set_status_message(MUST(ByteBuffer::copy("OK"sv.bytes())));
@@ -2150,7 +2150,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> nonstandard_resource_load
                 return {};
                 return {};
             },
             },
             [&](JS::Handle<FileAPI::Blob> const& blob_handle) -> WebIDL::ExceptionOr<void> {
             [&](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 {};
                 return {};
             },
             },
             [](Empty) -> WebIDL::ExceptionOr<void> {
             [](Empty) -> WebIDL::ExceptionOr<void> {

+ 1 - 1
Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp

@@ -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));
                 error_steps(WebIDL::UnknownError::create(realm, "Out-of-memory"_fly_string));
         },
         },
         [&](JS::Handle<FileAPI::Blob> const& blob) {
         [&](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));
                 error_steps(WebIDL::UnknownError::create(realm, "Out-of-memory"_fly_string));
         },
         },
         [&](Empty) {
         [&](Empty) {

+ 27 - 1
Userland/Libraries/LibWeb/FileAPI/Blob.cpp

@@ -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.
             // 3. If element is a Blob, append the bytes it represents to bytes.
             [&](JS::Handle<Blob> const& blob) -> ErrorOr<void> {
             [&](JS::Handle<Blob> const& blob) -> ErrorOr<void> {
-                return bytes.try_append(blob->bytes());
+                return bytes.try_append(blob->raw_bytes());
             }));
             }));
     }
     }
     // 3. Return 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);
+    }));
+}
+
 }
 }

+ 2 - 1
Userland/Libraries/LibWeb/FileAPI/Blob.h

@@ -50,8 +50,9 @@ public:
     JS::NonnullGCPtr<Streams::ReadableStream> stream();
     JS::NonnullGCPtr<Streams::ReadableStream> stream();
     JS::NonnullGCPtr<JS::Promise> text();
     JS::NonnullGCPtr<JS::Promise> text();
     JS::NonnullGCPtr<JS::Promise> array_buffer();
     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();
     JS::NonnullGCPtr<Streams::ReadableStream> get_stream();
 
 

+ 1 - 0
Userland/Libraries/LibWeb/FileAPI/Blob.idl

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

+ 1 - 1
Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.cpp

@@ -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))));
                 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.
                 // 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(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));
                 TRY(builder.try_append("\r\n"sv));
                 return {};
                 return {};
             },
             },

+ 1 - 1
Userland/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp

@@ -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
                 // 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.
                 // 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.
                 // 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:
                 // FIXME:
                 // 2. Apply the image sniffing rules to determine the file format of imageData, with MIME type of
                 // 2. Apply the image sniffing rules to determine the file format of imageData, with MIME type of

+ 1 - 1
Userland/Libraries/LibWeb/WebSockets/WebSocket.cpp

@@ -233,7 +233,7 @@ WebIDL::ExceptionOr<void> WebSocket::send(Variant<JS::Handle<WebIDL::BufferSourc
                     return {};
                     return {};
                 },
                 },
                 [this](JS::Handle<FileAPI::Blob> const& blob) -> ErrorOr<void> {
                 [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);
                     m_websocket->send(byte_buffer, false);
                     return {};
                     return {};
                 }));
                 }));