intl/strres/src/nsStringBundle.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsStringBundle.h"
michael@0 7 #include "nsID.h"
michael@0 8 #include "nsString.h"
michael@0 9 #include "nsIStringBundle.h"
michael@0 10 #include "nsStringBundleService.h"
michael@0 11 #include "nsStringBundleTextOverride.h"
michael@0 12 #include "nsISupportsPrimitives.h"
michael@0 13 #include "nsIMutableArray.h"
michael@0 14 #include "nsArrayEnumerator.h"
michael@0 15 #include "nscore.h"
michael@0 16 #include "nsHashtable.h"
michael@0 17 #include "nsMemory.h"
michael@0 18 #include "nsNetUtil.h"
michael@0 19 #include "nsIObserverService.h"
michael@0 20 #include "nsCOMArray.h"
michael@0 21 #include "nsTextFormatter.h"
michael@0 22 #include "nsIErrorService.h"
michael@0 23 #include "nsICategoryManager.h"
michael@0 24
michael@0 25 // for async loading
michael@0 26 #ifdef ASYNC_LOADING
michael@0 27 #include "nsIBinaryInputStream.h"
michael@0 28 #include "nsIStringStream.h"
michael@0 29 #endif
michael@0 30
michael@0 31 using namespace mozilla;
michael@0 32
michael@0 33 static NS_DEFINE_CID(kErrorServiceCID, NS_ERRORSERVICE_CID);
michael@0 34
michael@0 35 nsStringBundle::~nsStringBundle()
michael@0 36 {
michael@0 37 }
michael@0 38
michael@0 39 nsStringBundle::nsStringBundle(const char* aURLSpec,
michael@0 40 nsIStringBundleOverride* aOverrideStrings) :
michael@0 41 mPropertiesURL(aURLSpec),
michael@0 42 mOverrideStrings(aOverrideStrings),
michael@0 43 mReentrantMonitor("nsStringBundle.mReentrantMonitor"),
michael@0 44 mAttemptedLoad(false),
michael@0 45 mLoaded(false)
michael@0 46 {
michael@0 47 }
michael@0 48
michael@0 49 nsresult
michael@0 50 nsStringBundle::LoadProperties()
michael@0 51 {
michael@0 52 // this is different than mLoaded, because we only want to attempt
michael@0 53 // to load once
michael@0 54 // we only want to load once, but if we've tried once and failed,
michael@0 55 // continue to throw an error!
michael@0 56 if (mAttemptedLoad) {
michael@0 57 if (mLoaded)
michael@0 58 return NS_OK;
michael@0 59
michael@0 60 return NS_ERROR_UNEXPECTED;
michael@0 61 }
michael@0 62
michael@0 63 mAttemptedLoad = true;
michael@0 64
michael@0 65 nsresult rv;
michael@0 66
michael@0 67 // do it synchronously
michael@0 68 nsCOMPtr<nsIURI> uri;
michael@0 69 rv = NS_NewURI(getter_AddRefs(uri), mPropertiesURL);
michael@0 70 if (NS_FAILED(rv)) return rv;
michael@0 71
michael@0 72 // We don't use NS_OpenURI because we want to tweak the channel
michael@0 73 nsCOMPtr<nsIChannel> channel;
michael@0 74 rv = NS_NewChannel(getter_AddRefs(channel), uri);
michael@0 75 if (NS_FAILED(rv)) return rv;
michael@0 76
michael@0 77 // It's a string bundle. We expect a text/plain type, so set that as hint
michael@0 78 channel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
michael@0 79
michael@0 80 nsCOMPtr<nsIInputStream> in;
michael@0 81 rv = channel->Open(getter_AddRefs(in));
michael@0 82 if (NS_FAILED(rv)) return rv;
michael@0 83
michael@0 84 NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream");
michael@0 85 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE);
michael@0 86
michael@0 87 static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID);
michael@0 88 mProps = do_CreateInstance(kPersistentPropertiesCID, &rv);
michael@0 89 NS_ENSURE_SUCCESS(rv, rv);
michael@0 90
michael@0 91 mAttemptedLoad = mLoaded = true;
michael@0 92 rv = mProps->Load(in);
michael@0 93
michael@0 94 mLoaded = NS_SUCCEEDED(rv);
michael@0 95
michael@0 96 return rv;
michael@0 97 }
michael@0 98
michael@0 99
michael@0 100 nsresult
michael@0 101 nsStringBundle::GetStringFromID(int32_t aID, nsAString& aResult)
michael@0 102 {
michael@0 103 ReentrantMonitorAutoEnter automon(mReentrantMonitor);
michael@0 104 nsAutoCString name;
michael@0 105 name.AppendInt(aID, 10);
michael@0 106
michael@0 107 nsresult rv;
michael@0 108
michael@0 109 // try override first
michael@0 110 if (mOverrideStrings) {
michael@0 111 rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
michael@0 112 name,
michael@0 113 aResult);
michael@0 114 if (NS_SUCCEEDED(rv)) return rv;
michael@0 115 }
michael@0 116
michael@0 117 rv = mProps->GetStringProperty(name, aResult);
michael@0 118
michael@0 119 return rv;
michael@0 120 }
michael@0 121
michael@0 122 nsresult
michael@0 123 nsStringBundle::GetStringFromName(const nsAString& aName,
michael@0 124 nsAString& aResult)
michael@0 125 {
michael@0 126 nsresult rv;
michael@0 127
michael@0 128 // try override first
michael@0 129 if (mOverrideStrings) {
michael@0 130 rv = mOverrideStrings->GetStringFromName(mPropertiesURL,
michael@0 131 NS_ConvertUTF16toUTF8(aName),
michael@0 132 aResult);
michael@0 133 if (NS_SUCCEEDED(rv)) return rv;
michael@0 134 }
michael@0 135
michael@0 136 rv = mProps->GetStringProperty(NS_ConvertUTF16toUTF8(aName), aResult);
michael@0 137 return rv;
michael@0 138 }
michael@0 139
michael@0 140 NS_IMETHODIMP
michael@0 141 nsStringBundle::FormatStringFromID(int32_t aID,
michael@0 142 const char16_t **aParams,
michael@0 143 uint32_t aLength,
michael@0 144 char16_t ** aResult)
michael@0 145 {
michael@0 146 nsAutoString idStr;
michael@0 147 idStr.AppendInt(aID, 10);
michael@0 148
michael@0 149 return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
michael@0 150 }
michael@0 151
michael@0 152 // this function supports at most 10 parameters.. see below for why
michael@0 153 NS_IMETHODIMP
michael@0 154 nsStringBundle::FormatStringFromName(const char16_t *aName,
michael@0 155 const char16_t **aParams,
michael@0 156 uint32_t aLength,
michael@0 157 char16_t **aResult)
michael@0 158 {
michael@0 159 NS_ENSURE_ARG_POINTER(aName);
michael@0 160 NS_ASSERTION(aParams && aLength, "FormatStringFromName() without format parameters: use GetStringFromName() instead");
michael@0 161 NS_ENSURE_ARG_POINTER(aResult);
michael@0 162
michael@0 163 nsresult rv;
michael@0 164 rv = LoadProperties();
michael@0 165 if (NS_FAILED(rv)) return rv;
michael@0 166
michael@0 167 nsAutoString formatStr;
michael@0 168 rv = GetStringFromName(nsDependentString(aName), formatStr);
michael@0 169 if (NS_FAILED(rv)) return rv;
michael@0 170
michael@0 171 return FormatString(formatStr.get(), aParams, aLength, aResult);
michael@0 172 }
michael@0 173
michael@0 174
michael@0 175 NS_IMPL_ISUPPORTS(nsStringBundle, nsIStringBundle)
michael@0 176
michael@0 177 /* void GetStringFromID (in long aID, out wstring aResult); */
michael@0 178 NS_IMETHODIMP
michael@0 179 nsStringBundle::GetStringFromID(int32_t aID, char16_t **aResult)
michael@0 180 {
michael@0 181 nsresult rv;
michael@0 182 rv = LoadProperties();
michael@0 183 if (NS_FAILED(rv)) return rv;
michael@0 184
michael@0 185 *aResult = nullptr;
michael@0 186 nsAutoString tmpstr;
michael@0 187
michael@0 188 rv = GetStringFromID(aID, tmpstr);
michael@0 189 NS_ENSURE_SUCCESS(rv, rv);
michael@0 190
michael@0 191 *aResult = ToNewUnicode(tmpstr);
michael@0 192 NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
michael@0 193
michael@0 194 return NS_OK;
michael@0 195 }
michael@0 196
michael@0 197 /* void GetStringFromName (in wstring aName, out wstring aResult); */
michael@0 198 NS_IMETHODIMP
michael@0 199 nsStringBundle::GetStringFromName(const char16_t *aName, char16_t **aResult)
michael@0 200 {
michael@0 201 NS_ENSURE_ARG_POINTER(aName);
michael@0 202 NS_ENSURE_ARG_POINTER(aResult);
michael@0 203
michael@0 204 nsresult rv;
michael@0 205 rv = LoadProperties();
michael@0 206 if (NS_FAILED(rv)) return rv;
michael@0 207
michael@0 208 ReentrantMonitorAutoEnter automon(mReentrantMonitor);
michael@0 209 *aResult = nullptr;
michael@0 210 nsAutoString tmpstr;
michael@0 211 rv = GetStringFromName(nsDependentString(aName), tmpstr);
michael@0 212 if (NS_FAILED(rv))
michael@0 213 {
michael@0 214 #if 0
michael@0 215 // it is not uncommon for apps to request a string name which may not exist
michael@0 216 // so be quiet about it.
michael@0 217 NS_WARNING("String missing from string bundle");
michael@0 218 printf(" '%s' missing from bundle %s\n", NS_ConvertUTF16toUTF8(aName).get(), mPropertiesURL.get());
michael@0 219 #endif
michael@0 220 return rv;
michael@0 221 }
michael@0 222
michael@0 223 *aResult = ToNewUnicode(tmpstr);
michael@0 224 NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
michael@0 225
michael@0 226 return NS_OK;
michael@0 227 }
michael@0 228
michael@0 229 nsresult
michael@0 230 nsStringBundle::GetCombinedEnumeration(nsIStringBundleOverride* aOverrideStrings,
michael@0 231 nsISimpleEnumerator** aResult)
michael@0 232 {
michael@0 233 nsCOMPtr<nsISupports> supports;
michael@0 234 nsCOMPtr<nsIPropertyElement> propElement;
michael@0 235
michael@0 236 nsresult rv;
michael@0 237
michael@0 238 nsCOMPtr<nsIMutableArray> resultArray =
michael@0 239 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
michael@0 240 NS_ENSURE_SUCCESS(rv, rv);
michael@0 241
michael@0 242 // first, append the override elements
michael@0 243 nsCOMPtr<nsISimpleEnumerator> overrideEnumerator;
michael@0 244 rv = aOverrideStrings->EnumerateKeysInBundle(mPropertiesURL,
michael@0 245 getter_AddRefs(overrideEnumerator));
michael@0 246
michael@0 247 bool hasMore;
michael@0 248 rv = overrideEnumerator->HasMoreElements(&hasMore);
michael@0 249 NS_ENSURE_SUCCESS(rv, rv);
michael@0 250 while (hasMore) {
michael@0 251
michael@0 252 rv = overrideEnumerator->GetNext(getter_AddRefs(supports));
michael@0 253 if (NS_SUCCEEDED(rv))
michael@0 254 resultArray->AppendElement(supports, false);
michael@0 255
michael@0 256 rv = overrideEnumerator->HasMoreElements(&hasMore);
michael@0 257 NS_ENSURE_SUCCESS(rv, rv);
michael@0 258 }
michael@0 259
michael@0 260 // ok, now we have the override elements in resultArray
michael@0 261 nsCOMPtr<nsISimpleEnumerator> propEnumerator;
michael@0 262 rv = mProps->Enumerate(getter_AddRefs(propEnumerator));
michael@0 263 if (NS_FAILED(rv)) {
michael@0 264 // no elements in mProps anyway, just return what we have
michael@0 265 return NS_NewArrayEnumerator(aResult, resultArray);
michael@0 266 }
michael@0 267
michael@0 268 // second, append all the elements that are in mProps
michael@0 269 do {
michael@0 270 rv = propEnumerator->GetNext(getter_AddRefs(supports));
michael@0 271 if (NS_SUCCEEDED(rv) &&
michael@0 272 (propElement = do_QueryInterface(supports, &rv))) {
michael@0 273
michael@0 274 // now check if its in the override bundle
michael@0 275 nsAutoCString key;
michael@0 276 propElement->GetKey(key);
michael@0 277
michael@0 278 nsAutoString value;
michael@0 279 rv = aOverrideStrings->GetStringFromName(mPropertiesURL, key, value);
michael@0 280
michael@0 281 // if it isn't there, then it is safe to append
michael@0 282 if (NS_FAILED(rv))
michael@0 283 resultArray->AppendElement(propElement, false);
michael@0 284 }
michael@0 285
michael@0 286 rv = propEnumerator->HasMoreElements(&hasMore);
michael@0 287 NS_ENSURE_SUCCESS(rv, rv);
michael@0 288 } while (hasMore);
michael@0 289
michael@0 290 return resultArray->Enumerate(aResult);
michael@0 291 }
michael@0 292
michael@0 293
michael@0 294 NS_IMETHODIMP
michael@0 295 nsStringBundle::GetSimpleEnumeration(nsISimpleEnumerator** elements)
michael@0 296 {
michael@0 297 if (!elements)
michael@0 298 return NS_ERROR_INVALID_POINTER;
michael@0 299
michael@0 300 nsresult rv;
michael@0 301 rv = LoadProperties();
michael@0 302 if (NS_FAILED(rv)) return rv;
michael@0 303
michael@0 304 if (mOverrideStrings)
michael@0 305 return GetCombinedEnumeration(mOverrideStrings, elements);
michael@0 306
michael@0 307 return mProps->Enumerate(elements);
michael@0 308 }
michael@0 309
michael@0 310 nsresult
michael@0 311 nsStringBundle::FormatString(const char16_t *aFormatStr,
michael@0 312 const char16_t **aParams, uint32_t aLength,
michael@0 313 char16_t **aResult)
michael@0 314 {
michael@0 315 NS_ENSURE_ARG_POINTER(aResult);
michael@0 316 NS_ENSURE_ARG(aLength <= 10); // enforce 10-parameter limit
michael@0 317
michael@0 318 // implementation note: you would think you could use vsmprintf
michael@0 319 // to build up an arbitrary length array.. except that there
michael@0 320 // is no way to build up a va_list at runtime!
michael@0 321 // Don't believe me? See:
michael@0 322 // http://www.eskimo.com/~scs/C-faq/q15.13.html
michael@0 323 // -alecf
michael@0 324 char16_t *text =
michael@0 325 nsTextFormatter::smprintf(aFormatStr,
michael@0 326 aLength >= 1 ? aParams[0] : nullptr,
michael@0 327 aLength >= 2 ? aParams[1] : nullptr,
michael@0 328 aLength >= 3 ? aParams[2] : nullptr,
michael@0 329 aLength >= 4 ? aParams[3] : nullptr,
michael@0 330 aLength >= 5 ? aParams[4] : nullptr,
michael@0 331 aLength >= 6 ? aParams[5] : nullptr,
michael@0 332 aLength >= 7 ? aParams[6] : nullptr,
michael@0 333 aLength >= 8 ? aParams[7] : nullptr,
michael@0 334 aLength >= 9 ? aParams[8] : nullptr,
michael@0 335 aLength >= 10 ? aParams[9] : nullptr);
michael@0 336
michael@0 337 if (!text) {
michael@0 338 *aResult = nullptr;
michael@0 339 return NS_ERROR_OUT_OF_MEMORY;
michael@0 340 }
michael@0 341
michael@0 342 // nsTextFormatter does not use the shared nsMemory allocator.
michael@0 343 // Instead it is required to free the memory it allocates using
michael@0 344 // nsTextFormatter::smprintf_free. Let's instead use nsMemory based
michael@0 345 // allocation for the result that we give out and free the string
michael@0 346 // returned by smprintf ourselves!
michael@0 347 *aResult = NS_strdup(text);
michael@0 348 nsTextFormatter::smprintf_free(text);
michael@0 349
michael@0 350 return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
michael@0 351 }
michael@0 352
michael@0 353 NS_IMPL_ISUPPORTS(nsExtensibleStringBundle, nsIStringBundle)
michael@0 354
michael@0 355 nsExtensibleStringBundle::nsExtensibleStringBundle()
michael@0 356 {
michael@0 357 mLoaded = false;
michael@0 358 }
michael@0 359
michael@0 360 nsresult
michael@0 361 nsExtensibleStringBundle::Init(const char * aCategory,
michael@0 362 nsIStringBundleService* aBundleService)
michael@0 363 {
michael@0 364
michael@0 365 nsresult rv;
michael@0 366 nsCOMPtr<nsICategoryManager> catman =
michael@0 367 do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
michael@0 368 if (NS_FAILED(rv)) return rv;
michael@0 369
michael@0 370 nsCOMPtr<nsISimpleEnumerator> enumerator;
michael@0 371 rv = catman->EnumerateCategory(aCategory, getter_AddRefs(enumerator));
michael@0 372 if (NS_FAILED(rv)) return rv;
michael@0 373
michael@0 374 bool hasMore;
michael@0 375 while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
michael@0 376 nsCOMPtr<nsISupports> supports;
michael@0 377 rv = enumerator->GetNext(getter_AddRefs(supports));
michael@0 378 if (NS_FAILED(rv))
michael@0 379 continue;
michael@0 380
michael@0 381 nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports, &rv);
michael@0 382 if (NS_FAILED(rv))
michael@0 383 continue;
michael@0 384
michael@0 385 nsAutoCString name;
michael@0 386 rv = supStr->GetData(name);
michael@0 387 if (NS_FAILED(rv))
michael@0 388 continue;
michael@0 389
michael@0 390 nsCOMPtr<nsIStringBundle> bundle;
michael@0 391 rv = aBundleService->CreateBundle(name.get(), getter_AddRefs(bundle));
michael@0 392 if (NS_FAILED(rv))
michael@0 393 continue;
michael@0 394
michael@0 395 mBundles.AppendObject(bundle);
michael@0 396 }
michael@0 397
michael@0 398 return rv;
michael@0 399 }
michael@0 400
michael@0 401 nsExtensibleStringBundle::~nsExtensibleStringBundle()
michael@0 402 {
michael@0 403 }
michael@0 404
michael@0 405 nsresult nsExtensibleStringBundle::GetStringFromID(int32_t aID, char16_t ** aResult)
michael@0 406 {
michael@0 407 nsresult rv;
michael@0 408 const uint32_t size = mBundles.Count();
michael@0 409 for (uint32_t i = 0; i < size; ++i) {
michael@0 410 nsIStringBundle *bundle = mBundles[i];
michael@0 411 if (bundle) {
michael@0 412 rv = bundle->GetStringFromID(aID, aResult);
michael@0 413 if (NS_SUCCEEDED(rv))
michael@0 414 return NS_OK;
michael@0 415 }
michael@0 416 }
michael@0 417
michael@0 418 return NS_ERROR_FAILURE;
michael@0 419 }
michael@0 420
michael@0 421 nsresult nsExtensibleStringBundle::GetStringFromName(const char16_t *aName,
michael@0 422 char16_t ** aResult)
michael@0 423 {
michael@0 424 nsresult rv;
michael@0 425 const uint32_t size = mBundles.Count();
michael@0 426 for (uint32_t i = 0; i < size; ++i) {
michael@0 427 nsIStringBundle* bundle = mBundles[i];
michael@0 428 if (bundle) {
michael@0 429 rv = bundle->GetStringFromName(aName, aResult);
michael@0 430 if (NS_SUCCEEDED(rv))
michael@0 431 return NS_OK;
michael@0 432 }
michael@0 433 }
michael@0 434
michael@0 435 return NS_ERROR_FAILURE;
michael@0 436 }
michael@0 437
michael@0 438 NS_IMETHODIMP
michael@0 439 nsExtensibleStringBundle::FormatStringFromID(int32_t aID,
michael@0 440 const char16_t ** aParams,
michael@0 441 uint32_t aLength,
michael@0 442 char16_t ** aResult)
michael@0 443 {
michael@0 444 nsAutoString idStr;
michael@0 445 idStr.AppendInt(aID, 10);
michael@0 446 return FormatStringFromName(idStr.get(), aParams, aLength, aResult);
michael@0 447 }
michael@0 448
michael@0 449 NS_IMETHODIMP
michael@0 450 nsExtensibleStringBundle::FormatStringFromName(const char16_t *aName,
michael@0 451 const char16_t ** aParams,
michael@0 452 uint32_t aLength,
michael@0 453 char16_t ** aResult)
michael@0 454 {
michael@0 455 nsXPIDLString formatStr;
michael@0 456 nsresult rv;
michael@0 457 rv = GetStringFromName(aName, getter_Copies(formatStr));
michael@0 458 if (NS_FAILED(rv))
michael@0 459 return rv;
michael@0 460
michael@0 461 return nsStringBundle::FormatString(formatStr, aParams, aLength, aResult);
michael@0 462 }
michael@0 463
michael@0 464 nsresult nsExtensibleStringBundle::GetSimpleEnumeration(nsISimpleEnumerator ** aResult)
michael@0 465 {
michael@0 466 // XXX write me
michael@0 467 *aResult = nullptr;
michael@0 468 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 469 }
michael@0 470
michael@0 471 /////////////////////////////////////////////////////////////////////////////////////////
michael@0 472
michael@0 473 #define MAX_CACHED_BUNDLES 16
michael@0 474
michael@0 475 struct bundleCacheEntry_t MOZ_FINAL : public LinkedListElement<bundleCacheEntry_t> {
michael@0 476 nsCString mHashKey;
michael@0 477 nsCOMPtr<nsIStringBundle> mBundle;
michael@0 478
michael@0 479 bundleCacheEntry_t()
michael@0 480 {
michael@0 481 MOZ_COUNT_CTOR(bundleCacheEntry_t);
michael@0 482 }
michael@0 483
michael@0 484 ~bundleCacheEntry_t()
michael@0 485 {
michael@0 486 MOZ_COUNT_DTOR(bundleCacheEntry_t);
michael@0 487 }
michael@0 488 };
michael@0 489
michael@0 490
michael@0 491 nsStringBundleService::nsStringBundleService() :
michael@0 492 mBundleMap(MAX_CACHED_BUNDLES)
michael@0 493 {
michael@0 494 mErrorService = do_GetService(kErrorServiceCID);
michael@0 495 NS_ASSERTION(mErrorService, "Couldn't get error service");
michael@0 496 }
michael@0 497
michael@0 498 NS_IMPL_ISUPPORTS(nsStringBundleService,
michael@0 499 nsIStringBundleService,
michael@0 500 nsIObserver,
michael@0 501 nsISupportsWeakReference)
michael@0 502
michael@0 503 nsStringBundleService::~nsStringBundleService()
michael@0 504 {
michael@0 505 flushBundleCache();
michael@0 506 }
michael@0 507
michael@0 508 nsresult
michael@0 509 nsStringBundleService::Init()
michael@0 510 {
michael@0 511 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
michael@0 512 if (os) {
michael@0 513 os->AddObserver(this, "memory-pressure", true);
michael@0 514 os->AddObserver(this, "profile-do-change", true);
michael@0 515 os->AddObserver(this, "chrome-flush-caches", true);
michael@0 516 os->AddObserver(this, "xpcom-category-entry-added", true);
michael@0 517 }
michael@0 518
michael@0 519 // instantiate the override service, if there is any.
michael@0 520 // at some point we probably want to make this a category, and
michael@0 521 // support multiple overrides
michael@0 522 mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
michael@0 523
michael@0 524 return NS_OK;
michael@0 525 }
michael@0 526
michael@0 527 NS_IMETHODIMP
michael@0 528 nsStringBundleService::Observe(nsISupports* aSubject,
michael@0 529 const char* aTopic,
michael@0 530 const char16_t* aSomeData)
michael@0 531 {
michael@0 532 if (strcmp("memory-pressure", aTopic) == 0 ||
michael@0 533 strcmp("profile-do-change", aTopic) == 0 ||
michael@0 534 strcmp("chrome-flush-caches", aTopic) == 0)
michael@0 535 {
michael@0 536 flushBundleCache();
michael@0 537 }
michael@0 538 else if (strcmp("xpcom-category-entry-added", aTopic) == 0 &&
michael@0 539 NS_LITERAL_STRING("xpcom-autoregistration").Equals(aSomeData))
michael@0 540 {
michael@0 541 mOverrideStrings = do_GetService(NS_STRINGBUNDLETEXTOVERRIDE_CONTRACTID);
michael@0 542 }
michael@0 543
michael@0 544 return NS_OK;
michael@0 545 }
michael@0 546
michael@0 547 void
michael@0 548 nsStringBundleService::flushBundleCache()
michael@0 549 {
michael@0 550 // release all bundles in the cache
michael@0 551 mBundleMap.Clear();
michael@0 552
michael@0 553 while (!mBundleCache.isEmpty()) {
michael@0 554 delete mBundleCache.popFirst();
michael@0 555 }
michael@0 556 }
michael@0 557
michael@0 558 NS_IMETHODIMP
michael@0 559 nsStringBundleService::FlushBundles()
michael@0 560 {
michael@0 561 flushBundleCache();
michael@0 562 return NS_OK;
michael@0 563 }
michael@0 564
michael@0 565 nsresult
michael@0 566 nsStringBundleService::getStringBundle(const char *aURLSpec,
michael@0 567 nsIStringBundle **aResult)
michael@0 568 {
michael@0 569 nsDependentCString key(aURLSpec);
michael@0 570 bundleCacheEntry_t* cacheEntry = mBundleMap.Get(key);
michael@0 571
michael@0 572 if (cacheEntry) {
michael@0 573 // cache hit!
michael@0 574 // remove it from the list, it will later be reinserted
michael@0 575 // at the head of the list
michael@0 576 cacheEntry->remove();
michael@0 577
michael@0 578 } else {
michael@0 579
michael@0 580 // hasn't been cached, so insert it into the hash table
michael@0 581 nsRefPtr<nsStringBundle> bundle = new nsStringBundle(aURLSpec, mOverrideStrings);
michael@0 582 cacheEntry = insertIntoCache(bundle.forget(), key);
michael@0 583 }
michael@0 584
michael@0 585 // at this point the cacheEntry should exist in the hashtable,
michael@0 586 // but is not in the LRU cache.
michael@0 587 // put the cache entry at the front of the list
michael@0 588 mBundleCache.insertFront(cacheEntry);
michael@0 589
michael@0 590 // finally, return the value
michael@0 591 *aResult = cacheEntry->mBundle;
michael@0 592 NS_ADDREF(*aResult);
michael@0 593
michael@0 594 return NS_OK;
michael@0 595 }
michael@0 596
michael@0 597 bundleCacheEntry_t *
michael@0 598 nsStringBundleService::insertIntoCache(already_AddRefed<nsIStringBundle> aBundle,
michael@0 599 nsCString &aHashKey)
michael@0 600 {
michael@0 601 bundleCacheEntry_t *cacheEntry;
michael@0 602
michael@0 603 if (mBundleMap.Count() < MAX_CACHED_BUNDLES) {
michael@0 604 // cache not full - create a new entry
michael@0 605 cacheEntry = new bundleCacheEntry_t();
michael@0 606 } else {
michael@0 607 // cache is full
michael@0 608 // take the last entry in the list, and recycle it.
michael@0 609 cacheEntry = mBundleCache.getLast();
michael@0 610
michael@0 611 // remove it from the hash table and linked list
michael@0 612 NS_ASSERTION(mBundleMap.Contains(cacheEntry->mHashKey),
michael@0 613 "Element will not be removed!");
michael@0 614 mBundleMap.Remove(cacheEntry->mHashKey);
michael@0 615 cacheEntry->remove();
michael@0 616 }
michael@0 617
michael@0 618 // at this point we have a new cacheEntry that doesn't exist
michael@0 619 // in the hashtable, so set up the cacheEntry
michael@0 620 cacheEntry->mHashKey = aHashKey;
michael@0 621 cacheEntry->mBundle = aBundle;
michael@0 622
michael@0 623 // insert the entry into the cache and map, make it the MRU
michael@0 624 mBundleMap.Put(cacheEntry->mHashKey, cacheEntry);
michael@0 625
michael@0 626 return cacheEntry;
michael@0 627 }
michael@0 628
michael@0 629 NS_IMETHODIMP
michael@0 630 nsStringBundleService::CreateBundle(const char* aURLSpec,
michael@0 631 nsIStringBundle** aResult)
michael@0 632 {
michael@0 633 return getStringBundle(aURLSpec,aResult);
michael@0 634 }
michael@0 635
michael@0 636 NS_IMETHODIMP
michael@0 637 nsStringBundleService::CreateExtensibleBundle(const char* aCategory,
michael@0 638 nsIStringBundle** aResult)
michael@0 639 {
michael@0 640 NS_ENSURE_ARG_POINTER(aResult);
michael@0 641
michael@0 642 nsresult res;
michael@0 643
michael@0 644 nsExtensibleStringBundle * bundle = new nsExtensibleStringBundle();
michael@0 645
michael@0 646 res = bundle->Init(aCategory, this);
michael@0 647 if (NS_FAILED(res)) {
michael@0 648 delete bundle;
michael@0 649 return res;
michael@0 650 }
michael@0 651
michael@0 652 res = bundle->QueryInterface(NS_GET_IID(nsIStringBundle), (void**) aResult);
michael@0 653 if (NS_FAILED(res)) delete bundle;
michael@0 654
michael@0 655 return res;
michael@0 656 }
michael@0 657
michael@0 658 #define GLOBAL_PROPERTIES "chrome://global/locale/global-strres.properties"
michael@0 659
michael@0 660 nsresult
michael@0 661 nsStringBundleService::FormatWithBundle(nsIStringBundle* bundle, nsresult aStatus,
michael@0 662 uint32_t argCount, char16_t** argArray,
michael@0 663 char16_t* *result)
michael@0 664 {
michael@0 665 nsresult rv;
michael@0 666 nsXPIDLCString key;
michael@0 667
michael@0 668 // try looking up the error message with the int key:
michael@0 669 uint16_t code = NS_ERROR_GET_CODE(aStatus);
michael@0 670 rv = bundle->FormatStringFromID(code, (const char16_t**)argArray, argCount, result);
michael@0 671
michael@0 672 // If the int key fails, try looking up the default error message. E.g. print:
michael@0 673 // An unknown error has occurred (0x804B0003).
michael@0 674 if (NS_FAILED(rv)) {
michael@0 675 nsAutoString statusStr;
michael@0 676 statusStr.AppendInt(static_cast<uint32_t>(aStatus), 16);
michael@0 677 const char16_t* otherArgArray[1];
michael@0 678 otherArgArray[0] = statusStr.get();
michael@0 679 uint16_t code = NS_ERROR_GET_CODE(NS_ERROR_FAILURE);
michael@0 680 rv = bundle->FormatStringFromID(code, otherArgArray, 1, result);
michael@0 681 }
michael@0 682
michael@0 683 return rv;
michael@0 684 }
michael@0 685
michael@0 686 NS_IMETHODIMP
michael@0 687 nsStringBundleService::FormatStatusMessage(nsresult aStatus,
michael@0 688 const char16_t* aStatusArg,
michael@0 689 char16_t* *result)
michael@0 690 {
michael@0 691 nsresult rv;
michael@0 692 uint32_t i, argCount = 0;
michael@0 693 nsCOMPtr<nsIStringBundle> bundle;
michael@0 694 nsXPIDLCString stringBundleURL;
michael@0 695
michael@0 696 // XXX hack for mailnews who has already formatted their messages:
michael@0 697 if (aStatus == NS_OK && aStatusArg) {
michael@0 698 *result = NS_strdup(aStatusArg);
michael@0 699 NS_ENSURE_TRUE(*result, NS_ERROR_OUT_OF_MEMORY);
michael@0 700 return NS_OK;
michael@0 701 }
michael@0 702
michael@0 703 if (aStatus == NS_OK) {
michael@0 704 return NS_ERROR_FAILURE; // no message to format
michael@0 705 }
michael@0 706
michael@0 707 // format the arguments:
michael@0 708 const nsDependentString args(aStatusArg);
michael@0 709 argCount = args.CountChar(char16_t('\n')) + 1;
michael@0 710 NS_ENSURE_ARG(argCount <= 10); // enforce 10-parameter limit
michael@0 711 char16_t* argArray[10];
michael@0 712
michael@0 713 // convert the aStatusArg into a char16_t array
michael@0 714 if (argCount == 1) {
michael@0 715 // avoid construction for the simple case:
michael@0 716 argArray[0] = (char16_t*)aStatusArg;
michael@0 717 }
michael@0 718 else if (argCount > 1) {
michael@0 719 int32_t offset = 0;
michael@0 720 for (i = 0; i < argCount; i++) {
michael@0 721 int32_t pos = args.FindChar('\n', offset);
michael@0 722 if (pos == -1)
michael@0 723 pos = args.Length();
michael@0 724 argArray[i] = ToNewUnicode(Substring(args, offset, pos - offset));
michael@0 725 if (argArray[i] == nullptr) {
michael@0 726 rv = NS_ERROR_OUT_OF_MEMORY;
michael@0 727 argCount = i - 1; // don't try to free uninitialized memory
michael@0 728 goto done;
michael@0 729 }
michael@0 730 offset = pos + 1;
michael@0 731 }
michael@0 732 }
michael@0 733
michael@0 734 // find the string bundle for the error's module:
michael@0 735 rv = mErrorService->GetErrorStringBundle(NS_ERROR_GET_MODULE(aStatus),
michael@0 736 getter_Copies(stringBundleURL));
michael@0 737 if (NS_SUCCEEDED(rv)) {
michael@0 738 rv = getStringBundle(stringBundleURL, getter_AddRefs(bundle));
michael@0 739 if (NS_SUCCEEDED(rv)) {
michael@0 740 rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
michael@0 741 }
michael@0 742 }
michael@0 743 if (NS_FAILED(rv)) {
michael@0 744 rv = getStringBundle(GLOBAL_PROPERTIES, getter_AddRefs(bundle));
michael@0 745 if (NS_SUCCEEDED(rv)) {
michael@0 746 rv = FormatWithBundle(bundle, aStatus, argCount, argArray, result);
michael@0 747 }
michael@0 748 }
michael@0 749
michael@0 750 done:
michael@0 751 if (argCount > 1) {
michael@0 752 for (i = 0; i < argCount; i++) {
michael@0 753 if (argArray[i])
michael@0 754 nsMemory::Free(argArray[i]);
michael@0 755 }
michael@0 756 }
michael@0 757 return rv;
michael@0 758 }

mercurial