diff --git a/js/public/Stream.h b/js/public/Stream.h deleted file mode 100644 index a052dfc3ca35..000000000000 --- a/js/public/Stream.h +++ /dev/null @@ -1,522 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * JSAPI functions and callbacks related to WHATWG Stream objects. - * - * Much of the API here mirrors the JS API of ReadableStream and associated - * classes, e.g. ReadableStreamDefaultReader, ReadableStreamBYOBReader, - * ReadableStreamDefaultController, ReadableByteStreamController, and - * ReadableStreamBYOBRequest. - * - * There are some crucial differences, though: Functionality that's exposed - * as methods/accessors on controllers in JS is exposed as functions taking - * ReadableStream instances instead. This is because an analysis of how - * the API would be used showed that all functions that'd take controllers - * would do so by first getting the controller from the stream instance it's - * associated with and then call the function taking it. I.e., it would purely - * add boilerplate without any gains in ease of use of the API. - * - * It would probably still make sense to factor the API the same as the JS API - * if we had to keep any API stability guarantees: the JS API won't change, so - * we could be sure that the C++ API could stay the same, too. Given that we - * don't guarantee API stability, this concern isn't too pressing. - * - * Some functions exposed here deal with ReadableStream instances that have an - * embedding-provided underlying source. These instances are largely similar - * to byte streams as created using |new ReadableStream({type: "bytes"})|: - * They enable users to acquire ReadableStreamBYOBReaders and only vend chunks - * that're typed array instances. - * - * When creating an "external readable stream" using - * JS::NewReadableExternalSourceStreamObject, an underlying source and a set - * of flags can be passed to be stored on the stream. The underlying source is - * treated as an opaque void* pointer by the JS engine: it's purely meant as - * a reference to be used by the embedding to identify whatever actual source - * it uses to supply data for the stream. Similarly, the flags aren't - * interpreted by the JS engine, but are passed to some of the callbacks below - * and can be retrieved using JS::ReadableStreamGetEmbeddingFlags. - * - * External readable streams are optimized to allow the embedding to interact - * with them with a minimum of overhead: chunks aren't enqueued as individual - * typed array instances; instead, the embedding only updates the amount of - * data available using ReadableStreamUpdateDataAvailableFromSource. - * When content requests data by reading from a reader, - * WriteIntoReadRequestBufferCallback is invoked, asking the embedding to - * write data directly into the buffer we're about to hand to content. - * - * Additionally, ReadableStreamGetExternalUnderlyingSource can be used to - * get the void* pointer to the underlying source. This is equivalent to - * acquiring a reader for the stream in that it locks the stream until it - * is released again using JS::ReadableStreamReleaseExternalUnderlyingSource. - * - * Embeddings are expected to detect situations where an API exposed to JS - * takes a ReadableStream to read from that has an external underlying source. - * In those situations, it might be preferable to directly perform data - * transfers from the stream's underlying source to whatever sink the - * embedding uses, assuming that such direct transfers can be performed - * more efficiently. - * - * An example of such an optimized operation might be a ServiceWorker piping a - * fetch Response body to a TextDecoder: instead of writing chunks of data - * into JS typed array buffers only to immediately read from them again, the - * embedding can presumably directly feed the incoming data to the - * TextDecoder's underlying implementation. - */ - -#ifndef js_Stream_h -#define js_Stream_h - -#include "jstypes.h" - -#include "js/TypeDecls.h" - -namespace JS { - -/** - * Invoked whenever a reader desires more data from a ReadableStream's - * embedding-provided underlying source. - * - * The given |desiredSize| is the absolute size, not a delta from the previous - * desired size. - */ -typedef void -(* RequestReadableStreamDataCallback)(JSContext* cx, HandleObject stream, - void* underlyingSource, uint8_t flags, size_t desiredSize); - -/** - * Invoked to cause the embedding to fill the given |buffer| with data from - * the given embedding-provided underlying source. - * - * This can only happen after the embedding has updated the amount of data - * available using JS::ReadableStreamUpdateDataAvailableFromSource. If at - * least one read request is pending when - * JS::ReadableStreamUpdateDataAvailableFromSource is called, - * the WriteIntoReadRequestBufferCallback is invoked immediately from under - * the call to JS::WriteIntoReadRequestBufferCallback. If not, it is invoked - * if and when a new read request is made. - * - * Note: This callback *must not cause GC*, because that could potentially - * invalidate the |buffer| pointer. - */ -typedef void -(* WriteIntoReadRequestBufferCallback)(JSContext* cx, HandleObject stream, - void* underlyingSource, uint8_t flags, void* buffer, - size_t length, size_t* bytesWritten); - -/** - * Invoked in reaction to the ReadableStream being canceled to allow the - * embedding to free the underlying source. - * - * This is equivalent to calling |cancel| on non-external underlying sources - * provided to the ReadableStream constructor in JavaScript. - * - * The given |reason| is the JS::Value that was passed as an argument to - * ReadableStream#cancel(). - * - * The returned JS::Value will be used to resolve the Promise returned by - * ReadableStream#cancel(). - */ -typedef Value -(* CancelReadableStreamCallback)(JSContext* cx, HandleObject stream, - void* underlyingSource, uint8_t flags, HandleValue reason); - -/** - * Invoked in reaction to a ReadableStream with an embedding-provided - * underlying source being closed. - */ -typedef void -(* ReadableStreamClosedCallback)(JSContext* cx, HandleObject stream, void* underlyingSource, - uint8_t flags); - -/** - * Invoked in reaction to a ReadableStream with an embedding-provided - * underlying source being errored with the - * given reason. - */ -typedef void -(* ReadableStreamErroredCallback)(JSContext* cx, HandleObject stream, void* underlyingSource, - uint8_t flags, HandleValue reason); - -/** - * Invoked in reaction to a ReadableStream with an embedding-provided - * underlying source being finalized. Only the underlying source is passed - * as an argument, while the ReadableStream itself is not to prevent the - * embedding from operating on a JSObject that might not be in a valid state - * anymore. - * - * Note: the ReadableStream might be finalized on a background thread. That - * means this callback might be invoked from an arbitrary thread, which the - * embedding must be able to handle. - */ -typedef void -(* ReadableStreamFinalizeCallback)(void* underlyingSource, uint8_t flags); - -/** - * Sets runtime-wide callbacks to use for interacting with embedding-provided - * hooks for operating on ReadableStream instances. - * - * See the documentation for the individual callback types for details. - */ -extern JS_PUBLIC_API(void) -SetReadableStreamCallbacks(JSContext* cx, - RequestReadableStreamDataCallback dataRequestCallback, - WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback, - CancelReadableStreamCallback cancelCallback, - ReadableStreamClosedCallback closedCallback, - ReadableStreamErroredCallback erroredCallback, - ReadableStreamFinalizeCallback finalizeCallback); - -extern JS_PUBLIC_API(bool) -HasReadableStreamCallbacks(JSContext* cx); - -/** - * Returns a new instance of the ReadableStream builtin class in the current - * compartment, configured as a default stream. - * If a |proto| is passed, that gets set as the instance's [[Prototype]] - * instead of the original value of |ReadableStream.prototype|. - */ -extern JS_PUBLIC_API(JSObject*) -NewReadableDefaultStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr, - HandleFunction size = nullptr, double highWaterMark = 1, - HandleObject proto = nullptr); - -/** - * Returns a new instance of the ReadableStream builtin class in the current - * compartment, configured as a byte stream. - * If a |proto| is passed, that gets set as the instance's [[Prototype]] - * instead of the original value of |ReadableStream.prototype|. - */ -extern JS_PUBLIC_API(JSObject*) -NewReadableByteStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr, - double highWaterMark = 0, HandleObject proto = nullptr); - -/** - * Returns a new instance of the ReadableStream builtin class in the current - * compartment, with the right slot layout. If a |proto| is passed, that gets - * set as the instance's [[Prototype]] instead of the original value of - * |ReadableStream.prototype|. - * - * The instance is optimized for operating as a byte stream backed by an - * embedding-provided underlying source, using the callbacks set via - * |JS::SetReadableStreamCallbacks|. - * - * The given |flags| will be passed to all applicable callbacks and can be - * used to disambiguate between different types of stream sources the - * embedding might support. - * - * Note: the embedding is responsible for ensuring that the pointer to the - * underlying source stays valid as long as the stream can be read from. - * The underlying source can be freed if the tree is canceled or errored. - * It can also be freed if the stream is destroyed. The embedding is notified - * of that using ReadableStreamFinalizeCallback. - */ -extern JS_PUBLIC_API(JSObject*) -NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource, - uint8_t flags = 0, HandleObject proto = nullptr); - -/** - * Returns the flags that were passed to NewReadableExternalSourceStreamObject - * when creating the given stream. - * - * Asserts that the given stream has an embedding-provided underlying source. - */ -extern JS_PUBLIC_API(uint8_t) -ReadableStreamGetEmbeddingFlags(const JSObject* stream); - -/** - * Returns the embedding-provided underlying source of the given |stream|. - * - * Can be used to optimize operations if both the underlying source and the - * intended sink are embedding-provided. In that case it might be - * preferrable to pipe data directly from source to sink without interacting - * with the stream at all. - * - * Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is - * called. - * - * Throws an exception if the stream is locked, i.e. if a reader has been - * acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource - * has been used previously without releasing the external source again. - * - * Throws an exception if the stream isn't readable, i.e if it is errored or - * closed. This is different from ReadableStreamGetReader because we don't - * have a Promise to resolve/reject, which a reader provides. - * - * Asserts that the stream has an embedding-provided underlying source. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject stream, void** source); - -/** - * Releases the embedding-provided underlying source of the given |stream|, - * returning the stream into an unlocked state. - * - * Asserts that the stream was locked through - * ReadableStreamGetExternalUnderlyingSource. - * - * Asserts that the stream has an embedding-provided underlying source. - */ -extern JS_PUBLIC_API(void) -ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream); - -/** - * Update the amount of data available at the underlying source of the given - * |stream|. - * - * Can only be used for streams with an embedding-provided underlying source. - * The JS engine will use the given value to satisfy read requests for the - * stream by invoking the JS::WriteIntoReadRequestBuffer callback. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, HandleObject stream, - uint32_t availableData); - -/** - * Returns true if the given object is an unwrapped ReadableStream object, - * false otherwise. - */ -extern JS_PUBLIC_API(bool) -IsReadableStream(const JSObject* obj); - -/** - * Returns true if the given object is an unwrapped - * ReadableStreamDefaultReader or ReadableStreamBYOBReader object, - * false otherwise. - */ -extern JS_PUBLIC_API(bool) -IsReadableStreamReader(const JSObject* obj); - -/** - * Returns true if the given object is an unwrapped - * ReadableStreamDefaultReader object, false otherwise. - */ -extern JS_PUBLIC_API(bool) -IsReadableStreamDefaultReader(const JSObject* obj); - -/** - * Returns true if the given object is an unwrapped - * ReadableStreamBYOBReader object, false otherwise. - */ -extern JS_PUBLIC_API(bool) -IsReadableStreamBYOBReader(const JSObject* obj); - -enum class ReadableStreamMode { - Default, - Byte, - ExternalSource -}; - -/** - * Returns the stream's ReadableStreamMode. If the mode is |Byte| or - * |ExternalSource|, it's possible to acquire a BYOB reader for more optimized - * operations. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(ReadableStreamMode) -ReadableStreamGetMode(const JSObject* stream); - -enum class ReadableStreamReaderMode { - Default, - BYOB -}; - -/** - * Returns true if the given ReadableStream is readable, false if not. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamIsReadable(const JSObject* stream); - -/** - * Returns true if the given ReadableStream is locked, false if not. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamIsLocked(const JSObject* stream); - -/** - * Returns true if the given ReadableStream is disturbed, false if not. - * - * Asserts that |stream| is an ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamIsDisturbed(const JSObject* stream); - -/** - * Cancels the given ReadableStream with the given reason and returns a - * Promise resolved according to the result. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(JSObject*) -ReadableStreamCancel(JSContext* cx, HandleObject stream, HandleValue reason); - -/** - * Creates a reader of the type specified by the mode option and locks the - * stream to the new reader. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(JSObject*) -ReadableStreamGetReader(JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode); - -/** - * Tees the given ReadableStream and stores the two resulting streams in - * outparams. Returns false if the operation fails, e.g. because the stream is - * locked. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamTee(JSContext* cx, HandleObject stream, - MutableHandleObject branch1Stream, MutableHandleObject branch2Stream); - -/** - * Retrieves the desired combined size of additional chunks to fill the given - * ReadableStream's queue. Stores the result in |value| and sets |hasValue| to - * true on success, returns false on failure. - * - * If the stream is errored, the call will succeed but no value will be stored - * in |value| and |hasValue| will be set to false. - * - * Note: This is semantically equivalent to the |desiredSize| getter on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(void) -ReadableStreamGetDesiredSize(JSObject* stream, bool* hasValue, double* value); - -/** - * Closes the given ReadableStream. - * - * Throws a TypeError and returns false if the closing operation fails. - * - * Note: This is semantically equivalent to the |close| method on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamClose(JSContext* cx, HandleObject stream); - -/** - * Returns true if the given ReadableStream reader is locked, false otherwise. - * - * Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or - * ReadableStreamBYOBReader instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamReaderIsClosed(const JSObject* reader); - -/** - * Enqueues the given chunk in the given ReadableStream. - * - * Throws a TypeError and returns false if the enqueing operation fails. - * - * Note: This is semantically equivalent to the |enqueue| method on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * If the ReadableStream has an underlying byte source, the given chunk must - * be a typed array or a DataView. Consider using - * ReadableByteStreamEnqueueBuffer. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamEnqueue(JSContext* cx, HandleObject stream, HandleValue chunk); - -/** - * Enqueues the given buffer as a chunk in the given ReadableStream. - * - * Throws a TypeError and returns false if the enqueing operation fails. - * - * Note: This is semantically equivalent to the |enqueue| method on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. Additionally, the JS version only - * takes typed arrays and ArrayBufferView instances as arguments, whereas - * this takes an ArrayBuffer, obviating the need to wrap it into a typed - * array. - * - * Asserts that |stream| is an unwrapped ReadableStream instance and |buffer| - * an unwrapped ArrayBuffer instance. - */ -extern JS_PUBLIC_API(bool) -ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject stream, HandleObject buffer); - -/** - * Errors the given ReadableStream, causing all future interactions to fail - * with the given error value. - * - * Throws a TypeError and returns false if the erroring operation fails. - * - * Note: This is semantically equivalent to the |error| method on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * Asserts that |stream| is an unwrapped ReadableStream instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error); - -/** - * Cancels the given ReadableStream reader's associated stream. - * - * Throws a TypeError and returns false if the given reader isn't active. - * - * Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or - * ReadableStreamBYOBReader instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason); - -/** - * Cancels the given ReadableStream reader's associated stream. - * - * Throws a TypeError and returns false if the given reader has pending - * read or readInto (for default or byob readers, respectively) requests. - * - * Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or - * ReadableStreamBYOBReader instance. - */ -extern JS_PUBLIC_API(bool) -ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader); - -/** - * Requests a read from the reader's associated ReadableStream and returns the - * resulting PromiseObject. - * - * Returns a Promise that's resolved with the read result once available or - * rejected immediately if the stream is errored or the operation failed. - * - * Asserts that |reader| is an unwrapped ReadableStreamDefaultReader instance. - */ -extern JS_PUBLIC_API(JSObject*) -ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader); - -/** - * Requests a read from the reader's associated ReadableStream into the given - * ArrayBufferView and returns the resulting PromiseObject. - * - * Returns a Promise that's resolved with the read result once available or - * rejected immediately if the stream is errored or the operation failed. - * - * Asserts that |reader| is an unwrapped ReadableStreamDefaultReader and - * |view| an unwrapped typed array or DataView instance. - */ -extern JS_PUBLIC_API(JSObject*) -ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject reader, HandleObject view); - -} // namespace JS - -#endif // js_Realm_h diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp index e68abc366867..f041bad214c0 100644 --- a/js/src/builtin/Stream.cpp +++ b/js/src/builtin/Stream.cpp @@ -6,8 +6,6 @@ #include "builtin/Stream.h" -#include "js/Stream.h" - #include "jscntxt.h" #include "gc/Heap.h" @@ -76,14 +74,9 @@ enum ControllerFlags { ControllerFlag_CloseRequested = 1 << 3, ControllerFlag_TeeBranch = 1 << 4, ControllerFlag_TeeBranch1 = 1 << 5, - ControllerFlag_TeeBranch2 = 1 << 6, - ControllerFlag_ExternalSource = 1 << 7, - ControllerFlag_SourceLocked = 1 << 8, + ControllerFlag_TeeBranch2 = 1 << 6 }; -// Offset at which embedding flags are stored. -constexpr uint8_t ControllerEmbeddingFlagsOffset = 24; - enum BYOBRequestSlots { BYOBRequestSlot_Controller, BYOBRequestSlot_View, @@ -92,7 +85,7 @@ enum BYOBRequestSlots { template MOZ_ALWAYS_INLINE bool -Is(const HandleValue v) +Is(HandleValue v) { return v.isObject() && v.toObject().is(); } @@ -106,6 +99,13 @@ IsReadableStreamController(const JSObject* controller) } #endif // DEBUG +static bool +IsReadableStreamReader(const JSObject* reader) +{ + return reader->is() || + reader->is(); +} + static inline uint32_t ControllerFlags(const NativeObject* controller) { @@ -143,43 +143,30 @@ SetStreamState(ReadableStream* stream, uint32_t state) stream->setFixedSlot(StreamSlot_State, Int32Value(state)); } -bool +inline bool ReadableStream::readable() const { return StreamState(this) & Readable; } -bool +inline bool ReadableStream::closed() const { return StreamState(this) & Closed; } -bool +inline bool ReadableStream::errored() const { return StreamState(this) & Errored; } -bool +inline bool ReadableStream::disturbed() const { return StreamState(this) & Disturbed; } -inline static bool -ReaderHasStream(const NativeObject* reader) -{ - MOZ_ASSERT(JS::IsReadableStreamReader(reader)); - return !reader->getFixedSlot(ReaderSlot_Stream).isUndefined(); -} - -bool -js::ReadableStreamReaderIsClosed(const JSObject* reader) -{ - return !ReaderHasStream(&reader->as()); -} - inline static MOZ_MUST_USE ReadableStream* StreamFromController(const NativeObject* controller) { @@ -188,51 +175,28 @@ StreamFromController(const NativeObject* controller) } inline static MOZ_MUST_USE NativeObject* -ControllerFromStream(const ReadableStream* stream) +ControllerFromStream(ReadableStream* stream) { Value controllerVal = stream->getFixedSlot(StreamSlot_Controller); MOZ_ASSERT(IsReadableStreamController(&controllerVal.toObject())); return &controllerVal.toObject().as(); } -inline static bool -HasController(const ReadableStream* stream) -{ - return !stream->getFixedSlot(StreamSlot_Controller).isUndefined(); -} - -JS::ReadableStreamMode -ReadableStream::mode() const -{ - NativeObject* controller = ControllerFromStream(this); - if (controller->is()) - return JS::ReadableStreamMode::Default; - return controller->as().hasExternalSource() - ? JS::ReadableStreamMode::ExternalSource - : JS::ReadableStreamMode::Byte; -} - inline static MOZ_MUST_USE ReadableStream* StreamFromReader(const NativeObject* reader) { - MOZ_ASSERT(ReaderHasStream(reader)); + MOZ_ASSERT(IsReadableStreamReader(reader)); return &reader->getFixedSlot(ReaderSlot_Stream).toObject().as(); } inline static MOZ_MUST_USE NativeObject* -ReaderFromStream(const NativeObject* stream) +ReaderFromStream(NativeObject* stream) { Value readerVal = stream->getFixedSlot(StreamSlot_Reader); - MOZ_ASSERT(JS::IsReadableStreamReader(&readerVal.toObject())); + MOZ_ASSERT(IsReadableStreamReader(&readerVal.toObject())); return &readerVal.toObject().as(); } -inline static bool -HasReader(const ReadableStream* stream) -{ - return !stream->getFixedSlot(StreamSlot_Reader).isUndefined(); -} - inline static MOZ_MUST_USE JSFunction* NewHandler(JSContext *cx, Native handler, HandleObject target) { @@ -612,7 +576,7 @@ const Class TeeState::class_ = { JSCLASS_HAS_RESERVED_SLOTS(SlotCount) }; -#define CLASS_SPEC(cls, nCtorArgs, nSlots, specFlags, classFlags, classOps) \ +#define CLASS_SPEC(cls, nCtorArgs, nSlots, specFlags) \ const ClassSpec cls::classSpec_ = { \ GenericCreateConstructor, \ GenericCreatePrototype, \ @@ -627,9 +591,8 @@ const ClassSpec cls::classSpec_ = { \ const Class cls::class_ = { \ #cls, \ JSCLASS_HAS_RESERVED_SLOTS(nSlots) | \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##cls) | \ - classFlags, \ - classOps, \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##cls), \ + JS_NULL_CLASS_OPS, \ &cls::classSpec_ \ }; \ \ @@ -642,13 +605,13 @@ const Class cls::protoClass_ = { \ // Streams spec, 3.2.3., steps 1-4. ReadableStream* -ReadableStream::createStream(JSContext* cx, HandleObject proto /* = nullptr */) +ReadableStream::createStream(JSContext* cx) { - Rooted stream(cx, NewObjectWithClassProto(cx, proto)); + Rooted stream(cx, NewBuiltinClassInstance(cx)); if (!stream) return nullptr; - // Step 1: Set this.[[state]] to "readable". + // Step 1 (reordered): Set this.[[state]] to "readable". // Step 2: Set this.[[reader]] and this.[[storedError]] to undefined (implicit). // Step 3: Set this.[[disturbed]] to false (implicit). // Step 4: Set this.[[readableStreamController]] to undefined (implicit). @@ -665,18 +628,17 @@ CreateReadableStreamDefaultController(JSContext* cx, Handle str // Streams spec, 3.2.3., steps 1-4, 8. ReadableStream* ReadableStream::createDefaultStream(JSContext* cx, HandleValue underlyingSource, - HandleValue size, HandleValue highWaterMark, - HandleObject proto /* = nullptr */) + HandleValue size, HandleValue highWaterMark) { - // Steps 1-4. + Rooted stream(cx, createStream(cx)); if (!stream) return nullptr; - // Step 8.b: Set this.[[readableStreamController]] to - // ? Construct(ReadableStreamDefaultController, - // « this, underlyingSource, size, - // highWaterMark »). + // Step b: Set this.[[readableStreamController]] to + // ? Construct(ReadableStreamDefaultController, + // « this, underlyingSource, size, + // highWaterMark »). RootedObject controller(cx, CreateReadableStreamDefaultController(cx, stream, underlyingSource, size, @@ -697,16 +659,16 @@ CreateReadableByteStreamController(JSContext* cx, Handle stream // Streams spec, 3.2.3., steps 1-4, 7. ReadableStream* ReadableStream::createByteStream(JSContext* cx, HandleValue underlyingSource, - HandleValue highWaterMark, HandleObject proto /* = nullptr */) + HandleValue highWaterMark) { - // Steps 1-4. - Rooted stream(cx, createStream(cx, proto)); + + Rooted stream(cx, createStream(cx)); if (!stream) return nullptr; - // Step 7.b: Set this.[[readableStreamController]] to - // ? Construct(ReadableByteStreamController, - // « this, underlyingSource, highWaterMark »). + // Step b: Set this.[[readableStreamController]] to + // ? Construct(ReadableByteStreamController, « this, underlyingSource, + // highWaterMark »). RootedObject controller(cx, CreateReadableByteStreamController(cx, stream, underlyingSource, highWaterMark)); @@ -718,29 +680,6 @@ ReadableStream::createByteStream(JSContext* cx, HandleValue underlyingSource, return stream; } -static MOZ_MUST_USE ReadableByteStreamController* -CreateReadableByteStreamController(JSContext* cx, Handle stream, - void* underlyingSource); - -ReadableStream* -ReadableStream::createExternalSourceStream(JSContext* cx, void* underlyingSource, - uint8_t flags, HandleObject proto /* = nullptr */) -{ - Rooted stream(cx, createStream(cx, proto)); - if (!stream) - return nullptr; - - RootedNativeObject controller(cx, CreateReadableByteStreamController(cx, stream, - underlyingSource)); - if (!controller) - return nullptr; - - stream->setFixedSlot(StreamSlot_Controller, ObjectValue(*controller)); - AddControllerFlags(controller, flags << ControllerEmbeddingFlagsOffset); - - return stream; -} - // Streams spec, 3.2.3. bool ReadableStream::constructor(JSContext* cx, unsigned argc, Value* vp) @@ -786,8 +725,8 @@ ReadableStream::constructor(JSContext* cx, unsigned argc, Value* vp) if (!CompareStrings(cx, type, cx->names().bytes, ¬ByteStream)) return false; - // Step 7.a & 8.a (reordered): If highWaterMark is undefined, let - // highWaterMark be 1 (or 0 for byte streams). + // Step 7 & 8.a (reordered): If highWaterMark is undefined, let + // highWaterMark be 1 (or 0 for byte streams). if (highWaterMark.isUndefined()) highWaterMark = Int32Value(notByteStream ? 1 : 0); @@ -795,15 +734,9 @@ ReadableStream::constructor(JSContext* cx, unsigned argc, Value* vp) // Step 7: If typeString is "bytes", if (!notByteStream) { - // Step 7.b: Set this.[[readableStreamController]] to - // ? Construct(ReadableByteStreamController, - // « this, underlyingSource, highWaterMark »). stream = createByteStream(cx, underlyingSource, highWaterMark); } else if (typeVal.isUndefined()) { // Step 8: Otherwise, if type is undefined, - // Step 8.b: Set this.[[readableStreamController]] to - // ? Construct(ReadableStreamDefaultController, - // « this, underlyingSource, size, highWaterMark »). stream = createDefaultStream(cx, underlyingSource, size, highWaterMark); } else { // Step 9: Otherwise, throw a RangeError exception. @@ -818,6 +751,9 @@ ReadableStream::constructor(JSContext* cx, unsigned argc, Value* vp) return true; } +static MOZ_ALWAYS_INLINE bool +IsReadableStreamLocked(ReadableStream* stream); + // Streams spec, 3.2.4.1. get locked static MOZ_MUST_USE bool ReadableStream_locked_impl(JSContext* cx, const CallArgs& args) @@ -825,7 +761,7 @@ ReadableStream_locked_impl(JSContext* cx, const CallArgs& args) Rooted stream(cx, &args.thisv().toObject().as()); // Step 2: Return ! IsReadableStreamLocked(this). - args.rval().setBoolean(stream->locked()); + args.rval().setBoolean(IsReadableStreamLocked(stream)); return true; } @@ -837,6 +773,9 @@ ReadableStream_locked(JSContext* cx, unsigned argc, Value* vp) return CallNonGenericMethod, ReadableStream_locked_impl>(cx, args); } +static MOZ_MUST_USE JSObject* +ReadableStreamCancel(JSContext* cx, Handle stream, HandleValue reason); + // Streams spec, 3.2.4.2. cancel ( reason ) static MOZ_MUST_USE bool ReadableStream_cancel(JSContext* cx, unsigned argc, Value* vp) @@ -854,14 +793,14 @@ ReadableStream_cancel(JSContext* cx, unsigned argc, Value* vp) // Step 2: If ! IsReadableStreamLocked(this) is true, return a promise // rejected with a TypeError exception. - if (stream->locked()) { + if (IsReadableStreamLocked(stream)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_NOT_LOCKED, "cancel"); return ReturnPromiseRejectedWithPendingError(cx, args); } // Step 3: Return ! ReadableStreamCancel(this, reason). - RootedObject cancelPromise(cx, ReadableStream::cancel(cx, stream, args.get(0))); + RootedObject cancelPromise(cx, ReadableStreamCancel(cx, stream, args.get(0))); if (!cancelPromise) return false; args.rval().setObject(*cancelPromise); @@ -1000,7 +939,7 @@ static const JSPropertySpec ReadableStream_properties[] = { JS_PS_END }; -CLASS_SPEC(ReadableStream, 0, StreamSlotCount, 0, 0, JS_NULL_CLASS_OPS); +CLASS_SPEC(ReadableStream, 0, StreamSlotCount, 0); // Streams spec, 3.3.1. AcquireReadableStreamBYOBReader ( stream ) // Always inlined. @@ -1012,26 +951,22 @@ CLASS_SPEC(ReadableStream, 0, StreamSlotCount, 0, 0, JS_NULL_CLASS_OPS); // Using is instead. // Streams spec, 3.3.4. IsReadableStreamDisturbed ( stream ) -// Using stream->disturbed() instead. +static MOZ_ALWAYS_INLINE bool +IsReadableStreamDisturbed(ReadableStream* stream) +{ + // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). + // Step 2: Return stream.[[disturbed]]. + return stream->disturbed(); +} // Streams spec, 3.3.5. IsReadableStreamLocked ( stream ) -bool -ReadableStream::locked() const +static MOZ_ALWAYS_INLINE bool +IsReadableStreamLocked(ReadableStream* stream) { // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). // Step 2: If stream.[[reader]] is undefined, return false. // Step 3: Return true. - // Special-casing for streams with external sources. Those can be locked - // explicitly via JSAPI, which is indicated by a controller flag. - // IsReadableStreamLocked is called from the controller's constructor, at - // which point we can't yet call ControllerFromStream(stream), but the - // source also can't be locked yet. - if (HasController(this) && - (ControllerFlags(ControllerFromStream(this)) & ControllerFlag_SourceLocked)) - { - return true; - } - return HasReader(this); + return !stream->getFixedSlot(StreamSlot_Reader).isUndefined(); } static MOZ_MUST_USE bool @@ -1123,6 +1058,9 @@ TeeReaderReadHandler(JSContext* cx, unsigned argc, Value* vp) return true; } +static MOZ_MUST_USE JSObject* +ReadableStreamDefaultReaderRead(JSContext* cx, HandleNativeObject reader); + static MOZ_MUST_USE JSObject* ReadableStreamTee_Pull(JSContext* cx, Handle teeState, Handle branchStream) @@ -1136,7 +1074,7 @@ ReadableStreamTee_Pull(JSContext* cx, Handle teeState, // handler which takes the argument result and performs the // following steps: Rooted reader(cx, teeState->reader()); - RootedObject readPromise(cx, ReadableStreamDefaultReader::read(cx, reader)); + RootedObject readPromise(cx, ReadableStreamDefaultReaderRead(cx, reader)); if (!readPromise) return nullptr; @@ -1184,7 +1122,7 @@ ReadableStreamTee_Cancel(JSContext* cx, Handle teeState, Rooted promise(cx, teeState->promise()); // Step b: Let cancelResult be ! ReadableStreamCancel(stream, compositeReason). - RootedObject cancelResult(cx, ReadableStream::cancel(cx, stream, compositeReasonVal)); + RootedObject cancelResult(cx, ReadableStreamCancel(cx, stream, compositeReasonVal)); if (!cancelResult) { if (!RejectWithPendingError(cx, promise)) return nullptr; @@ -1379,6 +1317,9 @@ ReadableStreamAddReadRequest(JSContext* cx, Handle stream) return promise; } +static MOZ_MUST_USE bool +ReadableStreamClose(JSContext* cx, Handle stream); + static MOZ_MUST_USE JSObject* ReadableStreamControllerCancelSteps(JSContext* cx, HandleNativeObject controller, HandleValue reason); @@ -1392,12 +1333,9 @@ ReturnUndefined(JSContext* cx, unsigned argc, Value* vp) return true; } -MOZ_MUST_USE bool -ReadableStreamCloseInternal(JSContext* cx, Handle stream); - // Streams spec, 3.4.3. ReadableStreamCancel ( stream, reason ) -/* static */ MOZ_MUST_USE JSObject* -ReadableStream::cancel(JSContext* cx, Handle stream, HandleValue reason) +static MOZ_MUST_USE JSObject* +ReadableStreamCancel(JSContext* cx, Handle stream, HandleValue reason) { // Step 1: Set stream.[[disturbed]] to true. uint32_t state = StreamState(stream) | ReadableStream::Disturbed; @@ -1416,7 +1354,7 @@ ReadableStream::cancel(JSContext* cx, Handle stream, HandleValu } // Step 4: Perform ! ReadableStreamClose(stream). - if (!ReadableStreamCloseInternal(cx, stream)) + if (!ReadableStreamClose(cx, stream)) return nullptr; // Step 5: Let sourceCancelPromise be @@ -1430,15 +1368,16 @@ ReadableStream::cancel(JSContext* cx, Handle stream, HandleValu // Step 6: Return the result of transforming sourceCancelPromise by a // fulfillment handler that returns undefined. RootedAtom funName(cx, cx->names().empty); - RootedFunction returnUndefined(cx, NewNativeFunction(cx, ReturnUndefined, 0, funName)); + RootedFunction returnUndefined(cx, + NewNativeFunction(cx, ReturnUndefined, 0, funName)); if (!returnUndefined) return nullptr; return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, nullptr); } // Streams spec, 3.4.4. ReadableStreamClose ( stream ) -MOZ_MUST_USE bool -ReadableStreamCloseInternal(JSContext* cx, Handle stream) +static MOZ_MUST_USE bool +ReadableStreamClose(JSContext* cx, Handle stream) { // Step 1: Assert: stream.[[state]] is "readable". MOZ_ASSERT(stream->readable()); @@ -1486,23 +1425,12 @@ ReadableStreamCloseInternal(JSContext* cx, Handle stream) // Step 6: Resolve reader.[[closedPromise]] with undefined. // Step 7: Return (implicit). RootedObject closedPromise(cx, &reader->getFixedSlot(ReaderSlot_ClosedPromise).toObject()); - if (!ResolvePromise(cx, closedPromise, UndefinedHandleValue)) - return false; - - if (stream->mode() == JS::ReadableStreamMode::ExternalSource && - cx->runtime()->readableStreamClosedCallback) - { - NativeObject* controller = ControllerFromStream(stream); - void* source = controller->getFixedSlot(ControllerSlot_UnderlyingSource).toPrivate(); - cx->runtime()->readableStreamClosedCallback(cx, stream, source, stream->embeddingFlags()); - } - - return true; + return ResolvePromise(cx, closedPromise, UndefinedHandleValue); } // Streams spec, 3.4.5. ReadableStreamError ( stream, e ) -MOZ_MUST_USE bool -ReadableStreamErrorInternal(JSContext* cx, Handle stream, HandleValue e) +static MOZ_MUST_USE bool +ReadableStreamError(JSContext* cx, Handle stream, HandleValue e) { // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). @@ -1546,19 +1474,7 @@ ReadableStreamErrorInternal(JSContext* cx, Handle stream, Handl // Step 9: Reject reader.[[closedPromise]] with e. val = reader->getFixedSlot(ReaderSlot_ClosedPromise); Rooted closedPromise(cx, &val.toObject().as()); - if (!PromiseObject::reject(cx, closedPromise, e)) - return false; - - if (stream->mode() == JS::ReadableStreamMode::ExternalSource && - cx->runtime()->readableStreamErroredCallback) - { - NativeObject* controller = ControllerFromStream(stream); - void* source = controller->getFixedSlot(ControllerSlot_UnderlyingSource).toPrivate(); - cx->runtime()->readableStreamErroredCallback(cx, stream, source, - stream->embeddingFlags(), e); - } - - return true; + return PromiseObject::reject(cx, closedPromise, e); } // Streams spec, 3.4.6. ReadableStreamFulfillReadIntoRequest( stream, chunk, done ) @@ -1596,13 +1512,16 @@ ReadableStreamFulfillReadOrReadIntoRequest(JSContext* cx, Handleis()); + // Step 1: Return the number of elements in // stream.[[reader]].[[readRequests]]. - if (!HasReader(stream)) - return 0; - NativeObject* reader = ReaderFromStream(stream); + Value readerVal = stream->getFixedSlot(StreamSlot_Reader); + NativeObject* reader = &readerVal.toObject().as(); + MOZ_ASSERT(reader->is() || + reader->is()); Value readRequests = reader->getFixedSlot(ReaderSlot_Requests); return readRequests.toObject().as().getDenseInitializedLength(); } @@ -1648,7 +1567,7 @@ CreateReadableStreamDefaultReader(JSContext* cx, Handle stream) // Step 2: If ! IsReadableStreamLocked(stream) is true, throw a TypeError // exception. - if (stream->locked()) { + if (IsReadableStreamLocked(stream)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_LOCKED); return nullptr; @@ -1725,7 +1644,7 @@ ReadableStreamDefaultReader_cancel(JSContext* cx, unsigned argc, Value* vp) // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise // rejected with a TypeError exception. RootedNativeObject reader(cx, &args.thisv().toObject().as()); - if (!ReaderHasStream(reader)) { + if (reader->getFixedSlot(ReaderSlot_Stream).isUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAMREADER_NOT_OWNED, "cancel"); return ReturnPromiseRejectedWithPendingError(cx, args); @@ -1752,16 +1671,15 @@ ReadableStreamDefaultReader_read(JSContext* cx, unsigned argc, Value* vp) // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise // rejected with a TypeError exception. - Rooted reader(cx); - reader = &args.thisv().toObject().as(); - if (!ReaderHasStream(reader)) { + RootedNativeObject reader(cx, &args.thisv().toObject().as()); + if (reader->getFixedSlot(ReaderSlot_Stream).isUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAMREADER_NOT_OWNED, "read"); return ReturnPromiseRejectedWithPendingError(cx, args); } // Step 3: Return ! ReadableStreamDefaultReaderRead(this). - JSObject* readPromise = ReadableStreamDefaultReader::read(cx, reader); + JSObject* readPromise = ReadableStreamDefaultReaderRead(cx, reader); if (!readPromise) return false; args.rval().setObject(*readPromise); @@ -1779,7 +1697,7 @@ ReadableStreamDefaultReader_releaseLock_impl(JSContext* cx, const CallArgs& args reader = &args.thisv().toObject().as(); // Step 2: If this.[[ownerReadableStream]] is undefined, return. - if (!ReaderHasStream(reader)) { + if (reader->getFixedSlot(ReaderSlot_Stream).isUndefined()) { args.rval().setUndefined(); return true; } @@ -1823,8 +1741,7 @@ static const JSPropertySpec ReadableStreamDefaultReader_properties[] = { JS_PS_END }; -CLASS_SPEC(ReadableStreamDefaultReader, 1, ReaderSlotCount, ClassSpec::DontDefineConstructor, 0, - JS_NULL_CLASS_OPS); +CLASS_SPEC(ReadableStreamDefaultReader, 1, ReaderSlotCount, ClassSpec::DontDefineConstructor); // Streams spec, 3.6.3 new ReadableStreamBYOBReader ( stream ) @@ -1836,14 +1753,13 @@ CreateReadableStreamBYOBReader(JSContext* cx, Handle stream) // is false, throw a TypeError exception. if (!ControllerFromStream(stream)->is()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, - "ReadableStream.getReader('byob')"); + JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER); return nullptr; } // Step 3: If ! IsReadableStreamLocked(stream) is true, throw a TypeError // exception. - if (stream->locked()) { + if (IsReadableStreamLocked(stream)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_LOCKED); return nullptr; } @@ -1919,7 +1835,7 @@ ReadableStreamBYOBReader_cancel(JSContext* cx, unsigned argc, Value* vp) // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise // rejected with a TypeError exception. RootedNativeObject reader(cx, &args.thisv().toObject().as()); - if (!ReaderHasStream(reader)) { + if (reader->getFixedSlot(ReaderSlot_Stream).isUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAMREADER_NOT_OWNED, "cancel"); return ReturnPromiseRejectedWithPendingError(cx, args); @@ -1933,6 +1849,10 @@ ReadableStreamBYOBReader_cancel(JSContext* cx, unsigned argc, Value* vp) return true; } +static MOZ_MUST_USE JSObject* +ReadableStreamBYOBReaderRead(JSContext* cx, HandleNativeObject reader, + Handle view); + // Streams spec, 3.6.4.3 read ( ) static MOZ_MUST_USE bool ReadableStreamBYOBReader_read(JSContext* cx, unsigned argc, Value* vp) @@ -1947,9 +1867,8 @@ ReadableStreamBYOBReader_read(JSContext* cx, unsigned argc, Value* vp) // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise // rejected with a TypeError exception. - Rooted reader(cx); - reader = &args.thisv().toObject().as(); - if (!ReaderHasStream(reader)) { + RootedNativeObject reader(cx, &args.thisv().toObject().as()); + if (reader->getFixedSlot(ReaderSlot_Stream).isUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAMREADER_NOT_OWNED, "read"); return ReturnPromiseRejectedWithPendingError(cx, args); @@ -1964,20 +1883,20 @@ ReadableStreamBYOBReader_read(JSContext* cx, unsigned argc, Value* vp) return ReturnPromiseRejectedWithPendingError(cx, args); } - Rooted view(cx, &viewVal.toObject().as()); + Rooted view(cx, &viewVal.toObject().as()); // Step 5: If view.[[ByteLength]] is 0, return a promise rejected with a // TypeError exception. // Note: It's ok to use the length in number of elements here because all we // want to know is whether it's < 0. - if (JS_GetArrayBufferViewByteLength(view) == 0) { + if (view->length() == 0) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW); return ReturnPromiseRejectedWithPendingError(cx, args); } // Step 6: Return ! ReadableStreamBYOBReaderRead(this, view). - JSObject* readPromise = ReadableStreamBYOBReader::read(cx, reader, view); + JSObject* readPromise = ReadableStreamBYOBReaderRead(cx, reader, view); if (!readPromise) return false; args.rval().setObject(*readPromise); @@ -1995,7 +1914,7 @@ ReadableStreamBYOBReader_releaseLock_impl(JSContext* cx, const CallArgs& args) reader = &args.thisv().toObject().as(); // Step 2: If this.[[ownerReadableStream]] is undefined, return. - if (!ReaderHasStream(reader)) { + if (reader->getFixedSlot(ReaderSlot_Stream).isUndefined()) { args.rval().setUndefined(); return true; } @@ -2038,7 +1957,7 @@ static const JSFunctionSpec ReadableStreamBYOBReader_methods[] = { JS_FS_END }; -CLASS_SPEC(ReadableStreamBYOBReader, 1, 3, ClassSpec::DontDefineConstructor, 0, JS_NULL_CLASS_OPS); +CLASS_SPEC(ReadableStreamBYOBReader, 1, 3, ClassSpec::DontDefineConstructor); inline static MOZ_MUST_USE bool ReadableStreamControllerCallPullIfNeeded(JSContext* cx, HandleNativeObject controller); @@ -2059,7 +1978,7 @@ ReadableStreamReaderGenericCancel(JSContext* cx, HandleNativeObject reader, Hand // Step 2: Assert: stream is not undefined (implicit). // Step 3: Return ! ReadableStreamCancel(stream, reason). - return &ReadableStreamCancel(cx, stream, reason)->as(); + return ReadableStreamCancel(cx, stream, reason); } // Streams spec, 3.7.4. ReadableStreamReaderGenericInitialize ( reader, stream ) @@ -2148,12 +2067,12 @@ ReadableStreamReaderGenericRelease(JSContext* cx, HandleNativeObject reader) static MOZ_MUST_USE JSObject* ReadableByteStreamControllerPullInto(JSContext* cx, Handle controller, - Handle view); + HandleNativeObject view); // Streams spec, 3.7.6. ReadableStreamBYOBReaderRead ( reader, view ) -/* static */ MOZ_MUST_USE JSObject* -ReadableStreamBYOBReader::read(JSContext* cx, Handle reader, - Handle view) +static MOZ_MUST_USE JSObject* +ReadableStreamBYOBReaderRead(JSContext* cx, HandleNativeObject reader, + Handle view) { MOZ_ASSERT(reader->is()); @@ -2181,9 +2100,11 @@ static MOZ_MUST_USE JSObject* ReadableStreamControllerPullSteps(JSContext* cx, HandleNativeObject controller); // Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader ) -MOZ_MUST_USE JSObject* -ReadableStreamDefaultReader::read(JSContext* cx, Handle reader) +static MOZ_MUST_USE JSObject* +ReadableStreamDefaultReaderRead(JSContext* cx, HandleNativeObject reader) { + MOZ_ASSERT(reader->is()); + // Step 1: Let stream be reader.[[ownerReadableStream]]. // Step 2: Assert: stream is not undefined. Rooted stream(cx, StreamFromReader(reader)); @@ -2376,13 +2297,12 @@ ReadableStreamDefaultController::constructor(JSContext* cx, unsigned argc, Value // Step 2: If stream.[[readableStreamController]] is not undefined, throw a // TypeError exception. - if (HasController(stream)) { + if (!stream->getFixedSlot(StreamSlot_Controller).isUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_CONTROLLER_SET); return false; } - // Steps 3-11. RootedObject controller(cx, CreateReadableStreamDefaultController(cx, stream, args.get(1), args.get(2), args.get(3))); if (!controller) @@ -2440,10 +2360,13 @@ static MOZ_MUST_USE bool ReadableStreamDefaultControllerClose(JSContext* cx, Handle controller); -// Unified implementation of steps 2-3 of 3.8.4.2 and 3.10.4.3. +// Streams spec, 3.8.4.2 close() static MOZ_MUST_USE bool -VerifyControllerStateForClosing(JSContext* cx, HandleNativeObject controller) +ReadableStreamDefaultController_close_impl(JSContext* cx, const CallArgs& args) { + Rooted controller(cx); + controller = &args.thisv().toObject().as(); + // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception. if (ControllerFlags(controller) & ControllerFlag_CloseRequested) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, @@ -2460,20 +2383,6 @@ VerifyControllerStateForClosing(JSContext* cx, HandleNativeObject controller) return false; } - return true; -} - -// Streams spec, 3.8.4.2 close() -static MOZ_MUST_USE bool -ReadableStreamDefaultController_close_impl(JSContext* cx, const CallArgs& args) -{ - Rooted controller(cx); - controller = &args.thisv().toObject().as(); - - // Steps 2-3. - if (!VerifyControllerStateForClosing(cx, controller)) - return false; - // Step 4: Perform ! ReadableStreamDefaultControllerClose(this). if (!ReadableStreamDefaultControllerClose(cx, controller)) return false; @@ -2546,10 +2455,12 @@ ReadableStreamDefaultController_error_impl(JSContext* cx, const CallArgs& args) controller = &args.thisv().toObject().as(); // Step 2: Let stream be this.[[controlledReadableStream]]. + ReadableStream* stream = StreamFromController(controller); + // Step 3: If stream.[[state]] is not "readable", throw a TypeError exception. - if (!StreamFromController(controller)->readable()) { + if (!stream->readable()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, "error"); + JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, "close"); return false; } @@ -2583,8 +2494,7 @@ static const JSFunctionSpec ReadableStreamDefaultController_methods[] = { JS_FS_END }; -CLASS_SPEC(ReadableStreamDefaultController, 4, 7, ClassSpec::DontDefineConstructor, 0, - JS_NULL_CLASS_OPS); +CLASS_SPEC(ReadableStreamDefaultController, 4, 7, ClassSpec::DontDefineConstructor); /** * Unified implementation of ReadableStream controllers' [[CancelSteps]] internal @@ -2631,15 +2541,6 @@ ReadableStreamControllerCancelSteps(JSContext* cx, HandleNativeObject controller return ReadableStreamTee_Cancel(cx, teeState, defaultController, reason); } - if (ControllerFlags(controller) & ControllerFlag_ExternalSource) { - void* source = underlyingSource.toPrivate(); - Rooted stream(cx, StreamFromController(controller)); - RootedValue rval(cx); - rval = cx->runtime()->readableStreamCancelCallback(cx, stream, source, - stream->embeddingFlags(), reason); - return PromiseObject::unforgeableResolve(cx, rval); - } - return PromiseInvokeOrNoop(cx, underlyingSource, cx->names().cancel, reason); } @@ -2671,8 +2572,8 @@ ReadableStreamDefaultControllerPullSteps(JSContext* cx, HandleNativeObject contr // perform ! ReadableStreamClose(stream). bool closeRequested = ControllerFlags(controller) & ControllerFlag_CloseRequested; if (closeRequested && queue->getDenseInitializedLength() == 0) { - if (!ReadableStreamCloseInternal(cx, stream)) - return nullptr; + if (!ReadableStreamClose(cx, stream)) + return nullptr; } // Step c: Otherwise, perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this). @@ -2749,9 +2650,6 @@ ControllerPullFailedHandler(JSContext* cx, unsigned argc, Value* vp) static bool ReadableStreamControllerShouldCallPull(NativeObject* controller); -static MOZ_MUST_USE double -ReadableStreamControllerGetDesiredSizeUnchecked(NativeObject* controller); - // Streams spec, 3.9.2 ReadableStreamDefaultControllerCallPullIfNeeded ( controller ) // and // Streams spec, 3.12.3. ReadableByteStreamControllerCallPullIfNeeded ( controller ) @@ -2792,13 +2690,6 @@ ReadableStreamControllerCallPullIfNeeded(JSContext* cx, HandleNativeObject contr Rooted teeState(cx, &underlyingSource.toObject().as()); Rooted stream(cx, StreamFromController(controller)); pullPromise = ReadableStreamTee_Pull(cx, teeState, stream); - } else if (ControllerFlags(controller) & ControllerFlag_ExternalSource) { - void* source = underlyingSource.toPrivate(); - Rooted stream(cx, StreamFromController(controller)); - double desiredSize = ReadableStreamControllerGetDesiredSizeUnchecked(controller); - cx->runtime()->readableStreamDataRequestCallback(cx, stream, source, - stream->embeddingFlags(), desiredSize); - pullPromise = PromiseObject::unforgeableResolve(cx, UndefinedHandleValue); } else { pullPromise = PromiseInvokeOrNoop(cx, underlyingSource, cx->names().pull, controllerVal); } @@ -2846,7 +2737,7 @@ ReadableStreamControllerShouldCallPull(NativeObject* controller) // Step 5: If ! IsReadableStreamLocked(stream) is true and // ! ReadableStreamGetNumReadRequests(stream) > 0, return true. // Steps 5-6 of 3.12.24 are equivalent in our implementation. - if (stream->locked() && ReadableStreamGetNumReadRequests(stream) > 0) + if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) return true; // Step 6: Let desiredSize be ReadableStreamDefaultControllerGetDesiredSize(controller). @@ -2879,11 +2770,15 @@ ReadableStreamDefaultControllerClose(JSContext* cx, RootedNativeObject queue(cx); queue = &controller->getFixedSlot(QueueContainerSlot_Queue).toObject().as(); if (queue->getDenseInitializedLength() == 0) - return ReadableStreamCloseInternal(cx, stream); + return ReadableStreamClose(cx, stream); return true; } +static MOZ_MUST_USE bool +ReadableStreamFulfillReadOrReadIntoRequest(JSContext* cx, Handle stream, + HandleValue chunk, bool done); + static MOZ_MUST_USE bool EnqueueValueWithSize(JSContext* cx, HandleNativeObject container, HandleValue value, HandleValue sizeVal); @@ -2906,7 +2801,7 @@ ReadableStreamDefaultControllerEnqueue(JSContext* cx, // Step 4: If ! IsReadableStreamLocked(stream) is true and // ! ReadableStreamGetNumReadRequests(stream) > 0, perform // ! ReadableStreamFulfillReadRequest(stream, chunk, false). - if (stream->locked() && ReadableStreamGetNumReadRequests(stream) > 0) { + if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) { if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, stream, chunk, false)) return false; } else { @@ -2955,6 +2850,9 @@ ReadableStreamDefaultControllerEnqueue(JSContext* cx, return true; } +static MOZ_MUST_USE bool +ReadableStreamError(JSContext* cx, Handle stream, HandleValue e); + static MOZ_MUST_USE bool ReadableByteStreamControllerClearPendingPullIntos(JSContext* cx, HandleNativeObject controller); @@ -2986,7 +2884,7 @@ ReadableStreamControllerError(JSContext* cx, HandleNativeObject controller, Hand return false; // Step 4 (or 5): Perform ! ReadableStreamError(stream, e). - return ReadableStreamErrorInternal(cx, stream, e); + return ReadableStreamError(cx, stream, e); } // Streams spec, 3.9.7. ReadableStreamDefaultControllerErrorIfNeeded ( controller, e ) nothrow @@ -3117,11 +3015,6 @@ CreateReadableByteStreamController(JSContext* cx, Handle stream return controller; } -bool -ReadableByteStreamController::hasExternalSource() { - return ControllerFlags(this) & ControllerFlag_ExternalSource; -} - // Streams spec, 3.10.3. // new ReadableByteStreamController ( stream, underlyingByteSource, // highWaterMark ) @@ -3145,7 +3038,7 @@ ReadableByteStreamController::constructor(JSContext* cx, unsigned argc, Value* v // Step 2: If stream.[[readableStreamController]] is not undefined, throw a // TypeError exception. - if (HasController(stream)) { + if (!stream->getFixedSlot(StreamSlot_Controller).isUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_CONTROLLER_SET); return false; @@ -3160,71 +3053,6 @@ ReadableByteStreamController::constructor(JSContext* cx, unsigned argc, Value* v return true; } -// Version of the ReadableByteStreamConstructor that's specialized for -// handling external, embedding-provided, underlying sources. -static MOZ_MUST_USE ReadableByteStreamController* -CreateReadableByteStreamController(JSContext* cx, Handle stream, - void* underlyingSource) -{ - Rooted controller(cx); - controller = NewBuiltinClassInstance(cx); - if (!controller) - return nullptr; - - // Step 3: Set this.[[controlledReadableStream]] to stream. - controller->setFixedSlot(ControllerSlot_Stream, ObjectValue(*stream)); - - // Step 4: Set this.[[underlyingByteSource]] to underlyingByteSource. - controller->setFixedSlot(ControllerSlot_UnderlyingSource, PrivateValue(underlyingSource)); - - // Step 5: Set this.[[pullAgain]], and this.[[pulling]] to false. - controller->setFixedSlot(ControllerSlot_Flags, Int32Value(ControllerFlag_ExternalSource)); - - // Step 6: Perform ! ReadableByteStreamControllerClearPendingPullIntos(this). - // Omitted. - - // Step 7: Perform ! ResetQueue(this). - controller->setFixedSlot(QueueContainerSlot_TotalSize, Int32Value(0)); - - // Step 8: Set this.[[started]] and this.[[closeRequested]] to false. - // Step 9: Set this.[[strategyHWM]] to - // ? ValidateAndNormalizeHighWaterMark(highWaterMark). - controller->setFixedSlot(ControllerSlot_StrategyHWM, Int32Value(0)); - - // Step 10: Let autoAllocateChunkSize be - // ? GetV(underlyingByteSource, "autoAllocateChunkSize"). - // Step 11: If autoAllocateChunkSize is not undefined, - // Step 12: Set this.[[autoAllocateChunkSize]] to autoAllocateChunkSize. - // Omitted. - - // Step 13: Set this.[[pendingPullIntos]] to a new empty List. - if (!SetNewList(cx, controller, ByteControllerSlot_PendingPullIntos)) - return nullptr; - - // Step 14: Let controller be this (implicit). - // Step 15: Let startResult be - // ? InvokeOrNoop(underlyingSource, "start", « this »). - // Omitted. - - // Step 16: Let startPromise be a promise resolved with startResult: - RootedObject startPromise(cx, PromiseObject::unforgeableResolve(cx, UndefinedHandleValue)); - if (!startPromise) - return nullptr; - - RootedObject onStartFulfilled(cx, NewHandler(cx, ControllerStartHandler, controller)); - if (!onStartFulfilled) - return nullptr; - - RootedObject onStartRejected(cx, NewHandler(cx, ControllerStartFailedHandler, controller)); - if (!onStartRejected) - return nullptr; - - if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, onStartRejected)) - return nullptr; - - return controller; -} - static MOZ_MUST_USE ReadableStreamBYOBRequest* CreateReadableStreamBYOBRequest(JSContext* cx, Handle controller, HandleObject view); @@ -3307,9 +3135,20 @@ ReadableByteStreamController_close_impl(JSContext* cx, const CallArgs& args) Rooted controller(cx); controller = &args.thisv().toObject().as(); - // Steps 2-3. - if (!VerifyControllerStateForClosing(cx, controller)) + // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception. + if (ControllerFlags(controller) & ControllerFlag_CloseRequested) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_CLOSED, "close"); return false; + } + + // Step 3: If this.[[controlledReadableStream]].[[state]] is not "readable", + // throw a TypeError exception. + if (!StreamFromController(controller)->readable()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, "close"); + return false; + } // Step 4: Perform ? ReadableByteStreamControllerClose(this). if (!ReadableByteStreamControllerClose(cx, controller)) @@ -3361,8 +3200,7 @@ ReadableByteStreamController_enqueue_impl(JSContext* cx, const CallArgs& args) // throw a TypeError exception. if (!chunkVal.isObject() || !JS_IsArrayBufferViewObject(&chunkVal.toObject())) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, - "ReadableByteStreamController#enqueue"); + JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK); return false; } RootedObject chunk(cx, &chunkVal.toObject()); @@ -3430,42 +3268,7 @@ static const JSFunctionSpec ReadableByteStreamController_methods[] = { JS_FS_END }; -static void -ReadableByteStreamControllerFinalize(FreeOp* fop, JSObject* obj) -{ - ReadableByteStreamController& controller = obj->as(); - - if (controller.getFixedSlot(ControllerSlot_Flags).isUndefined()) - return; - - uint32_t flags = ControllerFlags(&controller); - if (!(flags & ControllerFlag_ExternalSource)) - return; - - uint8_t embeddingFlags = flags >> ControllerEmbeddingFlagsOffset; - - void* underlyingSource = controller.getFixedSlot(ControllerSlot_UnderlyingSource).toPrivate(); - obj->runtimeFromAnyThread()->readableStreamFinalizeCallback(underlyingSource, embeddingFlags); -} - -static const ClassOps ReadableByteStreamControllerClassOps = { - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* newEnumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - ReadableByteStreamControllerFinalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - nullptr, /* trace */ -}; - -CLASS_SPEC(ReadableByteStreamController, 3, 9, ClassSpec::DontDefineConstructor, - JSCLASS_BACKGROUND_FINALIZE, &ReadableByteStreamControllerClassOps); +CLASS_SPEC(ReadableByteStreamController, 3, 9, ClassSpec::DontDefineConstructor); // Streams spec, 3.10.5.1. [[PullSteps]] () // Unified with 3.8.5.1 above. @@ -3490,61 +3293,32 @@ ReadableByteStreamControllerPullSteps(JSContext* cx, HandleNativeObject controll // Step 3.a: MOZ_ASSERT: ! ReadableStreamGetNumReadRequests(_stream_) is 0. MOZ_ASSERT(ReadableStreamGetNumReadRequests(stream) == 0); - RootedObject view(cx); + // Step 3.b: Let entry be the first element of this.[[queue]]. + // Step 3.c: Remove entry from this.[[queue]], shifting all other elements + // downward (so that the second becomes the first, and so on). + val = controller->getFixedSlot(QueueContainerSlot_Queue); + RootedNativeObject queue(cx, &val.toObject().as()); + Rooted entry(cx, ShiftFromList(cx, queue)); + MOZ_ASSERT(entry); - if (stream->mode() == JS::ReadableStreamMode::ExternalSource) { - val = controller->getFixedSlot(ControllerSlot_UnderlyingSource); - void* underlyingSource = val.toPrivate(); - - view = JS_NewUint8Array(cx, queueTotalSize); - if (!view) - return nullptr; - - size_t bytesWritten; - { - JS::AutoSuppressGCAnalysis noGC(cx); - bool dummy; - void* buffer = JS_GetArrayBufferViewData(view, &dummy, noGC); - auto cb = cx->runtime()->readableStreamWriteIntoReadRequestCallback; - MOZ_ASSERT(cb); - // TODO: use bytesWritten to correctly update the request's state. - cb(cx, stream, underlyingSource, stream->embeddingFlags(), buffer, - queueTotalSize, &bytesWritten); - } - - queueTotalSize = queueTotalSize - bytesWritten; - } else { - // Step 3.b: Let entry be the first element of this.[[queue]]. - // Step 3.c: Remove entry from this.[[queue]], shifting all other elements - // downward (so that the second becomes the first, and so on). - val = controller->getFixedSlot(QueueContainerSlot_Queue); - RootedNativeObject queue(cx, &val.toObject().as()); - Rooted entry(cx, ShiftFromList(cx, queue)); - MOZ_ASSERT(entry); - - queueTotalSize = queueTotalSize - entry->byteLength(); - - // Step 3.f: Let view be ! Construct(%Uint8Array%, « entry.[[buffer]], - // entry.[[byteOffset]], entry.[[byteLength]] »). - // (reordered) - RootedObject buffer(cx, entry->buffer()); - - uint32_t byteOffset = entry->byteOffset(); - view = JS_NewUint8ArrayWithBuffer(cx, buffer, byteOffset, entry->byteLength()); - if (!view) - return nullptr; - } - - // Step 3.d: Set this.[[queueTotalSize]] to - // this.[[queueTotalSize]] − entry.[[byteLength]]. - // (reordered) - controller->setFixedSlot(QueueContainerSlot_TotalSize, Int32Value(queueTotalSize)); + // Step 3.d: Set this.[[queueTotalSize]] to this.[[queueTotalSize]] − entry.[[byteLength]]. + uint32_t byteLength = entry->byteLength(); + queueTotalSize = queueTotalSize - byteLength; + controller->setFixedSlot(QueueContainerSlot_TotalSize, NumberValue(queueTotalSize)); // Step 3.e: Perform ! ReadableByteStreamControllerHandleQueueDrain(this). - // (reordered) if (!ReadableByteStreamControllerHandleQueueDrain(cx, controller)) return nullptr; + // Step 3.f: Let view be ! Construct(%Uint8Array%, « entry.[[buffer]], + // entry.[[byteOffset]], entry.[[byteLength]] »). + RootedObject buffer(cx, entry->buffer()); + + uint32_t byteOffset = entry->byteOffset(); + RootedObject view(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, byteOffset, byteLength)); + if (!view) + return nullptr; + // Step 3.g: Return a promise resolved with ! CreateIterResultObject(view, false). val.setObject(*view); RootedObject iterResult(cx, CreateIterResultObject(cx, val, false)); @@ -3777,8 +3551,7 @@ ReadableStreamBYOBRequest_respondWithNewView_impl(JSContext* cx, const CallArgs& // a TypeError exception. if (!viewVal.isObject() || !JS_IsArrayBufferViewObject(&viewVal.toObject())) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, - "ReadableStreamBYOBRequest#respondWithNewView"); + JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK); return false; } @@ -3817,8 +3590,7 @@ static const JSFunctionSpec ReadableStreamBYOBRequest_methods[] = { JS_FS_END }; -CLASS_SPEC(ReadableStreamBYOBRequest, 3, 2, ClassSpec::DontDefineConstructor, 0, - JS_NULL_CLASS_OPS); +CLASS_SPEC(ReadableStreamBYOBRequest, 3, 2, ClassSpec::DontDefineConstructor); // Streams spec, 3.12.1. IsReadableStreamBYOBRequest ( x ) // Implemented via is() @@ -3896,7 +3668,7 @@ ReadableByteStreamControllerClose(JSContext* cx, Handlereadable()); - // To make enqueuing chunks via JSAPI nicer, we want to be able to deal - // with ArrayBuffer objects in addition to ArrayBuffer views here. - // This cannot happen when enqueuing happens via - // ReadableByteStreamController_enqueue because that throws if invoked - // with anything but an ArrayBuffer view. + // Step 4: Let buffer be chunk.[[ViewedArrayBuffer]]. + bool dummy; + RootedObject buffer(cx, JS_GetArrayBufferViewBuffer(cx, chunk, &dummy)); + if (!buffer) + return false; - Rooted buffer(cx); - uint32_t byteOffset; - uint32_t byteLength; + // Step 5: Let byteOffset be chunk.[[ByteOffset]]. + uint32_t byteOffset = JS_GetArrayBufferViewByteOffset(chunk); - if (chunk->is()) { - // Steps 4-6 for ArrayBuffer objects. - buffer = &chunk->as(); - byteOffset = 0; - byteLength = buffer->byteLength(); - } else { - // Step 4: Let buffer be chunk.[[ViewedArrayBuffer]]. - bool dummy; - JSObject* bufferObj = JS_GetArrayBufferViewBuffer(cx, chunk, &dummy); - if (!bufferObj) - return false; - buffer = &bufferObj->as(); - - // Step 5: Let byteOffset be chunk.[[ByteOffset]]. - byteOffset = JS_GetArrayBufferViewByteOffset(chunk); - - // Step 6: Let byteLength be chunk.[[ByteLength]]. - byteLength = JS_GetArrayBufferViewByteLength(chunk); - } + // Step 6: Let byteLength be chunk.[[ByteLength]]. + uint32_t byteLength = JS_GetArrayBufferViewByteLength(chunk); // Step 7: Let transferredBuffer be ! TransferArrayBuffer(buffer). RootedArrayBufferObject transferredBuffer(cx, TransferArrayBuffer(cx, buffer)); @@ -4103,7 +3857,7 @@ ReadableByteStreamControllerEnqueue(JSContext* cx, } else { // Step b: Otherwise, // Step i: Assert: ! IsReadableStreamLocked(stream) is false. - MOZ_ASSERT(!stream->locked()); + MOZ_ASSERT(!IsReadableStreamLocked(stream)); // Step ii: Perform // ! ReadableByteStreamControllerEnqueueChunkToQueue(controller, @@ -4228,39 +3982,6 @@ ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(JSContext* cx, *ready = true; } - if (ControllerFlags(controller) & ControllerFlag_ExternalSource) { - // TODO: it probably makes sense to eagerly drain the underlying source. - // We have a buffer lying around anyway, whereas the source might be - // able to free or reuse buffers once their content is copied into - // our buffer. - if (!ready) - return true; - - Value val = controller->getFixedSlot(ControllerSlot_UnderlyingSource); - void* underlyingSource = val.toPrivate(); - - RootedArrayBufferObject targetBuffer(cx, pullIntoDescriptor->buffer()); - Rooted stream(cx, StreamFromController(controller)); - - size_t bytesWritten; - { - JS::AutoSuppressGCAnalysis noGC(cx); - bool dummy; - uint8_t* buffer = JS_GetArrayBufferData(targetBuffer, &dummy, noGC); - buffer += bytesFilled; - auto cb = cx->runtime()->readableStreamWriteIntoReadRequestCallback; - MOZ_ASSERT(cb); - cb(cx, stream, underlyingSource, stream->embeddingFlags(), buffer, - totalBytesToCopyRemaining, &bytesWritten); - pullIntoDescriptor->setBytesFilled(bytesFilled + bytesWritten); - } - - queueTotalSize -= bytesWritten; - controller->setFixedSlot(QueueContainerSlot_TotalSize, Int32Value(queueTotalSize)); - - return true; - } - // Step 9: Let queue be controller.[[queue]]. RootedValue val(cx, controller->getFixedSlot(QueueContainerSlot_Queue)); RootedNativeObject queue(cx, &val.toObject().as()); @@ -4364,7 +4085,7 @@ ReadableByteStreamControllerHandleQueueDrain(JSContext* cx, HandleNativeObject c bool closeRequested = ControllerFlags(controller) & ControllerFlag_CloseRequested; if (totalSize == 0 && closeRequested) { // Step a: Perform ! ReadableStreamClose(controller.[[controlledReadableStream]]). - return ReadableStreamCloseInternal(cx, stream); + return ReadableStreamClose(cx, stream); } // Step 3: Otherwise, @@ -4454,9 +4175,10 @@ ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(JSContext* cx, static MOZ_MUST_USE JSObject* ReadableByteStreamControllerPullInto(JSContext* cx, Handle controller, - Handle view) + HandleNativeObject view) { MOZ_ASSERT(controller->is()); + MOZ_ASSERT(JS_IsArrayBufferViewObject(view)); // Step 1: Let stream be controller.[[controlledReadableStream]]. Rooted stream(cx, StreamFromController(controller)); @@ -4950,7 +4672,7 @@ static const JSFunctionSpec ByteLengthQueuingStrategy_methods[] = { JS_FS_END }; -CLASS_SPEC(ByteLengthQueuingStrategy, 1, 0, 0, 0, JS_NULL_CLASS_OPS); +CLASS_SPEC(ByteLengthQueuingStrategy, 1, 0, 0); // Streams spec, 6.2.2. new CountQueuingStrategy({ highWaterMark }) bool @@ -4997,7 +4719,7 @@ static const JSFunctionSpec CountQueuingStrategy_methods[] = { JS_FS_END }; -CLASS_SPEC(CountQueuingStrategy, 1, 0, 0, 0, JS_NULL_CLASS_OPS); +CLASS_SPEC(CountQueuingStrategy, 1, 0, 0); #undef CLASS_SPEC @@ -5236,258 +4958,3 @@ ValidateAndNormalizeQueuingStrategy(JSContext* cx, HandleValue size, // Step 3: Return Record {[[size]]: size, [[highWaterMark]]: highWaterMark}. return true; } - -MOZ_MUST_USE bool -js::ReadableStreamReaderCancel(JSContext* cx, HandleObject readerObj, HandleValue reason) -{ - MOZ_ASSERT(IsReadableStreamReader(readerObj)); - RootedNativeObject reader(cx, &readerObj->as()); - MOZ_ASSERT(StreamFromReader(reader)); - return ReadableStreamReaderGenericCancel(cx, reader, reason); -} - -MOZ_MUST_USE bool -js::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject readerObj) -{ - MOZ_ASSERT(IsReadableStreamReader(readerObj)); - RootedNativeObject reader(cx, &readerObj->as()); - MOZ_ASSERT(ReadableStreamGetNumReadRequests(StreamFromReader(reader)) == 0); - return ReadableStreamReaderGenericRelease(cx, reader); -} - -MOZ_MUST_USE bool -ReadableStream::enqueue(JSContext* cx, Handle stream, HandleValue chunk) -{ - Rooted controller(cx); - controller = &ControllerFromStream(stream)->as(); - - MOZ_ASSERT(!(ControllerFlags(controller) & ControllerFlag_CloseRequested)); - MOZ_ASSERT(stream->readable()); - - return ReadableStreamDefaultControllerEnqueue(cx, controller, chunk); -} - -MOZ_MUST_USE bool -ReadableStream::enqueueBuffer(JSContext* cx, Handle stream, - Handle chunk) -{ - Rooted controller(cx); - controller = &ControllerFromStream(stream)->as(); - - MOZ_ASSERT(!(ControllerFlags(controller) & ControllerFlag_CloseRequested)); - MOZ_ASSERT(stream->readable()); - - return ReadableByteStreamControllerEnqueue(cx, controller, chunk); -} - -void -ReadableStream::desiredSize(bool* hasSize, double* size) const -{ - if (errored()) { - *hasSize = false; - return; - } - - *hasSize = true; - - if (closed()) { - *size = 0; - return; - } - - NativeObject* controller = ControllerFromStream(this); - *size = ReadableStreamControllerGetDesiredSizeUnchecked(controller); -} - -/*static */ bool -ReadableStream::getExternalSource(JSContext* cx, Handle stream, void** source) -{ - MOZ_ASSERT(stream->mode() == JS::ReadableStreamMode::ExternalSource); - if (stream->locked()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_READABLESTREAM_LOCKED); - return false; - } - if (!stream->readable()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, - "ReadableStreamGetExternalUnderlyingSource"); - return false; - } - - auto controller = &ControllerFromStream(stream)->as(); - AddControllerFlags(controller, ControllerFlag_SourceLocked); - *source = controller->getFixedSlot(ControllerSlot_UnderlyingSource).toPrivate(); - return true; -} - -void -ReadableStream::releaseExternalSource() -{ - MOZ_ASSERT(mode() == JS::ReadableStreamMode::ExternalSource); - MOZ_ASSERT(locked()); - auto controller = ControllerFromStream(this); - MOZ_ASSERT(ControllerFlags(controller) & ControllerFlag_SourceLocked); - RemoveControllerFlags(controller, ControllerFlag_SourceLocked); -} - -uint8_t -ReadableStream::embeddingFlags() const -{ - uint8_t flags = ControllerFlags(ControllerFromStream(this)) >> ControllerEmbeddingFlagsOffset; - MOZ_ASSERT_IF(flags, mode() == JS::ReadableStreamMode::ExternalSource); - return flags; -} - -// Streams spec, 3.10.4.4. steps 1-3 -// and -// Streams spec, 3.12.8. steps 8-9 -// -// Adapted to handling updates signaled by the embedding for streams with -// external underlying sources. -// -// The remaining steps of those two functions perform checks and asserts that -// don't apply to streams with external underlying sources. -MOZ_MUST_USE bool -ReadableStream::updateDataAvailableFromSource(JSContext* cx, Handle stream, - uint32_t availableData) -{ - Rooted controller(cx); - controller = &ControllerFromStream(stream)->as(); - - // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception. - if (ControllerFlags(controller) & ControllerFlag_CloseRequested) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_CLOSED, "enqueue"); - return false; - } - - // Step 3: If this.[[controlledReadableStream]].[[state]] is not "readable", - // throw a TypeError exception. - if (!StreamFromController(controller)->readable()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, "enqueue"); - return false; - } - - RemoveControllerFlags(controller, ControllerFlag_Pulling | ControllerFlag_PullAgain); - -#if DEBUG - uint32_t oldAvailableData = controller->getFixedSlot(QueueContainerSlot_TotalSize).toInt32(); -#endif // DEBUG - controller->setFixedSlot(QueueContainerSlot_TotalSize, Int32Value(availableData)); - - // Step 8.a: If ! ReadableStreamGetNumReadRequests(stream) is 0, - // Reordered because for externally-sourced streams it applies regardless - // of reader type. - if (ReadableStreamGetNumReadRequests(stream) == 0) - return true; - - // Step 8: If ! ReadableStreamHasDefaultReader(stream) is true - if (ReadableStreamHasDefaultReader(stream)) { - // Step b: Otherwise, - // Step i: Assert: controller.[[queue]] is empty. - MOZ_ASSERT(oldAvailableData == 0); - - // Step ii: Let transferredView be - // ! Construct(%Uint8Array%, transferredBuffer, byteOffset, byteLength). - JSObject* viewObj = JS_NewUint8Array(cx, availableData); - Rooted transferredView(cx, &viewObj->as()); - if (!transferredView) - return false; - - Value val = controller->getFixedSlot(ControllerSlot_UnderlyingSource); - void* underlyingSource = val.toPrivate(); - - size_t bytesWritten; - { - JS::AutoSuppressGCAnalysis noGC(cx); - bool dummy; - void* buffer = JS_GetArrayBufferViewData(transferredView, &dummy, noGC); - auto cb = cx->runtime()->readableStreamWriteIntoReadRequestCallback; - MOZ_ASSERT(cb); - // TODO: use bytesWritten to correctly update the request's state. - cb(cx, stream, underlyingSource, stream->embeddingFlags(), buffer, - availableData, &bytesWritten); - } - - // Step iii: Perform ! ReadableStreamFulfillReadRequest(stream, transferredView, false). - RootedValue chunk(cx, ObjectValue(*transferredView)); - if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, stream, chunk, false)) - return false; - - controller->setFixedSlot(QueueContainerSlot_TotalSize, - Int32Value(availableData - bytesWritten)); - } else if (ReadableStreamHasBYOBReader(stream)) { - // Step 9: Otherwise, - // Step a: If ! ReadableStreamHasBYOBReader(stream) is true, - // Step i: Perform - // (Not needed for external underlying sources.) - - // Step ii: Perform ! ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller). - if (!ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(cx, controller)) - return false; - } else { - // Step b: Otherwise, - // Step i: Assert: ! IsReadableStreamLocked(stream) is false. - MOZ_ASSERT(!stream->locked()); - - // Step ii: Perform - // ! ReadableByteStreamControllerEnqueueChunkToQueue(controller, - // transferredBuffer, - // byteOffset, - // byteLength). - // (Not needed for external underlying sources.) - } - - return true; -} - -MOZ_MUST_USE bool -ReadableStream::close(JSContext* cx, Handle stream) -{ - RootedNativeObject controllerObj(cx, ControllerFromStream(stream)); - if (!VerifyControllerStateForClosing(cx, controllerObj)) - return false; - - if (controllerObj->is()) { - Rooted controller(cx); - controller = &controllerObj->as(); - return ReadableStreamDefaultControllerClose(cx, controller); - } - - Rooted controller(cx); - controller = &controllerObj->as(); - return ReadableByteStreamControllerClose(cx, controller); -} - -MOZ_MUST_USE bool -ReadableStream::error(JSContext* cx, Handle stream, HandleValue reason) -{ - // Step 3: If stream.[[state]] is not "readable", throw a TypeError exception. - if (!stream->readable()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, "error"); - return false; - } - - // Step 4: Perform ! ReadableStreamDefaultControllerError(this, e). - RootedNativeObject controller(cx, ControllerFromStream(stream)); - return ReadableStreamControllerError(cx, controller, reason); -} - -MOZ_MUST_USE bool -ReadableStream::tee(JSContext* cx, Handle stream, bool cloneForBranch2, - MutableHandle branch1Stream, - MutableHandle branch2Stream) -{ - return ReadableStreamTee(cx, stream, false, branch1Stream, branch2Stream); -} - -MOZ_MUST_USE NativeObject* -ReadableStream::getReader(JSContext* cx, Handle stream, - JS::ReadableStreamReaderMode mode) -{ - if (mode == JS::ReadableStreamReaderMode::Default) - return CreateReadableStreamDefaultReader(cx, stream); - return CreateReadableStreamBYOBReader(cx, stream); -} diff --git a/js/src/builtin/Stream.h b/js/src/builtin/Stream.h index 6c3ac20301b6..c8b6025d8f46 100644 --- a/js/src/builtin/Stream.h +++ b/js/src/builtin/Stream.h @@ -7,10 +7,8 @@ #ifndef builtin_Stream_h #define builtin_Stream_h -#include "builtin/Promise.h" #include "vm/NativeObject.h" - namespace js { class AutoSetNewObjectMetadata; @@ -19,50 +17,14 @@ class ReadableStream : public NativeObject { public: static ReadableStream* createDefaultStream(JSContext* cx, HandleValue underlyingSource, - HandleValue size, HandleValue highWaterMark, - HandleObject proto = nullptr); + HandleValue size, HandleValue highWaterMark); static ReadableStream* createByteStream(JSContext* cx, HandleValue underlyingSource, - HandleValue highWaterMark, - HandleObject proto = nullptr); - static ReadableStream* createExternalSourceStream(JSContext* cx, void* underlyingSource, - uint8_t flags, HandleObject proto = nullptr); + HandleValue highWaterMark); - bool readable() const; - bool closed() const; - bool errored() const; - bool disturbed() const; - - bool locked() const; - - void desiredSize(bool* hasSize, double* size) const; - - JS::ReadableStreamMode mode() const; - - static MOZ_MUST_USE bool close(JSContext* cx, Handle stream); - static MOZ_MUST_USE JSObject* cancel(JSContext* cx, Handle stream, - HandleValue reason); - static MOZ_MUST_USE bool error(JSContext* cx, Handle stream, - HandleValue error); - - static MOZ_MUST_USE NativeObject* getReader(JSContext* cx, Handle stream, - JS::ReadableStreamReaderMode mode); - - static MOZ_MUST_USE bool tee(JSContext* cx, - Handle stream, bool cloneForBranch2, - MutableHandle branch1Stream, - MutableHandle branch2Stream); - - static MOZ_MUST_USE bool enqueue(JSContext* cx, Handle stream, - HandleValue chunk); - static MOZ_MUST_USE bool enqueueBuffer(JSContext* cx, Handle stream, - Handle chunk); - static MOZ_MUST_USE bool getExternalSource(JSContext* cx, Handle stream, - void** source); - void releaseExternalSource(); - uint8_t embeddingFlags() const; - static MOZ_MUST_USE bool updateDataAvailableFromSource(JSContext* cx, - Handle stream, - uint32_t availableData); + inline bool readable() const; + inline bool closed() const; + inline bool errored() const; + inline bool disturbed() const; enum State { Readable = 1 << 0, @@ -72,7 +34,7 @@ class ReadableStream : public NativeObject }; private: - static MOZ_MUST_USE ReadableStream* createStream(JSContext* cx, HandleObject proto = nullptr); + static ReadableStream* createStream(JSContext* cx); public: static bool constructor(JSContext* cx, unsigned argc, Value* vp); @@ -85,8 +47,6 @@ class ReadableStream : public NativeObject class ReadableStreamDefaultReader : public NativeObject { public: - static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle reader); - static bool constructor(JSContext* cx, unsigned argc, Value* vp); static const ClassSpec classSpec_; static const Class class_; @@ -97,9 +57,6 @@ class ReadableStreamDefaultReader : public NativeObject class ReadableStreamBYOBReader : public NativeObject { public: - static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle reader, - Handle view); - static bool constructor(JSContext* cx, unsigned argc, Value* vp); static const ClassSpec classSpec_; static const Class class_; @@ -107,13 +64,6 @@ class ReadableStreamBYOBReader : public NativeObject static const Class protoClass_; }; -bool ReadableStreamReaderIsClosed(const JSObject* reader); - -MOZ_MUST_USE bool ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, - HandleValue reason); - -MOZ_MUST_USE bool ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader); - class ReadableStreamDefaultController : public NativeObject { public: @@ -127,8 +77,6 @@ class ReadableStreamDefaultController : public NativeObject class ReadableByteStreamController : public NativeObject { public: - bool hasExternalSource(); - static bool constructor(JSContext* cx, unsigned argc, Value* vp); static const ClassSpec classSpec_; static const Class class_; diff --git a/js/src/js.msg b/js/src/js.msg index 44e3fd2a265b..6317c4e4c69a 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -611,19 +611,18 @@ MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.") MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.") MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.") -MSG_DEF(JSMSG_READABLESTREAM_NOT_LOCKED, 1, JSEXN_TYPEERR, "'{0}' may only be called on a locked stream.") +MSG_DEF(JSMSG_READABLESTREAM_NOT_LOCKED, 1, JSEXN_TYPEERR, "The ReadableStream method '{0}' may only be called on a locked stream.") MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.") -MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableByteStreamController.") -MSG_DEF(JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.") +MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 0, JSEXN_TYPEERR, "ReadableStream.getReader('byob') requires a ReadableByteStreamController.") MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.") MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.") MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.") MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.") MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.") -MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "'{0}' called on a stream already closing.") -MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "'{0}' may only be called on a stream in the 'readable' state.") +MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "The ReadableStream controller method '{0}' called on a stream already closing.") +MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "The ReadableStream controller method '{0}' may only be called on a stream in the 'readable' state.") MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.") -MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 1, JSEXN_TYPEERR, "{0} passed a bad chunk.") +MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 0, JSEXN_TYPEERR, "ReadableByteStreamController passed a bad chunk.") MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.") MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.") MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.") diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index ff2ae88334d7..aaf1542368a3 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -125,11 +125,6 @@ if CONFIG['ENABLE_ION']: 'testJitRValueAlloc.cpp', ] -if CONFIG['ENABLE_STREAMS']: - UNIFIED_SOURCES += [ - 'testReadableStream.cpp', - ] - DEFINES['EXPORT_JS_API'] = True LOCAL_INCLUDES += [ diff --git a/js/src/jsapi-tests/testIntTypesABI.cpp b/js/src/jsapi-tests/testIntTypesABI.cpp index 28820a64f1fc..892b0cd9e4b3 100644 --- a/js/src/jsapi-tests/testIntTypesABI.cpp +++ b/js/src/jsapi-tests/testIntTypesABI.cpp @@ -30,7 +30,6 @@ #include "js/RequiredDefines.h" #include "js/RootingAPI.h" #include "js/SliceBudget.h" -#include "js/Stream.h" #include "js/StructuredClone.h" #include "js/TracingAPI.h" #include "js/TrackedOptimizationInfo.h" diff --git a/js/src/jsapi-tests/testReadableStream.cpp b/js/src/jsapi-tests/testReadableStream.cpp deleted file mode 100644 index 05fa0d7d39fd..000000000000 --- a/js/src/jsapi-tests/testReadableStream.cpp +++ /dev/null @@ -1,678 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "jsapi.h" - -#include "jsapi-tests/tests.h" - -using namespace JS; - -char test_buffer_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -static JSObject* -NewDefaultStream(JSContext* cx, HandleObject source = nullptr, HandleFunction size = nullptr, - double highWaterMark = 1, HandleObject proto = nullptr) -{ - RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, highWaterMark, - proto)); - MOZ_ASSERT_IF(stream, IsReadableStream(stream)); - return stream; -} - -static JSObject* -NewByteStream(JSContext* cx, double highWaterMark = 0, HandleObject proto = nullptr) -{ - RootedObject source(cx, JS_NewPlainObject(cx)); - MOZ_ASSERT(source); - - RootedObject stream(cx, NewReadableByteStreamObject(cx, source, highWaterMark, proto)); - MOZ_ASSERT_IF(stream, IsReadableStream(stream)); - return stream; -} - -static bool dataRequestCBCalled = false; -static void -DataRequestCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags, - size_t desiredSize) -{ - MOZ_ASSERT(!dataRequestCBCalled, "Invalid test setup"); - dataRequestCBCalled = true; -} - -static bool writeIntoRequestBufferCBCalled = false; -static void -WriteIntoRequestBufferCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags, - void* buffer, size_t length, size_t* bytesWritten) -{ - MOZ_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup"); - MOZ_ASSERT(length <= sizeof(test_buffer_data)); - memcpy(buffer, test_buffer_data, length); - writeIntoRequestBufferCBCalled = true; - *bytesWritten = length; -} - -static bool cancelStreamCBCalled = false; -static Value cancelStreamReason; -static Value -CancelStreamCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags, - HandleValue reason) -{ - MOZ_ASSERT(!cancelStreamCBCalled, "Invalid test setup"); - cancelStreamCBCalled = true; - cancelStreamReason = reason; - return reason; -} - -static bool streamClosedCBCalled = false; -static Value streamClosedReason; -static void -StreamClosedCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags) -{ - MOZ_ASSERT(!streamClosedCBCalled, "Invalid test setup"); - streamClosedCBCalled = true; -} - -static bool streamErroredCBCalled = false; -static Value streamErroredReason; -static void -StreamErroredCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags, - HandleValue reason) -{ - MOZ_ASSERT(!streamErroredCBCalled, "Invalid test setup"); - streamErroredCBCalled = true; - streamErroredReason = reason; -} - -static bool finalizeStreamCBCalled = false; -static void* finalizedStreamUnderlyingSource; -static void -FinalizeStreamCB(void* underlyingSource, uint8_t flags) -{ - MOZ_ASSERT(!finalizeStreamCBCalled, "Invalid test setup"); - finalizeStreamCBCalled = true; - finalizedStreamUnderlyingSource = underlyingSource; -} - -static void -ResetCallbacks() -{ - dataRequestCBCalled = false; - writeIntoRequestBufferCBCalled = false; - cancelStreamReason = UndefinedValue(); - cancelStreamCBCalled = false; - streamClosedCBCalled = false; - streamErroredCBCalled = false; - finalizeStreamCBCalled = false; -} - -static bool -GetIterResult(JSContext* cx, HandleObject promise, MutableHandleValue value, bool* done) -{ - RootedObject iterResult(cx, &GetPromiseResult(promise).toObject()); - - bool found; - if (!JS_HasProperty(cx, iterResult, "value", &found)) - return false; - MOZ_ASSERT(found); - if (!JS_HasProperty(cx, iterResult, "done", &found)) - return false; - MOZ_ASSERT(found); - - RootedValue doneVal(cx); - if (!JS_GetProperty(cx, iterResult, "value", value)) - return false; - if (!JS_GetProperty(cx, iterResult, "done", &doneVal)) - return false; - - *done = doneVal.toBoolean(); - MOZ_ASSERT_IF(*done, value.isUndefined()); - - return true; -} - -static JSObject* -GetReadChunk(JSContext* cx, HandleObject readRequest) -{ - MOZ_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled); - RootedValue resultVal(cx, GetPromiseResult(readRequest)); - MOZ_ASSERT(resultVal.isObject()); - RootedObject result(cx, &resultVal.toObject()); - RootedValue chunkVal(cx); - JS_GetProperty(cx, result, "value", &chunkVal); - return &chunkVal.toObject(); -} - -BEGIN_TEST(testReadableStream_NewReadableStream) -{ - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Default); - return true; -} -END_TEST(testReadableStream_NewReadableStream) - -BEGIN_TEST(testReadableStream_NewReadableByteStream) -{ - RootedObject stream(cx, NewByteStream(cx)); - CHECK(stream); - CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Byte); - return true; -} -END_TEST(testReadableStream_NewReadableByteStream) - -BEGIN_TEST(testReadableStream_ReadableStreamGetReaderDefault) -{ - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - CHECK(IsReadableStreamDefaultReader(reader)); - CHECK(ReadableStreamIsLocked(stream)); - CHECK(!ReadableStreamReaderIsClosed(reader)); - - return true; -} -END_TEST(testReadableStream_ReadableStreamGetReaderDefault) - -BEGIN_TEST(testReadableStream_ReadableStreamGetReaderBYOB) -{ - RootedObject stream(cx, NewByteStream(cx)); - CHECK(stream); - - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB)); - CHECK(reader); - CHECK(IsReadableStreamBYOBReader(reader)); - CHECK(ReadableStreamIsLocked(stream)); - CHECK(!ReadableStreamReaderIsClosed(reader)); - - return true; -} -END_TEST(testReadableStream_ReadableStreamGetReaderBYOB) - -BEGIN_TEST(testReadableStream_ReadableStreamTee) -{ - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject leftStream(cx); - RootedObject rightStream(cx); - CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream)); - CHECK(ReadableStreamIsLocked(stream)); - CHECK(leftStream); - CHECK(IsReadableStream(leftStream)); - CHECK(rightStream); - CHECK(IsReadableStream(rightStream)); - - return true; -} -END_TEST(testReadableStream_ReadableStreamTee) - -BEGIN_TEST(testReadableStream_ReadableStreamEnqueue) -{ - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject chunk(cx, JS_NewPlainObject(cx)); - CHECK(chunk); - RootedValue chunkVal(cx, ObjectValue(*chunk)); - CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); - - return true; -} -END_TEST(testReadableStream_ReadableStreamEnqueue) - -BEGIN_TEST(testReadableStream_ReadableByteStreamEnqueue) -{ - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject chunk(cx, JS_NewUint8Array(cx, 42)); - CHECK(chunk); - CHECK(!ReadableByteStreamEnqueueBuffer(cx, stream, chunk)); - CHECK(JS_IsExceptionPending(cx)); - - return true; -} -END_TEST(testReadableStream_ReadableByteStreamEnqueue) - -BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderRead) -{ - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - RootedObject chunk(cx, JS_NewPlainObject(cx)); - CHECK(chunk); - RootedValue chunkVal(cx, ObjectValue(*chunk)); - CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); - - CHECK(GetReadChunk(cx, request) == chunk); - - return true; -} -END_TEST(testReadableStream_ReadableStreamDefaultReaderRead) - -BEGIN_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead) -{ - RootedObject stream(cx, NewByteStream(cx)); - CHECK(stream); - - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - size_t length = sizeof(test_buffer_data); - RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data)); - CHECK(buffer); - RootedObject chunk(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, length)); - CHECK(chunk); - bool isShared; - CHECK(!JS_IsDetachedArrayBufferObject(buffer)); - - CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, chunk)); - - CHECK(JS_IsDetachedArrayBufferObject(buffer)); - RootedObject readChunk(cx, GetReadChunk(cx, request)); - CHECK(JS_IsUint8Array(readChunk)); - void* readBufferData; - { - JS::AutoCheckCannotGC autoNoGC(cx); - readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC); - } - CHECK(readBufferData); - CHECK(!memcmp(test_buffer_data, readBufferData, length)); - - return true; -} -END_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead) - -BEGIN_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead) -{ - RootedObject stream(cx, NewByteStream(cx)); - CHECK(stream); - - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB)); - CHECK(reader); - - size_t length = sizeof(test_buffer_data); - RootedObject targetArray(cx, JS_NewUint8Array(cx, length)); - bool isShared; - - RootedObject request(cx, ReadableStreamBYOBReaderRead(cx, reader, targetArray)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - CHECK(JS_IsDetachedArrayBufferObject(JS_GetArrayBufferViewBuffer(cx, targetArray, &isShared))); - - RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data)); - CHECK(buffer); - CHECK(!JS_IsDetachedArrayBufferObject(buffer)); - - CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, buffer)); - - CHECK(JS_IsDetachedArrayBufferObject(buffer)); - RootedObject readChunk(cx, GetReadChunk(cx, request)); - CHECK(JS_IsUint8Array(readChunk)); - void* readBufferData; - { - JS::AutoCheckCannotGC autoNoGC(cx); - readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC); - } - CHECK(readBufferData); - CHECK(!memcmp(test_buffer_data, readBufferData, length)); - // TODO: eliminate the memcpy that happens here. -// CHECK(readBufferData == test_buffer_data); - - return true; -} -END_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead) - -BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderClose) -{ - SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB, - &CancelStreamCB, &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB); - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - CHECK(ReadableStreamClose(cx, stream)); - - bool done; - RootedValue value(cx); - CHECK(GetPromiseState(request) == PromiseState::Fulfilled); - CHECK(GetIterResult(cx, request, &value, &done)); - CHECK(value.isUndefined()); - CHECK(done); - - // The callbacks are only invoked for external streams. - CHECK(!streamClosedCBCalled); - - return true; -} -END_TEST(testReadableStream_ReadableStreamDefaultReaderClose) - -BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderError) -{ - ResetCallbacks(); - SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB, - &CancelStreamCB, &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB); - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - CHECK(ReadableStreamIsLocked(stream)); - CHECK(ReadableStreamIsReadable(stream)); - RootedValue error(cx, Int32Value(42)); - CHECK(ReadableStreamError(cx, stream, error)); - - CHECK(GetPromiseState(request) == PromiseState::Rejected); - RootedValue reason(cx, GetPromiseResult(request)); - CHECK(reason.isInt32()); - CHECK(reason.toInt32() == 42); - - // The callbacks are only invoked for external streams. - CHECK(!streamErroredCBCalled); - - return true; -} -END_TEST(testReadableStream_ReadableStreamDefaultReaderError) - -static JSObject* -NewExternalSourceStream(JSContext* cx, void* underlyingSource, - RequestReadableStreamDataCallback dataRequestCallback, - WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback, - CancelReadableStreamCallback cancelCallback, - ReadableStreamClosedCallback closedCallback, - ReadableStreamErroredCallback erroredCallback, - ReadableStreamFinalizeCallback finalizeCallback) -{ - SetReadableStreamCallbacks(cx, dataRequestCallback, writeIntoReadRequestCallback, - cancelCallback, closedCallback, erroredCallback, - finalizeCallback); - RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, underlyingSource)); - MOZ_ASSERT_IF(stream, IsReadableStream(stream)); - return stream; -} - -BEGIN_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource) -{ - ResetCallbacks(); - - RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB, - &WriteIntoRequestBufferCB, &CancelStreamCB, - &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB)); - CHECK(stream); - CHECK(ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource); - void* underlyingSource; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource)); - CHECK(underlyingSource == &test_buffer_data); - CHECK(ReadableStreamIsLocked(stream)); - ReadableStreamReleaseExternalUnderlyingSource(stream); - - return true; -} -END_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource) - -BEGIN_TEST(testReadableStream_ExternalSourceCancel) -{ - ResetCallbacks(); - - RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB, - &WriteIntoRequestBufferCB, &CancelStreamCB, - &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB)); - CHECK(stream); - RootedValue reason(cx, Int32Value(42)); - CHECK(ReadableStreamCancel(cx, stream, reason)); - CHECK(cancelStreamCBCalled); - CHECK(cancelStreamReason == reason); - - return true; -} -END_TEST(testReadableStream_ExternalSourceCancel) - -BEGIN_TEST(testReadableStream_ExternalSourceGetReader) -{ - ResetCallbacks(); - - RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB, - &WriteIntoRequestBufferCB, &CancelStreamCB, - &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB)); - CHECK(stream); - - RootedValue streamVal(cx, ObjectValue(*stream)); - CHECK(JS_SetProperty(cx, global, "stream", streamVal)); - RootedValue rval(cx); - EVAL("stream.getReader()", &rval); - CHECK(rval.isObject()); - RootedObject reader(cx, &rval.toObject()); - CHECK(IsReadableStreamDefaultReader(reader)); - - return true; -} -END_TEST(testReadableStream_ExternalSourceGetReader) - -BEGIN_TEST(testReadableStream_ExternalSourceUpdateAvailableData) -{ - ResetCallbacks(); - - RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB, - &WriteIntoRequestBufferCB, &CancelStreamCB, - &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB)); - CHECK(stream); - - ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1024); - - return true; -} -END_TEST(testReadableStream_ExternalSourceUpdateAvailableData) - -struct ReadFromExternalSourceFixture : public JSAPITest -{ - virtual ~ReadFromExternalSourceFixture() {} - - bool readWithoutDataAvailable(const char* evalSrc, const char* evalSrc2, - uint32_t writtenLength) - { - ResetCallbacks(); - definePrint(); - - RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB, - &WriteIntoRequestBufferCB, - &CancelStreamCB, - &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB)); - CHECK(stream); - js::RunJobs(cx); - void* underlyingSource; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource)); - CHECK(underlyingSource == &test_buffer_data); - CHECK(ReadableStreamIsLocked(stream)); - ReadableStreamReleaseExternalUnderlyingSource(stream); - - RootedValue streamVal(cx, ObjectValue(*stream)); - CHECK(JS_SetProperty(cx, global, "stream", streamVal)); - - RootedValue rval(cx); - EVAL(evalSrc, &rval); - CHECK(dataRequestCBCalled); - CHECK(!writeIntoRequestBufferCBCalled); - CHECK(rval.isObject()); - RootedObject promise(cx, &rval.toObject()); - CHECK(IsPromiseObject(promise)); - CHECK(GetPromiseState(promise) == PromiseState::Pending); - - size_t length = sizeof(test_buffer_data); - ReadableStreamUpdateDataAvailableFromSource(cx, stream, length); - - CHECK(writeIntoRequestBufferCBCalled); - CHECK(GetPromiseState(promise) == PromiseState::Fulfilled); - RootedValue iterVal(cx); - bool done; - if (!GetIterResult(cx, promise, &iterVal, &done)) - return false; - - CHECK(!done); - RootedObject chunk(cx, &iterVal.toObject()); - CHECK(JS_IsUint8Array(chunk)); - - { - JS::AutoCheckCannotGC noGC(cx); - bool dummy; - void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC); - CHECK(!memcmp(buffer, test_buffer_data, writtenLength)); - } - - dataRequestCBCalled = false; - writeIntoRequestBufferCBCalled = false; - EVAL(evalSrc2, &rval); - CHECK(dataRequestCBCalled); - CHECK(!writeIntoRequestBufferCBCalled); - - return true; - } - - bool readWithDataAvailable(const char* evalSrc, uint32_t writtenLength) { - ResetCallbacks(); - definePrint(); - - RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB, - &WriteIntoRequestBufferCB, &CancelStreamCB, - &StreamClosedCB, &StreamErroredCB, - &FinalizeStreamCB)); - CHECK(stream); - void* underlyingSource; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource)); - CHECK(underlyingSource == &test_buffer_data); - CHECK(ReadableStreamIsLocked(stream)); - ReadableStreamReleaseExternalUnderlyingSource(stream); - - size_t length = sizeof(test_buffer_data); - ReadableStreamUpdateDataAvailableFromSource(cx, stream, length); - - RootedValue streamVal(cx, ObjectValue(*stream)); - CHECK(JS_SetProperty(cx, global, "stream", streamVal)); - - RootedValue rval(cx); - EVAL(evalSrc, &rval); - CHECK(writeIntoRequestBufferCBCalled); - CHECK(rval.isObject()); - RootedObject promise(cx, &rval.toObject()); - CHECK(IsPromiseObject(promise)); - CHECK(GetPromiseState(promise) == PromiseState::Fulfilled); - RootedValue iterVal(cx); - bool done; - if (!GetIterResult(cx, promise, &iterVal, &done)) - return false; - - CHECK(!done); - RootedObject chunk(cx, &iterVal.toObject()); - CHECK(JS_IsUint8Array(chunk)); - - { - JS::AutoCheckCannotGC noGC(cx); - bool dummy; - void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC); - CHECK(!memcmp(buffer, test_buffer_data, writtenLength)); - } - - return true; - } -}; - -BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) -{ - return readWithoutDataAvailable("r = stream.getReader(); r.read()", "r.read()", sizeof(test_buffer_data)); -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) - -BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceCloseWithPendingRead) -{ - CHECK(readWithoutDataAvailable("r = stream.getReader(); request0 = r.read(); " - "request1 = r.read(); request0", "r.read()", - sizeof(test_buffer_data))); - - RootedValue val(cx); - CHECK(JS_GetProperty(cx, global, "request1", &val)); - CHECK(val.isObject()); - RootedObject request(cx, &val.toObject()); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - CHECK(JS_GetProperty(cx, global, "stream", &val)); - RootedObject stream(cx, &val.toObject()); - ReadableStreamClose(cx, stream); - - val = GetPromiseResult(request); - MOZ_ASSERT(val.isObject()); - RootedObject result(cx, &val.toObject()); - - JS_GetProperty(cx, result, "done", &val); - CHECK(val.isBoolean()); - CHECK(val.toBoolean() == true); - - JS_GetProperty(cx, result, "value", &val); - CHECK(val.isUndefined()); - return true; -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceCloseWithPendingRead) - -BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable) -{ - return readWithDataAvailable("r = stream.getReader(); r.read()", sizeof(test_buffer_data)); -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable) - -BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable) -{ - return readWithoutDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(63))", "r.read(new Uint8Array(10))", 10); -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable) - -BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadBYOBWithDataAvailable) -{ - return readWithDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(10))", 10); -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadBYOBWithDataAvailable) diff --git a/js/src/jsapi-tests/tests.cpp b/js/src/jsapi-tests/tests.cpp index 7a11a4d3de0a..6b636ace8dfa 100644 --- a/js/src/jsapi-tests/tests.cpp +++ b/js/src/jsapi-tests/tests.cpp @@ -83,10 +83,6 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals) /* Create the global object. */ JS::RootedObject newGlobal(cx); JS::CompartmentOptions options; -#ifdef ENABLE_STREAMS - options.creationOptions().setStreamsEnabled(true); -#endif - printf("enabled\n"); options.behaviors().setVersion(JSVERSION_LATEST); newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook, options); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 6bf78613ba9f..0aa77b36e575 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -49,7 +49,6 @@ #include "builtin/MapObject.h" #include "builtin/Promise.h" #include "builtin/RegExp.h" -#include "builtin/Stream.h" #include "builtin/SymbolObject.h" #ifdef ENABLE_SIMD # include "builtin/SIMD.h" @@ -5212,6 +5211,7 @@ CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj, return false; } return true; + } JS_PUBLIC_API(JSObject*) @@ -5253,368 +5253,6 @@ JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises) return js::GetWaitForAllPromise(cx, promises); } -JS_PUBLIC_API(JSObject*) -JS::NewReadableDefaultStreamObject(JSContext* cx, - JS::HandleObject underlyingSource /* = nullptr */, - JS::HandleFunction size /* = nullptr */, - double highWaterMark /* = 1 */, - JS::HandleObject proto /* = nullptr */) -{ - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - - RootedObject source(cx, underlyingSource); - if (!source) { - source = NewBuiltinClassInstance(cx); - if (!source) - return nullptr; - } - RootedValue sourceVal(cx, ObjectValue(*source)); - RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue()); - RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark)); - return ReadableStream::createDefaultStream(cx, sourceVal, sizeVal, highWaterMarkVal, proto); -} - -JS_PUBLIC_API(JSObject*) -JS::NewReadableByteStreamObject(JSContext* cx, - JS::HandleObject underlyingSource /* = nullptr */, - double highWaterMark /* = 1 */, - JS::HandleObject proto /* = nullptr */) -{ - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - - RootedObject source(cx, underlyingSource); - if (!source) { - source = NewBuiltinClassInstance(cx); - if (!source) - return nullptr; - } - RootedValue sourceVal(cx, ObjectValue(*source)); - RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark)); - return ReadableStream::createByteStream(cx, sourceVal, highWaterMarkVal, proto); -} - -extern JS_PUBLIC_API(void) -JS::SetReadableStreamCallbacks(JSContext* cx, - JS::RequestReadableStreamDataCallback dataRequestCallback, - JS::WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback, - JS::CancelReadableStreamCallback cancelCallback, - JS::ReadableStreamClosedCallback closedCallback, - JS::ReadableStreamErroredCallback erroredCallback, - JS::ReadableStreamFinalizeCallback finalizeCallback) -{ - MOZ_ASSERT(dataRequestCallback); - MOZ_ASSERT(writeIntoReadRequestCallback); - MOZ_ASSERT(cancelCallback); - MOZ_ASSERT(closedCallback); - MOZ_ASSERT(erroredCallback); - MOZ_ASSERT(finalizeCallback); - - JSRuntime* rt = cx->runtime(); - - MOZ_ASSERT(!rt->readableStreamDataRequestCallback); - MOZ_ASSERT(!rt->readableStreamWriteIntoReadRequestCallback); - MOZ_ASSERT(!rt->readableStreamCancelCallback); - MOZ_ASSERT(!rt->readableStreamClosedCallback); - MOZ_ASSERT(!rt->readableStreamErroredCallback); - MOZ_ASSERT(!rt->readableStreamFinalizeCallback); - - rt->readableStreamDataRequestCallback = dataRequestCallback; - rt->readableStreamWriteIntoReadRequestCallback = writeIntoReadRequestCallback; - rt->readableStreamCancelCallback = cancelCallback; - rt->readableStreamClosedCallback = closedCallback; - rt->readableStreamErroredCallback = erroredCallback; - rt->readableStreamFinalizeCallback = finalizeCallback; -} - -JS_PUBLIC_API(bool) -JS::HasReadableStreamCallbacks(JSContext* cx) -{ - return cx->runtime()->readableStreamDataRequestCallback; -} - -JS_PUBLIC_API(JSObject*) -JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource, - uint8_t flags /* = 0 */, - HandleObject proto /* = nullptr */) -{ - MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment())); - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - -#ifdef DEBUG - JSRuntime* rt = cx->runtime(); - MOZ_ASSERT(rt->readableStreamDataRequestCallback); - MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback); - MOZ_ASSERT(rt->readableStreamCancelCallback); - MOZ_ASSERT(rt->readableStreamClosedCallback); - MOZ_ASSERT(rt->readableStreamErroredCallback); - MOZ_ASSERT(rt->readableStreamFinalizeCallback); -#endif // DEBUG - - return ReadableStream::createExternalSourceStream(cx, underlyingSource, flags, proto); -} - -JS_PUBLIC_API(uint8_t) -JS::ReadableStreamGetEmbeddingFlags(const JSObject* stream) -{ - return stream->as().embeddingFlags(); -} - -JS_PUBLIC_API(bool) -JS::IsReadableStream(const JSObject* obj) -{ - return obj->is(); -} - -JS_PUBLIC_API(bool) -JS::IsReadableStreamReader(const JSObject* obj) -{ - return obj->is() || obj->is(); -} - -JS_PUBLIC_API(bool) -JS::IsReadableStreamDefaultReader(const JSObject* obj) -{ - return obj->is(); -} - -JS_PUBLIC_API(bool) -JS::IsReadableStreamBYOBReader(const JSObject* obj) -{ - return obj->is(); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamIsReadable(const JSObject* stream) -{ - return stream->as().readable(); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamIsLocked(const JSObject* stream) -{ - return stream->as().locked(); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamIsDisturbed(const JSObject* stream) -{ - return stream->as().disturbed(); -} - -JS_PUBLIC_API(JSObject*) -JS::ReadableStreamCancel(JSContext* cx, HandleObject streamObj, HandleValue reason) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - assertSameCompartment(cx, reason); - - Rooted stream(cx, &streamObj->as()); - return ReadableStream::cancel(cx, stream, reason); -} - -JS_PUBLIC_API(JS::ReadableStreamMode) -JS::ReadableStreamGetMode(const JSObject* stream) -{ - return stream->as().mode(); -} - -JS_PUBLIC_API(JSObject*) -JS::ReadableStreamGetReader(JSContext* cx, HandleObject streamObj, ReadableStreamReaderMode mode) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - - Rooted stream(cx, &streamObj->as()); - return ReadableStream::getReader(cx, stream, mode); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj, void** source) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - - Rooted stream(cx, &streamObj->as()); - return ReadableStream::getExternalSource(cx, stream, source); -} - -JS_PUBLIC_API(void) -JS::ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream) -{ - stream->as().releaseExternalSource(); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, JS::HandleObject streamObj, - uint32_t availableData) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - - Rooted stream(cx, &streamObj->as()); - return ReadableStream::updateDataAvailableFromSource(cx, stream, availableData); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamTee(JSContext* cx, HandleObject streamObj, - MutableHandleObject branch1Obj, MutableHandleObject branch2Obj) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - - Rooted stream(cx, &streamObj->as()); - Rooted branch1Stream(cx); - Rooted branch2Stream(cx); - - if (!ReadableStream::tee(cx, stream, false, &branch1Stream, &branch2Stream)) - return false; - - branch1Obj.set(branch1Stream); - branch2Obj.set(branch2Stream); - - return true; -} - -JS_PUBLIC_API(void) -JS::ReadableStreamGetDesiredSize(JSObject* streamObj, bool* hasValue, double* value) -{ - streamObj->as().desiredSize(hasValue, value); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamClose(JSContext* cx, HandleObject streamObj) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - - Rooted stream(cx, &streamObj->as()); - return ReadableStream::close(cx, stream); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamEnqueue(JSContext* cx, HandleObject streamObj, HandleValue chunk) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - assertSameCompartment(cx, chunk); - - Rooted stream(cx, &streamObj->as()); - if (stream->mode() != JS::ReadableStreamMode::Default) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, - "JS::ReadableStreamEnqueue"); - return false; - } - return ReadableStream::enqueue(cx, stream, chunk); -} - -JS_PUBLIC_API(bool) -JS::ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject streamObj, HandleObject chunkObj) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - assertSameCompartment(cx, chunkObj); - - Rooted stream(cx, &streamObj->as()); - if (stream->mode() != JS::ReadableStreamMode::Byte) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, - "JS::ReadableByteStreamEnqueueBuffer"); - return false; - } - - Rooted buffer(cx); - if (chunkObj->is()) { - bool dummy; - buffer = &JS_GetArrayBufferViewBuffer(cx, chunkObj, &dummy)->as(); - } else if (chunkObj->is()) { - buffer = &chunkObj->as(); - } else { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, - "JS::ReadableByteStreamEnqueueBuffer"); - return false; - } - - return ReadableStream::enqueueBuffer(cx, stream, buffer); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamError(JSContext* cx, HandleObject streamObj, HandleValue error) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, streamObj); - assertSameCompartment(cx, error); - - Rooted stream(cx, &streamObj->as()); - return js::ReadableStream::error(cx, stream, error); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamReaderIsClosed(const JSObject* reader) -{ - return js::ReadableStreamReaderIsClosed(reader); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, reader); - assertSameCompartment(cx, reason); - - return js::ReadableStreamReaderCancel(cx, reader, reason); -} - -JS_PUBLIC_API(bool) -JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, reader); - - return js::ReadableStreamReaderReleaseLock(cx, reader); -} - -JS_PUBLIC_API(JSObject*) -JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, readerObj); - - Rooted reader(cx, &readerObj->as()); - return js::ReadableStreamDefaultReader::read(cx, reader); -} - -JS_PUBLIC_API(JSObject*) -JS::ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject readerObj, HandleObject viewObj) -{ - AssertHeapIsIdle(); - CHECK_REQUEST(cx); - assertSameCompartment(cx, readerObj); - assertSameCompartment(cx, viewObj); - - Rooted reader(cx, &readerObj->as()); - Rooted view(cx, &viewObj->as()); - return js::ReadableStreamBYOBReader::read(cx, reader, view); -} - JS_PUBLIC_API(void) JS::SetAsyncTaskCallbacks(JSContext* cx, JS::StartAsyncTaskCallback start, JS::FinishAsyncTaskCallback finish) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 4dc289875a4c..424295d1583e 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -36,7 +36,6 @@ #include "js/Realm.h" #include "js/RefCounted.h" #include "js/RootingAPI.h" -#include "js/Stream.h" #include "js/TracingAPI.h" #include "js/UniquePtr.h" #include "js/Utility.h" diff --git a/js/src/moz.build b/js/src/moz.build index ed2887906c5e..a910f4b5ccb4 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -125,7 +125,6 @@ EXPORTS.js += [ '../public/Result.h', '../public/RootingAPI.h', '../public/SliceBudget.h', - '../public/Stream.h', '../public/StructuredClone.h', '../public/SweepingAPI.h', '../public/TraceKind.h', diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index a60f6791f52b..ac44d1739b77 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -17,8 +17,6 @@ macro(anonymous, anonymous, "anonymous") \ macro(Any, Any, "Any") \ macro(apply, apply, "apply") \ - macro(AcquireReadableStreamBYOBReader, AcquireReadableStreamBYOBReader, "AcquireReadableStreamBYOBReader") \ - macro(AcquireReadableStreamDefaultReader, AcquireReadableStreamDefaultReader, "AcquireReadableStreamDefaultReader") \ macro(arguments, arguments, "arguments") \ macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \ macro(ArrayIterator, ArrayIterator, "Array Iterator") \ @@ -299,50 +297,6 @@ macro(prototype, prototype, "prototype") \ macro(proxy, proxy, "proxy") \ macro(raw, raw, "raw") \ - macro(ReadableByteStreamControllerGetDesiredSize, \ - ReadableByteStreamControllerGetDesiredSize, \ - "ReadableByteStreamControllerGetDesiredSize") \ - macro(ReadableByteStreamController_close, \ - ReadableByteStreamController_close, \ - "ReadableByteStreamController_close") \ - macro(ReadableByteStreamController_enqueue, \ - ReadableByteStreamController_enqueue, \ - "ReadableByteStreamController_enqueue") \ - macro(ReadableByteStreamController_error, \ - ReadableByteStreamController_error, \ - "ReadableByteStreamController_error") \ - macro(ReadableStreamBYOBReader_cancel, \ - ReadableStreamBYOBReader_cancel, \ - "ReadableStreamBYOBReader_cancel") \ - macro(ReadableStreamBYOBReader_read, \ - ReadableStreamBYOBReader_read, \ - "ReadableStreamBYOBReader_read") \ - macro(ReadableStreamBYOBReader_releaseLock, \ - ReadableStreamBYOBReader_releaseLock, \ - "ReadableStreamBYOBReader_releaseLock") \ - macro(ReadableStream_cancel, ReadableStream_cancel, "ReadableStream_cancel") \ - macro(ReadableStreamDefaultControllerGetDesiredSize, \ - ReadableStreamDefaultControllerGetDesiredSize, \ - "ReadableStreamDefaultControllerGetDesiredSize") \ - macro(ReadableStreamDefaultController_close, \ - ReadableStreamDefaultController_close, \ - "ReadableStreamDefaultController_close") \ - macro(ReadableStreamDefaultController_enqueue, \ - ReadableStreamDefaultController_enqueue, \ - "ReadableStreamDefaultController_enqueue") \ - macro(ReadableStreamDefaultController_error, \ - ReadableStreamDefaultController_error, \ - "ReadableStreamDefaultController_error") \ - macro(ReadableStreamDefaultReader_cancel, \ - ReadableStreamDefaultReader_cancel, \ - "ReadableStreamDefaultReader_cancel") \ - macro(ReadableStreamDefaultReader_read, \ - ReadableStreamDefaultReader_read, \ - "ReadableStreamDefaultReader_read") \ - macro(ReadableStreamDefaultReader_releaseLock, \ - ReadableStreamDefaultReader_releaseLock, \ - "ReadableStreamDefaultReader_releaseLock") \ - macro(ReadableStreamTee, ReadableStreamTee, "ReadableStreamTee") \ macro(reason, reason, "reason") \ macro(RegExpBuiltinExec, RegExpBuiltinExec, "RegExpBuiltinExec") \ macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \ diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index d0f48f965456..15c4b67bd1b6 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -108,12 +108,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime) startAsyncTaskCallback(nullptr), finishAsyncTaskCallback(nullptr), promiseTasksToDestroy(mutexid::PromiseTaskPtrVector), - readableStreamDataRequestCallback(nullptr), - readableStreamWriteIntoReadRequestCallback(nullptr), - readableStreamCancelCallback(nullptr), - readableStreamClosedCallback(nullptr), - readableStreamErroredCallback(nullptr), - readableStreamFinalizeCallback(nullptr), hadOutOfMemory(false), allowRelazificationForTesting(false), destroyCompartmentCallback(nullptr), diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index e0fb60508818..9fb0cf5ac4be 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -476,13 +476,6 @@ struct JSRuntime : public js::MallocProvider void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise); void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise); - js::UnprotectedData readableStreamDataRequestCallback; - js::UnprotectedData readableStreamWriteIntoReadRequestCallback; - js::UnprotectedData readableStreamCancelCallback; - js::UnprotectedData readableStreamClosedCallback; - js::UnprotectedData readableStreamErroredCallback; - js::UnprotectedData readableStreamFinalizeCallback; - /* Had an out-of-memory error which did not populate an exception. */ mozilla::Atomic hadOutOfMemory;