Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ReadableByteStreamController.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */


#include "mozilla/dom/ReadableByteStreamController.h"

#include "ReadIntoRequest.h"
#include "js/ArrayBuffer.h"
#include "js/ErrorReport.h"
#include "js/Exception.h"
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "js/ValueArray.h"
#include "js/experimental/TypedData.h"
#include "js/friend/ErrorMessages.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/dom/ByteStreamHelpers.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Promise-inl.h"
#include "mozilla/dom/ReadableByteStreamControllerBinding.h"
#include "mozilla/dom/ReadableStream.h"
#include "mozilla/dom/ReadableStreamBYOBReader.h"
#include "mozilla/dom/ReadableStreamBYOBRequest.h"
#include "mozilla/dom/ReadableStreamController.h"
#include "mozilla/dom/ReadableStreamDefaultController.h"
#include "mozilla/dom/ReadableStreamDefaultReader.h"
#include "mozilla/dom/ReadableStreamGenericReader.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/UnderlyingSourceCallbackHelpers.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIGlobalObject.h"
#include "nsISupports.h"

#include <algorithm>  // std::min

namespace mozilla::dom {

using namespace streams_abstract;

// https://streams.spec.whatwg.org/#readable-byte-stream-queue-entry
struct ReadableByteStreamQueueEntry
    : LinkedListElement<RefPtr<ReadableByteStreamQueueEntry>> {
  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(
      ReadableByteStreamQueueEntry)
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(
      ReadableByteStreamQueueEntry)

  ReadableByteStreamQueueEntry(JS::Handle<JSObject*> aBuffer,
                               size_t aByteOffset, size_t aByteLength)
      : mBuffer(aBuffer), mByteOffset(aByteOffset), mByteLength(aByteLength) {
    mozilla::HoldJSObjects(this);
  }

  JSObject* Buffer() const { return mBuffer; }
  void SetBuffer(JS::Handle<JSObject*> aBuffer) { mBuffer = aBuffer; }

  size_t ByteOffset() const { return mByteOffset; }
  void SetByteOffset(size_t aByteOffset) { mByteOffset = aByteOffset; }

  size_t ByteLength() const { return mByteLength; }
  void SetByteLength(size_t aByteLength) { mByteLength = aByteLength; }

 private:
  // An ArrayBuffer, which will be a transferred version of the one originally
  // supplied by the underlying byte source.
  JS::Heap<JSObject*> mBuffer;

  // A nonnegative integer number giving the byte offset derived from the view
  // originally supplied by the underlying byte source
  size_t mByteOffset = 0;

  // A nonnegative integer number giving the byte length derived from the view
  // originally supplied by the underlying byte source
  size_t mByteLength = 0;

  ~ReadableByteStreamQueueEntry() { mozilla::DropJSObjects(this); }
};

NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(ReadableByteStreamQueueEntry, (),
                                         (mBuffer));

struct PullIntoDescriptor final
    : LinkedListElement<RefPtr<PullIntoDescriptor>> {
  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PullIntoDescriptor)
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PullIntoDescriptor)

  enum Constructor {
    DataView,
#define DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES(ExternalT, NativeT, Name) Name,
    JS_FOR_EACH_TYPED_ARRAY(DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES)
#undef DEFINE_TYPED_CONSTRUCTOR_ENUM_NAMES
  };

  static Constructor constructorFromScalar(JS::Scalar::Type type) {
    switch (type) {
#define REMAP_PULL_INTO_DESCRIPTOR_TYPE(ExternalT, NativeT, Name) \
  case JS::Scalar::Name:                                          \
    return Constructor::Name;
      JS_FOR_EACH_TYPED_ARRAY(REMAP_PULL_INTO_DESCRIPTOR_TYPE)
#undef REMAP

      case JS::Scalar::Int64:
      case JS::Scalar::Simd128:
      case JS::Scalar::MaxTypedArrayViewType:
        break;
    }
    MOZ_CRASH("Unexpected Scalar::Type");
  }

  PullIntoDescriptor(JS::Handle<JSObject*> aBuffer, uint64_t aBufferByteLength,
                     uint64_t aByteOffset, uint64_t aByteLength,
                     uint64_t aBytesFilled, uint64_t aMinimumFill,
                     uint64_t aElementSize, Constructor aViewConstructor,
                     ReaderType aReaderType)
      : mBuffer(aBuffer),
        mBufferByteLength(aBufferByteLength),
        mByteOffset(aByteOffset),
        mByteLength(aByteLength),
        mBytesFilled(aBytesFilled),
        mMinimumFill(aMinimumFill),
        mElementSize(aElementSize),
        mViewConstructor(aViewConstructor),
        mReaderType(aReaderType) {
    mozilla::HoldJSObjects(this);
  }

  JSObject* Buffer() const { return mBuffer; }
  void SetBuffer(JS::Handle<JSObject*> aBuffer) { mBuffer = aBuffer; }

  uint64_t BufferByteLength() const { return mBufferByteLength; }
  void SetBufferByteLength(const uint64_t aBufferByteLength) {
    mBufferByteLength = aBufferByteLength;
  }

  uint64_t ByteOffset() const { return mByteOffset; }
  void SetByteOffset(const uint64_t aByteOffset) { mByteOffset = aByteOffset; }

  uint64_t ByteLength() const { return mByteLength; }
  void SetByteLength(const uint64_t aByteLength) { mByteLength = aByteLength; }

  uint64_t BytesFilled() const { return mBytesFilled; }
  void SetBytesFilled(const uint64_t aBytesFilled) {
    mBytesFilled = aBytesFilled;
  }

  uint64_t MinimumFill() const { return mMinimumFill; }

  uint64_t ElementSize() const { return mElementSize; }
  void SetElementSize(const uint64_t aElementSize) {
    mElementSize = aElementSize;
  }

  Constructor ViewConstructor() const { return mViewConstructor; }

  // Note: Named GetReaderType to avoid name conflict with type.
  ReaderType GetReaderType() const { return mReaderType; }
  void SetReaderType(const ReaderType aReaderType) {
    mReaderType = aReaderType;
  }

 private:
  JS::Heap<JSObject*> mBuffer;
  uint64_t mBufferByteLength = 0;
  uint64_t mByteOffset = 0;
  uint64_t mByteLength = 0;
  uint64_t mBytesFilled = 0;
  uint64_t mMinimumFill = 0;
  uint64_t mElementSize = 0;
  Constructor mViewConstructor;
  ReaderType mReaderType;

  ~PullIntoDescriptor() { mozilla::DropJSObjects(this); }
};

NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(PullIntoDescriptor, (), (mBuffer));

NS_IMPL_CYCLE_COLLECTION_CLASS(ReadableByteStreamController)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ReadableByteStreamController,
                                                ReadableStreamController)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mByobRequest, mQueue, mPendingPullIntos)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ReadableByteStreamController,
                                                  ReadableStreamController)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mByobRequest, mQueue, mPendingPullIntos)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ReadableByteStreamController,
                                               ReadableStreamController)
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_ADDREF_INHERITED(ReadableByteStreamController, ReadableStreamController)
NS_IMPL_RELEASE_INHERITED(ReadableByteStreamController,
                          ReadableStreamController)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableByteStreamController)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END_INHERITING(ReadableStreamController)

ReadableByteStreamController::ReadableByteStreamController(
    nsIGlobalObject* aGlobal)
    : ReadableStreamController(aGlobal) {}

ReadableByteStreamController::~ReadableByteStreamController() = default;

void ReadableByteStreamController::ClearQueue() { mQueue.clear(); }

void ReadableByteStreamController::ClearPendingPullIntos() {
  mPendingPullIntos.clear();
}

namespace streams_abstract {
// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollergetbyobrequest
already_AddRefed<ReadableStreamBYOBRequest>
ReadableByteStreamControllerGetBYOBRequest(
    JSContext* aCx, ReadableByteStreamController* aController,
    ErrorResult& aRv) {
  // Step 1.
  if (!aController->GetByobRequest() &&
      !aController->PendingPullIntos().isEmpty()) {
    // Step 1.1:
    PullIntoDescriptor* firstDescriptor =
        aController->PendingPullIntos().getFirst();

    // Step 1.2:
    aRv.MightThrowJSException();
    JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
    JS::Rooted<JSObject*> view(
        aCx, JS_NewUint8ArrayWithBuffer(
                 aCx, buffer,
                 firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled(),
                 int64_t(firstDescriptor->ByteLength() -
                         firstDescriptor->BytesFilled())));
    if (!view) {
      aRv.StealExceptionFromJSContext(aCx);
      return nullptr;
    }

    // Step 1.3:
    RefPtr<ReadableStreamBYOBRequest> byobRequest =
        new ReadableStreamBYOBRequest(aController->GetParentObject());

    // Step 1.4:
    byobRequest->SetController(aController);

    // Step 1.5:
    byobRequest->SetView(view);

    // Step 1.6:
    aController->SetByobRequest(byobRequest);
  }

  // Step 2.
  RefPtr<ReadableStreamBYOBRequest> request(aController->GetByobRequest());
  return request.forget();
}
}  // namespace streams_abstract

