michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsString.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: #include "nsIScriptableUConv.h" michael@0: #include "nsScriptableUConv.h" michael@0: #include "nsIStringStream.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsCharsetAlias.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: /* Implementation file */ michael@0: NS_IMPL_ISUPPORTS(nsScriptableUnicodeConverter, nsIScriptableUnicodeConverter) michael@0: michael@0: nsScriptableUnicodeConverter::nsScriptableUnicodeConverter() michael@0: : mIsInternal(false) michael@0: { michael@0: } michael@0: michael@0: nsScriptableUnicodeConverter::~nsScriptableUnicodeConverter() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptableUnicodeConverter::ConvertFromUnicodeWithLength(const nsAString& aSrc, michael@0: int32_t* aOutLen, michael@0: char **_retval) michael@0: { michael@0: if (!mEncoder) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = NS_OK; michael@0: int32_t inLength = aSrc.Length(); michael@0: const nsAFlatString& flatSrc = PromiseFlatString(aSrc); michael@0: rv = mEncoder->GetMaxLength(flatSrc.get(), inLength, aOutLen); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *_retval = (char*)moz_malloc(*aOutLen+1); michael@0: if (!*_retval) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = mEncoder->Convert(flatSrc.get(), &inLength, *_retval, aOutLen); michael@0: if (NS_SUCCEEDED(rv)) michael@0: { michael@0: (*_retval)[*aOutLen] = '\0'; michael@0: return NS_OK; michael@0: } michael@0: moz_free(*_retval); michael@0: } michael@0: *_retval = nullptr; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: /* ACString ConvertFromUnicode (in AString src); */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::ConvertFromUnicode(const nsAString& aSrc, michael@0: nsACString& _retval) michael@0: { michael@0: int32_t len; michael@0: char* str; michael@0: nsresult rv = ConvertFromUnicodeWithLength(aSrc, &len, &str); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // No Adopt on nsACString :( michael@0: if (!_retval.Assign(str, len, mozilla::fallible_t())) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: moz_free(str); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptableUnicodeConverter::FinishWithLength(char **_retval, int32_t* aLength) michael@0: { michael@0: if (!mEncoder) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: int32_t finLength = 32; michael@0: michael@0: *_retval = (char *)moz_malloc(finLength); michael@0: if (!*_retval) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv = mEncoder->Finish(*_retval, &finLength); michael@0: if (NS_SUCCEEDED(rv)) michael@0: *aLength = finLength; michael@0: else michael@0: moz_free(*_retval); michael@0: michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: /* ACString Finish(); */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::Finish(nsACString& _retval) michael@0: { michael@0: int32_t len; michael@0: char* str; michael@0: nsresult rv = FinishWithLength(&str, &len); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // No Adopt on nsACString :( michael@0: if (!_retval.Assign(str, len, mozilla::fallible_t())) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: moz_free(str); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* AString ConvertToUnicode (in ACString src); */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::ConvertToUnicode(const nsACString& aSrc, nsAString& _retval) michael@0: { michael@0: nsACString::const_iterator i; michael@0: aSrc.BeginReading(i); michael@0: return ConvertFromByteArray(reinterpret_cast(i.get()), michael@0: aSrc.Length(), michael@0: _retval); michael@0: } michael@0: michael@0: /* AString convertFromByteArray([const,array,size_is(aCount)] in octet aData, michael@0: in unsigned long aCount); michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::ConvertFromByteArray(const uint8_t* aData, michael@0: uint32_t aCount, michael@0: nsAString& _retval) michael@0: { michael@0: if (!mDecoder) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = NS_OK; michael@0: int32_t inLength = aCount; michael@0: int32_t outLength; michael@0: rv = mDecoder->GetMaxLength(reinterpret_cast(aData), michael@0: inLength, &outLength); michael@0: if (NS_SUCCEEDED(rv)) michael@0: { michael@0: char16_t* buf = (char16_t*)moz_malloc((outLength+1)*sizeof(char16_t)); michael@0: if (!buf) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = mDecoder->Convert(reinterpret_cast(aData), michael@0: &inLength, buf, &outLength); michael@0: if (NS_SUCCEEDED(rv)) michael@0: { michael@0: buf[outLength] = 0; michael@0: if (!_retval.Assign(buf, outLength, mozilla::fallible_t())) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: moz_free(buf); michael@0: return rv; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: } michael@0: michael@0: /* void convertToByteArray(in AString aString, michael@0: [optional] out unsigned long aLen, michael@0: [array, size_is(aLen),retval] out octet aData); michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::ConvertToByteArray(const nsAString& aString, michael@0: uint32_t* aLen, michael@0: uint8_t** _aData) michael@0: { michael@0: char* data; michael@0: int32_t len; michael@0: nsresult rv = ConvertFromUnicodeWithLength(aString, &len, &data); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: nsXPIDLCString str; michael@0: str.Adopt(data, len); // NOTE: This uses the XPIDLCString as a byte array michael@0: michael@0: rv = FinishWithLength(&data, &len); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: str.Append(data, len); michael@0: moz_free(data); michael@0: // NOTE: this being a byte array, it needs no null termination michael@0: *_aData = reinterpret_cast(moz_malloc(str.Length())); michael@0: if (!*_aData) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: memcpy(*_aData, str.get(), str.Length()); michael@0: *aLen = str.Length(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIInputStream convertToInputStream(in AString aString); */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::ConvertToInputStream(const nsAString& aString, michael@0: nsIInputStream** _retval) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr inputStream = michael@0: do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: uint8_t* data; michael@0: uint32_t dataLen; michael@0: rv = ConvertToByteArray(aString, &dataLen, &data); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = inputStream->AdoptData(reinterpret_cast(data), dataLen); michael@0: if (NS_FAILED(rv)) { michael@0: moz_free(data); michael@0: return rv; michael@0: } michael@0: michael@0: NS_ADDREF(*_retval = inputStream); michael@0: return rv; michael@0: } michael@0: michael@0: /* attribute string charset; */ michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::GetCharset(char * *aCharset) michael@0: { michael@0: *aCharset = ToNewCString(mCharset); michael@0: if (!*aCharset) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::SetCharset(const char * aCharset) michael@0: { michael@0: mCharset.Assign(aCharset); michael@0: return InitConverter(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::GetIsInternal(bool *aIsInternal) michael@0: { michael@0: *aIsInternal = mIsInternal; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptableUnicodeConverter::SetIsInternal(const bool aIsInternal) michael@0: { michael@0: mIsInternal = aIsInternal; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptableUnicodeConverter::InitConverter() michael@0: { michael@0: nsresult rv = NS_OK; michael@0: mEncoder = nullptr; michael@0: michael@0: nsCOMPtr ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv) || !ccm) { michael@0: return rv; michael@0: } michael@0: michael@0: // get an unicode converter michael@0: rv = ccm->GetUnicodeEncoder(mCharset.get(), getter_AddRefs(mEncoder)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, (char16_t)'?'); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString charset; michael@0: rv = mIsInternal ? nsCharsetAlias::GetPreferredInternal(mCharset, charset) michael@0: : nsCharsetAlias::GetPreferred(mCharset, charset); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(mDecoder)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: // The UTF-8 decoder used to throw regardless of the error behavior. michael@0: // Simulating the old behavior for compatibility with legacy callers michael@0: // (including addons). If callers want a control over the behavior, michael@0: // they should switch to TextDecoder. michael@0: if (charset.EqualsLiteral("UTF-8")) { michael@0: mDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal); michael@0: } michael@0: michael@0: return rv ; michael@0: }