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