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