TextDecoderStream.cpp (7393B)
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/TextDecoderStream.h" 8 9 #include "mozilla/Encoding.h" 10 #include "mozilla/dom/BufferSourceBinding.h" 11 #include "mozilla/dom/Promise.h" 12 #include "mozilla/dom/TextDecoderStreamBinding.h" 13 #include "mozilla/dom/TransformStream.h" 14 #include "mozilla/dom/TransformerCallbackHelpers.h" 15 #include "mozilla/dom/UnionTypes.h" 16 #include "nsContentUtils.h" 17 #include "nsIGlobalObject.h" 18 19 namespace mozilla::dom { 20 21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TextDecoderStream, mGlobal, mStream) 22 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextDecoderStream) 23 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextDecoderStream) 24 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextDecoderStream) 25 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 26 NS_INTERFACE_MAP_ENTRY(nsISupports) 27 NS_INTERFACE_MAP_END 28 29 TextDecoderStream::TextDecoderStream(nsISupports* aGlobal, 30 const Encoding& aEncoding, bool aFatal, 31 bool aIgnoreBOM, TransformStream& aStream) 32 : mGlobal(aGlobal), mStream(&aStream) { 33 mFatal = aFatal; 34 mIgnoreBOM = aIgnoreBOM; 35 aEncoding.Name(mEncoding); 36 if (aIgnoreBOM) { 37 mDecoder = aEncoding.NewDecoderWithoutBOMHandling(); 38 } else { 39 mDecoder = aEncoding.NewDecoderWithBOMRemoval(); 40 } 41 } 42 43 TextDecoderStream::~TextDecoderStream() = default; 44 45 JSObject* TextDecoderStream::WrapObject(JSContext* aCx, 46 JS::Handle<JSObject*> aGivenProto) { 47 return TextDecoderStream_Binding::Wrap(aCx, this, aGivenProto); 48 } 49 50 class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper { 51 NS_DECL_ISUPPORTS_INHERITED 52 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextDecoderStreamAlgorithms, 53 TransformerAlgorithmsBase) 54 55 void SetDecoderStream(TextDecoderStream& aStream) { 56 mDecoderStream = &aStream; 57 } 58 59 // The common part of decode-and-enqueue and flush-and-enqueue. 60 // Note that the most of the decoding algorithm is implemented in 61 // mozilla::Decoder, and this is mainly about calling it properly. 62 // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk 63 // TODO: This does not allow shared array buffers, just as the non-stream 64 // TextDecoder/Encoder don't. (Bug 1561594) 65 MOZ_CAN_RUN_SCRIPT void DecodeBufferSourceAndEnqueue( 66 JSContext* aCx, OwningBufferSource* aInput, bool aFlush, 67 TransformStreamDefaultController& aController, ErrorResult& aRv) { 68 nsString outDecodedString; 69 if (aInput) { 70 ProcessTypedArrays(*aInput, [&](const Span<const uint8_t>& aData, 71 JS::AutoCheckCannotGC&&) { 72 mDecoderStream->DecodeNative(aData, !aFlush, outDecodedString, aRv); 73 }); 74 } else { 75 mDecoderStream->DecodeNative(Span<const uint8_t>(), !aFlush, 76 outDecodedString, aRv); 77 } 78 79 if (aRv.Failed()) { 80 return; 81 } 82 83 if (outDecodedString.Length()) { 84 // Step 4.2. If outputChunk is non-empty, then enqueue outputChunk in 85 // decoder’s transform. 86 JS::Rooted<JS::Value> outputChunk(aCx); 87 if (!ToJSValue(aCx, outDecodedString, &outputChunk)) { 88 JS_ClearPendingException(aCx); 89 aRv.Throw(NS_ERROR_UNEXPECTED); 90 return; 91 } 92 aController.Enqueue(aCx, outputChunk, aRv); 93 } 94 } 95 96 // https://encoding.spec.whatwg.org/#dom-textdecoderstream 97 MOZ_CAN_RUN_SCRIPT void TransformCallbackImpl( 98 JS::Handle<JS::Value> aChunk, 99 TransformStreamDefaultController& aController, 100 ErrorResult& aRv) override { 101 // Step 7. Let transformAlgorithm be an algorithm which takes a chunk 102 // argument and runs the decode and enqueue a chunk algorithm with this and 103 // chunk. 104 105 // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk 106 107 AutoJSAPI jsapi; 108 if (!jsapi.Init(aController.GetParentObject())) { 109 aRv.ThrowUnknownError("Internal error"); 110 return; 111 } 112 JSContext* cx = jsapi.cx(); 113 114 // Step 1. Let bufferSource be the result of converting chunk to an 115 // [AllowShared] BufferSource. 116 RootedUnion<OwningBufferSource> bufferSource(cx); 117 if (!bufferSource.Init(cx, aChunk)) { 118 aRv.MightThrowJSException(); 119 aRv.StealExceptionFromJSContext(cx); 120 return; 121 } 122 123 DecodeBufferSourceAndEnqueue(cx, &bufferSource, false, aController, aRv); 124 } 125 126 // https://encoding.spec.whatwg.org/#dom-textdecoderstream 127 MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl( 128 TransformStreamDefaultController& aController, 129 ErrorResult& aRv) override { 130 // Step 8. Let flushAlgorithm be an algorithm which takes no arguments and 131 // runs the flush and enqueue algorithm with this. 132 133 AutoJSAPI jsapi; 134 if (!jsapi.Init(aController.GetParentObject())) { 135 aRv.ThrowUnknownError("Internal error"); 136 return; 137 } 138 JSContext* cx = jsapi.cx(); 139 140 // https://encoding.spec.whatwg.org/#flush-and-enqueue 141 // (The flush and enqueue algorithm is basically a subset of decode and 142 // enqueue one, so let's reuse it) 143 DecodeBufferSourceAndEnqueue(cx, nullptr, true, aController, aRv); 144 } 145 146 private: 147 ~TextDecoderStreamAlgorithms() override = default; 148 149 RefPtr<TextDecoderStream> mDecoderStream; 150 }; 151 152 NS_IMPL_CYCLE_COLLECTION_INHERITED(TextDecoderStreamAlgorithms, 153 TransformerAlgorithmsBase, mDecoderStream) 154 NS_IMPL_ADDREF_INHERITED(TextDecoderStreamAlgorithms, TransformerAlgorithmsBase) 155 NS_IMPL_RELEASE_INHERITED(TextDecoderStreamAlgorithms, 156 TransformerAlgorithmsBase) 157 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextDecoderStreamAlgorithms) 158 NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase) 159 160 // https://encoding.spec.whatwg.org/#dom-textdecoderstream 161 already_AddRefed<TextDecoderStream> TextDecoderStream::Constructor( 162 const GlobalObject& aGlobal, const nsAString& aLabel, 163 const TextDecoderOptions& aOptions, ErrorResult& aRv) { 164 // Step 1. Let encoding be the result of getting an encoding from label. 165 const Encoding* encoding = Encoding::ForLabelNoReplacement(aLabel); 166 167 // Step 2. If encoding is failure or replacement, then throw a RangeError 168 if (!encoding) { 169 NS_ConvertUTF16toUTF8 label(aLabel); 170 label.Trim(" \t\n\f\r"); 171 aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(label); 172 return nullptr; 173 } 174 175 // Step 3-6. (Done in the constructor) 176 177 // Step 7-8. 178 auto algorithms = MakeRefPtr<TextDecoderStreamAlgorithms>(); 179 180 // Step 9-10. 181 RefPtr<TransformStream> transformStream = 182 TransformStream::CreateGeneric(aGlobal, *algorithms, aRv); 183 if (aRv.Failed()) { 184 return nullptr; 185 } 186 187 // Step 11. (Done in the constructor) 188 auto decoderStream = MakeRefPtr<TextDecoderStream>( 189 aGlobal.GetAsSupports(), *encoding, aOptions.mFatal, aOptions.mIgnoreBOM, 190 *transformStream); 191 algorithms->SetDecoderStream(*decoderStream); 192 return decoderStream.forget(); 193 } 194 195 ReadableStream* TextDecoderStream::Readable() const { 196 return mStream->Readable(); 197 } 198 199 WritableStream* TextDecoderStream::Writable() const { 200 return mStream->Writable(); 201 } 202 203 } // namespace mozilla::dom