|
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/. */ |
|
5 |
|
6 #include "nsCOMPtr.h" |
|
7 #include "nsString.h" |
|
8 #include "nsUnicharUtils.h" |
|
9 #include "nsCharsetAlias.h" |
|
10 #include "nsICategoryManager.h" |
|
11 #include "nsICharsetConverterManager.h" |
|
12 #include "nsEncoderDecoderUtils.h" |
|
13 #include "nsIStringBundle.h" |
|
14 #include "nsTArray.h" |
|
15 #include "nsStringEnumerator.h" |
|
16 #include "mozilla/Services.h" |
|
17 |
|
18 #include "nsComponentManagerUtils.h" |
|
19 #include "nsISupportsPrimitives.h" |
|
20 #include "nsServiceManagerUtils.h" |
|
21 |
|
22 // just for CONTRACTIDs |
|
23 #include "nsCharsetConverterManager.h" |
|
24 |
|
25 static nsIStringBundle * sDataBundle; |
|
26 static nsIStringBundle * sTitleBundle; |
|
27 |
|
28 // Class nsCharsetConverterManager [implementation] |
|
29 |
|
30 NS_IMPL_ISUPPORTS(nsCharsetConverterManager, nsICharsetConverterManager) |
|
31 |
|
32 nsCharsetConverterManager::nsCharsetConverterManager() |
|
33 { |
|
34 } |
|
35 |
|
36 nsCharsetConverterManager::~nsCharsetConverterManager() |
|
37 { |
|
38 } |
|
39 |
|
40 //static |
|
41 void nsCharsetConverterManager::Shutdown() |
|
42 { |
|
43 NS_IF_RELEASE(sDataBundle); |
|
44 NS_IF_RELEASE(sTitleBundle); |
|
45 } |
|
46 |
|
47 static |
|
48 nsresult LoadExtensibleBundle(const char* aCategory, |
|
49 nsIStringBundle ** aResult) |
|
50 { |
|
51 nsCOMPtr<nsIStringBundleService> sbServ = |
|
52 mozilla::services::GetStringBundleService(); |
|
53 if (!sbServ) |
|
54 return NS_ERROR_FAILURE; |
|
55 |
|
56 return sbServ->CreateExtensibleBundle(aCategory, aResult); |
|
57 } |
|
58 |
|
59 static |
|
60 nsresult GetBundleValue(nsIStringBundle * aBundle, |
|
61 const char * aName, |
|
62 const nsAFlatString& aProp, |
|
63 char16_t ** aResult) |
|
64 { |
|
65 nsAutoString key; |
|
66 |
|
67 key.AssignWithConversion(aName); |
|
68 ToLowerCase(key); // we lowercase the main comparison key |
|
69 key.Append(aProp); |
|
70 |
|
71 return aBundle->GetStringFromName(key.get(), aResult); |
|
72 } |
|
73 |
|
74 static |
|
75 nsresult GetBundleValue(nsIStringBundle * aBundle, |
|
76 const char * aName, |
|
77 const nsAFlatString& aProp, |
|
78 nsAString& aResult) |
|
79 { |
|
80 nsresult rv = NS_OK; |
|
81 |
|
82 nsXPIDLString value; |
|
83 rv = GetBundleValue(aBundle, aName, aProp, getter_Copies(value)); |
|
84 if (NS_FAILED(rv)) |
|
85 return rv; |
|
86 |
|
87 aResult = value; |
|
88 |
|
89 return NS_OK; |
|
90 } |
|
91 |
|
92 static |
|
93 nsresult GetCharsetDataImpl(const char * aCharset, const char16_t * aProp, |
|
94 nsAString& aResult) |
|
95 { |
|
96 NS_ENSURE_ARG_POINTER(aCharset); |
|
97 // aProp can be nullptr |
|
98 |
|
99 if (!sDataBundle) { |
|
100 nsresult rv = LoadExtensibleBundle(NS_DATA_BUNDLE_CATEGORY, &sDataBundle); |
|
101 if (NS_FAILED(rv)) |
|
102 return rv; |
|
103 } |
|
104 |
|
105 return GetBundleValue(sDataBundle, aCharset, nsDependentString(aProp), aResult); |
|
106 } |
|
107 |
|
108 //static |
|
109 bool nsCharsetConverterManager::IsInternal(const nsACString& aCharset) |
|
110 { |
|
111 nsAutoString str; |
|
112 // fully qualify to possibly avoid vtable call |
|
113 nsresult rv = GetCharsetDataImpl(PromiseFlatCString(aCharset).get(), |
|
114 MOZ_UTF16(".isInternal"), |
|
115 str); |
|
116 |
|
117 return NS_SUCCEEDED(rv); |
|
118 } |
|
119 |
|
120 |
|
121 //----------------------------------------------------------------------------//---------------------------------------------------------------------------- |
|
122 // Interface nsICharsetConverterManager [implementation] |
|
123 |
|
124 NS_IMETHODIMP |
|
125 nsCharsetConverterManager::GetUnicodeEncoder(const char * aDest, |
|
126 nsIUnicodeEncoder ** aResult) |
|
127 { |
|
128 // resolve the charset first |
|
129 nsAutoCString charset; |
|
130 |
|
131 // fully qualify to possibly avoid vtable call |
|
132 nsCharsetConverterManager::GetCharsetAlias(aDest, charset); |
|
133 |
|
134 return nsCharsetConverterManager::GetUnicodeEncoderRaw(charset.get(), |
|
135 aResult); |
|
136 } |
|
137 |
|
138 |
|
139 NS_IMETHODIMP |
|
140 nsCharsetConverterManager::GetUnicodeEncoderRaw(const char * aDest, |
|
141 nsIUnicodeEncoder ** aResult) |
|
142 { |
|
143 *aResult= nullptr; |
|
144 nsCOMPtr<nsIUnicodeEncoder> encoder; |
|
145 |
|
146 nsresult rv = NS_OK; |
|
147 |
|
148 nsAutoCString |
|
149 contractid(NS_LITERAL_CSTRING(NS_UNICODEENCODER_CONTRACTID_BASE) + |
|
150 nsDependentCString(aDest)); |
|
151 |
|
152 // Always create an instance since encoders hold state. |
|
153 encoder = do_CreateInstance(contractid.get(), &rv); |
|
154 |
|
155 if (NS_FAILED(rv)) |
|
156 rv = NS_ERROR_UCONV_NOCONV; |
|
157 else |
|
158 { |
|
159 *aResult = encoder.get(); |
|
160 NS_ADDREF(*aResult); |
|
161 } |
|
162 return rv; |
|
163 } |
|
164 |
|
165 NS_IMETHODIMP |
|
166 nsCharsetConverterManager::GetUnicodeDecoder(const char * aSrc, |
|
167 nsIUnicodeDecoder ** aResult) |
|
168 { |
|
169 // resolve the charset first |
|
170 nsAutoCString charset; |
|
171 |
|
172 // fully qualify to possibly avoid vtable call |
|
173 if (NS_FAILED(nsCharsetConverterManager::GetCharsetAlias(aSrc, charset))) |
|
174 return NS_ERROR_UCONV_NOCONV; |
|
175 |
|
176 return nsCharsetConverterManager::GetUnicodeDecoderRaw(charset.get(), |
|
177 aResult); |
|
178 } |
|
179 |
|
180 NS_IMETHODIMP |
|
181 nsCharsetConverterManager::GetUnicodeDecoderInternal(const char * aSrc, |
|
182 nsIUnicodeDecoder ** aResult) |
|
183 { |
|
184 // resolve the charset first |
|
185 nsAutoCString charset; |
|
186 |
|
187 nsresult rv = nsCharsetAlias::GetPreferredInternal(nsDependentCString(aSrc), |
|
188 charset); |
|
189 NS_ENSURE_SUCCESS(rv, rv); |
|
190 |
|
191 return nsCharsetConverterManager::GetUnicodeDecoderRaw(charset.get(), |
|
192 aResult); |
|
193 } |
|
194 |
|
195 NS_IMETHODIMP |
|
196 nsCharsetConverterManager::GetUnicodeDecoderRaw(const char * aSrc, |
|
197 nsIUnicodeDecoder ** aResult) |
|
198 { |
|
199 *aResult= nullptr; |
|
200 nsCOMPtr<nsIUnicodeDecoder> decoder; |
|
201 |
|
202 nsresult rv = NS_OK; |
|
203 |
|
204 NS_NAMED_LITERAL_CSTRING(contractbase, NS_UNICODEDECODER_CONTRACTID_BASE); |
|
205 nsDependentCString src(aSrc); |
|
206 |
|
207 decoder = do_CreateInstance(PromiseFlatCString(contractbase + src).get(), |
|
208 &rv); |
|
209 NS_ENSURE_SUCCESS(rv, NS_ERROR_UCONV_NOCONV); |
|
210 |
|
211 decoder.forget(aResult); |
|
212 return rv; |
|
213 } |
|
214 |
|
215 static |
|
216 nsresult GetList(const nsACString& aCategory, |
|
217 const nsACString& aPrefix, |
|
218 nsIUTF8StringEnumerator** aResult) |
|
219 { |
|
220 NS_ENSURE_ARG_POINTER(aResult); |
|
221 *aResult = nullptr; |
|
222 |
|
223 nsresult rv; |
|
224 |
|
225 nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); |
|
226 if (NS_FAILED(rv)) |
|
227 return rv; |
|
228 |
|
229 nsTArray<nsCString>* array = new nsTArray<nsCString>; |
|
230 if (!array) |
|
231 return NS_ERROR_OUT_OF_MEMORY; |
|
232 |
|
233 nsCOMPtr<nsISimpleEnumerator> enumerator; |
|
234 catman->EnumerateCategory(PromiseFlatCString(aCategory).get(), |
|
235 getter_AddRefs(enumerator)); |
|
236 |
|
237 bool hasMore; |
|
238 while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { |
|
239 nsCOMPtr<nsISupports> supports; |
|
240 if (NS_FAILED(enumerator->GetNext(getter_AddRefs(supports)))) |
|
241 continue; |
|
242 |
|
243 nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports); |
|
244 if (!supStr) |
|
245 continue; |
|
246 |
|
247 nsAutoCString name; |
|
248 if (NS_FAILED(supStr->GetData(name))) |
|
249 continue; |
|
250 |
|
251 nsAutoCString fullName(aPrefix); |
|
252 fullName.Append(name); |
|
253 NS_ENSURE_TRUE(array->AppendElement(fullName), NS_ERROR_OUT_OF_MEMORY); |
|
254 } |
|
255 |
|
256 return NS_NewAdoptingUTF8StringEnumerator(aResult, array); |
|
257 } |
|
258 |
|
259 // we should change the interface so that we can just pass back a enumerator! |
|
260 NS_IMETHODIMP |
|
261 nsCharsetConverterManager::GetDecoderList(nsIUTF8StringEnumerator ** aResult) |
|
262 { |
|
263 return GetList(NS_LITERAL_CSTRING(NS_UNICODEDECODER_NAME), |
|
264 EmptyCString(), aResult); |
|
265 } |
|
266 |
|
267 NS_IMETHODIMP |
|
268 nsCharsetConverterManager::GetEncoderList(nsIUTF8StringEnumerator ** aResult) |
|
269 { |
|
270 return GetList(NS_LITERAL_CSTRING(NS_UNICODEENCODER_NAME), |
|
271 EmptyCString(), aResult); |
|
272 } |
|
273 |
|
274 NS_IMETHODIMP |
|
275 nsCharsetConverterManager::GetCharsetDetectorList(nsIUTF8StringEnumerator** aResult) |
|
276 { |
|
277 return GetList(NS_LITERAL_CSTRING("charset-detectors"), |
|
278 NS_LITERAL_CSTRING("chardet."), aResult); |
|
279 } |
|
280 |
|
281 // XXX Improve the implementation of this method. Right now, it is build on |
|
282 // top of the nsCharsetAlias service. We can make the nsCharsetAlias |
|
283 // better, with its own hash table (not the StringBundle anymore) and |
|
284 // a nicer file format. |
|
285 NS_IMETHODIMP |
|
286 nsCharsetConverterManager::GetCharsetAlias(const char * aCharset, |
|
287 nsACString& aResult) |
|
288 { |
|
289 NS_ENSURE_ARG_POINTER(aCharset); |
|
290 |
|
291 // We try to obtain the preferred name for this charset from the charset |
|
292 // aliases. |
|
293 nsresult rv; |
|
294 |
|
295 rv = nsCharsetAlias::GetPreferred(nsDependentCString(aCharset), aResult); |
|
296 NS_ENSURE_SUCCESS(rv, rv); |
|
297 |
|
298 return NS_OK; |
|
299 } |
|
300 |
|
301 |
|
302 NS_IMETHODIMP |
|
303 nsCharsetConverterManager::GetCharsetTitle(const char * aCharset, |
|
304 nsAString& aResult) |
|
305 { |
|
306 NS_ENSURE_ARG_POINTER(aCharset); |
|
307 |
|
308 if (!sTitleBundle) { |
|
309 nsresult rv = LoadExtensibleBundle(NS_TITLE_BUNDLE_CATEGORY, &sTitleBundle); |
|
310 NS_ENSURE_SUCCESS(rv, rv); |
|
311 } |
|
312 |
|
313 return GetBundleValue(sTitleBundle, aCharset, NS_LITERAL_STRING(".title"), aResult); |
|
314 } |
|
315 |
|
316 NS_IMETHODIMP |
|
317 nsCharsetConverterManager::GetCharsetData(const char * aCharset, |
|
318 const char16_t * aProp, |
|
319 nsAString& aResult) |
|
320 { |
|
321 return GetCharsetDataImpl(aCharset, aProp, aResult); |
|
322 } |
|
323 |
|
324 NS_IMETHODIMP |
|
325 nsCharsetConverterManager::GetCharsetLangGroup(const char * aCharset, |
|
326 nsIAtom** aResult) |
|
327 { |
|
328 // resolve the charset first |
|
329 nsAutoCString charset; |
|
330 |
|
331 nsresult rv = GetCharsetAlias(aCharset, charset); |
|
332 NS_ENSURE_SUCCESS(rv, rv); |
|
333 |
|
334 // fully qualify to possibly avoid vtable call |
|
335 return nsCharsetConverterManager::GetCharsetLangGroupRaw(charset.get(), |
|
336 aResult); |
|
337 } |
|
338 |
|
339 NS_IMETHODIMP |
|
340 nsCharsetConverterManager::GetCharsetLangGroupRaw(const char * aCharset, |
|
341 nsIAtom** aResult) |
|
342 { |
|
343 |
|
344 *aResult = nullptr; |
|
345 nsAutoString langGroup; |
|
346 // fully qualify to possibly avoid vtable call |
|
347 nsresult rv = nsCharsetConverterManager::GetCharsetData( |
|
348 aCharset, MOZ_UTF16(".LangGroup"), langGroup); |
|
349 |
|
350 if (NS_SUCCEEDED(rv)) { |
|
351 ToLowerCase(langGroup); // use lowercase for all language atoms |
|
352 *aResult = NS_NewAtom(langGroup).take(); |
|
353 } |
|
354 |
|
355 return rv; |
|
356 } |