1use std::rc::Rc;
6use std::time::SystemTime;
7
8use dom_struct::dom_struct;
9use embedder_traits::SelectedFile;
10use js::context::{JSContext, NoGC};
11use js::rust::HandleObject;
12use script_bindings::reflector::reflect_weak_referenceable_dom_object_with_proto;
13use servo_base::id::{FileId, FileIndex};
14use servo_constellation_traits::{BlobImpl, SerializableFile};
15use time::{Duration, OffsetDateTime};
16
17use crate::dom::bindings::codegen::Bindings::FileBinding;
18use crate::dom::bindings::codegen::Bindings::FileBinding::FileMethods;
19use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
20use crate::dom::bindings::error::{Error, Fallible};
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::bindings::serializable::Serializable;
24use crate::dom::bindings::str::{DOMString, USVString};
25use crate::dom::bindings::structuredclone::StructuredData;
26use crate::dom::blob::{Blob, normalize_type_string, process_blob_parts};
27use crate::dom::globalscope::GlobalScope;
28use crate::dom::window::Window;
29
30#[dom_struct]
31pub(crate) struct File {
32 blob: Blob,
33 name: DOMString,
34 modified: SystemTime,
35 webkit_relative_path: USVString,
39}
40
41impl File {
42 fn new_inherited(
43 blob_impl: &BlobImpl,
44 name: DOMString,
45 modified: Option<SystemTime>,
46 webkit_relative_path: USVString,
47 ) -> File {
48 File {
49 blob: Blob::new_inherited(blob_impl),
50 name,
51 modified: modified.unwrap_or_else(SystemTime::now),
53 webkit_relative_path,
54 }
55 }
56
57 pub(crate) fn new(
58 cx: &mut JSContext,
59 global: &GlobalScope,
60 blob_impl: BlobImpl,
61 name: DOMString,
62 modified: Option<SystemTime>,
63 ) -> DomRoot<File> {
64 Self::new_with_proto(
65 cx,
66 global,
67 None,
68 blob_impl,
69 name,
70 modified,
71 USVString::default(),
72 )
73 }
74
75 fn new_with_proto(
76 cx: &mut JSContext,
77 global: &GlobalScope,
78 proto: Option<HandleObject>,
79 blob_impl: BlobImpl,
80 name: DOMString,
81 modified: Option<SystemTime>,
82 webkit_relative_path: USVString,
83 ) -> DomRoot<File> {
84 let file = reflect_weak_referenceable_dom_object_with_proto(
85 cx,
86 Rc::new(File::new_inherited(
87 &blob_impl,
88 name,
89 modified,
90 webkit_relative_path,
91 )),
92 global,
93 proto,
94 );
95 global.track_file(&file, blob_impl);
96 file
97 }
98
99 pub(crate) fn new_from_selected(
101 cx: &mut JSContext,
102 window: &Window,
103 selected: SelectedFile,
104 ) -> DomRoot<File> {
105 let name = DOMString::from(
106 selected
107 .filename
108 .to_str()
109 .expect("File name encoding error"),
110 );
111
112 File::new(
113 cx,
114 window.upcast(),
115 BlobImpl::new_from_file(
116 selected.id,
117 selected.filename,
118 selected.size,
119 normalize_type_string(&selected.type_string.to_string()),
120 ),
121 name,
122 Some(selected.modified),
123 )
124 }
125
126 pub(crate) fn file_bytes(&self) -> Result<Vec<u8>, ()> {
127 self.blob.get_bytes()
128 }
129
130 pub(crate) fn name(&self) -> &DOMString {
131 &self.name
132 }
133
134 pub(crate) fn file_type(&self) -> String {
135 self.blob.type_string()
136 }
137
138 pub(crate) fn get_modified(&self) -> SystemTime {
139 self.modified
140 }
141
142 pub(crate) fn serialized_data(&self, no_gc: &NoGC) -> Result<SerializableFile, ()> {
143 let (_, blob_impl) = self.upcast::<Blob>().serialize(no_gc)?;
144 Ok(SerializableFile {
145 blob_impl,
146 name: self.name.to_string(),
147 modified: self.LastModified(),
148 webkit_relative_path: self.webkit_relative_path.to_string(),
149 })
150 }
151}
152
153impl Serializable for File {
154 type Index = FileIndex;
155 type Data = SerializableFile;
156
157 fn serialize(&self, no_gc: &NoGC) -> Result<(FileId, SerializableFile), ()> {
159 Ok((FileId::new(), self.serialized_data(no_gc)?))
160 }
161
162 fn deserialize(
164 cx: &mut JSContext,
165 owner: &GlobalScope,
166 serialized: SerializableFile,
167 ) -> Result<DomRoot<Self>, ()> {
168 let modified = OffsetDateTime::UNIX_EPOCH + Duration::milliseconds(serialized.modified);
169 Ok(File::new_with_proto(
170 cx,
171 owner,
172 None,
173 serialized.blob_impl,
174 serialized.name.into(),
175 Some(modified.into()),
176 USVString::from(serialized.webkit_relative_path),
177 ))
178 }
179
180 fn serialized_storage<'a>(
181 reader: StructuredData<'a, '_>,
182 ) -> &'a mut Option<rustc_hash::FxHashMap<FileId, Self::Data>> {
183 match reader {
184 StructuredData::Reader(r) => &mut r.files,
185 StructuredData::Writer(w) => &mut w.files,
186 }
187 }
188}
189
190impl FileMethods<crate::DomTypeHolder> for File {
191 #[expect(non_snake_case)]
193 fn Constructor(
194 cx: &mut JSContext,
195 global: &GlobalScope,
196 proto: Option<HandleObject>,
197 fileBits: Vec<ArrayBufferOrArrayBufferViewOrBlobOrString>,
198 filename: DOMString,
199 filePropertyBag: &FileBinding::FilePropertyBag,
200 ) -> Fallible<DomRoot<File>> {
201 let bytes: Vec<u8> =
202 match process_blob_parts(cx.no_gc(), fileBits, filePropertyBag.parent.endings) {
203 Ok(bytes) => bytes,
204 Err(_) => return Err(Error::InvalidCharacter(None)),
205 };
206
207 let blobPropertyBag = &filePropertyBag.parent;
208 let modified = filePropertyBag
209 .lastModified
210 .map(|modified| OffsetDateTime::UNIX_EPOCH + Duration::milliseconds(modified))
211 .map(Into::into);
212
213 let type_string = normalize_type_string(&blobPropertyBag.type_.str());
214 Ok(File::new_with_proto(
215 cx,
216 global,
217 proto,
218 BlobImpl::new_from_bytes(bytes, type_string),
219 filename,
220 modified,
221 USVString::default(),
222 ))
223 }
224
225 fn Name(&self) -> DOMString {
227 self.name.clone()
228 }
229
230 fn WebkitRelativePath(&self) -> USVString {
232 self.webkit_relative_path.clone()
233 }
234
235 fn LastModified(&self) -> i64 {
237 (OffsetDateTime::from(self.modified) - OffsetDateTime::UNIX_EPOCH).whole_milliseconds()
240 as i64
241 }
242}