1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_UnderlyingSourceCallbackHelpers_h
8 #define mozilla_dom_UnderlyingSourceCallbackHelpers_h
10 #include "mozilla/DOMEventTargetHelper.h"
11 #include "mozilla/HoldDropJSObjects.h"
12 #include "mozilla/dom/Promise.h"
13 #include "mozilla/dom/UnderlyingSourceBinding.h"
14 #include "mozilla/WeakPtr.h"
15 #include "nsIAsyncInputStream.h"
16 #include "nsISupports.h"
17 #include "nsISupportsImpl.h"
19 /* Since the streams specification has native descriptions of some callbacks
20 * (i.e. described in prose, rather than provided by user code), we need to be
21 * able to pass around native callbacks. To handle this, we define polymorphic
22 * classes That cover the difference between native callback and user-provided.
24 * The Streams specification wants us to invoke these callbacks, run through
25 * WebIDL as if they were methods. So we have to preserve the underlying object
26 * to use as the This value on invocation.
28 enum class nsresult
: uint32_t;
30 namespace mozilla::dom
{
32 class StrongWorkerRef
;
33 class BodyStreamHolder
;
34 class ReadableStreamController
;
37 class UnderlyingSourceAlgorithmsBase
: public nsISupports
{
39 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
40 NS_DECL_CYCLE_COLLECTION_CLASS(UnderlyingSourceAlgorithmsBase
)
42 MOZ_CAN_RUN_SCRIPT
virtual void StartCallback(
43 JSContext
* aCx
, ReadableStreamController
& aController
,
44 JS::MutableHandle
<JS::Value
> aRetVal
, ErrorResult
& aRv
) = 0;
46 // A promise-returning algorithm that pulls data from the underlying byte
48 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> PullCallback(
49 JSContext
* aCx
, ReadableStreamController
& aController
,
50 ErrorResult
& aRv
) = 0;
52 // A promise-returning algorithm, taking one argument (the cancel reason),
53 // which communicates a requested cancelation to the underlying byte source
54 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> CancelCallback(
55 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
56 ErrorResult
& aRv
) = 0;
58 // Implement this when you need to release underlying resources immediately
59 // from closed(canceled)/errored streams, without waiting for GC.
60 virtual void ReleaseObjects() {}
62 // Can be used to read chunks directly via nsIInputStream to skip JS-related
63 // overhead, if this readable stream is a wrapper of a native stream.
64 // Currently used by Fetch helper functions e.g. new Response(stream).text()
65 virtual nsIInputStream
* MaybeGetInputStreamIfUnread() { return nullptr; }
67 // https://streams.spec.whatwg.org/#other-specs-rs-create
68 // By "native" we mean "instances initialized via the above set up or set up
69 // with byte reading support algorithms (not, e.g., on web-developer-created
71 virtual bool IsNative() { return true; }
74 virtual ~UnderlyingSourceAlgorithmsBase() = default;
77 class UnderlyingSourceAlgorithms final
: public UnderlyingSourceAlgorithmsBase
{
79 NS_DECL_ISUPPORTS_INHERITED
80 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
81 UnderlyingSourceAlgorithms
, UnderlyingSourceAlgorithmsBase
)
83 UnderlyingSourceAlgorithms(nsIGlobalObject
* aGlobal
,
84 JS::Handle
<JSObject
*> aUnderlyingSource
,
85 UnderlyingSource
& aUnderlyingSourceDict
)
86 : mGlobal(aGlobal
), mUnderlyingSource(aUnderlyingSource
) {
87 // Step 6. (implicit Step 2.)
88 if (aUnderlyingSourceDict
.mStart
.WasPassed()) {
89 mStartCallback
= aUnderlyingSourceDict
.mStart
.Value();
92 // Step 7. (implicit Step 3.)
93 if (aUnderlyingSourceDict
.mPull
.WasPassed()) {
94 mPullCallback
= aUnderlyingSourceDict
.mPull
.Value();
97 // Step 8. (implicit Step 4.)
98 if (aUnderlyingSourceDict
.mCancel
.WasPassed()) {
99 mCancelCallback
= aUnderlyingSourceDict
.mCancel
.Value();
102 mozilla::HoldJSObjects(this);
105 MOZ_CAN_RUN_SCRIPT
void StartCallback(JSContext
* aCx
,
106 ReadableStreamController
& aController
,
107 JS::MutableHandle
<JS::Value
> aRetVal
,
108 ErrorResult
& aRv
) override
;
110 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> PullCallback(
111 JSContext
* aCx
, ReadableStreamController
& aController
,
112 ErrorResult
& aRv
) override
;
114 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
115 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
116 ErrorResult
& aRv
) override
;
118 bool IsNative() override
{ return false; }
121 ~UnderlyingSourceAlgorithms() override
{ mozilla::DropJSObjects(this); };
124 // Virtually const, but are cycle collected
125 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
126 JS::Heap
<JSObject
*> mUnderlyingSource
;
127 MOZ_KNOWN_LIVE RefPtr
<UnderlyingSourceStartCallback
> mStartCallback
;
128 MOZ_KNOWN_LIVE RefPtr
<UnderlyingSourcePullCallback
> mPullCallback
;
129 MOZ_KNOWN_LIVE RefPtr
<UnderlyingSourceCancelCallback
> mCancelCallback
;
132 // https://streams.spec.whatwg.org/#readablestream-set-up
133 // https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support
134 // Wrappers defined by the "Set up" methods in the spec. This helps you just
135 // return nullptr when an error occurred as this wrapper converts it to a
137 // Note that StartCallback is only for JS consumers to access
138 // the controller, and thus is no-op here since native consumers can call
139 // `EnqueueNative()` etc. without direct controller access.
140 class UnderlyingSourceAlgorithmsWrapper
141 : public UnderlyingSourceAlgorithmsBase
{
142 void StartCallback(JSContext
*, ReadableStreamController
&,
143 JS::MutableHandle
<JS::Value
> aRetVal
, ErrorResult
&) final
;
145 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> PullCallback(
146 JSContext
* aCx
, ReadableStreamController
& aController
,
147 ErrorResult
& aRv
) final
;
149 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
150 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
151 ErrorResult
& aRv
) final
;
153 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> PullCallbackImpl(
154 JSContext
* aCx
, ReadableStreamController
& aController
, ErrorResult
& aRv
) {
155 // pullAlgorithm is optional, return null by default
159 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> CancelCallbackImpl(
160 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
162 // cancelAlgorithm is optional, return null by default
167 class InputToReadableStreamAlgorithms
;
169 // This class exists to isolate InputToReadableStreamAlgorithms from the
170 // nsIAsyncInputStream. If we call AsyncWait(this,...), it holds a
171 // reference to 'this' which can't be cc'd, and we can leak the stream,
172 // causing a Worker to assert with globalScopeAlive. By isolating
173 // ourselves from the inputstream, we can safely be CC'd if needed and
174 // will inform the inputstream to shut down.
175 class InputStreamHolder final
: public nsIInputStreamCallback
,
176 public GlobalTeardownObserver
{
178 NS_DECL_THREADSAFE_ISUPPORTS
179 NS_DECL_NSIINPUTSTREAMCALLBACK
181 InputStreamHolder(nsIGlobalObject
* aGlobal
,
182 InputToReadableStreamAlgorithms
* aCallback
,
183 nsIAsyncInputStream
* aInput
);
185 void Init(JSContext
* aCx
);
187 void DisconnectFromOwner() override
;
189 // Used by global teardown
192 // These just proxy the calls to the nsIAsyncInputStream
193 nsresult
AsyncWait(uint32_t aFlags
, uint32_t aRequestedCount
,
194 nsIEventTarget
* aEventTarget
);
195 nsresult
Available(uint64_t* aSize
) { return mInput
->Available(aSize
); }
196 nsresult
Read(char* aBuffer
, uint32_t aLength
, uint32_t* aWritten
) {
197 return mInput
->Read(aBuffer
, aLength
, aWritten
);
199 nsresult
CloseWithStatus(nsresult aStatus
) {
200 return mInput
->CloseWithStatus(aStatus
);
203 nsIAsyncInputStream
* GetInputStream() { return mInput
; }
206 ~InputStreamHolder();
208 // WeakPtr to avoid cycles
209 WeakPtr
<InputToReadableStreamAlgorithms
> mCallback
;
210 // To ensure the worker sticks around
211 RefPtr
<StrongWorkerRef
> mAsyncWaitWorkerRef
;
212 RefPtr
<StrongWorkerRef
> mWorkerRef
;
213 nsCOMPtr
<nsIAsyncInputStream
> mInput
;
215 // To ensure the underlying source sticks around during an ongoing read
216 // operation. mAlgorithms is not cycle collected on purpose, and this holder
217 // is responsible to keep the underlying source algorithms until
218 // nsIAsyncInputStream responds.
220 // This is done because otherwise the whole stream objects may be cycle
221 // collected, including the promises created from read(), as our JS engine may
222 // throw unsettled promises away for optimization. See bug 1849860.
223 RefPtr
<InputToReadableStreamAlgorithms
> mAsyncWaitAlgorithms
;
226 // Using this class means you are also passing the lifetime control of your
227 // nsIAsyncInputStream, as it will be closed when this class tears down.
228 class InputToReadableStreamAlgorithms final
229 : public UnderlyingSourceAlgorithmsWrapper
,
230 public nsIInputStreamCallback
,
231 public SupportsWeakPtr
{
232 NS_DECL_ISUPPORTS_INHERITED
233 NS_DECL_NSIINPUTSTREAMCALLBACK
234 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InputToReadableStreamAlgorithms
,
235 UnderlyingSourceAlgorithmsWrapper
)
237 InputToReadableStreamAlgorithms(JSContext
* aCx
, nsIAsyncInputStream
* aInput
,
238 ReadableStream
* aStream
);
240 // Streams algorithms
242 already_AddRefed
<Promise
> PullCallbackImpl(
243 JSContext
* aCx
, ReadableStreamController
& aController
,
244 ErrorResult
& aRv
) override
;
246 void ReleaseObjects() override
;
248 nsIInputStream
* MaybeGetInputStreamIfUnread() override
;
251 ~InputToReadableStreamAlgorithms() {
257 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void CloseAndReleaseObjects(
258 JSContext
* aCx
, ReadableStream
* aStream
);
260 void WriteIntoReadRequestBuffer(JSContext
* aCx
, ReadableStream
* aStream
,
261 JS::Handle
<JSObject
*> aBuffer
,
262 uint32_t aLength
, uint32_t* aByteWritten
,
265 // https://streams.spec.whatwg.org/#readablestream-pull-from-bytes
266 // (Uses InputStreamHolder for the "byte sequence" in the spec)
267 MOZ_CAN_RUN_SCRIPT
void PullFromInputStream(JSContext
* aCx
,
271 void ErrorPropagation(JSContext
* aCx
, ReadableStream
* aStream
,
276 bool IsClosed() { return !mInput
; }
278 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
280 // This promise is created by PullCallback and resolved when
281 // OnInputStreamReady succeeds. No need to try hard to settle it though, see
282 // also ReleaseObjects() for the reason.
283 RefPtr
<Promise
> mPullPromise
;
285 RefPtr
<InputStreamHolder
> mInput
;
287 // mStream never changes after construction and before CC
288 MOZ_KNOWN_LIVE RefPtr
<ReadableStream
> mStream
;
291 class NonAsyncInputToReadableStreamAlgorithms
292 : public UnderlyingSourceAlgorithmsWrapper
{
294 NS_DECL_ISUPPORTS_INHERITED
295 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
296 NonAsyncInputToReadableStreamAlgorithms
,
297 UnderlyingSourceAlgorithmsWrapper
)
299 explicit NonAsyncInputToReadableStreamAlgorithms(nsIInputStream
& aInput
)
302 already_AddRefed
<Promise
> PullCallbackImpl(
303 JSContext
* aCx
, ReadableStreamController
& aController
,
304 ErrorResult
& aRv
) override
;
306 void ReleaseObjects() override
{
307 if (RefPtr
<InputToReadableStreamAlgorithms
> algorithms
=
308 mAsyncAlgorithms
.forget()) {
309 algorithms
->ReleaseObjects();
311 if (nsCOMPtr
<nsIInputStream
> input
= mInput
.forget()) {
316 nsIInputStream
* MaybeGetInputStreamIfUnread() override
{
317 MOZ_ASSERT(mInput
, "Should be only called on non-disturbed streams");
322 ~NonAsyncInputToReadableStreamAlgorithms() = default;
324 nsCOMPtr
<nsIInputStream
> mInput
;
325 RefPtr
<InputToReadableStreamAlgorithms
> mAsyncAlgorithms
;
328 } // namespace mozilla::dom