LCOV - code coverage report
Current view: top level - intl/locale - OSPreferences.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 26 156 16.7 %
Date: 2017-07-14 16:53:18 Functions: 7 15 46.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /**
       7             :  * This is a shared part of the OSPreferences API implementation.
       8             :  * It defines helper methods and public methods that are calling
       9             :  * platform-specific private methods.
      10             :  */
      11             : 
      12             : #include "OSPreferences.h"
      13             : 
      14             : #include "mozilla/ClearOnShutdown.h"
      15             : #include "mozilla/Services.h"
      16             : #include "nsIObserverService.h"
      17             : #ifdef ENABLE_INTL_API
      18             : #include "unicode/udat.h"
      19             : #include "unicode/udatpg.h"
      20             : #endif
      21             : 
      22             : using namespace mozilla::intl;
      23             : 
      24          26 : NS_IMPL_ISUPPORTS(OSPreferences, mozIOSPreferences)
      25             : 
      26           3 : mozilla::StaticRefPtr<OSPreferences> OSPreferences::sInstance;
      27             : 
      28             : OSPreferences*
      29           3 : OSPreferences::GetInstance()
      30             : {
      31           3 :   if (!sInstance) {
      32           2 :     sInstance = new OSPreferences();
      33           2 :     ClearOnShutdown(&sInstance);
      34             :   }
      35           3 :   return sInstance;
      36             : }
      37             : 
      38             : bool
      39           2 : OSPreferences::GetSystemLocales(nsTArray<nsCString>& aRetVal)
      40             : {
      41           2 :   if (!mSystemLocales.IsEmpty()) {
      42           0 :     aRetVal = mSystemLocales;
      43           0 :     return true;
      44             :   }
      45             : 
      46           2 :   if (ReadSystemLocales(aRetVal)) {
      47           2 :     mSystemLocales = aRetVal;
      48           2 :     return true;
      49             :   }
      50             : 
      51             :   // If we failed to get the system locale, we still need
      52             :   // to return something because there are tests out there that
      53             :   // depend on system locale to be set.
      54           0 :   aRetVal.AppendElement(NS_LITERAL_CSTRING("en-US"));
      55           0 :   return false;
      56             : }
      57             : 
      58             : void
      59           0 : OSPreferences::Refresh()
      60             : {
      61           0 :   nsTArray<nsCString> newLocales;
      62           0 :   ReadSystemLocales(newLocales);
      63             : 
      64           0 :   if (mSystemLocales != newLocales) {
      65           0 :     mSystemLocales = Move(newLocales);
      66           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
      67           0 :     if (obs) {
      68           0 :       obs->NotifyObservers(nullptr, "intl:system-locales-changed", nullptr);
      69             :     }
      70             :   }
      71           0 : }
      72             : 
      73             : /**
      74             :  * This method should be called by every method of OSPreferences that
      75             :  * retrieves a locale id from external source.
      76             :  *
      77             :  * It attempts to retrieve as much of the locale ID as possible, cutting
      78             :  * out bits that are not understood (non-strict behavior of ICU).
      79             :  *
      80             :  * It returns true if the canonicalization was successful.
      81             :  */
      82             : bool
      83           2 : OSPreferences::CanonicalizeLanguageTag(nsCString& aLoc)
      84             : {
      85             : #ifdef ENABLE_INTL_API
      86             :   char langTag[512];
      87             : 
      88           2 :   UErrorCode status = U_ZERO_ERROR;
      89             : 
      90             :   int32_t langTagLen =
      91           2 :     uloc_toLanguageTag(aLoc.get(), langTag, sizeof(langTag) - 1, false, &status);
      92             : 
      93           2 :   if (U_FAILURE(status)) {
      94           0 :     return false;
      95             :   }
      96             : 
      97           2 :   aLoc.Assign(langTag, langTagLen);
      98           2 :   return true;
      99             : #else
     100             :   return false;
     101             : #endif
     102             : }
     103             : 
     104             : /**
     105             :  * This method retrieves from ICU the best pattern for a given date/time style.
     106             :  */
     107             : bool
     108           0 : OSPreferences::GetDateTimePatternForStyle(DateTimeFormatStyle aDateStyle,
     109             :                                           DateTimeFormatStyle aTimeStyle,
     110             :                                           const nsACString& aLocale,
     111             :                                           nsAString& aRetVal)
     112             : {
     113             : #ifdef ENABLE_INTL_API
     114           0 :   UDateFormatStyle timeStyle = UDAT_NONE;
     115           0 :   UDateFormatStyle dateStyle = UDAT_NONE;
     116             : 
     117           0 :   switch (aTimeStyle) {
     118             :     case DateTimeFormatStyle::None:
     119           0 :       timeStyle = UDAT_NONE;
     120           0 :       break;
     121             :     case DateTimeFormatStyle::Short:
     122           0 :       timeStyle = UDAT_SHORT;
     123           0 :       break;
     124             :     case DateTimeFormatStyle::Medium:
     125           0 :       timeStyle = UDAT_MEDIUM;
     126           0 :       break;
     127             :     case DateTimeFormatStyle::Long:
     128           0 :       timeStyle = UDAT_LONG;
     129           0 :       break;
     130             :     case DateTimeFormatStyle::Full:
     131           0 :       timeStyle = UDAT_FULL;
     132           0 :       break;
     133             :     case DateTimeFormatStyle::Invalid:
     134           0 :       timeStyle = UDAT_NONE;
     135           0 :       break;
     136             :   }
     137             : 
     138           0 :   switch (aDateStyle) {
     139             :     case DateTimeFormatStyle::None:
     140           0 :       dateStyle = UDAT_NONE;
     141           0 :       break;
     142             :     case DateTimeFormatStyle::Short:
     143           0 :       dateStyle = UDAT_SHORT;
     144           0 :       break;
     145             :     case DateTimeFormatStyle::Medium:
     146           0 :       dateStyle = UDAT_MEDIUM;
     147           0 :       break;
     148             :     case DateTimeFormatStyle::Long:
     149           0 :       dateStyle = UDAT_LONG;
     150           0 :       break;
     151             :     case DateTimeFormatStyle::Full:
     152           0 :       dateStyle = UDAT_FULL;
     153           0 :       break;
     154             :     case DateTimeFormatStyle::Invalid:
     155           0 :       dateStyle = UDAT_NONE;
     156           0 :       break;
     157             :   }
     158             : 
     159           0 :   const int32_t kPatternMax = 160;
     160             :   UChar pattern[kPatternMax];
     161             : 
     162           0 :   nsAutoCString locale;
     163           0 :   if (aLocale.IsEmpty()) {
     164           0 :     LocaleService::GetInstance()->GetAppLocaleAsBCP47(locale);
     165             :   } else {
     166           0 :     locale.Assign(aLocale);
     167             :   }
     168             : 
     169           0 :   UErrorCode status = U_ZERO_ERROR;
     170           0 :   UDateFormat* df = udat_open(timeStyle, dateStyle,
     171             :                               locale.get(),
     172           0 :                               nullptr, -1, nullptr, -1, &status);
     173           0 :   if (U_FAILURE(status)) {
     174           0 :     return false;
     175             :   }
     176             : 
     177           0 :   int32_t patsize = udat_toPattern(df, false, pattern, kPatternMax, &status);
     178           0 :   udat_close(df);
     179           0 :   if (U_FAILURE(status)) {
     180           0 :     return false;
     181             :   }
     182           0 :   aRetVal.Assign((const char16_t*)pattern, patsize);
     183           0 :   return true;
     184             : #else
     185             :   return false;
     186             : #endif
     187             : }
     188             : 
     189             : 
     190             : /**
     191             :  * This method retrieves from ICU the best skeleton for a given date/time style.
     192             :  *
     193             :  * This is useful for cases where an OS does not provide its own patterns,
     194             :  * but provide ability to customize the skeleton, like alter hourCycle setting.
     195             :  *
     196             :  * The returned value is a skeleton that matches the styles.
     197             :  */
     198             : bool
     199           0 : OSPreferences::GetDateTimeSkeletonForStyle(DateTimeFormatStyle aDateStyle,
     200             :                                            DateTimeFormatStyle aTimeStyle,
     201             :                                            const nsACString& aLocale,
     202             :                                            nsAString& aRetVal)
     203             : {
     204             : #ifdef ENABLE_INTL_API
     205           0 :   nsAutoString pattern;
     206           0 :   if (!GetDateTimePatternForStyle(aDateStyle, aTimeStyle, aLocale, pattern)) {
     207           0 :     return false;
     208             :   }
     209             : 
     210           0 :   const int32_t kSkeletonMax = 160;
     211             :   UChar skeleton[kSkeletonMax];
     212             : 
     213           0 :   UErrorCode status = U_ZERO_ERROR;
     214           0 :   int32_t skelsize = udatpg_getSkeleton(
     215           0 :     nullptr, (const UChar*)pattern.BeginReading(), pattern.Length(),
     216             :     skeleton, kSkeletonMax, &status
     217           0 :   );
     218           0 :   if (U_FAILURE(status)) {
     219           0 :     return false;
     220             :   }
     221             : 
     222           0 :   aRetVal.Assign((const char16_t*)skeleton, skelsize);
     223           0 :   return true;
     224             : #else
     225             :   return false;
     226             : #endif
     227             : }
     228             : 
     229             : /**
     230             :  * This function is a counterpart to GetDateTimeSkeletonForStyle.
     231             :  *
     232             :  * It takes a skeleton and returns the best available pattern for a given locale
     233             :  * that represents the provided skeleton.
     234             :  *
     235             :  * For example:
     236             :  * "Hm" skeleton for "en-US" will return "H:m"
     237             :  */
     238             : bool
     239           0 : OSPreferences::GetPatternForSkeleton(const nsAString& aSkeleton,
     240             :                                      const nsACString& aLocale,
     241             :                                      nsAString& aRetVal)
     242             : {
     243             : #ifdef ENABLE_INTL_API
     244           0 :   UErrorCode status = U_ZERO_ERROR;
     245           0 :   UDateTimePatternGenerator* pg = udatpg_open(PromiseFlatCString(aLocale).get(), &status);
     246           0 :   if (U_FAILURE(status)) {
     247           0 :     return false;
     248             :   }
     249             : 
     250             :   int32_t len =
     251           0 :     udatpg_getBestPattern(pg, (const UChar*)aSkeleton.BeginReading(),
     252           0 :                           aSkeleton.Length(), nullptr, 0, &status);
     253           0 :   if (status == U_BUFFER_OVERFLOW_ERROR) { // expected
     254           0 :     aRetVal.SetLength(len);
     255           0 :     status = U_ZERO_ERROR;
     256           0 :     udatpg_getBestPattern(pg, (const UChar*)aSkeleton.BeginReading(),
     257           0 :                           aSkeleton.Length(), (UChar*)aRetVal.BeginWriting(),
     258           0 :                           len, &status);
     259             :   }
     260             : 
     261           0 :   udatpg_close(pg);
     262             : 
     263           0 :   return U_SUCCESS(status);
     264             : #else
     265             :   return false;
     266             : #endif
     267             : }
     268             : 
     269             : /**
     270             :  * This function returns a pattern that should be used to join date and time
     271             :  * patterns into a single date/time pattern string.
     272             :  *
     273             :  * It's useful for OSes that do not provide an API to retrieve such combined
     274             :  * pattern.
     275             :  *
     276             :  * An example output is "{1}, {0}".
     277             :  */
     278             : bool
     279           0 : OSPreferences::GetDateTimeConnectorPattern(const nsACString& aLocale,
     280             :                                            nsAString& aRetVal)
     281             : {
     282           0 :   bool result = false;
     283             : #ifdef ENABLE_INTL_API
     284           0 :   UErrorCode status = U_ZERO_ERROR;
     285           0 :   UDateTimePatternGenerator* pg = udatpg_open(PromiseFlatCString(aLocale).get(), &status);
     286           0 :   if (U_SUCCESS(status)) {
     287             :     int32_t resultSize;
     288           0 :     const UChar* value = udatpg_getDateTimeFormat(pg, &resultSize);
     289           0 :     MOZ_ASSERT(resultSize >= 0);
     290             : 
     291           0 :     aRetVal.Assign((char16_t*)value, resultSize);
     292           0 :     result = true;
     293             :   }
     294           0 :   udatpg_close(pg);
     295             : #endif
     296           0 :   return result;
     297             : }
     298             : 
     299             : /**
     300             :  * mozIOSPreferences methods
     301             :  */
     302             : NS_IMETHODIMP
     303           0 : OSPreferences::GetSystemLocales(uint32_t* aCount, char*** aOutArray)
     304             : {
     305           0 :   AutoTArray<nsCString,10> tempLocales;
     306             :   nsTArray<nsCString>* systemLocalesPtr;
     307             : 
     308           0 :   if (!mSystemLocales.IsEmpty()) {
     309             :     // use cached value
     310           0 :     systemLocalesPtr = &mSystemLocales;
     311             :   } else {
     312             :     // get a (perhaps temporary/fallback/hack) value
     313           0 :     GetSystemLocales(tempLocales);
     314           0 :     systemLocalesPtr = &tempLocales;
     315             :   }
     316           0 :   *aCount = systemLocalesPtr->Length();
     317           0 :   *aOutArray = static_cast<char**>(moz_xmalloc(*aCount * sizeof(char*)));
     318             : 
     319           0 :   for (uint32_t i = 0; i < *aCount; i++) {
     320           0 :     (*aOutArray)[i] = moz_xstrdup((*systemLocalesPtr)[i].get());
     321             :   }
     322             : 
     323           0 :   return NS_OK;
     324             : }
     325             : 
     326             : NS_IMETHODIMP
     327           3 : OSPreferences::GetSystemLocale(nsACString& aRetVal)
     328             : {
     329           3 :   if (!mSystemLocales.IsEmpty()) {
     330           1 :     aRetVal = mSystemLocales[0];
     331             :   } else {
     332           4 :     AutoTArray<nsCString,10> locales;
     333           2 :     GetSystemLocales(locales);
     334           2 :     if (!locales.IsEmpty()) {
     335           2 :       aRetVal = locales[0];
     336             :     }
     337             :   }
     338           3 :   return NS_OK;
     339             : }
     340             : 
     341             : static OSPreferences::DateTimeFormatStyle
     342           0 : ToDateTimeFormatStyle(int32_t aTimeFormat)
     343             : {
     344           0 :   switch (aTimeFormat) {
     345             :     // See mozIOSPreferences.idl for the integer values here.
     346             :     case 0:
     347           0 :       return OSPreferences::DateTimeFormatStyle::None;
     348             :     case 1:
     349           0 :       return OSPreferences::DateTimeFormatStyle::Short;
     350             :     case 2:
     351           0 :       return OSPreferences::DateTimeFormatStyle::Medium;
     352             :     case 3:
     353           0 :       return OSPreferences::DateTimeFormatStyle::Long;
     354             :     case 4:
     355           0 :       return OSPreferences::DateTimeFormatStyle::Full;
     356             :   }
     357           0 :   return OSPreferences::DateTimeFormatStyle::Invalid;
     358             : }
     359             : 
     360             : NS_IMETHODIMP
     361           0 : OSPreferences::GetDateTimePattern(int32_t aDateFormatStyle,
     362             :                                   int32_t aTimeFormatStyle,
     363             :                                   const nsACString& aLocale,
     364             :                                   nsAString& aRetVal)
     365             : {
     366           0 :   DateTimeFormatStyle dateStyle = ToDateTimeFormatStyle(aDateFormatStyle);
     367           0 :   if (dateStyle == DateTimeFormatStyle::Invalid) {
     368           0 :     return NS_ERROR_INVALID_ARG;
     369             :   }
     370           0 :   DateTimeFormatStyle timeStyle = ToDateTimeFormatStyle(aTimeFormatStyle);
     371           0 :   if (timeStyle == DateTimeFormatStyle::Invalid) {
     372           0 :     return NS_ERROR_INVALID_ARG;
     373             :   }
     374             : 
     375             :   // If the user is asking for None on both, date and time style,
     376             :   // let's exit early.
     377           0 :   if (timeStyle == DateTimeFormatStyle::None &&
     378             :       dateStyle == DateTimeFormatStyle::None) {
     379           0 :     return NS_OK;
     380             :   }
     381             : 
     382           0 :   if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, aRetVal)) {
     383           0 :     if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, aRetVal)) {
     384           0 :       return NS_ERROR_FAILURE;
     385             :     }
     386             :   }
     387             : 
     388           0 :   return NS_OK;
     389             : }

Generated by: LCOV version 1.13