Bug 1841050 - Add pref webgl.gl_khr_no_error:true. r=gfx-reviewers,bradwerth
[gecko.git] / dom / encoding / TextEncoderStream.cpp
blobf64a4bdb7ed688bdf6eab7d55802f5ca918e9911
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/TextEncoderStream.h"
9 #include "js/ArrayBuffer.h"
10 #include "js/experimental/TypedData.h"
11 #include "nsIGlobalObject.h"
12 #include "mozilla/Encoding.h"
13 #include "mozilla/dom/BindingUtils.h"
14 #include "mozilla/dom/TextEncoderStreamBinding.h"
15 #include "mozilla/dom/TransformerCallbackHelpers.h"
16 #include "mozilla/dom/TransformStream.h"
18 namespace mozilla::dom {
20 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TextEncoderStream, mGlobal, mStream)
21 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEncoderStream)
22 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEncoderStream)
23 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEncoderStream)
24 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
25 NS_INTERFACE_MAP_ENTRY(nsISupports)
26 NS_INTERFACE_MAP_END
28 TextEncoderStream::TextEncoderStream(nsISupports* aGlobal,
29 TransformStream& aStream)
30 : mGlobal(aGlobal), mStream(&aStream) {
31 // See the comment in EncodeNative() about why this uses a decoder instead of
32 // `UTF_8_ENCODING->NewEncoder()`.
33 // XXX: We have to consciously choose 16LE/BE because we ultimately have to read
34 // char16_t* as uint8_t*. See the same comment.
35 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
36 mDecoder = UTF_16LE_ENCODING->NewDecoder();
37 #else
38 mDecoder = UTF_16BE_ENCODING->NewDecoder();
39 #endif
42 TextEncoderStream::~TextEncoderStream() = default;
44 JSObject* TextEncoderStream::WrapObject(JSContext* aCx,
45 JS::Handle<JSObject*> aGivenProto) {
46 return TextEncoderStream_Binding::Wrap(aCx, this, aGivenProto);
49 // Note that the most of the encoding algorithm is implemented in
50 // mozilla::Decoder (see the comment in EncodeNative()), and this is mainly
51 // about calling it properly.
52 static void EncodeNative(JSContext* aCx, mozilla::Decoder* aDecoder,
53 Span<const char16_t> aInput, const bool aFlush,
54 JS::MutableHandle<JSObject*> aOutputArrayBufferView,
55 ErrorResult& aRv) {
56 // XXX: Adjust the length since Decoder always accepts uint8_t (whereas
57 // Encoder also accepts char16_t, see below).
58 if (aInput.Length() > SIZE_MAX / 2) {
59 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
60 return;
62 size_t lengthU8 = aInput.Length() * 2;
64 CheckedInt<nsAString::size_type> needed =
65 aDecoder->MaxUTF8BufferLength(lengthU8);
66 if (!needed.isValid()) {
67 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
68 return;
71 UniquePtr<uint8_t[], JS::FreePolicy> buffer(
72 static_cast<uint8_t*>(JS_malloc(aCx, needed.value())));
73 if (!buffer) {
74 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
75 return;
78 mozilla::Span<uint8_t> input((uint8_t*)aInput.data(), lengthU8);
79 mozilla::Span<uint8_t> output(buffer, needed.value());
81 // This originally wanted to use mozilla::Encoder::Encode() that accepts
82 // char16_t*, but it lacks the pending-high-surrogate feature to properly
83 // implement
84 // https://encoding.spec.whatwg.org/#convert-code-unit-to-scalar-value.
85 // See also https://github.com/hsivonen/encoding_rs/issues/82 about the
86 // reasoning.
87 // XXX: The code is more verbose here since we need to convert to
88 // uint8_t* which is the only type mozilla::Decoder accepts.
89 uint32_t result;
90 size_t read;
91 size_t written;
92 std::tie(result, read, written, std::ignore) =
93 aDecoder->DecodeToUTF8(input, output, aFlush);
94 MOZ_ASSERT(result == kInputEmpty);
95 MOZ_ASSERT(read == lengthU8);
96 MOZ_ASSERT(written <= needed.value());
98 // https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk
99 // Step 4.2.2.1. Let chunk be a Uint8Array object wrapping an ArrayBuffer
100 // containing output.
101 JS::Rooted<JSObject*> arrayBuffer(
102 aCx, JS::NewArrayBufferWithContents(aCx, written, std::move(buffer)));
103 if (!arrayBuffer.get()) {
104 JS_ClearPendingException(aCx);
105 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
106 return;
108 aOutputArrayBufferView.set(JS_NewUint8ArrayWithBuffer(
109 aCx, arrayBuffer, 0, static_cast<int64_t>(written)));
110 if (!aOutputArrayBufferView.get()) {
111 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
112 return;
116 class TextEncoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
117 NS_DECL_ISUPPORTS_INHERITED
118 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextEncoderStreamAlgorithms,
119 TransformerAlgorithmsBase)
121 void SetEncoderStream(TextEncoderStream& aStream) {
122 mEncoderStream = &aStream;
125 // The common part of encode-and-enqueue and encode-and-flush.
126 // https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
127 MOZ_CAN_RUN_SCRIPT void EncodeAndEnqueue(
128 JSContext* aCx, const nsAString& aInput,
129 TransformStreamDefaultController& aController, bool aFlush,
130 ErrorResult& aRv) {
131 JS::Rooted<JSObject*> outView(aCx);
132 // Passing a Decoder for a reason, see the comments in the method.
133 EncodeNative(aCx, mEncoderStream->Decoder(), aInput, aFlush, &outView, aRv);
135 if (JS_GetTypedArrayLength(outView) > 0) {
136 // Step 4.2.2.2. Enqueue chunk into encoder’s transform.
137 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*outView));
138 aController.Enqueue(aCx, value, aRv);
142 // https://encoding.spec.whatwg.org/#dom-textencoderstream
143 MOZ_CAN_RUN_SCRIPT void TransformCallbackImpl(
144 JS::Handle<JS::Value> aChunk,
145 TransformStreamDefaultController& aController,
146 ErrorResult& aRv) override {
147 // Step 2. Let transformAlgorithm be an algorithm which takes a chunk
148 // argument and runs the encode and enqueue a chunk algorithm with this and
149 // chunk.
151 AutoJSAPI jsapi;
152 if (!jsapi.Init(aController.GetParentObject())) {
153 aRv.ThrowUnknownError("Internal error");
154 return;
156 JSContext* cx = jsapi.cx();
158 // https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk
160 // Step 1. Let input be the result of converting chunk to a DOMString.
161 // Step 2. Convert input to an I/O queue of code units.
162 nsString str;
163 if (!ConvertJSValueToString(cx, aChunk, eStringify, eStringify, str)) {
164 aRv.MightThrowJSException();
165 aRv.StealExceptionFromJSContext(cx);
166 return;
169 EncodeAndEnqueue(cx, str, aController, false, aRv);
172 // https://encoding.spec.whatwg.org/#dom-textencoderstream
173 MOZ_CAN_RUN_SCRIPT void FlushCallbackImpl(
174 TransformStreamDefaultController& aController,
175 ErrorResult& aRv) override {
176 // Step 3. Let flushAlgorithm be an algorithm which runs the encode and
177 // flush algorithm with this.
179 AutoJSAPI jsapi;
180 if (!jsapi.Init(aController.GetParentObject())) {
181 aRv.ThrowUnknownError("Internal error");
182 return;
184 JSContext* cx = jsapi.cx();
186 // The spec manually manages pending high surrogate here, but let's call the
187 // encoder as it's managed there.
188 EncodeAndEnqueue(cx, u""_ns, aController, true, aRv);
191 private:
192 ~TextEncoderStreamAlgorithms() override = default;
194 RefPtr<TextEncoderStream> mEncoderStream;
197 NS_IMPL_CYCLE_COLLECTION_INHERITED(TextEncoderStreamAlgorithms,
198 TransformerAlgorithmsBase, mEncoderStream)
199 NS_IMPL_ADDREF_INHERITED(TextEncoderStreamAlgorithms, TransformerAlgorithmsBase)
200 NS_IMPL_RELEASE_INHERITED(TextEncoderStreamAlgorithms,
201 TransformerAlgorithmsBase)
202 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEncoderStreamAlgorithms)
203 NS_INTERFACE_MAP_END_INHERITING(TransformerAlgorithmsBase)
205 // https://encoding.spec.whatwg.org/#dom-textencoderstream
206 already_AddRefed<TextEncoderStream> TextEncoderStream::Constructor(
207 const GlobalObject& aGlobal, ErrorResult& aRv) {
208 // Step 1. Set this’s encoder to an instance of the UTF-8 encoder.
210 // Step 2-3
211 auto algorithms = MakeRefPtr<TextEncoderStreamAlgorithms>();
213 // Step 4-5
214 RefPtr<TransformStream> transformStream =
215 TransformStream::CreateGeneric(aGlobal, *algorithms, aRv);
216 if (aRv.Failed()) {
217 return nullptr;
220 // Step 6. Set this’s transform to transformStream.
221 // (Done in the constructor)
222 auto encoderStream =
223 MakeRefPtr<TextEncoderStream>(aGlobal.GetAsSupports(), *transformStream);
224 algorithms->SetEncoderStream(*encoderStream);
225 return encoderStream.forget();
228 ReadableStream* TextEncoderStream::Readable() const {
229 return mStream->Readable();
232 WritableStream* TextEncoderStream::Writable() const {
233 return mStream->Writable();
236 void TextEncoderStream::GetEncoding(nsCString& aRetVal) const {
237 aRetVal.AssignLiteral("utf-8");
240 } // namespace mozilla::dom