tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ReadableStreamBYOBReader.cpp (13987B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/ReadableStreamBYOBReader.h"
      8 
      9 #include "ReadIntoRequest.h"
     10 #include "js/ArrayBuffer.h"
     11 #include "js/experimental/TypedData.h"
     12 #include "mozilla/dom/ReadableStream.h"
     13 #include "mozilla/dom/ReadableStreamBYOBReader.h"
     14 #include "mozilla/dom/ReadableStreamBYOBReaderBinding.h"
     15 #include "mozilla/dom/ReadableStreamGenericReader.h"
     16 #include "mozilla/dom/RootedDictionary.h"
     17 #include "nsCOMPtr.h"
     18 #include "nsISupportsImpl.h"
     19 
     20 // Temporary Includes
     21 #include "mozilla/dom/ReadableByteStreamController.h"
     22 #include "mozilla/dom/ReadableStreamBYOBRequest.h"
     23 
     24 namespace mozilla::dom {
     25 
     26 using namespace streams_abstract;
     27 
     28 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(ReadableStreamBYOBReader,
     29                                                ReadableStreamGenericReader,
     30                                                mReadIntoRequests)
     31 NS_IMPL_ADDREF_INHERITED(ReadableStreamBYOBReader, ReadableStreamGenericReader)
     32 NS_IMPL_RELEASE_INHERITED(ReadableStreamBYOBReader, ReadableStreamGenericReader)
     33 
     34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamBYOBReader)
     35  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     36 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader)
     37 
     38 ReadableStreamBYOBReader::ReadableStreamBYOBReader(nsISupports* aGlobal)
     39    : ReadableStreamGenericReader(do_QueryInterface(aGlobal)) {}
     40 
     41 JSObject* ReadableStreamBYOBReader::WrapObject(
     42    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
     43  return ReadableStreamBYOBReader_Binding::Wrap(aCx, this, aGivenProto);
     44 }
     45 
     46 // https://streams.spec.whatwg.org/#set-up-readable-stream-byob-reader
     47 void SetUpReadableStreamBYOBReader(ReadableStreamBYOBReader* reader,
     48                                   ReadableStream& stream, ErrorResult& rv) {
     49  // Step 1. If !IsReadableStreamLocked(stream) is true, throw a TypeError
     50  // exception.
     51  if (IsReadableStreamLocked(&stream)) {
     52    rv.ThrowTypeError("Trying to read locked stream");
     53    return;
     54  }
     55 
     56  // Step 2. If stream.[[controller]] does not implement
     57  // ReadableByteStreamController, throw a TypeError exception.
     58  if (!stream.Controller()->IsByte()) {
     59    rv.ThrowTypeError("Trying to read with incompatible controller");
     60    return;
     61  }
     62 
     63  // Step 3. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
     64  ReadableStreamReaderGenericInitialize(reader, &stream);
     65 
     66  // Step 4. Set reader.[[readIntoRequests]] to a new empty list.
     67  reader->ReadIntoRequests().clear();
     68 }
     69 
     70 // https://streams.spec.whatwg.org/#byob-reader-constructor
     71 /* static */ already_AddRefed<ReadableStreamBYOBReader>
     72 ReadableStreamBYOBReader::Constructor(const GlobalObject& global,
     73                                      ReadableStream& stream, ErrorResult& rv) {
     74  nsCOMPtr<nsIGlobalObject> globalObject =
     75      do_QueryInterface(global.GetAsSupports());
     76  RefPtr<ReadableStreamBYOBReader> reader =
     77      new ReadableStreamBYOBReader(globalObject);
     78 
     79  // Step 1.
     80  SetUpReadableStreamBYOBReader(reader, stream, rv);
     81  if (rv.Failed()) {
     82    return nullptr;
     83  }
     84 
     85  return reader.forget();
     86 }
     87 
     88 struct Read_ReadIntoRequest final : public ReadIntoRequest {
     89  NS_DECL_ISUPPORTS_INHERITED
     90  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Read_ReadIntoRequest,
     91                                           ReadIntoRequest)
     92 
     93  RefPtr<Promise> mPromise;
     94 
     95  explicit Read_ReadIntoRequest(Promise* aPromise) : mPromise(aPromise) {}
     96 
     97  void ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
     98                  ErrorResult& aRv) override {
     99    MOZ_ASSERT(aChunk.isObject());
    100    // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
    101    //
    102    // chunk steps, given chunk:
    103    //         Resolve promise with «[ "value" → chunk, "done" → false ]».
    104 
    105    // We need to wrap this as the chunk could have come from
    106    // another compartment.
    107    JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
    108    if (!JS_WrapObject(aCx, &chunk)) {
    109      aRv.StealExceptionFromJSContext(aCx);
    110      return;
    111    }
    112 
    113    RootedDictionary<ReadableStreamReadResult> result(aCx);
    114    result.mValue = aChunk;
    115    result.mDone.Construct(false);
    116 
    117    mPromise->MaybeResolve(result);
    118  }
    119 
    120  void CloseSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
    121                  ErrorResult& aRv) override {
    122    MOZ_ASSERT(aChunk.isObject() || aChunk.isUndefined());
    123    // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
    124    //
    125    // close steps, given chunk:
    126    // Resolve promise with «[ "value" → chunk, "done" → true ]».
    127    RootedDictionary<ReadableStreamReadResult> result(aCx);
    128    if (aChunk.isObject()) {
    129      // We need to wrap this as the chunk could have come from
    130      // another compartment.
    131      JS::Rooted<JSObject*> chunk(aCx, &aChunk.toObject());
    132      if (!JS_WrapObject(aCx, &chunk)) {
    133        aRv.StealExceptionFromJSContext(aCx);
    134        return;
    135      }
    136 
    137      result.mValue = aChunk;
    138    }
    139    result.mDone.Construct(true);
    140 
    141    mPromise->MaybeResolve(result);
    142  }
    143 
    144  void ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
    145                  ErrorResult& aRv) override {
    146    // https://streams.spec.whatwg.org/#byob-reader-read Step 6.
    147    //
    148    // error steps, given e:
    149    // Reject promise with e.
    150    mPromise->MaybeReject(e);
    151  }
    152 
    153 protected:
    154  ~Read_ReadIntoRequest() override = default;
    155 };
    156 
    157 NS_IMPL_CYCLE_COLLECTION(ReadIntoRequest)
    158 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadIntoRequest)
    159 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadIntoRequest)
    160 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadIntoRequest)
    161  NS_INTERFACE_MAP_ENTRY(nsISupports)
    162 NS_INTERFACE_MAP_END
    163 
    164 NS_IMPL_CYCLE_COLLECTION_INHERITED(Read_ReadIntoRequest, ReadIntoRequest,
    165                                   mPromise)
    166 NS_IMPL_ADDREF_INHERITED(Read_ReadIntoRequest, ReadIntoRequest)
    167 NS_IMPL_RELEASE_INHERITED(Read_ReadIntoRequest, ReadIntoRequest)
    168 
    169 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadIntoRequest)
    170 NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest)
    171 
    172 namespace streams_abstract {
    173 // https://streams.spec.whatwg.org/#readable-stream-byob-reader-read
    174 void ReadableStreamBYOBReaderRead(JSContext* aCx,
    175                                  ReadableStreamBYOBReader* aReader,
    176                                  JS::Handle<JSObject*> aView, uint64_t aMin,
    177                                  ReadIntoRequest* aReadIntoRequest,
    178                                  ErrorResult& aRv) {
    179  // Step 1.Let stream be reader.[[stream]].
    180  ReadableStream* stream = aReader->GetStream();
    181 
    182  // Step 2. Assert: stream is not undefined.
    183  MOZ_ASSERT(stream);
    184 
    185  // Step 3. Set stream.[[disturbed]] to true.
    186  stream->SetDisturbed(true);
    187 
    188  // Step 4. If stream.[[state]] is "errored", perform readIntoRequest’s error
    189  // steps given stream.[[storedError]].
    190  if (stream->State() == ReadableStream::ReaderState::Errored) {
    191    JS::Rooted<JS::Value> error(aCx, stream->StoredError());
    192 
    193    aReadIntoRequest->ErrorSteps(aCx, error, aRv);
    194    return;
    195  }
    196 
    197  // Step 5. Otherwise, perform
    198  // !ReadableByteStreamControllerPullInto(stream.[[controller]], view, min,
    199  // readIntoRequest).
    200  MOZ_ASSERT(stream->Controller()->IsByte());
    201  RefPtr<ReadableByteStreamController> controller(
    202      stream->Controller()->AsByte());
    203  ReadableByteStreamControllerPullInto(aCx, controller, aView, aMin,
    204                                       aReadIntoRequest, aRv);
    205 }
    206 }  // namespace streams_abstract
    207 
    208 // https://streams.spec.whatwg.org/#byob-reader-read
    209 already_AddRefed<Promise> ReadableStreamBYOBReader::Read(
    210    const ArrayBufferView& aArray,
    211    const ReadableStreamBYOBReaderReadOptions& aOptions, ErrorResult& aRv) {
    212  AutoJSAPI jsapi;
    213  if (!jsapi.Init(GetParentObject())) {
    214    aRv.ThrowUnknownError("Internal error");
    215    return nullptr;
    216  }
    217  JSContext* cx = jsapi.cx();
    218 
    219  JS::Rooted<JSObject*> view(cx, aArray.Obj());
    220 
    221  // Step 1. If view.[[ByteLength]] is 0, return a promise rejected with a
    222  // TypeError exception.
    223  if (JS_GetArrayBufferViewByteLength(view) == 0) {
    224    // Binding code should convert this thrown value into a rejected promise.
    225    aRv.ThrowTypeError("Zero Length View");
    226    return nullptr;
    227  }
    228 
    229  // Step 2. If view.[[ViewedArrayBuffer]].[[ArrayBufferByteLength]] is 0,
    230  // return a promise rejected with a TypeError exception.
    231  bool isSharedMemory;
    232  JS::Rooted<JSObject*> viewedArrayBuffer(
    233      cx, JS_GetArrayBufferViewBuffer(cx, view, &isSharedMemory));
    234  if (!viewedArrayBuffer) {
    235    aRv.StealExceptionFromJSContext(cx);
    236    return nullptr;
    237  }
    238 
    239  if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
    240    aRv.ThrowTypeError("zero length viewed buffer");
    241    return nullptr;
    242  }
    243 
    244  // Step 3. If ! IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true, return a
    245  // promise rejected with a TypeError exception.
    246  if (JS::IsDetachedArrayBufferObject(viewedArrayBuffer)) {
    247    aRv.ThrowTypeError("Detached Buffer");
    248    return nullptr;
    249  }
    250 
    251  // Step 4. If options["min"] is 0, return a promise rejected with a TypeError
    252  // exception.
    253  if (aOptions.mMin == 0) {
    254    aRv.ThrowTypeError(
    255        "Zero is not a valid value for 'min' member of "
    256        "ReadableStreamBYOBReaderReadOptions.");
    257    return nullptr;
    258  }
    259 
    260  // Step 5. If view has a [[TypedArrayName]] internal slot,
    261  if (JS_IsTypedArrayObject(view)) {
    262    // Step 5.1. If options["min"] > view.[[ArrayLength]], return a promise
    263    // rejected with a RangeError exception.
    264    if (aOptions.mMin > JS_GetTypedArrayLength(view)) {
    265      aRv.ThrowRangeError(
    266          "Array length exceeded by 'min' member of "
    267          "ReadableStreamBYOBReaderReadOptions.");
    268      return nullptr;
    269    }
    270  } else {
    271    // Step 6. Otherwise (i.e., it is a DataView),
    272    // Step 6.1. If options["min"] > view.[[ByteLength]], return a promise
    273    // rejected with a RangeError exception.
    274    if (aOptions.mMin > JS_GetArrayBufferViewByteLength(view)) {
    275      aRv.ThrowRangeError(
    276          "byteLength exceeded by 'min' member of "
    277          "ReadableStreamBYOBReaderReadOptions.");
    278      return nullptr;
    279    }
    280  }
    281 
    282  // Step 7. If this.[[stream]] is undefined, return a promise rejected with a
    283  // TypeError exception.
    284  if (!GetStream()) {
    285    aRv.ThrowTypeError("Reader has undefined stream");
    286    return nullptr;
    287  }
    288 
    289  // Step 8. Let promise be a new promise.
    290  RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
    291 
    292  // Step 9. Let readIntoRequest be a new read-into request with the following
    293  // items:
    294  RefPtr<ReadIntoRequest> readIntoRequest = new Read_ReadIntoRequest(promise);
    295 
    296  // Step 10. Perform ! ReadableStreamBYOBReaderRead(this, view, options["min"],
    297  // readIntoRequest).
    298  ReadableStreamBYOBReaderRead(cx, this, view, aOptions.mMin, readIntoRequest,
    299                               aRv);
    300  if (aRv.Failed()) {
    301    return nullptr;
    302  }
    303 
    304  // Step 11. Return promise.
    305  return promise.forget();
    306 }
    307 
    308 namespace streams_abstract {
    309 
    310 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreadererrorreadintorequests
    311 void ReadableStreamBYOBReaderErrorReadIntoRequests(
    312    JSContext* aCx, ReadableStreamBYOBReader* aReader,
    313    JS::Handle<JS::Value> aError, ErrorResult& aRv) {
    314  // Step 1. Let readIntoRequests be reader.[[readIntoRequests]].
    315  LinkedList<RefPtr<ReadIntoRequest>> readIntoRequests =
    316      std::move(aReader->ReadIntoRequests());
    317 
    318  // Step 2. Set reader.[[readIntoRequests]] to a new empty list.
    319  // Note: The std::move already cleared this anyway.
    320  aReader->ReadIntoRequests().clear();
    321 
    322  // Step 3. For each readIntoRequest of readIntoRequests,
    323  while (RefPtr<ReadIntoRequest> readIntoRequest =
    324             readIntoRequests.popFirst()) {
    325    // Step 3.1. Perform readIntoRequest’s error steps, given e.
    326    readIntoRequest->ErrorSteps(aCx, aError, aRv);
    327    if (aRv.Failed()) {
    328      return;
    329    }
    330  }
    331 }
    332 
    333 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreambyobreaderrelease
    334 void ReadableStreamBYOBReaderRelease(JSContext* aCx,
    335                                     ReadableStreamBYOBReader* aReader,
    336                                     ErrorResult& aRv) {
    337  // Step 1. Perform ! ReadableStreamReaderGenericRelease(reader).
    338  ReadableStreamReaderGenericRelease(aReader, aRv);
    339  if (aRv.Failed()) {
    340    return;
    341  }
    342 
    343  // Step 2. Let e be a new TypeError exception.
    344  ErrorResult rv;
    345  rv.ThrowTypeError("Releasing lock");
    346  JS::Rooted<JS::Value> error(aCx);
    347  MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &error));
    348 
    349  // Step 3. Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
    350  ReadableStreamBYOBReaderErrorReadIntoRequests(aCx, aReader, error, aRv);
    351 }
    352 
    353 }  // namespace streams_abstract
    354 
    355 // https://streams.spec.whatwg.org/#byob-reader-release-lock
    356 void ReadableStreamBYOBReader::ReleaseLock(ErrorResult& aRv) {
    357  // Step 1. If this.[[stream]] is undefined, return.
    358  if (!mStream) {
    359    return;
    360  }
    361 
    362  AutoJSAPI jsapi;
    363  if (!jsapi.Init(mGlobal)) {
    364    return aRv.ThrowUnknownError("Internal error");
    365  }
    366  JSContext* cx = jsapi.cx();
    367 
    368  // Step 2. Perform ! ReadableStreamBYOBReaderRelease(this).
    369  RefPtr<ReadableStreamBYOBReader> thisRefPtr = this;
    370  ReadableStreamBYOBReaderRelease(cx, thisRefPtr, aRv);
    371 }
    372 
    373 namespace streams_abstract {
    374 // https://streams.spec.whatwg.org/#acquire-readable-stream-byob-reader
    375 already_AddRefed<ReadableStreamBYOBReader> AcquireReadableStreamBYOBReader(
    376    ReadableStream* aStream, ErrorResult& aRv) {
    377  // Step 1. Let reader be a new ReadableStreamBYOBReader.
    378  RefPtr<ReadableStreamBYOBReader> reader =
    379      new ReadableStreamBYOBReader(aStream->GetParentObject());
    380 
    381  // Step 2. Perform ? SetUpReadableStreamBYOBReader(reader, stream).
    382  SetUpReadableStreamBYOBReader(reader, *aStream, aRv);
    383  if (aRv.Failed()) {
    384    return nullptr;
    385  }
    386 
    387  // Step 3. Return reader.
    388  return reader.forget();
    389 }
    390 }  // namespace streams_abstract
    391 
    392 }  // namespace mozilla::dom