michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: #include "nsStringBundleTextOverride.h" michael@0: #include "nsString.h" michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: michael@0: // first we need a simple class which wraps a nsIPropertyElement and michael@0: // cuts out the leading URL from the key michael@0: class URLPropertyElement : public nsIPropertyElement michael@0: { michael@0: public: michael@0: URLPropertyElement(nsIPropertyElement *aRealElement, uint32_t aURLLength) : michael@0: mRealElement(aRealElement), michael@0: mURLLength(aURLLength) michael@0: { } michael@0: virtual ~URLPropertyElement() {} michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIPROPERTYELEMENT michael@0: michael@0: private: michael@0: nsCOMPtr mRealElement; michael@0: uint32_t mURLLength; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(URLPropertyElement, nsIPropertyElement) michael@0: michael@0: // we'll tweak the key on the way through, and remove the url prefix michael@0: NS_IMETHODIMP michael@0: URLPropertyElement::GetKey(nsACString& aKey) michael@0: { michael@0: nsresult rv = mRealElement->GetKey(aKey); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // chop off the url michael@0: aKey.Cut(0, mURLLength); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // values are unaffected michael@0: NS_IMETHODIMP michael@0: URLPropertyElement::GetValue(nsAString& aValue) michael@0: { michael@0: return mRealElement->GetValue(aValue); michael@0: } michael@0: michael@0: // setters are kind of strange, hopefully we'll never be called michael@0: NS_IMETHODIMP michael@0: URLPropertyElement::SetKey(const nsACString& aKey) michael@0: { michael@0: // this is just wrong - ideally you'd take the key, append it to michael@0: // the url, and set that as the key. However, that would require michael@0: // us to hold onto a copy of the string, and that's a waste, michael@0: // considering nobody should ever be calling this. michael@0: NS_ERROR("This makes no sense!"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: URLPropertyElement::SetValue(const nsAString& aValue) michael@0: { michael@0: return mRealElement->SetValue(aValue); michael@0: } michael@0: michael@0: michael@0: // this is a special enumerator which returns only the elements which michael@0: // are prefixed with a particular url michael@0: class nsPropertyEnumeratorByURL : public nsISimpleEnumerator michael@0: { michael@0: public: michael@0: nsPropertyEnumeratorByURL(const nsACString& aURL, michael@0: nsISimpleEnumerator* aOuter) : michael@0: mOuter(aOuter), michael@0: mURL(aURL) michael@0: { michael@0: // prepare the url once so we can use its value later michael@0: // persistent properties uses ":" as a delimiter, so escape michael@0: // that character michael@0: mURL.ReplaceSubstring(":", "%3A"); michael@0: // there is always a # between the url and the real key michael@0: mURL.Append('#'); michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSISIMPLEENUMERATOR michael@0: michael@0: virtual ~nsPropertyEnumeratorByURL() {} michael@0: private: michael@0: michael@0: // actual enumerator of all strings from nsIProperties michael@0: nsCOMPtr mOuter; michael@0: michael@0: // the current element that is valid for this url michael@0: nsCOMPtr mCurrent; michael@0: michael@0: // the url in question, pre-escaped and with the # already in it michael@0: nsCString mURL; michael@0: }; michael@0: michael@0: // michael@0: // nsStringBundleTextOverride implementation michael@0: // michael@0: NS_IMPL_ISUPPORTS(nsStringBundleTextOverride, michael@0: nsIStringBundleOverride) michael@0: michael@0: nsresult michael@0: nsStringBundleTextOverride::Init() michael@0: { michael@0: nsresult rv; michael@0: michael@0: // check for existence of custom-strings.txt michael@0: michael@0: nsCOMPtr customStringsFile; michael@0: rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, michael@0: getter_AddRefs(customStringsFile)); michael@0: michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // bail if not found - this will cause the service creation to michael@0: // bail as well, and cause this object to go away michael@0: michael@0: customStringsFile->AppendNative(NS_LITERAL_CSTRING("custom-strings.txt")); michael@0: michael@0: bool exists; michael@0: rv = customStringsFile->Exists(&exists); michael@0: if (NS_FAILED(rv) || !exists) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: NS_WARNING("Using custom-strings.txt to override string bundles."); michael@0: // read in the custom bundle. Keys are in the form michael@0: // chrome://package/locale/foo.properties:keyname michael@0: michael@0: nsAutoCString customStringsURLSpec; michael@0: rv = NS_GetURLSpecFromFile(customStringsFile, customStringsURLSpec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), customStringsURLSpec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr in; michael@0: rv = NS_OpenURI(getter_AddRefs(in), uri); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID); michael@0: mValues = do_CreateInstance(kPersistentPropertiesCID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mValues->Load(in); michael@0: michael@0: // turn this on to see the contents of custom-strings.txt michael@0: #ifdef DEBUG_alecf michael@0: nsCOMPtr enumerator; michael@0: mValues->Enumerate(getter_AddRefs(enumerator)); michael@0: NS_ASSERTION(enumerator, "no enumerator!\n"); michael@0: michael@0: printf("custom-strings.txt contains:\n"); michael@0: printf("----------------------------\n"); michael@0: michael@0: bool hasMore; michael@0: enumerator->HasMoreElements(&hasMore); michael@0: do { michael@0: nsCOMPtr sup; michael@0: enumerator->GetNext(getter_AddRefs(sup)); michael@0: michael@0: nsCOMPtr prop = do_QueryInterface(sup); michael@0: michael@0: nsAutoCString key; michael@0: nsAutoString value; michael@0: prop->GetKey(key); michael@0: prop->GetValue(value); michael@0: michael@0: printf("%s = '%s'\n", key.get(), NS_ConvertUTF16toUTF8(value).get()); michael@0: michael@0: enumerator->HasMoreElements(&hasMore); michael@0: } while (hasMore); michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleTextOverride::GetStringFromName(const nsACString& aURL, michael@0: const nsACString& key, michael@0: nsAString& aResult) michael@0: { michael@0: // concatenate url#key to get the key to read michael@0: nsAutoCString combinedURL(aURL + NS_LITERAL_CSTRING("#") + key); michael@0: michael@0: // persistent properties uses ":" as a delimiter, so escape that character michael@0: combinedURL.ReplaceSubstring(":", "%3A"); michael@0: michael@0: return mValues->GetStringProperty(combinedURL, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStringBundleTextOverride::EnumerateKeysInBundle(const nsACString& aURL, michael@0: nsISimpleEnumerator** aResult) michael@0: { michael@0: // enumerate all strings, and let the enumerator know michael@0: nsCOMPtr enumerator; michael@0: mValues->Enumerate(getter_AddRefs(enumerator)); michael@0: michael@0: // make the enumerator wrapper and pass it off michael@0: nsPropertyEnumeratorByURL* propEnum = michael@0: new nsPropertyEnumeratorByURL(aURL, enumerator); michael@0: michael@0: if (!propEnum) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(*aResult = propEnum); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // michael@0: // nsPropertyEnumeratorByURL implementation michael@0: // michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(nsPropertyEnumeratorByURL, nsISimpleEnumerator) michael@0: michael@0: NS_IMETHODIMP michael@0: nsPropertyEnumeratorByURL::GetNext(nsISupports **aResult) michael@0: { michael@0: if (!mCurrent) return NS_ERROR_UNEXPECTED; michael@0: michael@0: // wrap mCurrent instead of returning it michael@0: *aResult = new URLPropertyElement(mCurrent, mURL.Length()); michael@0: NS_ADDREF(*aResult); michael@0: michael@0: // release it so we don't return it twice michael@0: mCurrent = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPropertyEnumeratorByURL::HasMoreElements(bool * aResult) michael@0: { michael@0: bool hasMore; michael@0: mOuter->HasMoreElements(&hasMore); michael@0: while (hasMore) { michael@0: michael@0: nsCOMPtr supports; michael@0: mOuter->GetNext(getter_AddRefs(supports)); michael@0: michael@0: mCurrent = do_QueryInterface(supports); michael@0: michael@0: if (mCurrent) { michael@0: nsAutoCString curKey; michael@0: mCurrent->GetKey(curKey); michael@0: michael@0: if (StringBeginsWith(curKey, mURL)) michael@0: break; michael@0: } michael@0: michael@0: mOuter->HasMoreElements(&hasMore); michael@0: } michael@0: michael@0: if (!hasMore) michael@0: mCurrent = nullptr; michael@0: michael@0: *aResult = mCurrent ? true : false; michael@0: michael@0: return NS_OK; michael@0: }