already_AddRefed<ReadableStreamBYOBRequest>
ReadableByteStreamController::GetByobRequest(JSContext* aCx, ErrorResult& aRv) {
  return ReadableByteStreamControllerGetBYOBRequest(aCx, this, aRv);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-get-desired-size
Nullable<double> ReadableByteStreamControllerGetDesiredSize(
    const ReadableByteStreamController* aController) {
  // Step 1.
  ReadableStream::ReaderState state = aController->Stream()->State();

  // Step 2.
  if (state == ReadableStream::ReaderState::Errored) {
    return nullptr;
  }

  // Step 3.
  if (state == ReadableStream::ReaderState::Closed) {
    return 0.0;
  }

  // Step 4.
  return aController->StrategyHWM() - aController->QueueTotalSize();
}

Nullable<double> ReadableByteStreamController::GetDesiredSize() const {
  // Step 1.
  return ReadableByteStreamControllerGetDesiredSize(this);
}

JSObject* ReadableByteStreamController::WrapObject(
    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
  return ReadableByteStreamController_Binding::Wrap(aCx, this, aGivenProto);
}

namespace streams_abstract {

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-invalidate-byob-request
static void ReadableByteStreamControllerInvalidateBYOBRequest(
    ReadableByteStreamController* aController) {
  // Step 1.
  if (!aController->GetByobRequest()) {
    return;
  }

  // Step 2.
  aController->GetByobRequest()->SetController(nullptr);

  // Step 3.
  aController->GetByobRequest()->SetView(nullptr);

  // Step 4.
  aController->SetByobRequest(nullptr);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-pending-pull-intos
void ReadableByteStreamControllerClearPendingPullIntos(
    ReadableByteStreamController* aController) {
  // Step 1.
  ReadableByteStreamControllerInvalidateBYOBRequest(aController);

  // Step 2.
  aController->ClearPendingPullIntos();
}

// https://streams.spec.whatwg.org/#reset-queue
void ResetQueue(ReadableByteStreamController* aContainer) {
  // Step 1. Implied by type.
  // Step 2.
  aContainer->ClearQueue();

  // Step 3.
  aContainer->SetQueueTotalSize(0);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-clear-algorithms
void ReadableByteStreamControllerClearAlgorithms(
    ReadableByteStreamController* aController) {
  // Step 1. Set controller.[[pullAlgorithm]] to undefined.
  // Step 2. Set controller.[[cancelAlgorithm]] to undefined.
  aController->ClearAlgorithms();
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-error
void ReadableByteStreamControllerError(
    ReadableByteStreamController* aController, JS::Handle<JS::Value> aValue,
    ErrorResult& aRv) {
  // Step 1. Let stream be controller.[[stream]].
  ReadableStream* stream = aController->Stream();

  // Step 2. If stream.[[state]] is not "readable", return.
  if (stream->State() != ReadableStream::ReaderState::Readable) {
    return;
  }

  // Step 3. Perform
  // !ReadableByteStreamControllerClearPendingPullIntos(controller).
  ReadableByteStreamControllerClearPendingPullIntos(aController);

  // Step 4. Perform !ResetQueue(controller).
  ResetQueue(aController);

  // Step 5. Perform !ReadableByteStreamControllerClearAlgorithms(controller).
  ReadableByteStreamControllerClearAlgorithms(aController);

  // Step 6. Perform !ReadableStreamError(stream, e).
  AutoJSAPI jsapi;
  if (!jsapi.Init(aController->GetParentObject())) {
    return;
  }
  ReadableStreamError(jsapi.cx(), stream, aValue, aRv);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-close
void ReadableByteStreamControllerClose(
    JSContext* aCx, ReadableByteStreamController* aController,
    ErrorResult& aRv) {
  // Step 1.
  RefPtr<ReadableStream> stream = aController->Stream();

  // Step 2.
  if (aController->CloseRequested() ||
      stream->State() != ReadableStream::ReaderState::Readable) {
    return;
  }

  // Step 3.
  if (aController->QueueTotalSize() > 0) {
    // Step 3.1
    aController->SetCloseRequested(true);
    // Step 3.2
    return;
  }

  // Step 4.
  if (!aController->PendingPullIntos().isEmpty()) {
    // Step 4.1. Let firstPendingPullInto be controller.[[pendingPullIntos]][0].
    PullIntoDescriptor* firstPendingPullInto =
        aController->PendingPullIntos().getFirst();

    // Step 4.2. If the remainder after dividing firstPendingPullInto’s bytes
    // filled by firstPendingPullInto’s element size is not 0,
    if ((firstPendingPullInto->BytesFilled() %
         firstPendingPullInto->ElementSize()) != 0) {
      // Step 4.2.1
      ErrorResult rv;
      rv.ThrowTypeError("Leftover Bytes");

      JS::Rooted<JS::Value> exception(aCx);
      MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &exception));

      // Step 4.2.2
      ReadableByteStreamControllerError(aController, exception, aRv);
      if (aRv.Failed()) {
        return;
      }

      aRv.MightThrowJSException();
      aRv.ThrowJSException(aCx, exception);
      return;
    }
  }

  // Step 5.
  ReadableByteStreamControllerClearAlgorithms(aController);

  // Step 6.
  ReadableStreamClose(aCx, stream, aRv);
}

}  // namespace streams_abstract

// https://streams.spec.whatwg.org/#rbs-controller-close
void ReadableByteStreamController::Close(JSContext* aCx, ErrorResult& aRv) {
  // Step 1.
  if (mCloseRequested) {
    aRv.ThrowTypeError("Close already requested");
    return;
  }

  // Step 2.
  if (Stream()->State() != ReadableStream::ReaderState::Readable) {
    aRv.ThrowTypeError("Closing un-readable stream controller");
    return;
  }

  // Step 3.
  ReadableByteStreamControllerClose(aCx, this, aRv);
}

namespace streams_abstract {

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue-chunk-to-queue
void ReadableByteStreamControllerEnqueueChunkToQueue(
    ReadableByteStreamController* aController,
    JS::Handle<JSObject*> aTransferredBuffer, size_t aByteOffset,
    size_t aByteLength) {
  // Step 1.
  RefPtr<ReadableByteStreamQueueEntry> queueEntry =
      new ReadableByteStreamQueueEntry(aTransferredBuffer, aByteOffset,
                                       aByteLength);
  aController->Queue().insertBack(queueEntry);

  // Step 2.
  aController->AddToQueueTotalSize(double(aByteLength));
}

// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueueclonedchunktoqueue
void ReadableByteStreamControllerEnqueueClonedChunkToQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    JS::Handle<JSObject*> aBuffer, size_t aByteOffset, size_t aByteLength,
    ErrorResult& aRv) {
  // Step 1. Let cloneResult be CloneArrayBuffer(buffer, byteOffset, byteLength,
  // %ArrayBuffer%).
  aRv.MightThrowJSException();
  JS::Rooted<JSObject*> cloneResult(
      aCx, JS::ArrayBufferClone(aCx, aBuffer, aByteOffset, aByteLength));

  // Step 2. If cloneResult is an abrupt completion,
  if (!cloneResult) {
    JS::Rooted<JS::Value> exception(aCx);
    if (!JS_GetPendingException(aCx, &exception)) {
      // Uncatchable exception; we should mark aRv and return.
      aRv.StealExceptionFromJSContext(aCx);
      return;
    }
    JS_ClearPendingException(aCx);

    // Step 2.1. Perform ! ReadableByteStreamControllerError(controller,
    // cloneResult.[[Value]]).
    ReadableByteStreamControllerError(aController, exception, aRv);
    if (aRv.Failed()) {
      return;
    }

    // Step 2.2. Return cloneResult.
    aRv.ThrowJSException(aCx, exception);
    return;
  }

  // Step 3. Perform !
  // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
  // cloneResult.[[Value]], 0, byteLength).
  ReadableByteStreamControllerEnqueueChunkToQueue(aController, cloneResult, 0,
                                                  aByteLength);
}

already_AddRefed<PullIntoDescriptor>
ReadableByteStreamControllerShiftPendingPullInto(
    ReadableByteStreamController* aController);

// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerenqueuedetachedpullintotoqueue
void ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
  // Step 1. Assert: pullIntoDescriptor’s reader type is "none".
  MOZ_ASSERT(aPullIntoDescriptor->GetReaderType() == ReaderType::None);

  // Step 2. If pullIntoDescriptor’s bytes filled > 0,
  // perform ? ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
  // pullIntoDescriptor’s buffer, pullIntoDescriptor’s byte offset,
  // pullIntoDescriptor’s bytes filled).
  if (aPullIntoDescriptor->BytesFilled() > 0) {
    JS::Rooted<JSObject*> buffer(aCx, aPullIntoDescriptor->Buffer());
    ReadableByteStreamControllerEnqueueClonedChunkToQueue(
        aCx, aController, buffer, aPullIntoDescriptor->ByteOffset(),
        aPullIntoDescriptor->BytesFilled(), aRv);
    if (aRv.Failed()) {
      return;
    }
  }

  // Step 3. Perform !
  // ReadableByteStreamControllerShiftPendingPullInto(controller).
  RefPtr<PullIntoDescriptor> discarded =
      ReadableByteStreamControllerShiftPendingPullInto(aController);
  (void)discarded;
}

// https://streams.spec.whatwg.org/#readable-stream-get-num-read-into-requests
static size_t ReadableStreamGetNumReadIntoRequests(ReadableStream* aStream) {
  // Step 1.
  MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));

  // Step 2.
  return aStream->GetReader()->AsBYOB()->ReadIntoRequests().length();
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-should-call-pull
bool ReadableByteStreamControllerShouldCallPull(
    ReadableByteStreamController* aController) {
  // Step 1. Let stream be controller.[[stream]].
  ReadableStream* stream = aController->Stream();

  // Step 2. If stream.[[state]] is not "readable", return false.
  if (stream->State() != ReadableStream::ReaderState::Readable) {
    return false;
  }

  // Step 3. If controller.[[closeRequested]] is true, return false.
  if (aController->CloseRequested()) {
    return false;
  }

  // Step 4. If controller.[[started]] is false, return false.
  if (!aController->Started()) {
    return false;
  }

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

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

  // Step 7. Let desiredSize be
  // ! ReadableByteStreamControllerGetDesiredSize(controller).
  Nullable<double> desiredSize =
      ReadableByteStreamControllerGetDesiredSize(aController);

  // Step 8. Assert: desiredSize is not null.
  MOZ_ASSERT(!desiredSize.IsNull());

  // Step 9. If desiredSize > 0, return true.
  // Step 10. Return false.
  return desiredSize.Value() > 0;
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-call-pull-if-needed
void ReadableByteStreamControllerCallPullIfNeeded(
    JSContext* aCx, ReadableByteStreamController* aController,
    ErrorResult& aRv) {
  // Step 1.
  bool shouldPull = ReadableByteStreamControllerShouldCallPull(aController);

  // Step 2.
  if (!shouldPull) {
    return;
  }

  // Step 3.
  if (aController->Pulling()) {
    aController->SetPullAgain(true);
    return;
  }

  // Step 4.
  MOZ_ASSERT(!aController->PullAgain());

  // Step 5.
  aController->SetPulling(true);

  // Step 6.
  RefPtr<ReadableStreamController> controller(aController);
  RefPtr<UnderlyingSourceAlgorithmsBase> algorithms =
      aController->GetAlgorithms();
  RefPtr<Promise> pullPromise = algorithms->PullCallback(aCx, *controller, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Steps 7+8
  pullPromise->AddCallbacksWithCycleCollectedArgs(
      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
         ReadableByteStreamController* aController)
          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
            // Step 7.1
            aController->SetPulling(false);
            // Step 7.2
            if (aController->PullAgain()) {
              // Step 7.2.1
              aController->SetPullAgain(false);

              // Step 7.2.2
              ReadableByteStreamControllerCallPullIfNeeded(
                  aCx, MOZ_KnownLive(aController), aRv);
            }
          },
      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
         ReadableByteStreamController* aController) {
        // Step 8.1
        ReadableByteStreamControllerError(aController, aValue, aRv);
      },
      RefPtr(aController));
}

bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv);

JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
    JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv);

// https://streams.spec.whatwg.org/#readable-stream-fulfill-read-into-request
MOZ_CAN_RUN_SCRIPT
void ReadableStreamFulfillReadIntoRequest(JSContext* aCx,
                                          ReadableStream* aStream,
                                          JS::Handle<JS::Value> aChunk,
                                          bool done, ErrorResult& aRv) {
  // Step 1. Assert: !ReadableStreamHasBYOBReader(stream) is true.
  MOZ_ASSERT(ReadableStreamHasBYOBReader(aStream));

  // Step 2. Let reader be stream.[[reader]].
  ReadableStreamBYOBReader* reader = aStream->GetReader()->AsBYOB();

  // Step 3. Assert: reader.[[readIntoRequests]] is not empty.
  MOZ_ASSERT(!reader->ReadIntoRequests().isEmpty());

  // Step 4. Let readIntoRequest be reader.[[readIntoRequests]][0].
  // Step 5. Remove readIntoRequest from reader.[[readIntoRequests]].
  RefPtr<ReadIntoRequest> readIntoRequest =
      reader->ReadIntoRequests().popFirst();

  // Step 6. If done is true, perform readIntoRequest’s close steps, given
  // chunk.
  if (done) {
    readIntoRequest->CloseSteps(aCx, aChunk, aRv);
    return;
  }

  // Step 7. Otherwise, perform readIntoRequest’s chunk steps, given chunk.
  readIntoRequest->ChunkSteps(aCx, aChunk, aRv);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-commit-pull-into-descriptor
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerCommitPullIntoDescriptor(
    JSContext* aCx, ReadableStream* aStream,
    PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
  // Step 1. Assert: stream.[[state]] is not "errored".
  MOZ_ASSERT(aStream->State() != ReadableStream::ReaderState::Errored);

  // Step 2. Assert: pullIntoDescriptor.reader type is not "none".
  MOZ_ASSERT(pullIntoDescriptor->GetReaderType() != ReaderType::None);

  // Step 3. Let done be false.
  bool done = false;

  // Step 4. If stream.[[state]] is "closed",
  if (aStream->State() == ReadableStream::ReaderState::Closed) {
    // Step 4.1. Assert: the remainder after dividing pullIntoDescriptor’s bytes
    // filled by pullIntoDescriptor’s element size is 0.
    MOZ_ASSERT((pullIntoDescriptor->BytesFilled() %
                pullIntoDescriptor->ElementSize()) == 0);

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

  // Step 5. Let filledView be !
  // ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor).
  JS::Rooted<JSObject*> filledView(
      aCx, ReadableByteStreamControllerConvertPullIntoDescriptor(
               aCx, pullIntoDescriptor, aRv));
  if (aRv.Failed()) {
    return;
  }
  JS::Rooted<JS::Value> filledViewValue(aCx, JS::ObjectValue(*filledView));

  // Step 6. If pullIntoDescriptor’s reader type is "default",
  if (pullIntoDescriptor->GetReaderType() == ReaderType::Default) {
    // Step 6.1. Perform !ReadableStreamFulfillReadRequest(stream, filledView,
    // done).
    ReadableStreamFulfillReadRequest(aCx, aStream, filledViewValue, done, aRv);
    return;
  }

  // Step 7.1. Assert: pullIntoDescriptor’s reader type is "byob".
  MOZ_ASSERT(pullIntoDescriptor->GetReaderType() == ReaderType::BYOB);

  // Step 7.2 Perform !ReadableStreamFulfillReadIntoRequest(stream, filledView,
  // done).
  ReadableStreamFulfillReadIntoRequest(aCx, aStream, filledViewValue, done,
                                       aRv);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-process-pull-into-descriptors-using-queue
MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    nsTArray<RefPtr<PullIntoDescriptor>>& aFilledPullIntos, ErrorResult& aRv) {
  // Step 1. Assert: controller.[[closeRequested]] is false.
  MOZ_ASSERT(!aController->CloseRequested());

  // Step 2. Let filledPullIntos be a new empty list.
  MOZ_ASSERT(aFilledPullIntos.IsEmpty());

  // Step 3. While controller.[[pendingPullIntos]] is not empty,
  while (!aController->PendingPullIntos().isEmpty()) {
    // Step 3.1. If controller.[[queueTotalSize]] is 0, then break.
    if (aController->QueueTotalSize() == 0) {
      break;
    }

    // Step 3.2. Let pullIntoDescriptor be controller.[[pendingPullIntos]][0].
    RefPtr<PullIntoDescriptor> pullIntoDescriptor =
        aController->PendingPullIntos().getFirst();

    // Step 3.3. If
    // !ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
    // pullIntoDescriptor) is true,
    bool ready = ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
        aCx, aController, pullIntoDescriptor, aRv);
    if (aRv.Failed()) {
      return;
    }

    if (ready) {
      //  Step 3.3.1. Perform
      //  !ReadableByteStreamControllerShiftPendingPullInto(controller).
      RefPtr<PullIntoDescriptor> discardedPullIntoDescriptor =
          ReadableByteStreamControllerShiftPendingPullInto(aController);

      // Step 3.3.2. Append pullIntoDescriptor to filledPullIntos.
      aFilledPullIntos.AppendElement(pullIntoDescriptor);
    }
  }

  // Step 4: Return filledPullIntos. (Implicit)
}

MOZ_CAN_RUN_SCRIPT
void ReadableByteStreamControllerHandleQueueDrain(
    JSContext* aCx, ReadableByteStreamController* aController,
    ErrorResult& aRv);

// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontrollerfillreadrequestfromqueue
MOZ_CAN_RUN_SCRIPT void ReadableByteStreamControllerFillReadRequestFromQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    ReadRequest* aReadRequest, ErrorResult& aRv) {
  // Step 1. Assert: controller.[[queueTotalSize]] > 0.
  MOZ_ASSERT(aController->QueueTotalSize() > 0);
  // Also assert that the queue has a non-zero length;
  MOZ_ASSERT(aController->Queue().length() > 0);

  // Step 2. Let entry be controller.[[queue]][0].
  // Step 3. Remove entry from controller.[[queue]].
  RefPtr<ReadableByteStreamQueueEntry> entry = aController->Queue().popFirst();

  // Assert that we actually got an entry.
  MOZ_ASSERT(entry);

  // Step 4. Set controller.[[queueTotalSize]] to controller.[[queueTotalSize]]
  // − entry’s byte length.
  aController->SetQueueTotalSize(aController->QueueTotalSize() -
                                 double(entry->ByteLength()));

  // Step 5. Perform ! ReadableByteStreamControllerHandleQueueDrain(controller).
  ReadableByteStreamControllerHandleQueueDrain(aCx, aController, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 6. Let view be ! Construct(%Uint8Array%, « entry’s buffer, entry’s
  // byte offset, entry’s byte length »).
  aRv.MightThrowJSException();
  JS::Rooted<JSObject*> buffer(aCx, entry->Buffer());
  JS::Rooted<JSObject*> view(
      aCx, JS_NewUint8ArrayWithBuffer(aCx, buffer, entry->ByteOffset(),
                                      int64_t(entry->ByteLength())));
  if (!view) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }

  // Step 7. Perform readRequest’s chunk steps, given view.
  JS::Rooted<JS::Value> viewValue(aCx, JS::ObjectValue(*view));
  aReadRequest->ChunkSteps(aCx, viewValue, aRv);
}

MOZ_CAN_RUN_SCRIPT void
ReadableByteStreamControllerProcessReadRequestsUsingQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    ErrorResult& aRv) {
  // Step 1. Let reader be controller.[[stream]].[[reader]].
  // Step 2. Assert: reader implements ReadableStreamDefaultReader.
  RefPtr<ReadableStreamDefaultReader> reader =
      aController->Stream()->GetDefaultReader();

  // Step 3. While reader.[[readRequests]] is not empty,
  while (!reader->ReadRequests().isEmpty()) {
    // Step 3.1. If controller.[[queueTotalSize]] is 0, return.
    if (aController->QueueTotalSize() == 0) {
      return;
    }

    // Step 3.2. Let readRequest be reader.[[readRequests]][0].
    // Step 3.3. Remove readRequest from reader.[[readRequests]].
    RefPtr<ReadRequest> readRequest = reader->ReadRequests().popFirst();

    // Step 3.4. Perform !
    // ReadableByteStreamControllerFillReadRequestFromQueue(controller,
    // readRequest).
    ReadableByteStreamControllerFillReadRequestFromQueue(aCx, aController,
                                                         readRequest, aRv);
    if (aRv.Failed()) {
      return;
    }
  }
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue
void ReadableByteStreamControllerEnqueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    JS::Handle<JSObject*> aChunk, ErrorResult& aRv) {
  aRv.MightThrowJSException();

  // Step 1.
  RefPtr<ReadableStream> stream = aController->Stream();

  // Step 2.
  if (aController->CloseRequested() ||
      stream->State() != ReadableStream::ReaderState::Readable) {
    return;
  }

  // Step 3.
  bool isShared;
  JS::Rooted<JSObject*> buffer(
      aCx, JS_GetArrayBufferViewBuffer(aCx, aChunk, &isShared));
  if (!buffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }

  // Step 4.
  size_t byteOffset = JS_GetArrayBufferViewByteOffset(aChunk);

  // Step 5.
  size_t byteLength = JS_GetArrayBufferViewByteLength(aChunk);

  // Step 6.
  if (JS::IsDetachedArrayBufferObject(buffer)) {
    aRv.ThrowTypeError("Detached Array Buffer");
    return;
  }

  // Step 7.
  JS::Rooted<JSObject*> transferredBuffer(aCx,
                                          TransferArrayBuffer(aCx, buffer));
  if (!transferredBuffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }

  // Step 8.
  if (!aController->PendingPullIntos().isEmpty()) {
    // Step 8.1
    RefPtr<PullIntoDescriptor> firstPendingPullInto =
        aController->PendingPullIntos().getFirst();

    // Step 8.2
    JS::Rooted<JSObject*> pendingBuffer(aCx, firstPendingPullInto->Buffer());
    if (JS::IsDetachedArrayBufferObject(pendingBuffer)) {
      aRv.ThrowTypeError("Pending PullInto has detached buffer");
      return;
    }

    // Step 8.3. Perform !
    // ReadableByteStreamControllerInvalidateBYOBRequest(controller).
    ReadableByteStreamControllerInvalidateBYOBRequest(aController);

    // Step 8.4. Set firstPendingPullInto’s buffer to !
    // TransferArrayBuffer(firstPendingPullInto’s buffer).
    pendingBuffer = TransferArrayBuffer(aCx, pendingBuffer);
    if (!pendingBuffer) {
      aRv.StealExceptionFromJSContext(aCx);
      return;
    }
    firstPendingPullInto->SetBuffer(pendingBuffer);

    // Step 8.5. If firstPendingPullInto’s reader type is "none", perform ?
    // ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
    // firstPendingPullInto).
    if (firstPendingPullInto->GetReaderType() == ReaderType::None) {
      ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
          aCx, aController, firstPendingPullInto, aRv);
      if (aRv.Failed()) {
        return;
      }
    }
  }

  // Step 9. If ! ReadableStreamHasDefaultReader(stream) is true,
  if (ReadableStreamHasDefaultReader(stream)) {
    // Step 9.1. Perform !
    // ReadableByteStreamControllerProcessReadRequestsUsingQueue(controller).
    ReadableByteStreamControllerProcessReadRequestsUsingQueue(aCx, aController,
                                                              aRv);
    if (aRv.Failed()) {
      return;
    }

    // Step 9.2. If ! ReadableStreamGetNumReadRequests(stream) is 0,
    if (ReadableStreamGetNumReadRequests(stream) == 0) {
      // Step 9.2.1 Assert: controller.[[pendingPullIntos]] is empty.
      MOZ_ASSERT(aController->PendingPullIntos().isEmpty());

      // Step 9.2.2. Perform !
      // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
      // transferredBuffer, byteOffset, byteLength).
      ReadableByteStreamControllerEnqueueChunkToQueue(
          aController, transferredBuffer, byteOffset, byteLength);

      // Step 9.3. Otherwise,
    } else {
      // Step 9.3.1 Assert: controller.[[queue]] is empty.
      MOZ_ASSERT(aController->Queue().isEmpty());

      // Step 9.3.2. If controller.[[pendingPullIntos]] is not empty,
      if (!aController->PendingPullIntos().isEmpty()) {
        // Step 9.3.2.1. Assert: controller.[[pendingPullIntos]][0]'s reader
        // type is "default".
        MOZ_ASSERT(
            aController->PendingPullIntos().getFirst()->GetReaderType() ==
            ReaderType::Default);

        // Step 9.3.2.2. Perform !
        // ReadableByteStreamControllerShiftPendingPullInto(controller).
        RefPtr<PullIntoDescriptor> pullIntoDescriptor =
            ReadableByteStreamControllerShiftPendingPullInto(aController);
        (void)pullIntoDescriptor;
      }

      // Step 9.3.3. Let transferredView be ! Construct(%Uint8Array%, «
      // transferredBuffer, byteOffset, byteLength »).
      JS::Rooted<JSObject*> transferredView(
          aCx, JS_NewUint8ArrayWithBuffer(aCx, transferredBuffer, byteOffset,
                                          int64_t(byteLength)));
      if (!transferredView) {
        aRv.StealExceptionFromJSContext(aCx);
        return;
      }

      // Step 9.3.4. Perform ! ReadableStreamFulfillReadRequest(stream,
      // transferredView, false).
      JS::Rooted<JS::Value> transferredViewValue(
          aCx, JS::ObjectValue(*transferredView));
      ReadableStreamFulfillReadRequest(aCx, stream, transferredViewValue, false,
                                       aRv);
      if (aRv.Failed()) {
        return;
      }
    }

    // Step 10. Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true,
  } else if (ReadableStreamHasBYOBReader(stream)) {
    // Step 10.1. Perform !
    // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
    // transferredBuffer, byteOffset, byteLength).
    ReadableByteStreamControllerEnqueueChunkToQueue(
        aController, transferredBuffer, byteOffset, byteLength);

    // Step 10.2. Let filledPullIntos be the result of performing !
    // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
    nsTArray<RefPtr<PullIntoDescriptor>> filledPullIntos;
    ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
        aCx, aController, filledPullIntos, aRv);
    if (aRv.Failed()) {
      return;
    }

    // Step 10.3. For each filledPullInto of filledPullIntos,
    for (auto& filledPullInto : filledPullIntos) {
      // Step 10.3.1. Perform !
      // ReadableByteStreamControllerCommitPullIntoDescriptor(stream,
      // filledPullInto).
      ReadableByteStreamControllerCommitPullIntoDescriptor(
          aCx, stream, MOZ_KnownLive(filledPullInto), aRv);
      if (aRv.Failed()) {
        return;
      }
    }

    // Step 11. Otherwise,
  } else {
    // Step 11.1. Assert: ! IsReadableStreamLocked(stream) is false.
    MOZ_ASSERT(!IsReadableStreamLocked(stream));

    // Step 11.2. Perform !
    // ReadableByteStreamControllerEnqueueChunkToQueue(controller,
    // transferredBuffer, byteOffset, byteLength).
    ReadableByteStreamControllerEnqueueChunkToQueue(
        aController, transferredBuffer, byteOffset, byteLength);
  }

  // Step 12. Perform !
  // ReadableByteStreamControllerCallPullIfNeeded(controller).
  ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}

}  // namespace streams_abstract

