/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/TextDecoder.h" #include "mozilla/dom/UnionTypes.h" #include "mozilla/Encoding.h" #include "mozilla/UniquePtrExtensions.h" #include "nsContentUtils.h" #include namespace mozilla::dom { void TextDecoder::Init(const nsAString& aLabel, const TextDecoderOptions& aOptions, ErrorResult& aRv) { // Let encoding be the result of getting an encoding from label. // If encoding is failure or replacement, throw a RangeError // (https://encoding.spec.whatwg.org/#dom-textdecoder). const Encoding* encoding = Encoding::ForLabelNoReplacement(aLabel); if (!encoding) { NS_ConvertUTF16toUTF8 label(aLabel); label.Trim(" \t\n\f\r"); aRv.ThrowRangeError(label); return; } InitWithEncoding(WrapNotNull(encoding), aOptions); } void TextDecoder::InitWithEncoding(NotNull aEncoding, const TextDecoderOptions& aOptions) { aEncoding->Name(mEncoding); // Store the flags passed via our options dictionary. mFatal = aOptions.mFatal; mIgnoreBOM = aOptions.mIgnoreBOM; // Create a decoder object for mEncoding. if (mIgnoreBOM) { mDecoder = aEncoding->NewDecoderWithoutBOMHandling(); } else { mDecoder = aEncoding->NewDecoderWithBOMRemoval(); } } void TextDecoderCommon::DecodeNative(Span aInput, const bool aStream, nsAString& aOutDecodedString, ErrorResult& aRv) { aOutDecodedString.Truncate(); CheckedInt needed = mDecoder->MaxUTF16BufferLength(aInput.Length()); if (!needed.isValid()) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } auto output = aOutDecodedString.GetMutableData(needed.value(), fallible); if (!output) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } uint32_t result; size_t read; size_t written; if (mFatal) { std::tie(result, read, written) = mDecoder->DecodeToUTF16WithoutReplacement(aInput, *output, !aStream); if (result != kInputEmpty) { aRv.ThrowTypeError(); return; } } else { std::tie(result, read, written, std::ignore) = mDecoder->DecodeToUTF16(aInput, *output, !aStream); } MOZ_ASSERT(result == kInputEmpty); MOZ_ASSERT(read == aInput.Length()); MOZ_ASSERT(written <= aOutDecodedString.Length()); if (!aOutDecodedString.SetLength(written, fallible)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } // If the internal streaming flag of the decoder object is not set, // then reset the encoding algorithm state to the default values if (!aStream) { if (mIgnoreBOM) { mDecoder->Encoding()->NewDecoderWithoutBOMHandlingInto(*mDecoder); } else { mDecoder->Encoding()->NewDecoderWithBOMRemovalInto(*mDecoder); } } } void TextDecoder::Decode(const Optional& aBuffer, const TextDecodeOptions& aOptions, nsAString& aOutDecodedString, ErrorResult& aRv) { if (!aBuffer.WasPassed()) { DecodeNative(nullptr, aOptions.mStream, aOutDecodedString, aRv); return; } const ArrayBufferViewOrArrayBuffer& buf = aBuffer.Value(); uint8_t* data; uint32_t length; if (buf.IsArrayBufferView()) { buf.GetAsArrayBufferView().ComputeState(); data = buf.GetAsArrayBufferView().Data(); length = buf.GetAsArrayBufferView().Length(); } else { MOZ_ASSERT(buf.IsArrayBuffer()); buf.GetAsArrayBuffer().ComputeState(); data = buf.GetAsArrayBuffer().Data(); length = buf.GetAsArrayBuffer().Length(); } DecodeNative(Span(data, length), aOptions.mStream, aOutDecodedString, aRv); } void TextDecoderCommon::GetEncoding(nsAString& aEncoding) { CopyASCIItoUTF16(mEncoding, aEncoding); nsContentUtils::ASCIIToLower(aEncoding); } } // namespace mozilla::dom