UnderlyingSinkCallbackHelpers.h (7837B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_UnderlyingSinkCallbackHelpers_h 8 #define mozilla_dom_UnderlyingSinkCallbackHelpers_h 9 10 #include "mozilla/Buffer.h" 11 #include "mozilla/HoldDropJSObjects.h" 12 #include "mozilla/Maybe.h" 13 #include "mozilla/dom/Promise.h" 14 #include "mozilla/dom/UnderlyingSinkBinding.h" 15 #include "nsCycleCollectionParticipant.h" 16 #include "nsIAsyncOutputStream.h" 17 #include "nsISupports.h" 18 #include "nsISupportsImpl.h" 19 20 /* 21 * See the comment in UnderlyingSourceCallbackHelpers.h! 22 * 23 * A native implementation of these callbacks is however currently not required. 24 */ 25 namespace mozilla::dom { 26 27 class WritableStreamDefaultController; 28 29 class UnderlyingSinkAlgorithmsBase : public nsISupports { 30 public: 31 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 32 NS_DECL_CYCLE_COLLECTION_CLASS(UnderlyingSinkAlgorithmsBase) 33 34 MOZ_CAN_RUN_SCRIPT virtual void StartCallback( 35 JSContext* aCx, WritableStreamDefaultController& aController, 36 JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) = 0; 37 38 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> WriteCallback( 39 JSContext* aCx, JS::Handle<JS::Value> aChunk, 40 WritableStreamDefaultController& aController, ErrorResult& aRv) = 0; 41 42 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CloseCallback( 43 JSContext* aCx, ErrorResult& aRv) = 0; 44 45 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> AbortCallback( 46 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 47 ErrorResult& aRv) = 0; 48 49 // Implement this when you need to release underlying resources immediately 50 // from closed/errored(aborted) streams, without waiting for GC. 51 virtual void ReleaseObjects() {} 52 53 protected: 54 virtual ~UnderlyingSinkAlgorithmsBase() = default; 55 }; 56 57 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink 58 class UnderlyingSinkAlgorithms final : public UnderlyingSinkAlgorithmsBase { 59 public: 60 NS_DECL_ISUPPORTS_INHERITED 61 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( 62 UnderlyingSinkAlgorithms, UnderlyingSinkAlgorithmsBase) 63 64 UnderlyingSinkAlgorithms(nsIGlobalObject* aGlobal, 65 JS::Handle<JSObject*> aUnderlyingSink, 66 UnderlyingSink& aUnderlyingSinkDict) 67 : mGlobal(aGlobal), mUnderlyingSink(aUnderlyingSink) { 68 // Step 6. (implicit Step 2.) 69 if (aUnderlyingSinkDict.mStart.WasPassed()) { 70 mStartCallback = aUnderlyingSinkDict.mStart.Value(); 71 } 72 73 // Step 7. (implicit Step 3.) 74 if (aUnderlyingSinkDict.mWrite.WasPassed()) { 75 mWriteCallback = aUnderlyingSinkDict.mWrite.Value(); 76 } 77 78 // Step 8. (implicit Step 4.) 79 if (aUnderlyingSinkDict.mClose.WasPassed()) { 80 mCloseCallback = aUnderlyingSinkDict.mClose.Value(); 81 } 82 83 // Step 9. (implicit Step 5.) 84 if (aUnderlyingSinkDict.mAbort.WasPassed()) { 85 mAbortCallback = aUnderlyingSinkDict.mAbort.Value(); 86 } 87 88 mozilla::HoldJSObjects(this); 89 }; 90 91 MOZ_CAN_RUN_SCRIPT void StartCallback( 92 JSContext* aCx, WritableStreamDefaultController& aController, 93 JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) override; 94 95 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WriteCallback( 96 JSContext* aCx, JS::Handle<JS::Value> aChunk, 97 WritableStreamDefaultController& aController, ErrorResult& aRv) override; 98 99 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseCallback( 100 JSContext* aCx, ErrorResult& aRv) override; 101 102 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> AbortCallback( 103 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 104 ErrorResult& aRv) override; 105 106 protected: 107 ~UnderlyingSinkAlgorithms() override { mozilla::DropJSObjects(this); } 108 109 private: 110 // Virtually const, but are cycle collected 111 nsCOMPtr<nsIGlobalObject> mGlobal; 112 JS::Heap<JSObject*> mUnderlyingSink; 113 MOZ_KNOWN_LIVE RefPtr<UnderlyingSinkStartCallback> mStartCallback; 114 MOZ_KNOWN_LIVE RefPtr<UnderlyingSinkWriteCallback> mWriteCallback; 115 MOZ_KNOWN_LIVE RefPtr<UnderlyingSinkCloseCallback> mCloseCallback; 116 MOZ_KNOWN_LIVE RefPtr<UnderlyingSinkAbortCallback> mAbortCallback; 117 }; 118 119 // https://streams.spec.whatwg.org/#writablestream-set-up 120 // Wrappers defined by the "Set up" methods in the spec. 121 // (closeAlgorithmWrapper, abortAlgorithmWrapper) 122 // This helps you just return nullptr when 1) the algorithm is synchronous, or 123 // 2) an error occurred, as this wrapper will return a resolved or rejected 124 // promise respectively. 125 // Note that StartCallback is only for JS consumers to access the 126 // controller, and thus is no-op here since native consumers can call 127 // `ErrorNative()` etc. without direct controller access. 128 class UnderlyingSinkAlgorithmsWrapper : public UnderlyingSinkAlgorithmsBase { 129 public: 130 void StartCallback(JSContext* aCx, 131 WritableStreamDefaultController& aController, 132 JS::MutableHandle<JS::Value> aRetVal, 133 ErrorResult& aRv) final { 134 // Step 1: Let startAlgorithm be an algorithm that returns undefined. 135 aRetVal.setUndefined(); 136 } 137 138 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WriteCallback( 139 JSContext* aCx, JS::Handle<JS::Value> aChunk, 140 WritableStreamDefaultController& aController, ErrorResult& aRv) final; 141 142 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseCallback( 143 JSContext* aCx, ErrorResult& aRv) final; 144 145 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> AbortCallback( 146 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 147 ErrorResult& aRv) final; 148 149 virtual already_AddRefed<Promise> WriteCallbackImpl( 150 JSContext* aCx, JS::Handle<JS::Value> aChunk, 151 WritableStreamDefaultController& aController, ErrorResult& aRv) = 0; 152 153 virtual already_AddRefed<Promise> CloseCallbackImpl(JSContext* aCx, 154 ErrorResult& aRv) { 155 // (closeAlgorithm is optional, give null by default) 156 return nullptr; 157 } 158 159 virtual already_AddRefed<Promise> AbortCallbackImpl( 160 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 161 ErrorResult& aRv) { 162 // (abortAlgorithm is optional, give null by default) 163 return nullptr; 164 } 165 }; 166 167 class WritableStreamToOutput final : public UnderlyingSinkAlgorithmsWrapper, 168 public nsIOutputStreamCallback { 169 NS_DECL_ISUPPORTS_INHERITED 170 NS_DECL_NSIOUTPUTSTREAMCALLBACK 171 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WritableStreamToOutput, 172 UnderlyingSinkAlgorithmsBase) 173 174 WritableStreamToOutput(nsIGlobalObject* aParent, 175 nsIAsyncOutputStream* aOutput) 176 : mWritten(0), mParent(aParent), mOutput(aOutput) {} 177 178 // Streams algorithms 179 180 already_AddRefed<Promise> WriteCallbackImpl( 181 JSContext* aCx, JS::Handle<JS::Value> aChunk, 182 WritableStreamDefaultController& aController, ErrorResult& aRv) override; 183 184 // No CloseCallbackImpl() since ReleaseObjects() will call Close() 185 186 already_AddRefed<Promise> AbortCallbackImpl( 187 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 188 ErrorResult& aRv) override; 189 190 void ReleaseObjects() override; 191 192 private: 193 ~WritableStreamToOutput() override = default; 194 195 void ClearData() { 196 mData = Nothing(); 197 mPromise = nullptr; 198 mWritten = 0; 199 } 200 201 uint32_t mWritten; 202 nsCOMPtr<nsIGlobalObject> mParent; 203 nsCOMPtr<nsIAsyncOutputStream> mOutput; 204 RefPtr<Promise> mPromise; // Resolved when entirely written 205 Maybe<Buffer<uint8_t>> mData; 206 }; 207 208 } // namespace mozilla::dom 209 210 #endif