// https://streams.spec.whatwg.org/#rbs-controller-enqueue
void ReadableByteStreamController::Enqueue(JSContext* aCx,
                                           const ArrayBufferView& aChunk,
                                           ErrorResult& aRv) {
  // Step 1.
  JS::Rooted<JSObject*> chunk(aCx, aChunk.Obj());
  if (JS_GetArrayBufferViewByteLength(chunk) == 0) {
    aRv.ThrowTypeError("Zero Length View");
    return;
  }

  // Step 2.
  bool isShared;
  JS::Rooted<JSObject*> viewedArrayBuffer(
      aCx, JS_GetArrayBufferViewBuffer(aCx, chunk, &isShared));
  if (!viewedArrayBuffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }

  if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
    aRv.ThrowTypeError("Zero Length Buffer");
    return;
  }

  // Step 3.
  if (CloseRequested()) {
    aRv.ThrowTypeError("close requested");
    return;
  }

  // Step 4.
  if (Stream()->State() != ReadableStream::ReaderState::Readable) {
    aRv.ThrowTypeError("Not Readable");
    return;
  }

  // Step 5.
  ReadableByteStreamControllerEnqueue(aCx, this, chunk, aRv);
}

// https://streams.spec.whatwg.org/#rbs-controller-error
void ReadableByteStreamController::Error(JSContext* aCx,
                                         JS::Handle<JS::Value> aErrorValue,
                                         ErrorResult& aRv) {
  // Step 1.
  ReadableByteStreamControllerError(this, aErrorValue, aRv);
}

