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