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