1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::jsapi::{Heap, JSAutoRealm};
10use js::jsval::{JSVal, UndefinedValue};
11use js::rust::HandleValue as SafeHandleValue;
12
13use super::bindings::reflector::reflect_dom_object;
14use super::bindings::root::DomRoot;
15use super::bindings::structuredclone;
16use crate::dom::bindings::reflector::{DomGlobal, Reflector};
17use crate::dom::bindings::root::Dom;
18use crate::dom::bindings::trace::RootedTraceableBox;
19use crate::dom::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::promise::Promise;
22use crate::dom::readablestream::ReadableStream;
23use crate::microtask::{Microtask, MicrotaskRunnable};
24use crate::realms::enter_realm;
25use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
26
27#[derive(JSTraceable, MallocSizeOf)]
28#[cfg_attr(crown, allow(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, can_gc: CanGc) {
37 let cx = GlobalScope::get_cx();
38 self.tee_read_request.chunk_steps(cx, &self.chunk, can_gc);
39 }
40
41 fn enter_realm(&self) -> JSAutoRealm {
42 enter_realm(&*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 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
70 pub(crate) fn new(
71 stream: &ReadableStream,
72 branch_1: &ReadableStream,
73 branch_2: &ReadableStream,
74 reading: Rc<Cell<bool>>,
75 read_again: Rc<Cell<bool>>,
76 canceled_1: Rc<Cell<bool>>,
77 canceled_2: Rc<Cell<bool>>,
78 clone_for_branch_2: Rc<Cell<bool>>,
79 cancel_promise: Rc<Promise>,
80 tee_underlying_source: &DefaultTeeUnderlyingSource,
81 can_gc: CanGc,
82 ) -> DomRoot<Self> {
83 reflect_dom_object(
84 Box::new(DefaultTeeReadRequest {
85 reflector_: Reflector::new(),
86 stream: Dom::from_ref(stream),
87 branch_1: Dom::from_ref(branch_1),
88 branch_2: Dom::from_ref(branch_2),
89 reading,
90 read_again,
91 canceled_1,
92 canceled_2,
93 clone_for_branch_2,
94 cancel_promise,
95 tee_underlying_source: Dom::from_ref(tee_underlying_source),
96 }),
97 &*stream.global(),
98 can_gc,
99 )
100 }
101 pub(crate) fn stream_cancel(
104 &self,
105 cx: SafeJSContext,
106 global: &GlobalScope,
107 reason: SafeHandleValue,
108 can_gc: CanGc,
109 ) {
110 self.stream.cancel(cx, global, reason, can_gc);
111 }
112 pub(crate) fn enqueue_chunk_steps(&self, chunk: RootedTraceableBox<Heap<JSVal>>) {
115 let tee_read_request_chunk = DefaultTeeReadRequestMicrotask {
117 chunk: Heap::boxed(*chunk.handle()),
118 tee_read_request: Dom::from_ref(self),
119 };
120 self.stream
121 .global()
122 .enqueue_microtask(Microtask::ReadableStreamTeeReadRequest(
123 tee_read_request_chunk,
124 ));
125 }
126 #[expect(clippy::borrowed_box)]
128 pub(crate) fn chunk_steps(&self, cx: SafeJSContext, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
129 let global = &self.stream.global();
130 self.read_again.set(false);
132 let chunk1 = chunk;
134 let chunk2 = chunk;
135
136 rooted!(in(*cx) let chunk1_value = chunk1.get());
137 rooted!(in(*cx) let chunk2_value = chunk2.get());
138 if !self.canceled_2.get() && self.clone_for_branch_2.get() {
140 rooted!(in(*cx) let mut clone_result = UndefinedValue());
142 let data = structuredclone::write(cx, chunk2_value.handle(), None).unwrap();
143 if structuredclone::read(global, data, clone_result.handle_mut(), can_gc).is_err() {
145 self.readable_stream_default_controller_error(
147 &self.branch_1,
148 clone_result.handle(),
149 can_gc,
150 );
151
152 self.readable_stream_default_controller_error(
154 &self.branch_2,
155 clone_result.handle(),
156 can_gc,
157 );
158 self.stream_cancel(cx, global, clone_result.handle(), can_gc);
160 return;
162 } else {
163 chunk2.set(*clone_result);
165 }
166 }
167 if !self.canceled_1.get() {
169 self.readable_stream_default_controller_enqueue(
170 &self.branch_1,
171 chunk1_value.handle(),
172 can_gc,
173 );
174 }
175 if !self.canceled_2.get() {
177 self.readable_stream_default_controller_enqueue(
178 &self.branch_2,
179 chunk2_value.handle(),
180 can_gc,
181 );
182 }
183 self.reading.set(false);
185 if self.read_again.get() {
187 self.pull_algorithm(can_gc);
188 }
189 }
190 pub(crate) fn close_steps(&self, can_gc: CanGc) {
192 self.reading.set(false);
194 if !self.canceled_1.get() {
196 self.readable_stream_default_controller_close(&self.branch_1, can_gc);
197 }
198 if !self.canceled_2.get() {
200 self.readable_stream_default_controller_close(&self.branch_2, can_gc);
201 }
202 if !self.canceled_1.get() || !self.canceled_2.get() {
204 self.cancel_promise.resolve_native(&(), can_gc);
205 }
206 }
207 pub(crate) fn error_steps(&self) {
209 self.reading.set(false);
211 }
212 fn readable_stream_default_controller_enqueue(
215 &self,
216 stream: &ReadableStream,
217 chunk: SafeHandleValue,
218 can_gc: CanGc,
219 ) {
220 stream
221 .get_default_controller()
222 .enqueue(GlobalScope::get_cx(), chunk, can_gc)
223 .expect("enqueue failed for stream controller in DefaultTeeReadRequest");
224 }
225
226 fn readable_stream_default_controller_close(&self, stream: &ReadableStream, can_gc: CanGc) {
229 stream.get_default_controller().close(can_gc);
230 }
231
232 fn readable_stream_default_controller_error(
235 &self,
236 stream: &ReadableStream,
237 error: SafeHandleValue,
238 can_gc: CanGc,
239 ) {
240 stream.get_default_controller().error(error, can_gc);
241 }
242
243 pub(crate) fn pull_algorithm(&self, can_gc: CanGc) {
244 self.tee_underlying_source.pull_algorithm(can_gc);
245 }
246}