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