/* 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::cell::Cell;
use std::cmp::min;
use std::collections::VecDeque;
use std::rc::Rc;

use dom_struct::dom_struct;
use js::jsapi::{Heap, Type};
use js::jsval::UndefinedValue;
use js::rust::{HandleObject, HandleValue as SafeHandleValue, HandleValue};
use js::typedarray::{ArrayBufferU8, ArrayBufferViewU8};

use super::bindings::buffer_source::HeapBufferSource;
use super::bindings::cell::DomRefCell;
use super::bindings::codegen::Bindings::ReadableStreamBYOBReaderBinding::ReadableStreamBYOBReaderReadOptions;
use super::bindings::reflector::reflect_dom_object;
use super::bindings::root::Dom;
use super::readablestreambyobreader::ReadIntoRequest;
use super::readablestreamdefaultreader::ReadRequest;
use super::underlyingsourcecontainer::{UnderlyingSourceContainer, UnderlyingSourceType};
use crate::dom::bindings::buffer_source::{
    Constructor, byte_size, create_array_buffer_with_size, create_buffer_source_with_constructor,
};
use crate::dom::bindings::codegen::Bindings::ReadableByteStreamControllerBinding::ReadableByteStreamControllerMethods;
use crate::dom::bindings::codegen::UnionTypes::ReadableStreamDefaultControllerOrReadableByteStreamController as Controller;
use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
use crate::dom::bindings::reflector::{DomGlobal, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::dom::readablestream::ReadableStream;
use crate::dom::readablestreambyobrequest::ReadableStreamBYOBRequest;
use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};

/// <https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry>
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct QueueEntry {
    /// <https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry-buffer>
    #[ignore_malloc_size_of = "HeapBufferSource"]
    buffer: HeapBufferSource<ArrayBufferU8>,
    /// <https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry-byte-offset>
    byte_offset: usize,
    /// <https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry-byte-length>
    byte_length: usize,
}

impl QueueEntry {
    pub(crate) fn new(
        buffer: HeapBufferSource<ArrayBufferU8>,
        byte_offset: usize,
        byte_length: usize,
    ) -> QueueEntry {
        QueueEntry {
            buffer,
            byte_offset,
            byte_length,
        }
    }
}

#[derive(Debug, Eq, JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) enum ReaderType {
    /// <https://streams.spec.whatwg.org/#readablestreambyobreader>
    Byob,
    /// <https://streams.spec.whatwg.org/#readablestreamdefaultreader>
    Default,
}

/// <https://streams.spec.whatwg.org/#pull-into-descriptor>
#[derive(Eq, JSTraceable, MallocSizeOf, PartialEq)]
pub(crate) struct PullIntoDescriptor {
    #[ignore_malloc_size_of = "HeapBufferSource"]
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-buffer>
    buffer: HeapBufferSource<ArrayBufferU8>,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-buffer-byte-length>
    buffer_byte_length: u64,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-byte-offset>
    byte_offset: u64,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-byte-length>
    byte_length: u64,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-bytes-filled>
    bytes_filled: Cell<u64>,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-minimum-fill>
    minimum_fill: u64,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-element-size>
    element_size: u64,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-view-constructor>
    view_constructor: Constructor,
    /// <https://streams.spec.whatwg.org/#pull-into-descriptor-reader-type>
    reader_type: Option<ReaderType>,
}

/// The fulfillment handler for
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-start>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
struct StartAlgorithmFulfillmentHandler {
    controller: Dom<ReadableByteStreamController>,
}

impl Callback for StartAlgorithmFulfillmentHandler {
    /// Continuation of <https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller>
    /// Upon fulfillment of startPromise,
    fn callback(&self, _cx: SafeJSContext, _v: HandleValue, _realm: InRealm, can_gc: CanGc) {
        // Set controller.[[started]] to true.
        self.controller.started.set(true);

        // Assert: controller.[[pulling]] is false.
        assert!(!self.controller.pulling.get());

        // Assert: controller.[[pullAgain]] is false.
        assert!(!self.controller.pull_again.get());

        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
        self.controller.call_pull_if_needed(can_gc);
    }
}

/// The rejection handler for
/// <https://streams.spec.whatwg.org/#dom-underlyingsource-start>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
struct StartAlgorithmRejectionHandler {
    controller: Dom<ReadableByteStreamController>,
}

impl Callback for StartAlgorithmRejectionHandler {
    /// Continuation of <https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller>
    /// Upon rejection of startPromise with reason r,
    fn callback(&self, _cx: SafeJSContext, v: HandleValue, _realm: InRealm, can_gc: CanGc) {
        // Perform ! ReadableByteStreamControllerError(controller, r).
        self.controller.error(v, can_gc);
    }
}

/// The fulfillment handler for
/// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
struct PullAlgorithmFulfillmentHandler {
    controller: Dom<ReadableByteStreamController>,
}

impl Callback for PullAlgorithmFulfillmentHandler {
    /// Continuation of <https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed>
    /// Upon fulfillment of pullPromise
    fn callback(&self, _cx: SafeJSContext, _v: HandleValue, _realm: InRealm, can_gc: CanGc) {
        // Set controller.[[pulling]] to false.
        self.controller.pulling.set(false);

        // If controller.[[pullAgain]] is true,
        if self.controller.pull_again.get() {
            // Set controller.[[pullAgain]] to false.
            self.controller.pull_again.set(false);

            // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
            self.controller.call_pull_if_needed(can_gc);
        }
    }
}

/// The rejection handler for
/// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed>
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
struct PullAlgorithmRejectionHandler {
    controller: Dom<ReadableByteStreamController>,
}

impl Callback for PullAlgorithmRejectionHandler {
    /// Continuation of <https://streams.spec.whatwg.org/#readable-stream-byte-controller-call-pull-if-needed>
    /// Upon rejection of pullPromise with reason e.
    fn callback(&self, _cx: SafeJSContext, v: HandleValue, _realm: InRealm, can_gc: CanGc) {
        // Perform ! ReadableByteStreamControllerError(controller, e).
        self.controller.error(v, can_gc);
    }
}

/// <https://streams.spec.whatwg.org/#readablebytestreamcontroller>
#[dom_struct]
pub(crate) struct ReadableByteStreamController {
    reflector_: Reflector,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-autoallocatechunksize>
    auto_allocate_chunk_size: Option<u64>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-stream>
    stream: MutNullableDom<ReadableStream>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-strategyhwm>
    strategy_hwm: f64,
    /// A mutable reference to the underlying source is used to implement these two
    /// internal slots:
    ///
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-pullalgorithm>
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-cancelalgorithm>
    underlying_source: MutNullableDom<UnderlyingSourceContainer>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-queue>
    queue: DomRefCell<VecDeque<QueueEntry>>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-queuetotalsize>
    queue_total_size: Cell<f64>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-byobrequest>
    byob_request: MutNullableDom<ReadableStreamBYOBRequest>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-pendingpullintos>
    pending_pull_intos: DomRefCell<Vec<PullIntoDescriptor>>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-closerequested>
    close_requested: Cell<bool>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-started>
    started: Cell<bool>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-pulling>
    pulling: Cell<bool>,
    /// <https://streams.spec.whatwg.org/#readablebytestreamcontroller-pullalgorithm>
    pull_again: Cell<bool>,
}

impl ReadableByteStreamController {
    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
    fn new_inherited(
        underlying_source_type: UnderlyingSourceType,
        strategy_hwm: f64,
        global: &GlobalScope,
        can_gc: CanGc,
    ) -> ReadableByteStreamController {
        let underlying_source_container =
            UnderlyingSourceContainer::new(global, underlying_source_type, can_gc);
        let auto_allocate_chunk_size = underlying_source_container.auto_allocate_chunk_size();
        ReadableByteStreamController {
            reflector_: Reflector::new(),
            byob_request: MutNullableDom::new(None),
            stream: MutNullableDom::new(None),
            underlying_source: MutNullableDom::new(Some(&*underlying_source_container)),
            auto_allocate_chunk_size,
            pending_pull_intos: DomRefCell::new(Vec::new()),
            strategy_hwm,
            close_requested: Default::default(),
            queue: DomRefCell::new(Default::default()),
            queue_total_size: Default::default(),
            started: Default::default(),
            pulling: Default::default(),
            pull_again: Default::default(),
        }
    }

    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
    pub(crate) fn new(
        underlying_source_type: UnderlyingSourceType,
        strategy_hwm: f64,
        global: &GlobalScope,
        can_gc: CanGc,
    ) -> DomRoot<ReadableByteStreamController> {
        reflect_dom_object(
            Box::new(ReadableByteStreamController::new_inherited(
                underlying_source_type,
                strategy_hwm,
                global,
                can_gc,
            )),
            global,
            can_gc,
        )
    }

    pub(crate) fn set_stream(&self, stream: &ReadableStream) {
        self.stream.set(Some(stream))
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-pull-into>
    pub(crate) fn perform_pull_into(
        &self,
        cx: SafeJSContext,
        read_into_request: &ReadIntoRequest,
        view: HeapBufferSource<ArrayBufferViewU8>,
        options: &ReadableStreamBYOBReaderReadOptions,
        can_gc: CanGc,
    ) {
        // Let stream be controller.[[stream]].
        let stream = self.stream.get().unwrap();

        // Let elementSize be 1.
        let mut element_size = 1;

        // Let ctor be %DataView%.
        let mut ctor = Constructor::DataView;

        // If view has a [[TypedArrayName]] internal slot (i.e., it is not a DataView),
        if view.has_typed_array_name() {
            // Set elementSize to the element size specified in the
            // typed array constructors table for view.[[TypedArrayName]].
            let view_typw = view.get_array_buffer_view_type();
            element_size = byte_size(view_typw);

            // Set ctor to the constructor specified in the typed array constructors table for view.[[TypedArrayName]].
            ctor = Constructor::Name(view_typw);
        }

        // Let minimumFill be min × elementSize.
        let minimum_fill = options.min * element_size;

        // Assert: minimumFill ≥ 0 and minimumFill ≤ view.[[ByteLength]].
        assert!(minimum_fill <= (view.byte_length() as u64));

        // Assert: the remainder after dividing minimumFill by elementSize is 0.
        assert_eq!(minimum_fill % element_size, 0);

        // Let byteOffset be view.[[ByteOffset]].
        let byte_offset = view.get_byte_offset();

        // Let byteLength be view.[[ByteLength]].
        let byte_length = view.byte_length();

        // Let bufferResult be TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
        match view
            .get_array_buffer_view_buffer(cx)
            .transfer_array_buffer(cx)
        {
            Ok(buffer) => {
                // Let buffer be bufferResult.[[Value]].
                // Let pullIntoDescriptor be a new pull-into descriptor with
                // buffer   buffer
                // buffer byte length   buffer.[[ArrayBufferByteLength]]
                // byte offset  byteOffset
                // byte length  byteLength
                // bytes filled  0
                // minimum fill minimumFill
                // element size elementSize
                // view constructor ctor
                // reader type  "byob"
                let buffer_byte_length = buffer.byte_length();
                let pull_into_descriptor = PullIntoDescriptor {
                    buffer,
                    buffer_byte_length: buffer_byte_length as u64,
                    byte_offset: byte_offset as u64,
                    byte_length: byte_length as u64,
                    bytes_filled: Cell::new(0),
                    minimum_fill,
                    element_size,
                    view_constructor: ctor.clone(),
                    reader_type: Some(ReaderType::Byob),
                };

                // If controller.[[pendingPullIntos]] is not empty,
                {
                    let mut pending_pull_intos = self.pending_pull_intos.borrow_mut();
                    if !pending_pull_intos.is_empty() {
                        // Append pullIntoDescriptor to controller.[[pendingPullIntos]].
                        pending_pull_intos.push(pull_into_descriptor);

                        // Perform ! ReadableStreamAddReadIntoRequest(stream, readIntoRequest).
                        stream.add_read_into_request(read_into_request);

                        // Return.
                        return;
                    }
                }

                // If stream.[[state]] is "closed",
                if stream.is_closed() {
                    // Let emptyView be ! Construct(ctor, « pullIntoDescriptor’s buffer,
                    // pullIntoDescriptor’s byte offset, 0 »).
                    if let Ok(empty_view) = create_buffer_source_with_constructor(
                        cx,
                        &ctor,
                        &pull_into_descriptor.buffer,
                        pull_into_descriptor.byte_offset as usize,
                        0,
                    ) {
                        // Perform readIntoRequest’s close steps, given emptyView.
                        let result = RootedTraceableBox::new(Heap::default());
                        rooted!(in(*cx) let mut view_value = UndefinedValue());
                        empty_view.get_buffer_view_value(cx, view_value.handle_mut());
                        result.set(*view_value);

                        read_into_request.close_steps(Some(result), can_gc);

                        // Return.
                        return;
                    } else {
                        return;
                    }
                }

                // If controller.[[queueTotalSize]] > 0,
                if self.queue_total_size.get() > 0.0 {
                    // If ! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
                    // controller, pullIntoDescriptor) is true,
                    if self.fill_pull_into_descriptor_from_queue(cx, &pull_into_descriptor) {
                        // Let filledView be ! ReadableByteStreamControllerConvertPullIntoDescriptor(
                        // pullIntoDescriptor).
                        if let Ok(filled_view) =
                            self.convert_pull_into_descriptor(cx, &pull_into_descriptor)
                        {
                            // Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
                            self.handle_queue_drain(can_gc);

                            // Perform readIntoRequest’s chunk steps, given filledView.
                            let result = RootedTraceableBox::new(Heap::default());
                            rooted!(in(*cx) let mut view_value = UndefinedValue());
                            filled_view.get_buffer_view_value(cx, view_value.handle_mut());
                            result.set(*view_value);
                            read_into_request.chunk_steps(result, can_gc);

                            // Return.
                            return;
                        } else {
                            return;
                        }
                    }

                    // If controller.[[closeRequested]] is true,
                    if self.close_requested.get() {
                        // Let e be a new TypeError exception.
                        rooted!(in(*cx) let mut error = UndefinedValue());
                        Error::Type("close requested".to_owned()).to_jsval(
                            cx,
                            &self.global(),
                            error.handle_mut(),
                            can_gc,
                        );

                        // Perform ! ReadableByteStreamControllerError(controller, e).
                        self.error(error.handle(), can_gc);

                        // Perform readIntoRequest’s error steps, given e.
                        read_into_request.error_steps(error.handle(), can_gc);

                        // Return.
                        return;
                    }
                }

                // Append pullIntoDescriptor to controller.[[pendingPullIntos]].
                {
                    self.pending_pull_intos
                        .borrow_mut()
                        .push(pull_into_descriptor);
                }
                // Perform ! ReadableStreamAddReadIntoRequest(stream, readIntoRequest).
                stream.add_read_into_request(read_into_request);

                // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
                self.call_pull_if_needed(can_gc);
            },
            Err(error) => {
                // If bufferResult is an abrupt completion,

                // Perform readIntoRequest’s error steps, given bufferResult.[[Value]].
                rooted!(in(*cx) let mut rval = UndefinedValue());
                error
                    .clone()
                    .to_jsval(cx, &self.global(), rval.handle_mut(), can_gc);
                read_into_request.error_steps(rval.handle(), can_gc);

                // Return.
            },
        }
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond>
    pub(crate) fn respond(
        &self,
        cx: SafeJSContext,
        bytes_written: u64,
        can_gc: CanGc,
    ) -> Fallible<()> {
        {
            // Assert: controller.[[pendingPullIntos]] is not empty.
            let mut pending_pull_intos = self.pending_pull_intos.borrow_mut();
            assert!(!pending_pull_intos.is_empty());

            // Let firstDescriptor be controller.[[pendingPullIntos]][0].
            let first_descriptor = pending_pull_intos.first_mut().unwrap();

            // Let state be controller.[[stream]].[[state]].
            let stream = self.stream.get().unwrap();

            // If state is "closed",
            if stream.is_closed() {
                // If bytesWritten is not 0, throw a TypeError exception.
                if bytes_written != 0 {
                    return Err(Error::Type(
                        "bytesWritten not zero on closed stream".to_owned(),
                    ));
                }
            } else {
                // Assert: state is "readable".
                assert!(stream.is_readable());

                // If bytesWritten is 0, throw a TypeError exception.
                if bytes_written == 0 {
                    return Err(Error::Type("bytesWritten is 0".to_owned()));
                }

                // If firstDescriptor’s bytes filled + bytesWritten > firstDescriptor’s byte length,
                // throw a RangeError exception.
                if first_descriptor.bytes_filled.get() + bytes_written >
                    first_descriptor.byte_length
                {
                    return Err(Error::Range(
                        "bytes filled + bytesWritten > byte length".to_owned(),
                    ));
                }
            }

            // Set firstDescriptor’s buffer to ! TransferArrayBuffer(firstDescriptor’s buffer).
            first_descriptor.buffer = first_descriptor.buffer.transfer_array_buffer(cx)?;
        }

        // Perform ? ReadableByteStreamControllerRespondInternal(controller, bytesWritten).
        self.respond_internal(cx, bytes_written, can_gc)
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-internal>
    pub(crate) fn respond_internal(
        &self,
        cx: SafeJSContext,
        bytes_written: u64,
        can_gc: CanGc,
    ) -> Fallible<()> {
        {
            // Let firstDescriptor be controller.[[pendingPullIntos]][0].
            let pending_pull_intos = self.pending_pull_intos.borrow();
            let first_descriptor = pending_pull_intos.first().unwrap();

            // Assert: ! CanTransferArrayBuffer(firstDescriptor’s buffer) is true
            assert!(first_descriptor.buffer.can_transfer_array_buffer(cx));
        }

        // Perform ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
        self.invalidate_byob_request();

        // Let state be controller.[[stream]].[[state]].
        let stream = self.stream.get().unwrap();

        // If state is "closed",
        if stream.is_closed() {
            // Assert: bytesWritten is 0.
            assert_eq!(bytes_written, 0);

            // Perform ! ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor).
            self.respond_in_closed_state(cx, can_gc)?;
        } else {
            // Assert: state is "readable".
            assert!(stream.is_readable());

            // Assert: bytesWritten > 0.
            assert!(bytes_written > 0);

            // Perform ? ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor).
            self.respond_in_readable_state(cx, bytes_written, can_gc)?;
        }

        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
        self.call_pull_if_needed(can_gc);

        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-closed-state>
    pub(crate) fn respond_in_closed_state(&self, cx: SafeJSContext, can_gc: CanGc) -> Fallible<()> {
        let pending_pull_intos = self.pending_pull_intos.borrow();
        let first_descriptor = pending_pull_intos.first().unwrap();

        // Assert: the remainder after dividing firstDescriptor’s bytes filled
        // by firstDescriptor’s element size is 0.
        assert_eq!(
            first_descriptor.bytes_filled.get() % first_descriptor.element_size,
            0
        );

        // If firstDescriptor’s reader type is "none",
        // perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
        let reader_type = first_descriptor.reader_type.is_none();

        // needed to drop the borrow and avoid BorrowMutError
        drop(pending_pull_intos);

        if reader_type {
            self.shift_pending_pull_into();
        }

        // Let stream be controller.[[stream]].
        let stream = self.stream.get().unwrap();

        // If ! ReadableStreamHasBYOBReader(stream) is true,
        if stream.has_byob_reader() {
            // Let filledPullIntos be a new empty list.
            let mut filled_pull_intos = Vec::new();

            // While filledPullIntos’s size < ! ReadableStreamGetNumReadIntoRequests(stream),
            while filled_pull_intos.len() < stream.get_num_read_into_requests() {
                // Let pullIntoDescriptor be ! ReadableByteStreamControllerShiftPendingPullInto(controller).
                let pull_into_descriptor = self.shift_pending_pull_into();

                // Append pullIntoDescriptor to filledPullIntos.
                filled_pull_intos.push(pull_into_descriptor);
            }

            // For each filledPullInto of filledPullIntos,
            for filled_pull_into in filled_pull_intos {
                // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, filledPullInto).
                self.commit_pull_into_descriptor(cx, &filled_pull_into, can_gc)?;
            }
        }

        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-readable-state>
    pub(crate) fn respond_in_readable_state(
        &self,
        cx: SafeJSContext,
        bytes_written: u64,
        can_gc: CanGc,
    ) -> Fallible<()> {
        let pending_pull_intos = self.pending_pull_intos.borrow();
        let first_descriptor = pending_pull_intos.first().unwrap();

        // Assert: pullIntoDescriptor’s bytes filled + bytesWritten ≤ pullIntoDescriptor’s byte length.
        assert!(
            first_descriptor.bytes_filled.get() + bytes_written <= first_descriptor.byte_length
        );

        // Perform ! ReadableByteStreamControllerFillHeadPullIntoDescriptor(
        // controller, bytesWritten, pullIntoDescriptor).
        self.fill_head_pull_into_descriptor(bytes_written, first_descriptor);

        // If pullIntoDescriptor’s reader type is "none",
        if first_descriptor.reader_type.is_none() {
            // needed to drop the borrow and avoid BorrowMutError
            drop(pending_pull_intos);

            // Perform ? ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller, pullIntoDescriptor).
            self.enqueue_detached_pull_into_to_queue(cx, can_gc)?;

            // Let filledPullIntos be the result of performing
            // ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
            let filled_pull_intos = self.process_pull_into_descriptors_using_queue(cx);

            // For each filledPullInto of filledPullIntos,
            for filled_pull_into in filled_pull_intos {
                // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]]
                // , filledPullInto).
                self.commit_pull_into_descriptor(cx, &filled_pull_into, can_gc)?;
            }

            // Return.
            return Ok(());
        }

        // If pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s minimum fill, return.
        if first_descriptor.bytes_filled.get() < first_descriptor.minimum_fill {
            return Ok(());
        }

        // needed to drop the borrow and avoid BorrowMutError
        drop(pending_pull_intos);

        // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
        let pull_into_descriptor = self.shift_pending_pull_into();

        // Let remainderSize be the remainder after dividing pullIntoDescriptor’s bytes
        // filled by pullIntoDescriptor’s element size.
        let remainder_size =
            pull_into_descriptor.bytes_filled.get() % pull_into_descriptor.element_size;

        // If remainderSize > 0,
        if remainder_size > 0 {
            // Let end be pullIntoDescriptor’s byte offset + pullIntoDescriptor’s bytes filled.
            let end = pull_into_descriptor.byte_offset + pull_into_descriptor.bytes_filled.get();

            // Perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
            // pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
            self.enqueue_cloned_chunk_to_queue(
                cx,
                &pull_into_descriptor.buffer,
                end - remainder_size,
                remainder_size,
                can_gc,
            )?;
        }

        // Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes filled − remainderSize.
        pull_into_descriptor
            .bytes_filled
            .set(pull_into_descriptor.bytes_filled.get() - remainder_size);

        // Let filledPullIntos be the result of performing
        // ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
        let filled_pull_intos = self.process_pull_into_descriptors_using_queue(cx);

        // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], pullIntoDescriptor).
        self.commit_pull_into_descriptor(cx, &pull_into_descriptor, can_gc)?;

        // For each filledPullInto of filledPullIntos,
        for filled_pull_into in filled_pull_intos {
            // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]], filledPullInto).
            self.commit_pull_into_descriptor(cx, &filled_pull_into, can_gc)?;
        }

        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-with-new-view>
    pub(crate) fn respond_with_new_view(
        &self,
        cx: SafeJSContext,
        view: HeapBufferSource<ArrayBufferViewU8>,
        can_gc: CanGc,
    ) -> Fallible<()> {
        let view_byte_length;
        {
            // Assert: controller.[[pendingPullIntos]] is not empty.
            let mut pending_pull_intos = self.pending_pull_intos.borrow_mut();
            assert!(!pending_pull_intos.is_empty());

            // Assert: ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is false.
            assert!(!view.is_detached_buffer(cx));

            // Let firstDescriptor be controller.[[pendingPullIntos]][0].
            let first_descriptor = pending_pull_intos.first_mut().unwrap();

            // Let state be controller.[[stream]].[[state]].
            let stream = self.stream.get().unwrap();

            // If state is "closed",
            if stream.is_closed() {
                // If view.[[ByteLength]] is not 0, throw a TypeError exception.
                if view.byte_length() != 0 {
                    return Err(Error::Type("view byte length is not 0".to_owned()));
                }
            } else {
                // Assert: state is "readable".
                assert!(stream.is_readable());

                // If view.[[ByteLength]] is 0, throw a TypeError exception.
                if view.byte_length() == 0 {
                    return Err(Error::Type("view byte length is 0".to_owned()));
                }
            }

            // If firstDescriptor’s byte offset + firstDescriptor’ bytes filled is not view.[[ByteOffset]],
            // throw a RangeError exception.
            if first_descriptor.byte_offset + first_descriptor.bytes_filled.get() !=
                (view.get_byte_offset() as u64)
            {
                return Err(Error::Range(
                    "firstDescriptor's byte offset + bytes filled is not view byte offset"
                        .to_owned(),
                ));
            }

            // If firstDescriptor’s buffer byte length is not view.[[ViewedArrayBuffer]].[[ByteLength]],
            // throw a RangeError exception.
            if first_descriptor.buffer_byte_length !=
                (view.viewed_buffer_array_byte_length(cx) as u64)
            {
                return Err(Error::Range(
                "firstDescriptor's buffer byte length is not view viewed buffer array byte length"
                    .to_owned(),
            ));
            }

            // If firstDescriptor’s bytes filled + view.[[ByteLength]] > firstDescriptor’s byte length,
            // throw a RangeError exception.
            if first_descriptor.bytes_filled.get() + (view.byte_length()) as u64 >
                first_descriptor.byte_length
            {
                return Err(Error::Range(
                    "bytes filled + view byte length > byte length".to_owned(),
                ));
            }

            // Let viewByteLength be view.[[ByteLength]].
            view_byte_length = view.byte_length();

            // Set firstDescriptor’s buffer to ? TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
            first_descriptor.buffer = view
                .get_array_buffer_view_buffer(cx)
                .transfer_array_buffer(cx)?;
        }

        // Perform ? ReadableByteStreamControllerRespondInternal(controller, viewByteLength).
        self.respond_internal(cx, view_byte_length as u64, can_gc)
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-get-desired-size>
    pub(crate) fn get_desired_size(&self) -> Option<f64> {
        // Let state be controller.[[stream]].[[state]].
        let stream = self.stream.get()?;

        // If state is "errored", return null.
        if stream.is_errored() {
            return None;
        }

        // If state is "closed", return 0.
        if stream.is_closed() {
            return Some(0.0);
        }

        // Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
        Some(self.strategy_hwm - self.queue_total_size.get())
    }

    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollergetbyobrequest>
    pub(crate) fn get_byob_request(
        &self,
        cx: SafeJSContext,
        can_gc: CanGc,
    ) -> Fallible<Option<DomRoot<ReadableStreamBYOBRequest>>> {
        // If controller.[[byobRequest]] is null and controller.[[pendingPullIntos]] is not empty,
        let pending_pull_intos = self.pending_pull_intos.borrow();
        if self.byob_request.get().is_none() && !pending_pull_intos.is_empty() {
            // Let firstDescriptor be controller.[[pendingPullIntos]][0].
            let first_descriptor = pending_pull_intos.first().unwrap();
            // Let view be ! Construct(%Uint8Array%, « firstDescriptor’s buffer,
            // firstDescriptor’s byte offset + firstDescriptor’s bytes filled,
            // firstDescriptor’s byte length − firstDescriptor’s bytes filled »).

            let byte_offset = first_descriptor.byte_offset + first_descriptor.bytes_filled.get();
            let byte_length = first_descriptor.byte_length - first_descriptor.bytes_filled.get();

            let view = create_buffer_source_with_constructor(
                cx,
                &Constructor::Name(Type::Uint8),
                &first_descriptor.buffer,
                byte_offset as usize,
                byte_length as usize,
            )?;

            // Let byobRequest be a new ReadableStreamBYOBRequest.
            let byob_request = ReadableStreamBYOBRequest::new(&self.global(), can_gc);

            // Set byobRequest.[[controller]] to controller.
            byob_request.set_controller(Some(&DomRoot::from_ref(self)));

            // Set byobRequest.[[view]] to view.
            byob_request.set_view(Some(view));

            // Set controller.[[byobRequest]] to byobRequest.
            self.byob_request.set(Some(&byob_request));
        }

        // Return controller.[[byobRequest]].
        Ok(self.byob_request.get())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-close>
    pub(crate) fn close(&self, cx: SafeJSContext, can_gc: CanGc) -> Fallible<()> {
        // Let stream be controller.[[stream]].
        let stream = self.stream.get().unwrap();

        // If controller.[[closeRequested]] is true or stream.[[state]] is not "readable", return.
        if self.close_requested.get() || !stream.is_readable() {
            return Ok(());
        }

        // If controller.[[queueTotalSize]] > 0,
        if self.queue_total_size.get() > 0.0 {
            // Set controller.[[closeRequested]] to true.
            self.close_requested.set(true);
            // Return.
            return Ok(());
        }

        // If controller.[[pendingPullIntos]] is not empty,
        let pending_pull_intos = self.pending_pull_intos.borrow();
        if !pending_pull_intos.is_empty() {
            // Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
            let first_pending_pull_into = pending_pull_intos.first().unwrap();

            // If the remainder after dividing firstPendingPullInto’s bytes filled by
            // firstPendingPullInto’s element size is not 0,
            if first_pending_pull_into.bytes_filled.get() % first_pending_pull_into.element_size !=
                0
            {
                // needed to drop the borrow and avoid BorrowMutError
                drop(pending_pull_intos);

                // Let e be a new TypeError exception.
                let e = Error::Type(
                    "remainder after dividing firstPendingPullInto's bytes
                    filled by firstPendingPullInto's element size is not 0"
                        .to_owned(),
                );

                // Perform ! ReadableByteStreamControllerError(controller, e).
                rooted!(in(*cx) let mut error = UndefinedValue());
                e.clone()
                    .to_jsval(cx, &self.global(), error.handle_mut(), can_gc);
                self.error(error.handle(), can_gc);

                // Throw e.
                return Err(e);
            }
        }

        // Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
        self.clear_algorithms();

        // Perform ! ReadableStreamClose(stream).
        stream.close(can_gc);
        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-error>
    pub(crate) fn error(&self, e: SafeHandleValue, can_gc: CanGc) {
        // Let stream be controller.[[stream]].
        let stream = self.stream.get().unwrap();

        // If stream.[[state]] is not "readable", return.
        if !stream.is_readable() {
            return;
        }

        // Perform ! ReadableByteStreamControllerClearPendingPullIntos(controller).
        self.clear_pending_pull_intos();

        // Perform ! ResetQueue(controller).
        self.reset_queue();

        // Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
        self.clear_algorithms();

        // Perform ! ReadableStreamError(stream, e).
        stream.error(e, can_gc);
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-algorithms>
    fn clear_algorithms(&self) {
        // Set controller.[[pullAlgorithm]] to undefined.
        // Set controller.[[cancelAlgorithm]] to undefined.
        self.underlying_source.set(None);
    }

    /// <https://streams.spec.whatwg.org/#reset-queue>
    pub(crate) fn reset_queue(&self) {
        // Assert: container has [[queue]] and [[queueTotalSize]] internal slots.

        // Set container.[[queue]] to a new empty list.
        self.queue.borrow_mut().clear();

        // Set container.[[queueTotalSize]] to 0.
        self.queue_total_size.set(0.0);
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-pending-pull-intos>
    pub(crate) fn clear_pending_pull_intos(&self) {
        // Perform ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
        self.invalidate_byob_request();

        // Set controller.[[pendingPullIntos]] to a new empty list.
        self.pending_pull_intos.borrow_mut().clear();
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-invalidate-byob-request>
    pub(crate) fn invalidate_byob_request(&self) {
        if let Some(byob_request) = self.byob_request.get() {
            // Set controller.[[byobRequest]].[[controller]] to undefined.
            byob_request.set_controller(None);

            // Set controller.[[byobRequest]].[[view]] to null.
            byob_request.set_view(None);

            // Set controller.[[byobRequest]] to null.
            self.byob_request.set(None);
        }
        // If controller.[[byobRequest]] is null, return.
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue>
    pub(crate) fn enqueue(
        &self,
        cx: SafeJSContext,
        chunk: HeapBufferSource<ArrayBufferViewU8>,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // Let stream be controller.[[stream]].
        let stream = self.stream.get().unwrap();

        // If controller.[[closeRequested]] is true or stream.[[state]] is not "readable", return.
        if self.close_requested.get() || !stream.is_readable() {
            return Ok(());
        }

        // Let buffer be chunk.[[ViewedArrayBuffer]].
        let buffer = chunk.get_array_buffer_view_buffer(cx);

        // Let byteOffset be chunk.[[ByteOffset]].
        let byte_offset = chunk.get_byte_offset();

        // Let byteLength be chunk.[[ByteLength]].
        let byte_length = chunk.byte_length();

        // If ! IsDetachedBuffer(buffer) is true, throw a TypeError exception.
        if buffer.is_detached_buffer(cx) {
            return Err(Error::Type("buffer is detached".to_owned()));
        }

        // Let transferredBuffer be ? TransferArrayBuffer(buffer).
        let transferred_buffer = buffer.transfer_array_buffer(cx)?;

        // If controller.[[pendingPullIntos]] is not empty,
        {
            let mut pending_pull_intos = self.pending_pull_intos.borrow_mut();
            if !pending_pull_intos.is_empty() {
                // Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
                let first_descriptor = pending_pull_intos.first_mut().unwrap();
                // If ! IsDetachedBuffer(firstPendingPullInto’s buffer) is true, throw a TypeError exception.
                if first_descriptor.buffer.is_detached_buffer(cx) {
                    return Err(Error::Type("buffer is detached".to_owned()));
                }

                // Perform ! ReadableByteStreamControllerInvalidateBYOBRequest(controller).
                self.invalidate_byob_request();

                // Set firstPendingPullInto’s buffer to ! TransferArrayBuffer(firstPendingPullInto’s buffer).
                first_descriptor.buffer = first_descriptor.buffer.transfer_array_buffer(cx)?;

                // If firstPendingPullInto’s reader type is "none",
                if first_descriptor.reader_type.is_none() {
                    // needed to drop the borrow and avoid BorrowMutError
                    drop(pending_pull_intos);

                    // perform ? ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
                    // controller, firstPendingPullInto).
                    self.enqueue_detached_pull_into_to_queue(cx, can_gc)?;
                }
            }
        }

        // If ! ReadableStreamHasDefaultReader(stream) is true,
        if stream.has_default_reader() {
            // Perform ! ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller).
            self.process_read_requests_using_queue(cx, can_gc)?;

            // If ! ReadableStreamGetNumReadRequests(stream) is 0,
            if stream.get_num_read_requests() == 0 {
                // Assert: controller.[[pendingPullIntos]] is empty.
                {
                    assert!(self.pending_pull_intos.borrow().is_empty());
                }

                // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(
                // controller, transferredBuffer, byteOffset, byteLength).
                self.enqueue_chunk_to_queue(transferred_buffer, byte_offset, byte_length);
            } else {
                // Assert: controller.[[queue]] is empty.
                assert!(self.queue.borrow().is_empty());

                // If controller.[[pendingPullIntos]] is not empty,

                let pending_pull_intos = self.pending_pull_intos.borrow();
                if !pending_pull_intos.is_empty() {
                    // Assert: controller.[[pendingPullIntos]][0]'s reader type is "default".
                    assert!(matches!(
                        pending_pull_intos.first().unwrap().reader_type,
                        Some(ReaderType::Default)
                    ));

                    // needed to drop the borrow and avoid BorrowMutError
                    drop(pending_pull_intos);

                    // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
                    self.shift_pending_pull_into();
                }

                // Let transferredView be ! Construct(%Uint8Array%, « transferredBuffer, byteOffset, byteLength »).
                let transferred_view = create_buffer_source_with_constructor(
                    cx,
                    &Constructor::Name(Type::Uint8),
                    &transferred_buffer,
                    byte_offset,
                    byte_length,
                )?;

                // Perform ! ReadableStreamFulfillReadRequest(stream, transferredView, false).
                rooted!(in(*cx) let mut view_value = UndefinedValue());
                transferred_view.get_buffer_view_value(cx, view_value.handle_mut());
                stream.fulfill_read_request(view_value.handle(), false, can_gc);
            }
            // Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
        } else if stream.has_byob_reader() {
            // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue(
            // controller, transferredBuffer, byteOffset, byteLength).
            self.enqueue_chunk_to_queue(transferred_buffer, byte_offset, byte_length);

            // Let filledPullIntos be the result of performing !
            // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
            let filled_pull_intos = self.process_pull_into_descriptors_using_queue(cx);

            // For each filledPullInto of filledPullIntos,
            // Perform ! ReadableByteStreamControllerCommitPullIntoDescriptor(stream, filledPullInto).
            for filled_pull_into in filled_pull_intos {
                self.commit_pull_into_descriptor(cx, &filled_pull_into, can_gc)?;
            }
        } else {
            // Assert: ! IsReadableStreamLocked(stream) is false.
            assert!(!stream.is_locked());

            // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue
            // (controller, transferredBuffer, byteOffset, byteLength).
            self.enqueue_chunk_to_queue(transferred_buffer, byte_offset, byte_length);
        }

        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
        self.call_pull_if_needed(can_gc);

        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-commit-pull-into-descriptor>
    pub(crate) fn commit_pull_into_descriptor(
        &self,
        cx: SafeJSContext,
        pull_into_descriptor: &PullIntoDescriptor,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // Assert: stream.[[state]] is not "errored".
        let stream = self.stream.get().unwrap();
        assert!(!stream.is_errored());

        // Assert: pullIntoDescriptor.reader type is not "none".
        assert!(pull_into_descriptor.reader_type.is_some());

        // Let done be false.
        let mut done = false;

        // If stream.[[state]] is "closed",
        if stream.is_closed() {
            // Assert: the remainder after dividing pullIntoDescriptor’s bytes filled
            // by pullIntoDescriptor’s element size is 0.
            assert!(
                pull_into_descriptor.bytes_filled.get() % pull_into_descriptor.element_size == 0
            );

            // Set done to true.
            done = true;
        }

        // Let filledView be ! ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
        let filled_view = self.convert_pull_into_descriptor(cx, pull_into_descriptor)?;

        rooted!(in(*cx) let mut view_value = UndefinedValue());
        filled_view.get_buffer_view_value(cx, view_value.handle_mut());

        // If pullIntoDescriptor’s reader type is "default",
        if matches!(pull_into_descriptor.reader_type, Some(ReaderType::Default)) {
            // Perform ! ReadableStreamFulfillReadRequest(stream, filledView, done).

            stream.fulfill_read_request(view_value.handle(), done, can_gc);
        } else {
            // Assert: pullIntoDescriptor’s reader type is "byob".
            assert!(matches!(
                pull_into_descriptor.reader_type,
                Some(ReaderType::Byob)
            ));

            // Perform ! ReadableStreamFulfillReadIntoRequest(stream, filledView, done).
            stream.fulfill_read_into_request(view_value.handle(), done, can_gc);
        }
        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-convert-pull-into-descriptor>
    pub(crate) fn convert_pull_into_descriptor(
        &self,
        cx: SafeJSContext,
        pull_into_descriptor: &PullIntoDescriptor,
    ) -> Fallible<HeapBufferSource<ArrayBufferViewU8>> {
        // Let bytesFilled be pullIntoDescriptor’s bytes filled.
        let bytes_filled = pull_into_descriptor.bytes_filled.get();

        // Let elementSize be pullIntoDescriptor’s element size.
        let element_size = pull_into_descriptor.element_size;

        // Assert: bytesFilled ≤ pullIntoDescriptor’s byte length.
        assert!(bytes_filled <= pull_into_descriptor.byte_length);

        //Assert: the remainder after dividing bytesFilled by elementSize is 0.
        assert!(bytes_filled % element_size == 0);

        // Let buffer be ! TransferArrayBuffer(pullIntoDescriptor’s buffer).
        let buffer = pull_into_descriptor.buffer.transfer_array_buffer(cx)?;

        // Return ! Construct(pullIntoDescriptor’s view constructor,
        // « buffer, pullIntoDescriptor’s byte offset, bytesFilled ÷ elementSize »).
        create_buffer_source_with_constructor(
            cx,
            &pull_into_descriptor.view_constructor,
            &buffer,
            pull_into_descriptor.byte_offset as usize,
            (bytes_filled / element_size) as usize,
        )
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue>
    pub(crate) fn process_pull_into_descriptors_using_queue(
        &self,
        cx: SafeJSContext,
    ) -> Vec<PullIntoDescriptor> {
        // Assert: controller.[[closeRequested]] is false.
        assert!(!self.close_requested.get());

        // Let filledPullIntos be a new empty list.
        let mut filled_pull_intos = Vec::new();

        // While controller.[[pendingPullIntos]] is not empty,
        loop {
            // If controller.[[queueTotalSize]] is 0, then break.
            if self.queue_total_size.get() == 0.0 {
                break;
            }

            // Let pullIntoDescriptor be controller.[[pendingPullIntos]][0].
            let fill_pull_result = {
                let pending_pull_intos = self.pending_pull_intos.borrow();
                let Some(pull_into_descriptor) = pending_pull_intos.first() else {
                    break;
                };
                self.fill_pull_into_descriptor_from_queue(cx, pull_into_descriptor)
            };

            // If ! ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) is true,
            if fill_pull_result {
                // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
                let pull_into_descriptor = self.shift_pending_pull_into();

                // Append pullIntoDescriptor to filledPullIntos.
                filled_pull_intos.push(pull_into_descriptor);
            }
        }

        // Return filledPullIntos.
        filled_pull_intos
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue>
    pub(crate) fn fill_pull_into_descriptor_from_queue(
        &self,
        cx: SafeJSContext,
        pull_into_descriptor: &PullIntoDescriptor,
    ) -> bool {
        // Let maxBytesToCopy be min(controller.[[queueTotalSize]],
        // pullIntoDescriptor’s byte length − pullIntoDescriptor’s bytes filled).
        let max_bytes_to_copy = min(
            self.queue_total_size.get() as usize,
            (pull_into_descriptor.byte_length - pull_into_descriptor.bytes_filled.get()) as usize,
        );

        // Let maxBytesFilled be pullIntoDescriptor’s bytes filled + maxBytesToCopy.
        let max_bytes_filled = pull_into_descriptor.bytes_filled.get() as usize + max_bytes_to_copy;

        // Let totalBytesToCopyRemaining be maxBytesToCopy.
        let mut total_bytes_to_copy_remaining = max_bytes_to_copy;

        // Let ready be false.
        let mut ready = false;

        // Assert: ! IsDetachedBuffer(pullIntoDescriptor’s buffer) is false.
        assert!(!pull_into_descriptor.buffer.is_detached_buffer(cx));

        // Assert: pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s minimum fill.
        assert!(pull_into_descriptor.bytes_filled.get() < pull_into_descriptor.minimum_fill);

        // Let remainderBytes be the remainder after dividing maxBytesFilled by pullIntoDescriptor’s element size.
        let remainder_bytes = max_bytes_filled % pull_into_descriptor.element_size as usize;

        // Let maxAlignedBytes be maxBytesFilled − remainderBytes.
        let max_aligned_bytes = max_bytes_filled - remainder_bytes;

        // If maxAlignedBytes ≥ pullIntoDescriptor’s minimum fill,
        if max_aligned_bytes >= pull_into_descriptor.minimum_fill as usize {
            // Set totalBytesToCopyRemaining to maxAlignedBytes − pullIntoDescriptor’s bytes filled.
            total_bytes_to_copy_remaining =
                max_aligned_bytes - (pull_into_descriptor.bytes_filled.get() as usize);

            // Set ready to true.
            ready = true;
        }

        // Let queue be controller.[[queue]].
        let mut queue = self.queue.borrow_mut();

        // While totalBytesToCopyRemaining > 0,
        while total_bytes_to_copy_remaining > 0 {
            // Let headOfQueue be queue[0].
            let head_of_queue = queue.front_mut().unwrap();

            // Let bytesToCopy be min(totalBytesToCopyRemaining, headOfQueue’s byte length).
            let bytes_to_copy = total_bytes_to_copy_remaining.min(head_of_queue.byte_length);

            // Let destStart be pullIntoDescriptor’s byte offset + pullIntoDescriptor’s bytes filled.
            let dest_start =
                pull_into_descriptor.byte_offset + pull_into_descriptor.bytes_filled.get();

            // Let descriptorBuffer be pullIntoDescriptor’s buffer.
            let descriptor_buffer = &pull_into_descriptor.buffer;

            // Let queueBuffer be headOfQueue’s buffer.
            let queue_buffer = &head_of_queue.buffer;

            // Let queueByteOffset be headOfQueue’s byte offset.
            let queue_byte_offset = head_of_queue.byte_offset;

            // Assert: ! CanCopyDataBlockBytes(descriptorBuffer, destStart,
            // queueBuffer, queueByteOffset, bytesToCopy) is true.
            assert!(descriptor_buffer.can_copy_data_block_bytes(
                cx,
                dest_start as usize,
                queue_buffer,
                queue_byte_offset,
                bytes_to_copy
            ));

            // Perform ! CopyDataBlockBytes(descriptorBuffer.[[ArrayBufferData]], destStart,
            // queueBuffer.[[ArrayBufferData]], queueByteOffset, bytesToCopy).
            descriptor_buffer.copy_data_block_bytes(
                cx,
                dest_start as usize,
                queue_buffer,
                queue_byte_offset,
                bytes_to_copy,
            );

            // If headOfQueue’s byte length is bytesToCopy,
            if head_of_queue.byte_length == bytes_to_copy {
                // Remove queue[0].
                queue.pop_front().unwrap();
            } else {
                // Set headOfQueue’s byte offset to headOfQueue’s byte offset + bytesToCopy.
                head_of_queue.byte_offset += bytes_to_copy;

                // Set headOfQueue’s byte length to headOfQueue’s byte length − bytesToCopy.
                head_of_queue.byte_length -= bytes_to_copy;
            }

            // Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]] − bytesToCopy.
            self.queue_total_size
                .set(self.queue_total_size.get() - (bytes_to_copy as f64));

            // Perform ! ReadableByteStreamControllerFillHeadPullIntoDescriptor(
            // controller, bytesToCopy, pullIntoDescriptor).
            self.fill_head_pull_into_descriptor(bytes_to_copy as u64, pull_into_descriptor);

            // Set totalBytesToCopyRemaining to totalBytesToCopyRemaining − bytesToCopy.
            total_bytes_to_copy_remaining -= bytes_to_copy;
        }

        // If ready is false,
        if !ready {
            // Assert: controller.[[queueTotalSize]] is 0.
            assert!(self.queue_total_size.get() == 0.0);

            // Assert: pullIntoDescriptor’s bytes filled > 0.
            assert!(pull_into_descriptor.bytes_filled.get() > 0);

            // Assert: pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s minimum fill.
            assert!(pull_into_descriptor.bytes_filled.get() < pull_into_descriptor.minimum_fill);
        }

        // Return ready.
        ready
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-head-pull-into-descriptor>
    pub(crate) fn fill_head_pull_into_descriptor(
        &self,
        bytes_copied: u64,
        pull_into_descriptor: &PullIntoDescriptor,
    ) {
        // Assert: either controller.[[pendingPullIntos]] is empty,
        // or controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
        {
            let pending_pull_intos = self.pending_pull_intos.borrow();
            assert!(
                pending_pull_intos.is_empty() ||
                    pending_pull_intos.first().unwrap() == pull_into_descriptor
            );
        }

        // Assert: controller.[[byobRequest]] is null.
        assert!(self.byob_request.get().is_none());

        // Set pullIntoDescriptor’s bytes filled to bytes filled + size.
        pull_into_descriptor
            .bytes_filled
            .set(pull_into_descriptor.bytes_filled.get() + bytes_copied);
    }

    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueuedetachedpullintotoqueue>
    pub(crate) fn enqueue_detached_pull_into_to_queue(
        &self,
        cx: SafeJSContext,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // first_descriptor: &PullIntoDescriptor,
        let pending_pull_intos = self.pending_pull_intos.borrow();
        let first_descriptor = pending_pull_intos.first().unwrap();

        // Assert: pullIntoDescriptor’s reader type is "none".
        assert!(first_descriptor.reader_type.is_none());

        // If pullIntoDescriptor’s bytes filled > 0, perform ?
        // ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
        // pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset, pullIntoDescriptor’s bytes filled).

        if first_descriptor.bytes_filled.get() > 0 {
            self.enqueue_cloned_chunk_to_queue(
                cx,
                &first_descriptor.buffer,
                first_descriptor.byte_offset,
                first_descriptor.bytes_filled.get(),
                can_gc,
            )?;
        }

        // needed to drop the borrow and avoid BorrowMutError
        drop(pending_pull_intos);

        // Perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
        self.shift_pending_pull_into();

        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueueclonedchunktoqueue>
    pub(crate) fn enqueue_cloned_chunk_to_queue(
        &self,
        cx: SafeJSContext,
        buffer: &HeapBufferSource<ArrayBufferU8>,
        byte_offset: u64,
        byte_length: u64,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // Let cloneResult be CloneArrayBuffer(buffer, byteOffset, byteLength, %ArrayBuffer%).
        if let Some(clone_result) =
            buffer.clone_array_buffer(cx, byte_offset as usize, byte_length as usize)
        {
            // Perform ! ReadableByteStreamControllerEnqueueChunkToQueue
            // (controller, cloneResult.[[Value]], 0, byteLength).
            self.enqueue_chunk_to_queue(clone_result, 0, byte_length as usize);

            Ok(())
        } else {
            // If cloneResult is an abrupt completion,

            // Perform ! ReadableByteStreamControllerError(controller, cloneResult.[[Value]]).
            rooted!(in(*cx) let mut rval = UndefinedValue());
            let error = Error::Type("can not clone array buffer".to_owned());
            error
                .clone()
                .to_jsval(cx, &self.global(), rval.handle_mut(), can_gc);
            self.error(rval.handle(), can_gc);

            // Return cloneResult.
            Err(error)
        }
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue-chunk-to-queue>
    pub(crate) fn enqueue_chunk_to_queue(
        &self,
        buffer: HeapBufferSource<ArrayBufferU8>,
        byte_offset: usize,
        byte_length: usize,
    ) {
        // Let entry be a new ReadableByteStreamQueueEntry object.
        let entry = QueueEntry::new(buffer, byte_offset, byte_length);

        // Append entry to controller.[[queue]].
        self.queue.borrow_mut().push_back(entry);

        // Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]] + byteLength.
        self.queue_total_size
            .set(self.queue_total_size.get() + byte_length as f64);
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into>
    pub(crate) fn shift_pending_pull_into(&self) -> PullIntoDescriptor {
        // Assert: controller.[[byobRequest]] is null.
        assert!(self.byob_request.get().is_none());

        // Let descriptor be controller.[[pendingPullIntos]][0].
        // Remove descriptor from controller.[[pendingPullIntos]].
        let descriptor = self.pending_pull_intos.borrow_mut().remove(0);

        // Return descriptor.
        descriptor
    }

    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerprocessreadrequestsusingqueue>
    pub(crate) fn process_read_requests_using_queue(
        &self,
        cx: SafeJSContext,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // Let reader be controller.[[stream]].[[reader]].
        // Assert: reader implements ReadableStreamDefaultReader.
        let reader = self.stream.get().unwrap().get_default_reader();

        // Step 3
        reader.process_read_requests(cx, DomRoot::from_ref(self), can_gc)
    }

    /// <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerfillreadrequestfromqueue>
    pub(crate) fn fill_read_request_from_queue(
        &self,
        cx: SafeJSContext,
        read_request: &ReadRequest,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // Assert: controller.[[queueTotalSize]] > 0.
        assert!(self.queue_total_size.get() > 0.0);
        // Also assert that the queue has a non-zero length;
        assert!(!self.queue.borrow().is_empty());

        // Let entry be controller.[[queue]][0].
        // Remove entry from controller.[[queue]].
        let entry = self.remove_entry();

        // Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]] − entry’s byte length.
        self.queue_total_size
            .set(self.queue_total_size.get() - entry.byte_length as f64);

        // Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
        self.handle_queue_drain(can_gc);

        // Let view be ! Construct(%Uint8Array%, « entry’s buffer, entry’s byte offset, entry’s byte length »).
        let view = create_buffer_source_with_constructor(
            cx,
            &Constructor::Name(Type::Uint8),
            &entry.buffer,
            entry.byte_offset,
            entry.byte_length,
        )?;

        // Perform readRequest’s chunk steps, given view.
        let result = RootedTraceableBox::new(Heap::default());
        rooted!(in(*cx) let mut view_value = UndefinedValue());
        view.get_buffer_view_value(cx, view_value.handle_mut());
        result.set(*view_value);

        read_request.chunk_steps(result, can_gc);

        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-handle-queue-drain>
    pub(crate) fn handle_queue_drain(&self, can_gc: CanGc) {
        // Assert: controller.[[stream]].[[state]] is "readable".
        assert!(self.stream.get().unwrap().is_readable());

        // If controller.[[queueTotalSize]] is 0 and controller.[[closeRequested]] is true,
        if self.queue_total_size.get() == 0.0 && self.close_requested.get() {
            // Perform ! ReadableByteStreamControllerClearAlgorithms(controller).
            self.clear_algorithms();

            // Perform ! ReadableStreamClose(controller.[[stream]]).
            self.stream.get().unwrap().close(can_gc);
        } else {
            // Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller).
            self.call_pull_if_needed(can_gc);
        }
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed>
    pub(crate) fn call_pull_if_needed(&self, can_gc: CanGc) {
        // Let shouldPull be ! ReadableByteStreamControllerShouldCallPull(controller).
        let should_pull = self.should_call_pull();
        // If shouldPull is false, return.
        if !should_pull {
            return;
        }

        // If controller.[[pulling]] is true,
        if self.pulling.get() {
            // Set controller.[[pullAgain]] to true.
            self.pull_again.set(true);

            // Return.
            return;
        }

        // Assert: controller.[[pullAgain]] is false.
        assert!(!self.pull_again.get());

        // Set controller.[[pulling]] to true.
        self.pulling.set(true);

        // Let pullPromise be the result of performing controller.[[pullAlgorithm]].
        // Continues into the resolve and reject handling of the native handler.
        let global = self.global();
        let rooted_controller = DomRoot::from_ref(self);
        let controller = Controller::ReadableByteStreamController(rooted_controller.clone());

        if let Some(underlying_source) = self.underlying_source.get() {
            let handler = PromiseNativeHandler::new(
                &global,
                Some(Box::new(PullAlgorithmFulfillmentHandler {
                    controller: Dom::from_ref(&rooted_controller),
                })),
                Some(Box::new(PullAlgorithmRejectionHandler {
                    controller: Dom::from_ref(&rooted_controller),
                })),
                can_gc,
            );

            let realm = enter_realm(&*global);
            let comp = InRealm::Entered(&realm);
            let result = underlying_source
                .call_pull_algorithm(controller, &global, can_gc)
                .unwrap_or_else(|| {
                    let promise = Promise::new(&global, can_gc);
                    promise.resolve_native(&(), can_gc);
                    Ok(promise)
                });
            let promise = result.unwrap_or_else(|error| {
                let cx = GlobalScope::get_cx();
                rooted!(in(*cx) let mut rval = UndefinedValue());
                // TODO: check if `self.global()` is the right globalscope.
                error
                    .clone()
                    .to_jsval(cx, &self.global(), rval.handle_mut(), can_gc);
                let promise = Promise::new(&global, can_gc);
                promise.reject_native(&rval.handle(), can_gc);
                promise
            });
            promise.append_native_handler(&handler, comp, can_gc);
        }
    }

    /// <https://streams.spec.whatwg.org/#readable-byte-stream-controller-should-call-pull>
    fn should_call_pull(&self) -> bool {
        // Let stream be controller.[[stream]].
        // Note: the spec does not assert that stream is not undefined here,
        // so we return false if it is.
        let stream = self.stream.get().unwrap();

        // If stream.[[state]] is not "readable", return false.
        if !stream.is_readable() {
            return false;
        }

        // If controller.[[closeRequested]] is true, return false.
        if self.close_requested.get() {
            return false;
        }

        // If controller.[[started]] is false, return false.
        if !self.started.get() {
            return false;
        }

        // If ! ReadableStreamHasDefaultReader(stream) is true and ! ReadableStreamGetNumReadRequests(stream) > 0
        // , return true.
        if stream.has_default_reader() && stream.get_num_read_requests() > 0 {
            return true;
        }

        // If ! ReadableStreamHasBYOBReader(stream) is true and ! ReadableStreamGetNumReadIntoRequests(stream) > 0
        // , return true.
        if stream.has_byob_reader() && stream.get_num_read_into_requests() > 0 {
            return true;
        }

        // Let desiredSize be ! ReadableByteStreamControllerGetDesiredSize(controller).
        let desired_size = self.get_desired_size();

        // Assert: desiredSize is not null.
        assert!(desired_size.is_some());

        // If desiredSize > 0, return true.
        if desired_size.unwrap() > 0. {
            return true;
        }

        // Return false.
        false
    }
    /// <https://streams.spec.whatwg.org/#set-up-readable-byte-stream-controller>
    pub(crate) fn setup(
        &self,
        global: &GlobalScope,
        stream: DomRoot<ReadableStream>,
        can_gc: CanGc,
    ) -> Fallible<()> {
        // Assert: stream.[[controller]] is undefined.
        stream.assert_no_controller();

        // If autoAllocateChunkSize is not undefined,
        if self.auto_allocate_chunk_size.is_some() {
            // Assert: ! IsInteger(autoAllocateChunkSize) is true. Implicit
            // Assert: autoAllocateChunkSize is positive. (Implicit by type.)
        }

        // Set controller.[[stream]] to stream.
        self.stream.set(Some(&stream));

        // Set controller.[[pullAgain]] and controller.[[pulling]] to false.
        self.pull_again.set(false);
        self.pulling.set(false);

        // Set controller.[[byobRequest]] to null.
        self.byob_request.set(None);

        // Perform ! ResetQueue(controller).
        self.reset_queue();

        // Set controller.[[closeRequested]] and controller.[[started]] to false.
        self.close_requested.set(false);
        self.started.set(false);

        // Set controller.[[strategyHWM]] to highWaterMark.
        // Set controller.[[pullAlgorithm]] to pullAlgorithm.
        // Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
        // Set controller.[[autoAllocateChunkSize]] to autoAllocateChunkSize.
        // Set controller.[[pendingPullIntos]] to a new empty list.
        // Note: the above steps are done in `new`.

        // Set stream.[[controller]] to controller.
        let rooted_byte_controller = DomRoot::from_ref(self);
        stream.set_byte_controller(&rooted_byte_controller);

        if let Some(underlying_source) = rooted_byte_controller.underlying_source.get() {
            // Let startResult be the result of performing startAlgorithm. (This might throw an exception.)
            let start_result = underlying_source
                .call_start_algorithm(
                    Controller::ReadableByteStreamController(rooted_byte_controller.clone()),
                    can_gc,
                )
                .unwrap_or_else(|| {
                    let promise = Promise::new(global, can_gc);
                    promise.resolve_native(&(), can_gc);
                    Ok(promise)
                });

            // Let startPromise be a promise resolved with startResult.
            let start_promise = start_result?;

            // Upon fulfillment of startPromise, Upon rejection of startPromise with reason r,
            let handler = PromiseNativeHandler::new(
                global,
                Some(Box::new(StartAlgorithmFulfillmentHandler {
                    controller: Dom::from_ref(&rooted_byte_controller),
                })),
                Some(Box::new(StartAlgorithmRejectionHandler {
                    controller: Dom::from_ref(&rooted_byte_controller),
                })),
                can_gc,
            );
            let realm = enter_realm(global);
            let comp = InRealm::Entered(&realm);
            start_promise.append_native_handler(&handler, comp, can_gc);
        };

        Ok(())
    }

    // <https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontroller-releasesteps
    pub(crate) fn perform_release_steps(&self) -> Fallible<()> {
        // If this.[[pendingPullIntos]] is not empty,
        let mut pending_pull_intos = self.pending_pull_intos.borrow_mut();
        if !pending_pull_intos.is_empty() {
            // Let firstPendingPullInto be this.[[pendingPullIntos]][0].
            let mut first_pending_pull_into = pending_pull_intos.remove(0);

            // Set firstPendingPullInto’s reader type to "none".
            first_pending_pull_into.reader_type = None;

            // Set this.[[pendingPullIntos]] to the list « firstPendingPullInto »
            pending_pull_intos.clear();
            pending_pull_intos.push(first_pending_pull_into);
        }
        Ok(())
    }

    /// <https://streams.spec.whatwg.org/#rbs-controller-private-cancel>
    pub(crate) fn perform_cancel_steps(
        &self,
        cx: SafeJSContext,
        global: &GlobalScope,
        reason: SafeHandleValue,
        can_gc: CanGc,
    ) -> Rc<Promise> {
        // Perform ! ReadableByteStreamControllerClearPendingPullIntos(this).
        self.clear_pending_pull_intos();

        // Perform ! ResetQueue(this).
        self.reset_queue();

        let underlying_source = self
            .underlying_source
            .get()
            .expect("Controller should have a source when the cancel steps are called into.");

        // Let result be the result of performing this.[[cancelAlgorithm]], passing in reason.
        let result = underlying_source
            .call_cancel_algorithm(cx, global, reason, can_gc)
            .unwrap_or_else(|| {
                let promise = Promise::new(global, can_gc);
                promise.resolve_native(&(), can_gc);
                Ok(promise)
            });

        let promise = result.unwrap_or_else(|error| {
            let cx = GlobalScope::get_cx();
            rooted!(in(*cx) let mut rval = UndefinedValue());
            error
                .clone()
                .to_jsval(cx, global, rval.handle_mut(), can_gc);
            let promise = Promise::new(global, can_gc);
            promise.reject_native(&rval.handle(), can_gc);
            promise
        });

        // Perform ! ReadableByteStreamControllerClearAlgorithms(this).
        self.clear_algorithms();

        // Return result(the promise).
        promise
    }

    /// <https://streams.spec.whatwg.org/#rbs-controller-private-pull>
    pub(crate) fn perform_pull_steps(
        &self,
        cx: SafeJSContext,
        read_request: &ReadRequest,
        can_gc: CanGc,
    ) {
        // Let stream be this.[[stream]].
        let stream = self.stream.get().unwrap();

        // Assert: ! ReadableStreamHasDefaultReader(stream) is true.
        assert!(stream.has_default_reader());

        // If this.[[queueTotalSize]] > 0,
        if self.queue_total_size.get() > 0.0 {
            // Assert: ! ReadableStreamGetNumReadRequests(stream) is 0.
            assert_eq!(stream.get_num_read_requests(), 0);

            // Perform ! ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest).
            let _ = self.fill_read_request_from_queue(cx, read_request, can_gc);

            // Return.
            return;
        }

        // Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]].
        let auto_allocate_chunk_size = self.auto_allocate_chunk_size;

        // If autoAllocateChunkSize is not undefined,
        if let Some(auto_allocate_chunk_size) = auto_allocate_chunk_size {
            // create_array_buffer_with_size
            // Let buffer be Construct(%ArrayBuffer%, « autoAllocateChunkSize »).
            match create_array_buffer_with_size(cx, auto_allocate_chunk_size as usize) {
                Ok(buffer) => {
                    // Let pullIntoDescriptor be a new pull-into descriptor with
                    // buffer buffer.[[Value]]
                    // buffer byte length autoAllocateChunkSize
                    // byte offset  0
                    // byte length  autoAllocateChunkSize
                    // bytes filled  0
                    // minimum fill 1
                    // element size 1
                    // view constructor %Uint8Array%
                    // reader type  "default"
                    let pull_into_descriptor = PullIntoDescriptor {
                        buffer,
                        buffer_byte_length: auto_allocate_chunk_size,
                        byte_length: auto_allocate_chunk_size,
                        byte_offset: 0,
                        bytes_filled: Cell::new(0),
                        minimum_fill: 1,
                        element_size: 1,
                        view_constructor: Constructor::Name(Type::Uint8),
                        reader_type: Some(ReaderType::Default),
                    };

                    // Append pullIntoDescriptor to this.[[pendingPullIntos]].
                    self.pending_pull_intos
                        .borrow_mut()
                        .push(pull_into_descriptor);
                },
                Err(error) => {
                    // If buffer is an abrupt completion,
                    // Perform readRequest’s error steps, given buffer.[[Value]].

                    rooted!(in(*cx) let mut rval = UndefinedValue());
                    error
                        .clone()
                        .to_jsval(cx, &self.global(), rval.handle_mut(), can_gc);
                    read_request.error_steps(rval.handle(), can_gc);

                    // Return.
                    return;
                },
            }
        }

        // Perform ! ReadableStreamAddReadRequest(stream, readRequest).
        stream.add_read_request(read_request);

        // Perform ! ReadableByteStreamControllerCallPullIfNeeded(this).
        self.call_pull_if_needed(can_gc);
    }

    /// Setting the JS object after the heap has settled down.
    pub(crate) fn set_underlying_source_this_object(&self, this_object: HandleObject) {
        if let Some(underlying_source) = self.underlying_source.get() {
            underlying_source.set_underlying_source_this_object(this_object);
        }
    }

    pub(crate) fn remove_entry(&self) -> QueueEntry {
        self.queue
            .borrow_mut()
            .pop_front()
            .expect("Reader must have read request when remove is called into.")
    }

    pub(crate) fn get_queue_total_size(&self) -> f64 {
        self.queue_total_size.get()
    }
}

impl ReadableByteStreamControllerMethods<crate::DomTypeHolder> for ReadableByteStreamController {
    /// <https://streams.spec.whatwg.org/#rbs-controller-byob-request>
    fn GetByobRequest(
        &self,
        can_gc: CanGc,
    ) -> Fallible<Option<DomRoot<ReadableStreamBYOBRequest>>> {
        let cx = GlobalScope::get_cx();
        // Return ! ReadableByteStreamControllerGetBYOBRequest(this).
        self.get_byob_request(cx, can_gc)
    }

    /// <https://streams.spec.whatwg.org/#rbs-controller-desired-size>
    fn GetDesiredSize(&self) -> Option<f64> {
        // Return ! ReadableByteStreamControllerGetDesiredSize(this).
        self.get_desired_size()
    }

    /// <https://streams.spec.whatwg.org/#rbs-controller-close>
    fn Close(&self, can_gc: CanGc) -> Fallible<()> {
        let cx = GlobalScope::get_cx();
        // If this.[[closeRequested]] is true, throw a TypeError exception.
        if self.close_requested.get() {
            return Err(Error::Type("closeRequested is true".to_owned()));
        }

        // If this.[[stream]].[[state]] is not "readable", throw a TypeError exception.
        if !self.stream.get().unwrap().is_readable() {
            return Err(Error::Type("stream is not readable".to_owned()));
        }

        // Perform ? ReadableByteStreamControllerClose(this).
        self.close(cx, can_gc)
    }

    /// <https://streams.spec.whatwg.org/#rbs-controller-enqueue>
    fn Enqueue(
        &self,
        chunk: js::gc::CustomAutoRooterGuard<js::typedarray::ArrayBufferView>,
        can_gc: CanGc,
    ) -> Fallible<()> {
        let cx = GlobalScope::get_cx();

        let chunk = HeapBufferSource::<ArrayBufferViewU8>::from_view(chunk);

        // If chunk.[[ByteLength]] is 0, throw a TypeError exception.
        if chunk.byte_length() == 0 {
            return Err(Error::Type("chunk.ByteLength is 0".to_owned()));
        }

        // If chunk.[[ViewedArrayBuffer]].[[ByteLength]] is 0, throw a TypeError exception.
        if chunk.viewed_buffer_array_byte_length(cx) == 0 {
            return Err(Error::Type(
                "chunk.ViewedArrayBuffer.ByteLength is 0".to_owned(),
            ));
        }

        // If this.[[closeRequested]] is true, throw a TypeError exception.
        if self.close_requested.get() {
            return Err(Error::Type("closeRequested is true".to_owned()));
        }

        // If this.[[stream]].[[state]] is not "readable", throw a TypeError exception.
        if !self.stream.get().unwrap().is_readable() {
            return Err(Error::Type("stream is not readable".to_owned()));
        }

        // Return ? ReadableByteStreamControllerEnqueue(this, chunk).
        self.enqueue(cx, chunk, can_gc)
    }

    /// <https://streams.spec.whatwg.org/#rbs-controller-error>
    fn Error(&self, _cx: SafeJSContext, e: SafeHandleValue, can_gc: CanGc) -> Fallible<()> {
        // Perform ! ReadableByteStreamControllerError(this, e).
        self.error(e, can_gc);
        Ok(())
    }
}