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