script/dom/
defaultteeunderlyingsource.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use 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 super::bindings::root::{DomRoot, MutNullableDom};
14use super::types::{ReadableStream, ReadableStreamDefaultReader};
15use crate::dom::bindings::error::Error;
16use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
17use crate::dom::bindings::root::Dom;
18use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
19use crate::dom::globalscope::GlobalScope;
20use crate::dom::promise::Promise;
21use crate::dom::readablestreamdefaultreader::ReadRequest;
22use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
23
24#[derive(JSTraceable, MallocSizeOf)]
25pub(crate) enum TeeCancelAlgorithm {
26    Cancel1Algorithm,
27    Cancel2Algorithm,
28}
29
30#[dom_struct]
31/// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
32pub(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    #[ignore_malloc_size_of = "Rc"]
39    reading: Rc<Cell<bool>>,
40    #[ignore_malloc_size_of = "Rc"]
41    read_again: Rc<Cell<bool>>,
42    #[ignore_malloc_size_of = "Rc"]
43    canceled_1: Rc<Cell<bool>>,
44    #[ignore_malloc_size_of = "Rc"]
45    canceled_2: Rc<Cell<bool>>,
46    #[ignore_malloc_size_of = "Rc"]
47    clone_for_branch_2: Rc<Cell<bool>>,
48    #[ignore_malloc_size_of = "Rc"]
49    #[allow(clippy::redundant_allocation)]
50    reason_1: Rc<Box<Heap<Value>>>,
51    #[ignore_malloc_size_of = "Rc"]
52    #[allow(clippy::redundant_allocation)]
53    reason_2: Rc<Box<Heap<Value>>>,
54    #[ignore_malloc_size_of = "Rc"]
55    cancel_promise: Rc<Promise>,
56    tee_cancel_algorithm: TeeCancelAlgorithm,
57}
58
59impl DefaultTeeUnderlyingSource {
60    #[allow(clippy::too_many_arguments)]
61    #[allow(clippy::redundant_allocation)]
62    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
63    pub(crate) fn new(
64        reader: &ReadableStreamDefaultReader,
65        stream: &ReadableStream,
66        reading: Rc<Cell<bool>>,
67        read_again: Rc<Cell<bool>>,
68        canceled_1: Rc<Cell<bool>>,
69        canceled_2: Rc<Cell<bool>>,
70        clone_for_branch_2: Rc<Cell<bool>>,
71        reason_1: Rc<Box<Heap<Value>>>,
72        reason_2: Rc<Box<Heap<Value>>>,
73        cancel_promise: Rc<Promise>,
74        tee_cancel_algorithm: TeeCancelAlgorithm,
75        can_gc: CanGc,
76    ) -> DomRoot<DefaultTeeUnderlyingSource> {
77        reflect_dom_object(
78            Box::new(DefaultTeeUnderlyingSource {
79                reflector_: Reflector::new(),
80                reader: Dom::from_ref(reader),
81                stream: Dom::from_ref(stream),
82                branch_1: MutNullableDom::new(None),
83                branch_2: MutNullableDom::new(None),
84                reading,
85                read_again,
86                canceled_1,
87                canceled_2,
88                clone_for_branch_2,
89                reason_1,
90                reason_2,
91                cancel_promise,
92                tee_cancel_algorithm,
93            }),
94            &*stream.global(),
95            can_gc,
96        )
97    }
98
99    pub(crate) fn set_branch_1(&self, stream: &ReadableStream) {
100        self.branch_1.set(Some(stream));
101    }
102
103    pub(crate) fn set_branch_2(&self, stream: &ReadableStream) {
104        self.branch_2.set(Some(stream));
105    }
106
107    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
108    /// Let pullAlgorithm be the following steps:
109    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
110    pub(crate) fn pull_algorithm(&self, can_gc: CanGc) -> Rc<Promise> {
111        let cx = GlobalScope::get_cx();
112        // If reading is true,
113        if self.reading.get() {
114            // Set readAgain to true.
115            self.read_again.set(true);
116            // Return a promise resolved with undefined.
117            rooted!(in(*cx) let mut rval = UndefinedValue());
118            return Promise::new_resolved(&self.stream.global(), cx, rval.handle(), can_gc);
119        }
120
121        // Set reading to true.
122        self.reading.set(true);
123
124        // Let readRequest be a read request with the following items:
125        let tee_read_request = DefaultTeeReadRequest::new(
126            &self.stream,
127            &self.branch_1.get().expect("Branch 1 should be set."),
128            &self.branch_2.get().expect("Branch 2 should be set."),
129            self.reading.clone(),
130            self.read_again.clone(),
131            self.canceled_1.clone(),
132            self.canceled_2.clone(),
133            self.clone_for_branch_2.clone(),
134            self.cancel_promise.clone(),
135            self,
136            can_gc,
137        );
138
139        // Rooting: the tee read request is rooted above.
140        let read_request = ReadRequest::DefaultTee {
141            tee_read_request: Dom::from_ref(&tee_read_request),
142        };
143
144        // Perform ! ReadableStreamDefaultReaderRead(reader, readRequest).
145        self.reader.read(cx, &read_request, can_gc);
146
147        // Return a promise resolved with undefined.
148        rooted!(in(*cx) let mut rval = UndefinedValue());
149        Promise::new_resolved(&self.stream.global(), cx, rval.handle(), can_gc)
150    }
151
152    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaulttee>
153    /// Let cancel1Algorithm be the following steps, taking a reason argument
154    /// and
155    /// Let cancel2Algorithm be the following steps, taking a reason argument
156    #[allow(unsafe_code)]
157    pub(crate) fn cancel_algorithm(
158        &self,
159        cx: SafeJSContext,
160        global: &GlobalScope,
161        reason: SafeHandleValue,
162        can_gc: CanGc,
163    ) -> Option<Result<Rc<Promise>, Error>> {
164        match self.tee_cancel_algorithm {
165            TeeCancelAlgorithm::Cancel1Algorithm => {
166                // Set canceled_1 to true.
167                self.canceled_1.set(true);
168
169                // Set reason_1 to reason.
170                self.reason_1.set(reason.get());
171
172                // If canceled_2 is true,
173                if self.canceled_2.get() {
174                    self.resolve_cancel_promise(cx, global, can_gc);
175                }
176                // Return cancelPromise.
177                Some(Ok(self.cancel_promise.clone()))
178            },
179            TeeCancelAlgorithm::Cancel2Algorithm => {
180                // Set canceled_2 to true.
181                self.canceled_2.set(true);
182
183                // Set reason_2 to reason.
184                self.reason_2.set(reason.get());
185
186                // If canceled_1 is true,
187                if self.canceled_1.get() {
188                    self.resolve_cancel_promise(cx, global, can_gc);
189                }
190                // Return cancelPromise.
191                Some(Ok(self.cancel_promise.clone()))
192            },
193        }
194    }
195
196    #[allow(unsafe_code)]
197    fn resolve_cancel_promise(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) {
198        // Let compositeReason be ! CreateArrayFromList(« reason_1, reason_2 »).
199        rooted_vec!(let mut reasons_values);
200        reasons_values.push(self.reason_1.get());
201        reasons_values.push(self.reason_2.get());
202
203        let reasons_values_array = HandleValueArray::from(&reasons_values);
204        rooted!(in(*cx) let reasons = unsafe { NewArrayObject(*cx, &reasons_values_array) });
205        rooted!(in(*cx) let reasons_value = ObjectValue(reasons.get()));
206
207        // Let cancelResult be ! ReadableStreamCancel(stream, compositeReason).
208        let cancel_result = self
209            .stream
210            .cancel(cx, global, reasons_value.handle(), can_gc);
211
212        // Resolve cancelPromise with cancelResult.
213        self.cancel_promise.resolve_native(&cancel_result, can_gc);
214    }
215}