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