use std::cell::Cell;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::{HandleValueArray, Heap, NewArrayObject, Value};
use js::jsval::{ObjectValue, UndefinedValue};
use js::rust::HandleValue as SafeHandleValue;
use super::bindings::root::{DomRoot, MutNullableDom};
use super::types::{ReadableStream, ReadableStreamDefaultReader};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
use crate::dom::bindings::root::Dom;
use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::readablestreamdefaultreader::ReadRequest;
use crate::script_runtime::CanGc;
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) enum TeeCancelAlgorithm {
Cancel1Algorithm,
Cancel2Algorithm,
}
#[dom_struct]
pub(crate) struct DefaultTeeUnderlyingSource {
reflector_: Reflector,
reader: Dom<ReadableStreamDefaultReader>,
stream: Dom<ReadableStream>,
branch_1: MutNullableDom<ReadableStream>,
branch_2: MutNullableDom<ReadableStream>,
#[ignore_malloc_size_of = "Rc"]
reading: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
read_again: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_1: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
canceled_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
clone_for_branch_2: Rc<Cell<bool>>,
#[ignore_malloc_size_of = "Rc"]
#[allow(clippy::redundant_allocation)]
reason_1: Rc<Box<Heap<Value>>>,
#[ignore_malloc_size_of = "Rc"]
#[allow(clippy::redundant_allocation)]
reason_2: Rc<Box<Heap<Value>>>,
#[ignore_malloc_size_of = "Rc"]
cancel_promise: Rc<Promise>,
tee_cancel_algorithm: TeeCancelAlgorithm,
}
impl DefaultTeeUnderlyingSource {
#[allow(clippy::too_many_arguments)]
#[allow(clippy::redundant_allocation)]
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn new(
reader: &ReadableStreamDefaultReader,
stream: &ReadableStream,
reading: Rc<Cell<bool>>,
read_again: Rc<Cell<bool>>,
canceled_1: Rc<Cell<bool>>,
canceled_2: Rc<Cell<bool>>,
clone_for_branch_2: Rc<Cell<bool>>,
reason_1: Rc<Box<Heap<Value>>>,
reason_2: Rc<Box<Heap<Value>>>,
cancel_promise: Rc<Promise>,
tee_cancel_algorithm: TeeCancelAlgorithm,
can_gc: CanGc,
) -> DomRoot<DefaultTeeUnderlyingSource> {
reflect_dom_object(
Box::new(DefaultTeeUnderlyingSource {
reflector_: Reflector::new(),
reader: Dom::from_ref(reader),
stream: Dom::from_ref(stream),
branch_1: MutNullableDom::new(None),
branch_2: MutNullableDom::new(None),
reading,
read_again,
canceled_1,
canceled_2,
clone_for_branch_2,
reason_1,
reason_2,
cancel_promise,
tee_cancel_algorithm,
}),
&*stream.global(),
can_gc,
)
}
pub(crate) fn set_branch_1(&self, stream: &ReadableStream) {
self.branch_1.set(Some(stream));
}
pub(crate) fn set_branch_2(&self, stream: &ReadableStream) {
self.branch_2.set(Some(stream));
}
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) fn pull_algorithm(&self, can_gc: CanGc) -> Rc<Promise> {
let cx = GlobalScope::get_cx();
if self.reading.get() {
self.read_again.set(true);
rooted!(in(*cx) let mut rval = UndefinedValue());
return Promise::new_resolved(&self.stream.global(), cx, rval.handle(), can_gc);
}
self.reading.set(true);
let tee_read_request = DefaultTeeReadRequest::new(
&self.stream,
&self.branch_1.get().expect("Branch 1 should be set."),
&self.branch_2.get().expect("Branch 2 should be set."),
self.reading.clone(),
self.read_again.clone(),
self.canceled_1.clone(),
self.canceled_2.clone(),
self.clone_for_branch_2.clone(),
self.cancel_promise.clone(),
self,
can_gc,
);
let read_request = ReadRequest::DefaultTee {
tee_read_request: Dom::from_ref(&tee_read_request),
};
self.reader.read(cx, &read_request, can_gc);
rooted!(in(*cx) let mut rval = UndefinedValue());
Promise::new_resolved(&self.stream.global(), cx, rval.handle(), can_gc)
}
#[allow(unsafe_code)]
pub(crate) fn cancel_algorithm(
&self,
reason: SafeHandleValue,
can_gc: CanGc,
) -> Option<Result<Rc<Promise>, Error>> {
match self.tee_cancel_algorithm {
TeeCancelAlgorithm::Cancel1Algorithm => {
self.canceled_1.set(true);
self.reason_1.set(reason.get());
if self.canceled_2.get() {
self.resolve_cancel_promise(can_gc);
}
Some(Ok(self.cancel_promise.clone()))
},
TeeCancelAlgorithm::Cancel2Algorithm => {
self.canceled_2.set(true);
self.reason_2.set(reason.get());
if self.canceled_1.get() {
self.resolve_cancel_promise(can_gc);
}
Some(Ok(self.cancel_promise.clone()))
},
}
}
#[allow(unsafe_code)]
fn resolve_cancel_promise(&self, can_gc: CanGc) {
let cx = GlobalScope::get_cx();
rooted_vec!(let mut reasons_values);
reasons_values.push(self.reason_1.get());
reasons_values.push(self.reason_2.get());
let reasons_values_array = HandleValueArray::from(&reasons_values);
rooted!(in(*cx) let reasons = unsafe { NewArrayObject(*cx, &reasons_values_array) });
rooted!(in(*cx) let reasons_value = ObjectValue(reasons.get()));
let cancel_result = self.stream.cancel(reasons_value.handle(), can_gc);
self.cancel_promise.resolve_native(&cancel_result, can_gc);
}
}