Bug 1945643 - Update to mozilla-nimbus-schemas 2025.1.1 r=chumphreys
[gecko.git] / dom / streams / ReadableStreamBYOBReader.cpp
bloba8a632fbe8de1fffa7df3c32810b0cf20346c4b5
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/ReadableStreamBYOBReader.h"
9 #include "ReadIntoRequest.h"
10 #include "js/ArrayBuffer.h"
11 #include "js/experimental/TypedData.h"
12 #include "mozilla/dom/ReadableStreamBYOBReader.h"
13 #include "mozilla/dom/ReadableStream.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"
20 // Temporary Includes
21 #include "mozilla/dom/ReadableByteStreamController.h"
22 #include "mozilla/dom/ReadableStreamBYOBRequest.h"
24 namespace mozilla::dom {
26 using namespace streams_abstract;
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)
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ReadableStreamBYOBReader)
35 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
36 NS_INTERFACE_MAP_END_INHERITING(ReadableStreamGenericReader)
38 ReadableStreamBYOBReader::ReadableStreamBYOBReader(nsISupports* aGlobal)
39 : ReadableStreamGenericReader(do_QueryInterface(aGlobal)) {}
41 JSObject* ReadableStreamBYOBReader::WrapObject(
42 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
43 return ReadableStreamBYOBReader_Binding::Wrap(aCx, this, aGivenProto);
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;
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;
63 // Step 3. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
64 ReadableStreamReaderGenericInitialize(reader, &stream);
66 // Step 4. Set reader.[[readIntoRequests]] to a new empty list.
67 reader->ReadIntoRequests().clear();
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);
79 // Step 1.
80 SetUpReadableStreamBYOBReader(reader, stream, rv);
81 if (rv.Failed()) {
82 return nullptr;
85 return reader.forget();
88 struct Read_ReadIntoRequest final : public ReadIntoRequest {
89 NS_DECL_ISUPPORTS_INHERITED
90 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Read_ReadIntoRequest,
91 ReadIntoRequest)
93 RefPtr<Promise> mPromise;
95 explicit Read_ReadIntoRequest(Promise* aPromise) : mPromise(aPromise) {}
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.
102 // chunk steps, given chunk:
103 // Resolve promise with «[ "value" → chunk, "done" → false ]».
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;
113 RootedDictionary<ReadableStreamReadResult> result(aCx);
114 result.mValue = aChunk;
115 result.mDone.Construct(false);
117 mPromise->MaybeResolve(result);
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.
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;
137 result.mValue = aChunk;
139 result.mDone.Construct(true);
141 mPromise->MaybeResolve(result);
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.
148 // error steps, given e:
149 // Reject promise with e.
150 mPromise->MaybeReject(e);
153 protected:
154 ~Read_ReadIntoRequest() override = default;
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
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)
169 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Read_ReadIntoRequest)
170 NS_INTERFACE_MAP_END_INHERITING(ReadIntoRequest)
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();
182 // Step 2. Assert: stream is not undefined.
183 MOZ_ASSERT(stream);
185 // Step 3. Set stream.[[disturbed]] to true.
186 stream->SetDisturbed(true);
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());
193 aReadIntoRequest->ErrorSteps(aCx, error, aRv);
194 return;
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);
206 } // namespace streams_abstract
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;
217 JSContext* cx = jsapi.cx();
219 JS::Rooted<JSObject*> view(cx, aArray.Obj());
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;
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;
239 if (JS::GetArrayBufferByteLength(viewedArrayBuffer) == 0) {
240 aRv.ThrowTypeError("zero length viewed buffer");
241 return nullptr;
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;
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;
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;
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;
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;
289 // Step 8. Let promise be a new promise.
290 RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
292 // Step 9. Let readIntoRequest be a new read-into request with the following
293 // items:
294 RefPtr<ReadIntoRequest> readIntoRequest = new Read_ReadIntoRequest(promise);
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;
304 // Step 11. Return promise.
305 return promise.forget();
308 namespace streams_abstract {
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());
318 // Step 2. Set reader.[[readIntoRequests]] to a new empty list.
319 // Note: The std::move already cleared this anyway.
320 aReader->ReadIntoRequests().clear();
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;
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;
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 ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
350 ReadableStreamBYOBReaderErrorReadIntoRequests(aCx, aReader, error, aRv);
353 } // namespace streams_abstract
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;
362 AutoJSAPI jsapi;
363 if (!jsapi.Init(mGlobal)) {
364 return aRv.ThrowUnknownError("Internal error");
366 JSContext* cx = jsapi.cx();
368 // Step 2. Perform ! ReadableStreamBYOBReaderRelease(this).
369 RefPtr<ReadableStreamBYOBReader> thisRefPtr = this;
370 ReadableStreamBYOBReaderRelease(cx, thisRefPtr, aRv);
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());
381 // Step 2. Perform ? SetUpReadableStreamBYOBReader(reader, stream).
382 SetUpReadableStreamBYOBReader(reader, *aStream, aRv);
383 if (aRv.Failed()) {
384 return nullptr;
387 // Step 3. Return reader.
388 return reader.forget();
390 } // namespace streams_abstract
392 } // namespace mozilla::dom