123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /* -*- 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"
- #include "mozilla/CheckedInt.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)) {
- mozilla::CheckedInt<int32_t> needed(*aOutLen);
- needed += 1;
- if (!needed.isValid()) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- *_retval = (char*)malloc(needed.value());
- 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))
- {
- mozilla::CheckedInt<nsACString::size_type> needed(outLength);
- needed += 1;
- needed *= sizeof(char16_t);
- if (!needed.isValid()) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- char16_t* buf = (char16_t*)malloc(needed.value());
- 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)'?');
- }
|