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;
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: &mut js::context::JSContext,
122 global: &GlobalScope,
123 reason: SafeHandleValue,
124 ) -> Option<Result<Rc<Promise>, Error>> {
125 match &self.underlying_source_type {
126 UnderlyingSourceType::Js(source, this_obj) => {
127 if let Some(algo) = &source.cancel {
128 let result = unsafe {
129 algo.Call_(
130 &SafeHandle::from_raw(this_obj.handle()),
131 Some(reason),
132 ExceptionHandling::Rethrow,
133 CanGc::from_cx(cx),
134 )
135 };
136 return Some(result);
137 }
138 None
139 },
140 UnderlyingSourceType::Tee(tee_underlying_source) => {
141 tee_underlying_source.cancel_algorithm(cx, global, reason)
143 },
144 UnderlyingSourceType::Transform(stream, _) => {
145 Some(stream.transform_stream_default_source_cancel(
147 cx.into(),
148 global,
149 reason,
150 CanGc::from_cx(cx),
151 ))
152 },
153 UnderlyingSourceType::Transfer(port) => {
154 let result =
159 port.pack_and_post_message_handling_error("error", reason, CanGc::from_cx(cx));
160
161 self.global().disentangle_port(cx, port);
163
164 let promise = Promise::new2(cx, &self.global());
165
166 if let Err(error) = result {
168 promise.reject_error(error, CanGc::from_cx(cx));
170 } else {
171 promise.resolve_native(&(), CanGc::from_cx(cx));
173 }
174 Some(Ok(promise))
175 },
176 UnderlyingSourceType::TeeByte(tee_underlyin_source) => {
177 tee_underlyin_source.cancel_algorithm(cx, reason)
179 },
180 _ => None,
181 }
182 }
183
184 #[expect(unsafe_code)]
186 pub(crate) fn call_pull_algorithm(
187 &self,
188 controller: Controller,
189 can_gc: CanGc,
190 ) -> Option<Result<Rc<Promise>, Error>> {
191 match &self.underlying_source_type {
192 UnderlyingSourceType::Js(source, this_obj) => {
193 if let Some(algo) = &source.pull {
194 let result = unsafe {
195 algo.Call_(
196 &SafeHandle::from_raw(this_obj.handle()),
197 controller,
198 ExceptionHandling::Rethrow,
199 can_gc,
200 )
201 };
202 return Some(result);
203 }
204 None
205 },
206 UnderlyingSourceType::Tee(tee_underlying_source) => {
207 Some(Ok(tee_underlying_source.pull_algorithm(can_gc)))
209 },
210 UnderlyingSourceType::Transfer(port) => {
211 let cx = GlobalScope::get_cx();
215
216 rooted!(in(*cx) let mut value = UndefinedValue());
218 port.pack_and_post_message("pull", value.handle(), can_gc)
219 .expect("Sending pull should not fail.");
220
221 let promise = Promise::new(&self.global(), can_gc);
223 promise.resolve_native(&(), can_gc);
224 Some(Ok(promise))
225 },
226 UnderlyingSourceType::TeeByte(tee_underlyin_source) => {
227 Some(Ok(tee_underlyin_source.pull_algorithm(None, can_gc)))
229 },
230 UnderlyingSourceType::Transform(stream, _) => {
232 Some(stream.transform_stream_default_source_pull(&self.global(), can_gc))
234 },
235 _ => None,
236 }
237 }
238
239 #[expect(unsafe_code)]
247 pub(crate) fn call_start_algorithm(
248 &self,
249 cx: &mut js::context::JSContext,
250 controller: Controller,
251 ) -> Option<Result<Rc<Promise>, Error>> {
252 match &self.underlying_source_type {
253 UnderlyingSourceType::Js(source, this_obj) => {
254 if let Some(start) = &source.start {
255 rooted!(&in(cx) let mut result_object = ptr::null_mut::<JSObject>());
256 rooted!(&in(cx) let mut result: JSVal);
257 unsafe {
258 if let Err(error) = start.Call_(
259 &SafeHandle::from_raw(this_obj.handle()),
260 controller,
261 result.handle_mut(),
262 ExceptionHandling::Rethrow,
263 CanGc::from_cx(cx),
264 ) {
265 return Some(Err(error));
266 }
267 }
268 let is_promise = unsafe {
269 if result.is_object() {
270 result_object.set(result.to_object());
271 IsPromiseObject(result_object.handle().into_handle())
272 } else {
273 false
274 }
275 };
276 let promise = if is_promise {
277 Promise::new_with_js_promise(result_object.handle(), cx.into())
278 } else {
279 Promise::new_resolved(
280 &self.global(),
281 cx.into(),
282 result.get(),
283 CanGc::from_cx(cx),
284 )
285 };
286 return Some(Ok(promise));
287 }
288 None
289 },
290 UnderlyingSourceType::Tee(_) => {
291 None
293 },
294 UnderlyingSourceType::Transfer(_) => {
295 None
298 },
299 UnderlyingSourceType::Transform(_, start_promise) => {
300 Some(Ok(start_promise.clone()))
302 },
303 _ => None,
304 }
305 }
306
307 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
309 match &self.underlying_source_type {
310 UnderlyingSourceType::Js(source, _) => source.autoAllocateChunkSize,
311 _ => None,
312 }
313 }
314
315 pub(crate) fn in_memory(&self) -> bool {
317 self.underlying_source_type.in_memory()
318 }
319}