/src/serenity/Userland/Libraries/LibWeb/FileAPI/File.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2022-2024, Kenneth Myhra <kennethmyhra@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Time.h> |
8 | | #include <LibJS/Runtime/Completion.h> |
9 | | #include <LibWeb/Bindings/FilePrototype.h> |
10 | | #include <LibWeb/Bindings/Intrinsics.h> |
11 | | #include <LibWeb/FileAPI/File.h> |
12 | | #include <LibWeb/Infra/Strings.h> |
13 | | #include <LibWeb/MimeSniff/MimeType.h> |
14 | | |
15 | | namespace Web::FileAPI { |
16 | | |
17 | | JS_DEFINE_ALLOCATOR(File); |
18 | | |
19 | | File::File(JS::Realm& realm, ByteBuffer byte_buffer, String file_name, String type, i64 last_modified) |
20 | 0 | : Blob(realm, move(byte_buffer), move(type)) |
21 | 0 | , m_name(move(file_name)) |
22 | 0 | , m_last_modified(last_modified) |
23 | 0 | { |
24 | 0 | } |
25 | | |
26 | | File::File(JS::Realm& realm) |
27 | 0 | : Blob(realm, {}) |
28 | 0 | { |
29 | 0 | } |
30 | | |
31 | | void File::initialize(JS::Realm& realm) |
32 | 0 | { |
33 | 0 | Base::initialize(realm); |
34 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(File); |
35 | 0 | } |
36 | | |
37 | 0 | File::~File() = default; |
38 | | |
39 | | JS::NonnullGCPtr<File> File::create(JS::Realm& realm) |
40 | 0 | { |
41 | 0 | return realm.heap().allocate<File>(realm, realm); |
42 | 0 | } |
43 | | |
44 | | // https://w3c.github.io/FileAPI/#ref-for-dom-file-file |
45 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<File>> File::create(JS::Realm& realm, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options) |
46 | 0 | { |
47 | 0 | auto& vm = realm.vm(); |
48 | | |
49 | | // 1. Let bytes be the result of processing blob parts given fileBits and options. |
50 | 0 | auto bytes = TRY_OR_THROW_OOM(vm, process_blob_parts(file_bits, options.has_value() ? static_cast<BlobPropertyBag const&>(*options) : Optional<BlobPropertyBag> {})); |
51 | | |
52 | | // 2. Let n be the fileName argument to the constructor. |
53 | | // 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. |
54 | 0 | auto name = file_name; |
55 | |
|
56 | 0 | auto type = String {}; |
57 | 0 | i64 last_modified = 0; |
58 | | // 3. Process FilePropertyBag dictionary argument by running the following substeps: |
59 | 0 | if (options.has_value()) { |
60 | | // FIXME: 1. If the type member is provided and is not the empty string, let t be set to the type dictionary member. |
61 | | // 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. |
62 | | // FIXME: 2. Convert every character in t to ASCII lowercase. |
63 | | |
64 | | // NOTE: The spec is out of date, and we are supposed to call into the MimeType parser here. |
65 | 0 | auto maybe_parsed_type = Web::MimeSniff::MimeType::parse(options->type); |
66 | |
|
67 | 0 | if (maybe_parsed_type.has_value()) |
68 | 0 | type = maybe_parsed_type->serialized(); |
69 | | |
70 | | // 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]). |
71 | | // 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]. |
72 | 0 | last_modified = options->last_modified.has_value() ? options->last_modified.value() : UnixDateTime::now().milliseconds_since_epoch(); |
73 | 0 | } |
74 | | |
75 | | // 4. Return a new File object F such that: |
76 | | // 2. F refers to the bytes byte sequence. |
77 | | // NOTE: Spec started at 2 therefore keeping the same number sequence here. |
78 | | // 3. F.size is set to the number of total bytes in bytes. |
79 | | // 4. F.name is set to n. |
80 | | // 5. F.type is set to t. |
81 | | // 6. F.lastModified is set to d. |
82 | 0 | return realm.heap().allocate<File>(realm, realm, move(bytes), move(name), move(type), last_modified); |
83 | 0 | } |
84 | | |
85 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<File>> File::construct_impl(JS::Realm& realm, Vector<BlobPart> const& file_bits, String const& file_name, Optional<FilePropertyBag> const& options) |
86 | 0 | { |
87 | 0 | return create(realm, file_bits, file_name, options); |
88 | 0 | } |
89 | | |
90 | | WebIDL::ExceptionOr<void> File::serialization_steps(HTML::SerializationRecord& record, bool, HTML::SerializationMemory&) |
91 | 0 | { |
92 | 0 | auto& vm = this->vm(); |
93 | | |
94 | | // FIXME: 1. Set serialized.[[SnapshotState]] to value’s snapshot state. |
95 | | |
96 | | // NON-STANDARD: FileAPI spec doesn't specify that type should be serialized, although |
97 | | // to be conformant with other browsers this needs to be serialized. |
98 | 0 | TRY(HTML::serialize_string(vm, record, m_type)); |
99 | | |
100 | | // 2. Set serialized.[[ByteSequence]] to value’s underlying byte sequence. |
101 | 0 | TRY(HTML::serialize_bytes(vm, record, m_byte_buffer.bytes())); |
102 | | |
103 | | // 3. Set serialized.[[Name]] to the value of value’s name attribute. |
104 | 0 | TRY(HTML::serialize_string(vm, record, m_name)); |
105 | | |
106 | | // 4. Set serialized.[[LastModified]] to the value of value’s lastModified attribute. |
107 | 0 | HTML::serialize_primitive_type(record, m_last_modified); |
108 | |
|
109 | 0 | return {}; |
110 | 0 | } |
111 | | |
112 | | WebIDL::ExceptionOr<void> File::deserialization_steps(ReadonlySpan<u32> const& record, size_t& position, HTML::DeserializationMemory&) |
113 | 0 | { |
114 | 0 | auto& vm = this->vm(); |
115 | | |
116 | | // FIXME: 1. Set value’s snapshot state to serialized.[[SnapshotState]]. |
117 | | |
118 | | // NON-STANDARD: FileAPI spec doesn't specify that type should be deserialized, although |
119 | | // to be conformant with other browsers this needs to be deserialized. |
120 | 0 | m_type = TRY(HTML::deserialize_string(vm, record, position)); |
121 | | |
122 | | // 2. Set value’s underlying byte sequence to serialized.[[ByteSequence]]. |
123 | 0 | m_byte_buffer = TRY(HTML::deserialize_bytes(vm, record, position)); |
124 | | |
125 | | // 3. Initialize the value of value’s name attribute to serialized.[[Name]]. |
126 | 0 | m_name = TRY(HTML::deserialize_string(vm, record, position)); |
127 | | |
128 | | // 4. Initialize the value of value’s lastModified attribute to serialized.[[LastModified]]. |
129 | 0 | m_last_modified = HTML::deserialize_primitive_type<i64>(record, position); |
130 | |
|
131 | 0 | return {}; |
132 | 0 | } |
133 | | |
134 | | } |