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) => {
55 UnderlyingSource::Js(source.clone(), Heap::default())
56 },
57 UnderlyingSourceType::Tee(source) => UnderlyingSource::Tee(Dom::from_ref(source)),
58 UnderlyingSourceType::Transfer(port) => UnderlyingSource::Transfer(Dom::from_ref(port)),
59 UnderlyingSourceType::Transform(stream, promise) => {
60 UnderlyingSource::Transform(Dom::from_ref(stream), promise.to_traced())
61 },
62 UnderlyingSourceType::TeeByte(source) => {
63 UnderlyingSource::TeeByte(Dom::from_ref(source))
64 },
65 }
66 }
67}
68
69pub(crate) enum UnderlyingSourceType<'a> {
74 Memory(usize),
77 Blob(usize),
79 FetchResponse,
81 Js(&'a JsUnderlyingSource),
84 Tee(&'a DefaultTeeUnderlyingSource),
86 Transfer(&'a MessagePort),
88 Transform(&'a TransformStream, &'a RootedPromise),
92 TeeByte(&'a ByteTeeUnderlyingSource),
94}
95
96impl UnderlyingSourceType<'_> {
97 pub(crate) fn is_native(&self) -> bool {
99 matches!(
100 self,
101 UnderlyingSourceType::Memory(_) |
102 UnderlyingSourceType::Blob(_) |
103 UnderlyingSourceType::FetchResponse |
104 UnderlyingSourceType::Transfer(_)
105 )
106 }
107}
108
109#[dom_struct]
111pub(crate) struct UnderlyingSourceContainer {
112 reflector_: Reflector,
113 #[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."]
114 underlying_source_type: UnderlyingSource,
115}
116
117impl UnderlyingSourceContainer {
118 fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer {
119 UnderlyingSourceContainer {
120 reflector_: Reflector::new(),
121 underlying_source_type: underlying_source_type.into(),
122 }
123 }
124
125 pub(crate) fn new(
126 cx: &mut JSContext,
127 global: &GlobalScope,
128 underlying_source_type: UnderlyingSourceType,
129 ) -> DomRoot<UnderlyingSourceContainer> {
130 reflect_dom_object(
134 cx,
135 Box::new(UnderlyingSourceContainer::new_inherited(
136 underlying_source_type,
137 )),
138 global,
139 )
140 }
141
142 pub(crate) fn set_underlying_source_this_object(&self, object: HandleObject) {
144 if let UnderlyingSource::Js(_source, this_obj) = &self.underlying_source_type {
145 this_obj.set(*object);
146 }
147 }
148
149 pub(crate) fn call_cancel_algorithm(
151 &self,
152 cx: &mut JSContext,
153 global: &GlobalScope,
154 reason: SafeHandleValue,
155 ) -> Option<Result<RootedPromise, Error>> {
156 match &self.underlying_source_type {
157 UnderlyingSource::Js(source, this_obj) => {
158 if let Some(algo) = &source.cancel {
159 let result = algo.Call_(
160 cx,
161 &rooted_heap_handle(self, |_| this_obj),
162 Some(reason),
163 ExceptionHandling::Rethrow,
164 );
165 return Some(result);
166 }
167 None
168 },
169 UnderlyingSource::Tee(tee_underlying_source) => {
170 tee_underlying_source.cancel_algorithm(cx, global, reason)
172 },
173 UnderlyingSource::Transform(stream, _) => {
174 Some(stream.transform_stream_default_source_cancel(cx, global, reason))
176 },
177 UnderlyingSource::Transfer(port) => {
178 let result = port.pack_and_post_message_handling_error(cx, "error", reason);
183
184 self.global().disentangle_port(cx, port);
186
187 let promise = Promise::new(cx, &self.global());
188
189 if let Err(error) = result {
191 promise.reject_error(cx, error);
193 } else {
194 promise.resolve_native(cx, &());
196 }
197 Some(Ok(promise))
198 },
199 UnderlyingSource::TeeByte(tee_underlyin_source) => {
200 tee_underlyin_source.cancel_algorithm(cx, reason)
202 },
203 _ => None,
204 }
205 }
206
207 pub(crate) fn call_pull_algorithm(
209 &self,
210 cx: &mut JSContext,
211 controller: Controller,
212 ) -> Option<Result<RootedPromise, Error>> {
213 match &self.underlying_source_type {
214 UnderlyingSource::Js(source, this_obj) => {
215 if let Some(algo) = &source.pull {
216 let result = algo.Call_(
217 cx,
218 &rooted_heap_handle(self, |_| this_obj),
219 controller,
220 ExceptionHandling::Rethrow,
221 );
222 return Some(result);
223 }
224 None
225 },
226 UnderlyingSource::Tee(tee_underlying_source) => {
227 Some(Ok(tee_underlying_source.pull_algorithm(cx)))
229 },
230 UnderlyingSource::Transfer(port) => {
231 rooted!(&in(cx) let mut value = UndefinedValue());
236 port.pack_and_post_message(cx, "pull", value.handle())
237 .expect("Sending pull should not fail.");
238
239 let promise = Promise::new_resolved(cx, &self.global(), ());
241 Some(Ok(promise))
242 },
243 UnderlyingSource::TeeByte(tee_underlyin_source) => {
244 Some(Ok(tee_underlyin_source.pull_algorithm(cx, None)))
246 },
247 UnderlyingSource::Transform(stream, _) => {
249 Some(stream.transform_stream_default_source_pull(cx, &self.global()))
251 },
252 _ => None,
253 }
254 }
255
256 pub(crate) fn call_start_algorithm(
264 &self,
265 cx: &mut JSContext,
266 controller: Controller,
267 ) -> Option<Result<RootedPromise, Error>> {
268 match &self.underlying_source_type {
269 UnderlyingSource::Js(source, this_obj) => {
270 if let Some(start) = &source.start {
271 rooted!(&in(cx) let mut result: JSVal);
272 if let Err(error) = start.Call_(
273 cx,
274 &rooted_heap_handle(self, |_| this_obj),
275 controller,
276 result.handle_mut(),
277 ExceptionHandling::Rethrow,
278 ) {
279 return Some(Err(error));
280 }
281 let promise =
282 Promise::resolve_or_wrap_promise(cx, result.handle(), &self.global());
283 return Some(Ok(promise));
284 }
285 None
286 },
287 UnderlyingSource::Tee(_) => {
288 None
290 },
291 UnderlyingSource::Transfer(_) => {
292 None
295 },
296 UnderlyingSource::Transform(_, start_promise) => {
297 Some(Ok(start_promise.root(cx)))
299 },
300 _ => None,
301 }
302 }
303
304 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
306 match &self.underlying_source_type {
307 UnderlyingSource::Js(source, _) => source.autoAllocateChunkSize,
308 _ => None,
309 }
310 }
311
312 pub(crate) fn in_memory(&self) -> bool {
314 self.underlying_source_type.in_memory()
315 }
316}