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/HoldDropJSObjects.h"
11 #include "mozilla/dom/Promise.h"
12 #include "mozilla/dom/UnderlyingSourceBinding.h"
13 #include "mozilla/WeakPtr.h"
14 #include "nsIAsyncInputStream.h"
15 #include "nsISupports.h"
16 #include "nsISupportsImpl.h"
18 /* Since the streams specification has native descriptions of some callbacks
19 * (i.e. described in prose, rather than provided by user code), we need to be
20 * able to pass around native callbacks. To handle this, we define polymorphic
21 * classes That cover the difference between native callback and user-provided.
23 * The Streams specification wants us to invoke these callbacks, run through
24 * WebIDL as if they were methods. So we have to preserve the underlying object
25 * to use as the This value on invocation.
27 enum class nsresult
: uint32_t;
29 namespace mozilla::dom
{
31 class StrongWorkerRef
;
32 class BodyStreamHolder
;
33 class ReadableStreamController
;
36 class UnderlyingSourceAlgorithmsBase
: public nsISupports
{
38 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
39 NS_DECL_CYCLE_COLLECTION_CLASS(UnderlyingSourceAlgorithmsBase
)
41 MOZ_CAN_RUN_SCRIPT
virtual void StartCallback(
42 JSContext
* aCx
, ReadableStreamController
& aController
,
43 JS::MutableHandle
<JS::Value
> aRetVal
, ErrorResult
& aRv
) = 0;
45 // A promise-returning algorithm that pulls data from the underlying byte
47 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> PullCallback(
48 JSContext
* aCx
, ReadableStreamController
& aController
,
49 ErrorResult
& aRv
) = 0;
51 // A promise-returning algorithm, taking one argument (the cancel reason),
52 // which communicates a requested cancelation to the underlying byte source
53 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> CancelCallback(
54 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
55 ErrorResult
& aRv
) = 0;
57 // Implement this when you need to release underlying resources immediately
58 // from closed(canceled)/errored streams, without waiting for GC.
59 virtual void ReleaseObjects() {}
61 // Fetch wants to special-case nsIInputStream-based streams
62 virtual nsIInputStream
* MaybeGetInputStreamIfUnread() { return nullptr; }
64 // https://streams.spec.whatwg.org/#other-specs-rs-create
65 // By "native" we mean "instances initialized via the above set up or set up
66 // with byte reading support algorithms (not, e.g., on web-developer-created
68 virtual bool IsNative() { return true; }
71 virtual ~UnderlyingSourceAlgorithmsBase() = default;
74 class UnderlyingSourceAlgorithms final
: public UnderlyingSourceAlgorithmsBase
{
76 NS_DECL_ISUPPORTS_INHERITED
77 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
78 UnderlyingSourceAlgorithms
, UnderlyingSourceAlgorithmsBase
)
80 UnderlyingSourceAlgorithms(nsIGlobalObject
* aGlobal
,
81 JS::Handle
<JSObject
*> aUnderlyingSource
,
82 UnderlyingSource
& aUnderlyingSourceDict
)
83 : mGlobal(aGlobal
), mUnderlyingSource(aUnderlyingSource
) {
84 // Step 6. (implicit Step 2.)
85 if (aUnderlyingSourceDict
.mStart
.WasPassed()) {
86 mStartCallback
= aUnderlyingSourceDict
.mStart
.Value();
89 // Step 7. (implicit Step 3.)
90 if (aUnderlyingSourceDict
.mPull
.WasPassed()) {
91 mPullCallback
= aUnderlyingSourceDict
.mPull
.Value();
94 // Step 8. (implicit Step 4.)
95 if (aUnderlyingSourceDict
.mCancel
.WasPassed()) {
96 mCancelCallback
= aUnderlyingSourceDict
.mCancel
.Value();
99 mozilla::HoldJSObjects(this);
102 MOZ_CAN_RUN_SCRIPT
void StartCallback(JSContext
* aCx
,
103 ReadableStreamController
& aController
,
104 JS::MutableHandle
<JS::Value
> aRetVal
,
105 ErrorResult
& aRv
) override
;
107 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> PullCallback(
108 JSContext
* aCx
, ReadableStreamController
& aController
,
109 ErrorResult
& aRv
) override
;
111 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
112 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
113 ErrorResult
& aRv
) override
;
115 bool IsNative() override
{ return false; }
118 ~UnderlyingSourceAlgorithms() override
{ mozilla::DropJSObjects(this); };
121 // Virtually const, but are cycle collected
122 nsCOMPtr
<nsIGlobalObject
> mGlobal
;
123 JS::Heap
<JSObject
*> mUnderlyingSource
;
124 MOZ_KNOWN_LIVE RefPtr
<UnderlyingSourceStartCallback
> mStartCallback
;
125 MOZ_KNOWN_LIVE RefPtr
<UnderlyingSourcePullCallback
> mPullCallback
;
126 MOZ_KNOWN_LIVE RefPtr
<UnderlyingSourceCancelCallback
> mCancelCallback
;
129 // https://streams.spec.whatwg.org/#readablestream-set-up
130 // https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support
131 // Wrappers defined by the "Set up" methods in the spec. This helps you just
132 // return nullptr when an error occurred as this wrapper converts it to a
134 // Note that StartCallback is only for JS consumers to access
135 // the controller, and thus is no-op here since native consumers can call
136 // `EnqueueNative()` etc. without direct controller access.
137 class UnderlyingSourceAlgorithmsWrapper
138 : public UnderlyingSourceAlgorithmsBase
{
139 void StartCallback(JSContext
*, ReadableStreamController
&,
140 JS::MutableHandle
<JS::Value
> aRetVal
, ErrorResult
&) final
;
142 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> PullCallback(
143 JSContext
* aCx
, ReadableStreamController
& aController
,
144 ErrorResult
& aRv
) final
;
146 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
147 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
148 ErrorResult
& aRv
) final
;
150 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> PullCallbackImpl(
151 JSContext
* aCx
, ReadableStreamController
& aController
, ErrorResult
& aRv
) {
152 // pullAlgorithm is optional, return null by default
156 MOZ_CAN_RUN_SCRIPT
virtual already_AddRefed
<Promise
> CancelCallbackImpl(
157 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
159 // cancelAlgorithm is optional, return null by default
164 class InputToReadableStreamAlgorithms
;
166 // This class exists to isolate InputToReadableStreamAlgorithms from the
167 // nsIAsyncInputStream. If we call AsyncWait(this,...), it holds a
168 // reference to 'this' which can't be cc'd, and we can leak the stream,
169 // causing a Worker to assert with globalScopeAlive. By isolating
170 // ourselves from the inputstream, we can safely be CC'd if needed and
171 // will inform the inputstream to shut down.
172 class InputStreamHolder final
: public nsIInputStreamCallback
{
174 NS_DECL_THREADSAFE_ISUPPORTS
175 NS_DECL_NSIINPUTSTREAMCALLBACK
177 InputStreamHolder(InputToReadableStreamAlgorithms
* aCallback
,
178 nsIAsyncInputStream
* aInput
);
180 void Init(JSContext
* aCx
);
182 // Used by Worker shutdown
185 // These just proxy the calls to the nsIAsyncInputStream
186 nsresult
AsyncWait(uint32_t aFlags
, uint32_t aRequestedCount
,
187 nsIEventTarget
* aEventTarget
);
188 nsresult
Available(uint64_t* aSize
) { return mInput
->Available(aSize
); }
189 nsresult
Read(char* aBuffer
, uint32_t aLength
, uint32_t* aWritten
) {
190 return mInput
->Read(aBuffer
, aLength
, aWritten
);
192 nsresult
CloseWithStatus(nsresult aStatus
) {
193 return mInput
->CloseWithStatus(aStatus
);
197 ~InputStreamHolder();
199 // WeakPtr to avoid cycles
200 WeakPtr
<InputToReadableStreamAlgorithms
> mCallback
;
201 // To ensure the worker sticks around
202 RefPtr
<StrongWorkerRef
> mAsyncWaitWorkerRef
;
203 RefPtr
<StrongWorkerRef
> mWorkerRef
;
204 nsCOMPtr
<nsIAsyncInputStream
> mInput
;
207 class InputToReadableStreamAlgorithms final
208 : public UnderlyingSourceAlgorithmsWrapper
,
209 public nsIInputStreamCallback
,
210 public SupportsWeakPtr
{
211 NS_DECL_ISUPPORTS_INHERITED
212 NS_DECL_NSIINPUTSTREAMCALLBACK
213 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InputToReadableStreamAlgorithms
,
214 UnderlyingSourceAlgorithmsWrapper
)
216 InputToReadableStreamAlgorithms(JSContext
* aCx
, nsIAsyncInputStream
* aInput
,
217 ReadableStream
* aStream
)
218 : mOwningEventTarget(GetCurrentSerialEventTarget()),
219 mInput(new InputStreamHolder(this, aInput
)),
224 // Streams algorithms
226 already_AddRefed
<Promise
> PullCallbackImpl(
227 JSContext
* aCx
, ReadableStreamController
& aController
,
228 ErrorResult
& aRv
) override
;
230 void ReleaseObjects() override
;
233 ~InputToReadableStreamAlgorithms() {
239 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void CloseAndReleaseObjects(
240 JSContext
* aCx
, ReadableStream
* aStream
);
242 void WriteIntoReadRequestBuffer(JSContext
* aCx
, ReadableStream
* aStream
,
243 JS::Handle
<JSObject
*> aBuffer
,
244 uint32_t aLength
, uint32_t* aByteWritten
,
247 // https://streams.spec.whatwg.org/#readablestream-pull-from-bytes
248 // (Uses InputStreamHolder for the "byte sequence" in the spec)
249 MOZ_CAN_RUN_SCRIPT
void PullFromInputStream(JSContext
* aCx
,
253 void ErrorPropagation(JSContext
* aCx
, ReadableStream
* aStream
,
258 bool IsClosed() { return !mInput
; }
260 nsCOMPtr
<nsIEventTarget
> mOwningEventTarget
;
262 // This promise is created by PullCallback and resolved when
263 // OnInputStreamReady succeeds. No need to try hard to settle it though, see
264 // also ReleaseObjects() for the reason.
265 RefPtr
<Promise
> mPullPromise
;
267 RefPtr
<InputStreamHolder
> mInput
;
269 // mStream never changes after construction and before CC
270 MOZ_KNOWN_LIVE RefPtr
<ReadableStream
> mStream
;
273 class NonAsyncInputToReadableStreamAlgorithms
274 : public UnderlyingSourceAlgorithmsWrapper
{
276 NS_DECL_ISUPPORTS_INHERITED
277 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
278 NonAsyncInputToReadableStreamAlgorithms
,
279 UnderlyingSourceAlgorithmsWrapper
)
281 explicit NonAsyncInputToReadableStreamAlgorithms(nsIInputStream
& aInput
)
284 already_AddRefed
<Promise
> PullCallbackImpl(
285 JSContext
* aCx
, ReadableStreamController
& aController
,
286 ErrorResult
& aRv
) override
;
288 void ReleaseObjects() override
{
289 if (RefPtr
<InputToReadableStreamAlgorithms
> algorithms
=
290 mAsyncAlgorithms
.forget()) {
291 algorithms
->ReleaseObjects();
293 if (nsCOMPtr
<nsIInputStream
> input
= mInput
.forget()) {
298 nsIInputStream
* MaybeGetInputStreamIfUnread() override
{
299 MOZ_ASSERT(mInput
, "Should be only called on non-disturbed streams");
304 ~NonAsyncInputToReadableStreamAlgorithms() = default;
306 nsCOMPtr
<nsIInputStream
> mInput
;
307 RefPtr
<InputToReadableStreamAlgorithms
> mAsyncAlgorithms
;
310 } // namespace mozilla::dom