intl/strres/src/nsStringBundle.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial