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;
16use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
17
18use crate::dom::bindings::buffer_source::create_buffer_source;
19use crate::dom::bindings::codegen::Bindings::CompressionStreamBinding::{
20 CompressionFormat, CompressionStreamMethods,
21};
22use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
23use crate::dom::bindings::conversions::{SafeFromJSValConvertible, SafeToJSValConvertible};
24use crate::dom::bindings::error::{Error, Fallible};
25use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
26use crate::dom::bindings::root::{Dom, DomRoot};
27use crate::dom::transformstreamdefaultcontroller::TransformerType;
28use crate::dom::types::{
29 GlobalScope, ReadableStream, TransformStream, TransformStreamDefaultController, WritableStream,
30};
31use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
32
33enum Compressor {
36 Deflate(ZlibEncoder<Vec<u8>>),
37 DeflateRaw(DeflateEncoder<Vec<u8>>),
38 Gzip(GzEncoder<Vec<u8>>),
39}
40
41impl Compressor {
43 fn new(format: CompressionFormat) -> Compressor {
44 match format {
45 CompressionFormat::Deflate => {
46 Compressor::Deflate(ZlibEncoder::new(Vec::new(), Compression::default()))
47 },
48 CompressionFormat::Deflate_raw => {
49 Compressor::DeflateRaw(DeflateEncoder::new(Vec::new(), Compression::default()))
50 },
51 CompressionFormat::Gzip => {
52 Compressor::Gzip(GzEncoder::new(Vec::new(), Compression::default()))
53 },
54 }
55 }
56
57 fn get_ref(&self) -> &Vec<u8> {
58 match self {
59 Compressor::Deflate(zlib_encoder) => zlib_encoder.get_ref(),
60 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.get_ref(),
61 Compressor::Gzip(gz_encoder) => gz_encoder.get_ref(),
62 }
63 }
64
65 fn get_mut(&mut self) -> &mut Vec<u8> {
66 match self {
67 Compressor::Deflate(zlib_encoder) => zlib_encoder.get_mut(),
68 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.get_mut(),
69 Compressor::Gzip(gz_encoder) => gz_encoder.get_mut(),
70 }
71 }
72
73 fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> {
74 match self {
75 Compressor::Deflate(zlib_encoder) => zlib_encoder.write_all(buf),
76 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.write_all(buf),
77 Compressor::Gzip(gz_encoder) => gz_encoder.write_all(buf),
78 }
79 }
80
81 fn flush(&mut self) -> io::Result<()> {
82 match self {
83 Compressor::Deflate(zlib_encoder) => zlib_encoder.flush(),
84 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.flush(),
85 Compressor::Gzip(gz_encoder) => gz_encoder.flush(),
86 }
87 }
88
89 fn try_finish(&mut self) -> io::Result<()> {
90 match self {
91 Compressor::Deflate(zlib_encoder) => zlib_encoder.try_finish(),
92 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.try_finish(),
93 Compressor::Gzip(gz_encoder) => gz_encoder.try_finish(),
94 }
95 }
96}
97
98impl MallocSizeOf for Compressor {
99 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
100 match self {
101 Compressor::Deflate(zlib_encoder) => zlib_encoder.size_of(ops),
102 Compressor::DeflateRaw(deflate_encoder) => deflate_encoder.size_of(ops),
103 Compressor::Gzip(gz_encoder) => gz_encoder.size_of(ops),
104 }
105 }
106}
107
108#[dom_struct]
110pub(crate) struct CompressionStream {
111 reflector_: Reflector,
112
113 transform: Dom<TransformStream>,
115
116 format: CompressionFormat,
118
119 #[no_trace]
121 context: RefCell<Compressor>,
122}
123
124impl CompressionStream {
125 fn new_inherited(transform: &TransformStream, format: CompressionFormat) -> CompressionStream {
126 CompressionStream {
127 reflector_: Reflector::new(),
128 transform: Dom::from_ref(transform),
129 format,
130 context: RefCell::new(Compressor::new(format)),
131 }
132 }
133
134 fn new_with_proto(
135 global: &GlobalScope,
136 proto: Option<SafeHandleObject>,
137 transform: &TransformStream,
138 format: CompressionFormat,
139 can_gc: CanGc,
140 ) -> DomRoot<CompressionStream> {
141 reflect_dom_object_with_proto(
142 Box::new(CompressionStream::new_inherited(transform, format)),
143 global,
144 proto,
145 can_gc,
146 )
147 }
148}
149
150impl CompressionStreamMethods<crate::DomTypeHolder> for CompressionStream {
151 fn Constructor(
153 global: &GlobalScope,
154 proto: Option<SafeHandleObject>,
155 can_gc: CanGc,
156 format: CompressionFormat,
157 ) -> Fallible<DomRoot<CompressionStream>> {
158 let transform = TransformStream::new_with_proto(global, None, can_gc);
164 let compression_stream =
165 CompressionStream::new_with_proto(global, proto, &transform, format, can_gc);
166
167 let transformer_type = TransformerType::Compressor(compression_stream.clone());
172
173 let cx = GlobalScope::get_cx();
176 transform.set_up(cx, global, transformer_type, can_gc)?;
177
178 Ok(compression_stream)
179 }
180
181 fn Readable(&self) -> DomRoot<ReadableStream> {
183 self.transform.get_readable()
185 }
186
187 fn Writable(&self) -> DomRoot<WritableStream> {
189 self.transform.get_writable()
191 }
192}
193
194pub(crate) fn compress_and_enqueue_a_chunk(
196 cx: SafeJSContext,
197 global: &GlobalScope,
198 cs: &CompressionStream,
199 chunk: SafeHandleValue,
200 controller: &TransformStreamDefaultController,
201 can_gc: CanGc,
202) -> Fallible<()> {
203 let chunk = convert_chunk_to_vec(cx, chunk, can_gc)?;
205
206 let mut compressor = cs.context.borrow_mut();
209 let offset = compressor.get_ref().len();
210 compressor
211 .write_all(&chunk)
212 .map_err(|_| Error::Type("CompressionStream: write_all() failed".to_string()))?;
213 compressor
214 .flush()
215 .map_err(|_| Error::Type("CompressionStream: flush() failed".to_string()))?;
216 let buffer = &compressor.get_ref()[offset..];
217
218 if buffer.is_empty() {
220 return Ok(());
221 }
222
223 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
228 let buffer_source: Uint8Array =
229 create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
230 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
231 rooted!(in(*cx) let mut rval = UndefinedValue());
232 buffer_source.safe_to_jsval(cx, rval.handle_mut(), can_gc);
233 controller.enqueue(cx, global, rval.handle(), can_gc)?;
234
235 compressor.get_mut().clear();
238
239 Ok(())
240}
241
242pub(crate) fn compress_flush_and_enqueue(
244 cx: SafeJSContext,
245 global: &GlobalScope,
246 cs: &CompressionStream,
247 controller: &TransformStreamDefaultController,
248 can_gc: CanGc,
249) -> Fallible<()> {
250 let mut compressor = cs.context.borrow_mut();
254 let offset = compressor.get_ref().len();
255 compressor
256 .try_finish()
257 .map_err(|_| Error::Type("CompressionStream: try_finish() failed".to_string()))?;
258 let buffer = &compressor.get_ref()[offset..];
259
260 if buffer.is_empty() {
262 return Ok(());
263 }
264
265 rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
270 let buffer_source: Uint8Array =
271 create_buffer_source(cx, buffer, js_object.handle_mut(), can_gc)
272 .map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
273 rooted!(in(*cx) let mut rval = UndefinedValue());
274 buffer_source.safe_to_jsval(cx, rval.handle_mut(), can_gc);
275 controller.enqueue(cx, global, rval.handle(), can_gc)?;
276
277 compressor.get_mut().clear();
280
281 Ok(())
282}
283
284pub(crate) fn convert_chunk_to_vec(
285 cx: SafeJSContext,
286 chunk: SafeHandleValue,
287 can_gc: CanGc,
288) -> Result<Vec<u8>, Error> {
289 let conversion_result = ArrayBufferViewOrArrayBuffer::safe_from_jsval(cx, chunk, (), can_gc)
290 .map_err(|_| {
291 Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string())
292 })?;
293 let buffer_source = conversion_result.get_success_value().ok_or_else(|| {
294 Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string())
295 })?;
296 match buffer_source {
297 ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => Ok(view.to_vec()),
298 ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => Ok(buffer.to_vec()),
299 }
300}