/* -*- 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/. */ /* WritableStream controller classes and functions. */ #ifndef builtin_streams_WritableStreamDefaultController_h #define builtin_streams_WritableStreamDefaultController_h #include "mozilla/Assertions.h" // MOZ_ASSERT #include // uint32_t #include "builtin/streams/StreamController.h" // js::StreamController #include "builtin/streams/WritableStream.h" // js::WritableStream #include "js/Class.h" // JSClass, js::ClassSpec #include "js/RootingAPI.h" // JS::Handle #include "js/Stream.h" // JS::WritableStreamUnderlyingSink #include "js/Value.h" // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue #include "vm/NativeObject.h" // js::NativeObject namespace js { class WritableStreamDefaultController : public StreamController { public: /** * Memory layout for WritableStream default controllers, starting after the * slots reserved for queue container usage. (Note that this is the only * writable stream controller class in the spec: ReadableByteStreamController * exists, but WritableByteStreamController does not.) */ enum Slots { /** * The stream that this controller controls. Stream and controller are * initialized at the same time underneath the |WritableStream| constructor, * so they are same-compartment with each other. */ Slot_Stream = StreamController::SlotCount, /** * The underlying sink object that this controller and its associated stream * write to. * * This is a user-provided value, the first argument passed to * |new WritableStream|, so it may be a cross-compartment wrapper around an * object from another realm. */ Slot_UnderlyingSink, /** Number stored as DoubleValue. */ Slot_StrategyHWM, /** * Either undefined if each chunk has size 1, or a callable object to be * invoked on each chunk to determine its size. See * MakeSizeAlgorithmFromSizeFunction. */ Slot_StrategySize, /** * Slots containing the core of each of the write/close/abort algorithms the * spec creates from the underlying sink passed in when creating a * |WritableStream|. ("core", as in the value produced by * |CreateAlgorithmFromUnderlyingMethod| after validating the user-provided * input.) * * These slots are initialized underneath the |WritableStream| constructor, * so they are same-compartment with both stream and controller. (They * could be wrappers around arbitrary callable objects from other * compartments, tho.) */ Slot_WriteMethod, Slot_CloseMethod, Slot_AbortMethod, /** Bit field stored as Int32Value. */ Slot_Flags, SlotCount }; enum ControllerFlags { Flag_Started = 0b0001, Flag_ExternalSink = 0b0010, }; WritableStream* stream() const { return &getFixedSlot(Slot_Stream).toObject().as(); } void setStream(WritableStream* stream) { setFixedSlot(Slot_Stream, JS::ObjectValue(*stream)); } JS::Value underlyingSink() const { return getFixedSlot(Slot_UnderlyingSink); } void setUnderlyingSink(const JS::Value& underlyingSink) { setFixedSlot(Slot_UnderlyingSink, underlyingSink); } JS::WritableStreamUnderlyingSink* externalSink() const { static_assert(alignof(JS::WritableStreamUnderlyingSink) >= 2, "external underling sinks are stored as PrivateValues, so " "they must have even addresses"); MOZ_ASSERT(hasExternalSink()); return static_cast( underlyingSink().toPrivate()); } void setExternalSink(JS::WritableStreamUnderlyingSink* underlyingSink) { setUnderlyingSink(JS::PrivateValue(underlyingSink)); addFlags(Flag_ExternalSink); } static void clearUnderlyingSink( JS::Handle controller, bool finalizeSink = true) { if (controller->hasExternalSink()) { if (finalizeSink) { controller->externalSink()->finalize(); } controller->setFlags(controller->flags() & ~Flag_ExternalSink); } controller->setUnderlyingSink(JS::UndefinedHandleValue); } JS::Value writeMethod() const { return getFixedSlot(Slot_WriteMethod); } void setWriteMethod(const JS::Value& writeMethod) { setFixedSlot(Slot_WriteMethod, writeMethod); } void clearWriteMethod() { setWriteMethod(JS::UndefinedValue()); } JS::Value closeMethod() const { return getFixedSlot(Slot_CloseMethod); } void setCloseMethod(const JS::Value& closeMethod) { setFixedSlot(Slot_CloseMethod, closeMethod); } void clearCloseMethod() { setCloseMethod(JS::UndefinedValue()); } JS::Value abortMethod() const { return getFixedSlot(Slot_AbortMethod); } void setAbortMethod(const JS::Value& abortMethod) { setFixedSlot(Slot_AbortMethod, abortMethod); } void clearAbortMethod() { setAbortMethod(JS::UndefinedValue()); } double strategyHWM() const { return getFixedSlot(Slot_StrategyHWM).toDouble(); } void setStrategyHWM(double highWaterMark) { setFixedSlot(Slot_StrategyHWM, DoubleValue(highWaterMark)); } JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); } void setStrategySize(const JS::Value& size) { setFixedSlot(Slot_StrategySize, size); } void clearStrategySize() { setStrategySize(JS::UndefinedValue()); } 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 hasExternalSink() const { return flags() & Flag_ExternalSink; } 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_; }; inline WritableStreamDefaultController* WritableStream::controller() const { return &getFixedSlot(Slot_Controller) .toObject() .as(); } inline void WritableStream::setController( WritableStreamDefaultController* controller) { setFixedSlot(Slot_Controller, JS::ObjectValue(*controller)); } } // namespace js #endif // builtin_streams_WritableStreamDefaultController_h