1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/strres/src/nsStringBundle.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,758 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsStringBundle.h" 1.10 +#include "nsID.h" 1.11 +#include "nsString.h" 1.12 +#include "nsIStringBundle.h" 1.13 +#include "nsStringBundleService.h" 1.14 +#include "nsStringBundleTextOverride.h" 1.15 +#include "nsISupportsPrimitives.h" 1.16 +#include "nsIMutableArray.h" 1.17 +#include "nsArrayEnumerator.h" 1.18 +#include "nscore.h" 1.19 +#include "nsHashtable.h" 1.20 +#include "nsMemory.h" 1.21 +#include "nsNetUtil.h" 1.22 +#include "nsIObserverService.h" 1.23 +#include "nsCOMArray.h" 1.24 +#include "nsTextFormatter.h" 1.25 +#include "nsIErrorService.h" 1.26 +#include "nsICategoryManager.h" 1.27 + 1.28 +// for async loading 1.29 +#ifdef ASYNC_LOADING 1.30 +#include "nsIBinaryInputStream.h" 1.31 +#include "nsIStringStream.h" 1.32 +#endif 1.33 + 1.34 +using namespace mozilla; 1.35 + 1.36 +static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID); 1.37 + 1.38 +nsStringBundle::~nsStringBundle() 1.39 +{ 1.40 +} 1.41 + 1.42 +nsStringBundle::nsStringBundle(const char* aURLSpec, 1.43 + nsIStringBundleOverride* aOverrideStrings) : 1.44 + mPropertiesURL(aURLSpec), 1.45 + mOverrideStrings(aOverrideStrings), 1.46 + mReentrantMonitor("nsStringBundle.mReentrantMonitor"), 1.47 + mAttemptedLoad(false), 1.48 + mLoaded(false) 1.49 +{ 1.50 +} 1.51 + 1.52 +nsresult 1.53 +nsStringBundle::LoadProperties() 1.54 +{ 1.55 + // this is different than mLoaded, because we only want to attempt 1.56 + // to load once 1.57 + // we only want to load once, but if we've tried once and failed, 1.58 + // continue to throw an error! 1.59 + if (mAttemptedLoad) { 1.60 + if (mLoaded) 1.61 + return NS_OK; 1.62 + 1.63 + return NS_ERROR_UNEXPECTED; 1.64 + } 1.65 + 1.66 + mAttemptedLoad = true; 1.67 + 1.68 + nsresult rv; 1.69 + 1.70 + // do it synchronously 1.71 + nsCOMPtr<nsIURI> uri; 1.72 + rv = NS_NewURI(getter_AddRefs(uri), mPropertiesURL); 1.73 + if (NS_FAILED(rv)) return rv; 1.74 + 1.75 + // We don't use NS_OpenURI because we want to tweak the channel 1.76 + nsCOMPtr<nsIChannel> channel; 1.77 + rv = NS_NewChannel(getter_AddRefs(channel), uri); 1.78 + if (NS_FAILED(rv)) return rv; 1.79 + 1.80 + // It's a string bundle. We expect a text/plain type, so set that as hint 1.81 + channel->SetContentType(NS_LITERAL_CSTRING("text/plain")); 1.82 + 1.83 + nsCOMPtr<nsIInputStream> in; 1.84 + rv = channel->Open(getter_AddRefs(in)); 1.85 + if (NS_FAILED(rv)) return rv; 1.86 + 1.87 + NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream"); 1.88 + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE); 1.89 + 1.90 + static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID); 1.91 + mProps = do_CreateInstance(kPersistentPropertiesCID, &rv); 1.92 + NS_ENSURE_SUCCESS(rv, rv); 1.93 + 1.94 + mAttemptedLoad = mLoaded = true; 1.95 + rv = mProps->Load(in); 1.96 + 1.97 + mLoaded = NS_SUCCEEDED(rv); 1.98 + 1.99 + return rv; 1.100 +} 1.101 + 1.102 + 1.103 +nsresult 1.104 +nsStringBundle::GetStringFromID(int32_t aID, nsAString& aResult) 1.105 +{ 1.106 + ReentrantMonitorAutoEnter automon(mReentrantMonitor); 1.107 + nsAutoCString name; 1.108 + name.AppendInt(aID, 10); 1.109 + 1.110 + nsresult rv; 1.111 + 1.112 + // try override first 1.113 + if (mOverrideStrings) { 1.114 + rv = mOverrideStrings->GetStringFromName(mPropertiesURL, 1.115 + name, 1.116 + aResult); 1.117 + if (NS_SUCCEEDED(rv)) return rv; 1.118 + } 1.119 + 1.120 + rv = mProps->GetStringProperty(name, aResult); 1.121 + 1.122 + return rv; 1.123 +} 1.124 + 1.125 +nsresult 1.126 +nsStringBundle::GetStringFromName(const nsAString& aName, 1.127 + nsAString& aResult) 1.128 +{ 1.129 + nsresult rv; 1.130 + 1.131 + // try override first 1.132 + if (mOverrideStrings) { 1.133 + rv = mOverrideStrings->GetStringFromName(mPropertiesURL, 1.134 + NS_ConvertUTF16toUTF8(aName), 1.135 + aResult); 1.136 + if (NS_SUCCEEDED(rv)) return rv; 1.137 + } 1.138 + 1.139 + rv = mProps->GetStringProperty(NS_ConvertUTF16toUTF8(aName), aResult); 1.140 + return rv; 1.141 +} 1.142 + 1.143 +NS_IMETHODIMP 1.144 +nsStringBundle::FormatStringFromID(int32_t aID, 1.145 + const char16_t **aParams, 1.146 + uint32_t aLength, 1.147 + char16_t ** aResult) 1.148 +{ 1.149 + nsAutoString idStr; 1.150 + idStr.AppendInt(aID, 10); 1.151 + 1.152 + return FormatStringFromName(idStr.get(), aParams, aLength, aResult); 1.153 +} 1.154 + 1.155 +// this function supports at most 10 parameters.. see below for why 1.156 +NS_IMETHODIMP 1.157 +nsStringBundle::FormatStringFromName(const char16_t *aName, 1.158 + const char16_t **aParams, 1.159 + uint32_t aLength, 1.160 + char16_t **aResult) 1.161 +{ 1.162 + NS_ENSURE_ARG_POINTER(aName); 1.163 + NS_ASSERTION(aParams && aLength, "FormatStringFromName() without format parameters: use GetStringFromName() instead"); 1.164 + NS_ENSURE_ARG_POINTER(aResult); 1.165 + 1.166 + nsresult rv; 1.167 + rv = LoadProperties(); 1.168 + if (NS_FAILED(rv)) return rv; 1.169 + 1.170 + nsAutoString formatStr; 1.171 + rv = GetStringFromName(nsDependentString(aName), formatStr); 1.172 + if (NS_FAILED(rv)) return rv; 1.173 + 1.174 + return FormatString(formatStr.get(), aParams, aLength, aResult); 1.175 +} 1.176 + 1.177 + 1.178 +NS_IMPL_ISUPPORTS(nsStringBundle, nsIStringBundle) 1.179 + 1.180 +/* void GetStringFromID (in long aID, out wstring aResult); */ 1.181 +NS_IMETHODIMP 1.182 +nsStringBundle::GetStringFromID(int32_t aID, char16_t **aResult) 1.183 +{ 1.184 + nsresult rv; 1.185 + rv = LoadProperties(); 1.186 + if (NS_FAILED(rv)) return rv; 1.187 + 1.188 + *aResult = nullptr; 1.189 + nsAutoString tmpstr; 1.190 + 1.191 + rv = GetStringFromID(aID, tmpstr); 1.192 + NS_ENSURE_SUCCESS(rv, rv); 1.193 + 1.194 + *aResult = ToNewUnicode(tmpstr); 1.195 + NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); 1.196 + 1.197 + return NS_OK; 1.198 +} 1.199 + 1.200 +/* void GetStringFromName (in wstring aName, out wstring aResult); */ 1.201 +NS_IMETHODIMP 1.202 +nsStringBundle::GetStringFromName(const char16_t *aName, char16_t **aResult) 1.203 +{ 1.204 + NS_ENSURE_ARG_POINTER(aName); 1.205 + NS_ENSURE_ARG_POINTER(aResult); 1.206 + 1.207 + nsresult rv; 1.208 + rv = LoadProperties(); 1.209 + if (NS_FAILED(rv)) return rv; 1.210 + 1.211 + ReentrantMonitorAutoEnter automon(mReentrantMonitor); 1.212 + *aResult = nullptr; 1.213 + nsAutoString tmpstr; 1.214 + rv = GetStringFromName(nsDependentString(aName), tmpstr); 1.215 + if (NS_FAILED(rv)) 1.216 + { 1.217 +#if 0 1.218 + // it is not uncommon for apps to request a string name which may not exist 1.219 + // so be quiet about it. 1.220 + NS_WARNING("String missing from string bundle"); 1.221 + printf(" '%s' missing from bundle %s\n", NS_ConvertUTF16toUTF8(aName).get(), mPropertiesURL.get()); 1.222 +#endif 1.223 + return rv; 1.224 + } 1.225 + 1.226 + *aResult = ToNewUnicode(tmpstr); 1.227 + NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); 1.228 + 1.229 + return NS_OK; 1.230 +} 1.231 + 1.232 +nsresult 1.233 +nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride* aOverrideStrings, 1.234 + nsISimpleEnumerator** aResult) 1.235 +{ 1.236 + nsCOMPtr<nsISupports> supports; 1.237 + nsCOMPtr<nsIPropertyElement> propElement; 1.238 + 1.239 + nsresult rv; 1.240 + 1.241 + nsCOMPtr<nsIMutableArray> resultArray = 1.242 + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); 1.243 + NS_ENSURE_SUCCESS(rv, rv); 1.244 + 1.245 + // first, append the override elements 1.246 + nsCOMPtr<nsISimpleEnumerator> overrideEnumerator; 1.247 + rv = aOverrideStrings->EnumerateKeysInBundle(mPropertiesURL, 1.248 + getter_AddRefs(overrideEnumerator)); 1.249 + 1.250 + bool hasMore; 1.251 + rv = overrideEnumerator->HasMoreElements(&hasMore); 1.252 + NS_ENSURE_SUCCESS(rv, rv); 1.253 + while (hasMore) { 1.254 + 1.255 + rv = overrideEnumerator->GetNext(getter_AddRefs(supports)); 1.256 + if (NS_SUCCEEDED(rv)) 1.257 + resultArray->AppendElement(supports, false); 1.258 + 1.259 + rv = overrideEnumerator->HasMoreElements(&hasMore); 1.260 + NS_ENSURE_SUCCESS(rv, rv); 1.261 + } 1.262 + 1.263 + // ok, now we have the override elements in resultArray 1.264 + nsCOMPtr<nsISimpleEnumerator> propEnumerator; 1.265 + rv = mProps->Enumerate(getter_AddRefs(propEnumerator)); 1.266 + if (NS_FAILED(rv)) { 1.267 + // no elements in mProps anyway, just return what we have 1.268 + return NS_NewArrayEnumerator(aResult, resultArray); 1.269 + } 1.270 + 1.271 + // second, append all the elements that are in mProps 1.272 + do { 1.273 + rv = propEnumerator->GetNext(getter_AddRefs(supports)); 1.274 + if (NS_SUCCEEDED(rv) && 1.275 + (propElement = do_QueryInterface(supports, &rv))) { 1.276 + 1.277 + // now check if its in the override bundle 1.278 + nsAutoCString key; 1.279 + propElement->GetKey(key); 1.280 + 1.281 + nsAutoString value; 1.282 + rv = aOverrideStrings->GetStringFromName(mPropertiesURL, key, value); 1.283 + 1.284 + // if it isn't there, then it is safe to append 1.285 + if (NS_FAILED(rv)) 1.286 + resultArray->AppendElement(propElement, false); 1.287 + } 1.288 + 1.289 + rv = propEnumerator->HasMoreElements(&hasMore); 1.290 + NS_ENSURE_SUCCESS(rv, rv); 1.291 + } while (hasMore); 1.292 + 1.293 + return resultArray->Enumerate(aResult); 1.294 +} 1.295 + 1.296 + 1.297 +NS_IMETHODIMP 1.298 +nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator** elements) 1.299 +{ 1.300 + if (!elements) 1.301 + return NS_ERROR_INVALID_POINTER; 1.302 + 1.303 + nsresult rv; 1.304 + rv = LoadProperties(); 1.305 + if (NS_FAILED(rv)) return rv; 1.306 + 1.307 + if (mOverrideStrings) 1.308 + return GetCombinedEnumeration(mOverrideStrings, elements); 1.309 + 1.310 + return mProps->Enumerate(elements); 1.311 +} 1.312 + 1.313 +nsresult 1.314 +nsStringBundle::FormatString(const char16_t *aFormatStr, 1.315 + const char16_t **aParams, uint32_t aLength, 1.316 + char16_t **aResult) 1.317 +{ 1.318 + NS_ENSURE_ARG_POINTER(aResult); 1.319 + NS_ENSURE_ARG(aLength <= 10); // enforce 10-parameter limit 1.320 + 1.321 + // implementation note: you would think you could use vsmprintf 1.322 + // to build up an arbitrary length array.. except that there 1.323 + // is no way to build up a va_list at runtime! 1.324 + // Don't believe me? See: 1.325 + // http://www.eskimo.com/~scs/C-faq/q15.13.html 1.326 + // -alecf 1.327 + char16_t *text = 1.328 + nsTextFormatter::smprintf(aFormatStr, 1.329 + aLength >= 1 ? aParams[0] : nullptr, 1.330 + aLength >= 2 ? aParams[1] : nullptr, 1.331 + aLength >= 3 ? aParams[2] : nullptr, 1.332 + aLength >= 4 ? aParams[3] : nullptr, 1.333 + aLength >= 5 ? aParams[4] : nullptr, 1.334 + aLength >= 6 ? aParams[5] : nullptr, 1.335 + aLength >= 7 ? aParams[6] : nullptr, 1.336 + aLength >= 8 ? aParams[7] : nullptr, 1.337 + aLength >= 9 ? aParams[8] : nullptr, 1.338 + aLength >= 10 ? aParams[9] : nullptr); 1.339 + 1.340 + if (!text) { 1.341 + *aResult = nullptr; 1.342 + return NS_ERROR_OUT_OF_MEMORY; 1.343 + } 1.344 + 1.345 + // nsTextFormatter does not use the shared nsMemory allocator. 1.346 + // Instead it is required to free the memory it allocates using 1.347 + // nsTextFormatter::smprintf_free. Let's instead use nsMemory based 1.348 + // allocation for the result that we give out and free the string 1.349 + // returned by smprintf ourselves! 1.350 + *aResult = NS_strdup(text); 1.351 + nsTextFormatter::smprintf_free(text); 1.352 + 1.353 + return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY; 1.354 +} 1.355 + 1.356 +NS_IMPL_ISUPPORTS(nsExtensibleStringBundle, nsIStringBundle) 1.357 + 1.358 +nsExtensibleStringBundle::nsExtensibleStringBundle() 1.359 +{ 1.360 + mLoaded = false; 1.361 +} 1.362 + 1.363 +nsresult 1.364 +nsExtensibleStringBundle::Init(const char * aCategory, 1.365 + nsIStringBundleService* aBundleService) 1.366 +{ 1.367 + 1.368 + nsresult rv; 1.369 + nsCOMPtr<nsICategoryManager> catman = 1.370 + do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); 1.371 + if (NS_FAILED(rv)) return rv; 1.372 + 1.373 + nsCOMPtr<nsISimpleEnumerator> enumerator; 1.374 + rv = catman->EnumerateCategory(aCategory, getter_AddRefs(enumerator)); 1.375 + if (NS_FAILED(rv)) return rv; 1.376 + 1.377 + bool hasMore; 1.378 + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { 1.379 + nsCOMPtr<nsISupports> supports; 1.380 + rv = enumerator->GetNext(getter_AddRefs(supports)); 1.381 + if (NS_FAILED(rv)) 1.382 + continue; 1.383 + 1.384 + nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports, &rv); 1.385 + if (NS_FAILED(rv)) 1.386 + continue; 1.387 + 1.388 + nsAutoCString name; 1.389 + rv = supStr->GetData(name); 1.390 + if (NS_FAILED(rv)) 1.391 + continue; 1.392 + 1.393 + nsCOMPtr<nsIStringBundle> bundle; 1.394 + rv = aBundleService->CreateBundle(name.get(), getter_AddRefs(bundle)); 1.395 + if (NS_FAILED(rv)) 1.396 + continue; 1.397 + 1.398 + mBundles.AppendObject(bundle); 1.399 + } 1.400 + 1.401 + return rv; 1.402 +} 1.403 + 1.404 +nsExtensibleStringBundle::~nsExtensibleStringBundle() 1.405 +{ 1.406 +} 1.407 + 1.408 +nsresult nsExtensibleStringBundle::GetStringFromID(int32_t aID, char16_t ** aResult) 1.409 +{ 1.410 + nsresult rv; 1.411 + const uint32_t size = mBundles.Count(); 1.412 + for (uint32_t i = 0; i < size; ++i) { 1.413 + nsIStringBundle *bundle = mBundles[i]; 1.414 + if (bundle) { 1.415 + rv = bundle->GetStringFromID(aID, aResult); 1.416 + if (NS_SUCCEEDED(rv)) 1.417 + return NS_OK; 1.418 + } 1.419 + } 1.420 + 1.421 + return NS_ERROR_FAILURE; 1.422 +} 1.423 + 1.424 +nsresult nsExtensibleStringBundle::GetStringFromName(const char16_t *aName, 1.425 + char16_t ** aResult) 1.426 +{ 1.427 + nsresult rv; 1.428 + const uint32_t size = mBundles.Count(); 1.429 + for (uint32_t i = 0; i < size; ++i) { 1.430 + nsIStringBundle* bundle = mBundles[i]; 1.431 + if (bundle) { 1.432 + rv = bundle->GetStringFromName(aName, aResult); 1.433 + if (NS_SUCCEEDED(rv)) 1.434 + return NS_OK; 1.435 + } 1.436 + } 1.437 + 1.438 + return NS_ERROR_FAILURE; 1.439 +} 1.440 + 1.441 +NS_IMETHODIMP 1.442 +nsExtensibleStringBundle::FormatStringFromID(int32_t aID, 1.443 + const char16_t ** aParams, 1.444 + uint32_t aLength, 1.445 + char16_t ** aResult) 1.446 +{ 1.447 + nsAutoString idStr; 1.448 + idStr.AppendInt(aID, 10); 1.449 + return FormatStringFromName(idStr.get(), aParams, aLength, aResult); 1.450 +} 1.451 + 1.452 +NS_IMETHODIMP 1.453 +nsExtensibleStringBundle::FormatStringFromName(const char16_t *aName, 1.454 + const char16_t ** aParams, 1.455 + uint32_t aLength, 1.456 + char16_t ** aResult) 1.457 +{ 1.458 + nsXPIDLString formatStr; 1.459 + nsresult rv; 1.460 + rv = GetStringFromName(aName, getter_Copies(formatStr)); 1.461 + if (NS_FAILED(rv)) 1.462 + return rv; 1.463 + 1.464 + return nsStringBundle::FormatString(formatStr, aParams, aLength, aResult); 1.465 +} 1.466 + 1.467 +nsresult nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator ** aResult) 1.468 +{ 1.469 + // XXX write me 1.470 + *aResult = nullptr; 1.471 + return NS_ERROR_NOT_IMPLEMENTED; 1.472 +} 1.473 + 1.474 +///////////////////////////////////////////////////////////////////////////////////////// 1.475 + 1.476 +#define MAX_CACHED_BUNDLES 16 1.477 + 1.478 +struct bundleCacheEntry_t MOZ_FINAL : public LinkedListElement<bundleCacheEntry_t> { 1.479 + nsCString mHashKey; 1.480 + nsCOMPtr<nsIStringBundle> mBundle; 1.481 + 1.482 + bundleCacheEntry_t() 1.483 + { 1.484 + MOZ_COUNT_CTOR(bundleCacheEntry_t); 1.485 + } 1.486 + 1.487 + ~bundleCacheEntry_t() 1.488 + { 1.489 + MOZ_COUNT_DTOR(bundleCacheEntry_t); 1.490 + } 1.491 +}; 1.492 + 1.493 + 1.494 +nsStringBundleService::nsStringBundleService() : 1.495 + mBundleMap(MAX_CACHED_BUNDLES) 1.496 +{ 1.497 + mErrorService = do_GetService(kErrorServiceCID); 1.498 + NS_ASSERTION(mErrorService, "Couldn't get error service"); 1.499 +} 1.500 + 1.501 +NS_IMPL_ISUPPORTS(nsStringBundleService, 1.502 + nsIStringBundleService, 1.503 + nsIObserver, 1.504 + nsISupportsWeakReference) 1.505 + 1.506 +nsStringBundleService::~nsStringBundleService() 1.507 +{ 1.508 + flushBundleCache(); 1.509 +} 1.510 + 1.511 +nsresult 1.512 +nsStringBundleService::Init() 1.513 +{ 1.514 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.515 + if (os) { 1.516 + os->AddObserver(this, "memory-pressure", true); 1.517 + os->AddObserver(this, "profile-do-change", true); 1.518 + os->AddObserver(this, "chrome-flush-caches", true); 1.519 + os->AddObserver(this, "xpcom-category-entry-added", true); 1.520 + } 1.521 + 1.522 + // instantiate the override service, if there is any. 1.523 + // at some point we probably want to make this a category, and 1.524 + // support multiple overrides 1.525 + mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID); 1.526 + 1.527 + return NS_OK; 1.528 +} 1.529 + 1.530 +NS_IMETHODIMP 1.531 +nsStringBundleService::Observe(nsISupports* aSubject, 1.532 + const char* aTopic, 1.533 + const char16_t* aSomeData) 1.534 +{ 1.535 + if (strcmp("memory-pressure", aTopic) == 0 || 1.536 + strcmp("profile-do-change", aTopic) == 0 || 1.537 + strcmp("chrome-flush-caches", aTopic) == 0) 1.538 + { 1.539 + flushBundleCache(); 1.540 + } 1.541 + else if (strcmp("xpcom-category-entry-added", aTopic) == 0 && 1.542 + NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData)) 1.543 + { 1.544 + mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID); 1.545 + } 1.546 + 1.547 + return NS_OK; 1.548 +} 1.549 + 1.550 +void 1.551 +nsStringBundleService::flushBundleCache() 1.552 +{ 1.553 + // release all bundles in the cache 1.554 + mBundleMap.Clear(); 1.555 + 1.556 + while (!mBundleCache.isEmpty()) { 1.557 + delete mBundleCache.popFirst(); 1.558 + } 1.559 +} 1.560 + 1.561 +NS_IMETHODIMP 1.562 +nsStringBundleService::FlushBundles() 1.563 +{ 1.564 + flushBundleCache(); 1.565 + return NS_OK; 1.566 +} 1.567 + 1.568 +nsresult 1.569 +nsStringBundleService::getStringBundle(const char *aURLSpec, 1.570 + nsIStringBundle **aResult) 1.571 +{ 1.572 + nsDependentCString key(aURLSpec); 1.573 + bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key); 1.574 + 1.575 + if (cacheEntry) { 1.576 + // cache hit! 1.577 + // remove it from the list, it will later be reinserted 1.578 + // at the head of the list 1.579 + cacheEntry->remove(); 1.580 + 1.581 + } else { 1.582 + 1.583 + // hasn't been cached, so insert it into the hash table 1.584 + nsRefPtr<nsStringBundle> bundle = new nsStringBundle(aURLSpec, mOverrideStrings); 1.585 + cacheEntry = insertIntoCache(bundle.forget(), key); 1.586 + } 1.587 + 1.588 + // at this point the cacheEntry should exist in the hashtable, 1.589 + // but is not in the LRU cache. 1.590 + // put the cache entry at the front of the list 1.591 + mBundleCache.insertFront(cacheEntry); 1.592 + 1.593 + // finally, return the value 1.594 + *aResult = cacheEntry->mBundle; 1.595 + NS_ADDREF(*aResult); 1.596 + 1.597 + return NS_OK; 1.598 +} 1.599 + 1.600 +bundleCacheEntry_t * 1.601 +nsStringBundleService::insertIntoCache(already_AddRefed<nsIStringBundle> aBundle, 1.602 + nsCString &aHashKey) 1.603 +{ 1.604 + bundleCacheEntry_t *cacheEntry; 1.605 + 1.606 + if (mBundleMap.Count() < MAX_CACHED_BUNDLES) { 1.607 + // cache not full - create a new entry 1.608 + cacheEntry = new bundleCacheEntry_t(); 1.609 + } else { 1.610 + // cache is full 1.611 + // take the last entry in the list, and recycle it. 1.612 + cacheEntry = mBundleCache.getLast(); 1.613 + 1.614 + // remove it from the hash table and linked list 1.615 + NS_ASSERTION(mBundleMap.Contains(cacheEntry->mHashKey), 1.616 + "Element will not be removed!"); 1.617 + mBundleMap.Remove(cacheEntry->mHashKey); 1.618 + cacheEntry->remove(); 1.619 + } 1.620 + 1.621 + // at this point we have a new cacheEntry that doesn't exist 1.622 + // in the hashtable, so set up the cacheEntry 1.623 + cacheEntry->mHashKey = aHashKey; 1.624 + cacheEntry->mBundle = aBundle; 1.625 + 1.626 + // insert the entry into the cache and map, make it the MRU 1.627 + mBundleMap.Put(cacheEntry->mHashKey, cacheEntry); 1.628 + 1.629 + return cacheEntry; 1.630 +} 1.631 + 1.632 +NS_IMETHODIMP 1.633 +nsStringBundleService::CreateBundle(const char* aURLSpec, 1.634 + nsIStringBundle** aResult) 1.635 +{ 1.636 + return getStringBundle(aURLSpec,aResult); 1.637 +} 1.638 + 1.639 +NS_IMETHODIMP 1.640 +nsStringBundleService::CreateExtensibleBundle(const char* aCategory, 1.641 + nsIStringBundle** aResult) 1.642 +{ 1.643 + NS_ENSURE_ARG_POINTER(aResult); 1.644 + 1.645 + nsresult res; 1.646 + 1.647 + nsExtensibleStringBundle * bundle = new nsExtensibleStringBundle(); 1.648 + 1.649 + res = bundle->Init(aCategory, this); 1.650 + if (NS_FAILED(res)) { 1.651 + delete bundle; 1.652 + return res; 1.653 + } 1.654 + 1.655 + res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult); 1.656 + if (NS_FAILED(res)) delete bundle; 1.657 + 1.658 + return res; 1.659 +} 1.660 + 1.661 +#define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties" 1.662 + 1.663 +nsresult 1.664 +nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus, 1.665 + uint32_t argCount, char16_t** argArray, 1.666 + char16_t* *result) 1.667 +{ 1.668 + nsresult rv; 1.669 + nsXPIDLCString key; 1.670 + 1.671 + // try looking up the error message with the int key: 1.672 + uint16_t code = NS_ERROR_GET_CODE(aStatus); 1.673 + rv = bundle->FormatStringFromID(code, (const char16_t**)argArray, argCount, result); 1.674 + 1.675 + // If the int key fails, try looking up the default error message. E.g. print: 1.676 + // An unknown error has occurred (0x804B0003). 1.677 + if (NS_FAILED(rv)) { 1.678 + nsAutoString statusStr; 1.679 + statusStr.AppendInt(static_cast<uint32_t>(aStatus), 16); 1.680 + const char16_t* otherArgArray[1]; 1.681 + otherArgArray[0] = statusStr.get(); 1.682 + uint16_t code = NS_ERROR_GET_CODE(NS_ERROR_FAILURE); 1.683 + rv = bundle->FormatStringFromID(code, otherArgArray, 1, result); 1.684 + } 1.685 + 1.686 + return rv; 1.687 +} 1.688 + 1.689 +NS_IMETHODIMP 1.690 +nsStringBundleService::FormatStatusMessage(nsresult aStatus, 1.691 + const char16_t* aStatusArg, 1.692 + char16_t* *result) 1.693 +{ 1.694 + nsresult rv; 1.695 + uint32_t i, argCount = 0; 1.696 + nsCOMPtr<nsIStringBundle> bundle; 1.697 + nsXPIDLCString stringBundleURL; 1.698 + 1.699 + // XXX hack for mailnews who has already formatted their messages: 1.700 + if (aStatus == NS_OK && aStatusArg) { 1.701 + *result = NS_strdup(aStatusArg); 1.702 + NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY); 1.703 + return NS_OK; 1.704 + } 1.705 + 1.706 + if (aStatus == NS_OK) { 1.707 + return NS_ERROR_FAILURE; // no message to format 1.708 + } 1.709 + 1.710 + // format the arguments: 1.711 + const nsDependentString args(aStatusArg); 1.712 + argCount = args.CountChar(char16_t('\n')) + 1; 1.713 + NS_ENSURE_ARG(argCount <= 10); // enforce 10-parameter limit 1.714 + char16_t* argArray[10]; 1.715 + 1.716 + // convert the aStatusArg into a char16_t array 1.717 + if (argCount == 1) { 1.718 + // avoid construction for the simple case: 1.719 + argArray[0] = (char16_t*)aStatusArg; 1.720 + } 1.721 + else if (argCount > 1) { 1.722 + int32_t offset = 0; 1.723 + for (i = 0; i < argCount; i++) { 1.724 + int32_t pos = args.FindChar('\n', offset); 1.725 + if (pos == -1) 1.726 + pos = args.Length(); 1.727 + argArray[i] = ToNewUnicode(Substring(args, offset, pos - offset)); 1.728 + if (argArray[i] == nullptr) { 1.729 + rv = NS_ERROR_OUT_OF_MEMORY; 1.730 + argCount = i - 1; // don't try to free uninitialized memory 1.731 + goto done; 1.732 + } 1.733 + offset = pos + 1; 1.734 + } 1.735 + } 1.736 + 1.737 + // find the string bundle for the error's module: 1.738 + rv = mErrorService->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus), 1.739 + getter_Copies(stringBundleURL)); 1.740 + if (NS_SUCCEEDED(rv)) { 1.741 + rv = getStringBundle(stringBundleURL, getter_AddRefs(bundle)); 1.742 + if (NS_SUCCEEDED(rv)) { 1.743 + rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result); 1.744 + } 1.745 + } 1.746 + if (NS_FAILED(rv)) { 1.747 + rv = getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle)); 1.748 + if (NS_SUCCEEDED(rv)) { 1.749 + rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result); 1.750 + } 1.751 + } 1.752 + 1.753 +done: 1.754 + if (argCount > 1) { 1.755 + for (i = 0; i < argCount; i++) { 1.756 + if (argArray[i]) 1.757 + nsMemory::Free(argArray[i]); 1.758 + } 1.759 + } 1.760 + return rv; 1.761 +}