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};
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(
68 cx,
69 Box::new(DecompressionStream::new_inherited(transform, format)),
70 global,
71 proto,
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 buffer = {
134 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 buffer
144 };
145 rooted!(&in(cx) let mut js_object = ptr::null_mut::<JSObject>());
150 let array = create_buffer_source::<Uint8>(cx, &buffer, js_object.handle_mut())
151 .map_err(|_| Error::Type(c"Cannot convert byte sequence to Uint8Array".to_owned()))?;
152 rooted!(&in(cx) let mut rval = UndefinedValue());
153 array.safe_to_jsval(cx, rval.handle_mut());
154 controller.enqueue(cx, global, rval.handle())?;
155
156 if ds.context.borrow().is_ended {
159 return Err(Error::Type(
160 c"The end of the compressed input has been reached".to_owned(),
161 ));
162 }
163
164 Ok(())
165}
166
167pub(crate) fn decompress_flush_and_enqueue(
169 cx: &mut js::context::JSContext,
170 global: &GlobalScope,
171 ds: &DecompressionStream,
172 controller: &TransformStreamDefaultController,
173) -> Fallible<()> {
174 let buffer = {
178 let mut decompression_context = ds.context.borrow_mut();
179 decompression_context
180 .finalize()
181 .map_err(|_| Error::Type(c"Failed to finalize the decompression stream".into()))?
182 };
183 if !buffer.is_empty() {
185 rooted!(&in(cx) let mut js_object = ptr::null_mut::<JSObject>());
190 let array = create_buffer_source::<Uint8>(cx, &buffer, js_object.handle_mut())
191 .map_err(|_| Error::Type(c"Cannot convert byte sequence to Uint8Array".to_owned()))?;
192 rooted!(&in(cx) let mut rval = UndefinedValue());
193 array.safe_to_jsval(cx, rval.handle_mut());
194 controller.enqueue(cx, global, rval.handle())?;
195 }
196
197 if !ds.context.borrow().is_ended {
208 return Err(Error::Type(
209 c"The end of the compressed input has not been reached".to_owned(),
210 ));
211 }
212
213 Ok(())
214}
215
216enum Decoder {
218 Brotli(Box<BrotliDecoder<Vec<u8>>>),
219 Deflate(ZlibDecoder<Vec<u8>>),
220 DeflateRaw(DeflateDecoder<Vec<u8>>),
221 Gzip(GzDecoder<Vec<u8>>),
222}
223
224impl MallocSizeOf for Decoder {
225 #[expect(unsafe_code)]
226 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
227 match self {
228 Decoder::Brotli(decoder) => unsafe { ops.malloc_size_of(&**decoder) },
229 Decoder::Deflate(decoder) => decoder.size_of(ops),
230 Decoder::DeflateRaw(decoder) => decoder.size_of(ops),
231 Decoder::Gzip(decoder) => decoder.size_of(ops),
232 }
233 }
234}
235
236#[derive(MallocSizeOf)]
239struct DecompressionContext {
240 decoder: Decoder,
241 is_ended: bool,
242}
243
244impl DecompressionContext {
245 fn new(format: CompressionFormat) -> DecompressionContext {
246 let decoder = match format {
247 CompressionFormat::Brotli => {
248 Decoder::Brotli(Box::new(BrotliDecoder::new(Vec::new(), BROTLI_BUFFER_SIZE)))
249 },
250 CompressionFormat::Deflate => Decoder::Deflate(ZlibDecoder::new(Vec::new())),
251 CompressionFormat::Deflate_raw => Decoder::DeflateRaw(DeflateDecoder::new(Vec::new())),
252 CompressionFormat::Gzip => Decoder::Gzip(GzDecoder::new(Vec::new())),
253 };
254 DecompressionContext {
255 decoder,
256 is_ended: false,
257 }
258 }
259
260 fn decompress(&mut self, mut chunk: &[u8]) -> Result<Vec<u8>, io::Error> {
261 let mut result = Vec::new();
262
263 match &mut self.decoder {
264 Decoder::Brotli(decoder) => {
265 while !chunk.is_empty() {
266 let written = decoder.write(chunk)?;
267 if written == 0 {
268 self.is_ended = true;
269 break;
270 }
271 chunk = &chunk[written..];
272 }
273 decoder.flush()?;
274 result.append(decoder.get_mut());
275 },
276 Decoder::Deflate(decoder) => {
277 while !chunk.is_empty() {
278 let written = decoder.write(chunk)?;
279 if written == 0 {
280 self.is_ended = true;
281 break;
282 }
283 chunk = &chunk[written..];
284 }
285 decoder.flush()?;
286 result.append(decoder.get_mut());
287 },
288 Decoder::DeflateRaw(decoder) => {
289 while !chunk.is_empty() {
290 let written = decoder.write(chunk)?;
291 if written == 0 {
292 self.is_ended = true;
293 break;
294 }
295 chunk = &chunk[written..];
296 }
297 decoder.flush()?;
298 result.append(decoder.get_mut());
299 },
300 Decoder::Gzip(decoder) => {
301 while !chunk.is_empty() {
302 let written = decoder.write(chunk)?;
303 if written == 0 {
304 self.is_ended = true;
305 break;
306 }
307 chunk = &chunk[written..];
308 }
309 decoder.flush()?;
310 result.append(decoder.get_mut());
311 },
312 }
313
314 Ok(result)
315 }
316
317 fn finalize(&mut self) -> Result<Vec<u8>, io::Error> {
318 let mut result = Vec::new();
319
320 match &mut self.decoder {
321 Decoder::Brotli(decoder) => {
322 if decoder.close().is_ok() {
323 self.is_ended = true;
324 };
325 result.append(decoder.get_mut());
326 },
327 Decoder::Deflate(decoder) => {
328 decoder.flush()?;
339 result.append(decoder.get_mut());
340 if decoder.write(&[0])? == 0 {
341 self.is_ended = true;
342 }
343 decoder.try_finish()?;
344 },
345 Decoder::DeflateRaw(decoder) => {
346 if decoder.try_finish().is_ok() {
347 self.is_ended = true;
348 };
349 result.append(decoder.get_mut());
350 },
351 Decoder::Gzip(decoder) => {
352 if decoder.try_finish().is_ok() {
353 self.is_ended = true;
354 };
355 result.append(decoder.get_mut());
356 },
357 }
358
359 Ok(result)
360 }
361}