intl/unicharutil/util/ICUUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #ifdef MOZILLA_INTERNAL_API
     6 #ifdef ENABLE_INTL_API
     8 #include "ICUUtils.h"
     9 #include "mozilla/Preferences.h"
    10 #include "nsIContent.h"
    11 #include "nsIDocument.h"
    12 #include "nsIToolkitChromeRegistry.h"
    13 #include "nsStringGlue.h"
    14 #include "unicode/uloc.h"
    15 #include "unicode/unum.h"
    17 using namespace mozilla;
    19 /**
    20  * This pref just controls whether we format the number with grouping separator
    21  * characters when the internal value is set or updated. It does not stop the
    22  * user from typing in a number and using grouping separators.
    23  */
    24 static bool gLocaleNumberGroupingEnabled;
    25 static const char LOCALE_NUMBER_GROUPING_PREF_STR[] = "dom.forms.number.grouping";
    27 static bool
    28 LocaleNumberGroupingIsEnabled()
    29 {
    30   static bool sInitialized = false;
    32   if (!sInitialized) {
    33     /* check and register ourselves with the pref */
    34     Preferences::AddBoolVarCache(&gLocaleNumberGroupingEnabled,
    35                                  LOCALE_NUMBER_GROUPING_PREF_STR,
    36                                  false);
    37     sInitialized = true;
    38   }
    40   return gLocaleNumberGroupingEnabled;
    41 }
    43 void
    44 ICUUtils::LanguageTagIterForContent::GetNext(nsACString& aBCP47LangTag)
    45 {
    46   if (mCurrentFallbackIndex < 0) {
    47     mCurrentFallbackIndex = 0;
    48     // Try the language specified by a 'lang'/'xml:lang' attribute on mContent
    49     // or any ancestor, if such an attribute is specified:
    50     nsAutoString lang;
    51     mContent->GetLang(lang);
    52     if (!lang.IsEmpty()) {
    53       aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
    54       return;
    55     }
    56   }
    58   if (mCurrentFallbackIndex < 1) {
    59     mCurrentFallbackIndex = 1;
    60     // Else try the language specified by any Content-Language HTTP header or
    61     // pragma directive:
    62     nsIDocument* doc = mContent->OwnerDoc();
    63     nsAutoString lang;
    64     doc->GetContentLanguage(lang);
    65     if (!lang.IsEmpty()) {
    66       aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
    67       return;
    68     }
    69   }
    71   if (mCurrentFallbackIndex < 2) {
    72     mCurrentFallbackIndex = 2;
    73     // Else try the user-agent's locale:
    74     nsCOMPtr<nsIToolkitChromeRegistry> cr =
    75       mozilla::services::GetToolkitChromeRegistryService();
    76     nsAutoCString uaLangTag;
    77     if (cr) {
    78       cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), uaLangTag);
    79     }
    80     if (!uaLangTag.IsEmpty()) {
    81       aBCP47LangTag = uaLangTag;
    82       return;
    83     }
    84   }
    86   // TODO: Probably not worth it, but maybe have a fourth fallback to using
    87   // the OS locale?
    89   aBCP47LangTag.Truncate(); // Signal iterator exhausted
    90 }
    92 /* static */ bool
    93 ICUUtils::LocalizeNumber(double aValue,
    94                          LanguageTagIterForContent& aLangTags,
    95                          nsAString& aLocalizedValue)
    96 {
    97   MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
    99   static const int32_t kBufferSize = 256;
   101   UChar buffer[kBufferSize];
   103   nsAutoCString langTag;
   104   aLangTags.GetNext(langTag);
   105   while (!langTag.IsEmpty()) {
   106     UErrorCode status = U_ZERO_ERROR;
   107     AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
   108                                             langTag.get(), nullptr, &status));
   109     unum_setAttribute(format, UNUM_GROUPING_USED,
   110                       LocaleNumberGroupingIsEnabled());
   111     // ICU default is a maximum of 3 significant fractional digits. We don't
   112     // want that limit, so we set it to the maximum that a double can represent
   113     // (14-16 decimal fractional digits).
   114     unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, 16);
   115     int32_t length = unum_formatDouble(format, aValue, buffer, kBufferSize,
   116                                        nullptr, &status);
   117     NS_ASSERTION(length < kBufferSize &&
   118                  status != U_BUFFER_OVERFLOW_ERROR &&
   119                  status != U_STRING_NOT_TERMINATED_WARNING,
   120                  "Need a bigger buffer?!");
   121     if (U_SUCCESS(status)) {
   122       ICUUtils::AssignUCharArrayToString(buffer, length, aLocalizedValue);
   123       return true;
   124     }
   125     aLangTags.GetNext(langTag);
   126   }
   127   return false;
   128 }
   130 /* static */ double
   131 ICUUtils::ParseNumber(nsAString& aValue,
   132                       LanguageTagIterForContent& aLangTags)
   133 {
   134   MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
   136   if (aValue.IsEmpty()) {
   137     return std::numeric_limits<float>::quiet_NaN();
   138   }
   140   uint32_t length = aValue.Length();
   142   nsAutoCString langTag;
   143   aLangTags.GetNext(langTag);
   144   while (!langTag.IsEmpty()) {
   145     UErrorCode status = U_ZERO_ERROR;
   146     AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
   147                                             langTag.get(), nullptr, &status));
   148     int32_t parsePos = 0;
   149     static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
   150                   "Unexpected character size - the following cast is unsafe");
   151     double val = unum_parseDouble(format,
   152                                   (const UChar*)PromiseFlatString(aValue).get(),
   153                                   length, &parsePos, &status);
   154     if (U_SUCCESS(status) && parsePos == (int32_t)length) {
   155       return val;
   156     }
   157     aLangTags.GetNext(langTag);
   158   }
   159   return std::numeric_limits<float>::quiet_NaN();
   160 }
   162 /* static */ void
   163 ICUUtils::AssignUCharArrayToString(UChar* aICUString,
   164                                    int32_t aLength,
   165                                    nsAString& aMozString)
   166 {
   167   // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
   168   // cast here.
   170   static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
   171                 "Unexpected character size - the following cast is unsafe");
   173   aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
   175   NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
   176 }
   178 #if 0
   179 /* static */ Locale
   180 ICUUtils::BCP47CodeToLocale(const nsAString& aBCP47Code)
   181 {
   182   MOZ_ASSERT(!aBCP47Code.IsEmpty(), "Don't pass an empty BCP 47 code");
   184   Locale locale;
   185   locale.setToBogus();
   187   // BCP47 codes are guaranteed to be ASCII, so lossy conversion is okay
   188   NS_LossyConvertUTF16toASCII bcp47code(aBCP47Code);
   190   UErrorCode status = U_ZERO_ERROR;
   191   int32_t needed;
   193   char localeID[256];
   194   needed = uloc_forLanguageTag(bcp47code.get(), localeID,
   195                                PR_ARRAY_SIZE(localeID) - 1, nullptr,
   196                                &status);
   197   MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(localeID)) - 1,
   198              "Need a bigger buffer");
   199   if (needed <= 0 || U_FAILURE(status)) {
   200     return locale;
   201   }
   203   char lang[64];
   204   needed = uloc_getLanguage(localeID, lang, PR_ARRAY_SIZE(lang) - 1,
   205                             &status);
   206   MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(lang)) - 1,
   207              "Need a bigger buffer");
   208   if (needed <= 0 || U_FAILURE(status)) {
   209     return locale;
   210   }
   212   char country[64];
   213   needed = uloc_getCountry(localeID, country, PR_ARRAY_SIZE(country) - 1,
   214                            &status);
   215   MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(country)) - 1,
   216              "Need a bigger buffer");
   217   if (needed > 0 && U_SUCCESS(status)) {
   218     locale = Locale(lang, country);
   219   }
   221   if (locale.isBogus()) {
   222     // Using the country resulted in a bogus Locale, so try with only the lang
   223     locale = Locale(lang);
   224   }
   226   return locale;
   227 }
   229 /* static */ void
   230 ICUUtils::ToMozString(UnicodeString& aICUString, nsAString& aMozString)
   231 {
   232   // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
   233   // cast here.
   235   static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
   236                 "Unexpected character size - the following cast is unsafe");
   238   const nsAString::char_type* buf =
   239     (const nsAString::char_type*)aICUString.getTerminatedBuffer();
   240   aMozString.Assign(buf);
   242   NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
   243                "Conversion failed");
   244 }
   246 /* static */ void
   247 ICUUtils::ToICUString(nsAString& aMozString, UnicodeString& aICUString)
   248 {
   249   // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
   250   // cast here.
   252   static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
   253                 "Unexpected character size - the following cast is unsafe");
   255   aICUString.setTo((UChar*)PromiseFlatString(aMozString).get(),
   256                    aMozString.Length());
   258   NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
   259                "Conversion failed");
   260 }
   261 #endif
   263 #endif /* ENABLE_INTL_API */
   264 #endif /* MOZILLA_INTERNAL_API */

mercurial