nsStringBundle.cpp 22 KB

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