Bug 1787269 [wpt PR 35624] - Further refine STP download regexs, a=testonly
[gecko.git] / dom / encoding / TextDecoderStream.cpp
blob7a4e14feafdb4c67a60d7d1797a332928153c6bd
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 #include "mozilla/dom/TextDecoderStream.h"
9 #include "nsContentUtils.h"
10 #include "nsIGlobalObject.h"
11 #include "mozilla/Encoding.h"
12 #include "mozilla/dom/Promise.h"
13 #include "mozilla/dom/TextDecoderStreamBinding.h"
14 #include "mozilla/dom/TransformerCallbackHelpers.h"
15 #include "mozilla/dom/TransformStream.h"
16 #include "mozilla/dom/UnionTypes.h"
18 namespace mozilla::dom {
20 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TextDecoderStream, mGlobal, mStream)
21 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextDecoderStream)
22 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextDecoderStream)
23 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextDecoderStream)
24 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
25 NS_INTERFACE_MAP_ENTRY(nsISupports)
26 NS_INTERFACE_MAP_END
28 TextDecoderStream::TextDecoderStream(nsISupports* aGlobal,
29 const Encoding& aEncoding, bool aFatal,
30 bool aIgnoreBOM, TransformStream& aStream)
31 : mGlobal(aGlobal), mStream(&aStream) {
32 mFatal = aFatal;
33 mIgnoreBOM = aIgnoreBOM;
34 aEncoding.Name(mEncoding);
35 if (aIgnoreBOM) {
36 mDecoder = aEncoding.NewDecoderWithoutBOMHandling();
37 } else {
38 mDecoder = aEncoding.NewDecoderWithBOMRemoval();
42 TextDecoderStream::~TextDecoderStream() = default;
44 JSObject* TextDecoderStream::WrapObject(JSContext* aCx,
45 JS::Handle<JSObject*> aGivenProto) {
46 return TextDecoderStream_Binding::Wrap(aCx, this, aGivenProto);
49 // TODO: This function should probably be generated by bindings layer. (Bug
50 // 1784266)
51 // TODO: This does not allow shared array buffers, just as the non-stream
52 // TextDecoder/Encoder don't. (Bug 1561594)
53 static Span<const uint8_t> ExtractSpanFromBufferSource(
54 JSContext* aCx, JS::Handle<JS::Value> aBufferSource, ErrorResult& aRv) {
55 if (!aBufferSource.isObject()) {
56 aRv.ThrowTypeError("Input is not an ArrayBuffer nor an ArrayBufferView");
57 return Span<const uint8_t>();
60 bool tryNext;
61 RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(aCx);
62 if (bufferSource.TrySetToArrayBufferView(aCx, aBufferSource, tryNext,
63 false) &&
64 !tryNext) {
65 ArrayBufferView& view = bufferSource.GetAsArrayBufferView();
66 view.ComputeState();
67 return Span(view.Data(), view.Length());
69 if (!tryNext) {
70 aRv.MightThrowJSException();
71 aRv.StealExceptionFromJSContext(aCx);
72 return Span<const uint8_t>();
75 if (bufferSource.TrySetToArrayBuffer(aCx, aBufferSource, tryNext, false) &&
76 !tryNext) {
77 ArrayBuffer& buffer = bufferSource.GetAsArrayBuffer();
78 buffer.ComputeState();
79 return Span(buffer.Data(), buffer.Length());
81 if (!tryNext) {
82 aRv.MightThrowJSException();
83 aRv.StealExceptionFromJSContext(aCx);
84 return Span<const uint8_t>();
87 aRv.ThrowTypeError("Input is not an ArrayBuffer nor an ArrayBufferView");
88 return Span<const uint8_t>();
91 class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
92 NS_DECL_ISUPPORTS_INHERITED
93 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextDecoderStreamAlgorithms,
94 TransformerAlgorithmsBase)
96 void SetDecoderStream(TextDecoderStream& aStream) {
97 mDecoderStream = &aStream;
100 // The common part of decode-and-enqueue and flush-and-enqueue.
101 // Note that the most of the decoding algorithm is implemented in
102 // mozilla::Decoder, and this is mainly about calling it properly.
103 // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
104 MOZ_CAN_RUN_SCRIPT void DecodeSpanAndEnqueue(
105 JSContext* aCx, Span<const uint8_t> aInput, bool aFlush,
106 TransformStreamDefaultController& aController, ErrorResult& aRv) {
107 CheckedInt<nsAString::size_type> needed =
108 mDecoderStream->Decoder()->MaxUTF16BufferLength(aInput.Length());
109 if (!needed.isValid()) {
110 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
111 return;
114 nsString outDecodedString;
115 auto output = outDecodedString.GetMutableData(needed.value(), fallible);
116 if (!output) {
117 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
118 return;
121 mDecoderStream->DecodeNative(aInput, !aFlush, outDecodedString, aRv);
122 if (aRv.Failed()) {
123 return;
126 if (outDecodedString.Length()) {
127 // Step 4.2. If outputChunk is non-empty, then enqueue outputChunk in
128 // decoder’s transform.
129 JS::Rooted<JS::Value> outputChunk(aCx);
130 if (!xpc::NonVoidStringToJsval(aCx, outDecodedString, &outputChunk)) {
131 JS_ClearPendingException(aCx);
132 aRv.Throw(NS_ERROR_UNEXPECTED);
133 return;
135 aController.Enqueue(aCx, outputChunk, aRv);
139 // https://encoding.spec.whatwg.org/#dom-textdecoderstream
140 MOZ_CAN_RUN_SCRIPT void TransformCallbackImpl(
141 JS::Handle<JS::Value> aChunk,
142 TransformStreamDefaultController& aController,
143 ErrorResult& aRv) override {
144 // Step 7. Let transformAlgorithm be an algorithm which takes a chunk
145 // argument and runs the decode and enqueue a chunk algorithm with this and
146 // chunk.
148 // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
150 AutoJSAPI jsapi;
151 if (!jsapi.Init(aController.GetParentObject())) {
152 aRv.ThrowUnknownError("Internal error");
153 return;
155 JSContext* cx = jsapi.cx();
157 // Step 1. Let bufferSource be the result of converting chunk to an
158 // [AllowShared] BufferSource.
159 // (But here we get a mozilla::Span instead)
160 Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
161 if (aRv.Failed()) {
162 return;
165 DecodeSpanAndEnqueue(cx, input, false, aController, aRv);
168 // https://encoding.spec.whatwg.org/#dom-textdecoderstream
169 MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
170 TransformStreamDefaultController& aController,
171 ErrorResult& aRv) override {
172 // Step 8. Let flushAlgorithm be an algorithm which takes no arguments and
173 // runs the flush and enqueue algorithm with this.
175 AutoJSAPI jsapi;
176 if (!jsapi.Init(aController.GetParentObject())) {
177 aRv.ThrowUnknownError("Internal error");
178 return;
180 JSContext* cx = jsapi.cx();
182 // https://encoding.spec.whatwg.org/#flush-and-enqueue
183 // (The flush and enqueue algorithm is basically a subset of decode and
184 // enqueue one, so let's reuse it)
185 DecodeSpanAndEnqueue(cx, Span<const uint8_t>(), true, aController, aRv);
188 private:
189 ~TextDecoderStreamAlgorithms() override = default;
191 RefPtr<TextDecoderStream> mDecoderStream;
194 NS_IMPL_CYCLE_COLLECTION_INHERITED(TextDecoderStreamAlgorithms,
195 TransformerAlgorithmsBase, mDecoderStream)
196 NS_IMPL_ADDREF_INHERITED(TextDecoderStreamAlgorithms, TransformerAlgorithmsBase)
197 NS_IMPL_RELEASE_INHERITED(TextDecoderStreamAlgorithms,
198 TransformerAlgorithmsBase)
199 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextDecoderStreamAlgorithms)
200 NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase)
202 // https://encoding.spec.whatwg.org/#dom-textdecoderstream
203 already_AddRefed<TextDecoderStream> TextDecoderStream::Constructor(
204 const GlobalObject& aGlobal, const nsAString& aLabel,
205 const TextDecoderOptions& aOptions, ErrorResult& aRv) {
206 // Step 1. Let encoding be the result of getting an encoding from label.
207 const Encoding* encoding = Encoding::ForLabelNoReplacement(aLabel);
209 // Step 2. If encoding is failure or replacement, then throw a RangeError
210 if (!encoding) {
211 NS_ConvertUTF16toUTF8 label(aLabel);
212 label.Trim(" \t\n\f\r");
213 aRv.ThrowRangeError<MSG_ENCODING_NOT_SUPPORTED>(label);
214 return nullptr;
217 // Step 3-6. (Done in the constructor)
219 // Step 7-8.
220 auto algorithms = MakeRefPtr<TextDecoderStreamAlgorithms>();
222 // Step 9-10.
223 RefPtr<TransformStream> transformStream =
224 TransformStream::CreateGeneric(aGlobal, *algorithms, aRv);
225 if (aRv.Failed()) {
226 return nullptr;
229 // Step 11. (Done in the constructor)
230 auto decoderStream = MakeRefPtr<TextDecoderStream>(
231 aGlobal.GetAsSupports(), *encoding, aOptions.mFatal, aOptions.mIgnoreBOM,
232 *transformStream);
233 algorithms->SetDecoderStream(*decoderStream);
234 return decoderStream.forget();
237 ReadableStream* TextDecoderStream::Readable() const {
238 return mStream->Readable();
241 WritableStream* TextDecoderStream::Writable() const {
242 return mStream->Writable();
245 } // namespace mozilla::dom