LibWeb/FileAPI: Make sure to always run the constructor steps for Blob

Previously we did not execute the constructor steps if we constructed a
Blob from a ByteBuffer and a String. Though we only construct a Blob
from ByteBuffer and String internally we still need to run these steps.

By creating the new type 'BlobPartsOrByteBuffer' we can consolidate
those two approaches to creating a Blob into our already existing
constructor steps.
This commit is contained in:
Kenneth Myhra 2025-09-03 18:13:57 +02:00 committed by Jelle Raaijmakers
commit a021134457
Notes: github-actions[bot] 2025-09-03 19:45:09 +00:00
4 changed files with 27 additions and 15 deletions

View file

@ -30,7 +30,11 @@ GC_DEFINE_ALLOCATOR(Blob);
GC::Ref<Blob> Blob::create(JS::Realm& realm, ByteBuffer byte_buffer, String type)
{
return realm.create<Blob>(realm, move(byte_buffer), move(type));
BlobPropertyBag options = {
.type = move(type),
.endings = Bindings::EndingType::Transparent
};
return create(realm, move(byte_buffer), move(options));
}
// https://w3c.github.io/FileAPI/#convert-line-endings-to-native
@ -81,7 +85,7 @@ ErrorOr<String> convert_line_endings_to_native(StringView string)
}
// https://w3c.github.io/FileAPI/#process-blob-parts
ErrorOr<ByteBuffer> process_blob_parts(Vector<BlobPart> const& blob_parts, Optional<BlobPropertyBag> const& options)
ErrorOr<ByteBuffer> process_blob_parts(BlobParts const& blob_parts, Optional<BlobPropertyBag> const& options)
{
// 1. Let bytes be an empty sequence of bytes.
ByteBuffer bytes {};
@ -182,16 +186,22 @@ WebIDL::ExceptionOr<void> Blob::deserialization_steps(HTML::TransferDataDecoder&
}
// https://w3c.github.io/FileAPI/#ref-for-dom-blob-blob
GC::Ref<Blob> Blob::create(JS::Realm& realm, Optional<Vector<BlobPart>> const& blob_parts, Optional<BlobPropertyBag> const& options)
GC::Ref<Blob> Blob::create(JS::Realm& realm, Optional<BlobPartsOrByteBuffer> const& blob_parts_or_byte_buffer, Optional<BlobPropertyBag> const& options)
{
// 1. If invoked with zero parameters, return a new Blob object consisting of 0 bytes, with size set to 0, and with type set to the empty string.
if (!blob_parts.has_value() && !options.has_value())
if (!blob_parts_or_byte_buffer.has_value() && !options.has_value())
return realm.create<Blob>(realm);
ByteBuffer byte_buffer {};
// 2. Let bytes be the result of processing blob parts given blobParts and options.
if (blob_parts.has_value()) {
byte_buffer = MUST(process_blob_parts(blob_parts.value(), options));
if (blob_parts_or_byte_buffer.has_value()) {
byte_buffer = blob_parts_or_byte_buffer->visit(
[&](BlobParts const& blob_parts) {
return MUST(process_blob_parts(blob_parts, options));
},
[](ByteBuffer const& byte_buffer) {
return byte_buffer;
});
}
auto type = String {};
@ -214,9 +224,9 @@ GC::Ref<Blob> Blob::create(JS::Realm& realm, Optional<Vector<BlobPart>> const& b
return realm.create<Blob>(realm, move(byte_buffer), move(type));
}
WebIDL::ExceptionOr<GC::Ref<Blob>> Blob::construct_impl(JS::Realm& realm, Optional<Vector<BlobPart>> const& blob_parts, Optional<BlobPropertyBag> const& options)
WebIDL::ExceptionOr<GC::Ref<Blob>> Blob::construct_impl(JS::Realm& realm, Optional<BlobParts> const& blob_parts, Optional<BlobPropertyBag> const& options)
{
return Blob::create(realm, blob_parts, options);
return create(realm, blob_parts.has_value() ? blob_parts.value() : Optional<BlobPartsOrByteBuffer> {}, options);
}
// https://w3c.github.io/FileAPI/#dfn-slice

View file

@ -18,6 +18,8 @@
namespace Web::FileAPI {
using BlobPart = Variant<GC::Root<WebIDL::BufferSource>, GC::Root<Blob>, String>;
using BlobParts = Vector<BlobPart>;
using BlobPartsOrByteBuffer = Variant<BlobParts, ByteBuffer>;
struct BlobPropertyBag {
String type = String {};
@ -25,7 +27,7 @@ struct BlobPropertyBag {
};
[[nodiscard]] ErrorOr<String> convert_line_endings_to_native(StringView string);
[[nodiscard]] ErrorOr<ByteBuffer> process_blob_parts(Vector<BlobPart> const& blob_parts, Optional<BlobPropertyBag> const& options = {});
[[nodiscard]] ErrorOr<ByteBuffer> process_blob_parts(BlobParts const& blob_parts, Optional<BlobPropertyBag> const& options = {});
[[nodiscard]] bool is_basic_latin(StringView view);
class WEB_API Blob
@ -38,8 +40,8 @@ public:
virtual ~Blob() override;
[[nodiscard]] static GC::Ref<Blob> create(JS::Realm&, ByteBuffer, String type);
[[nodiscard]] static GC::Ref<Blob> create(JS::Realm&, Optional<Vector<BlobPart>> const& blob_parts = {}, Optional<BlobPropertyBag> const& options = {});
static WebIDL::ExceptionOr<GC::Ref<Blob>> construct_impl(JS::Realm&, Optional<Vector<BlobPart>> const& blob_parts = {}, Optional<BlobPropertyBag> const& options = {});
[[nodiscard]] static GC::Ref<Blob> create(JS::Realm&, Optional<BlobPartsOrByteBuffer> const& blob_parts_or_byte_buffer = {}, Optional<BlobPropertyBag> const& options = {});
static WebIDL::ExceptionOr<GC::Ref<Blob>> construct_impl(JS::Realm&, Optional<BlobParts> const& blob_parts = {}, Optional<BlobPropertyBag> const& options = {});
// https://w3c.github.io/FileAPI/#dfn-size
u64 size() const { return m_byte_buffer.size(); }

View file

@ -43,7 +43,7 @@ GC::Ref<File> File::create(JS::Realm& realm)
}
// https://w3c.github.io/FileAPI/#ref-for-dom-file-file
WebIDL::ExceptionOr<GC::Ref<File>> File::create(JS::Realm& realm, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options)
WebIDL::ExceptionOr<GC::Ref<File>> File::create(JS::Realm& realm, BlobParts const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options)
{
auto& vm = realm.vm();
@ -83,7 +83,7 @@ WebIDL::ExceptionOr<GC::Ref<File>> File::create(JS::Realm& realm, Vector<BlobPar
return realm.create<File>(realm, move(bytes), move(name), move(type), last_modified);
}
WebIDL::ExceptionOr<GC::Ref<File>> File::construct_impl(JS::Realm& realm, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options)
WebIDL::ExceptionOr<GC::Ref<File>> File::construct_impl(JS::Realm& realm, BlobParts const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options)
{
return create(realm, file_bits, file_name, options);
}

View file

@ -20,8 +20,8 @@ class File : public Blob {
public:
static GC::Ref<File> create(JS::Realm& realm);
static WebIDL::ExceptionOr<GC::Ref<File>> create(JS::Realm&, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options = {});
static WebIDL::ExceptionOr<GC::Ref<File>> construct_impl(JS::Realm&, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options = {});
static WebIDL::ExceptionOr<GC::Ref<File>> create(JS::Realm&, BlobParts const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options = {});
static WebIDL::ExceptionOr<GC::Ref<File>> construct_impl(JS::Realm&, BlobParts const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options = {});
virtual ~File() override;