Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / intl / strres / src / nsStringBundle.cpp
blobb748f3c7fb2c5df9a7d564932da6b0f0cfef9e03
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
13 * License.
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.
22 * Contributor(s):
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"
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 "nsIGenericFactory.h"
58 #include "nsIMemory.h"
59 #include "nsIObserverService.h"
60 #include "pratom.h"
61 #include "prmem.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"
71 // for async loading
72 #ifdef ASYNC_LOADING
73 #include "nsIBinaryInputStream.h"
74 #include "nsIStringStream.h"
75 #endif
77 #include "prenv.h"
78 #include "nsCRT.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),
92 mLoaded(PR_FALSE)
96 nsresult
97 nsStringBundle::LoadProperties()
99 // this is different than mLoaded, because we only want to attempt
100 // to load once
101 // we only want to load once, but if we've tried once and failed,
102 // continue to throw an error!
103 if (mAttemptedLoad) {
104 if (mLoaded)
105 return NS_OK;
107 return NS_ERROR_UNEXPECTED;
110 mAttemptedLoad = PR_TRUE;
112 nsresult rv;
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);
144 return rv;
148 nsresult
149 nsStringBundle::GetStringFromID(PRInt32 aID, nsAString& aResult)
151 nsAutoCMonitor(this);
152 nsCAutoString name;
153 name.AppendInt(aID, 10);
155 nsresult rv;
157 // try override first
158 if (mOverrideStrings) {
159 rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
160 name,
161 aResult);
162 if (NS_SUCCEEDED(rv)) return rv;
165 rv = mProps->GetStringProperty(name, aResult);
167 #ifdef DEBUG_tao_
168 char *s = ToNewCString(aResult);
169 printf("\n** GetStringFromID: aResult=%s, len=%d\n", s?s:"null",
170 aResult.Length());
171 if (s) nsMemory::Free(s);
172 #endif /* DEBUG_tao_ */
174 return rv;
177 nsresult
178 nsStringBundle::GetStringFromName(const nsAString& aName,
179 nsAString& aResult)
181 nsresult rv;
183 // try override first
184 if (mOverrideStrings) {
185 rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
186 NS_ConvertUTF16toUTF8(aName),
187 aResult);
188 if (NS_SUCCEEDED(rv)) return rv;
191 rv = mProps->GetStringProperty(NS_ConvertUTF16toUTF8(aName), aResult);
192 #ifdef DEBUG_tao_
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_ */
200 return rv;
203 NS_IMETHODIMP
204 nsStringBundle::FormatStringFromID(PRInt32 aID,
205 const PRUnichar **aParams,
206 PRUint32 aLength,
207 PRUnichar ** aResult)
209 nsAutoString idStr;
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
216 NS_IMETHODIMP
217 nsStringBundle::FormatStringFromName(const PRUnichar *aName,
218 const PRUnichar **aParams,
219 PRUint32 aLength,
220 PRUnichar **aResult)
222 NS_ENSURE_ARG_POINTER(aName);
223 NS_ASSERTION(aParams && aLength, "FormatStringFromName() without format parameters: use GetStringFromName() instead");
224 NS_ENSURE_ARG_POINTER(aResult);
226 nsresult rv;
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,
239 nsIStringBundle)
241 /* void GetStringFromID (in long aID, out wstring aResult); */
242 NS_IMETHODIMP
243 nsStringBundle::GetStringFromID(PRInt32 aID, PRUnichar **aResult)
245 nsresult rv;
246 rv = LoadProperties();
247 if (NS_FAILED(rv)) return rv;
249 *aResult = nsnull;
250 nsAutoString tmpstr;
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);
258 return NS_OK;
261 /* void GetStringFromName (in wstring aName, out wstring aResult); */
262 NS_IMETHODIMP
263 nsStringBundle::GetStringFromName(const PRUnichar *aName, PRUnichar **aResult)
265 NS_ENSURE_ARG_POINTER(aName);
266 NS_ENSURE_ARG_POINTER(aResult);
268 nsresult rv;
269 rv = LoadProperties();
270 if (NS_FAILED(rv)) return rv;
272 nsAutoCMonitor(this);
273 *aResult = nsnull;
274 nsAutoString tmpstr;
275 rv = GetStringFromName(nsDependentString(aName), tmpstr);
276 if (NS_FAILED(rv))
278 #if 0
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());
283 #endif
284 return rv;
287 *aResult = ToNewUnicode(tmpstr);
288 NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
290 return NS_OK;
293 nsresult
294 nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride* aOverrideStrings,
295 nsISimpleEnumerator** aResult)
297 nsCOMPtr<nsISupports> supports;
298 nsCOMPtr<nsIPropertyElement> propElement;
300 nsresult rv;
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));
311 PRBool hasMore;
312 rv = overrideEnumerator->HasMoreElements(&hasMore);
313 NS_ENSURE_SUCCESS(rv, rv);
314 while (hasMore) {
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));
327 if (NS_FAILED(rv)) {
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
333 do {
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
339 nsCAutoString key;
340 propElement->GetKey(key);
342 nsAutoString value;
343 rv = aOverrideStrings->GetStringFromName(mPropertiesURL, key, value);
345 // if it isn't there, then it is safe to append
346 if (NS_FAILED(rv))
347 resultArray->AppendElement(propElement, PR_FALSE);
350 rv = propEnumerator->HasMoreElements(&hasMore);
351 NS_ENSURE_SUCCESS(rv, rv);
352 } while (hasMore);
354 return resultArray->Enumerate(aResult);
358 NS_IMETHODIMP
359 nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator** elements)
361 if (!elements)
362 return NS_ERROR_INVALID_POINTER;
364 nsresult rv;
365 rv = LoadProperties();
366 if (NS_FAILED(rv)) return rv;
368 if (mOverrideStrings)
369 return GetCombinedEnumeration(mOverrideStrings, elements);
371 return mProps->Enumerate(elements);
374 nsresult
375 nsStringBundle::FormatString(const PRUnichar *aFormatStr,
376 const PRUnichar **aParams, PRUint32 aLength,
377 PRUnichar **aResult)
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
387 // -alecf
388 PRUnichar *text =
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);
401 if (!text) {
402 *aResult = 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()
421 mLoaded = PR_FALSE;
424 nsresult
425 nsExtensibleStringBundle::Init(const char * aCategory,
426 nsIStringBundleService* aBundleService)
429 nsresult rv;
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;
438 PRBool hasMore;
439 while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
440 nsCOMPtr<nsISupports> supports;
441 rv = enumerator->GetNext(getter_AddRefs(supports));
442 if (NS_FAILED(rv))
443 continue;
445 nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports, &rv);
446 if (NS_FAILED(rv))
447 continue;
449 nsCAutoString name;
450 rv = supStr->GetData(name);
451 if (NS_FAILED(rv))
452 continue;
454 nsCOMPtr<nsIStringBundle> bundle;
455 rv = aBundleService->CreateBundle(name.get(), getter_AddRefs(bundle));
456 if (NS_FAILED(rv))
457 continue;
459 mBundles.AppendObject(bundle);
462 return rv;
465 nsExtensibleStringBundle::~nsExtensibleStringBundle()
469 nsresult nsExtensibleStringBundle::GetStringFromID(PRInt32 aID, PRUnichar ** aResult)
471 nsresult rv;
472 const PRUint32 size = mBundles.Count();
473 for (PRUint32 i = 0; i < size; ++i) {
474 nsIStringBundle *bundle = mBundles[i];
475 if (bundle) {
476 rv = bundle->GetStringFromID(aID, aResult);
477 if (NS_SUCCEEDED(rv))
478 return NS_OK;
482 return NS_ERROR_FAILURE;
485 nsresult nsExtensibleStringBundle::GetStringFromName(const PRUnichar *aName,
486 PRUnichar ** aResult)
488 nsresult rv;
489 const PRUint32 size = mBundles.Count();
490 for (PRUint32 i = 0; i < size; ++i) {
491 nsIStringBundle* bundle = mBundles[i];
492 if (bundle) {
493 rv = bundle->GetStringFromName(aName, aResult);
494 if (NS_SUCCEEDED(rv))
495 return NS_OK;
499 return NS_ERROR_FAILURE;
502 NS_IMETHODIMP
503 nsExtensibleStringBundle::FormatStringFromID(PRInt32 aID,
504 const PRUnichar ** aParams,
505 PRUint32 aLength,
506 PRUnichar ** aResult)
508 nsAutoString idStr;
509 idStr.AppendInt(aID, 10);
510 return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
513 NS_IMETHODIMP
514 nsExtensibleStringBundle::FormatStringFromName(const PRUnichar *aName,
515 const PRUnichar ** aParams,
516 PRUint32 aLength,
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)
527 // XXX write me
528 *aResult = NULL;
529 return NS_ERROR_NOT_IMPLEMENTED;
532 /////////////////////////////////////////////////////////////////////////////////////////
534 #define MAX_CACHED_BUNDLES 16
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;
544 nsStringBundleService::nsStringBundleService() :
545 mBundleMap(MAX_CACHED_BUNDLES, PR_TRUE)
547 #ifdef DEBUG_tao_
548 printf("\n++ nsStringBundleService::nsStringBundleService ++\n");
549 #endif
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,
563 nsIObserver,
564 nsISupportsWeakReference)
566 nsStringBundleService::~nsStringBundleService()
568 flushBundleCache();
569 PL_FinishArenaPool(&mCacheEntryPool);
572 nsresult
573 nsStringBundleService::Init()
575 nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
576 if (os) {
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);
588 return NS_OK;
591 NS_IMETHODIMP
592 nsStringBundleService::Observe(nsISupports* aSubject,
593 const char* aTopic,
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)
600 flushBundleCache();
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);
608 return NS_OK;
611 void
612 nsStringBundleService::flushBundleCache()
614 // release all bundles in the cache
615 mBundleMap.Reset();
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);
631 NS_IMETHODIMP
632 nsStringBundleService::FlushBundles()
634 flushBundleCache();
635 return NS_OK;
638 nsresult
639 nsStringBundleService::getStringBundle(const char *aURLSpec,
640 nsIStringBundle **aResult)
642 nsCStringKey completeKey(aURLSpec);
644 bundleCacheEntry_t* cacheEntry =
645 (bundleCacheEntry_t*)mBundleMap.Get(&completeKey);
647 if (cacheEntry) {
648 // cache hit!
649 // remove it from the list, it will later be reinserted
650 // at the head of the list
651 PR_REMOVE_LINK((PRCList*)cacheEntry);
653 } else {
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;
658 NS_ADDREF(bundle);
660 cacheEntry = insertIntoCache(bundle, &completeKey);
661 NS_RELEASE(bundle); // cache should now be holding a ref
662 // in the cacheEntry
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;
673 NS_ADDREF(*aResult);
675 return NS_OK;
678 bundleCacheEntry_t *
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;
691 } else {
692 // cache is full
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!");
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 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);
722 return cacheEntry;
725 void
726 nsStringBundleService::recycleEntry(bundleCacheEntry_t *aEntry)
728 delete aEntry->mHashKey;
729 NS_RELEASE(aEntry->mBundle);
732 NS_IMETHODIMP
733 nsStringBundleService::CreateBundle(const char* aURLSpec,
734 nsIStringBundle** aResult)
736 #ifdef DEBUG_tao_
737 printf("\n++ nsStringBundleService::CreateBundle ++\n");
738 printf("\n** nsStringBundleService::CreateBundle: %s\n", aURLSpec ? aURLSpec : "null");
739 #endif
741 return getStringBundle(aURLSpec,aResult);
744 NS_IMETHODIMP
745 nsStringBundleService::CreateExtensibleBundle(const char* aCategory,
746 nsIStringBundle** aResult)
748 if (aResult == NULL) return NS_ERROR_NULL_POINTER;
750 nsresult res;
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)) {
757 delete bundle;
758 return res;
761 res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult);
762 if (NS_FAILED(res)) delete bundle;
764 return res;
767 #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
769 nsresult
770 nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
771 PRUint32 argCount, PRUnichar** argArray,
772 PRUnichar* *result)
774 nsresult rv;
775 nsXPIDLCString key;
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,
784 argCount, result);
787 // if the string key fails, try looking up the error message with the int key:
788 if (NS_FAILED(rv)) {
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).
795 if (NS_FAILED(rv)) {
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);
803 return rv;
806 NS_IMETHODIMP
807 nsStringBundleService::FormatStatusMessage(nsresult aStatus,
808 const PRUnichar* aStatusArg,
809 PRUnichar* *result)
811 nsresult rv;
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);
820 return NS_OK;
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
834 if (argCount == 1) {
835 // avoid construction for the simple case:
836 argArray[0] = (PRUnichar*)aStatusArg;
838 else if (argCount > 1) {
839 PRInt32 offset = 0;
840 for (i = 0; i < argCount; i++) {
841 PRInt32 pos = args.FindChar('\n', offset);
842 if (pos == -1)
843 pos = args.Length();
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
848 goto done;
850 offset = pos + 1;
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);
863 if (NS_FAILED(rv)) {
864 rv = getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle));
865 if (NS_SUCCEEDED(rv)) {
866 rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
870 done:
871 if (argCount > 1) {
872 for (i = 0; i < argCount; i++) {
873 if (argArray[i])
874 nsMemory::Free(argArray[i]);
877 return rv;