// https://streams.spec.whatwg.org/#rbs-controller-private-cancel
already_AddRefed<Promise> ReadableByteStreamController::CancelSteps(
    JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
  // Step 1.
  ReadableByteStreamControllerClearPendingPullIntos(this);

  // Step 2.
  ResetQueue(this);

  // Step 3.
  Optional<JS::Handle<JS::Value>> reason(aCx, aReason);
  RefPtr<UnderlyingSourceAlgorithmsBase> algorithms = mAlgorithms;
  RefPtr<Promise> result = algorithms->CancelCallback(aCx, reason, aRv);
  if (NS_WARN_IF(aRv.Failed())) {
    return nullptr;
  }
  // Step 4.
  ReadableByteStreamControllerClearAlgorithms(this);

  // Step 5.
  return result.forget();
}

namespace streams_abstract {
// https://streams.spec.whatwg.org/#readable-byte-stream-controller-handle-queue-drain
void ReadableByteStreamControllerHandleQueueDrain(
    JSContext* aCx, ReadableByteStreamController* aController,
    ErrorResult& aRv) {
  // Step 1.
  MOZ_ASSERT(aController->Stream()->State() ==
             ReadableStream::ReaderState::Readable);

  // Step 2.
  if (aController->QueueTotalSize() == 0 && aController->CloseRequested()) {
    // Step 2.1
    ReadableByteStreamControllerClearAlgorithms(aController);

    // Step 2.2
    RefPtr<ReadableStream> stream = aController->Stream();
    ReadableStreamClose(aCx, stream, aRv);
    return;
  }

  // Step 3.1
  ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}
}  // namespace streams_abstract

// https://streams.spec.whatwg.org/#rbs-controller-private-pull
void ReadableByteStreamController::PullSteps(JSContext* aCx,
                                             ReadRequest* aReadRequest,
                                             ErrorResult& aRv) {
  // Step 1. Let stream be this.[[stream]].
  ReadableStream* stream = Stream();

  // Step 2. Assert: ! ReadableStreamHasDefaultReader(stream) is true.
  MOZ_ASSERT(ReadableStreamHasDefaultReader(stream));

  // Step 3. If this.[[queueTotalSize]] > 0,
  if (QueueTotalSize() > 0) {
    // Step 3.1. Assert: ! ReadableStreamGetNumReadRequests ( stream ) is 0.
    MOZ_ASSERT(ReadableStreamGetNumReadRequests(stream) == 0);

    // Step 3.2. Perform !
    // ReadableByteStreamControllerFillReadRequestFromQueue(this, readRequest).
    ReadableByteStreamControllerFillReadRequestFromQueue(aCx, this,
                                                         aReadRequest, aRv);

    // Step 3.3. Return.
    return;
  }

  // Step 4. Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]].
  Maybe<uint64_t> autoAllocateChunkSize = AutoAllocateChunkSize();

  // Step 5. If autoAllocateChunkSize is not undefined,
  if (autoAllocateChunkSize) {
    // Step 5.1.  Let buffer be Construct(%ArrayBuffer%, « autoAllocateChunkSize
    // »).
    aRv.MightThrowJSException();
    JS::Rooted<JSObject*> buffer(
        aCx, JS::NewArrayBuffer(aCx, *autoAllocateChunkSize));

    // Step 5.2. If buffer is an abrupt completion,
    if (!buffer) {
      // Step 5.2.1. Perform readRequest’s error steps, given buffer.[[Value]].
      JS::Rooted<JS::Value> bufferError(aCx);
      if (!JS_GetPendingException(aCx, &bufferError)) {
        // Uncatchable exception; we should mark aRv and return.
        aRv.StealExceptionFromJSContext(aCx);
        return;
      }

      // It's not explicitly stated, but I assume the intention here is that
      // we perform a normal completion here.
      JS_ClearPendingException(aCx);

      aReadRequest->ErrorSteps(aCx, bufferError, aRv);

      // Step 5.2.2. Return.
      return;
    }

    // Step 5.3 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"
    RefPtr<PullIntoDescriptor> pullIntoDescriptor = new PullIntoDescriptor(
        buffer, /* aBufferByteLength */ *autoAllocateChunkSize,
        /*aByteOffset */ 0, /* aByteLength */ *autoAllocateChunkSize,
        /* aBytesFilled */ 0, /* aMinimumFill */ 1, /* aElementSize */ 1,
        PullIntoDescriptor::Constructor::Uint8, ReaderType::Default);

    //  Step 5.4. Append pullIntoDescriptor to this.[[pendingPullIntos]].
    PendingPullIntos().insertBack(pullIntoDescriptor);
  }

  // Step 6. Perform ! ReadableStreamAddReadRequest(stream, readRequest).
  ReadableStreamAddReadRequest(stream, aReadRequest);

  // Step 7. Perform ! ReadableByteStreamControllerCallPullIfNeeded(this).
  ReadableByteStreamControllerCallPullIfNeeded(aCx, this, aRv);
}

