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;
15
16use crate::dom::bindings::buffer_source::create_buffer_source;
17use crate::dom::bindings::codegen::Bindings::CompressionStreamBinding::CompressionFormat;
18use crate::dom::bindings::codegen::Bindings::DecompressionStreamBinding::DecompressionStreamMethods;
19use crate::dom::bindings::conversions::SafeToJSValConvertible;
20use crate::dom::bindings::error::{Error, Fallible};
21use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::compressionstream::convert_chunk_to_vec;
24use crate::dom::transformstreamdefaultcontroller::TransformerType;
25use crate::dom::types::{
26 GlobalScope, ReadableStream, TransformStream, TransformStreamDefaultController, WritableStream,
27};
28use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
29
30enum Decompressor {
33 Deflate(ZlibDecoder<Vec<u8>>),
34 DeflateRaw(DeflateDecoder<Vec<u8>>),
35 Gzip(GzDecoder<Vec<u8>>),
36}
37
38impl Decompressor {
40 fn new(format: CompressionFormat) -> Decompressor {
41 match format {
42 CompressionFormat::Deflate => Decompressor::Deflate(ZlibDecoder::new(Vec::new())),
43 CompressionFormat::Deflate_raw => {
44 Decompressor::DeflateRaw(DeflateDecoder::new(Vec::new()))
45 },
46 CompressionFormat::Gzip => Decompressor::Gzip(GzDecoder::new(Vec::new())),
47 }
48 }
49
50 fn get_ref(&self) -> &Vec<u8> {
51 match self {
52 Decompressor::Deflate(zlib_decoder) => zlib_decoder.get_ref(),
53 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.get_ref(),
54 Decompressor::Gzip(gz_decoder) => gz_decoder.get_ref(),
55 }
56 }
57
58 fn get_mut(&mut self) -> &mut Vec<u8> {
59 match self {
60 Decompressor::Deflate(zlib_decoder) => zlib_decoder.get_mut(),
61 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.get_mut(),
62 Decompressor::Gzip(gz_decoder) => gz_decoder.get_mut(),
63 }
64 }
65
66 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
67 match self {
68 Decompressor::Deflate(zlib_decoder) => zlib_decoder.write(buf),
69 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.write(buf),
70 Decompressor::Gzip(gz_decoder) => gz_decoder.write(buf),
71 }
72 }
73
74 fn flush(&mut self) -> io::Result<()> {
75 match self {
76 Decompressor::Deflate(zlib_decoder) => zlib_decoder.flush(),
77 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.flush(),
78 Decompressor::Gzip(gz_decoder) => gz_decoder.flush(),
79 }
80 }
81
82 fn try_finish(&mut self) -> io::Result<()> {
83 match self {
84 Decompressor::Deflate(zlib_decoder) => zlib_decoder.try_finish(),
85 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.try_finish(),
86 Decompressor::Gzip(gz_decoder) => gz_decoder.try_finish(),
87 }
88 }
89}
90
91#[dom_struct]
93pub(crate) struct DecompressionStream {
94 reflector_: Reflector,
95
96 transform: Dom<TransformStream>,
98
99 format: CompressionFormat,
101
102 #[ignore_malloc_size_of = "defined in flate2"]
104 #[no_trace]
105 context: RefCell<Decompressor>,
106}
107
108impl DecompressionStream {
109 fn new_inherited(
110 transform: &TransformStream,
111 format: CompressionFormat,
112 ) -> DecompressionStream {
113 DecompressionStream {
114 reflector_: Reflector::new(),
115 transform: Dom::from_ref(transform),
116 format,
117 context: RefCell::new(Decompressor::new(format)),
118 }
119 }
120
121 fn new_with_proto(
122 global: &GlobalScope,
123 proto: Option<SafeHandleObject>,
124 transform: &TransformStream,
125 format: CompressionFormat,
126 can_gc: CanGc,
127 ) -> DomRoot<DecompressionStream> {
128 reflect_dom_object_with_proto(
129 Box::new(DecompressionStream::new_inherited(transform, format)),
130 global,
131 proto,
132 can_gc,
133 )
134 }
135}
136
137impl DecompressionStreamMethods<crate::DomTypeHolder> for DecompressionStream {
138 fn Constructor(
140 global: &GlobalScope,
141 proto: Option<SafeHandleObject>,
142 can_gc: CanGc,
143 format: CompressionFormat,
144 ) -> Fallible<DomRoot<DecompressionStream>> {
145 let transform = TransformStream::new_with_proto(global, None, can_gc);
151 let decompression_stream =
152 DecompressionStream::new_with_proto(global, proto, &transform, format, can_gc);
153
154 let transformer_type = TransformerType::Decompressor(decompression_stream.clone());
159
160 let cx = GlobalScope::get_cx();
163 transform.set_up(cx, global, transformer_type, can_gc)?;
164
165 Ok(decompression_stream)
166 }
167
168 fn Readable(&self) -> DomRoot<ReadableStream> {
170 self.transform.get_readable()
172 }
173
174 fn Writable(&self) -> DomRoot<WritableStream> {
176 self.transform.get_writable()
178 }
179}
180
181pub(crate) fn decompress_and_enqueue_a_chunk(
183 cx: SafeJSContext,
184 global: &GlobalScope,
185 ds: &DecompressionStream,
186 chunk: SafeHandleValue,
187 controller: &TransformStreamDefaultController,
188 can_gc: CanGc,
189) -> Fallible<()> {
190 let chunk = convert_chunk_to_vec(cx, chunk)?;
192
193 let mut decompressor = ds.context.borrow_mut();
197 let mut offset = 0;
198 let mut written = 1;
199 while offset < chunk.len() && written > 0 {
200 written = decompressor
201 .write(&chunk[offset..])
202 .map_err(|_| Error::Type("DecompressionStream: write() failed".to_string()))?;
203 offset += written;
204 }
205 decompressor
206 .flush()
207 .map_err(|_| Error::Type("DecompressionStream: flush() failed".to_string()))?;
208 let buffer = decompressor.get_ref();
209
210 if buffer.is_empty() {
212 return Ok(());
213 }
214
215 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
220 let array: Uint8Array = create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
221 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
222 rooted!(in(*cx) let mut rval = UndefinedValue());
223 array.safe_to_jsval(cx, rval.handle_mut(), can_gc);
224 controller.enqueue(cx, global, rval.handle(), can_gc)?;
225
226 decompressor.get_mut().clear();
229
230 if offset < chunk.len() {
233 return Err(Error::Type(
234 "The end of the compressed input has been reached".to_string(),
235 ));
236 }
237
238 Ok(())
239}
240
241pub(crate) fn decompress_flush_and_enqueue(
243 cx: SafeJSContext,
244 global: &GlobalScope,
245 ds: &DecompressionStream,
246 controller: &TransformStreamDefaultController,
247 can_gc: CanGc,
248) -> Fallible<()> {
249 let mut decompressor = ds.context.borrow_mut();
253 let offset = decompressor.get_ref().len();
254 let is_ended = decompressor
255 .write(&[0])
256 .map_err(|_| Error::Type("DecompressionStream: write() failed".to_string()))? ==
257 0;
258 decompressor
259 .try_finish()
260 .map_err(|_| Error::Type("DecompressionStream: try_finish() failed".to_string()))?;
261 let buffer = &decompressor.get_ref()[offset..];
262
263 if !buffer.is_empty() {
265 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
270 let array: Uint8Array = create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
271 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
272 rooted!(in(*cx) let mut rval = UndefinedValue());
273 array.safe_to_jsval(cx, rval.handle_mut(), can_gc);
274 controller.enqueue(cx, global, rval.handle(), can_gc)?;
275 }
276
277 decompressor.get_mut().clear();
280
281 if !is_ended {
292 return Err(Error::Type(
293 "The end of the compressed input has not been reached".to_string(),
294 ));
295 }
296
297 Ok(())
298}