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::Uint8Array;
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};
24use crate::dom::bindings::root::{Dom, DomRoot};
25use crate::dom::compressionstream::convert_chunk_to_vec;
26use crate::dom::transformstreamdefaultcontroller::TransformerType;
27use crate::dom::types::{
28 GlobalScope, ReadableStream, TransformStream, TransformStreamDefaultController, WritableStream,
29};
30use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
31
32enum Decompressor {
35 Deflate(ZlibDecoder<Vec<u8>>),
36 DeflateRaw(DeflateDecoder<Vec<u8>>),
37 Gzip(GzDecoder<Vec<u8>>),
38 Brotli(Box<BrotliDecoder<Vec<u8>>>),
39}
40
41impl Decompressor {
43 fn new(format: CompressionFormat) -> Decompressor {
44 match format {
45 CompressionFormat::Deflate => Decompressor::Deflate(ZlibDecoder::new(Vec::new())),
46 CompressionFormat::Deflate_raw => {
47 Decompressor::DeflateRaw(DeflateDecoder::new(Vec::new()))
48 },
49 CompressionFormat::Gzip => Decompressor::Gzip(GzDecoder::new(Vec::new())),
50 CompressionFormat::Brotli => {
51 Decompressor::Brotli(Box::new(BrotliDecoder::new(Vec::new(), 4096)))
52 },
53 }
54 }
55
56 fn get_ref(&self) -> &Vec<u8> {
57 match self {
58 Decompressor::Deflate(zlib_decoder) => zlib_decoder.get_ref(),
59 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.get_ref(),
60 Decompressor::Gzip(gz_decoder) => gz_decoder.get_ref(),
61 Decompressor::Brotli(brotli_decoder) => brotli_decoder.get_ref(),
62 }
63 }
64
65 fn get_mut(&mut self) -> &mut Vec<u8> {
66 match self {
67 Decompressor::Deflate(zlib_decoder) => zlib_decoder.get_mut(),
68 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.get_mut(),
69 Decompressor::Gzip(gz_decoder) => gz_decoder.get_mut(),
70 Decompressor::Brotli(brotli_decoder) => brotli_decoder.get_mut(),
71 }
72 }
73
74 fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
75 match self {
76 Decompressor::Deflate(zlib_decoder) => zlib_decoder.write(buf),
77 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.write(buf),
78 Decompressor::Gzip(gz_decoder) => gz_decoder.write(buf),
79 Decompressor::Brotli(brotli_decoder) => brotli_decoder.write(buf),
80 }
81 }
82
83 fn flush(&mut self) -> io::Result<()> {
84 match self {
85 Decompressor::Deflate(zlib_decoder) => zlib_decoder.flush(),
86 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.flush(),
87 Decompressor::Gzip(gz_decoder) => gz_decoder.flush(),
88 Decompressor::Brotli(brotli_decoder) => brotli_decoder.flush(),
89 }
90 }
91
92 fn try_finish(&mut self) -> io::Result<()> {
93 match self {
94 Decompressor::Deflate(zlib_decoder) => zlib_decoder.try_finish(),
95 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.try_finish(),
96 Decompressor::Gzip(gz_decoder) => gz_decoder.try_finish(),
97 Decompressor::Brotli(brotli_decoder) => brotli_decoder.flush(),
98 }
99 }
100}
101
102impl MallocSizeOf for Decompressor {
103 #[expect(unsafe_code)]
104 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
105 match self {
106 Decompressor::Deflate(zlib_decoder) => zlib_decoder.size_of(ops),
107 Decompressor::DeflateRaw(deflate_decoder) => deflate_decoder.size_of(ops),
108 Decompressor::Gzip(gz_decoder) => gz_decoder.size_of(ops),
109 Decompressor::Brotli(brotli_decoder) => unsafe {
110 ops.malloc_size_of(&**brotli_decoder)
111 },
112 }
113 }
114}
115
116#[dom_struct]
118pub(crate) struct DecompressionStream {
119 reflector_: Reflector,
120
121 transform: Dom<TransformStream>,
123
124 format: CompressionFormat,
126
127 #[no_trace]
129 context: RefCell<Decompressor>,
130}
131
132impl DecompressionStream {
133 fn new_inherited(
134 transform: &TransformStream,
135 format: CompressionFormat,
136 ) -> DecompressionStream {
137 DecompressionStream {
138 reflector_: Reflector::new(),
139 transform: Dom::from_ref(transform),
140 format,
141 context: RefCell::new(Decompressor::new(format)),
142 }
143 }
144
145 fn new_with_proto(
146 global: &GlobalScope,
147 proto: Option<SafeHandleObject>,
148 transform: &TransformStream,
149 format: CompressionFormat,
150 can_gc: CanGc,
151 ) -> DomRoot<DecompressionStream> {
152 reflect_dom_object_with_proto(
153 Box::new(DecompressionStream::new_inherited(transform, format)),
154 global,
155 proto,
156 can_gc,
157 )
158 }
159}
160
161impl DecompressionStreamMethods<crate::DomTypeHolder> for DecompressionStream {
162 fn Constructor(
164 global: &GlobalScope,
165 proto: Option<SafeHandleObject>,
166 can_gc: CanGc,
167 format: CompressionFormat,
168 ) -> Fallible<DomRoot<DecompressionStream>> {
169 let transform = TransformStream::new_with_proto(global, None, can_gc);
175 let decompression_stream =
176 DecompressionStream::new_with_proto(global, proto, &transform, format, can_gc);
177
178 let transformer_type = TransformerType::Decompressor(decompression_stream.clone());
183
184 let cx = GlobalScope::get_cx();
187 transform.set_up(cx, global, transformer_type, can_gc)?;
188
189 Ok(decompression_stream)
190 }
191
192 fn Readable(&self) -> DomRoot<ReadableStream> {
194 self.transform.get_readable()
196 }
197
198 fn Writable(&self) -> DomRoot<WritableStream> {
200 self.transform.get_writable()
202 }
203}
204
205pub(crate) fn decompress_and_enqueue_a_chunk(
207 cx: SafeJSContext,
208 global: &GlobalScope,
209 ds: &DecompressionStream,
210 chunk: SafeHandleValue,
211 controller: &TransformStreamDefaultController,
212 can_gc: CanGc,
213) -> Fallible<()> {
214 let chunk = convert_chunk_to_vec(cx, chunk, can_gc)?;
216
217 let mut decompressor = ds.context.borrow_mut();
221 let mut offset = 0;
222 let mut written = 1;
223 while offset < chunk.len() && written > 0 {
224 written = decompressor
225 .write(&chunk[offset..])
226 .map_err(|_| Error::Type("DecompressionStream: write() failed".to_string()))?;
227 offset += written;
228 }
229 decompressor
230 .flush()
231 .map_err(|_| Error::Type("DecompressionStream: flush() failed".to_string()))?;
232 let buffer = decompressor.get_ref();
233
234 if buffer.is_empty() {
236 return Ok(());
237 }
238
239 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
244 let array: Uint8Array = create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
245 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
246 rooted!(in(*cx) let mut rval = UndefinedValue());
247 array.safe_to_jsval(cx, rval.handle_mut(), can_gc);
248 controller.enqueue(cx, global, rval.handle(), can_gc)?;
249
250 decompressor.get_mut().clear();
253
254 if offset < chunk.len() {
257 return Err(Error::Type(
258 "The end of the compressed input has been reached".to_string(),
259 ));
260 }
261
262 Ok(())
263}
264
265pub(crate) fn decompress_flush_and_enqueue(
267 cx: SafeJSContext,
268 global: &GlobalScope,
269 ds: &DecompressionStream,
270 controller: &TransformStreamDefaultController,
271 can_gc: CanGc,
272) -> Fallible<()> {
273 let mut decompressor = ds.context.borrow_mut();
277 let offset = decompressor.get_ref().len();
278 let is_ended = decompressor
279 .write(&[0])
280 .map_err(|_| Error::Type("DecompressionStream: write() failed".to_string()))? ==
281 0;
282 decompressor
283 .try_finish()
284 .map_err(|_| Error::Type("DecompressionStream: try_finish() failed".to_string()))?;
285 let buffer = &decompressor.get_ref()[offset..];
286
287 if !buffer.is_empty() {
289 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
294 let array: Uint8Array = create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
295 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
296 rooted!(in(*cx) let mut rval = UndefinedValue());
297 array.safe_to_jsval(cx, rval.handle_mut(), can_gc);
298 controller.enqueue(cx, global, rval.handle(), can_gc)?;
299 }
300
301 decompressor.get_mut().clear();
304
305 if !is_ended {
316 return Err(Error::Type(
317 "The end of the compressed input has not been reached".to_string(),
318 ));
319 }
320
321 Ok(())
322}