1use std::collections::HashMap;
6use std::ptr;
7use std::rc::Rc;
8
9use base::id::{BlobId, BlobIndex};
10use constellation_traits::{BlobData, BlobImpl};
11use dom_struct::dom_struct;
12use encoding_rs::UTF_8;
13use js::jsapi::JSObject;
14use js::rust::HandleObject;
15use js::typedarray::{ArrayBufferU8, Uint8};
16use net_traits::filemanager_thread::RelativePos;
17use uuid::Uuid;
18
19use crate::dom::bindings::buffer_source::create_buffer_source;
20use crate::dom::bindings::codegen::Bindings::BlobBinding;
21use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
22use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
23use crate::dom::bindings::error::{Error, Fallible};
24use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
25use crate::dom::bindings::root::DomRoot;
26use crate::dom::bindings::serializable::Serializable;
27use crate::dom::bindings::str::DOMString;
28use crate::dom::bindings::structuredclone::StructuredData;
29use crate::dom::globalscope::GlobalScope;
30use crate::dom::promise::Promise;
31use crate::dom::readablestream::ReadableStream;
32use crate::realms::{AlreadyInRealm, InRealm};
33use crate::script_runtime::CanGc;
34
35#[dom_struct]
37pub(crate) struct Blob {
38 reflector_: Reflector,
39 #[no_trace]
40 blob_id: BlobId,
41}
42
43impl Blob {
44 pub(crate) fn new(global: &GlobalScope, blob_impl: BlobImpl, can_gc: CanGc) -> DomRoot<Blob> {
45 Self::new_with_proto(global, None, blob_impl, can_gc)
46 }
47
48 fn new_with_proto(
49 global: &GlobalScope,
50 proto: Option<HandleObject>,
51 blob_impl: BlobImpl,
52 can_gc: CanGc,
53 ) -> DomRoot<Blob> {
54 let dom_blob = reflect_dom_object_with_proto(
55 Box::new(Blob::new_inherited(&blob_impl)),
56 global,
57 proto,
58 can_gc,
59 );
60 global.track_blob(&dom_blob, blob_impl);
61 dom_blob
62 }
63
64 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
65 pub(crate) fn new_inherited(blob_impl: &BlobImpl) -> Blob {
66 Blob {
67 reflector_: Reflector::new(),
68 blob_id: blob_impl.blob_id(),
69 }
70 }
71
72 pub(crate) fn get_bytes(&self) -> Result<Vec<u8>, ()> {
74 self.global().get_blob_bytes(&self.blob_id)
75 }
76
77 pub(crate) fn type_string(&self) -> String {
79 self.global().get_blob_type_string(&self.blob_id)
80 }
81
82 pub(crate) fn get_blob_url_id(&self) -> Uuid {
85 self.global().get_blob_url_id(&self.blob_id)
86 }
87
88 pub(crate) fn get_stream(&self, can_gc: CanGc) -> Fallible<DomRoot<ReadableStream>> {
90 self.global().get_blob_stream(&self.blob_id, can_gc)
91 }
92}
93
94impl Serializable for Blob {
95 type Index = BlobIndex;
96 type Data = BlobImpl;
97
98 fn serialize(&self) -> Result<(BlobId, BlobImpl), ()> {
100 let blob_id = self.blob_id;
101
102 let blob_impl = self.global().serialize_blob(&blob_id);
104
105 let new_blob_id = blob_impl.blob_id();
107
108 Ok((new_blob_id, blob_impl))
109 }
110
111 fn deserialize(
113 owner: &GlobalScope,
114 serialized: BlobImpl,
115 can_gc: CanGc,
116 ) -> Result<DomRoot<Self>, ()> {
117 let deserialized_blob = Blob::new(owner, serialized, can_gc);
118 Ok(deserialized_blob)
119 }
120
121 fn serialized_storage<'a>(
122 reader: StructuredData<'a, '_>,
123 ) -> &'a mut Option<HashMap<BlobId, Self::Data>> {
124 match reader {
125 StructuredData::Reader(r) => &mut r.blob_impls,
126 StructuredData::Writer(w) => &mut w.blobs,
127 }
128 }
129}
130
131#[allow(unsafe_code)]
134pub(crate) fn blob_parts_to_bytes(
135 mut blobparts: Vec<ArrayBufferOrArrayBufferViewOrBlobOrString>,
136) -> Result<Vec<u8>, ()> {
137 let mut ret = vec![];
138 for blobpart in &mut blobparts {
139 match blobpart {
140 ArrayBufferOrArrayBufferViewOrBlobOrString::String(s) => {
141 ret.extend(s.as_bytes());
142 },
143 ArrayBufferOrArrayBufferViewOrBlobOrString::Blob(b) => {
144 let bytes = b.get_bytes().unwrap_or(vec![]);
145 ret.extend(bytes);
146 },
147 ArrayBufferOrArrayBufferViewOrBlobOrString::ArrayBuffer(a) => unsafe {
148 let bytes = a.as_slice();
149 ret.extend(bytes);
150 },
151 ArrayBufferOrArrayBufferViewOrBlobOrString::ArrayBufferView(a) => unsafe {
152 let bytes = a.as_slice();
153 ret.extend(bytes);
154 },
155 }
156 }
157
158 Ok(ret)
159}
160
161impl BlobMethods<crate::DomTypeHolder> for Blob {
162 #[allow(non_snake_case)]
164 fn Constructor(
165 global: &GlobalScope,
166 proto: Option<HandleObject>,
167 can_gc: CanGc,
168 blobParts: Option<Vec<ArrayBufferOrArrayBufferViewOrBlobOrString>>,
169 blobPropertyBag: &BlobBinding::BlobPropertyBag,
170 ) -> Fallible<DomRoot<Blob>> {
171 let bytes: Vec<u8> = match blobParts {
172 None => Vec::new(),
173 Some(blobparts) => match blob_parts_to_bytes(blobparts) {
174 Ok(bytes) => bytes,
175 Err(_) => return Err(Error::InvalidCharacter),
176 },
177 };
178
179 let type_string = normalize_type_string(blobPropertyBag.type_.as_ref());
180 let blob_impl = BlobImpl::new_from_bytes(bytes, type_string);
181
182 Ok(Blob::new_with_proto(global, proto, blob_impl, can_gc))
183 }
184
185 fn Size(&self) -> u64 {
187 self.global().get_blob_size(&self.blob_id)
188 }
189
190 fn Type(&self) -> DOMString {
192 DOMString::from(self.type_string())
193 }
194
195 fn Stream(&self, can_gc: CanGc) -> Fallible<DomRoot<ReadableStream>> {
197 self.get_stream(can_gc)
198 }
199
200 fn Slice(
202 &self,
203 start: Option<i64>,
204 end: Option<i64>,
205 content_type: Option<DOMString>,
206 can_gc: CanGc,
207 ) -> DomRoot<Blob> {
208 let global = self.global();
209 let type_string = normalize_type_string(&content_type.unwrap_or_default());
210
211 let (parent, range) = match *global.get_blob_data(&self.blob_id) {
214 BlobData::Sliced(grandparent, parent_range) => {
215 let range = RelativePos {
216 start: parent_range.start + start.unwrap_or_default(),
217 end: end.map(|end| end + parent_range.start).or(parent_range.end),
218 };
219 (grandparent, range)
220 },
221 _ => (self.blob_id, RelativePos::from_opts(start, end)),
222 };
223
224 let blob_impl = BlobImpl::new_sliced(range, parent, type_string);
225 Blob::new(&global, blob_impl, can_gc)
226 }
227
228 fn Text(&self, can_gc: CanGc) -> Rc<Promise> {
230 let global = self.global();
231 let in_realm_proof = AlreadyInRealm::assert::<crate::DomTypeHolder>();
232 let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc);
233 let id = self.get_blob_url_id();
234 global.read_file_async(
235 id,
236 p.clone(),
237 Box::new(|promise, bytes| match bytes {
238 Ok(b) => {
239 let (text, _, _) = UTF_8.decode(&b);
240 let text = DOMString::from(text);
241 promise.resolve_native(&text, CanGc::note());
242 },
243 Err(e) => {
244 promise.reject_error(e, CanGc::note());
245 },
246 }),
247 );
248 p
249 }
250
251 fn ArrayBuffer(&self, in_realm: InRealm, can_gc: CanGc) -> Rc<Promise> {
253 let cx = GlobalScope::get_cx();
254 let global = GlobalScope::from_safe_context(cx, in_realm);
255 let promise = Promise::new_in_current_realm(in_realm, can_gc);
256
257 let stream = self.get_stream(can_gc);
259
260 let reader = match stream.and_then(|s| s.acquire_default_reader(can_gc)) {
263 Ok(reader) => reader,
264 Err(error) => {
265 promise.reject_error(error, can_gc);
266 return promise;
267 },
268 };
269
270 let success_promise = promise.clone();
272 let failure_promise = promise.clone();
273 reader.read_all_bytes(
274 cx,
275 &global,
276 Rc::new(move |bytes| {
277 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
278 let array_buffer = create_buffer_source::<ArrayBufferU8>(
281 cx,
282 bytes,
283 js_object.handle_mut(),
284 can_gc,
285 )
286 .expect("Converting input to ArrayBufferU8 should never fail");
287 success_promise.resolve_native(&array_buffer, can_gc);
288 }),
289 Rc::new(move |cx, value| {
290 failure_promise.reject(cx, value, can_gc);
291 }),
292 in_realm,
293 can_gc,
294 );
295
296 promise
297 }
298
299 fn Bytes(&self, in_realm: InRealm, can_gc: CanGc) -> Rc<Promise> {
301 let cx = GlobalScope::get_cx();
302 let global = GlobalScope::from_safe_context(cx, in_realm);
303 let p = Promise::new_in_current_realm(in_realm, can_gc);
304
305 let stream = self.get_stream(can_gc);
307
308 let reader = match stream.and_then(|s| s.acquire_default_reader(can_gc)) {
311 Ok(r) => r,
312 Err(e) => {
313 p.reject_error(e, can_gc);
314 return p;
315 },
316 };
317
318 let p_success = p.clone();
320 let p_failure = p.clone();
321 reader.read_all_bytes(
322 cx,
323 &global,
324 Rc::new(move |bytes| {
325 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
326 let arr = create_buffer_source::<Uint8>(cx, bytes, js_object.handle_mut(), can_gc)
327 .expect("Converting input to uint8 array should never fail");
328 p_success.resolve_native(&arr, can_gc);
329 }),
330 Rc::new(move |cx, v| {
331 p_failure.reject(cx, v, can_gc);
332 }),
333 in_realm,
334 can_gc,
335 );
336 p
337 }
338}
339
340pub(crate) fn normalize_type_string(s: &str) -> String {
346 if is_ascii_printable(s) {
347 s.to_ascii_lowercase()
348 } else {
352 "".to_string()
353 }
354}
355
356fn is_ascii_printable(string: &str) -> bool {
357 string.chars().all(|c| ('\x20'..='\x7E').contains(&c))
360}