1 /* vim:set expandtab ts=4 sw=4 sts=4 cin: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsCOMPtr.h"
7
8 #include "nsIOutputStream.h"
9 #include "nsString.h"
10
11 #include "nsConverterOutputStream.h"
12 #include "nsIUnicodeEncoder.h"
13 #include "mozilla/dom/EncodingUtils.h"
14
15 using mozilla::dom::EncodingUtils;
16
NS_IMPL_ISUPPORTS(nsConverterOutputStream,nsIUnicharOutputStream,nsIConverterOutputStream)17 NS_IMPL_ISUPPORTS(nsConverterOutputStream,
18 nsIUnicharOutputStream,
19 nsIConverterOutputStream)
20
21 nsConverterOutputStream::~nsConverterOutputStream()
22 {
23 Close();
24 }
25
26 NS_IMETHODIMP
Init(nsIOutputStream * aOutStream,const char * aCharset,uint32_t aBufferSize,char16_t aReplacementChar)27 nsConverterOutputStream::Init(nsIOutputStream* aOutStream,
28 const char* aCharset,
29 uint32_t aBufferSize /* ignored */,
30 char16_t aReplacementChar)
31 {
32 NS_PRECONDITION(aOutStream, "Null output stream!");
33
34 nsAutoCString label;
35 if (!aCharset) {
36 label.AssignLiteral("UTF-8");
37 } else {
38 label = aCharset;
39 }
40
41 nsAutoCString encoding;
42 if (label.EqualsLiteral("UTF-16")) {
43 // Make sure to output a BOM when UTF-16 requested
44 encoding.Assign(label);
45 } else if (!EncodingUtils::FindEncodingForLabelNoReplacement(label,
46 encoding)) {
47 return NS_ERROR_UCONV_NOCONV;
48 }
49 mConverter = EncodingUtils::EncoderForEncoding(encoding);
50
51 mOutStream = aOutStream;
52
53 int32_t behaviour = aReplacementChar ? nsIUnicodeEncoder::kOnError_Replace
54 : nsIUnicodeEncoder::kOnError_Signal;
55 return mConverter->
56 SetOutputErrorBehavior(behaviour,
57 nullptr,
58 aReplacementChar);
59 }
60
61 NS_IMETHODIMP
Write(uint32_t aCount,const char16_t * aChars,bool * aSuccess)62 nsConverterOutputStream::Write(uint32_t aCount, const char16_t* aChars,
63 bool* aSuccess)
64 {
65 if (!mOutStream) {
66 NS_ASSERTION(!mConverter, "Closed streams shouldn't have converters");
67 return NS_BASE_STREAM_CLOSED;
68 }
69 NS_ASSERTION(mConverter, "Must have a converter when not closed");
70
71 int32_t inLen = aCount;
72
73 int32_t maxLen;
74 nsresult rv = mConverter->GetMaxLength(aChars, inLen, &maxLen);
75 NS_ENSURE_SUCCESS(rv, rv);
76
77 nsAutoCString buf;
78 buf.SetLength(maxLen);
79 if (buf.Length() != (uint32_t) maxLen)
80 return NS_ERROR_OUT_OF_MEMORY;
81
82 int32_t outLen = maxLen;
83 rv = mConverter->Convert(aChars, &inLen, buf.BeginWriting(), &outLen);
84 if (NS_FAILED(rv))
85 return rv;
86 if (rv == NS_ERROR_UENC_NOMAPPING) {
87 // Yes, NS_ERROR_UENC_NOMAPPING is a success code
88 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
89 }
90 NS_ASSERTION((uint32_t) inLen == aCount,
91 "Converter didn't consume all the data!");
92
93 uint32_t written;
94 rv = mOutStream->Write(buf.get(), outLen, &written);
95 *aSuccess = NS_SUCCEEDED(rv) && written == uint32_t(outLen);
96 return rv;
97
98 }
99
100 NS_IMETHODIMP
WriteString(const nsAString & aString,bool * aSuccess)101 nsConverterOutputStream::WriteString(const nsAString& aString, bool* aSuccess)
102 {
103 int32_t inLen = aString.Length();
104 nsAString::const_iterator i;
105 aString.BeginReading(i);
106 return Write(inLen, i.get(), aSuccess);
107 }
108
109 NS_IMETHODIMP
Flush()110 nsConverterOutputStream::Flush()
111 {
112 if (!mOutStream)
113 return NS_OK; // Already closed.
114
115 char buf[1024];
116 int32_t size = sizeof(buf);
117 nsresult rv = mConverter->Finish(buf, &size);
118 NS_ASSERTION(rv != NS_OK_UENC_MOREOUTPUT,
119 "1024 bytes ought to be enough for everyone");
120 if (NS_FAILED(rv))
121 return rv;
122 if (size == 0)
123 return NS_OK;
124
125 uint32_t written;
126 rv = mOutStream->Write(buf, size, &written);
127 if (NS_FAILED(rv)) {
128 NS_WARNING("Flush() lost data!");
129 return rv;
130 }
131 if (written != uint32_t(size)) {
132 NS_WARNING("Flush() lost data!");
133 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
134 }
135 return rv;
136 }
137
138 NS_IMETHODIMP
Close()139 nsConverterOutputStream::Close()
140 {
141 if (!mOutStream)
142 return NS_OK; // Already closed.
143
144 nsresult rv1 = Flush();
145
146 nsresult rv2 = mOutStream->Close();
147 mOutStream = nullptr;
148 mConverter = nullptr;
149 return NS_FAILED(rv1) ? rv1 : rv2;
150 }
151
152