mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-22 17:01:54 +00:00
Our structured serialization implementation had its own bespoke encoder and decoder to serialize JS values. It also used a u32 buffer under the hood, which made using its structures a bit awkward. We had previously worked around its data structures in transferable streams, which nested transfers of MessagePort instances. We basically had to add hooks into the MessagePort to route to the correct transfer receiving steps, and we could not invoke the correct AOs directly as the spec dictates. We now use IPC mechanics to encode and decode data. This works because, although we are encoding JS values, we are only ultimately encoding primitive and basic AK types. The resulting data structures actually enforce that we implement transferable streams exactly as the spec is worded (I had planned to do that in a separate commit, but the fallout of this patch actually required that change).
133 lines
5.4 KiB
C++
133 lines
5.4 KiB
C++
/*
|
||
* Copyright (c) 2022-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||
*
|
||
* SPDX-License-Identifier: BSD-2-Clause
|
||
*/
|
||
|
||
#include <AK/Time.h>
|
||
#include <LibJS/Runtime/Completion.h>
|
||
#include <LibWeb/Bindings/FilePrototype.h>
|
||
#include <LibWeb/Bindings/Intrinsics.h>
|
||
#include <LibWeb/FileAPI/File.h>
|
||
#include <LibWeb/HTML/StructuredSerialize.h>
|
||
#include <LibWeb/Infra/Strings.h>
|
||
#include <LibWeb/MimeSniff/MimeType.h>
|
||
|
||
namespace Web::FileAPI {
|
||
|
||
GC_DEFINE_ALLOCATOR(File);
|
||
|
||
File::File(JS::Realm& realm, ByteBuffer byte_buffer, String file_name, String type, i64 last_modified)
|
||
: Blob(realm, move(byte_buffer), move(type))
|
||
, m_name(move(file_name))
|
||
, m_last_modified(last_modified)
|
||
{
|
||
}
|
||
|
||
File::File(JS::Realm& realm)
|
||
: Blob(realm, {})
|
||
{
|
||
}
|
||
|
||
void File::initialize(JS::Realm& realm)
|
||
{
|
||
WEB_SET_PROTOTYPE_FOR_INTERFACE(File);
|
||
Base::initialize(realm);
|
||
}
|
||
|
||
File::~File() = default;
|
||
|
||
GC::Ref<File> File::create(JS::Realm& realm)
|
||
{
|
||
return realm.create<File>(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)
|
||
{
|
||
auto& vm = realm.vm();
|
||
|
||
// 1. Let bytes be the result of processing blob parts given fileBits and options.
|
||
auto bytes = TRY_OR_THROW_OOM(vm, process_blob_parts(file_bits, options.has_value() ? static_cast<BlobPropertyBag const&>(*options) : Optional<BlobPropertyBag> {}));
|
||
|
||
// 2. Let n be the fileName argument to the constructor.
|
||
// NOTE: Underlying OS filesystems use differing conventions for file name; with constructed files, mandating UTF-16 lessens ambiquity when file names are converted to byte sequences.
|
||
auto name = file_name;
|
||
|
||
auto type = String {};
|
||
i64 last_modified = 0;
|
||
// 3. Process FilePropertyBag dictionary argument by running the following substeps:
|
||
if (options.has_value()) {
|
||
// FIXME: 1. If the type member is provided and is not the empty string, let t be set to the type dictionary member.
|
||
// If t contains any characters outside the range U+0020 to U+007E, then set t to the empty string and return from these substeps.
|
||
// FIXME: 2. Convert every character in t to ASCII lowercase.
|
||
|
||
// NOTE: The spec is out of date, and we are supposed to call into the MimeType parser here.
|
||
auto maybe_parsed_type = Web::MimeSniff::MimeType::parse(options->type);
|
||
|
||
if (maybe_parsed_type.has_value())
|
||
type = maybe_parsed_type->serialized();
|
||
|
||
// 3. If the lastModified member is provided, let d be set to the lastModified dictionary member. If it is not provided, set d to the current date and time represented as the number of milliseconds since the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]).
|
||
// Note: Since ECMA-262 Date objects convert to long long values representing the number of milliseconds since the Unix Epoch, the lastModified member could be a Date object [ECMA-262].
|
||
last_modified = options->last_modified.has_value() ? options->last_modified.value() : UnixDateTime::now().milliseconds_since_epoch();
|
||
}
|
||
|
||
// 4. Return a new File object F such that:
|
||
// 2. F refers to the bytes byte sequence.
|
||
// NOTE: Spec started at 2 therefore keeping the same number sequence here.
|
||
// 3. F.size is set to the number of total bytes in bytes.
|
||
// 4. F.name is set to n.
|
||
// 5. F.type is set to t.
|
||
// 6. F.lastModified is set to d.
|
||
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)
|
||
{
|
||
return create(realm, file_bits, file_name, options);
|
||
}
|
||
|
||
WebIDL::ExceptionOr<void> File::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&)
|
||
{
|
||
// FIXME: 1. Set serialized.[[SnapshotState]] to value’s snapshot state.
|
||
|
||
// NON-STANDARD: FileAPI spec doesn't specify that type should be serialized, although
|
||
// to be conformant with other browsers this needs to be serialized.
|
||
serialized.encode(m_type);
|
||
|
||
// 2. Set serialized.[[ByteSequence]] to value’s underlying byte sequence.
|
||
serialized.encode(m_byte_buffer);
|
||
|
||
// 3. Set serialized.[[Name]] to the value of value’s name attribute.
|
||
serialized.encode(m_name);
|
||
|
||
// 4. Set serialized.[[LastModified]] to the value of value’s lastModified attribute.
|
||
serialized.encode(m_last_modified);
|
||
|
||
return {};
|
||
}
|
||
|
||
WebIDL::ExceptionOr<void> File::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&)
|
||
{
|
||
auto& realm = this->realm();
|
||
|
||
// FIXME: 1. Set value’s snapshot state to serialized.[[SnapshotState]].
|
||
|
||
// NON-STANDARD: FileAPI spec doesn't specify that type should be deserialized, although
|
||
// to be conformant with other browsers this needs to be deserialized.
|
||
m_type = serialized.decode<String>();
|
||
|
||
// 2. Set value’s underlying byte sequence to serialized.[[ByteSequence]].
|
||
m_byte_buffer = TRY(serialized.decode_buffer(realm));
|
||
|
||
// 3. Initialize the value of value’s name attribute to serialized.[[Name]].
|
||
m_name = serialized.decode<String>();
|
||
|
||
// 4. Initialize the value of value’s lastModified attribute to serialized.[[LastModified]].
|
||
m_last_modified = serialized.decode<i64>();
|
||
|
||
return {};
|
||
}
|
||
|
||
}
|