1use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::jsapi::{Heap, JSObject};
8use js::jsval::{JSVal, UndefinedValue};
9use js::rust::{HandleObject, HandleValue as SafeHandleValue};
10use script_bindings::reflector::{Reflector, reflect_dom_object};
11use script_bindings::root::rooted_heap_handle;
12
13use super::byteteeunderlyingsource::ByteTeeUnderlyingSource;
14use crate::dom::bindings::callback::ExceptionHandling;
15use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource;
16use crate::dom::bindings::codegen::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller;
17use crate::dom::bindings::error::Error;
18use crate::dom::bindings::reflector::DomGlobal;
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::messageport::MessagePort;
22use crate::dom::promise::{Promise, RootedPromise, TracedPromise};
23use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
24use crate::dom::stream::transformstream::TransformStream;
25
26#[derive(JSTraceable)]
29#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
30enum UnderlyingSource {
31 Memory(usize),
32 Blob(usize),
33 FetchResponse,
34 Js(JsUnderlyingSource, Heap<*mut JSObject>),
35 Tee(Dom<DefaultTeeUnderlyingSource>),
36 Transfer(Dom<MessagePort>),
37 Transform(Dom<TransformStream>, TracedPromise),
38 TeeByte(Dom<ByteTeeUnderlyingSource>),
39}
40
41impl UnderlyingSource {
42 fn in_memory(&self) -> bool {
44 matches!(self, UnderlyingSource::Memory(_))
45 }
46}
47
48impl From<UnderlyingSourceType<'_>> for UnderlyingSource {
49 fn from(underlying_source_type: UnderlyingSourceType<'_>) -> Self {
50 match underlying_source_type {
51 UnderlyingSourceType::Memory(size) => UnderlyingSource::Memory(size),
52 UnderlyingSourceType::Blob(size) => UnderlyingSource::Blob(size),
53 UnderlyingSourceType::FetchResponse => UnderlyingSource::FetchResponse,
54 UnderlyingSourceType::Js(source) => UnderlyingSource::Js(source, Heap::default()),
55 UnderlyingSourceType::Tee(source) => UnderlyingSource::Tee(Dom::from_ref(source)),
56 UnderlyingSourceType::Transfer(port) => UnderlyingSource::Transfer(Dom::from_ref(port)),
57 UnderlyingSourceType::Transform(stream, promise) => {
58 UnderlyingSource::Transform(Dom::from_ref(stream), promise.to_traced())
59 },
60 UnderlyingSourceType::TeeByte(source) => {
61 UnderlyingSource::TeeByte(Dom::from_ref(source))
62 },
63 }
64 }
65}
66
67pub(crate) enum UnderlyingSourceType<'a> {
72 Memory(usize),
75 Blob(usize),
77 FetchResponse,
79 Js(JsUnderlyingSource),
82 Tee(&'a DefaultTeeUnderlyingSource),
84 Transfer(&'a MessagePort),
86 Transform(&'a TransformStream, &'a RootedPromise),
90 TeeByte(&'a ByteTeeUnderlyingSource),
92}
93
94impl UnderlyingSourceType<'_> {
95 pub(crate) fn is_native(&self) -> bool {
97 matches!(
98 self,
99 UnderlyingSourceType::Memory(_) |
100 UnderlyingSourceType::Blob(_) |
101 UnderlyingSourceType::FetchResponse |
102 UnderlyingSourceType::Transfer(_)
103 )
104 }
105}
106
107#[dom_struct]
109pub(crate) struct UnderlyingSourceContainer {
110 reflector_: Reflector,
111 #[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."]
112 underlying_source_type: UnderlyingSource,
113}
114
115impl UnderlyingSourceContainer {
116 fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer {
117 UnderlyingSourceContainer {
118 reflector_: Reflector::new(),
119 underlying_source_type: underlying_source_type.into(),
120 }
121 }
122
123 pub(crate) fn new(
124 cx: &mut JSContext,
125 global: &GlobalScope,
126 underlying_source_type: UnderlyingSourceType,
127 ) -> DomRoot<UnderlyingSourceContainer> {
128 reflect_dom_object(
132 cx,
133 Box::new(UnderlyingSourceContainer::new_inherited(
134 underlying_source_type,
135 )),
136 global,
137 )
138 }
139
140 pub(crate) fn set_underlying_source_this_object(&self, object: HandleObject) {
142 if let UnderlyingSource::Js(_source, this_obj) = &self.underlying_source_type {
143 this_obj.set(*object);
144 }
145 }
146
147 pub(crate) fn call_cancel_algorithm(
149 &self,
150 cx: &mut JSContext,
151 global: &GlobalScope,
152 reason: SafeHandleValue,
153 ) -> Option<Result<RootedPromise, Error>> {
154 match &self.underlying_source_type {
155 UnderlyingSource::Js(source, this_obj) => {
156 if let Some(algo) = &source.cancel {
157 let result = algo.Call_(
158 cx,
159 &rooted_heap_handle(self, |_| this_obj),
160 Some(reason),
161 ExceptionHandling::Rethrow,
162 );
163 return Some(result);
164 }
165 None
166 },
167 UnderlyingSource::Tee(tee_underlying_source) => {
168 tee_underlying_source.cancel_algorithm(cx, global, reason)
170 },
171 UnderlyingSource::Transform(stream, _) => {
172 Some(stream.transform_stream_default_source_cancel(cx, global, reason))
174 },
175 UnderlyingSource::Transfer(port) => {
176 let result = port.pack_and_post_message_handling_error(cx, "error", reason);
181
182 self.global().disentangle_port(cx, port);
184
185 let promise = Promise::new_rooted(cx, &self.global());
186
187 if let Err(error) = result {
189 promise.reject_error(cx, error);
191 } else {
192 promise.resolve_native(cx, &());
194 }
195 Some(Ok(promise))
196 },
197 UnderlyingSource::TeeByte(tee_underlyin_source) => {
198 tee_underlyin_source.cancel_algorithm(cx, reason)
200 },
201 _ => None,
202 }
203 }
204
205 pub(crate) fn call_pull_algorithm(
207 &self,
208 cx: &mut JSContext,
209 controller: Controller,
210 ) -> Option<Result<RootedPromise, Error>> {
211 match &self.underlying_source_type {
212 UnderlyingSource::Js(source, this_obj) => {
213 if let Some(algo) = &source.pull {
214 let result = algo.Call_(
215 cx,
216 &rooted_heap_handle(self, |_| this_obj),
217 controller,
218 ExceptionHandling::Rethrow,
219 );
220 return Some(result);
221 }
222 None
223 },
224 UnderlyingSource::Tee(tee_underlying_source) => {
225 Some(Ok(tee_underlying_source.pull_algorithm(cx)))
227 },
228 UnderlyingSource::Transfer(port) => {
229 rooted!(&in(cx) let mut value = UndefinedValue());
234 port.pack_and_post_message(cx, "pull", value.handle())
235 .expect("Sending pull should not fail.");
236
237 let promise = Promise::new_resolved_rooted(cx, &self.global(), ());
239 Some(Ok(promise))
240 },
241 UnderlyingSource::TeeByte(tee_underlyin_source) => {
242 Some(Ok(tee_underlyin_source.pull_algorithm(cx, None)))
244 },
245 UnderlyingSource::Transform(stream, _) => {
247 Some(stream.transform_stream_default_source_pull(cx, &self.global()))
249 },
250 _ => None,
251 }
252 }
253
254 pub(crate) fn call_start_algorithm(
262 &self,
263 cx: &mut JSContext,
264 controller: Controller,
265 ) -> Option<Result<RootedPromise, Error>> {
266 match &self.underlying_source_type {
267 UnderlyingSource::Js(source, this_obj) => {
268 if let Some(start) = &source.start {
269 rooted!(&in(cx) let mut result: JSVal);
270 if let Err(error) = start.Call_(
271 cx,
272 &rooted_heap_handle(self, |_| this_obj),
273 controller,
274 result.handle_mut(),
275 ExceptionHandling::Rethrow,
276 ) {
277 return Some(Err(error));
278 }
279 let promise =
280 Promise::resolve_or_wrap_promise(cx, result.handle(), &self.global());
281 return Some(Ok(promise));
282 }
283 None
284 },
285 UnderlyingSource::Tee(_) => {
286 None
288 },
289 UnderlyingSource::Transfer(_) => {
290 None
293 },
294 UnderlyingSource::Transform(_, start_promise) => {
295 Some(Ok(start_promise.root(cx)))
297 },
298 _ => None,
299 }
300 }
301
302 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
304 match &self.underlying_source_type {
305 UnderlyingSource::Js(source, _) => source.autoAllocateChunkSize,
306 _ => None,
307 }
308 }
309
310 pub(crate) fn in_memory(&self) -> bool {
312 self.underlying_source_type.in_memory()
313 }
314}