/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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/. */ /* ReadableStream controller classes and functions. */ #ifndef builtin_streams_ReadableStreamController_h #define builtin_streams_ReadableStreamController_h #include "mozilla/Assertions.h" // MOZ_ASSERT #include // uint32_t #include "builtin/streams/ReadableStream.h" // js::ReadableStream #include "builtin/streams/StreamController.h" // js::StreamController #include "js/Class.h" // JSClass, js::ClassSpec #include "js/RootingAPI.h" // JS::Handle #include "js/Stream.h" // JS::ReadableStreamUnderlyingSource #include "js/Value.h" // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue #include "vm/List.h" // js::ListObject #include "vm/NativeObject.h" // js::NativeObject namespace js { class PromiseObject; class ReadableStreamController : public StreamController { public: /** * Memory layout for ReadableStream controllers, starting after the slots * reserved for queue container usage. * * Storage of the internal slots listed in the standard is fairly * straightforward except for [[pullAlgorithm]] and [[cancelAlgorithm]]. * These algorithms are not stored as JSFunction objects. Rather, there are * three cases: * * - Streams created with `new ReadableStream`: The methods are stored * in Slot_PullMethod and Slot_CancelMethod. The underlying source * object (`this` for these methods) is in Slot_UnderlyingSource. * * - External source streams. Slot_UnderlyingSource is a PrivateValue * pointing to the JS::ReadableStreamUnderlyingSource object. The * algorithms are implemented using the .pull() and .cancel() methods * of that object. Slot_Pull/CancelMethod are undefined. * * - Tee streams. Slot_UnderlyingSource is a TeeState object. The * pull/cancel algorithms are implemented as separate functions in * Stream.cpp. Slot_Pull/CancelMethod are undefined. * * UnderlyingSource, PullMethod, and CancelMethod can be wrappers to objects * in other compartments. * * StrategyHWM and Flags are both primitive (numeric) values. */ enum Slots { Slot_Stream = StreamController::SlotCount, Slot_UnderlyingSource, Slot_PullMethod, Slot_CancelMethod, Slot_StrategyHWM, Slot_Flags, SlotCount }; enum ControllerFlags { Flag_Started = 1 << 0, Flag_Pulling = 1 << 1, Flag_PullAgain = 1 << 2, Flag_CloseRequested = 1 << 3, Flag_TeeBranch1 = 1 << 4, Flag_TeeBranch2 = 1 << 5, Flag_ExternalSource = 1 << 6, Flag_SourceLocked = 1 << 7, }; ReadableStream* stream() const { return &getFixedSlot(Slot_Stream).toObject().as(); } void setStream(ReadableStream* stream) { setFixedSlot(Slot_Stream, JS::ObjectValue(*stream)); } JS::Value underlyingSource() const { return getFixedSlot(Slot_UnderlyingSource); } void setUnderlyingSource(const JS::Value& underlyingSource) { setFixedSlot(Slot_UnderlyingSource, underlyingSource); } JS::Value pullMethod() const { return getFixedSlot(Slot_PullMethod); } void setPullMethod(const JS::Value& pullMethod) { setFixedSlot(Slot_PullMethod, pullMethod); } JS::Value cancelMethod() const { return getFixedSlot(Slot_CancelMethod); } void setCancelMethod(const JS::Value& cancelMethod) { setFixedSlot(Slot_CancelMethod, cancelMethod); } JS::ReadableStreamUnderlyingSource* externalSource() const { static_assert(alignof(JS::ReadableStreamUnderlyingSource) >= 2, "External underling sources are stored as PrivateValues, " "so they must have even addresses"); MOZ_ASSERT(hasExternalSource()); return static_cast( underlyingSource().toPrivate()); } void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) { setUnderlyingSource(JS::PrivateValue(underlyingSource)); addFlags(Flag_ExternalSource); } static void clearUnderlyingSource( JS::Handle controller, bool finalizeSource = true) { if (controller->hasExternalSource()) { if (finalizeSource) { controller->externalSource()->finalize(); } controller->setFlags(controller->flags() & ~Flag_ExternalSource); } controller->setUnderlyingSource(JS::UndefinedHandleValue); } double strategyHWM() const { return getFixedSlot(Slot_StrategyHWM).toNumber(); } void setStrategyHWM(double highWaterMark) { setFixedSlot(Slot_StrategyHWM, NumberValue(highWaterMark)); } uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); } void setFlags(uint32_t flags) { setFixedSlot(Slot_Flags, Int32Value(flags)); } void addFlags(uint32_t flags) { setFlags(this->flags() | flags); } void removeFlags(uint32_t flags) { setFlags(this->flags() & ~flags); } bool started() const { return flags() & Flag_Started; } void setStarted() { addFlags(Flag_Started); } bool pulling() const { return flags() & Flag_Pulling; } void setPulling() { addFlags(Flag_Pulling); } void clearPullFlags() { removeFlags(Flag_Pulling | Flag_PullAgain); } bool pullAgain() const { return flags() & Flag_PullAgain; } void setPullAgain() { addFlags(Flag_PullAgain); } bool closeRequested() const { return flags() & Flag_CloseRequested; } void setCloseRequested() { addFlags(Flag_CloseRequested); } bool isTeeBranch1() const { return flags() & Flag_TeeBranch1; } void setTeeBranch1() { MOZ_ASSERT(!isTeeBranch2()); addFlags(Flag_TeeBranch1); } bool isTeeBranch2() const { return flags() & Flag_TeeBranch2; } void setTeeBranch2() { MOZ_ASSERT(!isTeeBranch1()); addFlags(Flag_TeeBranch2); } bool hasExternalSource() const { return flags() & Flag_ExternalSource; } bool sourceLocked() const { return flags() & Flag_SourceLocked; } void setSourceLocked() { addFlags(Flag_SourceLocked); } void clearSourceLocked() { removeFlags(Flag_SourceLocked); } }; class ReadableStreamDefaultController : public ReadableStreamController { private: /** * Memory layout for ReadableStreamDefaultControllers, starting after the * slots shared among all types of controllers. * * StrategySize is treated as an opaque value when stored. The only use site * ensures that it's wrapped into the current cx compartment. */ enum Slots { Slot_StrategySize = ReadableStreamController::SlotCount, SlotCount }; public: JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); } void setStrategySize(const JS::Value& size) { setFixedSlot(Slot_StrategySize, size); } static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); static const ClassSpec classSpec_; static const JSClass class_; static const ClassSpec protoClassSpec_; static const JSClass protoClass_; }; class ReadableByteStreamController : public ReadableStreamController { public: /** * Memory layout for ReadableByteStreamControllers, starting after the * slots shared among all types of controllers. * * PendingPullIntos is guaranteed to be in the same compartment as the * controller, but might contain wrappers for objects from other * compartments. * * AutoAllocateSize is a primitive (numeric) value. */ enum Slots { Slot_BYOBRequest = ReadableStreamController::SlotCount, Slot_PendingPullIntos, Slot_AutoAllocateSize, SlotCount }; JS::Value byobRequest() const { return getFixedSlot(Slot_BYOBRequest); } void clearBYOBRequest() { setFixedSlot(Slot_BYOBRequest, JS::UndefinedValue()); } ListObject* pendingPullIntos() const { return &getFixedSlot(Slot_PendingPullIntos).toObject().as(); } JS::Value autoAllocateChunkSize() const { return getFixedSlot(Slot_AutoAllocateSize); } void setAutoAllocateChunkSize(const JS::Value& size) { setFixedSlot(Slot_AutoAllocateSize, size); } static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); static const ClassSpec classSpec_; static const JSClass class_; static const ClassSpec protoClassSpec_; static const JSClass protoClass_; }; extern MOZ_MUST_USE bool CheckReadableStreamControllerCanCloseOrEnqueue( JSContext* cx, JS::Handle unwrappedController, const char* action); extern MOZ_MUST_USE JSObject* ReadableStreamControllerCancelSteps( JSContext* cx, JS::Handle unwrappedController, JS::Handle reason); extern PromiseObject* ReadableStreamDefaultControllerPullSteps( JSContext* cx, JS::Handle unwrappedController); extern bool ReadableStreamControllerStartHandler(JSContext* cx, unsigned argc, JS::Value* vp); extern bool ReadableStreamControllerStartFailedHandler(JSContext* cx, unsigned argc, JS::Value* vp); } // namespace js template <> inline bool JSObject::is() const { return is() || is(); } namespace js { inline ReadableStreamController* ReadableStream::controller() const { return &getFixedSlot(Slot_Controller) .toObject() .as(); } inline void ReadableStream::setController( ReadableStreamController* controller) { setFixedSlot(Slot_Controller, JS::ObjectValue(*controller)); } } // namespace js #endif // builtin_streams_ReadableStreamController_h