Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / streams / UnderlyingSourceCallbackHelpers.h
blobe1c34e02dfc778832b9feb5a0ecbb28ba743959a
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;
34 class ReadableStream;
36 class UnderlyingSourceAlgorithmsBase : public nsISupports {
37 public:
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
46 // source
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
67 // instances)"
68 virtual bool IsNative() { return true; }
70 protected:
71 virtual ~UnderlyingSourceAlgorithmsBase() = default;
74 class UnderlyingSourceAlgorithms final : public UnderlyingSourceAlgorithmsBase {
75 public:
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; }
117 protected:
118 ~UnderlyingSourceAlgorithms() override { mozilla::DropJSObjects(this); };
120 private:
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
133 // rejected promise.
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
153 return nullptr;
156 MOZ_CAN_RUN_SCRIPT virtual already_AddRefed<Promise> CancelCallbackImpl(
157 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason,
158 ErrorResult& aRv) {
159 // cancelAlgorithm is optional, return null by default
160 return nullptr;
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 {
173 public:
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
183 void 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);
196 private:
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)),
220 mStream(aStream) {
221 mInput->Init(aCx);
224 // Streams algorithms
226 already_AddRefed<Promise> PullCallbackImpl(
227 JSContext* aCx, ReadableStreamController& aController,
228 ErrorResult& aRv) override;
230 void ReleaseObjects() override;
232 private:
233 ~InputToReadableStreamAlgorithms() {
234 if (mInput) {
235 mInput->Shutdown();
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,
245 ErrorResult& aRv);
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,
250 uint64_t aAvailable,
251 ErrorResult& aRv);
253 void ErrorPropagation(JSContext* aCx, ReadableStream* aStream,
254 nsresult aError);
256 // Common methods
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 {
275 public:
276 NS_DECL_ISUPPORTS_INHERITED
277 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
278 NonAsyncInputToReadableStreamAlgorithms,
279 UnderlyingSourceAlgorithmsWrapper)
281 explicit NonAsyncInputToReadableStreamAlgorithms(nsIInputStream& aInput)
282 : mInput(&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()) {
294 input->Close();
298 nsIInputStream* MaybeGetInputStreamIfUnread() override {
299 MOZ_ASSERT(mInput, "Should be only called on non-disturbed streams");
300 return mInput;
303 private:
304 ~NonAsyncInputToReadableStreamAlgorithms() = default;
306 nsCOMPtr<nsIInputStream> mInput;
307 RefPtr<InputToReadableStreamAlgorithms> mAsyncAlgorithms;
310 } // namespace mozilla::dom
312 #endif