tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ICUUtils.cpp (5851B)


      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/. */
      4 
      5 #ifdef MOZILLA_INTERNAL_API
      6 
      7 #  include "mozilla/Assertions.h"
      8 #  include "mozilla/UniquePtr.h"
      9 
     10 #  include "ICUUtils.h"
     11 #  include "mozilla/ClearOnShutdown.h"
     12 #  include "mozilla/StaticPrefs_dom.h"
     13 #  include "mozilla/intl/LocaleService.h"
     14 #  include "mozilla/intl/FormatBuffer.h"
     15 #  include "mozilla/intl/NumberFormat.h"
     16 #  include "mozilla/intl/NumberParser.h"
     17 #  include "nsIContent.h"
     18 #  include "mozilla/dom/Document.h"
     19 #  include "nsString.h"
     20 
     21 using namespace mozilla;
     22 using mozilla::intl::LocaleService;
     23 
     24 already_AddRefed<nsAtom> ICUUtils::LanguageTagIterForContent::GetNext() {
     25  if (mCurrentFallbackIndex < 0) {
     26    mCurrentFallbackIndex = 0;
     27    // Try the language specified by a 'lang'/'xml:lang' attribute on mContent
     28    // or any ancestor, if such an attribute is specified:
     29    if (auto* lang = mContent->GetLang()) {
     30      return do_AddRef(lang);
     31    }
     32  }
     33 
     34  if (mCurrentFallbackIndex < 1) {
     35    mCurrentFallbackIndex = 1;
     36    // Else try the language specified by any Content-Language HTTP header or
     37    // pragma directive:
     38    if (nsAtom* lang = mContent->OwnerDoc()->GetContentLanguage()) {
     39      return do_AddRef(lang);
     40    }
     41  }
     42 
     43  if (mCurrentFallbackIndex < 2) {
     44    mCurrentFallbackIndex = 2;
     45    // Else take the app's locale (or en-US, if spoof English applies):
     46    if (mContent->OwnerDoc()->ShouldResistFingerprinting(RFPTarget::JSLocale)) {
     47      return NS_Atomize(nsRFPService::GetSpoofedJSLocale());
     48    }
     49    nsAutoCString appLocale;
     50    LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
     51    return NS_Atomize(appLocale);
     52  }
     53 
     54  // TODO: Probably not worth it, but maybe have a fourth fallback to using
     55  // the OS locale?
     56  return nullptr;
     57 }
     58 
     59 /* static */
     60 bool ICUUtils::LocalizeNumber(double aValue,
     61                              LanguageTagIterForContent& aLangTags,
     62                              nsAString& aLocalizedValue) {
     63  MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
     64  MOZ_ASSERT(NS_IsMainThread());
     65  using LangToFormatterCache =
     66      nsTHashMap<RefPtr<nsAtom>, UniquePtr<intl::NumberFormat>>;
     67 
     68  static StaticAutoPtr<LangToFormatterCache> sCache;
     69  if (!sCache) {
     70    sCache = new LangToFormatterCache();
     71    ClearOnShutdown(&sCache);
     72  }
     73 
     74  intl::NumberFormatOptions options;
     75  if (StaticPrefs::dom_forms_number_grouping()) {
     76    options.mGrouping = intl::NumberFormatOptions::Grouping::Always;
     77  } else {
     78    options.mGrouping = intl::NumberFormatOptions::Grouping::Never;
     79  }
     80 
     81  // ICU default is a maximum of 3 significant fractional digits. We don't
     82  // want that limit, so we set it to the maximum that a double can represent
     83  // (14-16 decimal fractional digits).
     84  options.mFractionDigits = Some(std::make_pair(0, 16));
     85 
     86  while (RefPtr<nsAtom> langTag = aLangTags.GetNext()) {
     87    auto& formatter = sCache->LookupOrInsertWith(langTag, [&] {
     88      nsAutoCString tag;
     89      langTag->ToUTF8String(tag);
     90      if (tag.FindChar('\0') != kNotFound) {
     91        return UniquePtr<intl::NumberFormat>();
     92      }
     93      return intl::NumberFormat::TryCreate(tag, options).unwrapOr(nullptr);
     94    });
     95    if (!formatter) {
     96      continue;
     97    }
     98    intl::nsTStringToBufferAdapter adapter(aLocalizedValue);
     99    if (formatter->format(aValue, adapter).isOk()) {
    100      return true;
    101    }
    102  }
    103  return false;
    104 }
    105 
    106 /* static */
    107 double ICUUtils::ParseNumber(const nsAString& aValue,
    108                             LanguageTagIterForContent& aLangTags) {
    109  MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
    110  using LangToParserCache =
    111      nsTHashMap<RefPtr<nsAtom>, UniquePtr<intl::NumberParser>>;
    112  static StaticAutoPtr<LangToParserCache> sCache;
    113  if (aValue.IsEmpty()) {
    114    return std::numeric_limits<float>::quiet_NaN();
    115  }
    116 
    117  if (!sCache) {
    118    sCache = new LangToParserCache();
    119    ClearOnShutdown(&sCache);
    120  }
    121 
    122  const Span<const char16_t> value(aValue.BeginReading(), aValue.Length());
    123 
    124  while (RefPtr<nsAtom> langTag = aLangTags.GetNext()) {
    125    auto& parser = sCache->LookupOrInsertWith(langTag, [&] {
    126      nsAutoCString tag;
    127      langTag->ToUTF8String(tag);
    128      if (tag.FindChar('\0') != kNotFound) {
    129        return UniquePtr<intl::NumberParser>();
    130      }
    131      return intl::NumberParser::TryCreate(
    132                 tag, StaticPrefs::dom_forms_number_grouping())
    133          .unwrapOr(nullptr);
    134    });
    135    if (!parser) {
    136      continue;
    137    }
    138    static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
    139                  "Unexpected character size - the following cast is unsafe");
    140    auto parseResult = parser->ParseDouble(value);
    141    if (!parseResult.isOk()) {
    142      continue;
    143    }
    144    std::pair<double, int32_t> parsed = parseResult.unwrap();
    145    if (parsed.second == static_cast<int32_t>(value.Length())) {
    146      return parsed.first;
    147    }
    148  }
    149  return std::numeric_limits<float>::quiet_NaN();
    150 }
    151 
    152 /* static */
    153 void ICUUtils::AssignUCharArrayToString(UChar* aICUString, int32_t aLength,
    154                                        nsAString& aMozString) {
    155  // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
    156  // cast here.
    157 
    158  static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
    159                "Unexpected character size - the following cast is unsafe");
    160 
    161  aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
    162 
    163  NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
    164 }
    165 
    166 /* static */
    167 nsresult ICUUtils::ICUErrorToNsResult(const intl::ICUError aError) {
    168  switch (aError) {
    169    case intl::ICUError::OutOfMemory:
    170      return NS_ERROR_OUT_OF_MEMORY;
    171 
    172    default:
    173      return NS_ERROR_FAILURE;
    174  }
    175 }
    176 
    177 #endif /* MOZILLA_INTERNAL_API */