michael@0: /* vim:set expandtab ts=4 sw=4 sts=4 cin: */ 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 "nsCOMPtr.h" michael@0: michael@0: #include "nsIOutputStream.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: michael@0: #include "nsConverterOutputStream.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsConverterOutputStream, michael@0: nsIUnicharOutputStream, michael@0: nsIConverterOutputStream) michael@0: michael@0: nsConverterOutputStream::~nsConverterOutputStream() michael@0: { michael@0: Close(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsConverterOutputStream::Init(nsIOutputStream* aOutStream, michael@0: const char* aCharset, michael@0: uint32_t aBufferSize /* ignored */, michael@0: char16_t aReplacementChar) michael@0: { michael@0: NS_PRECONDITION(aOutStream, "Null output stream!"); michael@0: michael@0: if (!aCharset) michael@0: aCharset = "UTF-8"; michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr ccm = michael@0: do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = ccm->GetUnicodeEncoder(aCharset, getter_AddRefs(mConverter)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mOutStream = aOutStream; michael@0: michael@0: int32_t behaviour = aReplacementChar ? nsIUnicodeEncoder::kOnError_Replace michael@0: : nsIUnicodeEncoder::kOnError_Signal; michael@0: return mConverter-> michael@0: SetOutputErrorBehavior(behaviour, michael@0: nullptr, michael@0: aReplacementChar); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsConverterOutputStream::Write(uint32_t aCount, const char16_t* aChars, michael@0: bool* aSuccess) michael@0: { michael@0: if (!mOutStream) { michael@0: NS_ASSERTION(!mConverter, "Closed streams shouldn't have converters"); michael@0: return NS_BASE_STREAM_CLOSED; michael@0: } michael@0: NS_ASSERTION(mConverter, "Must have a converter when not closed"); michael@0: michael@0: int32_t inLen = aCount; michael@0: michael@0: int32_t maxLen; michael@0: nsresult rv = mConverter->GetMaxLength(aChars, inLen, &maxLen); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString buf; michael@0: buf.SetLength(maxLen); michael@0: if (buf.Length() != (uint32_t) maxLen) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: int32_t outLen = maxLen; michael@0: rv = mConverter->Convert(aChars, &inLen, buf.BeginWriting(), &outLen); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (rv == NS_ERROR_UENC_NOMAPPING) { michael@0: // Yes, NS_ERROR_UENC_NOMAPPING is a success code michael@0: return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; michael@0: } michael@0: NS_ASSERTION((uint32_t) inLen == aCount, michael@0: "Converter didn't consume all the data!"); michael@0: michael@0: uint32_t written; michael@0: rv = mOutStream->Write(buf.get(), outLen, &written); michael@0: *aSuccess = NS_SUCCEEDED(rv) && written == uint32_t(outLen); michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsConverterOutputStream::WriteString(const nsAString& aString, bool* aSuccess) michael@0: { michael@0: int32_t inLen = aString.Length(); michael@0: nsAString::const_iterator i; michael@0: aString.BeginReading(i); michael@0: return Write(inLen, i.get(), aSuccess); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsConverterOutputStream::Flush() michael@0: { michael@0: if (!mOutStream) michael@0: return NS_OK; // Already closed. michael@0: michael@0: char buf[1024]; michael@0: int32_t size = sizeof(buf); michael@0: nsresult rv = mConverter->Finish(buf, &size); michael@0: NS_ASSERTION(rv != NS_OK_UENC_MOREOUTPUT, michael@0: "1024 bytes ought to be enough for everyone"); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (size == 0) michael@0: return NS_OK; michael@0: michael@0: uint32_t written; michael@0: rv = mOutStream->Write(buf, size, &written); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Flush() lost data!"); michael@0: return rv; michael@0: } michael@0: if (written != uint32_t(size)) { michael@0: NS_WARNING("Flush() lost data!"); michael@0: return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsConverterOutputStream::Close() michael@0: { michael@0: if (!mOutStream) michael@0: return NS_OK; // Already closed. michael@0: michael@0: nsresult rv1 = Flush(); michael@0: michael@0: nsresult rv2 = mOutStream->Close(); michael@0: mOutStream = nullptr; michael@0: mConverter = nullptr; michael@0: return NS_FAILED(rv1) ? rv1 : rv2; michael@0: } michael@0: