diff options
Diffstat (limited to 'intl/uconv/nsScriptableUConv.cpp')
-rw-r--r-- | intl/uconv/nsScriptableUConv.cpp | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/intl/uconv/nsScriptableUConv.cpp b/intl/uconv/nsScriptableUConv.cpp new file mode 100644 index 000000000..7d4e932e2 --- /dev/null +++ b/intl/uconv/nsScriptableUConv.cpp @@ -0,0 +1,320 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsString.h" +#include "nsIScriptableUConv.h" +#include "nsScriptableUConv.h" +#include "nsIStringStream.h" +#include "nsComponentManagerUtils.h" +#include "nsIUnicodeDecoder.h" +#include "nsIUnicodeEncoder.h" +#include "mozilla/dom/EncodingUtils.h" + +using mozilla::dom::EncodingUtils; + +/* Implementation file */ +NS_IMPL_ISUPPORTS(nsScriptableUnicodeConverter, nsIScriptableUnicodeConverter) + +nsScriptableUnicodeConverter::nsScriptableUnicodeConverter() +: mIsInternal(false) +{ +} + +nsScriptableUnicodeConverter::~nsScriptableUnicodeConverter() +{ +} + +nsresult +nsScriptableUnicodeConverter::ConvertFromUnicodeWithLength(const nsAString& aSrc, + int32_t* aOutLen, + char **_retval) +{ + if (!mEncoder) + return NS_ERROR_FAILURE; + + nsresult rv = NS_OK; + int32_t inLength = aSrc.Length(); + const nsAFlatString& flatSrc = PromiseFlatString(aSrc); + rv = mEncoder->GetMaxLength(flatSrc.get(), inLength, aOutLen); + if (NS_SUCCEEDED(rv)) { + *_retval = (char*)malloc(*aOutLen+1); + if (!*_retval) + return NS_ERROR_OUT_OF_MEMORY; + + rv = mEncoder->Convert(flatSrc.get(), &inLength, *_retval, aOutLen); + if (NS_SUCCEEDED(rv)) + { + (*_retval)[*aOutLen] = '\0'; + return NS_OK; + } + free(*_retval); + } + *_retval = nullptr; + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::ConvertFromUnicode(const nsAString& aSrc, + nsACString& _retval) +{ + int32_t len; + char* str; + nsresult rv = ConvertFromUnicodeWithLength(aSrc, &len, &str); + if (NS_SUCCEEDED(rv)) { + // No Adopt on nsACString :( + if (!_retval.Assign(str, len, mozilla::fallible)) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + free(str); + } + return rv; +} + +nsresult +nsScriptableUnicodeConverter::FinishWithLength(char **_retval, int32_t* aLength) +{ + if (!mEncoder) + return NS_ERROR_FAILURE; + + int32_t finLength = 32; + + *_retval = (char *)malloc(finLength); + if (!*_retval) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = mEncoder->Finish(*_retval, &finLength); + if (NS_SUCCEEDED(rv)) + *aLength = finLength; + else + free(*_retval); + + return rv; + +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::Finish(nsACString& _retval) +{ + // The documentation for this method says it should be called after + // ConvertFromUnicode(). However, our own tests called it after + // convertFromByteArray(), i.e. when *decoding*. + // Assuming that there exists extensions that similarly call + // this at the wrong time, let's deal. In general, it is a design + // error for this class to handle conversions in both directions. + if (!mEncoder) { + _retval.Truncate(); + return NS_OK; + } + int32_t len; + char* str; + nsresult rv = FinishWithLength(&str, &len); + if (NS_SUCCEEDED(rv)) { + // No Adopt on nsACString :( + if (!_retval.Assign(str, len, mozilla::fallible)) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + free(str); + } + return rv; +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::ConvertToUnicode(const nsACString& aSrc, nsAString& _retval) +{ + nsACString::const_iterator i; + aSrc.BeginReading(i); + return ConvertFromByteArray(reinterpret_cast<const uint8_t*>(i.get()), + aSrc.Length(), + _retval); +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::ConvertFromByteArray(const uint8_t* aData, + uint32_t aCount, + nsAString& _retval) +{ + if (!mDecoder) + return NS_ERROR_FAILURE; + + nsresult rv = NS_OK; + int32_t inLength = aCount; + int32_t outLength; + rv = mDecoder->GetMaxLength(reinterpret_cast<const char*>(aData), + inLength, &outLength); + if (NS_SUCCEEDED(rv)) + { + char16_t* buf = (char16_t*)malloc((outLength+1) * sizeof(char16_t)); + if (!buf) + return NS_ERROR_OUT_OF_MEMORY; + + rv = mDecoder->Convert(reinterpret_cast<const char*>(aData), + &inLength, buf, &outLength); + if (NS_SUCCEEDED(rv)) + { + buf[outLength] = 0; + if (!_retval.Assign(buf, outLength, mozilla::fallible)) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + free(buf); + return rv; + } + return NS_ERROR_FAILURE; + +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::ConvertToByteArray(const nsAString& aString, + uint32_t* aLen, + uint8_t** _aData) +{ + char* data; + int32_t len; + nsresult rv = ConvertFromUnicodeWithLength(aString, &len, &data); + if (NS_FAILED(rv)) + return rv; + nsXPIDLCString str; + str.Adopt(data, len); // NOTE: This uses the XPIDLCString as a byte array + + rv = FinishWithLength(&data, &len); + if (NS_FAILED(rv)) + return rv; + + str.Append(data, len); + free(data); + // NOTE: this being a byte array, it needs no null termination + *_aData = reinterpret_cast<uint8_t*>(malloc(str.Length())); + if (!*_aData) + return NS_ERROR_OUT_OF_MEMORY; + memcpy(*_aData, str.get(), str.Length()); + *aLen = str.Length(); + return NS_OK; +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::ConvertToInputStream(const nsAString& aString, + nsIInputStream** _retval) +{ + nsresult rv; + nsCOMPtr<nsIStringInputStream> inputStream = + do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); + if (NS_FAILED(rv)) + return rv; + + uint8_t* data; + uint32_t dataLen; + rv = ConvertToByteArray(aString, &dataLen, &data); + if (NS_FAILED(rv)) + return rv; + + rv = inputStream->AdoptData(reinterpret_cast<char*>(data), dataLen); + if (NS_FAILED(rv)) { + free(data); + return rv; + } + + NS_ADDREF(*_retval = inputStream); + return rv; +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::GetCharset(char * *aCharset) +{ + *aCharset = ToNewCString(mCharset); + if (!*aCharset) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::SetCharset(const char * aCharset) +{ + mCharset.Assign(aCharset); + return InitConverter(); +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::GetIsInternal(bool *aIsInternal) +{ + *aIsInternal = mIsInternal; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptableUnicodeConverter::SetIsInternal(const bool aIsInternal) +{ + mIsInternal = aIsInternal; + return NS_OK; +} + +nsresult +nsScriptableUnicodeConverter::InitConverter() +{ + mEncoder = nullptr; + mDecoder = nullptr; + + nsAutoCString encoding; + if (mIsInternal) { + // For compatibility with legacy extensions, let's try to see if the label + // happens to be ASCII-case-insensitively an encoding. This should allow + // for things like "utf-7" and "x-Mac-Hebrew". + nsAutoCString contractId; + nsAutoCString label(mCharset); + EncodingUtils::TrimSpaceCharacters(label); + // Let's try in lower case if we didn't get an decoder. E.g. x-mac-ce + // and x-imap4-modified-utf7 are all lower case. + ToLowerCase(label); + if (label.EqualsLiteral("replacement")) { + // reject "replacement" + return NS_ERROR_UCONV_NOCONV; + } + contractId.AssignLiteral(NS_UNICODEENCODER_CONTRACTID_BASE); + contractId.Append(label); + mEncoder = do_CreateInstance(contractId.get()); + contractId.AssignLiteral(NS_UNICODEDECODER_CONTRACTID_BASE); + contractId.Append(label); + mDecoder = do_CreateInstance(contractId.get()); + if (!mDecoder) { + // The old code seemed to want both a decoder and an encoder. Since some + // internal encodings will be decoder-only in the future, let's relax + // this. Note that the other methods check mEncoder for null anyway. + // Let's try the upper case. E.g. UTF-7 and ISO-2022-CN have upper + // case Gecko-canonical names. + ToUpperCase(label); + contractId.AssignLiteral(NS_UNICODEENCODER_CONTRACTID_BASE); + contractId.Append(label); + mEncoder = do_CreateInstance(contractId.get()); + contractId.AssignLiteral(NS_UNICODEDECODER_CONTRACTID_BASE); + contractId.Append(label); + mDecoder = do_CreateInstance(contractId.get()); + // If still no decoder, use the normal non-internal case below. + } + } + + if (!mDecoder) { + if (!EncodingUtils::FindEncodingForLabelNoReplacement(mCharset, encoding)) { + return NS_ERROR_UCONV_NOCONV; + } + mEncoder = EncodingUtils::EncoderForEncoding(encoding); + mDecoder = EncodingUtils::DecoderForEncoding(encoding); + } + + // The UTF-8 decoder used to throw regardless of the error behavior. + // Simulating the old behavior for compatibility with legacy callers + // (including addons). If callers want a control over the behavior, + // they should switch to TextDecoder. + if (encoding.EqualsLiteral("UTF-8")) { + mDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal); + } + + if (!mEncoder) { + return NS_OK; + } + + return mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, + nullptr, + (char16_t)'?'); +} |