| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| use std::rc::Rc; |
| |
| use js::jsval::UndefinedValue; |
| use js::rust::HandleValue as SafeHandleValue; |
| |
| use super::readablestream::ReaderType; |
| use super::types::ReadableStream; |
| use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible}; |
| use crate::dom::bindings::reflector::DomGlobal; |
| use crate::dom::bindings::root::{DomRoot, MutNullableDom}; |
| use crate::dom::globalscope::GlobalScope; |
| use crate::dom::promise::Promise; |
| use crate::dom::readablestreambyobreader::ReadableStreamBYOBReader; |
| use crate::dom::readablestreamdefaultreader::ReadableStreamDefaultReader; |
| use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; |
| |
| /// <https://streams.spec.whatwg.org/#readablestreamgenericreader> |
| pub(crate) trait ReadableStreamGenericReader { |
| /// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize> |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| fn generic_initialize(&self, global: &GlobalScope, stream: &ReadableStream, can_gc: CanGc) { |
| // Set reader.[[stream]] to stream. |
| self.set_stream(Some(stream)); |
| |
| // Set stream.[[reader]] to reader. |
| let reader_type = if let Some(default_reader) = self.as_default_reader() { |
| ReaderType::Default(MutNullableDom::new(Some(default_reader))) |
| } else if let Some(byob_reader) = self.as_byob_reader() { |
| ReaderType::BYOB(MutNullableDom::new(Some(byob_reader))) |
| } else { |
| unreachable!("Reader must be either Default or BYOB."); |
| }; |
| stream.set_reader(Some(reader_type)); |
| |
| if stream.is_readable() { |
| // If stream.[[state]] is "readable |
| // Set reader.[[closedPromise]] to a new promise. |
| self.set_closed_promise(Promise::new(global, can_gc)); |
| } else if stream.is_closed() { |
| // Otherwise, if stream.[[state]] is "closed", |
| // Set reader.[[closedPromise]] to a promise resolved with undefined. |
| let cx = GlobalScope::get_cx(); |
| self.set_closed_promise(Promise::new_resolved(global, cx, (), can_gc)); |
| } else { |
| // Assert: stream.[[state]] is "errored" |
| assert!(stream.is_errored()); |
| |
| // Set reader.[[closedPromise]] to a promise rejected with stream.[[storedError]]. |
| let cx = GlobalScope::get_cx(); |
| rooted!(in(*cx) let mut error = UndefinedValue()); |
| stream.get_stored_error(error.handle_mut()); |
| self.set_closed_promise(Promise::new_rejected(global, cx, error.handle(), can_gc)); |
| |
| // Set reader.[[closedPromise]].[[PromiseIsHandled]] to true |
| self.get_closed_promise().set_promise_is_handled(); |
| } |
| } |
| |
| /// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel> |
| fn reader_generic_cancel( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| reason: SafeHandleValue, |
| can_gc: CanGc, |
| ) -> Rc<Promise> { |
| // Let stream be reader.[[stream]]. |
| let stream = self.get_stream(); |
| |
| // Assert: stream is not undefined. |
| let stream = |
| stream.expect("Reader should have a stream when generic cancel is called into."); |
| |
| // Return ! ReadableStreamCancel(stream, reason). |
| stream.cancel(cx, global, reason, can_gc) |
| } |
| |
| /// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-release> |
| fn generic_release(&self, can_gc: CanGc) -> Fallible<()> { |
| // Let stream be reader.[[stream]]. |
| |
| // Assert: stream is not undefined. |
| assert!(self.get_stream().is_some()); |
| |
| if let Some(stream) = self.get_stream() { |
| // Assert: stream.[[reader]] is reader. |
| if self.as_default_reader().is_some() { |
| assert!(stream.has_default_reader()); |
| } else { |
| assert!(stream.has_byob_reader()); |
| } |
| |
| if stream.is_readable() { |
| // If stream.[[state]] is "readable", reject reader.[[closedPromise]] with a TypeError exception. |
| self.get_closed_promise().reject_error( |
| Error::Type("stream state is not readable".to_owned()), |
| can_gc, |
| ); |
| } else { |
| // Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception. |
| let cx = GlobalScope::get_cx(); |
| rooted!(in(*cx) let mut error = UndefinedValue()); |
| Error::Type("Cannot release lock due to stream state.".to_owned()).to_jsval( |
| cx, |
| &stream.global(), |
| error.handle_mut(), |
| can_gc, |
| ); |
| |
| self.set_closed_promise(Promise::new_rejected( |
| &stream.global(), |
| cx, |
| error.handle(), |
| can_gc, |
| )); |
| } |
| // Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. |
| self.get_closed_promise().set_promise_is_handled(); |
| |
| // Perform ! stream.[[controller]].[[ReleaseSteps]](). |
| stream.perform_release_steps()?; |
| |
| // Set stream.[[reader]] to undefined. |
| stream.set_reader(None); |
| // Set reader.[[stream]] to undefined. |
| self.set_stream(None); |
| } |
| Ok(()) |
| } |
| |
| /// <https://streams.spec.whatwg.org/#generic-reader-closed> |
| fn closed(&self) -> Rc<Promise> { |
| self.get_closed_promise() |
| } |
| |
| // <https://streams.spec.whatwg.org/#generic-reader-cancel> |
| fn generic_cancel( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| reason: SafeHandleValue, |
| can_gc: CanGc, |
| ) -> Rc<Promise> { |
| if self.get_stream().is_none() { |
| // If this.[[stream]] is undefined, |
| // return a promise rejected with a TypeError exception. |
| let promise = Promise::new(global, can_gc); |
| promise.reject_error(Error::Type("stream is undefined".to_owned()), can_gc); |
| promise |
| } else { |
| // Return ! ReadableStreamReaderGenericCancel(this, reason). |
| self.reader_generic_cancel(cx, global, reason, can_gc) |
| } |
| } |
| |
| fn set_stream(&self, stream: Option<&ReadableStream>); |
| |
| fn get_stream(&self) -> Option<DomRoot<ReadableStream>>; |
| |
| fn set_closed_promise(&self, promise: Rc<Promise>); |
| |
| fn get_closed_promise(&self) -> Rc<Promise>; |
| |
| fn as_default_reader(&self) -> Option<&ReadableStreamDefaultReader> { |
| None |
| } |
| |
| fn as_byob_reader(&self) -> Option<&ReadableStreamBYOBReader> { |
| None |
| } |
| } |