1use std::ptr;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::jsapi::{Heap, IsPromiseObject, JSObject};
10use js::jsval::{JSVal, UndefinedValue};
11use js::rust::{Handle as SafeHandle, HandleObject, HandleValue as SafeHandleValue, IntoHandle};
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, Reflector, reflect_dom_object_with_proto};
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::messageport::MessagePort;
22use crate::dom::promise::Promise;
23use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
24use crate::dom::stream::transformstream::TransformStream;
25use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
26
27#[derive(JSTraceable)]
32#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
33pub(crate) enum UnderlyingSourceType {
34 Memory(usize),
37 Blob(usize),
39 FetchResponse,
41 Js(JsUnderlyingSource, Heap<*mut JSObject>),
44 Tee(Dom<DefaultTeeUnderlyingSource>),
46 Transfer(Dom<MessagePort>),
48 Transform(Dom<TransformStream>, Rc<Promise>),
52 TeeByte(Dom<ByteTeeUnderlyingSource>),
54}
55
56impl UnderlyingSourceType {
57 pub(crate) fn is_native(&self) -> bool {
59 matches!(
60 self,
61 UnderlyingSourceType::Memory(_) |
62 UnderlyingSourceType::Blob(_) |
63 UnderlyingSourceType::FetchResponse |
64 UnderlyingSourceType::Transfer(_)
65 )
66 }
67
68 pub(crate) fn in_memory(&self) -> bool {
70 matches!(self, UnderlyingSourceType::Memory(_))
71 }
72}
73
74#[dom_struct]
76pub(crate) struct UnderlyingSourceContainer {
77 reflector_: Reflector,
78 #[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."]
79 underlying_source_type: UnderlyingSourceType,
80}
81
82impl UnderlyingSourceContainer {
83 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
84 fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer {
85 UnderlyingSourceContainer {
86 reflector_: Reflector::new(),
87 underlying_source_type,
88 }
89 }
90
91 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
92 pub(crate) fn new(
93 global: &GlobalScope,
94 underlying_source_type: UnderlyingSourceType,
95 can_gc: CanGc,
96 ) -> DomRoot<UnderlyingSourceContainer> {
97 reflect_dom_object_with_proto(
101 Box::new(UnderlyingSourceContainer::new_inherited(
102 underlying_source_type,
103 )),
104 global,
105 None,
106 can_gc,
107 )
108 }
109
110 pub(crate) fn set_underlying_source_this_object(&self, object: HandleObject) {
112 if let UnderlyingSourceType::Js(_source, this_obj) = &self.underlying_source_type {
113 this_obj.set(*object);
114 }
115 }
116
117 #[expect(unsafe_code)]
119 pub(crate) fn call_cancel_algorithm(
120 &self,
121 cx: SafeJSContext,
122 global: &GlobalScope,
123 reason: SafeHandleValue,
124 can_gc: CanGc,
125 ) -> Option<Result<Rc<Promise>, Error>> {
126 match &self.underlying_source_type {
127 UnderlyingSourceType::Js(source, this_obj) => {
128 if let Some(algo) = &source.cancel {
129 let result = unsafe {
130 algo.Call_(
131 &SafeHandle::from_raw(this_obj.handle()),
132 Some(reason),
133 ExceptionHandling::Rethrow,
134 can_gc,
135 )
136 };
137 return Some(result);
138 }
139 None
140 },
141 UnderlyingSourceType::Tee(tee_underlying_source) => {
142 tee_underlying_source.cancel_algorithm(cx, global, reason, can_gc)
144 },
145 UnderlyingSourceType::Transform(stream, _) => {
146 Some(stream.transform_stream_default_source_cancel(cx, global, reason, can_gc))
148 },
149 UnderlyingSourceType::Transfer(port) => {
150 let result = port.pack_and_post_message_handling_error("error", reason, can_gc);
155
156 self.global().disentangle_port(port, can_gc);
158
159 let promise = Promise::new(&self.global(), can_gc);
160
161 if let Err(error) = result {
163 promise.reject_error(error, can_gc);
165 } else {
166 promise.resolve_native(&(), can_gc);
168 }
169 Some(Ok(promise))
170 },
171 UnderlyingSourceType::TeeByte(tee_underlyin_source) => {
172 tee_underlyin_source.cancel_algorithm(reason, can_gc)
174 },
175 _ => None,
176 }
177 }
178
179 #[expect(unsafe_code)]
181 pub(crate) fn call_pull_algorithm(
182 &self,
183 controller: Controller,
184 _global: &GlobalScope,
185 can_gc: CanGc,
186 ) -> Option<Result<Rc<Promise>, Error>> {
187 match &self.underlying_source_type {
188 UnderlyingSourceType::Js(source, this_obj) => {
189 if let Some(algo) = &source.pull {
190 let result = unsafe {
191 algo.Call_(
192 &SafeHandle::from_raw(this_obj.handle()),
193 controller,
194 ExceptionHandling::Rethrow,
195 can_gc,
196 )
197 };
198 return Some(result);
199 }
200 None
201 },
202 UnderlyingSourceType::Tee(tee_underlying_source) => {
203 Some(Ok(tee_underlying_source.pull_algorithm(can_gc)))
205 },
206 UnderlyingSourceType::Transfer(port) => {
207 let cx = GlobalScope::get_cx();
211
212 rooted!(in(*cx) let mut value = UndefinedValue());
214 port.pack_and_post_message("pull", value.handle(), can_gc)
215 .expect("Sending pull should not fail.");
216
217 let promise = Promise::new(&self.global(), can_gc);
219 promise.resolve_native(&(), can_gc);
220 Some(Ok(promise))
221 },
222 UnderlyingSourceType::TeeByte(tee_underlyin_source) => {
223 Some(Ok(tee_underlyin_source.pull_algorithm(None, can_gc)))
225 },
226 UnderlyingSourceType::Transform(stream, _) => {
228 Some(stream.transform_stream_default_source_pull(&self.global(), can_gc))
230 },
231 _ => None,
232 }
233 }
234
235 #[expect(unsafe_code)]
243 pub(crate) fn call_start_algorithm(
244 &self,
245 controller: Controller,
246 can_gc: CanGc,
247 ) -> Option<Result<Rc<Promise>, Error>> {
248 match &self.underlying_source_type {
249 UnderlyingSourceType::Js(source, this_obj) => {
250 if let Some(start) = &source.start {
251 let cx = GlobalScope::get_cx();
252 rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
253 rooted!(in(*cx) let mut result: JSVal);
254 unsafe {
255 if let Err(error) = start.Call_(
256 &SafeHandle::from_raw(this_obj.handle()),
257 controller,
258 result.handle_mut(),
259 ExceptionHandling::Rethrow,
260 can_gc,
261 ) {
262 return Some(Err(error));
263 }
264 }
265 let is_promise = unsafe {
266 if result.is_object() {
267 result_object.set(result.to_object());
268 IsPromiseObject(result_object.handle().into_handle())
269 } else {
270 false
271 }
272 };
273 let promise = if is_promise {
274 Promise::new_with_js_promise(result_object.handle(), cx)
275 } else {
276 let promise = Promise::new(&self.global(), can_gc);
277 promise.resolve_native(&result.get(), can_gc);
278 promise
279 };
280 return Some(Ok(promise));
281 }
282 None
283 },
284 UnderlyingSourceType::Tee(_) => {
285 None
287 },
288 UnderlyingSourceType::Transfer(_) => {
289 None
292 },
293 UnderlyingSourceType::Transform(_, start_promise) => {
294 Some(Ok(start_promise.clone()))
296 },
297 _ => None,
298 }
299 }
300
301 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
303 match &self.underlying_source_type {
304 UnderlyingSourceType::Js(source, _) => source.autoAllocateChunkSize,
305 _ => None,
306 }
307 }
308
309 pub(crate) fn in_memory(&self) -> bool {
311 self.underlying_source_type.in_memory()
312 }
313}