LCOV - code coverage report
Current view: directory - intl/strres/src - nsStringBundle.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 351 199 56.7 %
Date: 2012-06-02 Functions: 45 35 77.8 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      26                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsStringBundle.h"
      39                 : #include "nsID.h"
      40                 : #include "nsString.h"
      41                 : #include "nsReadableUtils.h"
      42                 : #include "nsIStringBundle.h"
      43                 : #include "nsStringBundleService.h"
      44                 : #include "nsStringBundle.h"
      45                 : #include "nsStringBundleTextOverride.h"
      46                 : #include "nsXPCOM.h"
      47                 : #include "nsISupportsPrimitives.h"
      48                 : #include "nsIMutableArray.h"
      49                 : #include "nsArrayEnumerator.h"
      50                 : #include "nscore.h"
      51                 : #include "nsHashtable.h"
      52                 : #include "nsMemory.h"
      53                 : #include "plstr.h"
      54                 : #include "nsNetUtil.h"
      55                 : #include "nsIURL.h"
      56                 : #include "nsIComponentManager.h"
      57                 : #include "nsIMemory.h"
      58                 : #include "nsIObserverService.h"
      59                 : #include "pratom.h"
      60                 : #include "prmem.h"
      61                 : #include "nsCOMArray.h"
      62                 : #include "nsTextFormatter.h"
      63                 : #include "nsIErrorService.h"
      64                 : #include "nsICategoryManager.h"
      65                 : 
      66                 : #include "nsPrintfCString.h"
      67                 : // for async loading
      68                 : #ifdef ASYNC_LOADING
      69                 : #include "nsIBinaryInputStream.h"
      70                 : #include "nsIStringStream.h"
      71                 : #endif
      72                 : 
      73                 : #include "prenv.h"
      74                 : #include "nsCRT.h"
      75                 : 
      76                 : using namespace mozilla;
      77                 : 
      78                 : static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID);
      79                 : static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID);
      80                 : 
      81            7980 : nsStringBundle::~nsStringBundle()
      82                 : {
      83           15960 : }
      84                 : 
      85            3997 : nsStringBundle::nsStringBundle(const char* aURLSpec,
      86                 :                                nsIStringBundleOverride* aOverrideStrings) :
      87                 :   mPropertiesURL(aURLSpec),
      88                 :   mOverrideStrings(aOverrideStrings),
      89                 :   mReentrantMonitor("nsStringBundle.mReentrantMonitor"),
      90                 :   mAttemptedLoad(false),
      91            3997 :   mLoaded(false)
      92                 : {
      93            3997 : }
      94                 : 
      95                 : nsresult
      96           24417 : nsStringBundle::LoadProperties()
      97                 : {
      98                 :   // this is different than mLoaded, because we only want to attempt
      99                 :   // to load once
     100                 :   // we only want to load once, but if we've tried once and failed,
     101                 :   // continue to throw an error!
     102           24417 :   if (mAttemptedLoad) {
     103           21866 :     if (mLoaded)
     104           21111 :       return NS_OK;
     105                 :       
     106             755 :     return NS_ERROR_UNEXPECTED;
     107                 :   }
     108                 :   
     109            2551 :   mAttemptedLoad = true;
     110                 : 
     111                 :   nsresult rv;
     112                 : 
     113                 :   // do it synchronously
     114            5102 :   nsCOMPtr<nsIURI> uri;
     115            2551 :   rv = NS_NewURI(getter_AddRefs(uri), mPropertiesURL);
     116            2551 :   if (NS_FAILED(rv)) return rv;
     117                 : 
     118                 :   // We don't use NS_OpenURI because we want to tweak the channel
     119            4590 :   nsCOMPtr<nsIChannel> channel;
     120            2295 :   rv = NS_NewChannel(getter_AddRefs(channel), uri);
     121            2295 :   if (NS_FAILED(rv)) return rv;
     122                 : 
     123                 :   // It's a string bundle.  We expect a text/plain type, so set that as hint
     124            2295 :   channel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
     125                 :   
     126            4590 :   nsCOMPtr<nsIInputStream> in;
     127            2295 :   rv = channel->Open(getter_AddRefs(in));
     128            2295 :   if (NS_FAILED(rv)) return rv;
     129                 : 
     130            2295 :   NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream");
     131            2295 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE);
     132                 :     
     133            2295 :   mProps = do_CreateInstance(kPersistentPropertiesCID, &rv);
     134            2295 :   NS_ENSURE_SUCCESS(rv, rv);
     135                 :   
     136            2295 :   mAttemptedLoad = mLoaded = true;
     137            2295 :   rv = mProps->Load(in);
     138                 : 
     139            2295 :   mLoaded = NS_SUCCEEDED(rv);
     140                 :   
     141            2295 :   return rv;
     142                 : }
     143                 : 
     144                 : 
     145                 : nsresult
     146              65 : nsStringBundle::GetStringFromID(PRInt32 aID, nsAString& aResult)
     147                 : {  
     148             130 :   ReentrantMonitorAutoEnter automon(mReentrantMonitor);
     149             130 :   nsCAutoString name;
     150              65 :   name.AppendInt(aID, 10);
     151                 : 
     152                 :   nsresult rv;
     153                 :   
     154                 :   // try override first
     155              65 :   if (mOverrideStrings) {
     156               0 :     rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
     157                 :                                              name,
     158               0 :                                              aResult);
     159               0 :     if (NS_SUCCEEDED(rv)) return rv;
     160                 :   }
     161                 :   
     162              65 :   rv = mProps->GetStringProperty(name, aResult);
     163                 : 
     164                 : #ifdef DEBUG_tao_
     165                 :   char *s = ToNewCString(aResult);
     166                 :   printf("\n** GetStringFromID: aResult=%s, len=%d\n", s?s:"null", 
     167                 :          aResult.Length());
     168                 :   if (s) nsMemory::Free(s);
     169                 : #endif /* DEBUG_tao_ */
     170                 : 
     171              65 :   return rv;
     172                 : }
     173                 : 
     174                 : nsresult
     175           23340 : nsStringBundle::GetStringFromName(const nsAString& aName,
     176                 :                                   nsAString& aResult)
     177                 : {
     178                 :   nsresult rv;
     179                 : 
     180                 :   // try override first
     181           23340 :   if (mOverrideStrings) {
     182               0 :     rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
     183               0 :                                              NS_ConvertUTF16toUTF8(aName),
     184               0 :                                              aResult);
     185               0 :     if (NS_SUCCEEDED(rv)) return rv;
     186                 :   }
     187                 :   
     188           23340 :   rv = mProps->GetStringProperty(NS_ConvertUTF16toUTF8(aName), aResult);
     189                 : #ifdef DEBUG_tao_
     190                 :   char *s = ToNewCString(aResult),
     191                 :        *ss = ToNewCString(aName);
     192                 :   printf("\n** GetStringFromName: aName=%s, aResult=%s, len=%d\n", 
     193                 :          ss?ss:"null", s?s:"null", aResult.Length());
     194                 :   if (s)  nsMemory::Free(s);
     195                 :   if (ss) nsMemory::Free(ss);
     196                 : #endif /* DEBUG_tao_ */
     197           23340 :   return rv;
     198                 : }
     199                 : 
     200                 : NS_IMETHODIMP
     201               0 : nsStringBundle::FormatStringFromID(PRInt32 aID,
     202                 :                                    const PRUnichar **aParams,
     203                 :                                    PRUint32 aLength,
     204                 :                                    PRUnichar ** aResult)
     205                 : {
     206               0 :   nsAutoString idStr;
     207               0 :   idStr.AppendInt(aID, 10);
     208                 : 
     209               0 :   return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
     210                 : }
     211                 : 
     212                 : // this function supports at most 10 parameters.. see below for why
     213                 : NS_IMETHODIMP
     214             460 : nsStringBundle::FormatStringFromName(const PRUnichar *aName,
     215                 :                                      const PRUnichar **aParams,
     216                 :                                      PRUint32 aLength,
     217                 :                                      PRUnichar **aResult)
     218                 : {
     219             460 :   NS_ENSURE_ARG_POINTER(aName);
     220             460 :   NS_ASSERTION(aParams && aLength, "FormatStringFromName() without format parameters: use GetStringFromName() instead");
     221             460 :   NS_ENSURE_ARG_POINTER(aResult);
     222                 : 
     223                 :   nsresult rv;
     224             460 :   rv = LoadProperties();
     225             460 :   if (NS_FAILED(rv)) return rv;
     226                 :   
     227             920 :   nsAutoString formatStr;
     228             460 :   rv = GetStringFromName(nsDependentString(aName), formatStr);
     229             460 :   if (NS_FAILED(rv)) return rv;
     230                 : 
     231             460 :   return FormatString(formatStr.get(), aParams, aLength, aResult);
     232                 : }
     233                 :                                      
     234                 : 
     235           40023 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsStringBundle,
     236                 :                               nsIStringBundle)
     237                 : 
     238                 : /* void GetStringFromID (in long aID, out wstring aResult); */
     239                 : NS_IMETHODIMP
     240              65 : nsStringBundle::GetStringFromID(PRInt32 aID, PRUnichar **aResult)
     241                 : {
     242                 :   nsresult rv;
     243              65 :   rv = LoadProperties();
     244              65 :   if (NS_FAILED(rv)) return rv;
     245                 :   
     246              65 :   *aResult = nsnull;
     247             130 :   nsAutoString tmpstr;
     248                 : 
     249              65 :   rv = GetStringFromID(aID, tmpstr);
     250              65 :   NS_ENSURE_SUCCESS(rv, rv);
     251                 : 
     252              65 :   *aResult = ToNewUnicode(tmpstr);
     253              65 :   NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
     254                 : 
     255              65 :   return NS_OK;
     256                 : }
     257                 : 
     258                 : /* void GetStringFromName (in wstring aName, out wstring aResult); */
     259                 : NS_IMETHODIMP 
     260           23892 : nsStringBundle::GetStringFromName(const PRUnichar *aName, PRUnichar **aResult)
     261                 : {
     262           23892 :   NS_ENSURE_ARG_POINTER(aName);
     263           23892 :   NS_ENSURE_ARG_POINTER(aResult);
     264                 : 
     265                 :   nsresult rv;
     266           23892 :   rv = LoadProperties();
     267           23892 :   if (NS_FAILED(rv)) return rv;
     268                 : 
     269           45760 :   ReentrantMonitorAutoEnter automon(mReentrantMonitor);
     270           22880 :   *aResult = nsnull;
     271           45760 :   nsAutoString tmpstr;
     272           22880 :   rv = GetStringFromName(nsDependentString(aName), tmpstr);
     273           22880 :   if (NS_FAILED(rv))
     274                 :   {
     275                 : #if 0
     276                 :     // it is not uncommon for apps to request a string name which may not exist
     277                 :     // so be quiet about it. 
     278                 :     NS_WARNING("String missing from string bundle");
     279                 :     printf("  '%s' missing from bundle %s\n", NS_ConvertUTF16toUTF8(aName).get(), mPropertiesURL.get());
     280                 : #endif
     281           16251 :     return rv;
     282                 :   }
     283                 : 
     284            6629 :   *aResult = ToNewUnicode(tmpstr);
     285            6629 :   NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
     286                 : 
     287            6629 :   return NS_OK;
     288                 : }
     289                 : 
     290                 : nsresult
     291               0 : nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride* aOverrideStrings,
     292                 :                                        nsISimpleEnumerator** aResult)
     293                 : {
     294               0 :   nsCOMPtr<nsISupports> supports;
     295               0 :   nsCOMPtr<nsIPropertyElement> propElement;
     296                 :   
     297                 :   nsresult rv;
     298                 : 
     299                 :   nsCOMPtr<nsIMutableArray> resultArray =
     300               0 :     do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
     301               0 :   NS_ENSURE_SUCCESS(rv, rv);
     302                 : 
     303                 :   // first, append the override elements
     304               0 :   nsCOMPtr<nsISimpleEnumerator> overrideEnumerator;
     305                 :   rv = aOverrideStrings->EnumerateKeysInBundle(mPropertiesURL,
     306               0 :                                                getter_AddRefs(overrideEnumerator));
     307                 :   
     308                 :   bool hasMore;
     309               0 :   rv = overrideEnumerator->HasMoreElements(&hasMore);
     310               0 :   NS_ENSURE_SUCCESS(rv, rv);
     311               0 :   while (hasMore) {
     312                 : 
     313               0 :     rv = overrideEnumerator->GetNext(getter_AddRefs(supports));
     314               0 :     if (NS_SUCCEEDED(rv))
     315               0 :       resultArray->AppendElement(supports, false);
     316                 : 
     317               0 :     rv = overrideEnumerator->HasMoreElements(&hasMore);
     318               0 :     NS_ENSURE_SUCCESS(rv, rv);
     319                 :   }
     320                 : 
     321                 :   // ok, now we have the override elements in resultArray
     322               0 :   nsCOMPtr<nsISimpleEnumerator> propEnumerator;
     323               0 :   rv = mProps->Enumerate(getter_AddRefs(propEnumerator));
     324               0 :   if (NS_FAILED(rv)) {
     325                 :     // no elements in mProps anyway, just return what we have
     326               0 :     return NS_NewArrayEnumerator(aResult, resultArray);
     327                 :   }
     328                 : 
     329                 :   // second, append all the elements that are in mProps
     330               0 :   do {
     331               0 :     rv = propEnumerator->GetNext(getter_AddRefs(supports));
     332               0 :     if (NS_SUCCEEDED(rv) &&
     333               0 :         (propElement = do_QueryInterface(supports, &rv))) {
     334                 : 
     335                 :       // now check if its in the override bundle
     336               0 :       nsCAutoString key;
     337               0 :       propElement->GetKey(key);
     338                 : 
     339               0 :       nsAutoString value;
     340               0 :       rv = aOverrideStrings->GetStringFromName(mPropertiesURL, key, value);
     341                 : 
     342                 :       // if it isn't there, then it is safe to append
     343               0 :       if (NS_FAILED(rv))
     344               0 :         resultArray->AppendElement(propElement, false);
     345                 :     }
     346                 : 
     347               0 :     rv = propEnumerator->HasMoreElements(&hasMore);
     348               0 :     NS_ENSURE_SUCCESS(rv, rv);
     349                 :   } while (hasMore);
     350                 : 
     351               0 :   return resultArray->Enumerate(aResult);
     352                 : }
     353                 :                                 
     354                 : 
     355                 : NS_IMETHODIMP
     356               0 : nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator** elements)
     357                 : {
     358               0 :   if (!elements)
     359               0 :     return NS_ERROR_INVALID_POINTER;
     360                 : 
     361                 :   nsresult rv;
     362               0 :   rv = LoadProperties();
     363               0 :   if (NS_FAILED(rv)) return rv;
     364                 :   
     365               0 :   if (mOverrideStrings)
     366               0 :       return GetCombinedEnumeration(mOverrideStrings, elements);
     367                 :   
     368               0 :   return mProps->Enumerate(elements);
     369                 : }
     370                 : 
     371                 : nsresult
     372             460 : nsStringBundle::FormatString(const PRUnichar *aFormatStr,
     373                 :                              const PRUnichar **aParams, PRUint32 aLength,
     374                 :                              PRUnichar **aResult)
     375                 : {
     376             460 :   NS_ENSURE_ARG_POINTER(aResult);
     377             460 :   NS_ENSURE_ARG(aLength <= 10); // enforce 10-parameter limit
     378                 : 
     379                 :   // implementation note: you would think you could use vsmprintf
     380                 :   // to build up an arbitrary length array.. except that there
     381                 :   // is no way to build up a va_list at runtime!
     382                 :   // Don't believe me? See:
     383                 :   //   http://www.eskimo.com/~scs/C-faq/q15.13.html
     384                 :   // -alecf
     385                 :   PRUnichar *text = 
     386                 :     nsTextFormatter::smprintf(aFormatStr,
     387                 :                               aLength >= 1 ? aParams[0] : nsnull,
     388             249 :                               aLength >= 2 ? aParams[1] : nsnull,
     389             133 :                               aLength >= 3 ? aParams[2] : nsnull,
     390              43 :                               aLength >= 4 ? aParams[3] : nsnull,
     391               0 :                               aLength >= 5 ? aParams[4] : nsnull,
     392               0 :                               aLength >= 6 ? aParams[5] : nsnull,
     393               0 :                               aLength >= 7 ? aParams[6] : nsnull,
     394               0 :                               aLength >= 8 ? aParams[7] : nsnull,
     395               0 :                               aLength >= 9 ? aParams[8] : nsnull,
     396             885 :                               aLength >= 10 ? aParams[9] : nsnull);
     397                 : 
     398             460 :   if (!text) {
     399               0 :     *aResult = nsnull;
     400               0 :     return NS_ERROR_OUT_OF_MEMORY;
     401                 :   }
     402                 : 
     403                 :   // nsTextFormatter does not use the shared nsMemory allocator.
     404                 :   // Instead it is required to free the memory it allocates using
     405                 :   // nsTextFormatter::smprintf_free.  Let's instead use nsMemory based
     406                 :   // allocation for the result that we give out and free the string
     407                 :   // returned by smprintf ourselves!
     408             460 :   *aResult = NS_strdup(text);
     409             460 :   nsTextFormatter::smprintf_free(text);
     410                 : 
     411             460 :   return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     412                 : }
     413                 : 
     414            1716 : NS_IMPL_ISUPPORTS1(nsExtensibleStringBundle, nsIStringBundle)
     415                 : 
     416             572 : nsExtensibleStringBundle::nsExtensibleStringBundle()
     417                 : {
     418             572 :   mLoaded = false;
     419             572 : }
     420                 : 
     421                 : nsresult
     422             572 : nsExtensibleStringBundle::Init(const char * aCategory,
     423                 :                                nsIStringBundleService* aBundleService) 
     424                 : {
     425                 : 
     426                 :   nsresult rv;
     427                 :   nsCOMPtr<nsICategoryManager> catman =
     428            1144 :     do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
     429             572 :   if (NS_FAILED(rv)) return rv;
     430                 : 
     431            1144 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
     432             572 :   rv = catman->EnumerateCategory(aCategory, getter_AddRefs(enumerator));
     433             572 :   if (NS_FAILED(rv)) return rv;
     434                 : 
     435                 :   bool hasMore;
     436            1716 :   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
     437            1144 :     nsCOMPtr<nsISupports> supports;
     438             572 :     rv = enumerator->GetNext(getter_AddRefs(supports));
     439             572 :     if (NS_FAILED(rv))
     440               0 :       continue;
     441                 : 
     442            1144 :     nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports, &rv);
     443             572 :     if (NS_FAILED(rv))
     444               0 :       continue;
     445                 : 
     446            1144 :     nsCAutoString name;
     447             572 :     rv = supStr->GetData(name);
     448             572 :     if (NS_FAILED(rv))
     449               0 :       continue;
     450                 : 
     451            1144 :     nsCOMPtr<nsIStringBundle> bundle;
     452             572 :     rv = aBundleService->CreateBundle(name.get(), getter_AddRefs(bundle));
     453             572 :     if (NS_FAILED(rv))
     454               0 :       continue;
     455                 : 
     456            1144 :     mBundles.AppendObject(bundle);
     457                 :   }
     458                 : 
     459             572 :   return rv;
     460                 : }
     461                 : 
     462            1144 : nsExtensibleStringBundle::~nsExtensibleStringBundle() 
     463                 : {
     464            2288 : }
     465                 : 
     466               0 : nsresult nsExtensibleStringBundle::GetStringFromID(PRInt32 aID, PRUnichar ** aResult)
     467                 : {
     468                 :   nsresult rv;
     469               0 :   const PRUint32 size = mBundles.Count();
     470               0 :   for (PRUint32 i = 0; i < size; ++i) {
     471               0 :     nsIStringBundle *bundle = mBundles[i];
     472               0 :     if (bundle) {
     473               0 :       rv = bundle->GetStringFromID(aID, aResult);
     474               0 :       if (NS_SUCCEEDED(rv))
     475               0 :         return NS_OK;
     476                 :     }
     477                 :   }
     478                 : 
     479               0 :   return NS_ERROR_FAILURE;
     480                 : }
     481                 : 
     482           15646 : nsresult nsExtensibleStringBundle::GetStringFromName(const PRUnichar *aName,
     483                 :                                                      PRUnichar ** aResult)
     484                 : {
     485                 :   nsresult rv;
     486           15646 :   const PRUint32 size = mBundles.Count();
     487           31179 :   for (PRUint32 i = 0; i < size; ++i) {
     488           15646 :     nsIStringBundle* bundle = mBundles[i];
     489           15646 :     if (bundle) {
     490           15646 :       rv = bundle->GetStringFromName(aName, aResult);
     491           15646 :       if (NS_SUCCEEDED(rv))
     492             113 :         return NS_OK;
     493                 :     }
     494                 :   }
     495                 : 
     496           15533 :   return NS_ERROR_FAILURE;
     497                 : }
     498                 : 
     499                 : NS_IMETHODIMP
     500               0 : nsExtensibleStringBundle::FormatStringFromID(PRInt32 aID,
     501                 :                                              const PRUnichar ** aParams,
     502                 :                                              PRUint32 aLength,
     503                 :                                              PRUnichar ** aResult)
     504                 : {
     505               0 :   nsAutoString idStr;
     506               0 :   idStr.AppendInt(aID, 10);
     507               0 :   return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
     508                 : }
     509                 : 
     510                 : NS_IMETHODIMP
     511               0 : nsExtensibleStringBundle::FormatStringFromName(const PRUnichar *aName,
     512                 :                                                const PRUnichar ** aParams,
     513                 :                                                PRUint32 aLength,
     514                 :                                                PRUnichar ** aResult)
     515                 : {
     516               0 :   nsXPIDLString formatStr;
     517                 :   nsresult rv;
     518               0 :   rv = GetStringFromName(aName, getter_Copies(formatStr));
     519               0 :   if (NS_FAILED(rv))
     520               0 :     return rv;
     521                 : 
     522               0 :   return nsStringBundle::FormatString(formatStr, aParams, aLength, aResult);
     523                 : }
     524                 : 
     525               0 : nsresult nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator ** aResult)
     526                 : {
     527                 :   // XXX write me
     528               0 :   *aResult = NULL;
     529               0 :   return NS_ERROR_NOT_IMPLEMENTED;
     530                 : }
     531                 : 
     532                 : /////////////////////////////////////////////////////////////////////////////////////////
     533                 : 
     534                 : #define MAX_CACHED_BUNDLES 16
     535                 : 
     536                 : struct bundleCacheEntry_t {
     537                 :   PRCList list;
     538                 :   nsCStringKey *mHashKey;
     539                 :   // do not use a nsCOMPtr - this is a struct not a class!
     540                 :   nsIStringBundle* mBundle;
     541                 : };
     542                 : 
     543                 : 
     544            1404 : nsStringBundleService::nsStringBundleService() :
     545            1404 :   mBundleMap(MAX_CACHED_BUNDLES, true)
     546                 : {
     547                 : #ifdef DEBUG_tao_
     548                 :   printf("\n++ nsStringBundleService::nsStringBundleService ++\n");
     549                 : #endif
     550                 : 
     551            1404 :   PR_INIT_CLIST(&mBundleCache);
     552                 :   PL_InitArenaPool(&mCacheEntryPool, "srEntries",
     553                 :                    sizeof(bundleCacheEntry_t)*MAX_CACHED_BUNDLES,
     554            1404 :                    sizeof(bundleCacheEntry_t));
     555                 : 
     556            1404 :   mErrorService = do_GetService(kErrorServiceCID);
     557            1404 :   NS_ASSERTION(mErrorService, "Couldn't get error service");
     558                 : 
     559            1404 : }
     560                 : 
     561           60099 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsStringBundleService,
     562                 :                               nsIStringBundleService,
     563                 :                               nsIObserver,
     564                 :                               nsISupportsWeakReference)
     565                 : 
     566            4212 : nsStringBundleService::~nsStringBundleService()
     567                 : {
     568            1404 :   flushBundleCache();
     569            1404 :   PL_FinishArenaPool(&mCacheEntryPool);
     570            5616 : }
     571                 : 
     572                 : nsresult
     573            1404 : nsStringBundleService::Init()
     574                 : {
     575            2808 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     576            1404 :   if (os) {
     577            1404 :     os->AddObserver(this, "memory-pressure", true);
     578            1404 :     os->AddObserver(this, "profile-do-change", true);
     579            1404 :     os->AddObserver(this, "chrome-flush-caches", true);
     580            1404 :     os->AddObserver(this, "xpcom-category-entry-added", true);
     581                 :   }
     582                 : 
     583                 :   // instantiate the override service, if there is any.
     584                 :   // at some point we probably want to make this a category, and
     585                 :   // support multiple overrides
     586            1404 :   mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
     587                 :   
     588            1404 :   return NS_OK;
     589                 : }
     590                 : 
     591                 : NS_IMETHODIMP
     592             655 : nsStringBundleService::Observe(nsISupports* aSubject,
     593                 :                                const char* aTopic,
     594                 :                                const PRUnichar* aSomeData)
     595                 : {
     596            1965 :   if (strcmp("memory-pressure", aTopic) == 0 ||
     597             655 :       strcmp("profile-do-change", aTopic) == 0 ||
     598             655 :       strcmp("chrome-flush-caches", aTopic) == 0)
     599                 :   {
     600             456 :     flushBundleCache();
     601                 :   }
     602             796 :   else if (strcmp("xpcom-category-entry-added", aTopic) == 0 &&
     603             597 :            NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData)) 
     604                 :   {
     605               0 :     mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
     606                 :   }
     607                 :   
     608             655 :   return NS_OK;
     609                 : }
     610                 : 
     611                 : void
     612            1860 : nsStringBundleService::flushBundleCache()
     613                 : {
     614                 :   // release all bundles in the cache
     615            1860 :   mBundleMap.Reset();
     616                 :   
     617            1860 :   PRCList *current = PR_LIST_HEAD(&mBundleCache);
     618            7717 :   while (current != &mBundleCache) {
     619            3997 :     bundleCacheEntry_t *cacheEntry = (bundleCacheEntry_t*)current;
     620                 : 
     621            3997 :     recycleEntry(cacheEntry);
     622            3997 :     PRCList *oldItem = current;
     623            3997 :     current = PR_NEXT_LINK(current);
     624                 :     
     625                 :     // will be freed in PL_FreeArenaPool
     626            3997 :     PR_REMOVE_LINK(oldItem);
     627                 :   }
     628            1860 :   PL_FreeArenaPool(&mCacheEntryPool);
     629            1860 : }
     630                 : 
     631                 : NS_IMETHODIMP
     632               0 : nsStringBundleService::FlushBundles()
     633                 : {
     634               0 :   flushBundleCache();
     635               0 :   return NS_OK;
     636                 : }
     637                 : 
     638                 : nsresult
     639            5092 : nsStringBundleService::getStringBundle(const char *aURLSpec,
     640                 :                                        nsIStringBundle **aResult)
     641                 : {
     642           10184 :   nsCStringKey completeKey(aURLSpec);
     643                 : 
     644                 :   bundleCacheEntry_t* cacheEntry =
     645            5092 :     (bundleCacheEntry_t*)mBundleMap.Get(&completeKey);
     646                 :   
     647            5092 :   if (cacheEntry) {
     648                 :     // cache hit!
     649                 :     // remove it from the list, it will later be reinserted
     650                 :     // at the head of the list
     651            1095 :     PR_REMOVE_LINK((PRCList*)cacheEntry);
     652                 :     
     653                 :   } else {
     654                 : 
     655                 :     // hasn't been cached, so insert it into the hash table
     656            7994 :     nsStringBundle* bundle = new nsStringBundle(aURLSpec, mOverrideStrings);
     657            3997 :     if (!bundle) return NS_ERROR_OUT_OF_MEMORY;
     658            3997 :     NS_ADDREF(bundle);
     659                 :     
     660            3997 :     cacheEntry = insertIntoCache(bundle, &completeKey);
     661            3997 :     NS_RELEASE(bundle);         // cache should now be holding a ref
     662                 :                                 // in the cacheEntry
     663                 :   }
     664                 : 
     665                 :   // at this point the cacheEntry should exist in the hashtable,
     666                 :   // but is not in the LRU cache.
     667                 :   // put the cache entry at the front of the list
     668                 :   
     669            5092 :   PR_INSERT_LINK((PRCList *)cacheEntry, &mBundleCache);
     670                 : 
     671                 :   // finally, return the value
     672            5092 :   *aResult = cacheEntry->mBundle;
     673            5092 :   NS_ADDREF(*aResult);
     674                 : 
     675            5092 :   return NS_OK;
     676                 : }
     677                 : 
     678                 : bundleCacheEntry_t *
     679            3997 : nsStringBundleService::insertIntoCache(nsIStringBundle* aBundle,
     680                 :                                        nsCStringKey* aHashKey)
     681                 : {
     682                 :   bundleCacheEntry_t *cacheEntry;
     683                 :   
     684            3997 :   if (mBundleMap.Count() < MAX_CACHED_BUNDLES) {
     685                 :     // cache not full - create a new entry
     686                 :     
     687                 :     void *cacheEntryArena;
     688            3997 :     PL_ARENA_ALLOCATE(cacheEntryArena, &mCacheEntryPool, sizeof(bundleCacheEntry_t));
     689            3997 :     cacheEntry = (bundleCacheEntry_t*)cacheEntryArena;
     690                 :       
     691                 :   } else {
     692                 :     // cache is full
     693                 :     // take the last entry in the list, and recycle it.
     694               0 :     cacheEntry = (bundleCacheEntry_t*)PR_LIST_TAIL(&mBundleCache);
     695                 :       
     696                 :     // remove it from the hash table and linked list
     697               0 :     NS_ASSERTION(mBundleMap.Exists(cacheEntry->mHashKey),
     698                 :                  "Element will not be removed!");
     699                 : #ifdef DEBUG_alecf
     700                 :     NS_WARNING(nsPrintfCString(300,
     701                 :                                "Booting %s to make room for %s\n",
     702                 :                                cacheEntry->mHashKey->GetString(),
     703                 :                                aHashKey->GetString()).get());
     704                 : #endif
     705               0 :     mBundleMap.Remove(cacheEntry->mHashKey);
     706               0 :     PR_REMOVE_LINK((PRCList*)cacheEntry);
     707                 : 
     708                 :     // free up excess memory
     709               0 :     recycleEntry(cacheEntry);
     710                 :   }
     711                 :     
     712                 :   // at this point we have a new cacheEntry that doesn't exist
     713                 :   // in the hashtable, so set up the cacheEntry
     714            3997 :   cacheEntry->mBundle = aBundle;
     715            3997 :   NS_ADDREF(cacheEntry->mBundle);
     716                 : 
     717            3997 :   cacheEntry->mHashKey = (nsCStringKey*)aHashKey->Clone();
     718                 :   
     719                 :   // insert the entry into the cache and map, make it the MRU
     720            3997 :   mBundleMap.Put(cacheEntry->mHashKey, cacheEntry);
     721                 : 
     722            3997 :   return cacheEntry;
     723                 : }
     724                 : 
     725                 : void
     726            3997 : nsStringBundleService::recycleEntry(bundleCacheEntry_t *aEntry)
     727                 : {
     728            3997 :   delete aEntry->mHashKey;
     729            3997 :   NS_RELEASE(aEntry->mBundle);
     730            3997 : }
     731                 : 
     732                 : NS_IMETHODIMP
     733            5092 : nsStringBundleService::CreateBundle(const char* aURLSpec, 
     734                 :                                     nsIStringBundle** aResult)
     735                 : {
     736                 : #ifdef DEBUG_tao_
     737                 :   printf("\n++ nsStringBundleService::CreateBundle ++\n");
     738                 :   printf("\n** nsStringBundleService::CreateBundle: %s\n", aURLSpec ? aURLSpec : "null");
     739                 : #endif
     740                 : 
     741            5092 :   return getStringBundle(aURLSpec,aResult);
     742                 : }
     743                 : 
     744                 : NS_IMETHODIMP
     745             572 : nsStringBundleService::CreateExtensibleBundle(const char* aCategory,
     746                 :                                               nsIStringBundle** aResult)
     747                 : {
     748             572 :   if (aResult == NULL) return NS_ERROR_NULL_POINTER;
     749                 : 
     750                 :   nsresult res;
     751                 : 
     752             572 :   nsExtensibleStringBundle * bundle = new nsExtensibleStringBundle();
     753             572 :   if (!bundle) return NS_ERROR_OUT_OF_MEMORY;
     754                 : 
     755             572 :   res = bundle->Init(aCategory, this);
     756             572 :   if (NS_FAILED(res)) {
     757               0 :     delete bundle;
     758               0 :     return res;
     759                 :   }
     760                 : 
     761             572 :   res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult);
     762             572 :   if (NS_FAILED(res)) delete bundle;
     763                 : 
     764             572 :   return res;
     765                 : }
     766                 : 
     767                 : #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
     768                 : 
     769                 : nsresult
     770               0 : nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
     771                 :                                         PRUint32 argCount, PRUnichar** argArray,
     772                 :                                         PRUnichar* *result)
     773                 : {
     774                 :   nsresult rv;
     775               0 :   nsXPIDLCString key;
     776                 : 
     777                 :   // then find a key into the string bundle for that particular error:
     778               0 :   rv = mErrorService->GetErrorStringBundleKey(aStatus, getter_Copies(key));
     779                 : 
     780                 :   // first try looking up the error message with the string key:
     781               0 :   if (NS_SUCCEEDED(rv)) {
     782               0 :     rv = bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(key).get(),
     783                 :                                       (const PRUnichar**)argArray, 
     784               0 :                                       argCount, result);
     785                 :   }
     786                 : 
     787                 :   // if the string key fails, try looking up the error message with the int key:
     788               0 :   if (NS_FAILED(rv)) {
     789               0 :     PRUint16 code = NS_ERROR_GET_CODE(aStatus);
     790               0 :     rv = bundle->FormatStringFromID(code, (const PRUnichar**)argArray, argCount, result);
     791                 :   }
     792                 : 
     793                 :   // If the int key fails, try looking up the default error message. E.g. print:
     794                 :   //   An unknown error has occurred (0x804B0003).
     795               0 :   if (NS_FAILED(rv)) {
     796               0 :     nsAutoString statusStr; statusStr.AppendInt(aStatus, 16);
     797                 :     const PRUnichar* otherArgArray[1];
     798               0 :     otherArgArray[0] = statusStr.get();
     799               0 :     PRUint16 code = NS_ERROR_GET_CODE(NS_ERROR_FAILURE);
     800               0 :     rv = bundle->FormatStringFromID(code, otherArgArray, 1, result);
     801                 :   }
     802                 : 
     803               0 :   return rv;
     804                 : }
     805                 : 
     806                 : NS_IMETHODIMP
     807               0 : nsStringBundleService::FormatStatusMessage(nsresult aStatus,
     808                 :                                            const PRUnichar* aStatusArg,
     809                 :                                            PRUnichar* *result)
     810                 : {
     811                 :   nsresult rv;
     812               0 :   PRUint32 i, argCount = 0;
     813               0 :   nsCOMPtr<nsIStringBundle> bundle;
     814               0 :   nsXPIDLCString stringBundleURL;
     815                 : 
     816                 :   // XXX hack for mailnews who has already formatted their messages:
     817               0 :   if (aStatus == NS_OK && aStatusArg) {
     818               0 :     *result = nsCRT::strdup(aStatusArg);
     819               0 :     NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY);
     820               0 :     return NS_OK;
     821                 :   }
     822                 : 
     823               0 :   if (aStatus == NS_OK) {
     824               0 :     return NS_ERROR_FAILURE;       // no message to format
     825                 :   }
     826                 : 
     827                 :   // format the arguments:
     828               0 :   const nsDependentString args(aStatusArg);
     829               0 :   argCount = args.CountChar(PRUnichar('\n')) + 1;
     830               0 :   NS_ENSURE_ARG(argCount <= 10); // enforce 10-parameter limit
     831                 :   PRUnichar* argArray[10];
     832                 : 
     833                 :   // convert the aStatusArg into a PRUnichar array
     834               0 :   if (argCount == 1) {
     835                 :     // avoid construction for the simple case:
     836               0 :     argArray[0] = (PRUnichar*)aStatusArg;
     837                 :   }
     838               0 :   else if (argCount > 1) {
     839               0 :     PRInt32 offset = 0;
     840               0 :     for (i = 0; i < argCount; i++) {
     841               0 :       PRInt32 pos = args.FindChar('\n', offset);
     842               0 :       if (pos == -1) 
     843               0 :         pos = args.Length();
     844               0 :       argArray[i] = ToNewUnicode(Substring(args, offset, pos - offset));
     845               0 :       if (argArray[i] == nsnull) {
     846               0 :         rv = NS_ERROR_OUT_OF_MEMORY;
     847               0 :         argCount = i - 1; // don't try to free uninitialized memory
     848               0 :         goto done;
     849                 :       }
     850               0 :       offset = pos + 1;
     851                 :     }
     852                 :   }
     853                 : 
     854                 :   // find the string bundle for the error's module:
     855               0 :   rv = mErrorService->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus), 
     856               0 :                                            getter_Copies(stringBundleURL));
     857               0 :   if (NS_SUCCEEDED(rv)) {
     858               0 :     rv = getStringBundle(stringBundleURL, getter_AddRefs(bundle));
     859               0 :     if (NS_SUCCEEDED(rv)) {
     860               0 :       rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
     861                 :     }
     862                 :   }
     863               0 :   if (NS_FAILED(rv)) {
     864               0 :     rv = getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle));
     865               0 :     if (NS_SUCCEEDED(rv)) {
     866               0 :       rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
     867                 :     }
     868                 :   }
     869                 : 
     870                 : done:
     871               0 :   if (argCount > 1) {
     872               0 :     for (i = 0; i < argCount; i++) {
     873               0 :       if (argArray[i])
     874               0 :         nsMemory::Free(argArray[i]);
     875                 :     }
     876                 :   }
     877               0 :   return rv;
     878                 : }
     879                 : 

Generated by: LCOV version 1.7