Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / streams / ReadableStreamDefaultReader.cpp
blobebc86d477895e99dd96e92e6d3a829b00b94e7b0
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/. */
7 #include "mozilla/dom/ReadableStreamDefaultReader.h"
9 #include "mozilla/dom/AutoEntryScript.h"
10 #include "mozilla/dom/ReadableStream.h"
11 #include "mozilla/dom/RootedDictionary.h"
12 #include "js/PropertyAndElement.h"
13 #include "js/TypeDecls.h"
14 #include "js/Value.h"
15 #include "jsapi.h"
16 #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
17 #include "mozilla/dom/UnderlyingSourceBinding.h"
18 #include "nsCOMPtr.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsISupports.h"
21 #include "nsWrapperCache.h"
23 namespace mozilla::dom {
25 using namespace streams_abstract;
27 NS_IMPL_CYCLE_COLLECTION(ReadableStreamGenericReader, mClosedPromise, mStream,
28 mGlobal)
29 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadableStreamGenericReader)
30 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadableStreamGenericReader)
31 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ReadableStreamGenericReader)
32 NS_IMPL_CYCLE_COLLECTION_TRACE_END
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamGenericReader)
35 NS_INTERFACE_MAP_ENTRY(nsISupports)
36 NS_INTERFACE_MAP_END
38 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_INHERITED(ReadableStreamDefaultReader,
39 ReadableStreamGenericReader,
40 mReadRequests)
41 NS_IMPL_ADDREF_INHERITED(ReadableStreamDefaultReader,
42 ReadableStreamGenericReader)
43 NS_IMPL_RELEASE_INHERITED(ReadableStreamDefaultReader,
44 ReadableStreamGenericReader)
46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultReader)
47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
48 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader)
50 ReadableStreamDefaultReader::ReadableStreamDefaultReader(nsISupports* aGlobal)
51 : ReadableStreamGenericReader(do_QueryInterface(aGlobal)) {}
53 ReadableStreamDefaultReader::~ReadableStreamDefaultReader() {
54 mReadRequests.clear();
57 JSObject* ReadableStreamDefaultReader::WrapObject(
58 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
59 return ReadableStreamDefaultReader_Binding::Wrap(aCx, this, aGivenProto);
62 namespace streams_abstract {
63 // https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize
64 bool ReadableStreamReaderGenericInitialize(ReadableStreamGenericReader* aReader,
65 ReadableStream* aStream) {
66 // Step 1.
67 aReader->SetStream(aStream);
69 // Step 2.
70 aStream->SetReader(aReader);
72 aReader->SetClosedPromise(
73 Promise::CreateInfallible(aReader->GetParentObject()));
75 switch (aStream->State()) {
76 // Step 3.
77 case ReadableStream::ReaderState::Readable:
78 // Step 3.1
79 // Promise created above.
80 return true;
81 // Step 4.
82 case ReadableStream::ReaderState::Closed:
83 // Step 4.1.
84 aReader->ClosedPromise()->MaybeResolve(JS::UndefinedHandleValue);
86 return true;
87 // Step 5.
88 case ReadableStream::ReaderState::Errored: {
89 // Step 5.1 Implicit
90 // Step 5.2
91 JS::RootingContext* rcx = RootingCx();
92 JS::Rooted<JS::Value> rootedError(rcx, aStream->StoredError());
93 aReader->ClosedPromise()->MaybeReject(rootedError);
95 // Step 5.3
96 aReader->ClosedPromise()->SetSettledPromiseIsHandled();
97 return true;
99 default:
100 MOZ_ASSERT_UNREACHABLE("Unknown ReaderState");
101 return false;
104 } // namespace streams_abstract
106 // https://streams.spec.whatwg.org/#default-reader-constructor &&
107 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
108 /* static */
109 already_AddRefed<ReadableStreamDefaultReader>
110 ReadableStreamDefaultReader::Constructor(const GlobalObject& aGlobal,
111 ReadableStream& aStream,
112 ErrorResult& aRv) {
113 RefPtr<ReadableStreamDefaultReader> reader =
114 new ReadableStreamDefaultReader(aGlobal.GetAsSupports());
116 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
117 // Step 1.
118 if (aStream.Locked()) {
119 aRv.ThrowTypeError(
120 "Cannot create a new reader for a readable stream already locked by "
121 "another reader.");
122 return nullptr;
125 // Step 2.
126 RefPtr<ReadableStream> streamPtr = &aStream;
127 if (!ReadableStreamReaderGenericInitialize(reader, streamPtr)) {
128 return nullptr;
131 // Step 3.
132 reader->mReadRequests.clear();
134 return reader.forget();
137 void Read_ReadRequest::ChunkSteps(JSContext* aCx, JS::Handle<JS::Value> aChunk,
138 ErrorResult& aRv) {
139 // https://streams.spec.whatwg.org/#default-reader-read Step 3.
140 // chunk steps, given chunk:
141 // Step 1. Resolve promise with «[ "value" → chunk, "done" → false ]».
143 // Value may need to be wrapped if stream and reader are in different
144 // compartments.
145 JS::Rooted<JS::Value> chunk(aCx, aChunk);
146 if (!JS_WrapValue(aCx, &chunk)) {
147 aRv.StealExceptionFromJSContext(aCx);
148 return;
151 RootedDictionary<ReadableStreamReadResult> result(aCx);
152 result.mValue = chunk;
153 result.mDone.Construct(false);
155 // Ensure that the object is created with the current global.
156 JS::Rooted<JS::Value> value(aCx);
157 if (!ToJSValue(aCx, std::move(result), &value)) {
158 aRv.StealExceptionFromJSContext(aCx);
159 return;
162 mPromise->MaybeResolve(value);
165 void Read_ReadRequest::CloseSteps(JSContext* aCx, ErrorResult& aRv) {
166 // https://streams.spec.whatwg.org/#default-reader-read Step 3.
167 // close steps:
168 // Step 1. Resolve promise with «[ "value" → undefined, "done" → true ]».
169 RootedDictionary<ReadableStreamReadResult> result(aCx);
170 result.mValue.setUndefined();
171 result.mDone.Construct(true);
173 JS::Rooted<JS::Value> value(aCx);
174 if (!ToJSValue(aCx, std::move(result), &value)) {
175 aRv.StealExceptionFromJSContext(aCx);
176 return;
179 mPromise->MaybeResolve(value);
182 void Read_ReadRequest::ErrorSteps(JSContext* aCx, JS::Handle<JS::Value> e,
183 ErrorResult& aRv) {
184 // https://streams.spec.whatwg.org/#default-reader-read Step 3.
185 // error steps:
186 // Step 1. Reject promise with e.
187 mPromise->MaybeReject(e);
190 NS_IMPL_CYCLE_COLLECTION(ReadRequest)
191 NS_IMPL_CYCLE_COLLECTION_INHERITED(Read_ReadRequest, ReadRequest, mPromise)
192 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReadRequest)
193 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReadRequest)
195 NS_IMPL_ADDREF_INHERITED(Read_ReadRequest, ReadRequest)
196 NS_IMPL_RELEASE_INHERITED(Read_ReadRequest, ReadRequest)
198 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadRequest)
199 NS_INTERFACE_MAP_ENTRY(nsISupports)
200 NS_INTERFACE_MAP_END
202 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadRequest)
203 NS_INTERFACE_MAP_END_INHERITING(ReadRequest)
205 namespace streams_abstract {
206 // https://streams.spec.whatwg.org/#readable-stream-default-reader-read
207 void ReadableStreamDefaultReaderRead(JSContext* aCx,
208 ReadableStreamGenericReader* aReader,
209 ReadRequest* aRequest, ErrorResult& aRv) {
210 // Step 1.
211 ReadableStream* stream = aReader->GetStream();
213 // Step 2.
214 MOZ_ASSERT(stream);
216 // Step 3.
217 stream->SetDisturbed(true);
219 switch (stream->State()) {
220 // Step 4.
221 case ReadableStream::ReaderState::Closed: {
222 aRequest->CloseSteps(aCx, aRv);
223 return;
226 case ReadableStream::ReaderState::Errored: {
227 JS::Rooted<JS::Value> storedError(aCx, stream->StoredError());
228 aRequest->ErrorSteps(aCx, storedError, aRv);
229 return;
232 case ReadableStream::ReaderState::Readable: {
233 RefPtr<ReadableStreamController> controller(stream->Controller());
234 MOZ_ASSERT(controller);
235 controller->PullSteps(aCx, aRequest, aRv);
236 return;
240 } // namespace streams_abstract
242 // Return a raw pointer here to avoid refcounting, but make sure it's safe
243 // (the object should be kept alive by the callee).
244 // https://streams.spec.whatwg.org/#default-reader-read
245 already_AddRefed<Promise> ReadableStreamDefaultReader::Read(ErrorResult& aRv) {
246 // Step 1.
247 if (!mStream) {
248 aRv.ThrowTypeError("Reading is not possible after calling releaseLock.");
249 return nullptr;
252 // Step 2.
253 RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
255 // Step 3.
256 RefPtr<ReadRequest> request = new Read_ReadRequest(promise);
258 // Step 4.
259 AutoEntryScript aes(mGlobal, "ReadableStreamDefaultReader::Read");
260 JSContext* cx = aes.cx();
262 ReadableStreamDefaultReaderRead(cx, this, request, aRv);
263 if (aRv.Failed()) {
264 return nullptr;
267 // Step 5.
268 return promise.forget();
271 namespace streams_abstract {
273 // https://streams.spec.whatwg.org/#readable-stream-reader-generic-release
274 void ReadableStreamReaderGenericRelease(ReadableStreamGenericReader* aReader,
275 ErrorResult& aRv) {
276 // Step 1. Let stream be reader.[[stream]].
277 RefPtr<ReadableStream> stream = aReader->GetStream();
279 // Step 2. Assert: stream is not undefined.
280 MOZ_ASSERT(stream);
282 // Step 3. Assert: stream.[[reader]] is reader.
283 MOZ_ASSERT(stream->GetReader() == aReader);
285 // Step 4. If stream.[[state]] is "readable", reject reader.[[closedPromise]]
286 // with a TypeError exception.
287 if (stream->State() == ReadableStream::ReaderState::Readable) {
288 aReader->ClosedPromise()->MaybeRejectWithTypeError(
289 "Releasing lock on readable stream");
290 } else {
291 // Step 5. Otherwise, set reader.[[closedPromise]] to a promise rejected
292 // with a TypeError exception.
293 RefPtr<Promise> promise = Promise::CreateRejectedWithTypeError(
294 aReader->GetParentObject(), "Lock Released"_ns, aRv);
295 aReader->SetClosedPromise(promise.forget());
298 // Step 6. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
299 aReader->ClosedPromise()->SetSettledPromiseIsHandled();
301 // Step 7. Perform ! stream.[[controller]].[[ReleaseSteps]]().
302 stream->Controller()->ReleaseSteps();
304 // Step 8. Set stream.[[reader]] to undefined.
305 stream->SetReader(nullptr);
307 // Step 9. Set reader.[[stream]] to undefined.
308 aReader->SetStream(nullptr);
311 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreadererrorreadrequests
312 void ReadableStreamDefaultReaderErrorReadRequests(
313 JSContext* aCx, ReadableStreamDefaultReader* aReader,
314 JS::Handle<JS::Value> aError, ErrorResult& aRv) {
315 // Step 1. Let readRequests be reader.[[readRequests]].
316 LinkedList<RefPtr<ReadRequest>> readRequests =
317 std::move(aReader->ReadRequests());
319 // Step 2. Set reader.[[readRequests]] to a new empty list.
320 // Note: The std::move already cleared this anyway.
321 aReader->ReadRequests().clear();
323 // Step 3. For each readRequest of readRequests,
324 while (RefPtr<ReadRequest> readRequest = readRequests.popFirst()) {
325 // Step 3.1. Perform readRequest’s error steps, given e.
326 readRequest->ErrorSteps(aCx, aError, aRv);
327 if (aRv.Failed()) {
328 return;
333 // https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease
334 void ReadableStreamDefaultReaderRelease(JSContext* aCx,
335 ReadableStreamDefaultReader* aReader,
336 ErrorResult& aRv) {
337 // Step 1. Perform ! ReadableStreamReaderGenericRelease(reader).
338 ReadableStreamReaderGenericRelease(aReader, aRv);
339 if (aRv.Failed()) {
340 return;
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));
349 // Step 3. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
350 ReadableStreamDefaultReaderErrorReadRequests(aCx, aReader, error, aRv);
353 } // namespace streams_abstract
355 // https://streams.spec.whatwg.org/#default-reader-release-lock
356 void ReadableStreamDefaultReader::ReleaseLock(ErrorResult& aRv) {
357 // Step 1. If this.[[stream]] is undefined, return.
358 if (!mStream) {
359 return;
362 AutoJSAPI jsapi;
363 if (!jsapi.Init(mGlobal)) {
364 return aRv.ThrowUnknownError("Internal error");
366 JSContext* cx = jsapi.cx();
368 // Step 2. Perform ! ReadableStreamDefaultReaderRelease(this).
369 RefPtr<ReadableStreamDefaultReader> thisRefPtr = this;
370 ReadableStreamDefaultReaderRelease(cx, thisRefPtr, aRv);
373 // https://streams.spec.whatwg.org/#generic-reader-closed
374 already_AddRefed<Promise> ReadableStreamGenericReader::Closed() const {
375 // Step 1.
376 return do_AddRef(mClosedPromise);
379 // https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel
380 MOZ_CAN_RUN_SCRIPT
381 static already_AddRefed<Promise> ReadableStreamGenericReaderCancel(
382 JSContext* aCx, ReadableStreamGenericReader* aReader,
383 JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
384 // Step 1 (Strong ref for below call).
385 RefPtr<ReadableStream> stream = aReader->GetStream();
387 // Step 2.
388 MOZ_ASSERT(stream);
390 // Step 3.
391 return ReadableStreamCancel(aCx, stream, aReason, aRv);
394 already_AddRefed<Promise> ReadableStreamGenericReader::Cancel(
395 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
396 // Step 1. If this.[[stream]] is undefined,
397 // return a promise rejected with a TypeError exception.
398 if (!mStream) {
399 aRv.ThrowTypeError("Canceling is not possible after calling releaseLock.");
400 return nullptr;
403 // Step 2. Return ! ReadableStreamReaderGenericCancel(this, reason).
404 return ReadableStreamGenericReaderCancel(aCx, this, aReason, aRv);
407 namespace streams_abstract {
408 // https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
409 void SetUpReadableStreamDefaultReader(ReadableStreamDefaultReader* aReader,
410 ReadableStream* aStream,
411 ErrorResult& aRv) {
412 // Step 1.
413 if (IsReadableStreamLocked(aStream)) {
414 return aRv.ThrowTypeError(
415 "Cannot get a new reader for a readable stream already locked by "
416 "another reader.");
419 // Step 2.
420 if (!ReadableStreamReaderGenericInitialize(aReader, aStream)) {
421 return;
424 // Step 3.
425 aReader->ReadRequests().clear();
427 } // namespace streams_abstract
429 // https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-a-chunk
430 // To read a chunk from a ReadableStreamDefaultReader reader, given a read
431 // request readRequest, perform ! ReadableStreamDefaultReaderRead(reader,
432 // readRequest).
433 void ReadableStreamDefaultReader::ReadChunk(JSContext* aCx,
434 ReadRequest& aRequest,
435 ErrorResult& aRv) {
436 ReadableStreamDefaultReaderRead(aCx, this, &aRequest, aRv);
439 } // namespace mozilla::dom