tor-browser

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

AppDateTimeFormat.cpp (8306B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 *
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsCOMPtr.h"
      8 #include "mozilla/intl/AppDateTimeFormat.h"
      9 #include "mozilla/intl/DateTimePatternGenerator.h"
     10 #include "mozilla/intl/FormatBuffer.h"
     11 #include "mozilla/intl/LocaleService.h"
     12 #include "OSPreferences.h"
     13 #include "mozIOSPreferences.h"
     14 #ifdef DEBUG
     15 #  include "nsThreadManager.h"
     16 #endif
     17 
     18 namespace mozilla::intl {
     19 
     20 nsCString* AppDateTimeFormat::sLocale = nullptr;
     21 nsTHashMap<nsCStringHashKey, UniquePtr<DateTimeFormat>>*
     22    AppDateTimeFormat::sFormatCache;
     23 
     24 static const int32_t DATETIME_FORMAT_INITIAL_LEN = 127;
     25 
     26 /*static*/
     27 nsresult AppDateTimeFormat::Initialize() {
     28  MOZ_ASSERT(NS_IsMainThread());
     29  if (sLocale) {
     30    return NS_OK;
     31  }
     32 
     33  sLocale = new nsCString();
     34  AutoTArray<nsCString, 10> regionalPrefsLocales;
     35  LocaleService::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales);
     36  sLocale->Assign(regionalPrefsLocales[0]);
     37 
     38  return NS_OK;
     39 }
     40 
     41 // performs a locale sensitive date formatting operation on the PRTime parameter
     42 /*static*/
     43 nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle,
     44                                   const PRTime aPrTime,
     45                                   nsAString& aStringOut) {
     46  return AppDateTimeFormat::Format(
     47      aStyle, (static_cast<double>(aPrTime) / PR_USEC_PER_MSEC), nullptr,
     48      aStringOut);
     49 }
     50 
     51 // performs a locale sensitive date formatting operation on the PRExplodedTime
     52 // parameter
     53 /*static*/
     54 nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle,
     55                                   const PRExplodedTime* aExplodedTime,
     56                                   nsAString& aStringOut) {
     57  return AppDateTimeFormat::Format(
     58      aStyle, (PR_ImplodeTime(aExplodedTime) / PR_USEC_PER_MSEC),
     59      &(aExplodedTime->tm_params), aStringOut);
     60 }
     61 
     62 // performs a locale sensitive date formatting operation on the PRExplodedTime
     63 // parameter, using the specified options.
     64 /*static*/
     65 nsresult AppDateTimeFormat::Format(const DateTimeFormat::ComponentsBag& aBag,
     66                                   const PRExplodedTime* aExplodedTime,
     67                                   nsAString& aStringOut) {
     68  // set up locale data
     69  nsresult rv = Initialize();
     70  if (NS_FAILED(rv)) {
     71    return rv;
     72  }
     73 
     74  aStringOut.Truncate();
     75 
     76  nsAutoString timeZoneID;
     77  BuildTimeZoneString(aExplodedTime->tm_params, timeZoneID);
     78 
     79  auto genResult = DateTimePatternGenerator::TryCreate(sLocale->get());
     80  NS_ENSURE_TRUE(genResult.isOk(), NS_ERROR_FAILURE);
     81  auto dateTimePatternGenerator = genResult.unwrap();
     82 
     83  auto result = DateTimeFormat::TryCreateFromComponents(
     84      *sLocale, aBag, dateTimePatternGenerator.get(),
     85      Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length())));
     86  NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE);
     87  auto dateTimeFormat = result.unwrap();
     88 
     89  double unixEpoch =
     90      static_cast<float>((PR_ImplodeTime(aExplodedTime) / PR_USEC_PER_MSEC));
     91 
     92  aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
     93  nsTStringToBufferAdapter buffer(aStringOut);
     94  NS_ENSURE_TRUE(dateTimeFormat->TryFormat(unixEpoch, buffer).isOk(),
     95                 NS_ERROR_FAILURE);
     96 
     97  return rv;
     98 }
     99 
    100 /**
    101 * An internal utility function to serialize a Maybe<DateTimeFormat::Style> to
    102 * an int, to be used as a caching key.
    103 */
    104 static int StyleToInt(const Maybe<DateTimeFormat::Style>& aStyle) {
    105  if (aStyle.isSome()) {
    106    switch (*aStyle) {
    107      case DateTimeFormat::Style::Full:
    108        return 1;
    109      case DateTimeFormat::Style::Long:
    110        return 2;
    111      case DateTimeFormat::Style::Medium:
    112        return 3;
    113      case DateTimeFormat::Style::Short:
    114        return 4;
    115    }
    116  }
    117  return 0;
    118 }
    119 
    120 /*static*/
    121 nsresult AppDateTimeFormat::Format(const DateTimeFormat::StyleBag& aStyle,
    122                                   const double aUnixEpoch,
    123                                   const PRTimeParameters* aTimeParameters,
    124                                   nsAString& aStringOut) {
    125  nsresult rv = NS_OK;
    126 
    127  // return, nothing to format
    128  if (aStyle.date.isNothing() && aStyle.time.isNothing()) {
    129    aStringOut.Truncate();
    130    return NS_OK;
    131  }
    132 
    133  // set up locale data
    134  rv = Initialize();
    135 
    136  if (NS_FAILED(rv)) {
    137    return rv;
    138  }
    139 
    140  nsAutoCString key;
    141  key.AppendInt(StyleToInt(aStyle.date));
    142  key.Append(':');
    143  key.AppendInt(StyleToInt(aStyle.time));
    144  if (aTimeParameters) {
    145    key.Append(':');
    146    key.AppendInt(aTimeParameters->tp_gmt_offset);
    147    key.Append(':');
    148    key.AppendInt(aTimeParameters->tp_dst_offset);
    149  }
    150 
    151  if (sFormatCache && sFormatCache->Count() == kMaxCachedFormats) {
    152    // Don't allow a pathological page to extend the cache unreasonably.
    153    NS_WARNING("flushing DateTimeFormat cache");
    154    DeleteCache();
    155  }
    156  if (!sFormatCache) {
    157    sFormatCache = new nsTHashMap<nsCStringHashKey, UniquePtr<DateTimeFormat>>(
    158        kMaxCachedFormats);
    159  }
    160 
    161  UniquePtr<DateTimeFormat>& dateTimeFormat = sFormatCache->LookupOrInsert(key);
    162 
    163  if (!dateTimeFormat) {
    164    // We didn't have a cached formatter for this key, so create one.
    165    int32_t dateFormatStyle = mozIOSPreferences::dateTimeFormatStyleNone;
    166    if (aStyle.date.isSome()) {
    167      switch (*aStyle.date) {
    168        case DateTimeFormat::Style::Full:
    169        case DateTimeFormat::Style::Long:
    170          dateFormatStyle = mozIOSPreferences::dateTimeFormatStyleLong;
    171          break;
    172        case DateTimeFormat::Style::Medium:
    173        case DateTimeFormat::Style::Short:
    174          dateFormatStyle = mozIOSPreferences::dateTimeFormatStyleShort;
    175          break;
    176      }
    177    }
    178 
    179    int32_t timeFormatStyle = mozIOSPreferences::dateTimeFormatStyleNone;
    180    if (aStyle.time.isSome()) {
    181      switch (*aStyle.time) {
    182        case DateTimeFormat::Style::Full:
    183        case DateTimeFormat::Style::Long:
    184          timeFormatStyle = mozIOSPreferences::dateTimeFormatStyleLong;
    185          break;
    186        case DateTimeFormat::Style::Medium:
    187        case DateTimeFormat::Style::Short:
    188          timeFormatStyle = mozIOSPreferences::dateTimeFormatStyleShort;
    189          break;
    190      }
    191    }
    192 
    193    nsAutoCString str;
    194    rv = OSPreferences::GetInstance()->GetDateTimePattern(
    195        dateFormatStyle, timeFormatStyle, nsDependentCString(sLocale->get()),
    196        str);
    197    NS_ENSURE_SUCCESS(rv, rv);
    198    nsAutoString pattern = NS_ConvertUTF8toUTF16(str);
    199 
    200    Maybe<Span<const char16_t>> timeZoneOverride = Nothing();
    201    nsAutoString timeZoneID;
    202    if (aTimeParameters) {
    203      BuildTimeZoneString(*aTimeParameters, timeZoneID);
    204      timeZoneOverride =
    205          Some(Span<const char16_t>(timeZoneID.Data(), timeZoneID.Length()));
    206    }
    207 
    208    auto result = DateTimeFormat::TryCreateFromPattern(*sLocale, pattern,
    209                                                       timeZoneOverride);
    210    NS_ENSURE_TRUE(result.isOk(), NS_ERROR_FAILURE);
    211    dateTimeFormat = result.unwrap();
    212  }
    213 
    214  MOZ_ASSERT(dateTimeFormat);
    215 
    216  aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
    217  nsTStringToBufferAdapter buffer(aStringOut);
    218  NS_ENSURE_TRUE(dateTimeFormat->TryFormat(aUnixEpoch, buffer).isOk(),
    219                 NS_ERROR_FAILURE);
    220 
    221  return rv;
    222 }
    223 
    224 /*static*/
    225 void AppDateTimeFormat::BuildTimeZoneString(
    226    const PRTimeParameters& aTimeParameters, nsAString& aStringOut) {
    227  aStringOut.Truncate();
    228  aStringOut.Append(u"GMT");
    229  int32_t totalOffsetMinutes =
    230      (aTimeParameters.tp_gmt_offset + aTimeParameters.tp_dst_offset) / 60;
    231  if (totalOffsetMinutes != 0) {
    232    char sign = totalOffsetMinutes < 0 ? '-' : '+';
    233    int32_t hours = abs(totalOffsetMinutes) / 60;
    234    int32_t minutes = abs(totalOffsetMinutes) % 60;
    235    aStringOut.AppendPrintf("%c%02d:%02d", sign, hours, minutes);
    236  }
    237 }
    238 
    239 /*static*/
    240 void AppDateTimeFormat::DeleteCache() {
    241  MOZ_ASSERT(NS_IsMainThread());
    242  if (sFormatCache) {
    243    delete sFormatCache;
    244    sFormatCache = nullptr;
    245  }
    246 }
    247 
    248 /*static*/
    249 void AppDateTimeFormat::Shutdown() {
    250  MOZ_ASSERT(NS_IsMainThread());
    251  DeleteCache();
    252  delete sLocale;
    253 }
    254 
    255 /*static*/
    256 void AppDateTimeFormat::ClearLocaleCache() {
    257  MOZ_ASSERT(NS_IsMainThread());
    258  DeleteCache();
    259  delete sLocale;
    260  sLocale = nullptr;
    261 }
    262 
    263 }  // namespace mozilla::intl