// https://streams.spec.whatwg.org/#abstract-opdef-readablebytestreamcontroller-releasesteps
void ReadableByteStreamController::ReleaseSteps() {
  // Step 1. If this.[[pendingPullIntos]] is not empty,
  if (!PendingPullIntos().isEmpty()) {
    // Step 1.1. Let firstPendingPullInto be this.[[pendingPullIntos]][0].
    RefPtr<PullIntoDescriptor> firstPendingPullInto =
        PendingPullIntos().popFirst();

    // Step 1.2. Set firstPendingPullInto’s reader type to "none".
    firstPendingPullInto->SetReaderType(ReaderType::None);

    // Step 1.3. Set this.[[pendingPullIntos]] to the list «
    // firstPendingPullInto ».
    PendingPullIntos().clear();
    PendingPullIntos().insertBack(firstPendingPullInto);
  }
}

namespace streams_abstract {

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-shift-pending-pull-into
already_AddRefed<PullIntoDescriptor>
ReadableByteStreamControllerShiftPendingPullInto(
    ReadableByteStreamController* aController) {
  // Step 1.
  MOZ_ASSERT(!aController->GetByobRequest());

  // Step 2 + 3
  RefPtr<PullIntoDescriptor> descriptor =
      aController->PendingPullIntos().popFirst();

  // Step 4.
  return descriptor.forget();
}

JSObject* ConstructFromPullIntoConstructor(
    JSContext* aCx, PullIntoDescriptor::Constructor constructor,
    JS::Handle<JSObject*> buffer, size_t byteOffset, size_t length) {
  switch (constructor) {
    case PullIntoDescriptor::Constructor::DataView:
      return JS_NewDataView(aCx, buffer, byteOffset, length);
      break;

#define CONSTRUCT_TYPED_ARRAY_TYPE(ExternalT, NativeT, Name)      \
  case PullIntoDescriptor::Constructor::Name:                     \
    return JS_New##Name##ArrayWithBuffer(aCx, buffer, byteOffset, \
                                         int64_t(length));        \
    break;

      JS_FOR_EACH_TYPED_ARRAY(CONSTRUCT_TYPED_ARRAY_TYPE)

#undef CONSTRUCT_TYPED_ARRAY_TYPE

    default:
      MOZ_ASSERT_UNREACHABLE("Unknown PullIntoDescriptor::Constructor");
      return nullptr;
  }
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-convert-pull-into-descriptor
JSObject* ReadableByteStreamControllerConvertPullIntoDescriptor(
    JSContext* aCx, PullIntoDescriptor* pullIntoDescriptor, ErrorResult& aRv) {
  // Step 1. Let bytesFilled be pullIntoDescriptor’s bytes filled.
  uint64_t bytesFilled = pullIntoDescriptor->BytesFilled();

  // Step 2. Let elementSize be pullIntoDescriptor’s element size.
  uint64_t elementSize = pullIntoDescriptor->ElementSize();

  // Step 3. Assert: bytesFilled ≤ pullIntoDescriptor’s byte length.
  MOZ_ASSERT(bytesFilled <= pullIntoDescriptor->ByteLength());

  // Step 4. Assert: the remainder after dividing bytesFilled by elementSize is
  // 0.
  MOZ_ASSERT(bytesFilled % elementSize == 0);

  // Step 5. Let buffer be ! TransferArrayBuffer(pullIntoDescriptor’s buffer).
  aRv.MightThrowJSException();
  JS::Rooted<JSObject*> srcBuffer(aCx, pullIntoDescriptor->Buffer());
  JS::Rooted<JSObject*> buffer(aCx, TransferArrayBuffer(aCx, srcBuffer));
  if (!buffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return nullptr;
  }

  // Step 6. Return ! Construct(pullIntoDescriptor’s view constructor,
  //  « buffer, pullIntoDescriptor’s byte offset, bytesFilled ÷ elementSize »).
  JS::Rooted<JSObject*> res(
      aCx, ConstructFromPullIntoConstructor(
               aCx, pullIntoDescriptor->ViewConstructor(), buffer,
               pullIntoDescriptor->ByteOffset(), bytesFilled / elementSize));
  if (!res) {
    aRv.StealExceptionFromJSContext(aCx);
    return nullptr;
  }
  return res;
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-closed-state
MOZ_CAN_RUN_SCRIPT
static void ReadableByteStreamControllerRespondInClosedState(
    JSContext* aCx, ReadableByteStreamController* aController,
    RefPtr<PullIntoDescriptor>& aFirstDescriptor, ErrorResult& aRv) {
  // Step 1. Assert: the remainder after dividing firstDescriptor’s bytes filled
  // by firstDescriptor’s element size is 0.
  MOZ_ASSERT(
      (aFirstDescriptor->BytesFilled() % aFirstDescriptor->ElementSize()) == 0);

  // Step 2. If firstDescriptor’s reader type is "none",
  // perform ! ReadableByteStreamControllerShiftPendingPullInto(controller).
  if (aFirstDescriptor->GetReaderType() == ReaderType::None) {
    RefPtr<PullIntoDescriptor> discarded =
        ReadableByteStreamControllerShiftPendingPullInto(aController);
    (void)discarded;
  }

  // Step 3. Let stream be controller.[[stream]].
  RefPtr<ReadableStream> stream = aController->Stream();

  // Step 4. If ! ReadableStreamHasBYOBReader(stream) is true,
  if (ReadableStreamHasBYOBReader(stream)) {
    // Step 4.1. Let filledPullIntos be a new empty list.
    nsTArray<RefPtr<PullIntoDescriptor>> filledPullIntos;

    // Step 4.2. While |filledPullInto|'s [=list/size=] < !
    // ReadableStreamGetNumReadIntoRequests(stream),
    while (filledPullIntos.Length() <
           ReadableStreamGetNumReadIntoRequests(stream)) {
      // Step 4.2.1. Let pullIntoDescriptor be !
      // ReadableByteStreamControllerShiftPendingPullInto(controller).
      RefPtr<PullIntoDescriptor> pullIntoDescriptor =
          ReadableByteStreamControllerShiftPendingPullInto(aController);

      // Step 4.2.2. Append pullIntoDescriptor to filledPullIntos.
      filledPullIntos.AppendElement(pullIntoDescriptor);
    }

    // Step 4.3. For each filledPullInto of filledPullIntos,
    for (auto& filledPullInto : filledPullIntos) {
      // Step 4.3.1. Perform !
      // ReadableByteStreamControllerCommitPullIntoDescriptor(stream,
      // filledPullInto).
      ReadableByteStreamControllerCommitPullIntoDescriptor(
          aCx, stream, MOZ_KnownLive(filledPullInto), aRv);
      if (aRv.Failed()) {
        return;
      }
    }
  }
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-head-pull-into-descriptor
void ReadableByteStreamControllerFillHeadPullIntoDescriptor(
    ReadableByteStreamController* aController, size_t aSize,
    PullIntoDescriptor* aPullIntoDescriptor) {
  // Step 1. Assert: either controller.[[pendingPullIntos]] is empty, or
  // controller.[[pendingPullIntos]][0] is pullIntoDescriptor.
  MOZ_ASSERT(aController->PendingPullIntos().isEmpty() ||
             aController->PendingPullIntos().getFirst() == aPullIntoDescriptor);

  // Step 2. Assert: controller.[[byobRequest]] is null.
  MOZ_ASSERT(!aController->GetByobRequest());

  // Step 3. Set pullIntoDescriptor’s bytes filled to bytes filled + size.
  aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() +
                                      aSize);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-in-readable-state
MOZ_CAN_RUN_SCRIPT
static void ReadableByteStreamControllerRespondInReadableState(
    JSContext* aCx, ReadableByteStreamController* aController,
    uint64_t aBytesWritten, PullIntoDescriptor* aPullIntoDescriptor,
    ErrorResult& aRv) {
  // Step 1. Assert: pullIntoDescriptor’s bytes filled + bytesWritten ≤
  // pullIntoDescriptor’s byte length.
  MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() + aBytesWritten <=
             aPullIntoDescriptor->ByteLength());

  // Step 2. Perform
  // !ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller,
  // bytesWritten, pullIntoDescriptor).
  ReadableByteStreamControllerFillHeadPullIntoDescriptor(
      aController, aBytesWritten, aPullIntoDescriptor);

  // Step 3. If pullIntoDescriptor’s reader type is "none",
  if (aPullIntoDescriptor->GetReaderType() == ReaderType::None) {
    // Step 3.1. Perform ?
    // ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(controller,
    // pullIntoDescriptor).
    ReadableByteStreamControllerEnqueueDetachedPullIntoToQueue(
        aCx, aController, aPullIntoDescriptor, aRv);
    if (aRv.Failed()) {
      return;
    }

    // Step 3.2. Let filledPullIntos be the result of performing !
    // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
    nsTArray<RefPtr<PullIntoDescriptor>> filledPullIntos;
    ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
        aCx, aController, filledPullIntos, aRv);
    if (aRv.Failed()) {
      return;
    }

    // Step 3.3. For each filledPullInto of filledPullIntos,
    for (auto& filledPullInto : filledPullIntos) {
      // Step 3.3.1. Perform !
      // ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
      // filledPullInto).
      ReadableByteStreamControllerCommitPullIntoDescriptor(
          aCx, MOZ_KnownLive(aController->Stream()),
          MOZ_KnownLive(filledPullInto), aRv);
      if (aRv.Failed()) {
        return;
      }
    }

    // Step 3.4. Return.
    return;
  }

  // Step 4. If pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s minimum
  // fill, return.
  if (aPullIntoDescriptor->BytesFilled() < aPullIntoDescriptor->MinimumFill()) {
    return;
  }

  // Step 5. Perform
  // !ReadableByteStreamControllerShiftPendingPullInto(controller).
  RefPtr<PullIntoDescriptor> pullIntoDescriptor =
      ReadableByteStreamControllerShiftPendingPullInto(aController);
  (void)pullIntoDescriptor;

  // Step 6. Let remainderSize be the remainder after dividing
  // pullIntoDescriptor’s bytes filled by pullIntoDescriptor’s element size.
  size_t remainderSize =
      aPullIntoDescriptor->BytesFilled() % aPullIntoDescriptor->ElementSize();

  // Step 7. If remainderSize > 0,
  if (remainderSize > 0) {
    // Step 7.1. Let end be pullIntoDescriptor’s byte offset +
    // pullIntoDescriptor’s bytes filled.
    size_t end =
        aPullIntoDescriptor->ByteOffset() + aPullIntoDescriptor->BytesFilled();

    // Step 7.2. Perform ?
    // ReadableByteStreamControllerEnqueueClonedChunkToQueue(controller,
    // pullIntoDescriptor’s buffer, end − remainderSize, remainderSize).
    JS::Rooted<JSObject*> pullIntoBuffer(aCx, aPullIntoDescriptor->Buffer());
    ReadableByteStreamControllerEnqueueClonedChunkToQueue(
        aCx, aController, pullIntoBuffer, end - remainderSize, remainderSize,
        aRv);
    if (aRv.Failed()) {
      return;
    }
  }

  // Step 8. Set pullIntoDescriptor’s bytes filled to pullIntoDescriptor’s bytes
  // filled − remainderSize.
  aPullIntoDescriptor->SetBytesFilled(aPullIntoDescriptor->BytesFilled() -
                                      remainderSize);

  // Step 9. Let filledPullIntos be the result of performing !
  // ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller).
  nsTArray<RefPtr<PullIntoDescriptor>> filledPullIntos;
  ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(
      aCx, aController, filledPullIntos, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 10. Perform
  // !ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
  // pullIntoDescriptor).
  RefPtr<ReadableStream> stream(aController->Stream());
  ReadableByteStreamControllerCommitPullIntoDescriptor(
      aCx, stream, aPullIntoDescriptor, aRv);
  if (aRv.Failed()) {
    return;
  }

  // Step 11. For each filledPullInto of filledPullIntos,
  for (auto& filledPullInto : filledPullIntos) {
    // Step 11.1. Perform !
    // ReadableByteStreamControllerCommitPullIntoDescriptor(controller.[[stream]],
    // filledPullInto).
    ReadableByteStreamControllerCommitPullIntoDescriptor(
        aCx, stream, MOZ_KnownLive(filledPullInto), aRv);
    if (aRv.Failed()) {
      return;
    }
  }
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-internal
void ReadableByteStreamControllerRespondInternal(
    JSContext* aCx, ReadableByteStreamController* aController,
    uint64_t aBytesWritten, ErrorResult& aRv) {
  // Step 1.
  RefPtr<PullIntoDescriptor> firstDescriptor =
      aController->PendingPullIntos().getFirst();

  // Step 2.
  JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
#ifdef DEBUG
  bool canTransferBuffer = CanTransferArrayBuffer(aCx, buffer, aRv);
  MOZ_ASSERT(!aRv.Failed());
  MOZ_ASSERT(canTransferBuffer);
#endif

  // Step 3.
  ReadableByteStreamControllerInvalidateBYOBRequest(aController);

  // Step 4.
  auto state = aController->Stream()->State();

  // Step 5.
  if (state == ReadableStream::ReaderState::Closed) {
    // Step 5.1
    MOZ_ASSERT(aBytesWritten == 0);

    // Step 5.2
    ReadableByteStreamControllerRespondInClosedState(aCx, aController,
                                                     firstDescriptor, aRv);
    if (aRv.Failed()) {
      return;
    }
  } else {
    // Step 6.1
    MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);

    // Step 6.2.
    MOZ_ASSERT(aBytesWritten > 0);

    // Step 6.3
    ReadableByteStreamControllerRespondInReadableState(
        aCx, aController, aBytesWritten, firstDescriptor, aRv);
    if (aRv.Failed()) {
      return;
    }
  }
  // Step 7.
  ReadableByteStreamControllerCallPullIfNeeded(aCx, aController, aRv);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond
void ReadableByteStreamControllerRespond(
    JSContext* aCx, ReadableByteStreamController* aController,
    uint64_t aBytesWritten, ErrorResult& aRv) {
  // Step 1.
  MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());

  // Step 2.
  PullIntoDescriptor* firstDescriptor =
      aController->PendingPullIntos().getFirst();

  // Step 3.
  auto state = aController->Stream()->State();

  // Step 4.
  if (state == ReadableStream::ReaderState::Closed) {
    // Step 4.1
    if (aBytesWritten != 0) {
      aRv.ThrowTypeError("bytesWritten not zero on closed stream");
      return;
    }
  } else {
    // Step 5.1
    MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);

    // Step 5.2
    if (aBytesWritten == 0) {
      aRv.ThrowTypeError("bytesWritten 0");
      return;
    }

    // Step 5.3
    if (firstDescriptor->BytesFilled() + aBytesWritten >
        firstDescriptor->ByteLength()) {
      aRv.ThrowRangeError("bytesFilled + bytesWritten > byteLength");
      return;
    }
  }

  // Step 6.
  aRv.MightThrowJSException();
  JS::Rooted<JSObject*> buffer(aCx, firstDescriptor->Buffer());
  JS::Rooted<JSObject*> transferredBuffer(aCx,
                                          TransferArrayBuffer(aCx, buffer));
  if (!transferredBuffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }
  firstDescriptor->SetBuffer(transferredBuffer);

  // Step 7.
  ReadableByteStreamControllerRespondInternal(aCx, aController, aBytesWritten,
                                              aRv);
}

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-respond-with-new-view
void ReadableByteStreamControllerRespondWithNewView(
    JSContext* aCx, ReadableByteStreamController* aController,
    JS::Handle<JSObject*> aView, ErrorResult& aRv) {
  aRv.MightThrowJSException();

  // Step 1.
  MOZ_ASSERT(!aController->PendingPullIntos().isEmpty());

  // Step 2.
  bool isSharedMemory;
  JS::Rooted<JSObject*> viewedArrayBuffer(
      aCx, JS_GetArrayBufferViewBuffer(aCx, aView, &isSharedMemory));
  if (!viewedArrayBuffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }
  MOZ_ASSERT(!JS::IsDetachedArrayBufferObject(viewedArrayBuffer));

  // Step 3.
  RefPtr<PullIntoDescriptor> firstDescriptor =
      aController->PendingPullIntos().getFirst();

  // Step 4.
  ReadableStream::ReaderState state = aController->Stream()->State();

  // Step 5.
  if (state == ReadableStream::ReaderState::Closed) {
    // Step 5.1
    if (JS_GetArrayBufferViewByteLength(aView) != 0) {
      aRv.ThrowTypeError("View has non-zero length in closed stream");
      return;
    }
  } else {
    // Step 6.1
    MOZ_ASSERT(state == ReadableStream::ReaderState::Readable);

    // Step 6.2
    if (JS_GetArrayBufferViewByteLength(aView) == 0) {
      aRv.ThrowTypeError("View has zero length in readable stream");
      return;
    }
  }

  // Step 7.
  if (firstDescriptor->ByteOffset() + firstDescriptor->BytesFilled() !=
      JS_GetArrayBufferViewByteOffset(aView)) {
    aRv.ThrowRangeError("Invalid Offset");
    return;
  }

  // Step 8.
  if (firstDescriptor->BufferByteLength() !=
      JS::GetArrayBufferByteLength(viewedArrayBuffer)) {
    aRv.ThrowRangeError("Mismatched buffer byte lengths");
    return;
  }

  // Step 9.
  if (firstDescriptor->BytesFilled() + JS_GetArrayBufferViewByteLength(aView) >
      firstDescriptor->ByteLength()) {
    aRv.ThrowRangeError("Too many bytes");
    return;
  }

  // Step 10. Let viewByteLength be view.[[ByteLength]].
  size_t viewByteLength = JS_GetArrayBufferViewByteLength(aView);

  // Step 11. Set firstDescriptor’s buffer to ?
  // TransferArrayBuffer(view.[[ViewedArrayBuffer]]).
  JS::Rooted<JSObject*> transferedBuffer(
      aCx, TransferArrayBuffer(aCx, viewedArrayBuffer));
  if (!transferedBuffer) {
    aRv.StealExceptionFromJSContext(aCx);
    return;
  }
  firstDescriptor->SetBuffer(transferedBuffer);

  // Step 12. Perform ? ReadableByteStreamControllerRespondInternal(controller,
  // viewByteLength).
  ReadableByteStreamControllerRespondInternal(aCx, aController, viewByteLength,
                                              aRv);
}

#ifdef DEBUG
// https://streams.spec.whatwg.org/#abstract-opdef-cancopydatablockbytes
bool CanCopyDataBlockBytes(JS::Handle<JSObject*> aToBuffer, size_t aToIndex,
                           JS::Handle<JSObject*> aFromBuffer, size_t aFromIndex,
                           size_t aCount) {
  // Step 1. Assert: toBuffer is an Object.
  // Step 2. Assert: toBuffer has an [[ArrayBufferData]] internal slot.
  MOZ_ASSERT(JS::IsArrayBufferObject(aToBuffer));

  // Step 3. Assert: fromBuffer is an Object.
  // Step 4. Assert: fromBuffer has an [[ArrayBufferData]] internal slot.
  MOZ_ASSERT(JS::IsArrayBufferObject(aFromBuffer));

  // Step 5. If toBuffer is fromBuffer, return false.
  // Note: JS API makes it safe to just compare the pointers.
  if (aToBuffer == aFromBuffer) {
    return false;
  }

  // Step 6. If ! IsDetachedBuffer(toBuffer) is true, return false.
  if (JS::IsDetachedArrayBufferObject(aToBuffer)) {
    return false;
  }

  // Step 7. If ! IsDetachedBuffer(fromBuffer) is true, return false.
  if (JS::IsDetachedArrayBufferObject(aFromBuffer)) {
    return false;
  }

  // Step 8. If toIndex + count > toBuffer.[[ArrayBufferByteLength]], return
  // false.
  if (aToIndex + aCount > JS::GetArrayBufferByteLength(aToBuffer)) {
    return false;
  }
  // (Not in the spec) also check overflow.
  if (aToIndex + aCount < aToIndex) {
    return false;
  }

  // Step 9. If fromIndex + count > fromBuffer.[[ArrayBufferByteLength]], return
  // false.
  if (aFromIndex + aCount > JS::GetArrayBufferByteLength(aFromBuffer)) {
    return false;
  }
  // (Not in the spec) also check overflow.
  if (aFromIndex + aCount < aFromIndex) {
    return false;
  }

  // Step 10. Return true.
  return true;
}
#endif

// https://streams.spec.whatwg.org/#readable-byte-stream-controller-fill-pull-into-descriptor-from-queue
bool ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(
    JSContext* aCx, ReadableByteStreamController* aController,
    PullIntoDescriptor* aPullIntoDescriptor, ErrorResult& aRv) {
  // Step 1. Let maxBytesToCopy be min(controller.[[queueTotalSize]],
  // pullIntoDescriptor’s byte length − pullIntoDescriptor’s bytes filled).
  size_t maxBytesToCopy =
      std::min(static_cast<size_t>(aController->QueueTotalSize()),
               static_cast<size_t>((aPullIntoDescriptor->ByteLength() -
                                    aPullIntoDescriptor->BytesFilled())));

  // Step 2. Let maxBytesFilled be pullIntoDescriptor’s bytes filled +
  // maxBytesToCopy.
  size_t maxBytesFilled = aPullIntoDescriptor->BytesFilled() + maxBytesToCopy;

  // Step 3. Let totalBytesToCopyRemaining be maxBytesToCopy.
  size_t totalBytesToCopyRemaining = maxBytesToCopy;

  // Step 4. Let ready be false.
  bool ready = false;

  // Step 5. Assert: ! IsDetachedBuffer(pullIntoDescriptor ’s buffer) is false.
  MOZ_ASSERT(!JS::IsDetachedArrayBufferObject(aPullIntoDescriptor->Buffer()));

  // Step 6. Assert: pullIntoDescriptor’s bytes filled < pullIntoDescriptor’s
  // minimum fill.
  MOZ_ASSERT(aPullIntoDescriptor->BytesFilled() <
             aPullIntoDescriptor->MinimumFill());

  // Step 7. Let remainderBytes be the remainder after dividing maxBytesFilled
  // by pullIntoDescriptor’s element size.
  size_t remainderBytes = maxBytesFilled % aPullIntoDescriptor->ElementSize();

  // Step 8. Let maxAlignedBytes be maxBytesFilled − remainderBytes.
  size_t maxAlignedBytes = maxBytesFilled - remainderBytes;

  // Step 9. If maxAlignedBytes ≥ pullIntoDescriptor’s minimum fill,
  if (maxAlignedBytes >= aPullIntoDescriptor->MinimumFill()) {
    // Step 9.1. Set totalBytesToCopyRemaining to maxAlignedBytes −
    // pullIntoDescriptor’s bytes filled.
    totalBytesToCopyRemaining =
        maxAlignedBytes - aPullIntoDescriptor->BytesFilled();
    // Step 9.2. Set ready to true.
    ready = true;
  }

  // Step 10. Let queue be controller.[[queue]].
  LinkedList<RefPtr<ReadableByteStreamQueueEntry>>& queue =
      aController->Queue();

  // Step 11. While totalBytesToCopyRemaining > 0,
  while (totalBytesToCopyRemaining > 0) {
    // Step 11.1  Let headOfQueue be queue[0].
    ReadableByteStreamQueueEntry* headOfQueue = queue.getFirst();

    // Step 11.2. Let bytesToCopy be min(totalBytesToCopyRemaining,
    // headOfQueue’s byte length).
--> --------------------

--> maximum size reached

--> --------------------

97%


¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge