Files
palemoon27/intl/strres/nsStringBundle.cpp
T
roytam1 2a3dd7f84c import change from rmottola/Arctic-Fox:
- Bug 1139453 - Send network UP/DOWN events on desktop Linux. (7466c1298)
- Bug 1141488 - On non-APZ platforms, combine any async transform with the OMTA transform rather than clobbering it the OMTA transform. (fe48d6774)
- Bug 1080752 - Hold wakelock when attempting to connect to push server. (b1e74295f)
- Bug 1126089 - Allow messages to be sent after frame script unload event. (167ebbd9a)
- Bug 1126089 - Add test for pageload event on browser element when tab closed (f2011e6e4)
- Bug 1090921 - Logging to see which tasks remain alive too long (ebfc3c9bd)
- Bug 1142544 - delete now-unnecessary |extern "C++"| block from nsISupportsUtils.h; r=mccr8 (07e6572f8)
- Bug 1142503 - don't use QueryInterface when the compiler can do the cast for us; r=ehsan (b13219054)
- Bug 1140161 - Sensibly handle patterns with absolute paths in TEST_HARNESS_FILES. (173e369a7)
- Bug 1082510 - When we detect a chrome popup at the current event coordinates and remote content has the mouse captured, release capture. r=Enn (da9fe26a7)
- Bug 1137722 - Ensure that system message cache is refreshed before openapp. r=fabrice (97dc41f50)
- Bug 1139904 - Add initial test runtime files for mochitest browser-chrome and devtools (9837c79b1)
- Bug 1061604 part.1 nsTextStore::GetTextExt() should rReturn previous character rect instead of TS_E_NOLAYOUT when Google Japanese Input retrieves first character of selected clause at composing r=emk (54779d30e)
- Bug 1061604 part.2 nsTextStore::GetTextExt() should rReturn previous character rect of modified range instead of TS_E_NOLAYOUT when Google Japanese Input retrieves caret rect during composition r=emk (ba7f93879)
- Bug 1136051 - component_insert_pair statrs checks only while we are still checking or haven't started pairing; (ae1ae1d4f)
- Bug 1137007 - Detect namespace and SECCOMP_FILTER_FLAG_TSYNC support in SandboxInfo. r=kang, r=Unfocused (6d9e32a8f)
- Bug 1142263 - Specify all syscall parameters when doing CLONE_NEWUSER detection; f=bwc r=kang (8bcba1831)
-  Bug 1142567 - Remove FAIL_ON_WARNINGS_DEBUG; It is not used. (bf22f1ccf)
- No bug - Add an explanatory message to the multiple-inheritance assertion in DOM bindings; # (ffd94ef07)
- Bug 1141252. Don't mark the TablePainter display item as having background-attachment:fixed content if the background-attachment:fixed image will be painted by a dedicated nsDisplayTableCellBackground. r=mats (98287b37c)
- Bug 1140084 - delete list of script counts in tail-recursive fashion. (90a2b7c45)
- Bug 1048741 - [WebGL2] texParameter: Fix silly bug in TEXTURE_COMPARE_FUNC handling. (ccc90dae3)
- Bug 1142478. Fix integer attribute parsing to not lose track of leading zeroes. (d596a1ce1)
- Bug 1139388 - Remove superfluous locking in the OS X profiler implementation to fix deadlock. (r=mstange) (a13088958)
- Bug 1128603: Remove findReferences and the tests that use it. (bccc64734)
- Bug 1140683: Fix async plugin init using null plugin funcs on MacOSX (47b591d54)
- Bug 1142494 - Fix OSX packaging mistake. (15555f4ee)
- Bug 1136784 - Add screenshot to LogShake produced data. (7ff264243)
- Bug 1142550 - Fix screenshot. (d684b6469)
- Bug 1142006 - Set MACOSX_DEPLOYMENT_TARGET earlier. r=mshal (c333b954e)
- Bug 1100966 - Remember all ranges for all selections when joining nodes in the editor transactions; r=roc (bf34bd0c8)
- Bug 1136507 - L8, LA8, and A8 should not be renderable. (96e807795)
- Bug 1128019 - Init SCISSOR_BOX with initial width and height. (7258cc44c)
- Bug 1142211 - Add layerization testing mechanisms to reftest. r=roc (4c6aa11f8)
- Bug 1106905 - Modify mobile desktop mode implementation to use a desktop viewport. (644ede0e7)
- Bug 1139675 - Simplify the APIs for getting and setting the pres shell resolution. r=mstange,mattwoodrow (c520a8c98)
- Bug 1141563 - Don't update Shapes in parallel after compacting GC (098af79c9)
2019-07-13 08:35:45 +08:00

759 lines
21 KiB
C++

/* -*- 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 "nsIObserverService.h"
#include "nsCOMArray.h"
#include "nsTextFormatter.h"
#include "nsIErrorService.h"
#include "nsICategoryManager.h"
#include "nsContentUtils.h"
// for async loading
#ifdef ASYNC_LOADING
#include "nsIBinaryInputStream.h"
#include "nsIStringStream.h"
#endif
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;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_NORMAL,
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->Open(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)
/* void GetStringFromID (in long aID, out wstring aResult); */
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;
}
/* void GetStringFromName (in wstring aName, out wstring aResult); */
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);
}
// 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("xpcom-category-entry-added", aTopic) == 0 &&
NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData))
{
mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
}
return NS_OK;
}
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
nsRefPtr<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;
nsRefPtr<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;
}