1use std::ptr;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::jsapi::{Heap, IsPromiseObject, JSObject};
11use js::jsval::{JSVal, UndefinedValue};
12use js::rust::{Handle as SafeHandle, HandleObject, HandleValue as SafeHandleValue, IntoHandle};
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
14
15use super::byteteeunderlyingsource::ByteTeeUnderlyingSource;
16use crate::dom::bindings::callback::ExceptionHandling;
17use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource;
18use crate::dom::bindings::codegen::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller;
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::{Dom, DomRoot};
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::messageport::MessagePort;
24use crate::dom::promise::Promise;
25use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
26use crate::dom::stream::transformstream::TransformStream;
27use crate::script_runtime::CanGc;
28
29#[derive(JSTraceable)]
32#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
33enum UnderlyingSource {
34 Memory(usize),
35 Blob(usize),
36 FetchResponse,
37 Js(JsUnderlyingSource, Heap<*mut JSObject>),
38 Tee(Dom<DefaultTeeUnderlyingSource>),
39 Transfer(Dom<MessagePort>),
40 Transform(Dom<TransformStream>, Rc<Promise>),
41 TeeByte(Dom<ByteTeeUnderlyingSource>),
42}
43
44impl UnderlyingSource {
45 fn in_memory(&self) -> bool {
47 matches!(self, UnderlyingSource::Memory(_))
48 }
49}
50
51impl From<UnderlyingSourceType<'_>> for UnderlyingSource {
52 fn from(underlying_source_type: UnderlyingSourceType<'_>) -> Self {
53 match underlying_source_type {
54 UnderlyingSourceType::Memory(size) => UnderlyingSource::Memory(size),
55 UnderlyingSourceType::Blob(size) => UnderlyingSource::Blob(size),
56 UnderlyingSourceType::FetchResponse => UnderlyingSource::FetchResponse,
57 UnderlyingSourceType::Js(source) => UnderlyingSource::Js(source, Heap::default()),
58 UnderlyingSourceType::Tee(source) => UnderlyingSource::Tee(Dom::from_ref(source)),
59 UnderlyingSourceType::Transfer(port) => UnderlyingSource::Transfer(Dom::from_ref(port)),
60 UnderlyingSourceType::Transform(stream, promise) => {
61 UnderlyingSource::Transform(Dom::from_ref(stream), promise)
62 },
63 UnderlyingSourceType::TeeByte(source) => {
64 UnderlyingSource::TeeByte(Dom::from_ref(source))
65 },
66 }
67 }
68}
69
70pub(crate) enum UnderlyingSourceType<'a> {
75 Memory(usize),
78 Blob(usize),
80 FetchResponse,
82 Js(JsUnderlyingSource),
85 Tee(&'a DefaultTeeUnderlyingSource),
87 Transfer(&'a MessagePort),
89 Transform(&'a TransformStream, Rc<Promise>),
93 TeeByte(&'a ByteTeeUnderlyingSource),
95}
96
97impl UnderlyingSourceType<'_> {
98 pub(crate) fn is_native(&self) -> bool {
100 matches!(
101 self,
102 UnderlyingSourceType::Memory(_) |
103 UnderlyingSourceType::Blob(_) |
104 UnderlyingSourceType::FetchResponse |
105 UnderlyingSourceType::Transfer(_)
106 )
107 }
108}
109
110#[dom_struct]
112pub(crate) struct UnderlyingSourceContainer {
113 reflector_: Reflector,
114 #[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."]
115 underlying_source_type: UnderlyingSource,
116}
117
118impl UnderlyingSourceContainer {
119 fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer {
120 UnderlyingSourceContainer {
121 reflector_: Reflector::new(),
122 underlying_source_type: underlying_source_type.into(),
123 }
124 }
125
126 pub(crate) fn new(
127 global: &GlobalScope,
128 underlying_source_type: UnderlyingSourceType,
129 can_gc: CanGc,
130 ) -> DomRoot<UnderlyingSourceContainer> {
131 reflect_dom_object_with_proto(
135 Box::new(UnderlyingSourceContainer::new_inherited(
136 underlying_source_type,
137 )),
138 global,
139 None,
140 can_gc,
141 )
142 }
143
144 pub(crate) fn set_underlying_source_this_object(&self, object: HandleObject) {
146 if let UnderlyingSource::Js(_source, this_obj) = &self.underlying_source_type {
147 this_obj.set(*object);
148 }
149 }
150
151 #[expect(unsafe_code)]
153 pub(crate) fn call_cancel_algorithm(
154 &self,
155 cx: &mut JSContext,
156 global: &GlobalScope,
157 reason: SafeHandleValue,
158 ) -> Option<Result<Rc<Promise>, Error>> {
159 match &self.underlying_source_type {
160 UnderlyingSource::Js(source, this_obj) => {
161 if let Some(algo) = &source.cancel {
162 let result = unsafe {
163 algo.Call_(
164 cx,
165 &SafeHandle::from_raw(this_obj.handle()),
166 Some(reason),
167 ExceptionHandling::Rethrow,
168 )
169 };
170 return Some(result);
171 }
172 None
173 },
174 UnderlyingSource::Tee(tee_underlying_source) => {
175 tee_underlying_source.cancel_algorithm(cx, global, reason)
177 },
178 UnderlyingSource::Transform(stream, _) => {
179 Some(stream.transform_stream_default_source_cancel(cx, global, reason))
181 },
182 UnderlyingSource::Transfer(port) => {
183 let result = port.pack_and_post_message_handling_error(cx, "error", reason);
188
189 self.global().disentangle_port(cx, port);
191
192 let promise = Promise::new2(cx, &self.global());
193
194 if let Err(error) = result {
196 promise.reject_error(error, CanGc::from_cx(cx));
198 } else {
199 promise.resolve_native(&(), CanGc::from_cx(cx));
201 }
202 Some(Ok(promise))
203 },
204 UnderlyingSource::TeeByte(tee_underlyin_source) => {
205 tee_underlyin_source.cancel_algorithm(cx, reason)
207 },
208 _ => None,
209 }
210 }
211
212 #[expect(unsafe_code)]
214 pub(crate) fn call_pull_algorithm(
215 &self,
216 cx: &mut JSContext,
217 controller: Controller,
218 ) -> Option<Result<Rc<Promise>, Error>> {
219 match &self.underlying_source_type {
220 UnderlyingSource::Js(source, this_obj) => {
221 if let Some(algo) = &source.pull {
222 let result = unsafe {
223 algo.Call_(
224 cx,
225 &SafeHandle::from_raw(this_obj.handle()),
226 controller,
227 ExceptionHandling::Rethrow,
228 )
229 };
230 return Some(result);
231 }
232 None
233 },
234 UnderlyingSource::Tee(tee_underlying_source) => {
235 Some(Ok(tee_underlying_source.pull_algorithm(cx)))
237 },
238 UnderlyingSource::Transfer(port) => {
239 rooted!(&in(cx) let mut value = UndefinedValue());
244 port.pack_and_post_message(cx, "pull", value.handle())
245 .expect("Sending pull should not fail.");
246
247 let promise =
249 Promise::new_resolved(&self.global(), cx.into(), (), CanGc::from_cx(cx));
250 Some(Ok(promise))
251 },
252 UnderlyingSource::TeeByte(tee_underlyin_source) => {
253 Some(Ok(tee_underlyin_source.pull_algorithm(cx, None)))
255 },
256 UnderlyingSource::Transform(stream, _) => {
258 Some(
260 stream.transform_stream_default_source_pull(&self.global(), CanGc::from_cx(cx)),
261 )
262 },
263 _ => None,
264 }
265 }
266
267 #[expect(unsafe_code)]
275 pub(crate) fn call_start_algorithm(
276 &self,
277 cx: &mut JSContext,
278 controller: Controller,
279 ) -> Option<Result<Rc<Promise>, Error>> {
280 match &self.underlying_source_type {
281 UnderlyingSource::Js(source, this_obj) => {
282 if let Some(start) = &source.start {
283 rooted!(&in(cx) let mut result_object = ptr::null_mut::<JSObject>());
284 rooted!(&in(cx) let mut result: JSVal);
285 unsafe {
286 if let Err(error) = start.Call_(
287 cx,
288 &SafeHandle::from_raw(this_obj.handle()),
289 controller,
290 result.handle_mut(),
291 ExceptionHandling::Rethrow,
292 ) {
293 return Some(Err(error));
294 }
295 }
296 let is_promise = unsafe {
297 if result.is_object() {
298 result_object.set(result.to_object());
299 IsPromiseObject(result_object.handle().into_handle())
300 } else {
301 false
302 }
303 };
304 let promise = if is_promise {
305 Promise::new_with_js_promise(result_object.handle(), cx.into())
306 } else {
307 Promise::new_resolved(
308 &self.global(),
309 cx.into(),
310 result.get(),
311 CanGc::from_cx(cx),
312 )
313 };
314 return Some(Ok(promise));
315 }
316 None
317 },
318 UnderlyingSource::Tee(_) => {
319 None
321 },
322 UnderlyingSource::Transfer(_) => {
323 None
326 },
327 UnderlyingSource::Transform(_, start_promise) => {
328 Some(Ok(start_promise.clone()))
330 },
331 _ => None,
332 }
333 }
334
335 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
337 match &self.underlying_source_type {
338 UnderlyingSource::Js(source, _) => source.autoAllocateChunkSize,
339 _ => None,
340 }
341 }
342
343 pub(crate) fn in_memory(&self) -> bool {
345 self.underlying_source_type.in_memory()
346 }
347}