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 crate::dom::bindings::callback::ExceptionHandling;
14use crate::dom::bindings::codegen::Bindings::UnderlyingSourceBinding::UnderlyingSource as JsUnderlyingSource;
15use crate::dom::bindings::codegen::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller;
16use crate::dom::bindings::error::Error;
17use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
18use crate::dom::bindings::root::{Dom, DomRoot};
19use crate::dom::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::messageport::MessagePort;
22use crate::dom::promise::Promise;
23use crate::dom::transformstream::TransformStream;
24use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
25
26#[derive(JSTraceable)]
31#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
32pub(crate) enum UnderlyingSourceType {
33 Memory(usize),
36 Blob(usize),
38 FetchResponse,
40 Js(JsUnderlyingSource, Heap<*mut JSObject>),
43 Tee(Dom<DefaultTeeUnderlyingSource>),
45 Transfer(Dom<MessagePort>),
47 Transform(Dom<TransformStream>, Rc<Promise>),
51}
52
53impl UnderlyingSourceType {
54 pub(crate) fn is_native(&self) -> bool {
56 matches!(
57 self,
58 UnderlyingSourceType::Memory(_) |
59 UnderlyingSourceType::Blob(_) |
60 UnderlyingSourceType::FetchResponse |
61 UnderlyingSourceType::Transfer(_)
62 )
63 }
64
65 pub(crate) fn in_memory(&self) -> bool {
67 matches!(self, UnderlyingSourceType::Memory(_))
68 }
69}
70
71#[dom_struct]
73pub(crate) struct UnderlyingSourceContainer {
74 reflector_: Reflector,
75 #[ignore_malloc_size_of = "JsUnderlyingSource implemented in SM."]
76 underlying_source_type: UnderlyingSourceType,
77}
78
79impl UnderlyingSourceContainer {
80 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
81 fn new_inherited(underlying_source_type: UnderlyingSourceType) -> UnderlyingSourceContainer {
82 UnderlyingSourceContainer {
83 reflector_: Reflector::new(),
84 underlying_source_type,
85 }
86 }
87
88 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
89 pub(crate) fn new(
90 global: &GlobalScope,
91 underlying_source_type: UnderlyingSourceType,
92 can_gc: CanGc,
93 ) -> DomRoot<UnderlyingSourceContainer> {
94 reflect_dom_object_with_proto(
98 Box::new(UnderlyingSourceContainer::new_inherited(
99 underlying_source_type,
100 )),
101 global,
102 None,
103 can_gc,
104 )
105 }
106
107 pub(crate) fn set_underlying_source_this_object(&self, object: HandleObject) {
109 if let UnderlyingSourceType::Js(_source, this_obj) = &self.underlying_source_type {
110 this_obj.set(*object);
111 }
112 }
113
114 #[allow(unsafe_code)]
116 pub(crate) fn call_cancel_algorithm(
117 &self,
118 cx: SafeJSContext,
119 global: &GlobalScope,
120 reason: SafeHandleValue,
121 can_gc: CanGc,
122 ) -> Option<Result<Rc<Promise>, Error>> {
123 match &self.underlying_source_type {
124 UnderlyingSourceType::Js(source, this_obj) => {
125 if let Some(algo) = &source.cancel {
126 let result = unsafe {
127 algo.Call_(
128 &SafeHandle::from_raw(this_obj.handle()),
129 Some(reason),
130 ExceptionHandling::Rethrow,
131 can_gc,
132 )
133 };
134 return Some(result);
135 }
136 None
137 },
138 UnderlyingSourceType::Tee(tee_underlying_source) => {
139 tee_underlying_source.cancel_algorithm(cx, global, reason, can_gc)
141 },
142 UnderlyingSourceType::Transform(stream, _) => {
143 Some(stream.transform_stream_default_source_cancel(cx, global, reason, can_gc))
145 },
146 UnderlyingSourceType::Transfer(port) => {
147 let result = port.pack_and_post_message_handling_error("error", reason, can_gc);
152
153 self.global().disentangle_port(port, can_gc);
155
156 let promise = Promise::new(&self.global(), can_gc);
157
158 if let Err(error) = result {
160 promise.reject_error(error, can_gc);
162 } else {
163 promise.resolve_native(&(), can_gc);
165 }
166 Some(Ok(promise))
167 },
168 _ => None,
169 }
170 }
171
172 #[allow(unsafe_code)]
174 pub(crate) fn call_pull_algorithm(
175 &self,
176 controller: Controller,
177 _global: &GlobalScope,
178 can_gc: CanGc,
179 ) -> Option<Result<Rc<Promise>, Error>> {
180 match &self.underlying_source_type {
181 UnderlyingSourceType::Js(source, this_obj) => {
182 if let Some(algo) = &source.pull {
183 let result = unsafe {
184 algo.Call_(
185 &SafeHandle::from_raw(this_obj.handle()),
186 controller,
187 ExceptionHandling::Rethrow,
188 can_gc,
189 )
190 };
191 return Some(result);
192 }
193 None
194 },
195 UnderlyingSourceType::Tee(tee_underlying_source) => {
196 Some(Ok(tee_underlying_source.pull_algorithm(can_gc)))
198 },
199 UnderlyingSourceType::Transfer(port) => {
200 let cx = GlobalScope::get_cx();
204
205 rooted!(in(*cx) let mut value = UndefinedValue());
207 port.pack_and_post_message("pull", value.handle(), can_gc)
208 .expect("Sending pull should not fail.");
209
210 let promise = Promise::new(&self.global(), can_gc);
212 promise.resolve_native(&(), can_gc);
213 Some(Ok(promise))
214 },
215 UnderlyingSourceType::Transform(stream, _) => {
217 Some(stream.transform_stream_default_source_pull(&self.global(), can_gc))
219 },
220 _ => None,
221 }
222 }
223
224 #[allow(unsafe_code)]
232 pub(crate) fn call_start_algorithm(
233 &self,
234 controller: Controller,
235 can_gc: CanGc,
236 ) -> Option<Result<Rc<Promise>, Error>> {
237 match &self.underlying_source_type {
238 UnderlyingSourceType::Js(source, this_obj) => {
239 if let Some(start) = &source.start {
240 let cx = GlobalScope::get_cx();
241 rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
242 rooted!(in(*cx) let mut result: JSVal);
243 unsafe {
244 if let Err(error) = start.Call_(
245 &SafeHandle::from_raw(this_obj.handle()),
246 controller,
247 result.handle_mut(),
248 ExceptionHandling::Rethrow,
249 can_gc,
250 ) {
251 return Some(Err(error));
252 }
253 }
254 let is_promise = unsafe {
255 if result.is_object() {
256 result_object.set(result.to_object());
257 IsPromiseObject(result_object.handle().into_handle())
258 } else {
259 false
260 }
261 };
262 let promise = if is_promise {
263 Promise::new_with_js_promise(result_object.handle(), cx)
264 } else {
265 let promise = Promise::new(&self.global(), can_gc);
266 promise.resolve_native(&result.get(), can_gc);
267 promise
268 };
269 return Some(Ok(promise));
270 }
271 None
272 },
273 UnderlyingSourceType::Tee(_) => {
274 None
276 },
277 UnderlyingSourceType::Transfer(_) => {
278 None
281 },
282 UnderlyingSourceType::Transform(_, start_promise) => {
283 Some(Ok(start_promise.clone()))
285 },
286 _ => None,
287 }
288 }
289
290 pub(crate) fn auto_allocate_chunk_size(&self) -> Option<u64> {
292 match &self.underlying_source_type {
293 UnderlyingSourceType::Js(source, _) => source.autoAllocateChunkSize,
294 _ => None,
295 }
296 }
297
298 pub(crate) fn in_memory(&self) -> bool {
300 self.underlying_source_type.in_memory()
301 }
302}