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};
17
18use crate::dom::bindings::buffer_source::create_buffer_source;
19use crate::dom::bindings::codegen::Bindings::CompressionStreamBinding::CompressionFormat;
20use crate::dom::bindings::codegen::Bindings::DecompressionStreamBinding::DecompressionStreamMethods;
21use crate::dom::bindings::conversions::SafeToJSValConvertible;
22use crate::dom::bindings::error::{Error, Fallible};
23use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
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};
30use crate::script_runtime::CanGc;
31
32#[dom_struct]
34pub(crate) struct DecompressionStream {
35 reflector_: Reflector,
36
37 transform: Dom<TransformStream>,
39
40 format: CompressionFormat,
42
43 #[no_trace]
45 context: RefCell<DecompressionContext>,
46}
47
48impl DecompressionStream {
49 fn new_inherited(
50 transform: &TransformStream,
51 format: CompressionFormat,
52 ) -> DecompressionStream {
53 DecompressionStream {
54 reflector_: Reflector::new(),
55 transform: Dom::from_ref(transform),
56 format,
57 context: RefCell::new(DecompressionContext::new(format)),
58 }
59 }
60
61 fn new_with_proto(
62 cx: &mut js::context::JSContext,
63 global: &GlobalScope,
64 proto: Option<SafeHandleObject>,
65 transform: &TransformStream,
66 format: CompressionFormat,
67 ) -> DomRoot<DecompressionStream> {
68 reflect_dom_object_with_proto_and_cx(
69 Box::new(DecompressionStream::new_inherited(transform, format)),
70 global,
71 proto,
72 cx,
73 )
74 }
75}
76
77impl DecompressionStreamMethods<crate::DomTypeHolder> for DecompressionStream {
78 fn Constructor(
80 cx: &mut js::context::JSContext,
81 global: &GlobalScope,
82 proto: Option<SafeHandleObject>,
83 format: CompressionFormat,
84 ) -> Fallible<DomRoot<DecompressionStream>> {
85 let transform = TransformStream::new_with_proto(global, None, CanGc::from_cx(cx));
91 let decompression_stream =
92 DecompressionStream::new_with_proto(cx, global, proto, &transform, format);
93
94 let transformer_type = TransformerType::Decompressor(decompression_stream.clone());
99
100 transform.set_up(cx, global, transformer_type)?;
103
104 Ok(decompression_stream)
105 }
106
107 fn Readable(&self) -> DomRoot<ReadableStream> {
109 self.transform.get_readable()
111 }
112
113 fn Writable(&self) -> DomRoot<WritableStream> {
115 self.transform.get_writable()
117 }
118}
119
120pub(crate) fn decompress_and_enqueue_a_chunk(
122 cx: &mut js::context::JSContext,
123 global: &GlobalScope,
124 ds: &DecompressionStream,
125 chunk: SafeHandleValue,
126 controller: &TransformStreamDefaultController,
127) -> Fallible<()> {
128 let chunk = convert_chunk_to_vec(cx.into(), chunk, CanGc::from_cx(cx))?;
130
131 let mut decompression_context = ds.context.borrow_mut();
135 let buffer = decompression_context
136 .decompress(&chunk)
137 .map_err(|_| Error::Type(c"Failed to decompress a chunk of compressed input".into()))?;
138
139 if buffer.is_empty() {
141 return Ok(());
142 }
143
144 rooted!(&in(cx) let mut js_object = ptr::null_mut::<JSObject>());
149 let array = create_buffer_source::<Uint8>(
150 cx.into(),
151 &buffer,
152 js_object.handle_mut(),
153 CanGc::from_cx(cx),
154 )
155 .map_err(|_| Error::Type(c"Cannot convert byte sequence to Uint8Array".to_owned()))?;
156 rooted!(&in(cx) let mut rval = UndefinedValue());
157 array.safe_to_jsval(cx.into(), rval.handle_mut(), CanGc::from_cx(cx));
158 controller.enqueue(cx, global, rval.handle())?;
159
160 if decompression_context.is_ended {
163 return Err(Error::Type(
164 c"The end of the compressed input has been reached".to_owned(),
165 ));
166 }
167
168 Ok(())
169}
170
171pub(crate) fn decompress_flush_and_enqueue(
173 cx: &mut js::context::JSContext,
174 global: &GlobalScope,
175 ds: &DecompressionStream,
176 controller: &TransformStreamDefaultController,
177) -> Fallible<()> {
178 let mut decompression_context = ds.context.borrow_mut();
182 let buffer = decompression_context
183 .finalize()
184 .map_err(|_| Error::Type(c"Failed to finalize the decompression stream".into()))?;
185
186 if !buffer.is_empty() {
188 rooted!(&in(cx) let mut js_object = ptr::null_mut::<JSObject>());
193 let array = create_buffer_source::<Uint8>(
194 cx.into(),
195 &buffer,
196 js_object.handle_mut(),
197 CanGc::from_cx(cx),
198 )
199 .map_err(|_| Error::Type(c"Cannot convert byte sequence to Uint8Array".to_owned()))?;
200 rooted!(&in(cx) let mut rval = UndefinedValue());
201 array.safe_to_jsval(cx.into(), rval.handle_mut(), CanGc::from_cx(cx));
202 controller.enqueue(cx, global, rval.handle())?;
203 }
204
205 if !decompression_context.is_ended {
216 return Err(Error::Type(
217 c"The end of the compressed input has not been reached".to_owned(),
218 ));
219 }
220
221 Ok(())
222}
223
224enum Decoder {
226 Brotli(Box<BrotliDecoder<Vec<u8>>>),
227 Deflate(ZlibDecoder<Vec<u8>>),
228 DeflateRaw(DeflateDecoder<Vec<u8>>),
229 Gzip(GzDecoder<Vec<u8>>),
230}
231
232impl MallocSizeOf for Decoder {
233 #[expect(unsafe_code)]
234 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
235 match self {
236 Decoder::Brotli(decoder) => unsafe { ops.malloc_size_of(&**decoder) },
237 Decoder::Deflate(decoder) => decoder.size_of(ops),
238 Decoder::DeflateRaw(decoder) => decoder.size_of(ops),
239 Decoder::Gzip(decoder) => decoder.size_of(ops),
240 }
241 }
242}
243
244#[derive(MallocSizeOf)]
247struct DecompressionContext {
248 decoder: Decoder,
249 is_ended: bool,
250}
251
252impl DecompressionContext {
253 fn new(format: CompressionFormat) -> DecompressionContext {
254 let decoder = match format {
255 CompressionFormat::Brotli => {
256 Decoder::Brotli(Box::new(BrotliDecoder::new(Vec::new(), BROTLI_BUFFER_SIZE)))
257 },
258 CompressionFormat::Deflate => Decoder::Deflate(ZlibDecoder::new(Vec::new())),
259 CompressionFormat::Deflate_raw => Decoder::DeflateRaw(DeflateDecoder::new(Vec::new())),
260 CompressionFormat::Gzip => Decoder::Gzip(GzDecoder::new(Vec::new())),
261 };
262 DecompressionContext {
263 decoder,
264 is_ended: false,
265 }
266 }
267
268 fn decompress(&mut self, mut chunk: &[u8]) -> Result<Vec<u8>, io::Error> {
269 let mut result = Vec::new();
270
271 match &mut self.decoder {
272 Decoder::Brotli(decoder) => {
273 while !chunk.is_empty() {
274 let written = decoder.write(chunk)?;
275 if written == 0 {
276 self.is_ended = true;
277 break;
278 }
279 chunk = &chunk[written..];
280 }
281 decoder.flush()?;
282 result.append(decoder.get_mut());
283 },
284 Decoder::Deflate(decoder) => {
285 while !chunk.is_empty() {
286 let written = decoder.write(chunk)?;
287 if written == 0 {
288 self.is_ended = true;
289 break;
290 }
291 chunk = &chunk[written..];
292 }
293 decoder.flush()?;
294 result.append(decoder.get_mut());
295 },
296 Decoder::DeflateRaw(decoder) => {
297 while !chunk.is_empty() {
298 let written = decoder.write(chunk)?;
299 if written == 0 {
300 self.is_ended = true;
301 break;
302 }
303 chunk = &chunk[written..];
304 }
305 decoder.flush()?;
306 result.append(decoder.get_mut());
307 },
308 Decoder::Gzip(decoder) => {
309 while !chunk.is_empty() {
310 let written = decoder.write(chunk)?;
311 if written == 0 {
312 self.is_ended = true;
313 break;
314 }
315 chunk = &chunk[written..];
316 }
317 decoder.flush()?;
318 result.append(decoder.get_mut());
319 },
320 }
321
322 Ok(result)
323 }
324
325 fn finalize(&mut self) -> Result<Vec<u8>, io::Error> {
326 let mut result = Vec::new();
327
328 match &mut self.decoder {
329 Decoder::Brotli(decoder) => {
330 if decoder.close().is_ok() {
331 self.is_ended = true;
332 };
333 result.append(decoder.get_mut());
334 },
335 Decoder::Deflate(decoder) => {
336 decoder.flush()?;
347 result.append(decoder.get_mut());
348 if decoder.write(&[0])? == 0 {
349 self.is_ended = true;
350 }
351 decoder.try_finish()?;
352 },
353 Decoder::DeflateRaw(decoder) => {
354 if decoder.try_finish().is_ok() {
355 self.is_ended = true;
356 };
357 result.append(decoder.get_mut());
358 },
359 Decoder::Gzip(decoder) => {
360 if decoder.try_finish().is_ok() {
361 self.is_ended = true;
362 };
363 result.append(decoder.get_mut());
364 },
365 }
366
367 Ok(result)
368 }
369}