|
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 "nsICharsetConverterManager.h" |
|
10 |
|
11 #include "nsConverterOutputStream.h" |
|
12 #include "nsServiceManagerUtils.h" |
|
13 |
|
14 NS_IMPL_ISUPPORTS(nsConverterOutputStream, |
|
15 nsIUnicharOutputStream, |
|
16 nsIConverterOutputStream) |
|
17 |
|
18 nsConverterOutputStream::~nsConverterOutputStream() |
|
19 { |
|
20 Close(); |
|
21 } |
|
22 |
|
23 NS_IMETHODIMP |
|
24 nsConverterOutputStream::Init(nsIOutputStream* aOutStream, |
|
25 const char* aCharset, |
|
26 uint32_t aBufferSize /* ignored */, |
|
27 char16_t aReplacementChar) |
|
28 { |
|
29 NS_PRECONDITION(aOutStream, "Null output stream!"); |
|
30 |
|
31 if (!aCharset) |
|
32 aCharset = "UTF-8"; |
|
33 |
|
34 nsresult rv; |
|
35 nsCOMPtr<nsICharsetConverterManager> ccm = |
|
36 do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv); |
|
37 if (NS_FAILED(rv)) return rv; |
|
38 |
|
39 rv = ccm->GetUnicodeEncoder(aCharset, getter_AddRefs(mConverter)); |
|
40 if (NS_FAILED(rv)) |
|
41 return rv; |
|
42 |
|
43 mOutStream = aOutStream; |
|
44 |
|
45 int32_t behaviour = aReplacementChar ? nsIUnicodeEncoder::kOnError_Replace |
|
46 : nsIUnicodeEncoder::kOnError_Signal; |
|
47 return mConverter-> |
|
48 SetOutputErrorBehavior(behaviour, |
|
49 nullptr, |
|
50 aReplacementChar); |
|
51 } |
|
52 |
|
53 NS_IMETHODIMP |
|
54 nsConverterOutputStream::Write(uint32_t aCount, const char16_t* aChars, |
|
55 bool* aSuccess) |
|
56 { |
|
57 if (!mOutStream) { |
|
58 NS_ASSERTION(!mConverter, "Closed streams shouldn't have converters"); |
|
59 return NS_BASE_STREAM_CLOSED; |
|
60 } |
|
61 NS_ASSERTION(mConverter, "Must have a converter when not closed"); |
|
62 |
|
63 int32_t inLen = aCount; |
|
64 |
|
65 int32_t maxLen; |
|
66 nsresult rv = mConverter->GetMaxLength(aChars, inLen, &maxLen); |
|
67 NS_ENSURE_SUCCESS(rv, rv); |
|
68 |
|
69 nsAutoCString buf; |
|
70 buf.SetLength(maxLen); |
|
71 if (buf.Length() != (uint32_t) maxLen) |
|
72 return NS_ERROR_OUT_OF_MEMORY; |
|
73 |
|
74 int32_t outLen = maxLen; |
|
75 rv = mConverter->Convert(aChars, &inLen, buf.BeginWriting(), &outLen); |
|
76 if (NS_FAILED(rv)) |
|
77 return rv; |
|
78 if (rv == NS_ERROR_UENC_NOMAPPING) { |
|
79 // Yes, NS_ERROR_UENC_NOMAPPING is a success code |
|
80 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; |
|
81 } |
|
82 NS_ASSERTION((uint32_t) inLen == aCount, |
|
83 "Converter didn't consume all the data!"); |
|
84 |
|
85 uint32_t written; |
|
86 rv = mOutStream->Write(buf.get(), outLen, &written); |
|
87 *aSuccess = NS_SUCCEEDED(rv) && written == uint32_t(outLen); |
|
88 return rv; |
|
89 |
|
90 } |
|
91 |
|
92 NS_IMETHODIMP |
|
93 nsConverterOutputStream::WriteString(const nsAString& aString, bool* aSuccess) |
|
94 { |
|
95 int32_t inLen = aString.Length(); |
|
96 nsAString::const_iterator i; |
|
97 aString.BeginReading(i); |
|
98 return Write(inLen, i.get(), aSuccess); |
|
99 } |
|
100 |
|
101 NS_IMETHODIMP |
|
102 nsConverterOutputStream::Flush() |
|
103 { |
|
104 if (!mOutStream) |
|
105 return NS_OK; // Already closed. |
|
106 |
|
107 char buf[1024]; |
|
108 int32_t size = sizeof(buf); |
|
109 nsresult rv = mConverter->Finish(buf, &size); |
|
110 NS_ASSERTION(rv != NS_OK_UENC_MOREOUTPUT, |
|
111 "1024 bytes ought to be enough for everyone"); |
|
112 if (NS_FAILED(rv)) |
|
113 return rv; |
|
114 if (size == 0) |
|
115 return NS_OK; |
|
116 |
|
117 uint32_t written; |
|
118 rv = mOutStream->Write(buf, size, &written); |
|
119 if (NS_FAILED(rv)) { |
|
120 NS_WARNING("Flush() lost data!"); |
|
121 return rv; |
|
122 } |
|
123 if (written != uint32_t(size)) { |
|
124 NS_WARNING("Flush() lost data!"); |
|
125 return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA; |
|
126 } |
|
127 return rv; |
|
128 } |
|
129 |
|
130 NS_IMETHODIMP |
|
131 nsConverterOutputStream::Close() |
|
132 { |
|
133 if (!mOutStream) |
|
134 return NS_OK; // Already closed. |
|
135 |
|
136 nsresult rv1 = Flush(); |
|
137 |
|
138 nsresult rv2 = mOutStream->Close(); |
|
139 mOutStream = nullptr; |
|
140 mConverter = nullptr; |
|
141 return NS_FAILED(rv1) ? rv1 : rv2; |
|
142 } |
|
143 |