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::realm::AutoRealm;
13use js::rust::HandleValue as SafeHandleValue;
14use script_bindings::reflector::{Reflector, reflect_dom_object};
15
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::{Microtask, MicrotaskRunnable};
25use crate::realms::enter_auto_realm;
26use crate::script_runtime::CanGc;
27
28#[derive(JSTraceable, MallocSizeOf)]
29#[cfg_attr(crown, expect(crown::unrooted_must_root))]
30pub(crate) struct DefaultTeeReadRequestMicrotask {
31 #[ignore_malloc_size_of = "mozjs"]
32 chunk: Box<Heap<JSVal>>,
33 tee_read_request: Dom<DefaultTeeReadRequest>,
34}
35
36impl MicrotaskRunnable for DefaultTeeReadRequestMicrotask {
37 fn handler(&self, cx: &mut JSContext) {
38 self.tee_read_request.chunk_steps(cx, &self.chunk);
39 }
40
41 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
42 enter_auto_realm(cx, &*self.tee_read_request)
43 }
44}
45
46#[dom_struct]
47pub(crate) struct DefaultTeeReadRequest {
49 reflector_: Reflector,
50 stream: Dom<ReadableStream>,
51 branch_1: Dom<ReadableStream>,
52 branch_2: Dom<ReadableStream>,
53 #[conditional_malloc_size_of]
54 reading: Rc<Cell<bool>>,
55 #[conditional_malloc_size_of]
56 read_again: Rc<Cell<bool>>,
57 #[conditional_malloc_size_of]
58 canceled_1: Rc<Cell<bool>>,
59 #[conditional_malloc_size_of]
60 canceled_2: Rc<Cell<bool>>,
61 #[conditional_malloc_size_of]
62 clone_for_branch_2: Rc<Cell<bool>>,
63 #[conditional_malloc_size_of]
64 cancel_promise: Rc<Promise>,
65 tee_underlying_source: Dom<DefaultTeeUnderlyingSource>,
66}
67impl DefaultTeeReadRequest {
68 #[expect(clippy::too_many_arguments)]
69 pub(crate) fn new(
70 stream: &ReadableStream,
71 branch_1: &ReadableStream,
72 branch_2: &ReadableStream,
73 reading: Rc<Cell<bool>>,
74 read_again: Rc<Cell<bool>>,
75 canceled_1: Rc<Cell<bool>>,
76 canceled_2: Rc<Cell<bool>>,
77 clone_for_branch_2: Rc<Cell<bool>>,
78 cancel_promise: Rc<Promise>,
79 tee_underlying_source: &DefaultTeeUnderlyingSource,
80 can_gc: CanGc,
81 ) -> DomRoot<Self> {
82 reflect_dom_object(
83 Box::new(DefaultTeeReadRequest {
84 reflector_: Reflector::new(),
85 stream: Dom::from_ref(stream),
86 branch_1: Dom::from_ref(branch_1),
87 branch_2: Dom::from_ref(branch_2),
88 reading,
89 read_again,
90 canceled_1,
91 canceled_2,
92 clone_for_branch_2,
93 cancel_promise,
94 tee_underlying_source: Dom::from_ref(tee_underlying_source),
95 }),
96 &*stream.global(),
97 can_gc,
98 )
99 }
100 pub(crate) fn stream_cancel(
103 &self,
104 cx: &mut JSContext,
105 global: &GlobalScope,
106 reason: SafeHandleValue,
107 ) {
108 self.stream.cancel(cx, global, reason);
109 }
110 pub(crate) fn enqueue_chunk_steps(&self, chunk: RootedTraceableBox<Heap<JSVal>>) {
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(Microtask::ReadableStreamTeeReadRequest(
121 tee_read_request_chunk,
122 ));
123 }
124 #[expect(clippy::borrowed_box)]
126 pub(crate) fn chunk_steps(&self, cx: &mut JSContext, chunk: &Box<Heap<JSVal>>) {
127 let global = &self.stream.global();
128 self.read_again.set(false);
130 let chunk1 = chunk;
132 let chunk2 = chunk;
133
134 rooted!(&in(cx) let chunk1_value = chunk1.get());
135 rooted!(&in(cx) let chunk2_value = chunk2.get());
136 if !self.canceled_2.get() && self.clone_for_branch_2.get() {
138 rooted!(&in(cx) let mut clone_result = UndefinedValue());
140 let data = structuredclone::write(cx, chunk2_value.handle(), None).unwrap();
141 if structuredclone::read(cx, global, data, clone_result.handle_mut()).is_err() {
143 self.readable_stream_default_controller_error(
145 cx,
146 &self.branch_1,
147 clone_result.handle(),
148 );
149
150 self.readable_stream_default_controller_error(
152 cx,
153 &self.branch_2,
154 clone_result.handle(),
155 );
156 self.stream_cancel(cx, global, clone_result.handle());
158 return;
160 } else {
161 chunk2.set(*clone_result);
163 }
164 }
165 if !self.canceled_1.get() {
167 self.readable_stream_default_controller_enqueue(
168 cx,
169 &self.branch_1,
170 chunk1_value.handle(),
171 );
172 }
173 if !self.canceled_2.get() {
175 self.readable_stream_default_controller_enqueue(
176 cx,
177 &self.branch_2,
178 chunk2_value.handle(),
179 );
180 }
181 self.reading.set(false);
183 if self.read_again.get() {
185 self.pull_algorithm(cx);
186 }
187 }
188 pub(crate) fn close_steps(&self, cx: &mut JSContext) {
190 self.reading.set(false);
192 if !self.canceled_1.get() {
194 self.readable_stream_default_controller_close(cx, &self.branch_1);
195 }
196 if !self.canceled_2.get() {
198 self.readable_stream_default_controller_close(cx, &self.branch_2);
199 }
200 if !self.canceled_1.get() || !self.canceled_2.get() {
202 self.cancel_promise.resolve_native(&(), CanGc::from_cx(cx));
203 }
204 }
205 pub(crate) fn error_steps(&self) {
207 self.reading.set(false);
209 }
210 fn readable_stream_default_controller_enqueue(
213 &self,
214 cx: &mut JSContext,
215 stream: &ReadableStream,
216 chunk: SafeHandleValue,
217 ) {
218 stream
219 .get_default_controller()
220 .enqueue(cx, chunk)
221 .expect("enqueue failed for stream controller in DefaultTeeReadRequest");
222 }
223
224 fn readable_stream_default_controller_close(
227 &self,
228 cx: &mut JSContext,
229 stream: &ReadableStream,
230 ) {
231 stream.get_default_controller().close(cx);
232 }
233
234 fn readable_stream_default_controller_error(
237 &self,
238 cx: &mut JSContext,
239 stream: &ReadableStream,
240 error: SafeHandleValue,
241 ) {
242 stream.get_default_controller().error(cx, error);
243 }
244
245 pub(crate) fn pull_algorithm(&self, cx: &mut JSContext) {
246 self.tee_underlying_source.pull_algorithm(cx);
247 }
248}