LCOV - code coverage report
Current view: top level - intl/strres - nsStringBundle.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 161 336 47.9 %
Date: 2017-07-14 16:53:18 Functions: 24 46 52.2 %
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             : #include "nsStringBundle.h"
       7             : #include "nsID.h"
       8             : #include "nsString.h"
       9             : #include "nsIStringBundle.h"
      10             : #include "nsStringBundleService.h"
      11             : #include "nsStringBundleTextOverride.h"
      12             : #include "nsISupportsPrimitives.h"
      13             : #include "nsIMutableArray.h"
      14             : #include "nsArrayEnumerator.h"
      15             : #include "nscore.h"
      16             : #include "nsMemory.h"
      17             : #include "nsNetUtil.h"
      18             : #include "nsComponentManagerUtils.h"
      19             : #include "nsServiceManagerUtils.h"
      20             : #include "nsIInputStream.h"
      21             : #include "nsIURI.h"
      22             : #include "nsIObserverService.h"
      23             : #include "nsCOMArray.h"
      24             : #include "nsTextFormatter.h"
      25             : #include "nsIErrorService.h"
      26             : #include "nsICategoryManager.h"
      27             : #include "nsContentUtils.h"
      28             : 
      29             : // for async loading
      30             : #ifdef ASYNC_LOADING
      31             : #include "nsIBinaryInputStream.h"
      32             : #include "nsIStringStream.h"
      33             : #endif
      34             : 
      35             : using namespace mozilla;
      36             : 
      37             : static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID);
      38             : 
      39           0 : nsStringBundle::~nsStringBundle()
      40             : {
      41           0 : }
      42             : 
      43          26 : nsStringBundle::nsStringBundle(const char* aURLSpec,
      44          26 :                                nsIStringBundleOverride* aOverrideStrings) :
      45             :   mPropertiesURL(aURLSpec),
      46             :   mOverrideStrings(aOverrideStrings),
      47             :   mReentrantMonitor("nsStringBundle.mReentrantMonitor"),
      48             :   mAttemptedLoad(false),
      49          26 :   mLoaded(false)
      50             : {
      51          26 : }
      52             : 
      53             : nsresult
      54         155 : nsStringBundle::LoadProperties()
      55             : {
      56             :   // this is different than mLoaded, because we only want to attempt
      57             :   // to load once
      58             :   // we only want to load once, but if we've tried once and failed,
      59             :   // continue to throw an error!
      60         155 :   if (mAttemptedLoad) {
      61         134 :     if (mLoaded)
      62         134 :       return NS_OK;
      63             : 
      64           0 :     return NS_ERROR_UNEXPECTED;
      65             :   }
      66             : 
      67          21 :   mAttemptedLoad = true;
      68             : 
      69             :   nsresult rv;
      70             : 
      71             :   // do it synchronously
      72          42 :   nsCOMPtr<nsIURI> uri;
      73          21 :   rv = NS_NewURI(getter_AddRefs(uri), mPropertiesURL);
      74          21 :   if (NS_FAILED(rv)) return rv;
      75             : 
      76             :   // whitelist check for local schemes
      77          42 :   nsCString scheme;
      78          21 :   uri->GetScheme(scheme);
      79          42 :   if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("jar") &&
      80          21 :       !scheme.EqualsLiteral("resource") && !scheme.EqualsLiteral("file") &&
      81           0 :       !scheme.EqualsLiteral("data")) {
      82           0 :     return NS_ERROR_ABORT;
      83             :   }
      84             : 
      85          42 :   nsCOMPtr<nsIChannel> channel;
      86          21 :   rv = NS_NewChannel(getter_AddRefs(channel),
      87             :                      uri,
      88             :                      nsContentUtils::GetSystemPrincipal(),
      89             :                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
      90             :                      nsIContentPolicy::TYPE_OTHER);
      91             : 
      92          21 :   if (NS_FAILED(rv)) return rv;
      93             : 
      94             :   // It's a string bundle.  We expect a text/plain type, so set that as hint
      95          21 :   channel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
      96             : 
      97          42 :   nsCOMPtr<nsIInputStream> in;
      98          21 :   rv = channel->Open2(getter_AddRefs(in));
      99          21 :   if (NS_FAILED(rv)) return rv;
     100             : 
     101          21 :   NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream");
     102          21 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE);
     103             : 
     104             :   static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID);
     105          21 :   mProps = do_CreateInstance(kPersistentPropertiesCID, &rv);
     106          21 :   NS_ENSURE_SUCCESS(rv, rv);
     107             : 
     108          21 :   mAttemptedLoad = mLoaded = true;
     109          21 :   rv = mProps->Load(in);
     110             : 
     111          21 :   mLoaded = NS_SUCCEEDED(rv);
     112             : 
     113          21 :   return rv;
     114             : }
     115             : 
     116             : 
     117             : nsresult
     118           0 : nsStringBundle::GetStringFromID(int32_t aID, nsAString& aResult)
     119             : {
     120           0 :   ReentrantMonitorAutoEnter automon(mReentrantMonitor);
     121           0 :   nsAutoCString name;
     122           0 :   name.AppendInt(aID, 10);
     123             : 
     124             :   nsresult rv;
     125             : 
     126             :   // try override first
     127           0 :   if (mOverrideStrings) {
     128           0 :     rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
     129             :                                              name,
     130           0 :                                              aResult);
     131           0 :     if (NS_SUCCEEDED(rv)) return rv;
     132             :   }
     133             : 
     134           0 :   rv = mProps->GetStringProperty(name, aResult);
     135             : 
     136           0 :   return rv;
     137             : }
     138             : 
     139             : nsresult
     140         155 : nsStringBundle::GetStringFromName(const nsAString& aName,
     141             :                                   nsAString& aResult)
     142             : {
     143             :   nsresult rv;
     144             : 
     145             :   // try override first
     146         155 :   if (mOverrideStrings) {
     147           0 :     rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
     148           0 :                                              NS_ConvertUTF16toUTF8(aName),
     149           0 :                                              aResult);
     150           0 :     if (NS_SUCCEEDED(rv)) return rv;
     151             :   }
     152             : 
     153         155 :   rv = mProps->GetStringProperty(NS_ConvertUTF16toUTF8(aName), aResult);
     154         155 :   return rv;
     155             : }
     156             : 
     157             : NS_IMETHODIMP
     158           9 : nsStringBundle::FormatStringFromID(int32_t aID,
     159             :                                    const char16_t **aParams,
     160             :                                    uint32_t aLength,
     161             :                                    char16_t ** aResult)
     162             : {
     163          18 :   nsAutoString idStr;
     164           9 :   idStr.AppendInt(aID, 10);
     165             : 
     166          18 :   return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
     167             : }
     168             : 
     169             : // this function supports at most 10 parameters.. see below for why
     170             : NS_IMETHODIMP
     171          79 : nsStringBundle::FormatStringFromName(const char16_t *aName,
     172             :                                      const char16_t **aParams,
     173             :                                      uint32_t aLength,
     174             :                                      char16_t **aResult)
     175             : {
     176          79 :   NS_ENSURE_ARG_POINTER(aName);
     177          79 :   NS_ASSERTION(aParams && aLength, "FormatStringFromName() without format parameters: use GetStringFromName() instead");
     178          79 :   NS_ENSURE_ARG_POINTER(aResult);
     179             : 
     180             :   nsresult rv;
     181          79 :   rv = LoadProperties();
     182          79 :   if (NS_FAILED(rv)) return rv;
     183             : 
     184         158 :   nsAutoString formatStr;
     185          79 :   rv = GetStringFromName(nsDependentString(aName), formatStr);
     186          79 :   if (NS_FAILED(rv)) return rv;
     187             : 
     188          79 :   return FormatString(formatStr.get(), aParams, aLength, aResult);
     189             : }
     190             : 
     191             : 
     192         675 : NS_IMPL_ISUPPORTS(nsStringBundle, nsIStringBundle)
     193             : 
     194             : NS_IMETHODIMP
     195           0 : nsStringBundle::GetStringFromID(int32_t aID, char16_t **aResult)
     196             : {
     197             :   nsresult rv;
     198           0 :   rv = LoadProperties();
     199           0 :   if (NS_FAILED(rv)) return rv;
     200             : 
     201           0 :   *aResult = nullptr;
     202           0 :   nsAutoString tmpstr;
     203             : 
     204           0 :   rv = GetStringFromID(aID, tmpstr);
     205           0 :   NS_ENSURE_SUCCESS(rv, rv);
     206             : 
     207           0 :   *aResult = ToNewUnicode(tmpstr);
     208           0 :   NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
     209             : 
     210           0 :   return NS_OK;
     211             : }
     212             : 
     213             : NS_IMETHODIMP
     214          76 : nsStringBundle::GetStringFromName(const char16_t *aName, char16_t **aResult)
     215             : {
     216          76 :   NS_ENSURE_ARG_POINTER(aName);
     217          76 :   NS_ENSURE_ARG_POINTER(aResult);
     218             : 
     219             :   nsresult rv;
     220          76 :   rv = LoadProperties();
     221          76 :   if (NS_FAILED(rv)) return rv;
     222             : 
     223         152 :   ReentrantMonitorAutoEnter automon(mReentrantMonitor);
     224          76 :   *aResult = nullptr;
     225         152 :   nsAutoString tmpstr;
     226          76 :   rv = GetStringFromName(nsDependentString(aName), tmpstr);
     227          76 :   if (NS_FAILED(rv))
     228             :   {
     229             : #if 0
     230             :     // it is not uncommon for apps to request a string name which may not exist
     231             :     // so be quiet about it.
     232             :     NS_WARNING("String missing from string bundle");
     233             :     printf("  '%s' missing from bundle %s\n", NS_ConvertUTF16toUTF8(aName).get(), mPropertiesURL.get());
     234             : #endif
     235           7 :     return rv;
     236             :   }
     237             : 
     238          69 :   *aResult = ToNewUnicode(tmpstr);
     239          69 :   NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
     240             : 
     241          69 :   return NS_OK;
     242             : }
     243             : 
     244             : nsresult
     245           0 : nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride* aOverrideStrings,
     246             :                                        nsISimpleEnumerator** aResult)
     247             : {
     248           0 :   nsCOMPtr<nsISupports> supports;
     249           0 :   nsCOMPtr<nsIPropertyElement> propElement;
     250             : 
     251             :   nsresult rv;
     252             : 
     253             :   nsCOMPtr<nsIMutableArray> resultArray =
     254           0 :     do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     255           0 :   NS_ENSURE_SUCCESS(rv, rv);
     256             : 
     257             :   // first, append the override elements
     258           0 :   nsCOMPtr<nsISimpleEnumerator> overrideEnumerator;
     259           0 :   rv = aOverrideStrings->EnumerateKeysInBundle(mPropertiesURL,
     260           0 :                                                getter_AddRefs(overrideEnumerator));
     261             : 
     262             :   bool hasMore;
     263           0 :   rv = overrideEnumerator->HasMoreElements(&hasMore);
     264           0 :   NS_ENSURE_SUCCESS(rv, rv);
     265           0 :   while (hasMore) {
     266             : 
     267           0 :     rv = overrideEnumerator->GetNext(getter_AddRefs(supports));
     268           0 :     if (NS_SUCCEEDED(rv))
     269           0 :       resultArray->AppendElement(supports, false);
     270             : 
     271           0 :     rv = overrideEnumerator->HasMoreElements(&hasMore);
     272           0 :     NS_ENSURE_SUCCESS(rv, rv);
     273             :   }
     274             : 
     275             :   // ok, now we have the override elements in resultArray
     276           0 :   nsCOMPtr<nsISimpleEnumerator> propEnumerator;
     277           0 :   rv = mProps->Enumerate(getter_AddRefs(propEnumerator));
     278           0 :   if (NS_FAILED(rv)) {
     279             :     // no elements in mProps anyway, just return what we have
     280           0 :     return NS_NewArrayEnumerator(aResult, resultArray);
     281             :   }
     282             : 
     283             :   // second, append all the elements that are in mProps
     284           0 :   do {
     285           0 :     rv = propEnumerator->GetNext(getter_AddRefs(supports));
     286           0 :     if (NS_SUCCEEDED(rv) &&
     287           0 :         (propElement = do_QueryInterface(supports, &rv))) {
     288             : 
     289             :       // now check if its in the override bundle
     290           0 :       nsAutoCString key;
     291           0 :       propElement->GetKey(key);
     292             : 
     293           0 :       nsAutoString value;
     294           0 :       rv = aOverrideStrings->GetStringFromName(mPropertiesURL, key, value);
     295             : 
     296             :       // if it isn't there, then it is safe to append
     297           0 :       if (NS_FAILED(rv))
     298           0 :         resultArray->AppendElement(propElement, false);
     299             :     }
     300             : 
     301           0 :     rv = propEnumerator->HasMoreElements(&hasMore);
     302           0 :     NS_ENSURE_SUCCESS(rv, rv);
     303             :   } while (hasMore);
     304             : 
     305           0 :   return resultArray->Enumerate(aResult);
     306             : }
     307             : 
     308             : 
     309             : NS_IMETHODIMP
     310           0 : nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator** elements)
     311             : {
     312           0 :   if (!elements)
     313           0 :     return NS_ERROR_INVALID_POINTER;
     314             : 
     315             :   nsresult rv;
     316           0 :   rv = LoadProperties();
     317           0 :   if (NS_FAILED(rv)) return rv;
     318             : 
     319           0 :   if (mOverrideStrings)
     320           0 :       return GetCombinedEnumeration(mOverrideStrings, elements);
     321             : 
     322           0 :   return mProps->Enumerate(elements);
     323             : }
     324             : 
     325             : nsresult
     326          79 : nsStringBundle::FormatString(const char16_t *aFormatStr,
     327             :                              const char16_t **aParams, uint32_t aLength,
     328             :                              char16_t **aResult)
     329             : {
     330          79 :   NS_ENSURE_ARG_POINTER(aResult);
     331          79 :   NS_ENSURE_ARG(aLength <= 10); // enforce 10-parameter limit
     332             : 
     333             :   // implementation note: you would think you could use vsmprintf
     334             :   // to build up an arbitrary length array.. except that there
     335             :   // is no way to build up a va_list at runtime!
     336             :   // Don't believe me? See:
     337             :   //   http://www.eskimo.com/~scs/C-faq/q15.13.html
     338             :   // -alecf
     339             :   char16_t *text =
     340          79 :     nsTextFormatter::smprintf(aFormatStr,
     341             :                               aLength >= 1 ? aParams[0] : nullptr,
     342             :                               aLength >= 2 ? aParams[1] : nullptr,
     343             :                               aLength >= 3 ? aParams[2] : nullptr,
     344             :                               aLength >= 4 ? aParams[3] : nullptr,
     345             :                               aLength >= 5 ? aParams[4] : nullptr,
     346             :                               aLength >= 6 ? aParams[5] : nullptr,
     347             :                               aLength >= 7 ? aParams[6] : nullptr,
     348             :                               aLength >= 8 ? aParams[7] : nullptr,
     349             :                               aLength >= 9 ? aParams[8] : nullptr,
     350          79 :                               aLength >= 10 ? aParams[9] : nullptr);
     351             : 
     352          79 :   if (!text) {
     353           0 :     *aResult = nullptr;
     354           0 :     return NS_ERROR_OUT_OF_MEMORY;
     355             :   }
     356             : 
     357             :   // nsTextFormatter does not use the shared nsMemory allocator.
     358             :   // Instead it is required to free the memory it allocates using
     359             :   // nsTextFormatter::smprintf_free.  Let's instead use nsMemory based
     360             :   // allocation for the result that we give out and free the string
     361             :   // returned by smprintf ourselves!
     362          79 :   *aResult = NS_strdup(text);
     363          79 :   nsTextFormatter::smprintf_free(text);
     364             : 
     365          79 :   return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     366             : }
     367             : 
     368           0 : NS_IMPL_ISUPPORTS(nsExtensibleStringBundle, nsIStringBundle)
     369             : 
     370           0 : nsExtensibleStringBundle::nsExtensibleStringBundle()
     371             : {
     372           0 :   mLoaded = false;
     373           0 : }
     374             : 
     375             : nsresult
     376           0 : nsExtensibleStringBundle::Init(const char * aCategory,
     377             :                                nsIStringBundleService* aBundleService)
     378             : {
     379             : 
     380             :   nsresult rv;
     381             :   nsCOMPtr<nsICategoryManager> catman =
     382           0 :     do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
     383           0 :   if (NS_FAILED(rv)) return rv;
     384             : 
     385           0 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
     386           0 :   rv = catman->EnumerateCategory(aCategory, getter_AddRefs(enumerator));
     387           0 :   if (NS_FAILED(rv)) return rv;
     388             : 
     389             :   bool hasMore;
     390           0 :   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
     391           0 :     nsCOMPtr<nsISupports> supports;
     392           0 :     rv = enumerator->GetNext(getter_AddRefs(supports));
     393           0 :     if (NS_FAILED(rv))
     394           0 :       continue;
     395             : 
     396           0 :     nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports, &rv);
     397           0 :     if (NS_FAILED(rv))
     398           0 :       continue;
     399             : 
     400           0 :     nsAutoCString name;
     401           0 :     rv = supStr->GetData(name);
     402           0 :     if (NS_FAILED(rv))
     403           0 :       continue;
     404             : 
     405           0 :     nsCOMPtr<nsIStringBundle> bundle;
     406           0 :     rv = aBundleService->CreateBundle(name.get(), getter_AddRefs(bundle));
     407           0 :     if (NS_FAILED(rv))
     408           0 :       continue;
     409             : 
     410           0 :     mBundles.AppendObject(bundle);
     411             :   }
     412             : 
     413           0 :   return rv;
     414             : }
     415             : 
     416           0 : nsExtensibleStringBundle::~nsExtensibleStringBundle()
     417             : {
     418           0 : }
     419             : 
     420           0 : nsresult nsExtensibleStringBundle::GetStringFromID(int32_t aID, char16_t ** aResult)
     421             : {
     422             :   nsresult rv;
     423           0 :   const uint32_t size = mBundles.Count();
     424           0 :   for (uint32_t i = 0; i < size; ++i) {
     425           0 :     nsIStringBundle *bundle = mBundles[i];
     426           0 :     if (bundle) {
     427           0 :       rv = bundle->GetStringFromID(aID, aResult);
     428           0 :       if (NS_SUCCEEDED(rv))
     429           0 :         return NS_OK;
     430             :     }
     431             :   }
     432             : 
     433           0 :   return NS_ERROR_FAILURE;
     434             : }
     435             : 
     436           0 : nsresult nsExtensibleStringBundle::GetStringFromName(const char16_t *aName,
     437             :                                                      char16_t ** aResult)
     438             : {
     439             :   nsresult rv;
     440           0 :   const uint32_t size = mBundles.Count();
     441           0 :   for (uint32_t i = 0; i < size; ++i) {
     442           0 :     nsIStringBundle* bundle = mBundles[i];
     443           0 :     if (bundle) {
     444           0 :       rv = bundle->GetStringFromName(aName, aResult);
     445           0 :       if (NS_SUCCEEDED(rv))
     446           0 :         return NS_OK;
     447             :     }
     448             :   }
     449             : 
     450           0 :   return NS_ERROR_FAILURE;
     451             : }
     452             : 
     453             : NS_IMETHODIMP
     454           0 : nsExtensibleStringBundle::FormatStringFromID(int32_t aID,
     455             :                                              const char16_t ** aParams,
     456             :                                              uint32_t aLength,
     457             :                                              char16_t ** aResult)
     458             : {
     459           0 :   nsAutoString idStr;
     460           0 :   idStr.AppendInt(aID, 10);
     461           0 :   return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
     462             : }
     463             : 
     464             : NS_IMETHODIMP
     465           0 : nsExtensibleStringBundle::FormatStringFromName(const char16_t *aName,
     466             :                                                const char16_t ** aParams,
     467             :                                                uint32_t aLength,
     468             :                                                char16_t ** aResult)
     469             : {
     470           0 :   nsXPIDLString formatStr;
     471             :   nsresult rv;
     472           0 :   rv = GetStringFromName(aName, getter_Copies(formatStr));
     473           0 :   if (NS_FAILED(rv))
     474           0 :     return rv;
     475             : 
     476           0 :   return nsStringBundle::FormatString(formatStr, aParams, aLength, aResult);
     477             : }
     478             : 
     479           0 : nsresult nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator ** aResult)
     480             : {
     481             :   // XXX write me
     482           0 :   *aResult = nullptr;
     483           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     484             : }
     485             : 
     486             : /////////////////////////////////////////////////////////////////////////////////////////
     487             : 
     488             : #define MAX_CACHED_BUNDLES 16
     489             : 
     490             : struct bundleCacheEntry_t final : public LinkedListElement<bundleCacheEntry_t> {
     491             :   nsCString mHashKey;
     492             :   nsCOMPtr<nsIStringBundle> mBundle;
     493             : 
     494          24 :   bundleCacheEntry_t()
     495          24 :   {
     496          24 :     MOZ_COUNT_CTOR(bundleCacheEntry_t);
     497          24 :   }
     498             : 
     499           3 :   ~bundleCacheEntry_t()
     500           3 :   {
     501           3 :     MOZ_COUNT_DTOR(bundleCacheEntry_t);
     502           3 :   }
     503             : };
     504             : 
     505             : 
     506           3 : nsStringBundleService::nsStringBundleService() :
     507           3 :   mBundleMap(MAX_CACHED_BUNDLES)
     508             : {
     509           3 :   mErrorService = do_GetService(kErrorServiceCID);
     510           3 :   NS_ASSERTION(mErrorService, "Couldn't get error service");
     511           3 : }
     512             : 
     513         704 : NS_IMPL_ISUPPORTS(nsStringBundleService,
     514             :                   nsIStringBundleService,
     515             :                   nsIObserver,
     516             :                   nsISupportsWeakReference)
     517             : 
     518           0 : nsStringBundleService::~nsStringBundleService()
     519             : {
     520           0 :   flushBundleCache();
     521           0 : }
     522             : 
     523             : nsresult
     524           3 : nsStringBundleService::Init()
     525             : {
     526           6 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     527           3 :   if (os) {
     528           3 :     os->AddObserver(this, "memory-pressure", true);
     529           3 :     os->AddObserver(this, "profile-do-change", true);
     530           3 :     os->AddObserver(this, "chrome-flush-caches", true);
     531           3 :     os->AddObserver(this, "xpcom-category-entry-added", true);
     532           3 :     os->AddObserver(this, "intl:app-locales-changed", true);
     533             :   }
     534             : 
     535             :   // instantiate the override service, if there is any.
     536             :   // at some point we probably want to make this a category, and
     537             :   // support multiple overrides
     538           3 :   mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
     539             : 
     540           6 :   return NS_OK;
     541             : }
     542             : 
     543             : NS_IMETHODIMP
     544           7 : nsStringBundleService::Observe(nsISupports* aSubject,
     545             :                                const char* aTopic,
     546             :                                const char16_t* aSomeData)
     547             : {
     548          14 :   if (strcmp("memory-pressure", aTopic) == 0 ||
     549          13 :       strcmp("profile-do-change", aTopic) == 0 ||
     550          12 :       strcmp("chrome-flush-caches", aTopic) == 0 ||
     551           6 :       strcmp("intl:app-locales-changed", aTopic) == 0)
     552             :   {
     553           3 :     flushBundleCache();
     554             :   }
     555          16 :   else if (strcmp("xpcom-category-entry-added", aTopic) == 0 &&
     556          16 :            NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData))
     557             :   {
     558           0 :     mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
     559             :   }
     560             : 
     561           7 :   return NS_OK;
     562             : }
     563             : 
     564             : void
     565           3 : nsStringBundleService::flushBundleCache()
     566             : {
     567             :   // release all bundles in the cache
     568           3 :   mBundleMap.Clear();
     569             : 
     570           9 :   while (!mBundleCache.isEmpty()) {
     571           3 :     delete mBundleCache.popFirst();
     572             :   }
     573           3 : }
     574             : 
     575             : NS_IMETHODIMP
     576           0 : nsStringBundleService::FlushBundles()
     577             : {
     578           0 :   flushBundleCache();
     579           0 :   return NS_OK;
     580             : }
     581             : 
     582             : void
     583          57 : nsStringBundleService::getStringBundle(const char *aURLSpec,
     584             :                                        nsIStringBundle **aResult)
     585             : {
     586         114 :   nsDependentCString key(aURLSpec);
     587          57 :   bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key);
     588             : 
     589          57 :   if (cacheEntry) {
     590             :     // cache hit!
     591             :     // remove it from the list, it will later be reinserted
     592             :     // at the head of the list
     593          31 :     cacheEntry->remove();
     594             : 
     595             :   } else {
     596             : 
     597             :     // hasn't been cached, so insert it into the hash table
     598          78 :     RefPtr<nsStringBundle> bundle = new nsStringBundle(aURLSpec, mOverrideStrings);
     599          26 :     cacheEntry = insertIntoCache(bundle.forget(), key);
     600             :   }
     601             : 
     602             :   // at this point the cacheEntry should exist in the hashtable,
     603             :   // but is not in the LRU cache.
     604             :   // put the cache entry at the front of the list
     605          57 :   mBundleCache.insertFront(cacheEntry);
     606             : 
     607             :   // finally, return the value
     608          57 :   *aResult = cacheEntry->mBundle;
     609          57 :   NS_ADDREF(*aResult);
     610          57 : }
     611             : 
     612             : bundleCacheEntry_t *
     613          26 : nsStringBundleService::insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
     614             :                                        nsCString &aHashKey)
     615             : {
     616             :   bundleCacheEntry_t *cacheEntry;
     617             : 
     618          26 :   if (mBundleMap.Count() < MAX_CACHED_BUNDLES) {
     619             :     // cache not full - create a new entry
     620          24 :     cacheEntry = new bundleCacheEntry_t();
     621             :   } else {
     622             :     // cache is full
     623             :     // take the last entry in the list, and recycle it.
     624           2 :     cacheEntry = mBundleCache.getLast();
     625             : 
     626             :     // remove it from the hash table and linked list
     627           2 :     NS_ASSERTION(mBundleMap.Contains(cacheEntry->mHashKey),
     628             :                  "Element will not be removed!");
     629           2 :     mBundleMap.Remove(cacheEntry->mHashKey);
     630           2 :     cacheEntry->remove();
     631             :   }
     632             : 
     633             :   // at this point we have a new cacheEntry that doesn't exist
     634             :   // in the hashtable, so set up the cacheEntry
     635          26 :   cacheEntry->mHashKey = aHashKey;
     636          26 :   cacheEntry->mBundle = aBundle;
     637             : 
     638             :   // insert the entry into the cache and map, make it the MRU
     639          26 :   mBundleMap.Put(cacheEntry->mHashKey, cacheEntry);
     640             : 
     641          26 :   return cacheEntry;
     642             : }
     643             : 
     644             : NS_IMETHODIMP
     645          48 : nsStringBundleService::CreateBundle(const char* aURLSpec,
     646             :                                     nsIStringBundle** aResult)
     647             : {
     648          48 :   getStringBundle(aURLSpec,aResult);
     649          48 :   return NS_OK;
     650             : }
     651             : 
     652             : NS_IMETHODIMP
     653           0 : nsStringBundleService::CreateExtensibleBundle(const char* aCategory,
     654             :                                               nsIStringBundle** aResult)
     655             : {
     656           0 :   NS_ENSURE_ARG_POINTER(aResult);
     657           0 :   *aResult = nullptr;
     658             : 
     659           0 :   RefPtr<nsExtensibleStringBundle> bundle = new nsExtensibleStringBundle();
     660             : 
     661           0 :   nsresult res = bundle->Init(aCategory, this);
     662           0 :   if (NS_FAILED(res)) {
     663           0 :     return res;
     664             :   }
     665             : 
     666           0 :   bundle.forget(aResult);
     667           0 :   return NS_OK;
     668             : }
     669             : 
     670             : #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
     671             : 
     672             : nsresult
     673           9 : nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
     674             :                                         uint32_t argCount, char16_t** argArray,
     675             :                                         char16_t* *result)
     676             : {
     677             :   nsresult rv;
     678          18 :   nsXPIDLCString key;
     679             : 
     680             :   // try looking up the error message with the int key:
     681           9 :   uint16_t code = NS_ERROR_GET_CODE(aStatus);
     682           9 :   rv = bundle->FormatStringFromID(code, (const char16_t**)argArray, argCount, result);
     683             : 
     684             :   // If the int key fails, try looking up the default error message. E.g. print:
     685             :   //   An unknown error has occurred (0x804B0003).
     686           9 :   if (NS_FAILED(rv)) {
     687           0 :     nsAutoString statusStr;
     688           0 :     statusStr.AppendInt(static_cast<uint32_t>(aStatus), 16);
     689             :     const char16_t* otherArgArray[1];
     690           0 :     otherArgArray[0] = statusStr.get();
     691           0 :     uint16_t code = NS_ERROR_GET_CODE(NS_ERROR_FAILURE);
     692           0 :     rv = bundle->FormatStringFromID(code, otherArgArray, 1, result);
     693             :   }
     694             : 
     695          18 :   return rv;
     696             : }
     697             : 
     698             : NS_IMETHODIMP
     699           9 : nsStringBundleService::FormatStatusMessage(nsresult aStatus,
     700             :                                            const char16_t* aStatusArg,
     701             :                                            char16_t* *result)
     702             : {
     703             :   nsresult rv;
     704           9 :   uint32_t i, argCount = 0;
     705          18 :   nsCOMPtr<nsIStringBundle> bundle;
     706          18 :   nsXPIDLCString stringBundleURL;
     707             : 
     708             :   // XXX hack for mailnews who has already formatted their messages:
     709           9 :   if (aStatus == NS_OK && aStatusArg) {
     710           0 :     *result = NS_strdup(aStatusArg);
     711           0 :     NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY);
     712           0 :     return NS_OK;
     713             :   }
     714             : 
     715           9 :   if (aStatus == NS_OK) {
     716           0 :     return NS_ERROR_FAILURE;       // no message to format
     717             :   }
     718             : 
     719             :   // format the arguments:
     720          18 :   const nsDependentString args(aStatusArg);
     721           9 :   argCount = args.CountChar(char16_t('\n')) + 1;
     722           9 :   NS_ENSURE_ARG(argCount <= 10); // enforce 10-parameter limit
     723             :   char16_t* argArray[10];
     724             : 
     725             :   // convert the aStatusArg into a char16_t array
     726           9 :   if (argCount == 1) {
     727             :     // avoid construction for the simple case:
     728           9 :     argArray[0] = (char16_t*)aStatusArg;
     729             :   }
     730           0 :   else if (argCount > 1) {
     731           0 :     int32_t offset = 0;
     732           0 :     for (i = 0; i < argCount; i++) {
     733           0 :       int32_t pos = args.FindChar('\n', offset);
     734           0 :       if (pos == -1)
     735           0 :         pos = args.Length();
     736           0 :       argArray[i] = ToNewUnicode(Substring(args, offset, pos - offset));
     737           0 :       if (argArray[i] == nullptr) {
     738           0 :         rv = NS_ERROR_OUT_OF_MEMORY;
     739           0 :         argCount = i - 1; // don't try to free uninitialized memory
     740           0 :         goto done;
     741             :       }
     742           0 :       offset = pos + 1;
     743             :     }
     744             :   }
     745             : 
     746             :   // find the string bundle for the error's module:
     747          18 :   rv = mErrorService->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus),
     748          18 :                                            getter_Copies(stringBundleURL));
     749           9 :   if (NS_SUCCEEDED(rv)) {
     750           9 :     getStringBundle(stringBundleURL, getter_AddRefs(bundle));
     751           9 :     rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
     752             :   }
     753           9 :   if (NS_FAILED(rv)) {
     754           0 :     getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle));
     755           0 :     rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
     756             :   }
     757             : 
     758             : done:
     759           9 :   if (argCount > 1) {
     760           0 :     for (i = 0; i < argCount; i++) {
     761           0 :       if (argArray[i])
     762           0 :         free(argArray[i]);
     763             :     }
     764             :   }
     765           9 :   return rv;
     766             : }

Generated by: LCOV version 1.13