michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsStringBundle.h" michael@0: #include "nsID.h" michael@0: #include "nsString.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsStringBundleService.h" michael@0: #include "nsStringBundleTextOverride.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nscore.h" michael@0: #include "nsHashtable.h" michael@0: #include "nsMemory.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsTextFormatter.h" michael@0: #include "nsIErrorService.h" michael@0: #include "nsICategoryManager.h" michael@0: michael@0: // for async loading michael@0: #ifdef ASYNC_LOADING michael@0: #include "nsIBinaryInputStream.h" michael@0: #include "nsIStringStream.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID); michael@0: michael@0: nsStringBundle::~nsStringBundle() michael@0: { michael@0: } michael@0: michael@0: nsStringBundle::nsStringBundle(const char* aURLSpec, michael@0: nsIStringBundleOverride* aOverrideStrings) : michael@0: mPropertiesURL(aURLSpec), michael@0: mOverrideStrings(aOverrideStrings), michael@0: mReentrantMonitor("nsStringBundle.mReentrantMonitor"), michael@0: mAttemptedLoad(false), michael@0: mLoaded(false) michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsStringBundle::LoadProperties() michael@0: { michael@0: // this is different than mLoaded, because we only want to attempt michael@0: // to load once michael@0: // we only want to load once, but if we've tried once and failed, michael@0: // continue to throw an error! michael@0: if (mAttemptedLoad) { michael@0: if (mLoaded) michael@0: return NS_OK; michael@0: michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: mAttemptedLoad = true; michael@0: michael@0: nsresult rv; michael@0: michael@0: // do it synchronously michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), mPropertiesURL); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // We don't use NS_OpenURI because we want to tweak the channel michael@0: nsCOMPtr channel; michael@0: rv = NS_NewChannel(getter_AddRefs(channel), uri); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // It's a string bundle. We expect a text/plain type, so set that as hint michael@0: channel->SetContentType(NS_LITERAL_CSTRING("text/plain")); michael@0: michael@0: nsCOMPtr in; michael@0: rv = channel->Open(getter_AddRefs(in)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream"); michael@0: NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE); michael@0: michael@0: static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID); michael@0: mProps = do_CreateInstance(kPersistentPropertiesCID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mAttemptedLoad = mLoaded = true; michael@0: rv = mProps->Load(in); michael@0: michael@0: mLoaded = NS_SUCCEEDED(rv); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsStringBundle::GetStringFromID(int32_t aID, nsAString& aResult) michael@0: { michael@0: ReentrantMonitorAutoEnter automon(mReentrantMonitor); michael@0: nsAutoCString name; michael@0: name.AppendInt(aID, 10); michael@0: michael@0: nsresult rv; michael@0: michael@0: // try override first michael@0: if (mOverrideStrings) { michael@0: rv = mOverrideStrings->GetStringFromName(mPropertiesURL, michael@0: name, michael@0: aResult); michael@0: if (NS_SUCCEEDED(rv)) return rv; michael@0: } michael@0: michael@0: rv = mProps->GetStringProperty(name, aResult); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsStringBundle::GetStringFromName(const nsAString& aName, michael@0: nsAString& aResult) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // try override first michael@0: if (mOverrideStrings) { michael@0: rv = mOverrideStrings->GetStringFromName(mPropertiesURL, michael@0: NS_ConvertUTF16toUTF8(aName), michael@0: aResult); michael@0: if (NS_SUCCEEDED(rv)) return rv; michael@0: } michael@0: michael@0: rv = mProps->GetStringProperty(NS_ConvertUTF16toUTF8(aName), aResult); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundle::FormatStringFromID(int32_t aID, michael@0: const char16_t **aParams, michael@0: uint32_t aLength, michael@0: char16_t ** aResult) michael@0: { michael@0: nsAutoString idStr; michael@0: idStr.AppendInt(aID, 10); michael@0: michael@0: return FormatStringFromName(idStr.get(), aParams, aLength, aResult); michael@0: } michael@0: michael@0: // this function supports at most 10 parameters.. see below for why michael@0: NS_IMETHODIMP michael@0: nsStringBundle::FormatStringFromName(const char16_t *aName, michael@0: const char16_t **aParams, michael@0: uint32_t aLength, michael@0: char16_t **aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aName); michael@0: NS_ASSERTION(aParams && aLength, "FormatStringFromName() without format parameters: use GetStringFromName() instead"); michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: michael@0: nsresult rv; michael@0: rv = LoadProperties(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoString formatStr; michael@0: rv = GetStringFromName(nsDependentString(aName), formatStr); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return FormatString(formatStr.get(), aParams, aLength, aResult); michael@0: } michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsStringBundle, nsIStringBundle) michael@0: michael@0: /* void GetStringFromID (in long aID, out wstring aResult); */ michael@0: NS_IMETHODIMP michael@0: nsStringBundle::GetStringFromID(int32_t aID, char16_t **aResult) michael@0: { michael@0: nsresult rv; michael@0: rv = LoadProperties(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *aResult = nullptr; michael@0: nsAutoString tmpstr; michael@0: michael@0: rv = GetStringFromID(aID, tmpstr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *aResult = ToNewUnicode(tmpstr); michael@0: NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void GetStringFromName (in wstring aName, out wstring aResult); */ michael@0: NS_IMETHODIMP michael@0: nsStringBundle::GetStringFromName(const char16_t *aName, char16_t **aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aName); michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: michael@0: nsresult rv; michael@0: rv = LoadProperties(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: ReentrantMonitorAutoEnter automon(mReentrantMonitor); michael@0: *aResult = nullptr; michael@0: nsAutoString tmpstr; michael@0: rv = GetStringFromName(nsDependentString(aName), tmpstr); michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: #if 0 michael@0: // it is not uncommon for apps to request a string name which may not exist michael@0: // so be quiet about it. michael@0: NS_WARNING("String missing from string bundle"); michael@0: printf(" '%s' missing from bundle %s\n", NS_ConvertUTF16toUTF8(aName).get(), mPropertiesURL.get()); michael@0: #endif michael@0: return rv; michael@0: } michael@0: michael@0: *aResult = ToNewUnicode(tmpstr); michael@0: NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride* aOverrideStrings, michael@0: nsISimpleEnumerator** aResult) michael@0: { michael@0: nsCOMPtr supports; michael@0: nsCOMPtr propElement; michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr resultArray = michael@0: do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // first, append the override elements michael@0: nsCOMPtr overrideEnumerator; michael@0: rv = aOverrideStrings->EnumerateKeysInBundle(mPropertiesURL, michael@0: getter_AddRefs(overrideEnumerator)); michael@0: michael@0: bool hasMore; michael@0: rv = overrideEnumerator->HasMoreElements(&hasMore); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: while (hasMore) { michael@0: michael@0: rv = overrideEnumerator->GetNext(getter_AddRefs(supports)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: resultArray->AppendElement(supports, false); michael@0: michael@0: rv = overrideEnumerator->HasMoreElements(&hasMore); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // ok, now we have the override elements in resultArray michael@0: nsCOMPtr propEnumerator; michael@0: rv = mProps->Enumerate(getter_AddRefs(propEnumerator)); michael@0: if (NS_FAILED(rv)) { michael@0: // no elements in mProps anyway, just return what we have michael@0: return NS_NewArrayEnumerator(aResult, resultArray); michael@0: } michael@0: michael@0: // second, append all the elements that are in mProps michael@0: do { michael@0: rv = propEnumerator->GetNext(getter_AddRefs(supports)); michael@0: if (NS_SUCCEEDED(rv) && michael@0: (propElement = do_QueryInterface(supports, &rv))) { michael@0: michael@0: // now check if its in the override bundle michael@0: nsAutoCString key; michael@0: propElement->GetKey(key); michael@0: michael@0: nsAutoString value; michael@0: rv = aOverrideStrings->GetStringFromName(mPropertiesURL, key, value); michael@0: michael@0: // if it isn't there, then it is safe to append michael@0: if (NS_FAILED(rv)) michael@0: resultArray->AppendElement(propElement, false); michael@0: } michael@0: michael@0: rv = propEnumerator->HasMoreElements(&hasMore); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } while (hasMore); michael@0: michael@0: return resultArray->Enumerate(aResult); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator** elements) michael@0: { michael@0: if (!elements) michael@0: return NS_ERROR_INVALID_POINTER; michael@0: michael@0: nsresult rv; michael@0: rv = LoadProperties(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (mOverrideStrings) michael@0: return GetCombinedEnumeration(mOverrideStrings, elements); michael@0: michael@0: return mProps->Enumerate(elements); michael@0: } michael@0: michael@0: nsresult michael@0: nsStringBundle::FormatString(const char16_t *aFormatStr, michael@0: const char16_t **aParams, uint32_t aLength, michael@0: char16_t **aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: NS_ENSURE_ARG(aLength <= 10); // enforce 10-parameter limit michael@0: michael@0: // implementation note: you would think you could use vsmprintf michael@0: // to build up an arbitrary length array.. except that there michael@0: // is no way to build up a va_list at runtime! michael@0: // Don't believe me? See: michael@0: // http://www.eskimo.com/~scs/C-faq/q15.13.html michael@0: // -alecf michael@0: char16_t *text = michael@0: nsTextFormatter::smprintf(aFormatStr, michael@0: aLength >= 1 ? aParams[0] : nullptr, michael@0: aLength >= 2 ? aParams[1] : nullptr, michael@0: aLength >= 3 ? aParams[2] : nullptr, michael@0: aLength >= 4 ? aParams[3] : nullptr, michael@0: aLength >= 5 ? aParams[4] : nullptr, michael@0: aLength >= 6 ? aParams[5] : nullptr, michael@0: aLength >= 7 ? aParams[6] : nullptr, michael@0: aLength >= 8 ? aParams[7] : nullptr, michael@0: aLength >= 9 ? aParams[8] : nullptr, michael@0: aLength >= 10 ? aParams[9] : nullptr); michael@0: michael@0: if (!text) { michael@0: *aResult = nullptr; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: // nsTextFormatter does not use the shared nsMemory allocator. michael@0: // Instead it is required to free the memory it allocates using michael@0: // nsTextFormatter::smprintf_free. Let's instead use nsMemory based michael@0: // allocation for the result that we give out and free the string michael@0: // returned by smprintf ourselves! michael@0: *aResult = NS_strdup(text); michael@0: nsTextFormatter::smprintf_free(text); michael@0: michael@0: return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsExtensibleStringBundle, nsIStringBundle) michael@0: michael@0: nsExtensibleStringBundle::nsExtensibleStringBundle() michael@0: { michael@0: mLoaded = false; michael@0: } michael@0: michael@0: nsresult michael@0: nsExtensibleStringBundle::Init(const char * aCategory, michael@0: nsIStringBundleService* aBundleService) michael@0: { michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr catman = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr enumerator; michael@0: rv = catman->EnumerateCategory(aCategory, getter_AddRefs(enumerator)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool hasMore; michael@0: while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr supports; michael@0: rv = enumerator->GetNext(getter_AddRefs(supports)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsCOMPtr supStr = do_QueryInterface(supports, &rv); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsAutoCString name; michael@0: rv = supStr->GetData(name); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsCOMPtr bundle; michael@0: rv = aBundleService->CreateBundle(name.get(), getter_AddRefs(bundle)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: mBundles.AppendObject(bundle); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsExtensibleStringBundle::~nsExtensibleStringBundle() michael@0: { michael@0: } michael@0: michael@0: nsresult nsExtensibleStringBundle::GetStringFromID(int32_t aID, char16_t ** aResult) michael@0: { michael@0: nsresult rv; michael@0: const uint32_t size = mBundles.Count(); michael@0: for (uint32_t i = 0; i < size; ++i) { michael@0: nsIStringBundle *bundle = mBundles[i]; michael@0: if (bundle) { michael@0: rv = bundle->GetStringFromID(aID, aResult); michael@0: if (NS_SUCCEEDED(rv)) michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult nsExtensibleStringBundle::GetStringFromName(const char16_t *aName, michael@0: char16_t ** aResult) michael@0: { michael@0: nsresult rv; michael@0: const uint32_t size = mBundles.Count(); michael@0: for (uint32_t i = 0; i < size; ++i) { michael@0: nsIStringBundle* bundle = mBundles[i]; michael@0: if (bundle) { michael@0: rv = bundle->GetStringFromName(aName, aResult); michael@0: if (NS_SUCCEEDED(rv)) michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExtensibleStringBundle::FormatStringFromID(int32_t aID, michael@0: const char16_t ** aParams, michael@0: uint32_t aLength, michael@0: char16_t ** aResult) michael@0: { michael@0: nsAutoString idStr; michael@0: idStr.AppendInt(aID, 10); michael@0: return FormatStringFromName(idStr.get(), aParams, aLength, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExtensibleStringBundle::FormatStringFromName(const char16_t *aName, michael@0: const char16_t ** aParams, michael@0: uint32_t aLength, michael@0: char16_t ** aResult) michael@0: { michael@0: nsXPIDLString formatStr; michael@0: nsresult rv; michael@0: rv = GetStringFromName(aName, getter_Copies(formatStr)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return nsStringBundle::FormatString(formatStr, aParams, aLength, aResult); michael@0: } michael@0: michael@0: nsresult nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator ** aResult) michael@0: { michael@0: // XXX write me michael@0: *aResult = nullptr; michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #define MAX_CACHED_BUNDLES 16 michael@0: michael@0: struct bundleCacheEntry_t MOZ_FINAL : public LinkedListElement { michael@0: nsCString mHashKey; michael@0: nsCOMPtr mBundle; michael@0: michael@0: bundleCacheEntry_t() michael@0: { michael@0: MOZ_COUNT_CTOR(bundleCacheEntry_t); michael@0: } michael@0: michael@0: ~bundleCacheEntry_t() michael@0: { michael@0: MOZ_COUNT_DTOR(bundleCacheEntry_t); michael@0: } michael@0: }; michael@0: michael@0: michael@0: nsStringBundleService::nsStringBundleService() : michael@0: mBundleMap(MAX_CACHED_BUNDLES) michael@0: { michael@0: mErrorService = do_GetService(kErrorServiceCID); michael@0: NS_ASSERTION(mErrorService, "Couldn't get error service"); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsStringBundleService, michael@0: nsIStringBundleService, michael@0: nsIObserver, michael@0: nsISupportsWeakReference) michael@0: michael@0: nsStringBundleService::~nsStringBundleService() michael@0: { michael@0: flushBundleCache(); michael@0: } michael@0: michael@0: nsresult michael@0: nsStringBundleService::Init() michael@0: { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->AddObserver(this, "memory-pressure", true); michael@0: os->AddObserver(this, "profile-do-change", true); michael@0: os->AddObserver(this, "chrome-flush-caches", true); michael@0: os->AddObserver(this, "xpcom-category-entry-added", true); michael@0: } michael@0: michael@0: // instantiate the override service, if there is any. michael@0: // at some point we probably want to make this a category, and michael@0: // support multiple overrides michael@0: mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleService::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aSomeData) michael@0: { michael@0: if (strcmp("memory-pressure", aTopic) == 0 || michael@0: strcmp("profile-do-change", aTopic) == 0 || michael@0: strcmp("chrome-flush-caches", aTopic) == 0) michael@0: { michael@0: flushBundleCache(); michael@0: } michael@0: else if (strcmp("xpcom-category-entry-added", aTopic) == 0 && michael@0: NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData)) michael@0: { michael@0: mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsStringBundleService::flushBundleCache() michael@0: { michael@0: // release all bundles in the cache michael@0: mBundleMap.Clear(); michael@0: michael@0: while (!mBundleCache.isEmpty()) { michael@0: delete mBundleCache.popFirst(); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleService::FlushBundles() michael@0: { michael@0: flushBundleCache(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStringBundleService::getStringBundle(const char *aURLSpec, michael@0: nsIStringBundle **aResult) michael@0: { michael@0: nsDependentCString key(aURLSpec); michael@0: bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key); michael@0: michael@0: if (cacheEntry) { michael@0: // cache hit! michael@0: // remove it from the list, it will later be reinserted michael@0: // at the head of the list michael@0: cacheEntry->remove(); michael@0: michael@0: } else { michael@0: michael@0: // hasn't been cached, so insert it into the hash table michael@0: nsRefPtr bundle = new nsStringBundle(aURLSpec, mOverrideStrings); michael@0: cacheEntry = insertIntoCache(bundle.forget(), key); michael@0: } michael@0: michael@0: // at this point the cacheEntry should exist in the hashtable, michael@0: // but is not in the LRU cache. michael@0: // put the cache entry at the front of the list michael@0: mBundleCache.insertFront(cacheEntry); michael@0: michael@0: // finally, return the value michael@0: *aResult = cacheEntry->mBundle; michael@0: NS_ADDREF(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bundleCacheEntry_t * michael@0: nsStringBundleService::insertIntoCache(already_AddRefed aBundle, michael@0: nsCString &aHashKey) michael@0: { michael@0: bundleCacheEntry_t *cacheEntry; michael@0: michael@0: if (mBundleMap.Count() < MAX_CACHED_BUNDLES) { michael@0: // cache not full - create a new entry michael@0: cacheEntry = new bundleCacheEntry_t(); michael@0: } else { michael@0: // cache is full michael@0: // take the last entry in the list, and recycle it. michael@0: cacheEntry = mBundleCache.getLast(); michael@0: michael@0: // remove it from the hash table and linked list michael@0: NS_ASSERTION(mBundleMap.Contains(cacheEntry->mHashKey), michael@0: "Element will not be removed!"); michael@0: mBundleMap.Remove(cacheEntry->mHashKey); michael@0: cacheEntry->remove(); michael@0: } michael@0: michael@0: // at this point we have a new cacheEntry that doesn't exist michael@0: // in the hashtable, so set up the cacheEntry michael@0: cacheEntry->mHashKey = aHashKey; michael@0: cacheEntry->mBundle = aBundle; michael@0: michael@0: // insert the entry into the cache and map, make it the MRU michael@0: mBundleMap.Put(cacheEntry->mHashKey, cacheEntry); michael@0: michael@0: return cacheEntry; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleService::CreateBundle(const char* aURLSpec, michael@0: nsIStringBundle** aResult) michael@0: { michael@0: return getStringBundle(aURLSpec,aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleService::CreateExtensibleBundle(const char* aCategory, michael@0: nsIStringBundle** aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: michael@0: nsresult res; michael@0: michael@0: nsExtensibleStringBundle * bundle = new nsExtensibleStringBundle(); michael@0: michael@0: res = bundle->Init(aCategory, this); michael@0: if (NS_FAILED(res)) { michael@0: delete bundle; michael@0: return res; michael@0: } michael@0: michael@0: res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult); michael@0: if (NS_FAILED(res)) delete bundle; michael@0: michael@0: return res; michael@0: } michael@0: michael@0: #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties" michael@0: michael@0: nsresult michael@0: nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus, michael@0: uint32_t argCount, char16_t** argArray, michael@0: char16_t* *result) michael@0: { michael@0: nsresult rv; michael@0: nsXPIDLCString key; michael@0: michael@0: // try looking up the error message with the int key: michael@0: uint16_t code = NS_ERROR_GET_CODE(aStatus); michael@0: rv = bundle->FormatStringFromID(code, (const char16_t**)argArray, argCount, result); michael@0: michael@0: // If the int key fails, try looking up the default error message. E.g. print: michael@0: // An unknown error has occurred (0x804B0003). michael@0: if (NS_FAILED(rv)) { michael@0: nsAutoString statusStr; michael@0: statusStr.AppendInt(static_cast(aStatus), 16); michael@0: const char16_t* otherArgArray[1]; michael@0: otherArgArray[0] = statusStr.get(); michael@0: uint16_t code = NS_ERROR_GET_CODE(NS_ERROR_FAILURE); michael@0: rv = bundle->FormatStringFromID(code, otherArgArray, 1, result); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleService::FormatStatusMessage(nsresult aStatus, michael@0: const char16_t* aStatusArg, michael@0: char16_t* *result) michael@0: { michael@0: nsresult rv; michael@0: uint32_t i, argCount = 0; michael@0: nsCOMPtr bundle; michael@0: nsXPIDLCString stringBundleURL; michael@0: michael@0: // XXX hack for mailnews who has already formatted their messages: michael@0: if (aStatus == NS_OK && aStatusArg) { michael@0: *result = NS_strdup(aStatusArg); michael@0: NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aStatus == NS_OK) { michael@0: return NS_ERROR_FAILURE; // no message to format michael@0: } michael@0: michael@0: // format the arguments: michael@0: const nsDependentString args(aStatusArg); michael@0: argCount = args.CountChar(char16_t('\n')) + 1; michael@0: NS_ENSURE_ARG(argCount <= 10); // enforce 10-parameter limit michael@0: char16_t* argArray[10]; michael@0: michael@0: // convert the aStatusArg into a char16_t array michael@0: if (argCount == 1) { michael@0: // avoid construction for the simple case: michael@0: argArray[0] = (char16_t*)aStatusArg; michael@0: } michael@0: else if (argCount > 1) { michael@0: int32_t offset = 0; michael@0: for (i = 0; i < argCount; i++) { michael@0: int32_t pos = args.FindChar('\n', offset); michael@0: if (pos == -1) michael@0: pos = args.Length(); michael@0: argArray[i] = ToNewUnicode(Substring(args, offset, pos - offset)); michael@0: if (argArray[i] == nullptr) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: argCount = i - 1; // don't try to free uninitialized memory michael@0: goto done; michael@0: } michael@0: offset = pos + 1; michael@0: } michael@0: } michael@0: michael@0: // find the string bundle for the error's module: michael@0: rv = mErrorService->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus), michael@0: getter_Copies(stringBundleURL)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = getStringBundle(stringBundleURL, getter_AddRefs(bundle)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result); michael@0: } michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: rv = getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result); michael@0: } michael@0: } michael@0: michael@0: done: michael@0: if (argCount > 1) { michael@0: for (i = 0; i < argCount; i++) { michael@0: if (argArray[i]) michael@0: nsMemory::Free(argArray[i]); michael@0: } michael@0: } michael@0: return rv; michael@0: }