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 #include "js/Exception.h"
8 #include "js/TypeDecls.h"
9 #include "js/Value.h"
10 #include "mozilla/AlreadyAddRefed.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/dom/AbortSignal.h"
13 #include "mozilla/dom/Promise.h"
14 #include "mozilla/dom/PromiseNativeHandler.h"
15 #include "mozilla/dom/WritableStream.h"
16 #include "mozilla/dom/WritableStreamDefaultController.h"
17 #include "mozilla/dom/WritableStreamDefaultControllerBinding.h"
18 // #include "mozilla/dom/ReadableStreamDefaultReaderBinding.h"
19 #include "mozilla/dom/UnderlyingSinkBinding.h"
20 #include "nsCycleCollectionParticipant.h"
21 #include "nsDebug.h"
22 #include "nsISupports.h"
23 
24 namespace mozilla::dom {
25 
26 // Note: Using the individual macros vs NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE
27 // because I need to specificy a manual implementation of
28 // NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN.
29 NS_IMPL_CYCLE_COLLECTION_CLASS(WritableStreamDefaultController)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WritableStreamDefaultController)
31   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal, mStrategySizeAlgorithm,
32                                   mWriteAlgorithm, mCloseAlgorithm,
33                                   mAbortAlgorithm, mStream)
34   tmp->mQueue.clear();
35   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WritableStreamDefaultController)
38   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal, mStrategySizeAlgorithm,
39                                     mWriteAlgorithm, mCloseAlgorithm,
40                                     mAbortAlgorithm, mStream)
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
42 
43 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WritableStreamDefaultController)
44   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
45   // Trace the associated queue.
46   for (const auto& queueEntry : tmp->mQueue) {
47     aCallbacks.Trace(&queueEntry->mValue, "mQueue.mValue", aClosure);
48   }
49 NS_IMPL_CYCLE_COLLECTION_TRACE_END
50 
NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultController)51 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultController)
52 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStreamDefaultController)
53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStreamDefaultController)
54   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
55   NS_INTERFACE_MAP_ENTRY(nsISupports)
56 NS_INTERFACE_MAP_END
57 
58 WritableStreamDefaultController::WritableStreamDefaultController(
59     nsISupports* aGlobal, WritableStream& aStream)
60     : mGlobal(do_QueryInterface(aGlobal)), mStream(&aStream) {}
61 
~WritableStreamDefaultController()62 WritableStreamDefaultController::~WritableStreamDefaultController() {
63   // MG:XXX: LinkedLists are required to be empty at destruction, but it seems
64   //         it is possible to have a controller be destructed while still
65   //         having entries in its queue.
66   //
67   //         This needs to be verified as not indicating some other issue.
68   mQueue.clear();
69 }
70 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)71 JSObject* WritableStreamDefaultController::WrapObject(
72     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
73   return WritableStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto);
74 }
75 
76 // https://streams.spec.whatwg.org/#ws-default-controller-error
Error(JSContext * aCx,JS::Handle<JS::Value> aError,ErrorResult & aRv)77 void WritableStreamDefaultController::Error(JSContext* aCx,
78                                             JS::Handle<JS::Value> aError,
79                                             ErrorResult& aRv) {
80   // Step 1. Let state be this.[[stream]].[[state]].
81   // Step 2. If state is not "writable", return.
82   if (mStream->State() != WritableStream::WriterState::Writable) {
83     return;
84   }
85   // Step 3. Perform ! WritableStreamDefaultControllerError(this, e).
86   RefPtr<WritableStreamDefaultController> thisRefPtr = this;
87   WritableStreamDefaultControllerError(aCx, thisRefPtr, aError, aRv);
88 }
89 
90 // https://streams.spec.whatwg.org/#ws-default-controller-private-abort
AbortSteps(JSContext * aCx,JS::Handle<JS::Value> aReason,ErrorResult & aRv)91 already_AddRefed<Promise> WritableStreamDefaultController::AbortSteps(
92     JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
93   // Step 1. Let result be the result of performing this.[[abortAlgorithm]],
94   // passing reason.
95   RefPtr<UnderlyingSinkAbortCallbackHelper> abortAlgorithm(mAbortAlgorithm);
96   Optional<JS::Handle<JS::Value>> optionalReason(aCx, aReason);
97   RefPtr<Promise> abortPromise =
98       abortAlgorithm
99           ? abortAlgorithm->AbortCallback(aCx, optionalReason, aRv)
100           : Promise::CreateResolvedWithUndefined(GetParentObject(), aRv);
101   if (aRv.Failed()) {
102     return nullptr;
103   }
104 
105   // Step 2. Perform ! WritableStreamDefaultControllerClearAlgorithms(this).
106   ClearAlgorithms();
107 
108   // Step 3. Return result.
109   return abortPromise.forget();
110 }
111 
112 // https://streams.spec.whatwg.org/#ws-default-controller-private-error
ErrorSteps()113 void WritableStreamDefaultController::ErrorSteps() {
114   // Step 1. Perform ! ResetQueue(this).
115   ResetQueue(this);
116 }
117 
SetSignal(AbortSignal * aSignal)118 void WritableStreamDefaultController::SetSignal(AbortSignal* aSignal) {
119   MOZ_ASSERT(aSignal);
120   mSignal = aSignal;
121 }
122 
123 MOZ_CAN_RUN_SCRIPT static void
124 WritableStreamDefaultControllerAdvanceQueueIfNeeded(
125     JSContext* aCx, WritableStreamDefaultController* aController,
126     ErrorResult& aRv);
127 
128 class WritableStartPromiseNativeHandler final : public PromiseNativeHandler {
129   ~WritableStartPromiseNativeHandler() override = default;
130 
131   // Virtually const, but cycle collected
132   RefPtr<WritableStreamDefaultController> mController;
133 
134  public:
135   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(WritableStartPromiseNativeHandler)136   NS_DECL_CYCLE_COLLECTION_CLASS(WritableStartPromiseNativeHandler)
137 
138   explicit WritableStartPromiseNativeHandler(
139       WritableStreamDefaultController* aController)
140       : PromiseNativeHandler(), mController(aController) {}
141 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)142   MOZ_CAN_RUN_SCRIPT void ResolvedCallback(JSContext* aCx,
143                                            JS::Handle<JS::Value> aValue,
144                                            ErrorResult& aRv) override {
145     // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
146     // Step 17. Upon fulfillment of startPromise,
147     // Step 17.1. Assert: stream.[[state]] is "writable" or "erroring".
148     MOZ_ASSERT(mController->Stream()->State() ==
149                    WritableStream::WriterState::Writable ||
150                mController->Stream()->State() ==
151                    WritableStream::WriterState::Erroring);
152     // Step 17.2. Set controller.[[started]] to true.
153     mController->SetStarted(true);
154     // Step 17.3 Perform
155     // ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
156     WritableStreamDefaultControllerAdvanceQueueIfNeeded(
157         aCx, MOZ_KnownLive(mController), aRv);
158   }
159 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)160   MOZ_CAN_RUN_SCRIPT void RejectedCallback(JSContext* aCx,
161                                            JS::Handle<JS::Value> aValue,
162                                            ErrorResult& aRv) override {
163     // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
164     RefPtr<WritableStream> stream = mController->Stream();
165     // Step 18. Upon rejection of startPromise with reason r,
166     // Step 18.1. Assert: stream.[[state]] is "writable" or "erroring".
167     MOZ_ASSERT(stream->State() == WritableStream::WriterState::Writable ||
168                stream->State() == WritableStream::WriterState::Erroring);
169     // Step 18.2. Set controller.[[started]] to true.
170     mController->SetStarted(true);
171     // Step 18.3. Perform ! WritableStreamDealWithRejection(stream, r).
172     stream->DealWithRejection(aCx, aValue, aRv);
173   }
174 };
175 
NS_IMPL_CYCLE_COLLECTION(WritableStartPromiseNativeHandler,mController)176 NS_IMPL_CYCLE_COLLECTION(WritableStartPromiseNativeHandler, mController)
177 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStartPromiseNativeHandler)
178 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStartPromiseNativeHandler)
179 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStartPromiseNativeHandler)
180   NS_INTERFACE_MAP_ENTRY(nsISupports)
181 NS_INTERFACE_MAP_END
182 
183 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
184 void SetUpWritableStreamDefaultController(
185     JSContext* aCx, WritableStream* aStream,
186     WritableStreamDefaultController* aController,
187     UnderlyingSinkStartCallbackHelper* aStartAlgorithm,
188     UnderlyingSinkWriteCallbackHelper* aWriteAlgorithm,
189     UnderlyingSinkCloseCallbackHelper* aCloseAlgorithm,
190     UnderlyingSinkAbortCallbackHelper* aAbortAlgorithm, double aHighWaterMark,
191     QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
192   // Step 1. Assert: stream implements WritableStream.
193   // Step 2. Assert: stream.[[controller]] is undefined.
194   MOZ_ASSERT(!aStream->Controller());
195 
196   // Step 3. Set controller.[[stream]] to stream.
197   // Note: Already set in
198   // SetUpWritableStreamDefaultControllerFromUnderlyingSink.
199   MOZ_ASSERT(aController->Stream() == aStream);
200 
201   // Step 4. Set stream.[[controller]] to controller.
202   aStream->SetController(aController);
203 
204   // Step 5. Perform ! ResetQueue(controller).
205   ResetQueue(aController);
206 
207   // Step 6. Set controller.[[signal]] to a new AbortSignal.
208   RefPtr<AbortSignal> signal = new AbortSignal(aController->GetParentObject(),
209                                                false, JS::UndefinedHandleValue);
210   aController->SetSignal(signal);
211 
212   // Step 7. Set controller.[[started]] to false.
213   aController->SetStarted(false);
214 
215   // Step 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
216   aController->SetStrategySizeAlgorithm(aSizeAlgorithm);
217 
218   // Step 9. Set controller.[[strategyHWM]] to highWaterMark.
219   aController->SetStrategyHWM(aHighWaterMark);
220 
221   // Step 10. Set controller.[[writeAlgorithm]] to writeAlgorithm.
222   aController->SetWriteAlgorithm(aWriteAlgorithm);
223 
224   // Step 11. Set controller.[[closeAlgorithm]] to closeAlgorithm.
225   aController->SetCloseAlgorithm(aCloseAlgorithm);
226 
227   // Step 12. Set controller.[[abortAlgorithm]] to abortAlgorithm.
228   aController->SetAbortAlgorithm(aAbortAlgorithm);
229 
230   // Step 13. Let backpressure be !
231   // WritableStreamDefaultControllerGetBackpressure(controller).
232   bool backpressure = aController->GetBackpressure();
233 
234   // Step 14. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
235   aStream->UpdateBackpressure(backpressure, aRv);
236   if (aRv.Failed()) {
237     return;
238   }
239 
240   // Step 15. Let startResult be the result of performing startAlgorithm. (This
241   // may throw an exception.)
242   JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue());
243   if (aStartAlgorithm) {
244     // Strong Refs:
245     RefPtr<UnderlyingSinkStartCallbackHelper> startAlgorithm(aStartAlgorithm);
246     RefPtr<WritableStreamDefaultController> controller(aController);
247 
248     startAlgorithm->StartCallback(aCx, *controller, &startResult, aRv);
249     if (aRv.Failed()) {
250       return;
251     }
252   }
253 
254   // Step 16. Let startPromise be a promise resolved with startResult.
255   RefPtr<Promise> startPromise = Promise::Create(GetIncumbentGlobal(), aRv);
256   if (aRv.Failed()) {
257     return;
258   }
259   startPromise->MaybeResolve(startResult);
260 
261   // Step 17/18.
262   RefPtr<WritableStartPromiseNativeHandler> startPromiseHandler =
263       new WritableStartPromiseNativeHandler(aController);
264   startPromise->AppendNativeHandler(startPromiseHandler);
265 }
266 
267 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
SetUpWritableStreamDefaultControllerFromUnderlyingSink(JSContext * aCx,WritableStream * aStream,JS::HandleObject aUnderlyingSink,UnderlyingSink & aUnderlyingSinkDict,double aHighWaterMark,QueuingStrategySize * aSizeAlgorithm,ErrorResult & aRv)268 void SetUpWritableStreamDefaultControllerFromUnderlyingSink(
269     JSContext* aCx, WritableStream* aStream, JS::HandleObject aUnderlyingSink,
270     UnderlyingSink& aUnderlyingSinkDict, double aHighWaterMark,
271     QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
272   // Step 1.
273   RefPtr<WritableStreamDefaultController> controller =
274       new WritableStreamDefaultController(aStream->GetParentObject(), *aStream);
275 
276   // Step 6. (implicit Step 2.)
277   RefPtr<UnderlyingSinkStartCallbackHelper> startAlgorithm =
278       aUnderlyingSinkDict.mStart.WasPassed()
279           ? new UnderlyingSinkStartCallbackHelper(
280                 aUnderlyingSinkDict.mStart.Value(), aUnderlyingSink)
281           : nullptr;
282 
283   // Step 7. (implicit Step 3.)
284   RefPtr<UnderlyingSinkWriteCallbackHelper> writeAlgorithm =
285       aUnderlyingSinkDict.mWrite.WasPassed()
286           ? new UnderlyingSinkWriteCallbackHelper(
287                 aUnderlyingSinkDict.mWrite.Value(), aUnderlyingSink)
288           : nullptr;
289 
290   // Step 8. (implicit Step 4.)
291   RefPtr<UnderlyingSinkCloseCallbackHelper> closeAlgorithm =
292       aUnderlyingSinkDict.mClose.WasPassed()
293           ? new UnderlyingSinkCloseCallbackHelper(
294                 aUnderlyingSinkDict.mClose.Value(), aUnderlyingSink)
295           : nullptr;
296 
297   // Step 9. (implicit Step 5.)
298   RefPtr<UnderlyingSinkAbortCallbackHelper> abortAlgorithm =
299       aUnderlyingSinkDict.mAbort.WasPassed()
300           ? new UnderlyingSinkAbortCallbackHelper(
301                 aUnderlyingSinkDict.mAbort.Value(), aUnderlyingSink)
302           : nullptr;
303 
304   // Step 10.
305   SetUpWritableStreamDefaultController(
306       aCx, aStream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm,
307       abortAlgorithm, aHighWaterMark, aSizeAlgorithm, aRv);
308 }
309 
310 // MG:XXX: Probably can find base class between this and
311 // StartPromiseNativeHandler
312 class SinkCloseNativePromiseHandler final : public PromiseNativeHandler {
313   ~SinkCloseNativePromiseHandler() override = default;
314 
315  public:
316   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(SinkCloseNativePromiseHandler)317   NS_DECL_CYCLE_COLLECTION_CLASS(SinkCloseNativePromiseHandler)
318 
319   explicit SinkCloseNativePromiseHandler(
320       WritableStreamDefaultController* aController)
321       : PromiseNativeHandler(), mController(aController) {}
322 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)323   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
324                         ErrorResult& aRv) override {
325     // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
326     RefPtr<WritableStream> stream = mController->Stream();
327     // Step 7. Upon fulfillment of sinkClosePromise,
328     // Step 7.1. Perform ! WritableStreamFinishInFlightClose(stream).
329     stream->FinishInFlightClose();
330   }
331 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)332   MOZ_CAN_RUN_SCRIPT void RejectedCallback(JSContext* aCx,
333                                            JS::Handle<JS::Value> aValue,
334                                            ErrorResult& aRv) override {
335     // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
336     RefPtr<WritableStream> stream = mController->Stream();
337     // Step 8. Upon rejection of sinkClosePromise with reason reason,
338     // Step 8.1. Perform ! WritableStreamFinishInFlightCloseWithError(stream,
339     // reason).
340 
341     stream->FinishInFlightCloseWithError(aCx, aValue, aRv);
342   }
343 
344  private:
345   RefPtr<WritableStreamDefaultController> mController;
346 };
347 
348 // Cycle collection methods for promise handler.
NS_IMPL_CYCLE_COLLECTION(SinkCloseNativePromiseHandler,mController)349 NS_IMPL_CYCLE_COLLECTION(SinkCloseNativePromiseHandler, mController)
350 NS_IMPL_CYCLE_COLLECTING_ADDREF(SinkCloseNativePromiseHandler)
351 NS_IMPL_CYCLE_COLLECTING_RELEASE(SinkCloseNativePromiseHandler)
352 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SinkCloseNativePromiseHandler)
353   NS_INTERFACE_MAP_ENTRY(nsISupports)
354 NS_INTERFACE_MAP_END
355 
356 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
357 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessClose(
358     JSContext* aCx, WritableStreamDefaultController* aController,
359     ErrorResult& aRv) {
360   // Step 1. Let stream be controller.[[stream]].
361   RefPtr<WritableStream> stream = aController->Stream();
362 
363   // Step 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream).
364   stream->MarkCloseRequestInFlight();
365 
366   // Step 3. Perform ! DequeueValue(controller).
367   JS::Rooted<JS::Value> value(aCx);
368   DequeueValue(aController, &value);
369 
370   // Step 4. Assert: controller.[[queue]] is empty.
371   MOZ_ASSERT(aController->Queue().isEmpty());
372 
373   // Step 5. Let sinkClosePromise be the result of performing
374   // controller.[[closeAlgorithm]].
375   RefPtr<UnderlyingSinkCloseCallbackHelper> closeAlgorithm(
376       aController->GetCloseAlgorithm());
377 
378   RefPtr<Promise> sinkClosePromise =
379       closeAlgorithm ? closeAlgorithm->CloseCallback(aCx, aRv)
380                      : Promise::CreateResolvedWithUndefined(
381                            aController->GetParentObject(), aRv);
382 
383   if (aRv.Failed()) {
384     return;
385   }
386 
387   // Step 6. Perform !
388   // WritableStreamDefaultControllerClearAlgorithms(controller).
389   aController->ClearAlgorithms();
390 
391   // Step 7 + 8.
392   sinkClosePromise->AppendNativeHandler(
393       new SinkCloseNativePromiseHandler(aController));
394 }
395 
396 // MG:XXX: Probably can find base class between this and
397 // StartPromiseNativeHandler
398 class SinkWriteNativePromiseHandler final : public PromiseNativeHandler {
399   ~SinkWriteNativePromiseHandler() override = default;
400 
401   // Virtually const, but is cycle collected
402   RefPtr<WritableStreamDefaultController> mController;
403 
404  public:
405   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(SinkWriteNativePromiseHandler)406   NS_DECL_CYCLE_COLLECTION_CLASS(SinkWriteNativePromiseHandler)
407 
408   explicit SinkWriteNativePromiseHandler(
409       WritableStreamDefaultController* aController)
410       : PromiseNativeHandler(), mController(aController) {}
411 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)412   MOZ_CAN_RUN_SCRIPT void ResolvedCallback(JSContext* aCx,
413                                            JS::Handle<JS::Value> aValue,
414                                            ErrorResult& aRv) override {
415     // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
416     RefPtr<WritableStream> stream = mController->Stream();
417 
418     // Step 4.1. Perform ! WritableStreamFinishInFlightWrite(stream).
419     stream->FinishInFlightWrite();
420 
421     // Step 4.2. Let state be stream.[[state]].
422     WritableStream::WriterState state = stream->State();
423 
424     // Step 4.3. Assert: state is "writable" or "erroring".
425     MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
426                state == WritableStream::WriterState::Erroring);
427 
428     // Step 4.4. Perform ! DequeueValue(controller).
429     JS::Rooted<JS::Value> value(aCx);
430     DequeueValue(mController, &value);
431 
432     // Step 4.5. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
433     // state is "writable",
434     if (!stream->CloseQueuedOrInFlight() &&
435         state == WritableStream::WriterState::Writable) {
436       // Step 4.5.1. Let backpressure be !
437       // WritableStreamDefaultControllerGetBackpressure(controller).
438       bool backpressure = mController->GetBackpressure();
439       // Step 4.5.2. Perform ! WritableStreamUpdateBackpressure(stream,
440       // backpressure).
441       stream->UpdateBackpressure(backpressure, aRv);
442       if (aRv.Failed()) {
443         return;
444       }
445     }
446 
447     // Step 4.6. Perform !
448     // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
449     WritableStreamDefaultControllerAdvanceQueueIfNeeded(
450         aCx, MOZ_KnownLive(mController), aRv);
451   }
452 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)453   MOZ_CAN_RUN_SCRIPT void RejectedCallback(JSContext* aCx,
454                                            JS::Handle<JS::Value> aValue,
455                                            ErrorResult& aRv) override {
456     // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
457     RefPtr<WritableStream> stream = mController->Stream();
458 
459     // Step 5.1. If stream.[[state]] is "writable", perform !
460     // WritableStreamDefaultControllerClearAlgorithms(controller).
461     if (stream->State() == WritableStream::WriterState::Writable) {
462       mController->ClearAlgorithms();
463     }
464 
465     // Step 5.2. Perform ! WritableStreamFinishInFlightWriteWithError(stream,
466     // reason)
467 
468     stream->FinishInFlightWriteWithError(aCx, aValue, aRv);
469   }
470 };
471 
472 // Cycle collection methods for promise handler.
NS_IMPL_CYCLE_COLLECTION(SinkWriteNativePromiseHandler,mController)473 NS_IMPL_CYCLE_COLLECTION(SinkWriteNativePromiseHandler, mController)
474 NS_IMPL_CYCLE_COLLECTING_ADDREF(SinkWriteNativePromiseHandler)
475 NS_IMPL_CYCLE_COLLECTING_RELEASE(SinkWriteNativePromiseHandler)
476 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SinkWriteNativePromiseHandler)
477   NS_INTERFACE_MAP_ENTRY(nsISupports)
478 NS_INTERFACE_MAP_END
479 
480 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
481 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessWrite(
482     JSContext* aCx, WritableStreamDefaultController* aController,
483     JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
484   // Step 1. Let stream be controller.[[stream]].
485   RefPtr<WritableStream> stream = aController->Stream();
486 
487   // Step 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream).
488   stream->MarkFirstWriteRequestInFlight();
489 
490   // Step 3. Let sinkWritePromise be the result of performing
491   // controller.[[writeAlgorithm]], passing in chunk.
492   RefPtr<UnderlyingSinkWriteCallbackHelper> writeAlgorithm(
493       aController->GetWriteAlgorithm());
494 
495   RefPtr<Promise> sinkWritePromise =
496       writeAlgorithm
497           ? writeAlgorithm->WriteCallback(aCx, aChunk, *aController, aRv)
498           : Promise::CreateResolvedWithUndefined(aController->GetParentObject(),
499                                                  aRv);
500   if (aRv.Failed()) {
501     return;
502   }
503 
504   // Step 4 + 5:
505   sinkWritePromise->AppendNativeHandler(
506       new SinkWriteNativePromiseHandler(aController));
507 }
508 
509 // We use a JS::MagicValue to represent the close sentinel required by the spec.
510 // Normal JavaScript code can not generate magic values, so we can use this
511 // as a special value. However care has to be taken to not leak the magic value
512 // to other code.
513 constexpr JSWhyMagic CLOSE_SENTINEL = JS_GENERIC_MAGIC;
514 
515 // https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed
WritableStreamDefaultControllerAdvanceQueueIfNeeded(JSContext * aCx,WritableStreamDefaultController * aController,ErrorResult & aRv)516 static void WritableStreamDefaultControllerAdvanceQueueIfNeeded(
517     JSContext* aCx, WritableStreamDefaultController* aController,
518     ErrorResult& aRv) {
519   // Step 1. Let stream be controller.[[stream]].
520   RefPtr<WritableStream> stream = aController->Stream();
521 
522   // Step 2. If controller.[[started]] is false, return.
523   if (!aController->Started()) {
524     return;
525   }
526 
527   // Step 3. If stream.[[inFlightWriteRequest]] is not undefined, return.
528   if (stream->GetInFlightWriteRequest()) {
529     return;
530   }
531 
532   // Step 4. Let state be stream.[[state]].
533   WritableStream::WriterState state = stream->State();
534 
535   // Step 5. Assert: state is not "closed" or "errored".
536   MOZ_ASSERT(state != WritableStream::WriterState::Closed &&
537              state != WritableStream::WriterState::Errored);
538 
539   // Step 6. If state is "erroring",
540   if (state == WritableStream::WriterState::Erroring) {
541     // Step 6.1. Perform ! WritableStreamFinishErroring(stream).
542     stream->FinishErroring(aCx, aRv);
543 
544     // Step 6.2. Return.
545     return;
546   }
547 
548   // Step 7. If controller.[[queue]] is empty, return.
549   if (aController->Queue().isEmpty()) {
550     return;
551   }
552 
553   // Step 8. Let value be ! PeekQueueValue(controller).
554   JS::Rooted<JS::Value> value(aCx);
555   PeekQueueValue(aController, &value);
556 
557   // Step 9. If value is the close sentinel, perform !
558   // WritableStreamDefaultControllerProcessClose(controller).
559   if (value.isMagic(CLOSE_SENTINEL)) {
560     WritableStreamDefaultControllerProcessClose(aCx, aController, aRv);
561     return;
562   }
563 
564   // Step 10. Otherwise, perform !
565   // WritableStreamDefaultControllerProcessWrite(controller, value).
566   WritableStreamDefaultControllerProcessWrite(aCx, aController, value, aRv);
567 }
568 
569 // https://streams.spec.whatwg.org/#writable-stream-default-controller-close
WritableStreamDefaultControllerClose(JSContext * aCx,WritableStreamDefaultController * aController,ErrorResult & aRv)570 void WritableStreamDefaultControllerClose(
571     JSContext* aCx, WritableStreamDefaultController* aController,
572     ErrorResult& aRv) {
573   // Step 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0).
574   JS::Rooted<JS::Value> aCloseSentinel(aCx, JS::MagicValue(CLOSE_SENTINEL));
575   EnqueueValueWithSize(aController, aCloseSentinel, 0, aRv);
576   MOZ_ASSERT(!aRv.Failed());
577 
578   // Step 2. Perform !
579   // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
580   WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv);
581 }
582 
583 // https://streams.spec.whatwg.org/#writable-stream-default-controller-write
WritableStreamDefaultControllerWrite(JSContext * aCx,WritableStreamDefaultController * aController,JS::Handle<JS::Value> aChunk,double chunkSize,ErrorResult & aRv)584 void WritableStreamDefaultControllerWrite(
585     JSContext* aCx, WritableStreamDefaultController* aController,
586     JS::Handle<JS::Value> aChunk, double chunkSize, ErrorResult& aRv) {
587   // Step 1. Let enqueueResult be EnqueueValueWithSize(controller, chunk,
588   // chunkSize).
589   IgnoredErrorResult rv;
590   EnqueueValueWithSize(aController, aChunk, chunkSize, rv);
591 
592   // Step 2. If enqueueResult is an abrupt completion,
593   if (rv.MaybeSetPendingException(aCx,
594                                   "WritableStreamDefaultController.write")) {
595     JS::Rooted<JS::Value> error(aCx);
596     JS_GetPendingException(aCx, &error);
597     JS_ClearPendingException(aCx);
598 
599     // Step 2.1. Perform !
600     // WritableStreamDefaultControllerErrorIfNeeded(controller,
601     // enqueueResult.[[Value]]).
602     WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv);
603 
604     // Step 2.2. Return.
605     return;
606   }
607 
608   // Step 3. Let stream be controller.[[stream]].
609   RefPtr<WritableStream> stream = aController->Stream();
610 
611   // Step 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
612   // stream.[[state]] is "writable",
613   if (!stream->CloseQueuedOrInFlight() &&
614       stream->State() == WritableStream::WriterState::Writable) {
615     // Step 4.1. Let backpressure be
616     // !WritableStreamDefaultControllerGetBackpressure(controller).
617     bool backpressure = aController->GetBackpressure();
618 
619     // Step 4.2. Perform ! WritableStreamUpdateBackpressure(stream,
620     // backpressure).
621     stream->UpdateBackpressure(backpressure, aRv);
622     if (aRv.Failed()) {
623       return;
624     }
625   }
626 
627   // Step 5. Perform
628   // ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
629   WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv);
630 }
631 
WritableStreamDefaultControllerError(JSContext * aCx,WritableStreamDefaultController * aController,JS::Handle<JS::Value> aError,ErrorResult & aRv)632 void WritableStreamDefaultControllerError(
633     JSContext* aCx, WritableStreamDefaultController* aController,
634     JS::Handle<JS::Value> aError, ErrorResult& aRv) {
635   // Step 1. Let stream be controller.[[stream]].
636   RefPtr<WritableStream> stream = aController->Stream();
637 
638   // Step 2. Assert: stream.[[state]] is "writable".
639   MOZ_ASSERT(stream->State() == WritableStream::WriterState::Writable);
640 
641   // Step 3. Perform
642   // ! WritableStreamDefaultControllerClearAlgorithms(controller).
643   aController->ClearAlgorithms();
644 
645   // Step 4.Perform ! WritableStreamStartErroring(stream, error).
646   stream->StartErroring(aCx, aError, aRv);
647 }
648 
649 // https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed
WritableStreamDefaultControllerErrorIfNeeded(JSContext * aCx,WritableStreamDefaultController * aController,JS::Handle<JS::Value> aError,ErrorResult & aRv)650 void WritableStreamDefaultControllerErrorIfNeeded(
651     JSContext* aCx, WritableStreamDefaultController* aController,
652     JS::Handle<JS::Value> aError, ErrorResult& aRv) {
653   // Step 1. If controller.[[stream]].[[state]] is "writable", perform
654   // !WritableStreamDefaultControllerError(controller, error).
655   if (aController->Stream()->State() == WritableStream::WriterState::Writable) {
656     WritableStreamDefaultControllerError(aCx, aController, aError, aRv);
657   }
658 }
659 
660 // https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size
WritableStreamDefaultControllerGetChunkSize(JSContext * aCx,WritableStreamDefaultController * aController,JS::Handle<JS::Value> aChunk,ErrorResult & aRv)661 double WritableStreamDefaultControllerGetChunkSize(
662     JSContext* aCx, WritableStreamDefaultController* aController,
663     JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
664   // Step 1. Let returnValue be the result of performing
665   // controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting
666   // the result as a completion record.
667   RefPtr<QueuingStrategySize> sizeAlgorithm(
668       aController->StrategySizeAlgorithm());
669 
670   // If !sizeAlgorithm, we return 1, which is inlined from
671   // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
672   Optional<JS::Handle<JS::Value>> optionalChunk(aCx, aChunk);
673 
674   double chunkSize =
675       sizeAlgorithm
676           ? sizeAlgorithm->Call(
677                 optionalChunk, aRv,
678                 "WritableStreamDefaultController.[[strategySizeAlgorithm]]",
679                 CallbackObject::eRethrowExceptions)
680           : 1.0;
681 
682   // Step 2. If returnValue is an abrupt completion,
683   if (aRv.MaybeSetPendingException(
684           aCx, "WritableStreamDefaultController.[[strategySizeAlgorithm]]")) {
685     JS::Rooted<JS::Value> error(aCx);
686     JS_GetPendingException(aCx, &error);
687     JS_ClearPendingException(aCx);
688 
689     // Step 2.1. Perform !
690     // WritableStreamDefaultControllerErrorIfNeeded(controller,
691     // returnValue.[[Value]]).
692     WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv);
693 
694     // Step 2.2. Return 1.
695     return 1.0;
696   }
697 
698   // Step 3. Return returnValue.[[Value]].
699   return chunkSize;
700 }
701 
702 }  // namespace mozilla::dom
703