|
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 <locale.h> |
|
7 |
|
8 #include "mozilla/ArrayUtils.h" |
|
9 |
|
10 #include "nsIPlatformCharset.h" |
|
11 #include "nsUConvPropertySearch.h" |
|
12 #include "nsCOMPtr.h" |
|
13 #include "nsReadableUtils.h" |
|
14 #include "nsIComponentManager.h" |
|
15 #include "nsIServiceManager.h" |
|
16 #include "nsIUnicodeDecoder.h" |
|
17 #include "nsIUnicodeEncoder.h" |
|
18 #include "nsICharsetConverterManager.h" |
|
19 #include "nsEncoderDecoderUtils.h" |
|
20 #if HAVE_GNU_LIBC_VERSION_H |
|
21 #include <gnu/libc-version.h> |
|
22 #endif |
|
23 #ifdef HAVE_NL_TYPES_H |
|
24 #include <nl_types.h> |
|
25 #endif |
|
26 #if HAVE_LANGINFO_CODESET |
|
27 #include <langinfo.h> |
|
28 #endif |
|
29 #include "nsPlatformCharset.h" |
|
30 #include "prinit.h" |
|
31 #include "nsUnicharUtils.h" |
|
32 |
|
33 using namespace mozilla; |
|
34 |
|
35 static const char* kUnixCharsets[][3] = { |
|
36 #include "unixcharset.properties.h" |
|
37 }; |
|
38 |
|
39 NS_IMPL_ISUPPORTS(nsPlatformCharset, nsIPlatformCharset) |
|
40 |
|
41 nsPlatformCharset::nsPlatformCharset() |
|
42 { |
|
43 } |
|
44 |
|
45 static nsresult |
|
46 ConvertLocaleToCharsetUsingDeprecatedConfig(const nsACString& locale, |
|
47 nsACString& oResult) |
|
48 { |
|
49 if (!(locale.IsEmpty())) { |
|
50 nsAutoCString localeKey; |
|
51 localeKey.AssignLiteral("locale.all."); |
|
52 localeKey.Append(locale); |
|
53 if (NS_SUCCEEDED(nsUConvPropertySearch::SearchPropertyValue(kUnixCharsets, |
|
54 ArrayLength(kUnixCharsets), localeKey, oResult))) { |
|
55 return NS_OK; |
|
56 } |
|
57 } |
|
58 NS_ERROR("unable to convert locale to charset using deprecated config"); |
|
59 oResult.AssignLiteral("ISO-8859-1"); |
|
60 return NS_SUCCESS_USING_FALLBACK_LOCALE; |
|
61 } |
|
62 |
|
63 nsPlatformCharset::~nsPlatformCharset() |
|
64 { |
|
65 } |
|
66 |
|
67 NS_IMETHODIMP |
|
68 nsPlatformCharset::GetCharset(nsPlatformCharsetSel selector, nsACString& oResult) |
|
69 { |
|
70 oResult = mCharset; |
|
71 return NS_OK; |
|
72 } |
|
73 |
|
74 NS_IMETHODIMP |
|
75 nsPlatformCharset::GetDefaultCharsetForLocale(const nsAString& localeName, nsACString &oResult) |
|
76 { |
|
77 // |
|
78 // if this locale is the user's locale then use the charset |
|
79 // we already determined at initialization |
|
80 // |
|
81 if (mLocale.Equals(localeName) || |
|
82 // support the 4.x behavior |
|
83 (mLocale.LowerCaseEqualsLiteral("en_us") && |
|
84 localeName.LowerCaseEqualsLiteral("c"))) { |
|
85 oResult = mCharset; |
|
86 return NS_OK; |
|
87 } |
|
88 |
|
89 #if HAVE_LANGINFO_CODESET |
|
90 // |
|
91 // This locale appears to be a different locale from the user's locale. |
|
92 // To do this we would need to lock the global resource we are currently |
|
93 // using or use a library that provides multi locale support. |
|
94 // ICU is a possible example of a multi locale library. |
|
95 // http://oss.software.ibm.com/icu/ |
|
96 // |
|
97 // A more common cause of hitting this warning than the above is that |
|
98 // Mozilla is launched under an ll_CC.UTF-8 locale. In xpLocale, |
|
99 // we only store the language and the region (ll-CC) losing 'UTF-8', which |
|
100 // leads |mLocale| to be different from |localeName|. Although we lose |
|
101 // 'UTF-8', we init'd |mCharset| with the value obtained via |
|
102 // |nl_langinfo(CODESET)| so that we're all right here. |
|
103 // |
|
104 NS_WARNING("GetDefaultCharsetForLocale: need to add multi locale support"); |
|
105 #ifdef DEBUG_jungshik |
|
106 printf("localeName=%s mCharset=%s\n", NS_ConvertUTF16toUTF8(localeName).get(), |
|
107 mCharset.get()); |
|
108 #endif |
|
109 // until we add multi locale support: use the the charset of the user's locale |
|
110 oResult = mCharset; |
|
111 return NS_SUCCESS_USING_FALLBACK_LOCALE; |
|
112 #else |
|
113 // |
|
114 // convert from locale to charset |
|
115 // using the deprecated locale to charset mapping |
|
116 // |
|
117 NS_LossyConvertUTF16toASCII localeStr(localeName); |
|
118 return ConvertLocaleToCharsetUsingDeprecatedConfig(localeStr, oResult); |
|
119 #endif |
|
120 } |
|
121 |
|
122 nsresult |
|
123 nsPlatformCharset::InitGetCharset(nsACString &oString) |
|
124 { |
|
125 char* nl_langinfo_codeset = nullptr; |
|
126 nsCString aCharset; |
|
127 nsresult res; |
|
128 |
|
129 #if HAVE_LANGINFO_CODESET |
|
130 nl_langinfo_codeset = nl_langinfo(CODESET); |
|
131 NS_ASSERTION(nl_langinfo_codeset, "cannot get nl_langinfo(CODESET)"); |
|
132 |
|
133 // |
|
134 // see if we can use nl_langinfo(CODESET) directly |
|
135 // |
|
136 if (nl_langinfo_codeset) { |
|
137 aCharset.Assign(nl_langinfo_codeset); |
|
138 res = VerifyCharset(aCharset); |
|
139 if (NS_SUCCEEDED(res)) { |
|
140 oString = aCharset; |
|
141 return res; |
|
142 } |
|
143 } |
|
144 |
|
145 NS_ERROR("unable to use nl_langinfo(CODESET)"); |
|
146 #endif |
|
147 |
|
148 // |
|
149 // try falling back on a deprecated (locale based) name |
|
150 // |
|
151 char* locale = setlocale(LC_CTYPE, nullptr); |
|
152 nsAutoCString localeStr; |
|
153 localeStr.Assign(locale); |
|
154 return ConvertLocaleToCharsetUsingDeprecatedConfig(localeStr, oString); |
|
155 } |
|
156 |
|
157 NS_IMETHODIMP |
|
158 nsPlatformCharset::Init() |
|
159 { |
|
160 // |
|
161 // remember default locale so we can use the |
|
162 // same charset when asked for the same locale |
|
163 // |
|
164 char* locale = setlocale(LC_CTYPE, nullptr); |
|
165 NS_ASSERTION(locale, "cannot setlocale"); |
|
166 if (locale) { |
|
167 CopyASCIItoUTF16(locale, mLocale); |
|
168 } else { |
|
169 mLocale.AssignLiteral("en_US"); |
|
170 } |
|
171 |
|
172 // InitGetCharset only returns NS_OK or NS_SUCESS_USING_FALLBACK_LOCALE |
|
173 return InitGetCharset(mCharset); |
|
174 } |
|
175 |
|
176 nsresult |
|
177 nsPlatformCharset::VerifyCharset(nsCString &aCharset) |
|
178 { |
|
179 // fast path for UTF-8. Most platform uses UTF-8 as charset now. |
|
180 if (aCharset.EqualsLiteral("UTF-8")) { |
|
181 return NS_OK; |
|
182 } |
|
183 |
|
184 nsresult res; |
|
185 // |
|
186 // get the convert manager |
|
187 // |
|
188 nsCOMPtr <nsICharsetConverterManager> charsetConverterManager; |
|
189 charsetConverterManager = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res); |
|
190 if (NS_FAILED(res)) |
|
191 return res; |
|
192 |
|
193 // |
|
194 // check if we can get an input converter |
|
195 // |
|
196 nsCOMPtr <nsIUnicodeEncoder> enc; |
|
197 res = charsetConverterManager->GetUnicodeEncoder(aCharset.get(), getter_AddRefs(enc)); |
|
198 if (NS_FAILED(res)) { |
|
199 NS_ERROR("failed to create encoder"); |
|
200 return res; |
|
201 } |
|
202 |
|
203 // |
|
204 // check if we can get an output converter |
|
205 // |
|
206 nsCOMPtr <nsIUnicodeDecoder> dec; |
|
207 res = charsetConverterManager->GetUnicodeDecoder(aCharset.get(), getter_AddRefs(dec)); |
|
208 if (NS_FAILED(res)) { |
|
209 NS_ERROR("failed to create decoder"); |
|
210 return res; |
|
211 } |
|
212 |
|
213 // |
|
214 // check if we recognize the charset string |
|
215 // |
|
216 |
|
217 nsAutoCString result; |
|
218 res = charsetConverterManager->GetCharsetAlias(aCharset.get(), result); |
|
219 if (NS_FAILED(res)) { |
|
220 return res; |
|
221 } |
|
222 |
|
223 // |
|
224 // return the preferred string |
|
225 // |
|
226 |
|
227 aCharset.Assign(result); |
|
228 return NS_OK; |
|
229 } |