TransformStreamDefaultController.cpp (9468B)
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 "mozilla/dom/TransformStreamDefaultController.h" 8 9 #include "TransformerCallbackHelpers.h" 10 #include "mozilla/Attributes.h" 11 #include "mozilla/dom/Promise.h" 12 #include "mozilla/dom/ReadableStream.h" 13 #include "mozilla/dom/ReadableStreamDefaultController.h" 14 #include "mozilla/dom/TransformStream.h" 15 #include "mozilla/dom/TransformStreamDefaultControllerBinding.h" 16 #include "nsWrapperCache.h" 17 18 namespace mozilla::dom { 19 20 using namespace streams_abstract; 21 22 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStreamDefaultController, mGlobal, 23 mStream, mTransformerAlgorithms) 24 NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStreamDefaultController) 25 NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStreamDefaultController) 26 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamDefaultController) 27 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 28 NS_INTERFACE_MAP_ENTRY(nsISupports) 29 NS_INTERFACE_MAP_END 30 31 TransformStream* TransformStreamDefaultController::Stream() { return mStream; } 32 33 void TransformStreamDefaultController::SetStream(TransformStream& aStream) { 34 MOZ_ASSERT(!mStream); 35 mStream = &aStream; 36 } 37 38 TransformerAlgorithmsBase* TransformStreamDefaultController::Algorithms() { 39 return mTransformerAlgorithms; 40 } 41 42 void TransformStreamDefaultController::SetAlgorithms( 43 TransformerAlgorithmsBase* aTransformerAlgorithms) { 44 mTransformerAlgorithms = aTransformerAlgorithms; 45 } 46 47 TransformStreamDefaultController::TransformStreamDefaultController( 48 nsIGlobalObject* aGlobal) 49 : mGlobal(aGlobal) { 50 mozilla::HoldJSObjects(this); 51 } 52 53 TransformStreamDefaultController::~TransformStreamDefaultController() { 54 mozilla::DropJSObjects(this); 55 } 56 57 JSObject* TransformStreamDefaultController::WrapObject( 58 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 59 return TransformStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto); 60 } 61 62 // https://streams.spec.whatwg.org/#ts-default-controller-desired-size 63 Nullable<double> TransformStreamDefaultController::GetDesiredSize() const { 64 // Step 1. Let readableController be 65 // this.[[stream]].[[readable]].[[controller]]. 66 RefPtr<ReadableStreamDefaultController> readableController = 67 mStream->Readable()->Controller()->AsDefault(); 68 69 // Step 2. Return ! 70 // ReadableStreamDefaultControllerGetDesiredSize(readableController). 71 return ReadableStreamDefaultControllerGetDesiredSize(readableController); 72 } 73 74 // https://streams.spec.whatwg.org/#rs-default-controller-has-backpressure 75 // Looks like a readable stream thing but the spec explicitly says this is for 76 // TransformStream. 77 static bool ReadableStreamDefaultControllerHasBackpressure( 78 ReadableStreamDefaultController* aController) { 79 // Step 1: If ! ReadableStreamDefaultControllerShouldCallPull(controller) is 80 // true, return false. 81 // Step 2: Otherwise, return true. 82 return !ReadableStreamDefaultControllerShouldCallPull(aController); 83 } 84 85 void TransformStreamDefaultController::Enqueue(JSContext* aCx, 86 JS::Handle<JS::Value> aChunk, 87 ErrorResult& aRv) { 88 // Step 1: Perform ? TransformStreamDefaultControllerEnqueue(this, chunk). 89 90 // Inlining TransformStreamDefaultControllerEnqueue here. 91 // https://streams.spec.whatwg.org/#transform-stream-default-controller-enqueue 92 93 // Step 1: Let stream be controller.[[stream]]. 94 RefPtr<TransformStream> stream = mStream; 95 96 // Step 2: Let readableController be stream.[[readable]].[[controller]]. 97 RefPtr<ReadableStreamDefaultController> readableController = 98 stream->Readable()->Controller()->AsDefault(); 99 100 // Step 3: If ! 101 // ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController) is 102 // false, throw a TypeError exception. 103 if (!ReadableStreamDefaultControllerCanCloseOrEnqueueAndThrow( 104 readableController, CloseOrEnqueue::Enqueue, aRv)) { 105 return; 106 } 107 108 // Step 4: Let enqueueResult be 109 // ReadableStreamDefaultControllerEnqueue(readableController, chunk). 110 ErrorResult rv; 111 ReadableStreamDefaultControllerEnqueue(aCx, readableController, aChunk, rv); 112 113 // Step 5: If enqueueResult is an abrupt completion, 114 if (rv.MaybeSetPendingException(aCx)) { 115 JS::Rooted<JS::Value> error(aCx); 116 if (!JS_GetPendingException(aCx, &error)) { 117 // Uncatchable exception; we should mark aRv and return. 118 aRv.StealExceptionFromJSContext(aCx); 119 return; 120 } 121 JS_ClearPendingException(aCx); 122 123 // Step 5.1: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, 124 // enqueueResult.[[Value]]). 125 TransformStreamErrorWritableAndUnblockWrite(aCx, stream, error, aRv); 126 127 // Step 5.2: Throw stream.[[readable]].[[storedError]]. 128 JS::Rooted<JS::Value> storedError(aCx, stream->Readable()->StoredError()); 129 aRv.MightThrowJSException(); 130 aRv.ThrowJSException(aCx, storedError); 131 return; 132 } 133 134 // Step 6: Let backpressure be ! 135 // ReadableStreamDefaultControllerHasBackpressure(readableController). 136 bool backpressure = 137 ReadableStreamDefaultControllerHasBackpressure(readableController); 138 139 // Step 7: If backpressure is not stream.[[backpressure]], 140 if (backpressure != stream->Backpressure()) { 141 // Step 7.1: Assert: backpressure is true. 142 MOZ_ASSERT(backpressure); 143 144 // Step 7.2: Perform ! TransformStreamSetBackpressure(true). 145 stream->SetBackpressure(true); 146 } 147 } 148 149 // https://streams.spec.whatwg.org/#ts-default-controller-error 150 void TransformStreamDefaultController::Error(JSContext* aCx, 151 JS::Handle<JS::Value> aError, 152 ErrorResult& aRv) { 153 // Step 1: Perform ? TransformStreamDefaultControllerError(this, e). 154 155 // Inlining TransformStreamDefaultControllerError here. 156 // https://streams.spec.whatwg.org/#transform-stream-default-controller-error 157 158 // Perform ! TransformStreamError(controller.[[stream]], e). 159 // mStream is set in initialization step and only modified in cycle 160 // collection. 161 // TODO: Move mStream initialization to a method/constructor and make it 162 // MOZ_KNOWN_LIVE again. (See bug 1769854) 163 TransformStreamError(aCx, MOZ_KnownLive(mStream), aError, aRv); 164 } 165 166 // https://streams.spec.whatwg.org/#ts-default-controller-terminate 167 168 void TransformStreamDefaultController::Terminate(JSContext* aCx, 169 ErrorResult& aRv) { 170 // Step 1: Perform ? TransformStreamDefaultControllerTerminate(this). 171 172 // Inlining TransformStreamDefaultControllerTerminate here. 173 // https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate 174 175 // Step 1: Let stream be controller.[[stream]]. 176 RefPtr<TransformStream> stream = mStream; 177 178 // Step 2: Let readableController be stream.[[readable]].[[controller]]. 179 RefPtr<ReadableStreamDefaultController> readableController = 180 stream->Readable()->Controller()->AsDefault(); 181 182 // Step 3: Perform ! ReadableStreamDefaultControllerClose(readableController). 183 ReadableStreamDefaultControllerClose(aCx, readableController, aRv); 184 185 // Step 4: Let error be a TypeError exception indicating that the stream has 186 // been terminated. 187 ErrorResult rv; 188 rv.ThrowTypeError("Terminating the stream"); 189 JS::Rooted<JS::Value> error(aCx); 190 MOZ_ALWAYS_TRUE(ToJSValue(aCx, std::move(rv), &error)); 191 192 // Step 5: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, 193 // error). 194 TransformStreamErrorWritableAndUnblockWrite(aCx, stream, error, aRv); 195 } 196 197 namespace streams_abstract { 198 199 // https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller 200 void SetUpTransformStreamDefaultController( 201 JSContext* aCx, TransformStream& aStream, 202 TransformStreamDefaultController& aController, 203 TransformerAlgorithmsBase& aTransformerAlgorithms) { 204 // Step 1. Assert: stream implements TransformStream. 205 // Step 2. Assert: stream.[[controller]] is undefined. 206 MOZ_ASSERT(!aStream.Controller()); 207 208 // Step 3. Set controller.[[stream]] to stream. 209 aController.SetStream(aStream); 210 211 // Step 4. Set stream.[[controller]] to controller. 212 aStream.SetController(aController); 213 214 // Step 5. Set controller.[[transformAlgorithm]] to transformAlgorithm. 215 // Step 6. Set controller.[[flushAlgorithm]] to flushAlgorithm. 216 aController.SetAlgorithms(&aTransformerAlgorithms); 217 } 218 219 // https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer 220 void SetUpTransformStreamDefaultControllerFromTransformer( 221 JSContext* aCx, TransformStream& aStream, 222 JS::Handle<JSObject*> aTransformer, Transformer& aTransformerDict) { 223 // Step 1. Let controller be a new TransformStreamDefaultController. 224 auto controller = 225 MakeRefPtr<TransformStreamDefaultController>(aStream.GetParentObject()); 226 227 // Step 2 - 5: 228 auto algorithms = MakeRefPtr<TransformerAlgorithms>( 229 aStream.GetParentObject(), aTransformer, aTransformerDict); 230 231 // Step 6: Perform ! SetUpTransformStreamDefaultController(stream, controller, 232 // transformAlgorithm, flushAlgorithm). 233 SetUpTransformStreamDefaultController(aCx, aStream, *controller, *algorithms); 234 } 235 236 } // namespace streams_abstract 237 238 } // namespace mozilla::dom