ReadableStreamDefaultReader.cpp (14915B)
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/ReadableStreamDefaultReader.h" 8 9 #include "js/PropertyAndElement.h" 10 #include "js/TypeDecls.h" 11 #include "js/Value.h" 12 #include "jsapi.h" 13 #include "mozilla/dom/AutoEntryScript.h" 14 #include "mozilla/dom/ReadableStream.h" 15 #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h" 16 #include "mozilla/dom/RootedDictionary.h" 17 #include "mozilla/dom/UnderlyingSourceBinding.h" 18 #include "nsCOMPtr.h" 19 #include "nsCycleCollectionParticipant.h" 20 #include "nsISupports.h" 21 #include "nsWrapperCache.h" 22 23 namespace mozilla::dom { 24 25 using namespace streams_abstract; 26 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 33 34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamGenericReader) 35 NS_INTERFACE_MAP_ENTRY(nsISupports) 36 NS_INTERFACE_MAP_END 37 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) 45 46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamDefaultReader) 47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 48 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader) 49 50 ReadableStreamDefaultReader::ReadableStreamDefaultReader(nsISupports* aGlobal) 51 : ReadableStreamGenericReader(do_QueryInterface(aGlobal)) {} 52 53 ReadableStreamDefaultReader::~ReadableStreamDefaultReader() { 54 mReadRequests.clear(); 55 } 56 57 JSObject* ReadableStreamDefaultReader::WrapObject( 58 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 59 return ReadableStreamDefaultReader_Binding::Wrap(aCx, this, aGivenProto); 60 } 61 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); 68 69 // Step 2. 70 aStream->SetReader(aReader); 71 72 aReader->SetClosedPromise( 73 Promise::CreateInfallible(aReader->GetParentObject())); 74 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); 85 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); 94 95 // Step 5.3 96 aReader->ClosedPromise()->SetSettledPromiseIsHandled(); 97 return true; 98 } 99 default: 100 MOZ_ASSERT_UNREACHABLE("Unknown ReaderState"); 101 return false; 102 } 103 } 104 } // namespace streams_abstract 105 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()); 115 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; 123 } 124 125 // Step 2. 126 RefPtr<ReadableStream> streamPtr = &aStream; 127 if (!ReadableStreamReaderGenericInitialize(reader, streamPtr)) { 128 return nullptr; 129 } 130 131 // Step 3. 132 reader->mReadRequests.clear(); 133 134 return reader.forget(); 135 } 136 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 ]». 142 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; 149 } 150 151 RootedDictionary<ReadableStreamReadResult> result(aCx); 152 result.mValue = chunk; 153 result.mDone.Construct(false); 154 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; 160 } 161 162 mPromise->MaybeResolve(value); 163 } 164 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); 172 173 JS::Rooted<JS::Value> value(aCx); 174 if (!ToJSValue(aCx, std::move(result), &value)) { 175 aRv.StealExceptionFromJSContext(aCx); 176 return; 177 } 178 179 mPromise->MaybeResolve(value); 180 } 181 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); 188 } 189 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) 194 195 NS_IMPL_ADDREF_INHERITED(Read_ReadRequest, ReadRequest) 196 NS_IMPL_RELEASE_INHERITED(Read_ReadRequest, ReadRequest) 197 198 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadRequest) 199 NS_INTERFACE_MAP_ENTRY(nsISupports) 200 NS_INTERFACE_MAP_END 201 202 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadRequest) 203 NS_INTERFACE_MAP_END_INHERITING(ReadRequest) 204 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(); 212 213 // Step 2. 214 MOZ_ASSERT(stream); 215 216 // Step 3. 217 stream->SetDisturbed(true); 218 219 switch (stream->State()) { 220 // Step 4. 221 case ReadableStream::ReaderState::Closed: { 222 aRequest->CloseSteps(aCx, aRv); 223 return; 224 } 225 226 case ReadableStream::ReaderState::Errored: { 227 JS::Rooted<JS::Value> storedError(aCx, stream->StoredError()); 228 aRequest->ErrorSteps(aCx, storedError, aRv); 229 return; 230 } 231 232 case ReadableStream::ReaderState::Readable: { 233 RefPtr<ReadableStreamControllerBase> controller(stream->Controller()); 234 MOZ_ASSERT(controller); 235 controller->PullSteps(aCx, aRequest, aRv); 236 return; 237 } 238 } 239 } 240 } // namespace streams_abstract 241 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; 250 } 251 252 // Step 2. 253 RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject()); 254 255 // Step 3. 256 RefPtr<ReadRequest> request = new Read_ReadRequest(promise); 257 258 // Step 4. 259 AutoEntryScript aes(mGlobal, "ReadableStreamDefaultReader::Read"); 260 JSContext* cx = aes.cx(); 261 262 ReadableStreamDefaultReaderRead(cx, this, request, aRv); 263 if (aRv.Failed()) { 264 return nullptr; 265 } 266 267 // Step 5. 268 return promise.forget(); 269 } 270 271 namespace streams_abstract { 272 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(); 278 279 // Step 2. Assert: stream is not undefined. 280 MOZ_ASSERT(stream); 281 282 // Step 3. Assert: stream.[[reader]] is reader. 283 MOZ_ASSERT(stream->GetReader() == aReader); 284 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()); 296 } 297 298 // Step 6. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. 299 aReader->ClosedPromise()->SetSettledPromiseIsHandled(); 300 301 // Step 7. Perform ! stream.[[controller]].[[ReleaseSteps]](). 302 stream->Controller()->ReleaseSteps(); 303 304 // Step 8. Set stream.[[reader]] to undefined. 305 stream->SetReader(nullptr); 306 307 // Step 9. Set reader.[[stream]] to undefined. 308 aReader->SetStream(nullptr); 309 } 310 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()); 318 319 // Step 2. Set reader.[[readRequests]] to a new empty list. 320 // Note: The std::move already cleared this anyway. 321 aReader->ReadRequests().clear(); 322 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; 329 } 330 } 331 } 332 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; 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 ! ReadableStreamDefaultReaderErrorReadRequests(reader, e). 350 ReadableStreamDefaultReaderErrorReadRequests(aCx, aReader, error, aRv); 351 } 352 353 } // namespace streams_abstract 354 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; 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 ! ReadableStreamDefaultReaderRelease(this). 369 RefPtr<ReadableStreamDefaultReader> thisRefPtr = this; 370 ReadableStreamDefaultReaderRelease(cx, thisRefPtr, aRv); 371 } 372 373 // https://streams.spec.whatwg.org/#generic-reader-closed 374 already_AddRefed<Promise> ReadableStreamGenericReader::Closed() const { 375 // Step 1. 376 return do_AddRef(mClosedPromise); 377 } 378 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(); 386 387 // Step 2. 388 MOZ_ASSERT(stream); 389 390 // Step 3. 391 return ReadableStreamCancel(aCx, stream, aReason, aRv); 392 } 393 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; 401 } 402 403 // Step 2. Return ! ReadableStreamReaderGenericCancel(this, reason). 404 return ReadableStreamGenericReaderCancel(aCx, this, aReason, aRv); 405 } 406 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."); 417 } 418 419 // Step 2. 420 if (!ReadableStreamReaderGenericInitialize(aReader, aStream)) { 421 return; 422 } 423 424 // Step 3. 425 aReader->ReadRequests().clear(); 426 } 427 } // namespace streams_abstract 428 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); 437 } 438 439 } // namespace mozilla::dom