1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::jsapi::{HandleValueArray, Heap, NewArrayObject, Value};
10use js::jsval::{ObjectValue, UndefinedValue};
11use js::rust::HandleValue as SafeHandleValue;
12
13use crate::dom::bindings::error::Error;
14use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
15use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
16use crate::dom::globalscope::GlobalScope;
17use crate::dom::promise::Promise;
18use crate::dom::stream::defaultteereadrequest::DefaultTeeReadRequest;
19use crate::dom::stream::readablestreamdefaultreader::ReadRequest;
20use crate::dom::types::{ReadableStream, ReadableStreamDefaultReader};
21use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
22
23#[derive(JSTraceable, MallocSizeOf)]
24pub(crate) enum DefaultTeeCancelAlgorithm {
25 Cancel1Algorithm,
26 Cancel2Algorithm,
27}
28
29#[dom_struct]
30pub(crate) struct DefaultTeeUnderlyingSource {
32 reflector_: Reflector,
33 reader: Dom<ReadableStreamDefaultReader>,
34 stream: Dom<ReadableStream>,
35 branch_1: MutNullableDom<ReadableStream>,
36 branch_2: MutNullableDom<ReadableStream>,
37 #[conditional_malloc_size_of]
38 reading: Rc<Cell<bool>>,
39 #[conditional_malloc_size_of]
40 read_again: Rc<Cell<bool>>,
41 #[conditional_malloc_size_of]
42 canceled_1: Rc<Cell<bool>>,
43 #[conditional_malloc_size_of]
44 canceled_2: Rc<Cell<bool>>,
45 #[conditional_malloc_size_of]
46 clone_for_branch_2: Rc<Cell<bool>>,
47 #[ignore_malloc_size_of = "mozjs"]
48 #[expect(clippy::redundant_allocation)]
49 reason_1: Rc<Box<Heap<Value>>>,
50 #[ignore_malloc_size_of = "mozjs"]
51 #[expect(clippy::redundant_allocation)]
52 reason_2: Rc<Box<Heap<Value>>>,
53 #[conditional_malloc_size_of]
54 cancel_promise: Rc<Promise>,
55 tee_cancel_algorithm: DefaultTeeCancelAlgorithm,
56}
57
58impl DefaultTeeUnderlyingSource {
59 #[expect(clippy::too_many_arguments)]
60 #[expect(clippy::redundant_allocation)]
61 pub(crate) fn new(
62 reader: &ReadableStreamDefaultReader,
63 stream: &ReadableStream,
64 reading: Rc<Cell<bool>>,
65 read_again: Rc<Cell<bool>>,
66 canceled_1: Rc<Cell<bool>>,
67 canceled_2: Rc<Cell<bool>>,
68 clone_for_branch_2: Rc<Cell<bool>>,
69 reason_1: Rc<Box<Heap<Value>>>,
70 reason_2: Rc<Box<Heap<Value>>>,
71 cancel_promise: Rc<Promise>,
72 tee_cancel_algorithm: DefaultTeeCancelAlgorithm,
73 can_gc: CanGc,
74 ) -> DomRoot<DefaultTeeUnderlyingSource> {
75 reflect_dom_object(
76 Box::new(DefaultTeeUnderlyingSource {
77 reflector_: Reflector::new(),
78 reader: Dom::from_ref(reader),
79 stream: Dom::from_ref(stream),
80 branch_1: MutNullableDom::new(None),
81 branch_2: MutNullableDom::new(None),
82 reading,
83 read_again,
84 canceled_1,
85 canceled_2,
86 clone_for_branch_2,
87 reason_1,
88 reason_2,
89 cancel_promise,
90 tee_cancel_algorithm,
91 }),
92 &*stream.global(),
93 can_gc,
94 )
95 }
96
97 pub(crate) fn set_branch_1(&self, stream: &ReadableStream) {
98 self.branch_1.set(Some(stream));
99 }
100
101 pub(crate) fn set_branch_2(&self, stream: &ReadableStream) {
102 self.branch_2.set(Some(stream));
103 }
104
105 pub(crate) fn pull_algorithm(&self, can_gc: CanGc) -> Rc<Promise> {
108 let cx = GlobalScope::get_cx();
109 if self.reading.get() {
111 self.read_again.set(true);
113 rooted!(in(*cx) let mut rval = UndefinedValue());
115 return Promise::new_resolved(&self.stream.global(), cx, rval.handle(), can_gc);
116 }
117
118 self.reading.set(true);
120
121 let tee_read_request = DefaultTeeReadRequest::new(
123 &self.stream,
124 &self.branch_1.get().expect("Branch 1 should be set."),
125 &self.branch_2.get().expect("Branch 2 should be set."),
126 self.reading.clone(),
127 self.read_again.clone(),
128 self.canceled_1.clone(),
129 self.canceled_2.clone(),
130 self.clone_for_branch_2.clone(),
131 self.cancel_promise.clone(),
132 self,
133 can_gc,
134 );
135
136 let read_request = ReadRequest::DefaultTee {
138 tee_read_request: Dom::from_ref(&tee_read_request),
139 };
140
141 self.reader.read(cx, &read_request, can_gc);
143
144 rooted!(in(*cx) let mut rval = UndefinedValue());
146 Promise::new_resolved(&self.stream.global(), cx, rval.handle(), can_gc)
147 }
148
149 pub(crate) fn cancel_algorithm(
154 &self,
155 cx: SafeJSContext,
156 global: &GlobalScope,
157 reason: SafeHandleValue,
158 can_gc: CanGc,
159 ) -> Option<Result<Rc<Promise>, Error>> {
160 match self.tee_cancel_algorithm {
161 DefaultTeeCancelAlgorithm::Cancel1Algorithm => {
162 self.canceled_1.set(true);
164
165 self.reason_1.set(reason.get());
167
168 if self.canceled_2.get() {
170 self.resolve_cancel_promise(cx, global, can_gc);
171 }
172 Some(Ok(self.cancel_promise.clone()))
174 },
175 DefaultTeeCancelAlgorithm::Cancel2Algorithm => {
176 self.canceled_2.set(true);
178
179 self.reason_2.set(reason.get());
181
182 if self.canceled_1.get() {
184 self.resolve_cancel_promise(cx, global, can_gc);
185 }
186 Some(Ok(self.cancel_promise.clone()))
188 },
189 }
190 }
191
192 #[expect(unsafe_code)]
193 fn resolve_cancel_promise(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) {
194 rooted_vec!(let mut reasons_values);
196 reasons_values.push(self.reason_1.get());
197 reasons_values.push(self.reason_2.get());
198
199 let reasons_values_array = HandleValueArray::from(&reasons_values);
200 rooted!(in(*cx) let reasons = unsafe { NewArrayObject(*cx, &reasons_values_array) });
201 rooted!(in(*cx) let reasons_value = ObjectValue(reasons.get()));
202
203 let cancel_result = self
205 .stream
206 .cancel(cx, global, reasons_value.handle(), can_gc);
207
208 self.cancel_promise.resolve_native(&cancel_result, can_gc);
210 }
211}