1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::jsapi::Heap;
11use js::jsval::{JSVal, UndefinedValue};
12use js::rust::HandleValue as SafeHandleValue;
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
14
15use crate::dom::bindings::error::ErrorToJsval;
16use crate::dom::bindings::reflector::DomGlobal;
17use crate::dom::bindings::root::{Dom, DomRoot};
18use crate::dom::bindings::structuredclone;
19use crate::dom::bindings::trace::RootedTraceableBox;
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::promise::Promise;
22use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
23use crate::dom::stream::readablestream::ReadableStream;
24use crate::microtask::MicrotaskRunnable;
25use crate::realms::enter_auto_realm;
26
27#[derive(JSTraceable, MallocSizeOf)]
28#[cfg_attr(crown, expect(crown::unrooted_must_root))]
29pub(crate) struct DefaultTeeReadRequestMicrotask {
30 #[ignore_malloc_size_of = "mozjs"]
31 chunk: Box<Heap<JSVal>>,
32 tee_read_request: Dom<DefaultTeeReadRequest>,
33}
34
35impl MicrotaskRunnable for DefaultTeeReadRequestMicrotask {
36 fn handler(&self, cx: &mut JSContext) {
37 let mut realm = enter_auto_realm(cx, &*self.tee_read_request);
38 self.tee_read_request.chunk_steps(&mut realm, &self.chunk);
39 }
40}
41
42#[dom_struct]
43pub(crate) struct DefaultTeeReadRequest {
45 reflector_: Reflector,
46 stream: Dom<ReadableStream>,
47 branch_1: Dom<ReadableStream>,
48 branch_2: Dom<ReadableStream>,
49 #[conditional_malloc_size_of]
50 reading: Rc<Cell<bool>>,
51 #[conditional_malloc_size_of]
52 read_again: Rc<Cell<bool>>,
53 #[conditional_malloc_size_of]
54 canceled_1: Rc<Cell<bool>>,
55 #[conditional_malloc_size_of]
56 canceled_2: Rc<Cell<bool>>,
57 #[conditional_malloc_size_of]
58 clone_for_branch_2: Rc<Cell<bool>>,
59 #[conditional_malloc_size_of]
60 cancel_promise: Rc<Promise>,
61 tee_underlying_source: Dom<DefaultTeeUnderlyingSource>,
62}
63impl DefaultTeeReadRequest {
64 #[expect(clippy::too_many_arguments)]
65 pub(crate) fn new(
66 cx: &mut JSContext,
67 stream: &ReadableStream,
68 branch_1: &ReadableStream,
69 branch_2: &ReadableStream,
70 reading: Rc<Cell<bool>>,
71 read_again: Rc<Cell<bool>>,
72 canceled_1: Rc<Cell<bool>>,
73 canceled_2: Rc<Cell<bool>>,
74 clone_for_branch_2: Rc<Cell<bool>>,
75 cancel_promise: Rc<Promise>,
76 tee_underlying_source: &DefaultTeeUnderlyingSource,
77 ) -> DomRoot<Self> {
78 reflect_dom_object_with_cx(
79 Box::new(DefaultTeeReadRequest {
80 reflector_: Reflector::new(),
81 stream: Dom::from_ref(stream),
82 branch_1: Dom::from_ref(branch_1),
83 branch_2: Dom::from_ref(branch_2),
84 reading,
85 read_again,
86 canceled_1,
87 canceled_2,
88 clone_for_branch_2,
89 cancel_promise,
90 tee_underlying_source: Dom::from_ref(tee_underlying_source),
91 }),
92 &*stream.global(),
93 cx,
94 )
95 }
96 pub(crate) fn stream_cancel(
99 &self,
100 cx: &mut JSContext,
101 global: &GlobalScope,
102 reason: SafeHandleValue,
103 ) {
104 self.stream.cancel(cx, global, reason);
105 }
106 pub(crate) fn enqueue_chunk_steps(
109 &self,
110 cx: &mut JSContext,
111 chunk: RootedTraceableBox<Heap<JSVal>>,
112 ) {
113 let tee_read_request_chunk = DefaultTeeReadRequestMicrotask {
115 chunk: Heap::boxed(*chunk.handle()),
116 tee_read_request: Dom::from_ref(self),
117 };
118 self.stream
119 .global()
120 .enqueue_microtask(cx, Box::new(tee_read_request_chunk));
121 }
122 #[expect(clippy::borrowed_box)]
124 pub(crate) fn chunk_steps(&self, cx: &mut JSContext, chunk: &Box<Heap<JSVal>>) {
125 let global = &self.stream.global();
126 self.read_again.set(false);
128 rooted!(&in(cx) let chunk1_value = chunk.get());
130 rooted!(&in(cx) let mut chunk2_value = chunk.get());
131 if !self.canceled_2.get() && self.clone_for_branch_2.get() {
133 let data = match structuredclone::write(cx, chunk2_value.handle(), None) {
135 Ok(data) => data,
136 Err(error) => {
137 rooted!(&in(cx) let mut error_value = UndefinedValue());
139 error.to_jsval(cx, global, error_value.handle_mut());
140 self.readable_stream_default_controller_error(
142 cx,
143 &self.branch_1,
144 error_value.handle(),
145 );
146
147 self.readable_stream_default_controller_error(
149 cx,
150 &self.branch_2,
151 error_value.handle(),
152 );
153 self.stream_cancel(cx, global, error_value.handle());
155 return;
157 },
158 };
159 if let Err(error) = structuredclone::read(cx, global, data, chunk2_value.handle_mut()) {
161 rooted!(&in(cx) let mut error_value = UndefinedValue());
162 error.to_jsval(cx, global, error_value.handle_mut());
163 self.readable_stream_default_controller_error(
165 cx,
166 &self.branch_1,
167 error_value.handle(),
168 );
169
170 self.readable_stream_default_controller_error(
172 cx,
173 &self.branch_2,
174 error_value.handle(),
175 );
176 self.stream_cancel(cx, global, error_value.handle());
178 return;
180 }
181 }
182 if !self.canceled_1.get() {
184 self.readable_stream_default_controller_enqueue(
185 cx,
186 &self.branch_1,
187 chunk1_value.handle(),
188 );
189 }
190 if !self.canceled_2.get() {
192 self.readable_stream_default_controller_enqueue(
193 cx,
194 &self.branch_2,
195 chunk2_value.handle(),
196 );
197 }
198 self.reading.set(false);
200 if self.read_again.get() {
202 self.pull_algorithm(cx);
203 }
204 }
205 pub(crate) fn close_steps(&self, cx: &mut JSContext) {
207 self.reading.set(false);
209 if !self.canceled_1.get() {
211 self.readable_stream_default_controller_close(cx, &self.branch_1);
212 }
213 if !self.canceled_2.get() {
215 self.readable_stream_default_controller_close(cx, &self.branch_2);
216 }
217 if !self.canceled_1.get() || !self.canceled_2.get() {
219 self.cancel_promise.resolve_native(cx, &());
220 }
221 }
222 pub(crate) fn error_steps(&self) {
224 self.reading.set(false);
226 }
227 fn readable_stream_default_controller_enqueue(
230 &self,
231 cx: &mut JSContext,
232 stream: &ReadableStream,
233 chunk: SafeHandleValue,
234 ) {
235 stream
236 .get_default_controller()
237 .enqueue(cx, chunk)
238 .expect("enqueue failed for stream controller in DefaultTeeReadRequest");
239 }
240
241 fn readable_stream_default_controller_close(
244 &self,
245 cx: &mut JSContext,
246 stream: &ReadableStream,
247 ) {
248 stream.get_default_controller().close(cx);
249 }
250
251 fn readable_stream_default_controller_error(
254 &self,
255 cx: &mut JSContext,
256 stream: &ReadableStream,
257 error: SafeHandleValue,
258 ) {
259 stream.get_default_controller().error(cx, error);
260 }
261
262 pub(crate) fn pull_algorithm(&self, cx: &mut JSContext) {
263 self.tee_underlying_source.pull_algorithm(cx);
264 }
265}