1use std::cell::RefCell;
6use std::io::{self, Write};
7use std::ptr;
8
9use dom_struct::dom_struct;
10use flate2::Compression;
11use flate2::write::{DeflateEncoder, GzEncoder, ZlibEncoder};
12use js::jsapi::JSObject;
13use js::jsval::UndefinedValue;
14use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
15use js::typedarray::Uint8Array;
16
17use crate::dom::bindings::buffer_source::create_buffer_source;
18use crate::dom::bindings::codegen::Bindings::CompressionStreamBinding::{
19 CompressionFormat, CompressionStreamMethods,
20};
21use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
22use crate::dom::bindings::conversions::{SafeFromJSValConvertible, SafeToJSValConvertible};
23use crate::dom::bindings::error::{Error, Fallible};
24use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
25use crate::dom::bindings::root::{Dom, DomRoot};
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 Compressor {
35 Deflate(ZlibEncoder<Vec<u8>>),
36 DeflateRaw(DeflateEncoder<Vec<u8>>),
37 Gzip(GzEncoder<Vec<u8>>),
38}
39
40impl Compressor {
42 fn new(format: CompressionFormat) -> Compressor {
43 match format {
44 CompressionFormat::Deflate => {
45 Compressor::Deflate(ZlibEncoder::new(Vec::new(), Compression::default()))
46 },
47 CompressionFormat::Deflate_raw => {
48 Compressor::DeflateRaw(DeflateEncoder::new(Vec::new(), Compression::default()))
49 },
50 CompressionFormat::Gzip => {
51 Compressor::Gzip(GzEncoder::new(Vec::new(), Compression::default()))
52 },
53 }
54 }
55
56 fn get_ref(&self) -> &Vec<u8> {
57 match self {
58 Compressor::Deflate(zlib_encoder) => zlib_encoder.get_ref(),
59 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.get_ref(),
60 Compressor::Gzip(gz_encoder) => gz_encoder.get_ref(),
61 }
62 }
63
64 fn get_mut(&mut self) -> &mut Vec<u8> {
65 match self {
66 Compressor::Deflate(zlib_encoder) => zlib_encoder.get_mut(),
67 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.get_mut(),
68 Compressor::Gzip(gz_encoder) => gz_encoder.get_mut(),
69 }
70 }
71
72 fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> {
73 match self {
74 Compressor::Deflate(zlib_encoder) => zlib_encoder.write_all(buf),
75 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.write_all(buf),
76 Compressor::Gzip(gz_encoder) => gz_encoder.write_all(buf),
77 }
78 }
79
80 fn flush(&mut self) -> io::Result<()> {
81 match self {
82 Compressor::Deflate(zlib_encoder) => zlib_encoder.flush(),
83 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.flush(),
84 Compressor::Gzip(gz_encoder) => gz_encoder.flush(),
85 }
86 }
87
88 fn try_finish(&mut self) -> io::Result<()> {
89 match self {
90 Compressor::Deflate(zlib_encoder) => zlib_encoder.try_finish(),
91 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.try_finish(),
92 Compressor::Gzip(gz_encoder) => gz_encoder.try_finish(),
93 }
94 }
95}
96
97#[dom_struct]
99pub(crate) struct CompressionStream {
100 reflector_: Reflector,
101
102 transform: Dom<TransformStream>,
104
105 format: CompressionFormat,
107
108 #[ignore_malloc_size_of = "defined in flate2"]
110 #[no_trace]
111 context: RefCell<Compressor>,
112}
113
114impl CompressionStream {
115 fn new_inherited(transform: &TransformStream, format: CompressionFormat) -> CompressionStream {
116 CompressionStream {
117 reflector_: Reflector::new(),
118 transform: Dom::from_ref(transform),
119 format,
120 context: RefCell::new(Compressor::new(format)),
121 }
122 }
123
124 fn new_with_proto(
125 global: &GlobalScope,
126 proto: Option<SafeHandleObject>,
127 transform: &TransformStream,
128 format: CompressionFormat,
129 can_gc: CanGc,
130 ) -> DomRoot<CompressionStream> {
131 reflect_dom_object_with_proto(
132 Box::new(CompressionStream::new_inherited(transform, format)),
133 global,
134 proto,
135 can_gc,
136 )
137 }
138}
139
140impl CompressionStreamMethods<crate::DomTypeHolder> for CompressionStream {
141 fn Constructor(
143 global: &GlobalScope,
144 proto: Option<SafeHandleObject>,
145 can_gc: CanGc,
146 format: CompressionFormat,
147 ) -> Fallible<DomRoot<CompressionStream>> {
148 let transform = TransformStream::new_with_proto(global, None, can_gc);
154 let compression_stream =
155 CompressionStream::new_with_proto(global, proto, &transform, format, can_gc);
156
157 let transformer_type = TransformerType::Compressor(compression_stream.clone());
162
163 let cx = GlobalScope::get_cx();
166 transform.set_up(cx, global, transformer_type, can_gc)?;
167
168 Ok(compression_stream)
169 }
170
171 fn Readable(&self) -> DomRoot<ReadableStream> {
173 self.transform.get_readable()
175 }
176
177 fn Writable(&self) -> DomRoot<WritableStream> {
179 self.transform.get_writable()
181 }
182}
183
184pub(crate) fn compress_and_enqueue_a_chunk(
186 cx: SafeJSContext,
187 global: &GlobalScope,
188 cs: &CompressionStream,
189 chunk: SafeHandleValue,
190 controller: &TransformStreamDefaultController,
191 can_gc: CanGc,
192) -> Fallible<()> {
193 let chunk = convert_chunk_to_vec(cx, chunk)?;
195
196 let mut compressor = cs.context.borrow_mut();
199 let offset = compressor.get_ref().len();
200 compressor
201 .write_all(&chunk)
202 .map_err(|_| Error::Type("CompressionStream: write_all() failed".to_string()))?;
203 compressor
204 .flush()
205 .map_err(|_| Error::Type("CompressionStream: flush() failed".to_string()))?;
206 let buffer = &compressor.get_ref()[offset..];
207
208 if buffer.is_empty() {
210 return Ok(());
211 }
212
213 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
218 let buffer_source: Uint8Array =
219 create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
220 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
221 rooted!(in(*cx) let mut rval = UndefinedValue());
222 buffer_source.safe_to_jsval(cx, rval.handle_mut(), can_gc);
223 controller.enqueue(cx, global, rval.handle(), can_gc)?;
224
225 compressor.get_mut().clear();
228
229 Ok(())
230}
231
232pub(crate) fn compress_flush_and_enqueue(
234 cx: SafeJSContext,
235 global: &GlobalScope,
236 cs: &CompressionStream,
237 controller: &TransformStreamDefaultController,
238 can_gc: CanGc,
239) -> Fallible<()> {
240 let mut compressor = cs.context.borrow_mut();
244 let offset = compressor.get_ref().len();
245 compressor
246 .try_finish()
247 .map_err(|_| Error::Type("CompressionStream: try_finish() failed".to_string()))?;
248 let buffer = &compressor.get_ref()[offset..];
249
250 if buffer.is_empty() {
252 return Ok(());
253 }
254
255 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
260 let buffer_source: Uint8Array =
261 create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
262 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
263 rooted!(in(*cx) let mut rval = UndefinedValue());
264 buffer_source.safe_to_jsval(cx, rval.handle_mut(), can_gc);
265 controller.enqueue(cx, global, rval.handle(), can_gc)?;
266
267 compressor.get_mut().clear();
270
271 Ok(())
272}
273
274pub(crate) fn convert_chunk_to_vec(
275 cx: SafeJSContext,
276 chunk: SafeHandleValue,
277) -> Result<Vec<u8>, Error> {
278 let conversion_result =
279 ArrayBufferViewOrArrayBuffer::safe_from_jsval(cx, chunk, ()).map_err(|_| {
280 Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string())
281 })?;
282 let buffer_source = conversion_result.get_success_value().ok_or_else(|| {
283 Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string())
284 })?;
285 match buffer_source {
286 ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => Ok(view.to_vec()),
287 ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => Ok(buffer.to_vec()),
288 }
289}