1use std::cell::RefCell;
6use std::io::{self, Write};
7use std::ptr;
8
9use brotli::DecompressorWriter as BrotliDecoder;
10use dom_struct::dom_struct;
11use flate2::write::{DeflateDecoder, GzDecoder, ZlibDecoder};
12use js::jsapi::JSObject;
13use js::jsval::UndefinedValue;
14use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
15use js::typedarray::Uint8;
16use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
17use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
18
19use crate::dom::bindings::buffer_source::create_buffer_source;
20use crate::dom::bindings::codegen::Bindings::CompressionStreamBinding::CompressionFormat;
21use crate::dom::bindings::codegen::Bindings::DecompressionStreamBinding::DecompressionStreamMethods;
22use crate::dom::bindings::conversions::SafeToJSValConvertible;
23use crate::dom::bindings::error::{Error, Fallible};
24use crate::dom::bindings::root::{Dom, DomRoot};
25use crate::dom::stream::compressionstream::{BROTLI_BUFFER_SIZE, convert_chunk_to_vec};
26use crate::dom::stream::transformstreamdefaultcontroller::TransformerType;
27use crate::dom::types::{
28 GlobalScope, ReadableStream, TransformStream, TransformStreamDefaultController, WritableStream,
29};
30
31#[dom_struct]
33pub(crate) struct DecompressionStream {
34 reflector_: Reflector,
35
36 transform: Dom<TransformStream>,
38
39 format: CompressionFormat,
41
42 #[no_trace]
44 context: RefCell<DecompressionContext>,
45}
46
47impl DecompressionStream {
48 fn new_inherited(
49 transform: &TransformStream,
50 format: CompressionFormat,
51 ) -> DecompressionStream {
52 DecompressionStream {
53 reflector_: Reflector::new(),
54 transform: Dom::from_ref(transform),
55 format,
56 context: RefCell::new(DecompressionContext::new(format)),
57 }
58 }
59
60 fn new_with_proto(
61 cx: &mut js::context::JSContext,
62 global: &GlobalScope,
63 proto: Option<SafeHandleObject>,
64 transform: &TransformStream,
65 format: CompressionFormat,
66 ) -> DomRoot<DecompressionStream> {
67 reflect_dom_object_with_proto_and_cx(
68 Box::new(DecompressionStream::new_inherited(transform, format)),
69 global,
70 proto,
71 cx,
72 )
73 }
74}
75
76impl DecompressionStreamMethods<crate::DomTypeHolder> for DecompressionStream {
77 fn Constructor(
79 cx: &mut js::context::JSContext,
80 global: &GlobalScope,
81 proto: Option<SafeHandleObject>,
82 format: CompressionFormat,
83 ) -> Fallible<DomRoot<DecompressionStream>> {
84 let transform = TransformStream::new_with_proto(cx, global, None);
90 let decompression_stream =
91 DecompressionStream::new_with_proto(cx, global, proto, &transform, format);
92
93 let transformer_type = TransformerType::Decompressor(decompression_stream.clone());
98
99 transform.set_up(cx, global, transformer_type)?;
102
103 Ok(decompression_stream)
104 }
105
106 fn Readable(&self) -> DomRoot<ReadableStream> {
108 self.transform.get_readable()
110 }
111
112 fn Writable(&self) -> DomRoot<WritableStream> {
114 self.transform.get_writable()
116 }
117}
118
119pub(crate) fn decompress_and_enqueue_a_chunk(
121 cx: &mut js::context::JSContext,
122 global: &GlobalScope,
123 ds: &DecompressionStream,
124 chunk: SafeHandleValue,
125 controller: &TransformStreamDefaultController,
126) -> Fallible<()> {
127 let chunk = convert_chunk_to_vec(cx, chunk)?;
129
130 let mut decompression_context = ds.context.borrow_mut();
134 let buffer = decompression_context
135 .decompress(&chunk)
136 .map_err(|_| Error::Type(c"Failed to decompress a chunk of compressed input".into()))?;
137
138 if buffer.is_empty() {
140 return Ok(());
141 }
142
143 rooted!(&in(cx) let mut js_object = ptr::null_mut::<JSObject>());
148 let array = create_buffer_source::<Uint8>(cx, &buffer, js_object.handle_mut())
149 .map_err(|_| Error::Type(c"Cannot convert byte sequence to Uint8Array".to_owned()))?;
150 rooted!(&in(cx) let mut rval = UndefinedValue());
151 array.safe_to_jsval(cx, rval.handle_mut());
152 controller.enqueue(cx, global, rval.handle())?;
153
154 if decompression_context.is_ended {
157 return Err(Error::Type(
158 c"The end of the compressed input has been reached".to_owned(),
159 ));
160 }
161
162 Ok(())
163}
164
165pub(crate) fn decompress_flush_and_enqueue(
167 cx: &mut js::context::JSContext,
168 global: &GlobalScope,
169 ds: &DecompressionStream,
170 controller: &TransformStreamDefaultController,
171) -> Fallible<()> {
172 let mut decompression_context = ds.context.borrow_mut();
176 let buffer = decompression_context
177 .finalize()
178 .map_err(|_| Error::Type(c"Failed to finalize the decompression stream".into()))?;
179
180 if !buffer.is_empty() {
182 rooted!(&in(cx) let mut js_object = ptr::null_mut::<JSObject>());
187 let array = create_buffer_source::<Uint8>(cx, &buffer, js_object.handle_mut())
188 .map_err(|_| Error::Type(c"Cannot convert byte sequence to Uint8Array".to_owned()))?;
189 rooted!(&in(cx) let mut rval = UndefinedValue());
190 array.safe_to_jsval(cx, rval.handle_mut());
191 controller.enqueue(cx, global, rval.handle())?;
192 }
193
194 if !decompression_context.is_ended {
205 return Err(Error::Type(
206 c"The end of the compressed input has not been reached".to_owned(),
207 ));
208 }
209
210 Ok(())
211}
212
213enum Decoder {
215 Brotli(Box<BrotliDecoder<Vec<u8>>>),
216 Deflate(ZlibDecoder<Vec<u8>>),
217 DeflateRaw(DeflateDecoder<Vec<u8>>),
218 Gzip(GzDecoder<Vec<u8>>),
219}
220
221impl MallocSizeOf for Decoder {
222 #[expect(unsafe_code)]
223 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
224 match self {
225 Decoder::Brotli(decoder) => unsafe { ops.malloc_size_of(&**decoder) },
226 Decoder::Deflate(decoder) => decoder.size_of(ops),
227 Decoder::DeflateRaw(decoder) => decoder.size_of(ops),
228 Decoder::Gzip(decoder) => decoder.size_of(ops),
229 }
230 }
231}
232
233#[derive(MallocSizeOf)]
236struct DecompressionContext {
237 decoder: Decoder,
238 is_ended: bool,
239}
240
241impl DecompressionContext {
242 fn new(format: CompressionFormat) -> DecompressionContext {
243 let decoder = match format {
244 CompressionFormat::Brotli => {
245 Decoder::Brotli(Box::new(BrotliDecoder::new(Vec::new(), BROTLI_BUFFER_SIZE)))
246 },
247 CompressionFormat::Deflate => Decoder::Deflate(ZlibDecoder::new(Vec::new())),
248 CompressionFormat::Deflate_raw => Decoder::DeflateRaw(DeflateDecoder::new(Vec::new())),
249 CompressionFormat::Gzip => Decoder::Gzip(GzDecoder::new(Vec::new())),
250 };
251 DecompressionContext {
252 decoder,
253 is_ended: false,
254 }
255 }
256
257 fn decompress(&mut self, mut chunk: &[u8]) -> Result<Vec<u8>, io::Error> {
258 let mut result = Vec::new();
259
260 match &mut self.decoder {
261 Decoder::Brotli(decoder) => {
262 while !chunk.is_empty() {
263 let written = decoder.write(chunk)?;
264 if written == 0 {
265 self.is_ended = true;
266 break;
267 }
268 chunk = &chunk[written..];
269 }
270 decoder.flush()?;
271 result.append(decoder.get_mut());
272 },
273 Decoder::Deflate(decoder) => {
274 while !chunk.is_empty() {
275 let written = decoder.write(chunk)?;
276 if written == 0 {
277 self.is_ended = true;
278 break;
279 }
280 chunk = &chunk[written..];
281 }
282 decoder.flush()?;
283 result.append(decoder.get_mut());
284 },
285 Decoder::DeflateRaw(decoder) => {
286 while !chunk.is_empty() {
287 let written = decoder.write(chunk)?;
288 if written == 0 {
289 self.is_ended = true;
290 break;
291 }
292 chunk = &chunk[written..];
293 }
294 decoder.flush()?;
295 result.append(decoder.get_mut());
296 },
297 Decoder::Gzip(decoder) => {
298 while !chunk.is_empty() {
299 let written = decoder.write(chunk)?;
300 if written == 0 {
301 self.is_ended = true;
302 break;
303 }
304 chunk = &chunk[written..];
305 }
306 decoder.flush()?;
307 result.append(decoder.get_mut());
308 },
309 }
310
311 Ok(result)
312 }
313
314 fn finalize(&mut self) -> Result<Vec<u8>, io::Error> {
315 let mut result = Vec::new();
316
317 match &mut self.decoder {
318 Decoder::Brotli(decoder) => {
319 if decoder.close().is_ok() {
320 self.is_ended = true;
321 };
322 result.append(decoder.get_mut());
323 },
324 Decoder::Deflate(decoder) => {
325 decoder.flush()?;
336 result.append(decoder.get_mut());
337 if decoder.write(&[0])? == 0 {
338 self.is_ended = true;
339 }
340 decoder.try_finish()?;
341 },
342 Decoder::DeflateRaw(decoder) => {
343 if decoder.try_finish().is_ok() {
344 self.is_ended = true;
345 };
346 result.append(decoder.get_mut());
347 },
348 Decoder::Gzip(decoder) => {
349 if decoder.try_finish().is_ok() {
350 self.is_ended = true;
351 };
352 result.append(decoder.get_mut());
353 },
354 }
355
356 Ok(result)
357 }
358}