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 crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::bindings::structuredclone;
16use crate::dom::bindings::trace::RootedTraceableBox;
17use crate::dom::globalscope::GlobalScope;
18use crate::dom::promise::Promise;
19use crate::dom::stream::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
20use crate::dom::stream::readablestream::ReadableStream;
21use crate::microtask::{Microtask, MicrotaskRunnable};
22use crate::realms::enter_realm;
23use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
24
25#[derive(JSTraceable, MallocSizeOf)]
26#[cfg_attr(crown, expect(crown::unrooted_must_root))]
27pub(crate) struct DefaultTeeReadRequestMicrotask {
28 #[ignore_malloc_size_of = "mozjs"]
29 chunk: Box<Heap<JSVal>>,
30 tee_read_request: Dom<DefaultTeeReadRequest>,
31}
32
33impl MicrotaskRunnable for DefaultTeeReadRequestMicrotask {
34 fn handler(&self, can_gc: CanGc) {
35 let cx = GlobalScope::get_cx();
36 self.tee_read_request.chunk_steps(cx, &self.chunk, can_gc);
37 }
38
39 fn enter_realm(&self) -> JSAutoRealm {
40 enter_realm(&*self.tee_read_request)
41 }
42}
43
44#[dom_struct]
45pub(crate) struct DefaultTeeReadRequest {
47 reflector_: Reflector,
48 stream: Dom<ReadableStream>,
49 branch_1: Dom<ReadableStream>,
50 branch_2: Dom<ReadableStream>,
51 #[conditional_malloc_size_of]
52 reading: Rc<Cell<bool>>,
53 #[conditional_malloc_size_of]
54 read_again: Rc<Cell<bool>>,
55 #[conditional_malloc_size_of]
56 canceled_1: Rc<Cell<bool>>,
57 #[conditional_malloc_size_of]
58 canceled_2: Rc<Cell<bool>>,
59 #[conditional_malloc_size_of]
60 clone_for_branch_2: Rc<Cell<bool>>,
61 #[conditional_malloc_size_of]
62 cancel_promise: Rc<Promise>,
63 tee_underlying_source: Dom<DefaultTeeUnderlyingSource>,
64}
65impl DefaultTeeReadRequest {
66 #[expect(clippy::too_many_arguments)]
67 pub(crate) fn new(
68 stream: &ReadableStream,
69 branch_1: &ReadableStream,
70 branch_2: &ReadableStream,
71 reading: Rc<Cell<bool>>,
72 read_again: Rc<Cell<bool>>,
73 canceled_1: Rc<Cell<bool>>,
74 canceled_2: Rc<Cell<bool>>,
75 clone_for_branch_2: Rc<Cell<bool>>,
76 cancel_promise: Rc<Promise>,
77 tee_underlying_source: &DefaultTeeUnderlyingSource,
78 can_gc: CanGc,
79 ) -> DomRoot<Self> {
80 reflect_dom_object(
81 Box::new(DefaultTeeReadRequest {
82 reflector_: Reflector::new(),
83 stream: Dom::from_ref(stream),
84 branch_1: Dom::from_ref(branch_1),
85 branch_2: Dom::from_ref(branch_2),
86 reading,
87 read_again,
88 canceled_1,
89 canceled_2,
90 clone_for_branch_2,
91 cancel_promise,
92 tee_underlying_source: Dom::from_ref(tee_underlying_source),
93 }),
94 &*stream.global(),
95 can_gc,
96 )
97 }
98 pub(crate) fn stream_cancel(
101 &self,
102 cx: SafeJSContext,
103 global: &GlobalScope,
104 reason: SafeHandleValue,
105 can_gc: CanGc,
106 ) {
107 self.stream.cancel(cx, global, reason, can_gc);
108 }
109 pub(crate) fn enqueue_chunk_steps(&self, chunk: RootedTraceableBox<Heap<JSVal>>) {
112 let tee_read_request_chunk = DefaultTeeReadRequestMicrotask {
114 chunk: Heap::boxed(*chunk.handle()),
115 tee_read_request: Dom::from_ref(self),
116 };
117 self.stream
118 .global()
119 .enqueue_microtask(Microtask::ReadableStreamTeeReadRequest(
120 tee_read_request_chunk,
121 ));
122 }
123 #[expect(clippy::borrowed_box)]
125 pub(crate) fn chunk_steps(&self, cx: SafeJSContext, chunk: &Box<Heap<JSVal>>, can_gc: CanGc) {
126 let global = &self.stream.global();
127 self.read_again.set(false);
129 let chunk1 = chunk;
131 let chunk2 = chunk;
132
133 rooted!(in(*cx) let chunk1_value = chunk1.get());
134 rooted!(in(*cx) let chunk2_value = chunk2.get());
135 if !self.canceled_2.get() && self.clone_for_branch_2.get() {
137 rooted!(in(*cx) let mut clone_result = UndefinedValue());
139 let data = structuredclone::write(cx, chunk2_value.handle(), None).unwrap();
140 if structuredclone::read(global, data, clone_result.handle_mut(), can_gc).is_err() {
142 self.readable_stream_default_controller_error(
144 &self.branch_1,
145 clone_result.handle(),
146 can_gc,
147 );
148
149 self.readable_stream_default_controller_error(
151 &self.branch_2,
152 clone_result.handle(),
153 can_gc,
154 );
155 self.stream_cancel(cx, global, clone_result.handle(), can_gc);
157 return;
159 } else {
160 chunk2.set(*clone_result);
162 }
163 }
164 if !self.canceled_1.get() {
166 self.readable_stream_default_controller_enqueue(
167 &self.branch_1,
168 chunk1_value.handle(),
169 can_gc,
170 );
171 }
172 if !self.canceled_2.get() {
174 self.readable_stream_default_controller_enqueue(
175 &self.branch_2,
176 chunk2_value.handle(),
177 can_gc,
178 );
179 }
180 self.reading.set(false);
182 if self.read_again.get() {
184 self.pull_algorithm(can_gc);
185 }
186 }
187 pub(crate) fn close_steps(&self, can_gc: CanGc) {
189 self.reading.set(false);
191 if !self.canceled_1.get() {
193 self.readable_stream_default_controller_close(&self.branch_1, can_gc);
194 }
195 if !self.canceled_2.get() {
197 self.readable_stream_default_controller_close(&self.branch_2, can_gc);
198 }
199 if !self.canceled_1.get() || !self.canceled_2.get() {
201 self.cancel_promise.resolve_native(&(), can_gc);
202 }
203 }
204 pub(crate) fn error_steps(&self) {
206 self.reading.set(false);
208 }
209 fn readable_stream_default_controller_enqueue(
212 &self,
213 stream: &ReadableStream,
214 chunk: SafeHandleValue,
215 can_gc: CanGc,
216 ) {
217 stream
218 .get_default_controller()
219 .enqueue(GlobalScope::get_cx(), chunk, can_gc)
220 .expect("enqueue failed for stream controller in DefaultTeeReadRequest");
221 }
222
223 fn readable_stream_default_controller_close(&self, stream: &ReadableStream, can_gc: CanGc) {
226 stream.get_default_controller().close(can_gc);
227 }
228
229 fn readable_stream_default_controller_error(
232 &self,
233 stream: &ReadableStream,
234 error: SafeHandleValue,
235 can_gc: CanGc,
236 ) {
237 stream.get_default_controller().error(error, can_gc);
238 }
239
240 pub(crate) fn pull_algorithm(&self, can_gc: CanGc) {
241 self.tee_underlying_source.pull_algorithm(can_gc);
242 }
243}