1use std::cell::RefCell;
6use std::io::{self, Write};
7use std::ptr;
8
9use dom_struct::dom_struct;
10use flate2::write::{DeflateDecoder, GzDecoder, ZlibDecoder};
11use js::jsapi::JSObject;
12use js::jsval::UndefinedValue;
13use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
14use js::typedarray::Uint8Array;
15use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
16
17use crate::dom::bindings::buffer_source::create_buffer_source;
18use crate::dom::bindings::codegen::Bindings::CompressionStreamBinding::CompressionFormat;
19use crate::dom::bindings::codegen::Bindings::DecompressionStreamBinding::DecompressionStreamMethods;
20use crate::dom::bindings::conversions::SafeToJSValConvertible;
21use crate::dom::bindings::error::{Error, Fallible};
22use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::compressionstream::convert_chunk_to_vec;
25use crate::dom::transformstreamdefaultcontroller::TransformerType;
26use crate::dom::types::{
27 GlobalScope, ReadableStream, TransformStream, TransformStreamDefaultController, WritableStream,
28};
29use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
30
31enum Decompressor {
34 Deflate(ZlibDecoder<Vec<u8>>),
35 DeflateRaw(DeflateDecoder<Vec<u8>>),
36 Gzip(GzDecoder<Vec<u8>>),
37}
38
39impl Decompressor {
41 fn new(format: CompressionFormat) -> Decompressor {
42 match format {
43 CompressionFormat::Deflate => Decompressor::Deflate(ZlibDecoder::new(Vec::new())),
44 CompressionFormat::Deflate_raw => {
45 Decompressor::DeflateRaw(DeflateDecoder::new(Vec::new()))
46 },
47 CompressionFormat::Gzip => Decompressor::Gzip(GzDecoder::new(Vec::new())),
48 }
49 }
50
51 fn get_ref(&self) -> &Vec<u8> {
52 match self {
53 Decompressor::Deflate(zlib_decoder) => zlib_decoder.get_ref(),
54 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.get_ref(),
55 Decompressor::Gzip(gz_decoder) => gz_decoder.get_ref(),
56 }
57 }
58
59 fn get_mut(&mut self) -> &mut Vec<u8> {
60 match self {
61 Decompressor::Deflate(zlib_decoder) => zlib_decoder.get_mut(),
62 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.get_mut(),
63 Decompressor::Gzip(gz_decoder) => gz_decoder.get_mut(),
64 }
65 }
66
67 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
68 match self {
69 Decompressor::Deflate(zlib_decoder) => zlib_decoder.write(buf),
70 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.write(buf),
71 Decompressor::Gzip(gz_decoder) => gz_decoder.write(buf),
72 }
73 }
74
75 fn flush(&mut self) -> io::Result<()> {
76 match self {
77 Decompressor::Deflate(zlib_decoder) => zlib_decoder.flush(),
78 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.flush(),
79 Decompressor::Gzip(gz_decoder) => gz_decoder.flush(),
80 }
81 }
82
83 fn try_finish(&mut self) -> io::Result<()> {
84 match self {
85 Decompressor::Deflate(zlib_decoder) => zlib_decoder.try_finish(),
86 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.try_finish(),
87 Decompressor::Gzip(gz_decoder) => gz_decoder.try_finish(),
88 }
89 }
90}
91
92impl MallocSizeOf for Decompressor {
93 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
94 match self {
95 Decompressor::Deflate(zlib_decoder) => zlib_decoder.size_of(ops),
96 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.size_of(ops),
97 Decompressor::Gzip(gz_decoder) => gz_decoder.size_of(ops),
98 }
99 }
100}
101
102#[dom_struct]
104pub(crate) struct DecompressionStream {
105 reflector_: Reflector,
106
107 transform: Dom<TransformStream>,
109
110 format: CompressionFormat,
112
113 #[no_trace]
115 context: RefCell<Decompressor>,
116}
117
118impl DecompressionStream {
119 fn new_inherited(
120 transform: &TransformStream,
121 format: CompressionFormat,
122 ) -> DecompressionStream {
123 DecompressionStream {
124 reflector_: Reflector::new(),
125 transform: Dom::from_ref(transform),
126 format,
127 context: RefCell::new(Decompressor::new(format)),
128 }
129 }
130
131 fn new_with_proto(
132 global: &GlobalScope,
133 proto: Option<SafeHandleObject>,
134 transform: &TransformStream,
135 format: CompressionFormat,
136 can_gc: CanGc,
137 ) -> DomRoot<DecompressionStream> {
138 reflect_dom_object_with_proto(
139 Box::new(DecompressionStream::new_inherited(transform, format)),
140 global,
141 proto,
142 can_gc,
143 )
144 }
145}
146
147impl DecompressionStreamMethods<crate::DomTypeHolder> for DecompressionStream {
148 fn Constructor(
150 global: &GlobalScope,
151 proto: Option<SafeHandleObject>,
152 can_gc: CanGc,
153 format: CompressionFormat,
154 ) -> Fallible<DomRoot<DecompressionStream>> {
155 let transform = TransformStream::new_with_proto(global, None, can_gc);
161 let decompression_stream =
162 DecompressionStream::new_with_proto(global, proto, &transform, format, can_gc);
163
164 let transformer_type = TransformerType::Decompressor(decompression_stream.clone());
169
170 let cx = GlobalScope::get_cx();
173 transform.set_up(cx, global, transformer_type, can_gc)?;
174
175 Ok(decompression_stream)
176 }
177
178 fn Readable(&self) -> DomRoot<ReadableStream> {
180 self.transform.get_readable()
182 }
183
184 fn Writable(&self) -> DomRoot<WritableStream> {
186 self.transform.get_writable()
188 }
189}
190
191pub(crate) fn decompress_and_enqueue_a_chunk(
193 cx: SafeJSContext,
194 global: &GlobalScope,
195 ds: &DecompressionStream,
196 chunk: SafeHandleValue,
197 controller: &TransformStreamDefaultController,
198 can_gc: CanGc,
199) -> Fallible<()> {
200 let chunk = convert_chunk_to_vec(cx, chunk, can_gc)?;
202
203 let mut decompressor = ds.context.borrow_mut();
207 let mut offset = 0;
208 let mut written = 1;
209 while offset < chunk.len() && written > 0 {
210 written = decompressor
211 .write(&chunk[offset..])
212 .map_err(|_| Error::Type("DecompressionStream: write() failed".to_string()))?;
213 offset += written;
214 }
215 decompressor
216 .flush()
217 .map_err(|_| Error::Type("DecompressionStream: flush() failed".to_string()))?;
218 let buffer = decompressor.get_ref();
219
220 if buffer.is_empty() {
222 return Ok(());
223 }
224
225 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
230 let array: Uint8Array = create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
231 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
232 rooted!(in(*cx) let mut rval = UndefinedValue());
233 array.safe_to_jsval(cx, rval.handle_mut(), can_gc);
234 controller.enqueue(cx, global, rval.handle(), can_gc)?;
235
236 decompressor.get_mut().clear();
239
240 if offset < chunk.len() {
243 return Err(Error::Type(
244 "The end of the compressed input has been reached".to_string(),
245 ));
246 }
247
248 Ok(())
249}
250
251pub(crate) fn decompress_flush_and_enqueue(
253 cx: SafeJSContext,
254 global: &GlobalScope,
255 ds: &DecompressionStream,
256 controller: &TransformStreamDefaultController,
257 can_gc: CanGc,
258) -> Fallible<()> {
259 let mut decompressor = ds.context.borrow_mut();
263 let offset = decompressor.get_ref().len();
264 let is_ended = decompressor
265 .write(&[0])
266 .map_err(|_| Error::Type("DecompressionStream: write() failed".to_string()))? ==
267 0;
268 decompressor
269 .try_finish()
270 .map_err(|_| Error::Type("DecompressionStream: try_finish() failed".to_string()))?;
271 let buffer = &decompressor.get_ref()[offset..];
272
273 if !buffer.is_empty() {
275 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
280 let array: Uint8Array = create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
281 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
282 rooted!(in(*cx) let mut rval = UndefinedValue());
283 array.safe_to_jsval(cx, rval.handle_mut(), can_gc);
284 controller.enqueue(cx, global, rval.handle(), can_gc)?;
285 }
286
287 decompressor.get_mut().clear();
290
291 if !is_ended {
302 return Err(Error::Type(
303 "The end of the compressed input has not been reached".to_string(),
304 ));
305 }
306
307 Ok(())
308}