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_cx};
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;
27
28#[derive(JSTraceable)]
31#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
32enum UnderlyingSource {
33 Memory(usize),
34 Blob(usize),
35 FetchResponse,
36 Js(JsUnderlyingSource, Heap<*mut JSObject>),
37 Tee(Dom<DefaultTeeUnderlyingSource>),
38 Transfer(Dom<MessagePort>),
39 Transform(Dom<TransformStream>, Rc<Promise>),
40 TeeByte(Dom<ByteTeeUnderlyingSource>),
41}
42
43impl UnderlyingSource {
44 fn in_memory(&self) -> bool {
46 matches!(self, UnderlyingSource::Memory(_))
47 }
48}
49
50impl From<UnderlyingSourceType<'_>> for UnderlyingSource {
51 fn from(underlying_source_type: UnderlyingSourceType<'_>) -> Self {
52 match underlying_source_type {
53 UnderlyingSourceType::Memory(size) => UnderlyingSource::Memory(size),
54 UnderlyingSourceType::Blob(size) => UnderlyingSource::Blob(size),
55 UnderlyingSourceType::FetchResponse => UnderlyingSource::FetchResponse,
56 UnderlyingSourceType::Js(source) => UnderlyingSource::Js(source, Heap::default()),
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)
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(JsUnderlyingSource),
84 Tee(&'a DefaultTeeUnderlyingSource),
86 Transfer(&'a MessagePort),
88 Transform(&'a TransformStream, Rc<Promise>),
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_with_cx(
134 Box::new(UnderlyingSourceContainer::new_inherited(
135 underlying_source_type,
136 )),
137 global,
138 cx,
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 #[expect(unsafe_code)]
151 pub(crate) fn call_cancel_algorithm(
152 &self,
153 cx: &mut JSContext,
154 global: &GlobalScope,
155 reason: SafeHandleValue,
156 ) -> Option<Result<Rc<Promise>, Error>> {
157 match &self.underlying_source_type {
158 UnderlyingSource::Js(source, this_obj) => {
159 if let Some(algo) = &source.cancel {
160 let result = unsafe {
161 algo.Call_(
162 cx,
163 &SafeHandle::from_raw(this_obj.handle()),
164 Some(reason),
165 ExceptionHandling::Rethrow,
166 )
167 };
168 return Some(result);
169 }
170 None
171 },
172 UnderlyingSource::Tee(tee_underlying_source) => {
173 tee_underlying_source.cancel_algorithm(cx, global, reason)
175 },
176 UnderlyingSource::Transform(stream, _) => {
177 Some(stream.transform_stream_default_source_cancel(cx, global, reason))
179 },
180 UnderlyingSource::Transfer(port) => {
181 let result = port.pack_and_post_message_handling_error(cx, "error", reason);
186
187 self.global().disentangle_port(cx, port);
189
190 let promise = Promise::new(cx, &self.global());
191
192 if let Err(error) = result {
194 promise.reject_error(cx, error);
196 } else {
197 promise.resolve_native(cx, &());
199 }
200 Some(Ok(promise))
201 },
202 UnderlyingSource::TeeByte(tee_underlyin_source) => {
203 tee_underlyin_source.cancel_algorithm(cx, reason)
205 },
206 _ => None,
207 }
208 }
209
210 #[expect(unsafe_code)]
212 pub(crate) fn call_pull_algorithm(
213 &self,
214 cx: &mut JSContext,
215 controller: Controller,
216 ) -> Option<Result<Rc<Promise>, Error>> {
217 match &self.underlying_source_type {
218 UnderlyingSource::Js(source, this_obj) => {
219 if let Some(algo) = &source.pull {
220 let result = unsafe {
221 algo.Call_(
222 cx,
223 &SafeHandle::from_raw(this_obj.handle()),
224 controller,
225 ExceptionHandling::Rethrow,
226 )
227 };
228 return Some(result);
229 }
230 None
231 },
232 UnderlyingSource::Tee(tee_underlying_source) => {
233 Some(Ok(tee_underlying_source.pull_algorithm(cx)))
235 },
236 UnderlyingSource::Transfer(port) => {
237 rooted!(&in(cx) let mut value = UndefinedValue());
242 port.pack_and_post_message(cx, "pull", value.handle())
243 .expect("Sending pull should not fail.");
244
245 let promise = Promise::new_resolved(cx, &self.global(), ());
247 Some(Ok(promise))
248 },
249 UnderlyingSource::TeeByte(tee_underlyin_source) => {
250 Some(Ok(tee_underlyin_source.pull_algorithm(cx, None)))
252 },
253 UnderlyingSource::Transform(stream, _) => {
255 Some(stream.transform_stream_default_source_pull(cx, &self.global()))
257 },
258 _ => None,
259 }
260 }
261
262 #[expect(unsafe_code)]
270 pub(crate) fn call_start_algorithm(
271 &self,
272 cx: &mut JSContext,
273 controller: Controller,
274 ) -> Option<Result<Rc<Promise>, Error>> {
275 match &self.underlying_source_type {
276 UnderlyingSource::Js(source, this_obj) => {
277 if let Some(start) = &source.start {
278 rooted!(&in(cx) let mut result_object = ptr::null_mut::<JSObject>());
279 rooted!(&in(cx) let mut result: JSVal);
280 unsafe {
281 if let Err(error) = start.Call_(
282 cx,
283 &SafeHandle::from_raw(this_obj.handle()),
284 controller,
285 result.handle_mut(),
286 ExceptionHandling::Rethrow,
287 ) {
288 return Some(Err(error));
289 }
290 }
291 let is_promise = unsafe {
292 if result.is_object() {
293 result_object.set(result.to_object());
294 IsPromiseObject(result_object.handle().into_handle())
295 } else {
296 false
297 }
298 };
299 let promise = if is_promise {
300 Promise::new_with_js_promise(cx, result_object.handle())
301 } else {
302 Promise::new_resolved(cx, &self.global(), result.get())
303 };
304 return Some(Ok(promise));
305 }
306 None
307 },
308 UnderlyingSource::Tee(_) => {
309 None
311 },
312 UnderlyingSource::Transfer(_) => {
313 None
316 },
317 UnderlyingSource::Transform(_, start_promise) => {
318 Some(Ok(start_promise.clone()))
320 },
321 _ => None,
322 }
323 }
324
325 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
327 match &self.underlying_source_type {
328 UnderlyingSource::Js(source, _) => source.autoAllocateChunkSize,
329 _ => None,
330 }
331 }
332
333 pub(crate) fn in_memory(&self) -> bool {
335 self.underlying_source_type.in_memory()
336 }
337}