tor-browser

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

DisplayNames.cpp (7910B)


      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
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 #include "mozilla/intl/DisplayNames.h"
      5 #include "ScopedICUObject.h"
      6 
      7 namespace mozilla::intl {
      8 
      9 DisplayNames::~DisplayNames() {
     10  // The mDisplayNames will not exist when the DisplayNames is being
     11  // moved.
     12  if (auto* uldn = mULocaleDisplayNames.GetMut()) {
     13    uldn_close(uldn);
     14  }
     15 }
     16 
     17 DisplayNamesError DisplayNames::ToError(ICUError aError) const {
     18  switch (aError) {
     19    case ICUError::InternalError:
     20    case ICUError::OverflowError:
     21      return DisplayNamesError::InternalError;
     22    case ICUError::OutOfMemory:
     23      return DisplayNamesError::OutOfMemory;
     24  }
     25  MOZ_ASSERT_UNREACHABLE();
     26  return DisplayNamesError::InternalError;
     27 }
     28 
     29 DisplayNamesError DisplayNames::ToError(
     30    Locale::CanonicalizationError aError) const {
     31  switch (aError) {
     32    case Locale::CanonicalizationError::DuplicateVariant:
     33      return DisplayNamesError::DuplicateVariantSubtag;
     34    case Locale::CanonicalizationError::InternalError:
     35      return DisplayNamesError::InternalError;
     36    case Locale::CanonicalizationError::OutOfMemory:
     37      return DisplayNamesError::OutOfMemory;
     38  }
     39  MOZ_ASSERT_UNREACHABLE();
     40  return DisplayNamesError::InternalError;
     41 }
     42 
     43 /* static */
     44 Result<UniquePtr<DisplayNames>, ICUError> DisplayNames::TryCreate(
     45    const char* aLocale, Options aOptions) {
     46  UErrorCode status = U_ZERO_ERROR;
     47  UDisplayContext contexts[] = {
     48      // Use either standard or dialect names.
     49      // For example either "English (GB)" or "British English".
     50      aOptions.languageDisplay == DisplayNames::LanguageDisplay::Standard
     51          ? UDISPCTX_STANDARD_NAMES
     52          : UDISPCTX_DIALECT_NAMES,
     53 
     54      // Assume the display names are used in a stand-alone context.
     55      UDISPCTX_CAPITALIZATION_FOR_STANDALONE,
     56 
     57      // Select either the long or short form. There's no separate narrow form
     58      // available in ICU, therefore we equate "narrow"/"short" styles here.
     59      aOptions.style == DisplayNames::Style::Long ? UDISPCTX_LENGTH_FULL
     60                                                  : UDISPCTX_LENGTH_SHORT,
     61 
     62      // Don't apply substitutes, because we need to apply our own fallbacks.
     63      UDISPCTX_NO_SUBSTITUTE,
     64  };
     65 
     66  const char* locale = IcuLocale(aLocale);
     67 
     68  ULocaleDisplayNames* uLocaleDisplayNames =
     69      uldn_openForContext(locale, contexts, std::size(contexts), &status);
     70 
     71  if (U_FAILURE(status)) {
     72    return Err(ToICUError(status));
     73  }
     74  return MakeUnique<DisplayNames>(uLocaleDisplayNames, MakeStringSpan(locale),
     75                                  aOptions);
     76 };
     77 
     78 #ifdef DEBUG
     79 static bool IsStandaloneMonth(UDateFormatSymbolType symbolType) {
     80  switch (symbolType) {
     81    case UDAT_STANDALONE_MONTHS:
     82    case UDAT_STANDALONE_SHORT_MONTHS:
     83    case UDAT_STANDALONE_NARROW_MONTHS:
     84      return true;
     85 
     86    case UDAT_ERAS:
     87    case UDAT_MONTHS:
     88    case UDAT_SHORT_MONTHS:
     89    case UDAT_WEEKDAYS:
     90    case UDAT_SHORT_WEEKDAYS:
     91    case UDAT_AM_PMS:
     92 #  ifndef U_HIDE_DRAFT_API
     93    case UDAT_AM_PMS_NARROW:
     94    case UDAT_AM_PMS_WIDE:
     95 #  endif
     96    case UDAT_LOCALIZED_CHARS:
     97    case UDAT_ERA_NAMES:
     98    case UDAT_NARROW_MONTHS:
     99    case UDAT_NARROW_WEEKDAYS:
    100    case UDAT_STANDALONE_WEEKDAYS:
    101    case UDAT_STANDALONE_SHORT_WEEKDAYS:
    102    case UDAT_STANDALONE_NARROW_WEEKDAYS:
    103    case UDAT_QUARTERS:
    104    case UDAT_SHORT_QUARTERS:
    105    case UDAT_STANDALONE_QUARTERS:
    106    case UDAT_STANDALONE_SHORT_QUARTERS:
    107    case UDAT_SHORTER_WEEKDAYS:
    108    case UDAT_STANDALONE_SHORTER_WEEKDAYS:
    109    case UDAT_CYCLIC_YEARS_WIDE:
    110    case UDAT_CYCLIC_YEARS_ABBREVIATED:
    111    case UDAT_CYCLIC_YEARS_NARROW:
    112    case UDAT_ZODIAC_NAMES_WIDE:
    113    case UDAT_ZODIAC_NAMES_ABBREVIATED:
    114    case UDAT_ZODIAC_NAMES_NARROW:
    115    case UDAT_NARROW_QUARTERS:
    116    case UDAT_STANDALONE_NARROW_QUARTERS:
    117      return false;
    118  }
    119 
    120  MOZ_ASSERT_UNREACHABLE("unenumerated, undocumented symbol type");
    121  return false;
    122 }
    123 #endif
    124 
    125 Result<Ok, DisplayNamesError> DisplayNames::ComputeDateTimeDisplayNames(
    126    UDateFormatSymbolType symbolType, mozilla::Span<const int32_t> indices,
    127    Span<const char> aCalendar) {
    128  if (!mDateTimeDisplayNames.empty()) {
    129    // No need to re-compute the display names.
    130    return Ok();
    131  }
    132  mozilla::intl::Locale tag;
    133  // Do not use mLocale.AsSpan() as it includes the null terminator inside the
    134  // span.
    135  if (LocaleParser::TryParse(Span(mLocale.Elements(), mLocale.Length() - 1),
    136                             tag)
    137          .isErr()) {
    138    return Err(DisplayNamesError::InvalidLanguageTag);
    139  }
    140 
    141  if (!aCalendar.empty()) {
    142    // Add the calendar extension to the locale. This is only available via
    143    // the MozExtension.
    144    Vector<char, 32> extension;
    145    Span<const char> prefix = MakeStringSpan("u-ca-");
    146    if (!extension.append(prefix.data(), prefix.size()) ||
    147        !extension.append(aCalendar.data(), aCalendar.size())) {
    148      return Err(DisplayNamesError::OutOfMemory);
    149    }
    150    // This overwrites any other Unicode extensions, but should be okay to do
    151    // here.
    152    if (auto result = tag.SetUnicodeExtension(extension); result.isErr()) {
    153      return Err(ToError(result.unwrapErr()));
    154    }
    155  }
    156 
    157  constexpr char16_t* timeZone = nullptr;
    158  constexpr int32_t timeZoneLength = 0;
    159 
    160  constexpr char16_t* pattern = nullptr;
    161  constexpr int32_t patternLength = 0;
    162 
    163  Vector<char, DisplayNames::LocaleVecLength> localeWithCalendar;
    164  VectorToBufferAdaptor buffer(localeWithCalendar);
    165  if (auto result = tag.ToString(buffer); result.isErr()) {
    166    return Err(ToError(result.unwrapErr()));
    167  }
    168  if (!localeWithCalendar.append('\0')) {
    169    return Err(DisplayNamesError::OutOfMemory);
    170  }
    171 
    172  UErrorCode status = U_ZERO_ERROR;
    173  UDateFormat* fmt = udat_open(
    174      UDAT_DEFAULT, UDAT_DEFAULT,
    175      IcuLocale(
    176          // IcuLocale takes a Span that does not include the null terminator.
    177          Span(localeWithCalendar.begin(), localeWithCalendar.length() - 1)),
    178      timeZone, timeZoneLength, pattern, patternLength, &status);
    179  if (U_FAILURE(status)) {
    180    return Err(DisplayNamesError::InternalError);
    181  }
    182  ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
    183 
    184  Vector<char16_t, DisplayNames::LocaleVecLength> name;
    185  for (int32_t index : indices) {
    186    auto result = FillBufferWithICUCall(name, [&](UChar* target, int32_t length,
    187                                                  UErrorCode* status) {
    188      return udat_getSymbols(fmt, symbolType, index, target, length, status);
    189    });
    190    if (result.isErr()) {
    191      return Err(ToError(result.unwrapErr()));
    192    }
    193 
    194    // Everything except Undecimber should always have a non-empty name.
    195    MOZ_ASSERT_IF(!IsStandaloneMonth(symbolType) || index != UCAL_UNDECIMBER,
    196                  !name.empty());
    197 
    198    if (!mDateTimeDisplayNames.emplaceBack(Span(name.begin(), name.length()))) {
    199      return Err(DisplayNamesError::OutOfMemory);
    200    }
    201  }
    202  return Ok();
    203 }
    204 
    205 Span<const char> DisplayNames::ToCodeString(Month aMonth) {
    206  switch (aMonth) {
    207    case Month::January:
    208      return MakeStringSpan("1");
    209    case Month::February:
    210      return MakeStringSpan("2");
    211    case Month::March:
    212      return MakeStringSpan("3");
    213    case Month::April:
    214      return MakeStringSpan("4");
    215    case Month::May:
    216      return MakeStringSpan("5");
    217    case Month::June:
    218      return MakeStringSpan("6");
    219    case Month::July:
    220      return MakeStringSpan("7");
    221    case Month::August:
    222      return MakeStringSpan("8");
    223    case Month::September:
    224      return MakeStringSpan("9");
    225    case Month::October:
    226      return MakeStringSpan("10");
    227    case Month::November:
    228      return MakeStringSpan("11");
    229    case Month::December:
    230      return MakeStringSpan("12");
    231    case Month::Undecimber:
    232      return MakeStringSpan("13");
    233  }
    234  MOZ_ASSERT_UNREACHABLE();
    235  return MakeStringSpan("1");
    236 };
    237 
    238 }  // namespace mozilla::intl