Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "nsString.h"
7 #include "nsICharsetConverterManager.h"
8 #include "nsIScriptableUConv.h"
9 #include "nsScriptableUConv.h"
10 #include "nsIStringStream.h"
11 #include "nsComponentManagerUtils.h"
12 #include "nsCharsetAlias.h"
13 #include "nsServiceManagerUtils.h"
15 /* Implementation file */
16 NS_IMPL_ISUPPORTS(nsScriptableUnicodeConverter, nsIScriptableUnicodeConverter)
18 nsScriptableUnicodeConverter::nsScriptableUnicodeConverter()
19 : mIsInternal(false)
20 {
21 }
23 nsScriptableUnicodeConverter::~nsScriptableUnicodeConverter()
24 {
25 }
27 nsresult
28 nsScriptableUnicodeConverter::ConvertFromUnicodeWithLength(const nsAString& aSrc,
29 int32_t* aOutLen,
30 char **_retval)
31 {
32 if (!mEncoder)
33 return NS_ERROR_FAILURE;
35 nsresult rv = NS_OK;
36 int32_t inLength = aSrc.Length();
37 const nsAFlatString& flatSrc = PromiseFlatString(aSrc);
38 rv = mEncoder->GetMaxLength(flatSrc.get(), inLength, aOutLen);
39 if (NS_SUCCEEDED(rv)) {
40 *_retval = (char*)moz_malloc(*aOutLen+1);
41 if (!*_retval)
42 return NS_ERROR_OUT_OF_MEMORY;
44 rv = mEncoder->Convert(flatSrc.get(), &inLength, *_retval, aOutLen);
45 if (NS_SUCCEEDED(rv))
46 {
47 (*_retval)[*aOutLen] = '\0';
48 return NS_OK;
49 }
50 moz_free(*_retval);
51 }
52 *_retval = nullptr;
53 return NS_ERROR_FAILURE;
54 }
56 /* ACString ConvertFromUnicode (in AString src); */
57 NS_IMETHODIMP
58 nsScriptableUnicodeConverter::ConvertFromUnicode(const nsAString& aSrc,
59 nsACString& _retval)
60 {
61 int32_t len;
62 char* str;
63 nsresult rv = ConvertFromUnicodeWithLength(aSrc, &len, &str);
64 if (NS_SUCCEEDED(rv)) {
65 // No Adopt on nsACString :(
66 if (!_retval.Assign(str, len, mozilla::fallible_t())) {
67 rv = NS_ERROR_OUT_OF_MEMORY;
68 }
69 moz_free(str);
70 }
71 return rv;
72 }
74 nsresult
75 nsScriptableUnicodeConverter::FinishWithLength(char **_retval, int32_t* aLength)
76 {
77 if (!mEncoder)
78 return NS_ERROR_FAILURE;
80 int32_t finLength = 32;
82 *_retval = (char *)moz_malloc(finLength);
83 if (!*_retval)
84 return NS_ERROR_OUT_OF_MEMORY;
86 nsresult rv = mEncoder->Finish(*_retval, &finLength);
87 if (NS_SUCCEEDED(rv))
88 *aLength = finLength;
89 else
90 moz_free(*_retval);
92 return rv;
94 }
96 /* ACString Finish(); */
97 NS_IMETHODIMP
98 nsScriptableUnicodeConverter::Finish(nsACString& _retval)
99 {
100 int32_t len;
101 char* str;
102 nsresult rv = FinishWithLength(&str, &len);
103 if (NS_SUCCEEDED(rv)) {
104 // No Adopt on nsACString :(
105 if (!_retval.Assign(str, len, mozilla::fallible_t())) {
106 rv = NS_ERROR_OUT_OF_MEMORY;
107 }
108 moz_free(str);
109 }
110 return rv;
111 }
113 /* AString ConvertToUnicode (in ACString src); */
114 NS_IMETHODIMP
115 nsScriptableUnicodeConverter::ConvertToUnicode(const nsACString& aSrc, nsAString& _retval)
116 {
117 nsACString::const_iterator i;
118 aSrc.BeginReading(i);
119 return ConvertFromByteArray(reinterpret_cast<const uint8_t*>(i.get()),
120 aSrc.Length(),
121 _retval);
122 }
124 /* AString convertFromByteArray([const,array,size_is(aCount)] in octet aData,
125 in unsigned long aCount);
126 */
127 NS_IMETHODIMP
128 nsScriptableUnicodeConverter::ConvertFromByteArray(const uint8_t* aData,
129 uint32_t aCount,
130 nsAString& _retval)
131 {
132 if (!mDecoder)
133 return NS_ERROR_FAILURE;
135 nsresult rv = NS_OK;
136 int32_t inLength = aCount;
137 int32_t outLength;
138 rv = mDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
139 inLength, &outLength);
140 if (NS_SUCCEEDED(rv))
141 {
142 char16_t* buf = (char16_t*)moz_malloc((outLength+1)*sizeof(char16_t));
143 if (!buf)
144 return NS_ERROR_OUT_OF_MEMORY;
146 rv = mDecoder->Convert(reinterpret_cast<const char*>(aData),
147 &inLength, buf, &outLength);
148 if (NS_SUCCEEDED(rv))
149 {
150 buf[outLength] = 0;
151 if (!_retval.Assign(buf, outLength, mozilla::fallible_t())) {
152 rv = NS_ERROR_OUT_OF_MEMORY;
153 }
154 }
155 moz_free(buf);
156 return rv;
157 }
158 return NS_ERROR_FAILURE;
160 }
162 /* void convertToByteArray(in AString aString,
163 [optional] out unsigned long aLen,
164 [array, size_is(aLen),retval] out octet aData);
165 */
166 NS_IMETHODIMP
167 nsScriptableUnicodeConverter::ConvertToByteArray(const nsAString& aString,
168 uint32_t* aLen,
169 uint8_t** _aData)
170 {
171 char* data;
172 int32_t len;
173 nsresult rv = ConvertFromUnicodeWithLength(aString, &len, &data);
174 if (NS_FAILED(rv))
175 return rv;
176 nsXPIDLCString str;
177 str.Adopt(data, len); // NOTE: This uses the XPIDLCString as a byte array
179 rv = FinishWithLength(&data, &len);
180 if (NS_FAILED(rv))
181 return rv;
183 str.Append(data, len);
184 moz_free(data);
185 // NOTE: this being a byte array, it needs no null termination
186 *_aData = reinterpret_cast<uint8_t*>(moz_malloc(str.Length()));
187 if (!*_aData)
188 return NS_ERROR_OUT_OF_MEMORY;
189 memcpy(*_aData, str.get(), str.Length());
190 *aLen = str.Length();
191 return NS_OK;
192 }
194 /* nsIInputStream convertToInputStream(in AString aString); */
195 NS_IMETHODIMP
196 nsScriptableUnicodeConverter::ConvertToInputStream(const nsAString& aString,
197 nsIInputStream** _retval)
198 {
199 nsresult rv;
200 nsCOMPtr<nsIStringInputStream> inputStream =
201 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
202 if (NS_FAILED(rv))
203 return rv;
205 uint8_t* data;
206 uint32_t dataLen;
207 rv = ConvertToByteArray(aString, &dataLen, &data);
208 if (NS_FAILED(rv))
209 return rv;
211 rv = inputStream->AdoptData(reinterpret_cast<char*>(data), dataLen);
212 if (NS_FAILED(rv)) {
213 moz_free(data);
214 return rv;
215 }
217 NS_ADDREF(*_retval = inputStream);
218 return rv;
219 }
221 /* attribute string charset; */
222 NS_IMETHODIMP
223 nsScriptableUnicodeConverter::GetCharset(char * *aCharset)
224 {
225 *aCharset = ToNewCString(mCharset);
226 if (!*aCharset)
227 return NS_ERROR_OUT_OF_MEMORY;
229 return NS_OK;
230 }
232 NS_IMETHODIMP
233 nsScriptableUnicodeConverter::SetCharset(const char * aCharset)
234 {
235 mCharset.Assign(aCharset);
236 return InitConverter();
237 }
239 NS_IMETHODIMP
240 nsScriptableUnicodeConverter::GetIsInternal(bool *aIsInternal)
241 {
242 *aIsInternal = mIsInternal;
243 return NS_OK;
244 }
246 NS_IMETHODIMP
247 nsScriptableUnicodeConverter::SetIsInternal(const bool aIsInternal)
248 {
249 mIsInternal = aIsInternal;
250 return NS_OK;
251 }
253 nsresult
254 nsScriptableUnicodeConverter::InitConverter()
255 {
256 nsresult rv = NS_OK;
257 mEncoder = nullptr;
259 nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
260 if (NS_FAILED(rv) || !ccm) {
261 return rv;
262 }
264 // get an unicode converter
265 rv = ccm->GetUnicodeEncoder(mCharset.get(), getter_AddRefs(mEncoder));
266 if (NS_FAILED(rv)) {
267 return rv;
268 }
270 rv = mEncoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nullptr, (char16_t)'?');
271 if (NS_FAILED(rv)) {
272 return rv;
273 }
275 nsAutoCString charset;
276 rv = mIsInternal ? nsCharsetAlias::GetPreferredInternal(mCharset, charset)
277 : nsCharsetAlias::GetPreferred(mCharset, charset);
278 if (NS_FAILED(rv)) {
279 return rv;
280 }
282 rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(mDecoder));
283 if (NS_FAILED(rv)) {
284 return rv;
285 }
287 // The UTF-8 decoder used to throw regardless of the error behavior.
288 // Simulating the old behavior for compatibility with legacy callers
289 // (including addons). If callers want a control over the behavior,
290 // they should switch to TextDecoder.
291 if (charset.EqualsLiteral("UTF-8")) {
292 mDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Signal);
293 }
295 return rv ;
296 }