1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsCOMPtr.h"
40 : #include "nsString.h"
41 : #include "nsReadableUtils.h"
42 : #include "nsUnicharUtils.h"
43 : #include "nsCharsetAlias.h"
44 : #include "nsIServiceManager.h"
45 : #include "nsICategoryManager.h"
46 : #include "nsICharsetConverterManager.h"
47 : #include "nsEncoderDecoderUtils.h"
48 : #include "nsIStringBundle.h"
49 : #include "prmem.h"
50 : #include "nsCRT.h"
51 : #include "nsTArray.h"
52 : #include "nsStringEnumerator.h"
53 : #include "nsThreadUtils.h"
54 : #include "mozilla/Services.h"
55 :
56 : #include "nsXPCOM.h"
57 : #include "nsComponentManagerUtils.h"
58 : #include "nsISupportsPrimitives.h"
59 :
60 : // just for CONTRACTIDs
61 : #include "nsCharsetConverterManager.h"
62 :
63 : // Class nsCharsetConverterManager [implementation]
64 :
65 69523 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsCharsetConverterManager,
66 : nsICharsetConverterManager)
67 :
68 594 : nsCharsetConverterManager::nsCharsetConverterManager()
69 : : mDataBundle(NULL)
70 594 : , mTitleBundle(NULL)
71 : {
72 594 : }
73 :
74 1188 : nsCharsetConverterManager::~nsCharsetConverterManager()
75 : {
76 594 : NS_IF_RELEASE(mDataBundle);
77 594 : NS_IF_RELEASE(mTitleBundle);
78 2376 : }
79 :
80 572 : nsresult nsCharsetConverterManager::LoadExtensibleBundle(
81 : const char* aCategory,
82 : nsIStringBundle ** aResult)
83 : {
84 : nsCOMPtr<nsIStringBundleService> sbServ =
85 1144 : mozilla::services::GetStringBundleService();
86 572 : if (!sbServ)
87 0 : return NS_ERROR_FAILURE;
88 :
89 572 : return sbServ->CreateExtensibleBundle(aCategory, aResult);
90 : }
91 :
92 15646 : nsresult nsCharsetConverterManager::GetBundleValue(nsIStringBundle * aBundle,
93 : const char * aName,
94 : const nsAFlatString& aProp,
95 : PRUnichar ** aResult)
96 : {
97 31292 : nsAutoString key;
98 :
99 15646 : key.AssignWithConversion(aName);
100 15646 : ToLowerCase(key); // we lowercase the main comparison key
101 15646 : key.Append(aProp);
102 :
103 15646 : return aBundle->GetStringFromName(key.get(), aResult);
104 : }
105 :
106 15646 : nsresult nsCharsetConverterManager::GetBundleValue(nsIStringBundle * aBundle,
107 : const char * aName,
108 : const nsAFlatString& aProp,
109 : nsAString& aResult)
110 : {
111 15646 : nsresult rv = NS_OK;
112 :
113 31292 : nsXPIDLString value;
114 15646 : rv = GetBundleValue(aBundle, aName, aProp, getter_Copies(value));
115 15646 : if (NS_FAILED(rv))
116 15533 : return rv;
117 :
118 113 : aResult = value;
119 :
120 113 : return NS_OK;
121 : }
122 :
123 :
124 : //----------------------------------------------------------------------------//----------------------------------------------------------------------------
125 : // Interface nsICharsetConverterManager [implementation]
126 :
127 : NS_IMETHODIMP
128 11504 : nsCharsetConverterManager::GetUnicodeEncoder(const char * aDest,
129 : nsIUnicodeEncoder ** aResult)
130 : {
131 : // resolve the charset first
132 23008 : nsCAutoString charset;
133 :
134 : // fully qualify to possibly avoid vtable call
135 11504 : nsCharsetConverterManager::GetCharsetAlias(aDest, charset);
136 :
137 : return nsCharsetConverterManager::GetUnicodeEncoderRaw(charset.get(),
138 11504 : aResult);
139 : }
140 :
141 :
142 : NS_IMETHODIMP
143 11504 : nsCharsetConverterManager::GetUnicodeEncoderRaw(const char * aDest,
144 : nsIUnicodeEncoder ** aResult)
145 : {
146 11504 : *aResult= nsnull;
147 23008 : nsCOMPtr<nsIUnicodeEncoder> encoder;
148 :
149 11504 : nsresult rv = NS_OK;
150 :
151 : nsCAutoString
152 11504 : contractid(NS_LITERAL_CSTRING(NS_UNICODEENCODER_CONTRACTID_BASE) +
153 34512 : nsDependentCString(aDest));
154 :
155 : // Always create an instance since encoders hold state.
156 11504 : encoder = do_CreateInstance(contractid.get(), &rv);
157 :
158 11504 : if (NS_FAILED(rv))
159 4 : rv = NS_ERROR_UCONV_NOCONV;
160 : else
161 : {
162 11500 : *aResult = encoder.get();
163 11500 : NS_ADDREF(*aResult);
164 : }
165 11504 : return rv;
166 : }
167 :
168 : NS_IMETHODIMP
169 15561 : nsCharsetConverterManager::GetUnicodeDecoderRaw(const char * aSrc,
170 : nsIUnicodeDecoder ** aResult)
171 : {
172 : nsresult rv;
173 :
174 31122 : nsAutoString str;
175 15561 : rv = GetCharsetData(aSrc, NS_LITERAL_STRING(".isXSSVulnerable").get(), str);
176 15561 : if (NS_SUCCEEDED(rv))
177 29 : return NS_ERROR_UCONV_NOCONV;
178 :
179 15532 : return GetUnicodeDecoderRawInternal(aSrc, aResult);
180 : }
181 :
182 : NS_IMETHODIMP
183 11164 : nsCharsetConverterManager::GetUnicodeDecoder(const char * aSrc,
184 : nsIUnicodeDecoder ** aResult)
185 : {
186 : // resolve the charset first
187 22328 : nsCAutoString charset;
188 :
189 : // fully qualify to possibly avoid vtable call
190 11164 : nsCharsetConverterManager::GetCharsetAlias(aSrc, charset);
191 :
192 : return nsCharsetConverterManager::GetUnicodeDecoderRaw(charset.get(),
193 11164 : aResult);
194 : }
195 :
196 : NS_IMETHODIMP
197 18 : nsCharsetConverterManager::GetUnicodeDecoderInternal(const char * aSrc,
198 : nsIUnicodeDecoder ** aResult)
199 : {
200 : // resolve the charset first
201 36 : nsCAutoString charset;
202 :
203 : // fully qualify to possibly avoid vtable call
204 18 : nsresult rv = nsCharsetConverterManager::GetCharsetAlias(aSrc, charset);
205 18 : NS_ENSURE_SUCCESS(rv, rv);
206 :
207 : return nsCharsetConverterManager::GetUnicodeDecoderRawInternal(charset.get(),
208 18 : aResult);
209 : }
210 :
211 : NS_IMETHODIMP
212 15550 : nsCharsetConverterManager::GetUnicodeDecoderRawInternal(const char * aSrc,
213 : nsIUnicodeDecoder ** aResult)
214 : {
215 15550 : *aResult= nsnull;
216 31100 : nsCOMPtr<nsIUnicodeDecoder> decoder;
217 :
218 15550 : nsresult rv = NS_OK;
219 :
220 31100 : NS_NAMED_LITERAL_CSTRING(contractbase, NS_UNICODEDECODER_CONTRACTID_BASE);
221 31100 : nsDependentCString src(aSrc);
222 :
223 15550 : decoder = do_CreateInstance(PromiseFlatCString(contractbase + src).get(),
224 15550 : &rv);
225 15550 : NS_ENSURE_SUCCESS(rv, NS_ERROR_UCONV_NOCONV);
226 :
227 15550 : decoder.forget(aResult);
228 15550 : return rv;
229 : }
230 :
231 : nsresult
232 6 : nsCharsetConverterManager::GetList(const nsACString& aCategory,
233 : const nsACString& aPrefix,
234 : nsIUTF8StringEnumerator** aResult)
235 : {
236 6 : if (aResult == NULL)
237 0 : return NS_ERROR_NULL_POINTER;
238 6 : *aResult = NULL;
239 :
240 : nsresult rv;
241 :
242 12 : nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
243 6 : if (NS_FAILED(rv))
244 0 : return rv;
245 :
246 6 : nsTArray<nsCString>* array = new nsTArray<nsCString>;
247 6 : if (!array)
248 0 : return NS_ERROR_OUT_OF_MEMORY;
249 :
250 12 : nsCOMPtr<nsISimpleEnumerator> enumerator;
251 12 : catman->EnumerateCategory(PromiseFlatCString(aCategory).get(),
252 12 : getter_AddRefs(enumerator));
253 :
254 : bool hasMore;
255 441 : while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
256 858 : nsCOMPtr<nsISupports> supports;
257 429 : if (NS_FAILED(enumerator->GetNext(getter_AddRefs(supports))))
258 0 : continue;
259 :
260 858 : nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports);
261 429 : if (!supStr)
262 0 : continue;
263 :
264 858 : nsCAutoString name;
265 429 : if (NS_FAILED(supStr->GetData(name)))
266 0 : continue;
267 :
268 858 : nsCAutoString fullName(aPrefix);
269 429 : fullName.Append(name);
270 429 : NS_ENSURE_TRUE(array->AppendElement(fullName), NS_ERROR_OUT_OF_MEMORY);
271 : }
272 :
273 6 : return NS_NewAdoptingUTF8StringEnumerator(aResult, array);
274 : }
275 :
276 : // we should change the interface so that we can just pass back a enumerator!
277 : NS_IMETHODIMP
278 4 : nsCharsetConverterManager::GetDecoderList(nsIUTF8StringEnumerator ** aResult)
279 : {
280 4 : return GetList(NS_LITERAL_CSTRING(NS_UNICODEDECODER_NAME),
281 4 : EmptyCString(), aResult);
282 : }
283 :
284 : NS_IMETHODIMP
285 1 : nsCharsetConverterManager::GetEncoderList(nsIUTF8StringEnumerator ** aResult)
286 : {
287 1 : return GetList(NS_LITERAL_CSTRING(NS_UNICODEENCODER_NAME),
288 1 : EmptyCString(), aResult);
289 : }
290 :
291 : NS_IMETHODIMP
292 1 : nsCharsetConverterManager::GetCharsetDetectorList(nsIUTF8StringEnumerator** aResult)
293 : {
294 1 : return GetList(NS_LITERAL_CSTRING("charset-detectors"),
295 2 : NS_LITERAL_CSTRING("chardet."), aResult);
296 : }
297 :
298 : // XXX Improve the implementation of this method. Right now, it is build on
299 : // top of the nsCharsetAlias service. We can make the nsCharsetAlias
300 : // better, with its own hash table (not the StringBundle anymore) and
301 : // a nicer file format.
302 : NS_IMETHODIMP
303 23013 : nsCharsetConverterManager::GetCharsetAlias(const char * aCharset,
304 : nsACString& aResult)
305 : {
306 23013 : NS_ENSURE_ARG_POINTER(aCharset);
307 :
308 : // We try to obtain the preferred name for this charset from the charset
309 : // aliases.
310 : nsresult rv;
311 :
312 23013 : rv = nsCharsetAlias::GetPreferred(nsDependentCString(aCharset), aResult);
313 23013 : NS_ENSURE_SUCCESS(rv, rv);
314 :
315 23012 : return NS_OK;
316 : }
317 :
318 :
319 : NS_IMETHODIMP
320 2 : nsCharsetConverterManager::GetCharsetTitle(const char * aCharset,
321 : nsAString& aResult)
322 : {
323 2 : NS_ENSURE_ARG_POINTER(aCharset);
324 :
325 2 : if (mTitleBundle == NULL) {
326 1 : nsresult rv = LoadExtensibleBundle(NS_TITLE_BUNDLE_CATEGORY, &mTitleBundle);
327 1 : NS_ENSURE_SUCCESS(rv, rv);
328 : }
329 :
330 2 : return GetBundleValue(mTitleBundle, aCharset, NS_LITERAL_STRING(".title"), aResult);
331 : }
332 :
333 : NS_IMETHODIMP
334 15561 : nsCharsetConverterManager::GetCharsetData(const char * aCharset,
335 : const PRUnichar * aProp,
336 : nsAString& aResult)
337 : {
338 15561 : if (aCharset == NULL)
339 0 : return NS_ERROR_NULL_POINTER;
340 : // aProp can be NULL
341 :
342 15561 : if (mDataBundle == NULL) {
343 570 : nsresult rv = LoadExtensibleBundle(NS_DATA_BUNDLE_CATEGORY, &mDataBundle);
344 570 : if (NS_FAILED(rv))
345 0 : return rv;
346 : }
347 :
348 15561 : return GetBundleValue(mDataBundle, aCharset, nsDependentString(aProp), aResult);
349 : }
350 :
351 : NS_IMETHODIMP
352 83 : nsCharsetConverterManager::GetCharsetLangGroup(const char * aCharset,
353 : nsIAtom** aResult)
354 : {
355 : // resolve the charset first
356 166 : nsCAutoString charset;
357 :
358 83 : nsresult rv = GetCharsetAlias(aCharset, charset);
359 83 : NS_ENSURE_SUCCESS(rv, rv);
360 :
361 : // fully qualify to possibly avoid vtable call
362 : return nsCharsetConverterManager::GetCharsetLangGroupRaw(charset.get(),
363 83 : aResult);
364 : }
365 :
366 : NS_IMETHODIMP
367 83 : nsCharsetConverterManager::GetCharsetLangGroupRaw(const char * aCharset,
368 : nsIAtom** aResult)
369 : {
370 :
371 83 : *aResult = nsnull;
372 83 : if (aCharset == NULL)
373 0 : return NS_ERROR_NULL_POINTER;
374 :
375 83 : nsresult rv = NS_OK;
376 :
377 83 : if (mDataBundle == NULL) {
378 1 : rv = LoadExtensibleBundle(NS_DATA_BUNDLE_CATEGORY, &mDataBundle);
379 1 : if (NS_FAILED(rv))
380 0 : return rv;
381 : }
382 :
383 166 : nsAutoString langGroup;
384 83 : rv = GetBundleValue(mDataBundle, aCharset, NS_LITERAL_STRING(".LangGroup"), langGroup);
385 :
386 83 : if (NS_SUCCEEDED(rv)) {
387 83 : ToLowerCase(langGroup); // use lowercase for all language atoms
388 83 : *aResult = NS_NewAtom(langGroup);
389 : }
390 :
391 83 : return rv;
392 : }
|