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
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/
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
15 * The Original Code is mozilla.org code.
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.
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.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsStringBundle.h"
41 #include "nsReadableUtils.h"
42 #include "nsIStringBundle.h"
43 #include "nsStringBundleService.h"
44 #include "nsStringBundle.h"
45 #include "nsStringBundleTextOverride.h"
47 #include "nsISupportsPrimitives.h"
48 #include "nsIMutableArray.h"
49 #include "nsArrayEnumerator.h"
51 #include "nsHashtable.h"
54 #include "nsNetUtil.h"
56 #include "nsIComponentManager.h"
57 #include "nsIGenericFactory.h"
58 #include "nsIMemory.h"
59 #include "nsIObserverService.h"
62 #include "nsIModule.h"
63 #include "nsCOMArray.h"
64 #include "nsAutoLock.h"
65 #include "nsTextFormatter.h"
66 #include "nsIErrorService.h"
67 #include "nsITimelineService.h"
68 #include "nsICategoryManager.h"
70 #include "nsPrintfCString.h"
73 #include "nsIBinaryInputStream.h"
74 #include "nsIStringStream.h"
80 static NS_DEFINE_CID(kErrorServiceCID
, NS_ERRORSERVICE_CID
);
81 static NS_DEFINE_CID(kPersistentPropertiesCID
, NS_IPERSISTENTPROPERTIES_CID
);
83 nsStringBundle::~nsStringBundle()
87 nsStringBundle::nsStringBundle(const char* aURLSpec
,
88 nsIStringBundleOverride
* aOverrideStrings
) :
89 mPropertiesURL(aURLSpec
),
90 mOverrideStrings(aOverrideStrings
),
91 mAttemptedLoad(PR_FALSE
),
97 nsStringBundle::LoadProperties()
99 // this is different than mLoaded, because we only want to attempt
101 // we only want to load once, but if we've tried once and failed,
102 // continue to throw an error!
103 if (mAttemptedLoad
) {
107 return NS_ERROR_UNEXPECTED
;
110 mAttemptedLoad
= PR_TRUE
;
114 // do it synchronously
115 nsCOMPtr
<nsIURI
> uri
;
116 rv
= NS_NewURI(getter_AddRefs(uri
), mPropertiesURL
);
117 if (NS_FAILED(rv
)) return rv
;
119 // We don't use NS_OpenURI because we want to tweak the channel
120 nsCOMPtr
<nsIChannel
> channel
;
121 rv
= NS_NewChannel(getter_AddRefs(channel
), uri
);
122 if (NS_FAILED(rv
)) return rv
;
124 // It's a string bundle. We expect a text/plain type, so set that as hint
125 channel
->SetContentType(NS_LITERAL_CSTRING("text/plain"));
127 nsCOMPtr
<nsIInputStream
> in
;
128 rv
= channel
->Open(getter_AddRefs(in
));
129 if (NS_FAILED(rv
)) return rv
;
131 NS_TIMELINE_MARK_FUNCTION("loading properties");
133 NS_ASSERTION(NS_SUCCEEDED(rv
) && in
, "Error in OpenBlockingStream");
134 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && in
, NS_ERROR_FAILURE
);
136 mProps
= do_CreateInstance(kPersistentPropertiesCID
, &rv
);
137 NS_ENSURE_SUCCESS(rv
, rv
);
139 mAttemptedLoad
= mLoaded
= PR_TRUE
;
140 rv
= mProps
->Load(in
);
142 mLoaded
= NS_SUCCEEDED(rv
);
149 nsStringBundle::GetStringFromID(PRInt32 aID
, nsAString
& aResult
)
151 nsAutoCMonitor(this);
153 name
.AppendInt(aID
, 10);
157 // try override first
158 if (mOverrideStrings
) {
159 rv
= mOverrideStrings
->GetStringFromName(mPropertiesURL
,
162 if (NS_SUCCEEDED(rv
)) return rv
;
165 rv
= mProps
->GetStringProperty(name
, aResult
);
168 char *s
= ToNewCString(aResult
);
169 printf("\n** GetStringFromID: aResult=%s, len=%d\n", s
?s
:"null",
171 if (s
) nsMemory::Free(s
);
172 #endif /* DEBUG_tao_ */
178 nsStringBundle::GetStringFromName(const nsAString
& aName
,
183 // try override first
184 if (mOverrideStrings
) {
185 rv
= mOverrideStrings
->GetStringFromName(mPropertiesURL
,
186 NS_ConvertUTF16toUTF8(aName
),
188 if (NS_SUCCEEDED(rv
)) return rv
;
191 rv
= mProps
->GetStringProperty(NS_ConvertUTF16toUTF8(aName
), aResult
);
193 char *s
= ToNewCString(aResult
),
194 *ss
= ToNewCString(aName
);
195 printf("\n** GetStringFromName: aName=%s, aResult=%s, len=%d\n",
196 ss
?ss
:"null", s
?s
:"null", aResult
.Length());
197 if (s
) nsMemory::Free(s
);
198 if (ss
) nsMemory::Free(ss
);
199 #endif /* DEBUG_tao_ */
204 nsStringBundle::FormatStringFromID(PRInt32 aID
,
205 const PRUnichar
**aParams
,
207 PRUnichar
** aResult
)
210 idStr
.AppendInt(aID
, 10);
212 return FormatStringFromName(idStr
.get(), aParams
, aLength
, aResult
);
215 // this function supports at most 10 parameters.. see below for why
217 nsStringBundle::FormatStringFromName(const PRUnichar
*aName
,
218 const PRUnichar
**aParams
,
222 NS_ENSURE_ARG_POINTER(aName
);
223 NS_ASSERTION(aParams
&& aLength
, "FormatStringFromName() without format parameters: use GetStringFromName() instead");
224 NS_ENSURE_ARG_POINTER(aResult
);
227 rv
= LoadProperties();
228 if (NS_FAILED(rv
)) return rv
;
230 nsAutoString formatStr
;
231 rv
= GetStringFromName(nsDependentString(aName
), formatStr
);
232 if (NS_FAILED(rv
)) return rv
;
234 return FormatString(formatStr
.get(), aParams
, aLength
, aResult
);
238 NS_IMPL_THREADSAFE_ISUPPORTS1(nsStringBundle
,
241 /* void GetStringFromID (in long aID, out wstring aResult); */
243 nsStringBundle::GetStringFromID(PRInt32 aID
, PRUnichar
**aResult
)
246 rv
= LoadProperties();
247 if (NS_FAILED(rv
)) return rv
;
252 rv
= GetStringFromID(aID
, tmpstr
);
253 NS_ENSURE_SUCCESS(rv
, rv
);
255 *aResult
= ToNewUnicode(tmpstr
);
256 NS_ENSURE_TRUE(*aResult
, NS_ERROR_OUT_OF_MEMORY
);
261 /* void GetStringFromName (in wstring aName, out wstring aResult); */
263 nsStringBundle::GetStringFromName(const PRUnichar
*aName
, PRUnichar
**aResult
)
265 NS_ENSURE_ARG_POINTER(aName
);
266 NS_ENSURE_ARG_POINTER(aResult
);
269 rv
= LoadProperties();
270 if (NS_FAILED(rv
)) return rv
;
272 nsAutoCMonitor(this);
275 rv
= GetStringFromName(nsDependentString(aName
), tmpstr
);
279 // it is not uncommon for apps to request a string name which may not exist
280 // so be quiet about it.
281 NS_WARNING("String missing from string bundle");
282 printf(" '%s' missing from bundle %s\n", NS_ConvertUTF16toUTF8(aName
).get(), mPropertiesURL
.get());
287 *aResult
= ToNewUnicode(tmpstr
);
288 NS_ENSURE_TRUE(*aResult
, NS_ERROR_OUT_OF_MEMORY
);
294 nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride
* aOverrideStrings
,
295 nsISimpleEnumerator
** aResult
)
297 nsCOMPtr
<nsISupports
> supports
;
298 nsCOMPtr
<nsIPropertyElement
> propElement
;
302 nsCOMPtr
<nsIMutableArray
> resultArray
=
303 do_CreateInstance(NS_ARRAY_CONTRACTID
, &rv
);
304 NS_ENSURE_SUCCESS(rv
, rv
);
306 // first, append the override elements
307 nsCOMPtr
<nsISimpleEnumerator
> overrideEnumerator
;
308 rv
= aOverrideStrings
->EnumerateKeysInBundle(mPropertiesURL
,
309 getter_AddRefs(overrideEnumerator
));
312 rv
= overrideEnumerator
->HasMoreElements(&hasMore
);
313 NS_ENSURE_SUCCESS(rv
, rv
);
316 rv
= overrideEnumerator
->GetNext(getter_AddRefs(supports
));
317 if (NS_SUCCEEDED(rv
))
318 resultArray
->AppendElement(supports
, PR_FALSE
);
320 rv
= overrideEnumerator
->HasMoreElements(&hasMore
);
321 NS_ENSURE_SUCCESS(rv
, rv
);
324 // ok, now we have the override elements in resultArray
325 nsCOMPtr
<nsISimpleEnumerator
> propEnumerator
;
326 rv
= mProps
->Enumerate(getter_AddRefs(propEnumerator
));
328 // no elements in mProps anyway, just return what we have
329 return NS_NewArrayEnumerator(aResult
, resultArray
);
332 // second, append all the elements that are in mProps
334 rv
= propEnumerator
->GetNext(getter_AddRefs(supports
));
335 if (NS_SUCCEEDED(rv
) &&
336 (propElement
= do_QueryInterface(supports
, &rv
))) {
338 // now check if its in the override bundle
340 propElement
->GetKey(key
);
343 rv
= aOverrideStrings
->GetStringFromName(mPropertiesURL
, key
, value
);
345 // if it isn't there, then it is safe to append
347 resultArray
->AppendElement(propElement
, PR_FALSE
);
350 rv
= propEnumerator
->HasMoreElements(&hasMore
);
351 NS_ENSURE_SUCCESS(rv
, rv
);
354 return resultArray
->Enumerate(aResult
);
359 nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator
** elements
)
362 return NS_ERROR_INVALID_POINTER
;
365 rv
= LoadProperties();
366 if (NS_FAILED(rv
)) return rv
;
368 if (mOverrideStrings
)
369 return GetCombinedEnumeration(mOverrideStrings
, elements
);
371 return mProps
->Enumerate(elements
);
375 nsStringBundle::FormatString(const PRUnichar
*aFormatStr
,
376 const PRUnichar
**aParams
, PRUint32 aLength
,
379 NS_ENSURE_ARG_POINTER(aResult
);
380 NS_ENSURE_ARG(aLength
<= 10); // enforce 10-parameter limit
382 // implementation note: you would think you could use vsmprintf
383 // to build up an arbitrary length array.. except that there
384 // is no way to build up a va_list at runtime!
385 // Don't believe me? See:
386 // http://www.eskimo.com/~scs/C-faq/q15.13.html
389 nsTextFormatter::smprintf(aFormatStr
,
390 aLength
>= 1 ? aParams
[0] : nsnull
,
391 aLength
>= 2 ? aParams
[1] : nsnull
,
392 aLength
>= 3 ? aParams
[2] : nsnull
,
393 aLength
>= 4 ? aParams
[3] : nsnull
,
394 aLength
>= 5 ? aParams
[4] : nsnull
,
395 aLength
>= 6 ? aParams
[5] : nsnull
,
396 aLength
>= 7 ? aParams
[6] : nsnull
,
397 aLength
>= 8 ? aParams
[7] : nsnull
,
398 aLength
>= 9 ? aParams
[8] : nsnull
,
399 aLength
>= 10 ? aParams
[9] : nsnull
);
403 return NS_ERROR_OUT_OF_MEMORY
;
406 // nsTextFormatter does not use the shared nsMemory allocator.
407 // Instead it is required to free the memory it allocates using
408 // nsTextFormatter::smprintf_free. Let's instead use nsMemory based
409 // allocation for the result that we give out and free the string
410 // returned by smprintf ourselves!
411 *aResult
= NS_strdup(text
);
412 nsTextFormatter::smprintf_free(text
);
414 return *aResult
? NS_OK
: NS_ERROR_OUT_OF_MEMORY
;
417 NS_IMPL_ISUPPORTS1(nsExtensibleStringBundle
, nsIStringBundle
)
419 nsExtensibleStringBundle::nsExtensibleStringBundle()
425 nsExtensibleStringBundle::Init(const char * aCategory
,
426 nsIStringBundleService
* aBundleService
)
430 nsCOMPtr
<nsICategoryManager
> catman
=
431 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
, &rv
);
432 if (NS_FAILED(rv
)) return rv
;
434 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
435 rv
= catman
->EnumerateCategory(aCategory
, getter_AddRefs(enumerator
));
436 if (NS_FAILED(rv
)) return rv
;
439 while (NS_SUCCEEDED(enumerator
->HasMoreElements(&hasMore
)) && hasMore
) {
440 nsCOMPtr
<nsISupports
> supports
;
441 rv
= enumerator
->GetNext(getter_AddRefs(supports
));
445 nsCOMPtr
<nsISupportsCString
> supStr
= do_QueryInterface(supports
, &rv
);
450 rv
= supStr
->GetData(name
);
454 nsCOMPtr
<nsIStringBundle
> bundle
;
455 rv
= aBundleService
->CreateBundle(name
.get(), getter_AddRefs(bundle
));
459 mBundles
.AppendObject(bundle
);
465 nsExtensibleStringBundle::~nsExtensibleStringBundle()
469 nsresult
nsExtensibleStringBundle::GetStringFromID(PRInt32 aID
, PRUnichar
** aResult
)
472 const PRUint32 size
= mBundles
.Count();
473 for (PRUint32 i
= 0; i
< size
; ++i
) {
474 nsIStringBundle
*bundle
= mBundles
[i
];
476 rv
= bundle
->GetStringFromID(aID
, aResult
);
477 if (NS_SUCCEEDED(rv
))
482 return NS_ERROR_FAILURE
;
485 nsresult
nsExtensibleStringBundle::GetStringFromName(const PRUnichar
*aName
,
486 PRUnichar
** aResult
)
489 const PRUint32 size
= mBundles
.Count();
490 for (PRUint32 i
= 0; i
< size
; ++i
) {
491 nsIStringBundle
* bundle
= mBundles
[i
];
493 rv
= bundle
->GetStringFromName(aName
, aResult
);
494 if (NS_SUCCEEDED(rv
))
499 return NS_ERROR_FAILURE
;
503 nsExtensibleStringBundle::FormatStringFromID(PRInt32 aID
,
504 const PRUnichar
** aParams
,
506 PRUnichar
** aResult
)
509 idStr
.AppendInt(aID
, 10);
510 return FormatStringFromName(idStr
.get(), aParams
, aLength
, aResult
);
514 nsExtensibleStringBundle::FormatStringFromName(const PRUnichar
*aName
,
515 const PRUnichar
** aParams
,
517 PRUnichar
** aResult
)
519 nsXPIDLString formatStr
;
520 GetStringFromName(aName
, getter_Copies(formatStr
));
522 return nsStringBundle::FormatString(formatStr
, aParams
, aLength
, aResult
);
525 nsresult
nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator
** aResult
)
529 return NS_ERROR_NOT_IMPLEMENTED
;
532 /////////////////////////////////////////////////////////////////////////////////////////
534 #define MAX_CACHED_BUNDLES 16
536 struct bundleCacheEntry_t
{
538 nsCStringKey
*mHashKey
;
539 // do not use a nsCOMPtr - this is a struct not a class!
540 nsIStringBundle
* mBundle
;
544 nsStringBundleService::nsStringBundleService() :
545 mBundleMap(MAX_CACHED_BUNDLES
, PR_TRUE
)
548 printf("\n++ nsStringBundleService::nsStringBundleService ++\n");
551 PR_INIT_CLIST(&mBundleCache
);
552 PL_InitArenaPool(&mCacheEntryPool
, "srEntries",
553 sizeof(bundleCacheEntry_t
)*MAX_CACHED_BUNDLES
,
554 sizeof(bundleCacheEntry_t
));
556 mErrorService
= do_GetService(kErrorServiceCID
);
557 NS_ASSERTION(mErrorService
, "Couldn't get error service");
561 NS_IMPL_THREADSAFE_ISUPPORTS3(nsStringBundleService
,
562 nsIStringBundleService
,
564 nsISupportsWeakReference
)
566 nsStringBundleService::~nsStringBundleService()
569 PL_FinishArenaPool(&mCacheEntryPool
);
573 nsStringBundleService::Init()
575 nsCOMPtr
<nsIObserverService
> os
= do_GetService("@mozilla.org/observer-service;1");
577 os
->AddObserver(this, "memory-pressure", PR_TRUE
);
578 os
->AddObserver(this, "profile-do-change", PR_TRUE
);
579 os
->AddObserver(this, "chrome-flush-caches", PR_TRUE
);
580 os
->AddObserver(this, "xpcom-category-entry-added", PR_TRUE
);
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 mOverrideStrings
= do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID
);
592 nsStringBundleService::Observe(nsISupports
* aSubject
,
594 const PRUnichar
* aSomeData
)
596 if (strcmp("memory-pressure", aTopic
) == 0 ||
597 strcmp("profile-do-change", aTopic
) == 0 ||
598 strcmp("chrome-flush-caches", aTopic
) == 0)
602 else if (strcmp("xpcom-category-entry-added", aTopic
) == 0 &&
603 NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData
))
605 mOverrideStrings
= do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID
);
612 nsStringBundleService::flushBundleCache()
614 // release all bundles in the cache
617 PRCList
*current
= PR_LIST_HEAD(&mBundleCache
);
618 while (current
!= &mBundleCache
) {
619 bundleCacheEntry_t
*cacheEntry
= (bundleCacheEntry_t
*)current
;
621 recycleEntry(cacheEntry
);
622 PRCList
*oldItem
= current
;
623 current
= PR_NEXT_LINK(current
);
625 // will be freed in PL_FreeArenaPool
626 PR_REMOVE_LINK(oldItem
);
628 PL_FreeArenaPool(&mCacheEntryPool
);
632 nsStringBundleService::FlushBundles()
639 nsStringBundleService::getStringBundle(const char *aURLSpec
,
640 nsIStringBundle
**aResult
)
642 nsCStringKey
completeKey(aURLSpec
);
644 bundleCacheEntry_t
* cacheEntry
=
645 (bundleCacheEntry_t
*)mBundleMap
.Get(&completeKey
);
649 // remove it from the list, it will later be reinserted
650 // at the head of the list
651 PR_REMOVE_LINK((PRCList
*)cacheEntry
);
655 // hasn't been cached, so insert it into the hash table
656 nsStringBundle
* bundle
= new nsStringBundle(aURLSpec
, mOverrideStrings
);
657 if (!bundle
) return NS_ERROR_OUT_OF_MEMORY
;
660 cacheEntry
= insertIntoCache(bundle
, &completeKey
);
661 NS_RELEASE(bundle
); // cache should now be holding a ref
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
669 PR_INSERT_LINK((PRCList
*)cacheEntry
, &mBundleCache
);
671 // finally, return the value
672 *aResult
= cacheEntry
->mBundle
;
679 nsStringBundleService::insertIntoCache(nsIStringBundle
* aBundle
,
680 nsCStringKey
* aHashKey
)
682 bundleCacheEntry_t
*cacheEntry
;
684 if (mBundleMap
.Count() < MAX_CACHED_BUNDLES
) {
685 // cache not full - create a new entry
687 void *cacheEntryArena
;
688 PL_ARENA_ALLOCATE(cacheEntryArena
, &mCacheEntryPool
, sizeof(bundleCacheEntry_t
));
689 cacheEntry
= (bundleCacheEntry_t
*)cacheEntryArena
;
693 // take the last entry in the list, and recycle it.
694 cacheEntry
= (bundleCacheEntry_t
*)PR_LIST_TAIL(&mBundleCache
);
696 // remove it from the hash table and linked list
697 NS_ASSERTION(mBundleMap
.Exists(cacheEntry
->mHashKey
),
698 "Element will not be removed!");
700 NS_WARNING(nsPrintfCString(300,
701 "Booting %s to make room for %s\n",
702 cacheEntry
->mHashKey
->GetString(),
703 aHashKey
->GetString()).get());
705 mBundleMap
.Remove(cacheEntry
->mHashKey
);
706 PR_REMOVE_LINK((PRCList
*)cacheEntry
);
708 // free up excess memory
709 recycleEntry(cacheEntry
);
712 // at this point we have a new cacheEntry that doesn't exist
713 // in the hashtable, so set up the cacheEntry
714 cacheEntry
->mBundle
= aBundle
;
715 NS_ADDREF(cacheEntry
->mBundle
);
717 cacheEntry
->mHashKey
= (nsCStringKey
*)aHashKey
->Clone();
719 // insert the entry into the cache and map, make it the MRU
720 mBundleMap
.Put(cacheEntry
->mHashKey
, cacheEntry
);
726 nsStringBundleService::recycleEntry(bundleCacheEntry_t
*aEntry
)
728 delete aEntry
->mHashKey
;
729 NS_RELEASE(aEntry
->mBundle
);
733 nsStringBundleService::CreateBundle(const char* aURLSpec
,
734 nsIStringBundle
** aResult
)
737 printf("\n++ nsStringBundleService::CreateBundle ++\n");
738 printf("\n** nsStringBundleService::CreateBundle: %s\n", aURLSpec
? aURLSpec
: "null");
741 return getStringBundle(aURLSpec
,aResult
);
745 nsStringBundleService::CreateExtensibleBundle(const char* aCategory
,
746 nsIStringBundle
** aResult
)
748 if (aResult
== NULL
) return NS_ERROR_NULL_POINTER
;
752 nsExtensibleStringBundle
* bundle
= new nsExtensibleStringBundle();
753 if (!bundle
) return NS_ERROR_OUT_OF_MEMORY
;
755 res
= bundle
->Init(aCategory
, this);
756 if (NS_FAILED(res
)) {
761 res
= bundle
->QueryInterface(NS_GET_IID(nsIStringBundle
), (void**) aResult
);
762 if (NS_FAILED(res
)) delete bundle
;
767 #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
770 nsStringBundleService::FormatWithBundle(nsIStringBundle
* bundle
, nsresult aStatus
,
771 PRUint32 argCount
, PRUnichar
** argArray
,
777 // then find a key into the string bundle for that particular error:
778 rv
= mErrorService
->GetErrorStringBundleKey(aStatus
, getter_Copies(key
));
780 // first try looking up the error message with the string key:
781 if (NS_SUCCEEDED(rv
)) {
782 rv
= bundle
->FormatStringFromName(NS_ConvertASCIItoUTF16(key
).get(),
783 (const PRUnichar
**)argArray
,
787 // if the string key fails, try looking up the error message with the int key:
789 PRUint16 code
= NS_ERROR_GET_CODE(aStatus
);
790 rv
= bundle
->FormatStringFromID(code
, (const PRUnichar
**)argArray
, argCount
, result
);
793 // If the int key fails, try looking up the default error message. E.g. print:
794 // An unknown error has occurred (0x804B0003).
796 nsAutoString statusStr
; statusStr
.AppendInt(aStatus
, 16);
797 const PRUnichar
* otherArgArray
[1];
798 otherArgArray
[0] = statusStr
.get();
799 PRUint16 code
= NS_ERROR_GET_CODE(NS_ERROR_FAILURE
);
800 rv
= bundle
->FormatStringFromID(code
, otherArgArray
, 1, result
);
807 nsStringBundleService::FormatStatusMessage(nsresult aStatus
,
808 const PRUnichar
* aStatusArg
,
812 PRUint32 i
, argCount
= 0;
813 nsCOMPtr
<nsIStringBundle
> bundle
;
814 nsXPIDLCString stringBundleURL
;
816 // XXX hack for mailnews who has already formatted their messages:
817 if (aStatus
== NS_OK
&& aStatusArg
) {
818 *result
= nsCRT::strdup(aStatusArg
);
819 NS_ENSURE_TRUE(*result
, NS_ERROR_OUT_OF_MEMORY
);
823 if (aStatus
== NS_OK
) {
824 return NS_ERROR_FAILURE
; // no message to format
827 // format the arguments:
828 const nsDependentString
args(aStatusArg
);
829 argCount
= args
.CountChar(PRUnichar('\n')) + 1;
830 NS_ENSURE_ARG(argCount
<= 10); // enforce 10-parameter limit
831 PRUnichar
* argArray
[10];
833 // convert the aStatusArg into a PRUnichar array
835 // avoid construction for the simple case:
836 argArray
[0] = (PRUnichar
*)aStatusArg
;
838 else if (argCount
> 1) {
840 for (i
= 0; i
< argCount
; i
++) {
841 PRInt32 pos
= args
.FindChar('\n', offset
);
844 argArray
[i
] = ToNewUnicode(Substring(args
, offset
, pos
- offset
));
845 if (argArray
[i
] == nsnull
) {
846 rv
= NS_ERROR_OUT_OF_MEMORY
;
847 argCount
= i
- 1; // don't try to free uninitialized memory
854 // find the string bundle for the error's module:
855 rv
= mErrorService
->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus
),
856 getter_Copies(stringBundleURL
));
857 if (NS_SUCCEEDED(rv
)) {
858 rv
= getStringBundle(stringBundleURL
, getter_AddRefs(bundle
));
859 if (NS_SUCCEEDED(rv
)) {
860 rv
= FormatWithBundle(bundle
, aStatus
, argCount
, argArray
, result
);
864 rv
= getStringBundle(GLOBAL_PROPERTIES
, getter_AddRefs(bundle
));
865 if (NS_SUCCEEDED(rv
)) {
866 rv
= FormatWithBundle(bundle
, aStatus
, argCount
, argArray
, result
);
872 for (i
= 0; i
< argCount
; i
++) {
874 nsMemory::Free(argArray
[i
]);