Skip to main content

script/dom/stream/
readablestreamgenericreader.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 js::context::JSContext;
6use js::jsval::UndefinedValue;
7use js::rust::HandleValue as SafeHandleValue;
8
9use super::readablestream::ReaderType;
10use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
11use crate::dom::bindings::reflector::DomGlobal;
12use crate::dom::bindings::root::{DomRoot, MutNullableDom};
13use crate::dom::globalscope::GlobalScope;
14use crate::dom::promise::{Promise, RootedPromise};
15use crate::dom::stream::readablestreambyobreader::ReadableStreamBYOBReader;
16use crate::dom::stream::readablestreamdefaultreader::ReadableStreamDefaultReader;
17use crate::dom::types::ReadableStream;
18
19/// <https://streams.spec.whatwg.org/#readablestreamgenericreader>
20pub(crate) trait ReadableStreamGenericReader {
21    /// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize>
22    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
23    fn generic_initialize(
24        &self,
25        cx: &mut JSContext,
26        global: &GlobalScope,
27        stream: &ReadableStream,
28    ) {
29        // Set reader.[[stream]] to stream.
30        self.set_stream(Some(stream));
31
32        // Set stream.[[reader]] to reader.
33        let reader_type = if let Some(default_reader) = self.as_default_reader() {
34            ReaderType::Default(MutNullableDom::new(Some(default_reader)))
35        } else if let Some(byob_reader) = self.as_byob_reader() {
36            ReaderType::BYOB(MutNullableDom::new(Some(byob_reader)))
37        } else {
38            unreachable!("Reader must be either Default or BYOB.");
39        };
40        stream.set_reader(Some(reader_type));
41
42        if stream.is_readable() {
43            // If stream.[[state]] is "readable
44            // Set reader.[[closedPromise]] to a new promise.
45            self.set_closed_promise(&Promise::new_rooted(cx, global));
46        } else if stream.is_closed() {
47            // Otherwise, if stream.[[state]] is "closed",
48            // Set reader.[[closedPromise]] to a promise resolved with undefined.
49            self.set_closed_promise(&Promise::new_resolved_rooted(cx, global, ()));
50        } else {
51            // Assert: stream.[[state]] is "errored"
52            assert!(stream.is_errored());
53
54            // Set reader.[[closedPromise]] to a promise rejected with stream.[[storedError]].
55            rooted!(&in(cx) let mut error = UndefinedValue());
56            stream.get_stored_error(error.handle_mut());
57            self.set_closed_promise(&Promise::new_rejected_rooted(cx, global, error.handle()));
58
59            // Set reader.[[closedPromise]].[[PromiseIsHandled]] to true
60            self.get_closed_promise(cx).set_promise_is_handled(cx);
61        }
62    }
63
64    /// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel>
65    fn reader_generic_cancel(
66        &self,
67        cx: &mut JSContext,
68        global: &GlobalScope,
69        reason: SafeHandleValue,
70    ) -> RootedPromise {
71        // Let stream be reader.[[stream]].
72        let stream = self.get_stream();
73
74        // Assert: stream is not undefined.
75        let stream =
76            stream.expect("Reader should have a stream when generic cancel is called into.");
77
78        // Return ! ReadableStreamCancel(stream, reason).
79        stream.cancel(cx, global, reason)
80    }
81
82    /// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-release>
83    fn generic_release(&self, cx: &mut JSContext) -> Fallible<()> {
84        // Let stream be reader.[[stream]].
85
86        // Assert: stream is not undefined.
87        assert!(self.get_stream().is_some());
88
89        if let Some(stream) = self.get_stream() {
90            // Assert: stream.[[reader]] is reader.
91            if self.as_default_reader().is_some() {
92                assert!(stream.has_default_reader());
93            } else {
94                assert!(stream.has_byob_reader());
95            }
96
97            if stream.is_readable() {
98                // If stream.[[state]] is "readable", reject reader.[[closedPromise]] with a TypeError exception.
99                self.get_closed_promise(cx)
100                    .reject_error(cx, Error::Type(c"stream state is not readable".to_owned()));
101            } else {
102                // Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception.
103                rooted!(&in(cx) let mut error = UndefinedValue());
104                Error::Type(c"Cannot release lock due to stream state.".to_owned()).to_jsval(
105                    cx,
106                    &stream.global(),
107                    error.handle_mut(),
108                );
109
110                self.set_closed_promise(&Promise::new_rejected_rooted(
111                    cx,
112                    &stream.global(),
113                    error.handle(),
114                ));
115            }
116            // Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
117            self.get_closed_promise(cx).set_promise_is_handled(cx);
118
119            // Perform ! stream.[[controller]].[[ReleaseSteps]]().
120            stream
121                .perform_release_steps()
122                .expect("Stream should have a controller");
123
124            // Set stream.[[reader]] to undefined.
125            stream.set_reader(None);
126            // Set reader.[[stream]] to undefined.
127            self.set_stream(None);
128        }
129        Ok(())
130    }
131
132    /// <https://streams.spec.whatwg.org/#generic-reader-closed>
133    fn closed(&self, cx: &JSContext) -> RootedPromise {
134        self.get_closed_promise(cx)
135    }
136
137    // <https://streams.spec.whatwg.org/#generic-reader-cancel>
138    fn generic_cancel(
139        &self,
140        cx: &mut JSContext,
141        global: &GlobalScope,
142        reason: SafeHandleValue,
143    ) -> RootedPromise {
144        if self.get_stream().is_none() {
145            // If this.[[stream]] is undefined,
146            // return a promise rejected with a TypeError exception.
147            let promise = Promise::new_rooted(cx, global);
148            promise.reject_error(cx, Error::Type(c"stream is undefined".to_owned()));
149            promise
150        } else {
151            // Return ! ReadableStreamReaderGenericCancel(this, reason).
152            self.reader_generic_cancel(cx, global, reason)
153        }
154    }
155
156    fn set_stream(&self, stream: Option<&ReadableStream>);
157
158    fn get_stream(&self) -> Option<DomRoot<ReadableStream>>;
159
160    fn set_closed_promise(&self, promise: &RootedPromise);
161
162    fn get_closed_promise(&self, cx: &JSContext) -> RootedPromise;
163
164    fn as_default_reader(&self) -> Option<&ReadableStreamDefaultReader> {
165        None
166    }
167
168    fn as_byob_reader(&self) -> Option<&ReadableStreamBYOBReader> {
169        None
170    }
171}