michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: #include "nsCOMPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsCharsetAlias.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "nsICharsetConverterManager.h" michael@0: #include "nsEncoderDecoderUtils.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsTArray.h" michael@0: #include "nsStringEnumerator.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: // just for CONTRACTIDs michael@0: #include "nsCharsetConverterManager.h" michael@0: michael@0: static nsIStringBundle * sDataBundle; michael@0: static nsIStringBundle * sTitleBundle; michael@0: michael@0: // Class nsCharsetConverterManager [implementation] michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCharsetConverterManager, nsICharsetConverterManager) michael@0: michael@0: nsCharsetConverterManager::nsCharsetConverterManager() michael@0: { michael@0: } michael@0: michael@0: nsCharsetConverterManager::~nsCharsetConverterManager() michael@0: { michael@0: } michael@0: michael@0: //static michael@0: void nsCharsetConverterManager::Shutdown() michael@0: { michael@0: NS_IF_RELEASE(sDataBundle); michael@0: NS_IF_RELEASE(sTitleBundle); michael@0: } michael@0: michael@0: static michael@0: nsresult LoadExtensibleBundle(const char* aCategory, michael@0: nsIStringBundle ** aResult) michael@0: { michael@0: nsCOMPtr sbServ = michael@0: mozilla::services::GetStringBundleService(); michael@0: if (!sbServ) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return sbServ->CreateExtensibleBundle(aCategory, aResult); michael@0: } michael@0: michael@0: static michael@0: nsresult GetBundleValue(nsIStringBundle * aBundle, michael@0: const char * aName, michael@0: const nsAFlatString& aProp, michael@0: char16_t ** aResult) michael@0: { michael@0: nsAutoString key; michael@0: michael@0: key.AssignWithConversion(aName); michael@0: ToLowerCase(key); // we lowercase the main comparison key michael@0: key.Append(aProp); michael@0: michael@0: return aBundle->GetStringFromName(key.get(), aResult); michael@0: } michael@0: michael@0: static michael@0: nsresult GetBundleValue(nsIStringBundle * aBundle, michael@0: const char * aName, michael@0: const nsAFlatString& aProp, michael@0: nsAString& aResult) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsXPIDLString value; michael@0: rv = GetBundleValue(aBundle, aName, aProp, getter_Copies(value)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: aResult = value; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: nsresult GetCharsetDataImpl(const char * aCharset, const char16_t * aProp, michael@0: nsAString& aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCharset); michael@0: // aProp can be nullptr michael@0: michael@0: if (!sDataBundle) { michael@0: nsresult rv = LoadExtensibleBundle(NS_DATA_BUNDLE_CATEGORY, &sDataBundle); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: return GetBundleValue(sDataBundle, aCharset, nsDependentString(aProp), aResult); michael@0: } michael@0: michael@0: //static michael@0: bool nsCharsetConverterManager::IsInternal(const nsACString& aCharset) michael@0: { michael@0: nsAutoString str; michael@0: // fully qualify to possibly avoid vtable call michael@0: nsresult rv = GetCharsetDataImpl(PromiseFlatCString(aCharset).get(), michael@0: MOZ_UTF16(".isInternal"), michael@0: str); michael@0: michael@0: return NS_SUCCEEDED(rv); michael@0: } michael@0: michael@0: michael@0: //----------------------------------------------------------------------------//---------------------------------------------------------------------------- michael@0: // Interface nsICharsetConverterManager [implementation] michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetUnicodeEncoder(const char * aDest, michael@0: nsIUnicodeEncoder ** aResult) michael@0: { michael@0: // resolve the charset first michael@0: nsAutoCString charset; michael@0: michael@0: // fully qualify to possibly avoid vtable call michael@0: nsCharsetConverterManager::GetCharsetAlias(aDest, charset); michael@0: michael@0: return nsCharsetConverterManager::GetUnicodeEncoderRaw(charset.get(), michael@0: aResult); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetUnicodeEncoderRaw(const char * aDest, michael@0: nsIUnicodeEncoder ** aResult) michael@0: { michael@0: *aResult= nullptr; michael@0: nsCOMPtr encoder; michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsAutoCString michael@0: contractid(NS_LITERAL_CSTRING(NS_UNICODEENCODER_CONTRACTID_BASE) + michael@0: nsDependentCString(aDest)); michael@0: michael@0: // Always create an instance since encoders hold state. michael@0: encoder = do_CreateInstance(contractid.get(), &rv); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: rv = NS_ERROR_UCONV_NOCONV; michael@0: else michael@0: { michael@0: *aResult = encoder.get(); michael@0: NS_ADDREF(*aResult); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetUnicodeDecoder(const char * aSrc, michael@0: nsIUnicodeDecoder ** aResult) michael@0: { michael@0: // resolve the charset first michael@0: nsAutoCString charset; michael@0: michael@0: // fully qualify to possibly avoid vtable call michael@0: if (NS_FAILED(nsCharsetConverterManager::GetCharsetAlias(aSrc, charset))) michael@0: return NS_ERROR_UCONV_NOCONV; michael@0: michael@0: return nsCharsetConverterManager::GetUnicodeDecoderRaw(charset.get(), michael@0: aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetUnicodeDecoderInternal(const char * aSrc, michael@0: nsIUnicodeDecoder ** aResult) michael@0: { michael@0: // resolve the charset first michael@0: nsAutoCString charset; michael@0: michael@0: nsresult rv = nsCharsetAlias::GetPreferredInternal(nsDependentCString(aSrc), michael@0: charset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return nsCharsetConverterManager::GetUnicodeDecoderRaw(charset.get(), michael@0: aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetUnicodeDecoderRaw(const char * aSrc, michael@0: nsIUnicodeDecoder ** aResult) michael@0: { michael@0: *aResult= nullptr; michael@0: nsCOMPtr decoder; michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: NS_NAMED_LITERAL_CSTRING(contractbase, NS_UNICODEDECODER_CONTRACTID_BASE); michael@0: nsDependentCString src(aSrc); michael@0: michael@0: decoder = do_CreateInstance(PromiseFlatCString(contractbase + src).get(), michael@0: &rv); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_UCONV_NOCONV); michael@0: michael@0: decoder.forget(aResult); michael@0: return rv; michael@0: } michael@0: michael@0: static michael@0: nsresult GetList(const nsACString& aCategory, michael@0: const nsACString& aPrefix, michael@0: nsIUTF8StringEnumerator** aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: *aResult = nullptr; michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsTArray* array = new nsTArray; michael@0: if (!array) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsCOMPtr enumerator; michael@0: catman->EnumerateCategory(PromiseFlatCString(aCategory).get(), michael@0: getter_AddRefs(enumerator)); michael@0: michael@0: bool hasMore; michael@0: while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { michael@0: nsCOMPtr supports; michael@0: if (NS_FAILED(enumerator->GetNext(getter_AddRefs(supports)))) michael@0: continue; michael@0: michael@0: nsCOMPtr supStr = do_QueryInterface(supports); michael@0: if (!supStr) michael@0: continue; michael@0: michael@0: nsAutoCString name; michael@0: if (NS_FAILED(supStr->GetData(name))) michael@0: continue; michael@0: michael@0: nsAutoCString fullName(aPrefix); michael@0: fullName.Append(name); michael@0: NS_ENSURE_TRUE(array->AppendElement(fullName), NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: return NS_NewAdoptingUTF8StringEnumerator(aResult, array); michael@0: } michael@0: michael@0: // we should change the interface so that we can just pass back a enumerator! michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetDecoderList(nsIUTF8StringEnumerator ** aResult) michael@0: { michael@0: return GetList(NS_LITERAL_CSTRING(NS_UNICODEDECODER_NAME), michael@0: EmptyCString(), aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetEncoderList(nsIUTF8StringEnumerator ** aResult) michael@0: { michael@0: return GetList(NS_LITERAL_CSTRING(NS_UNICODEENCODER_NAME), michael@0: EmptyCString(), aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetCharsetDetectorList(nsIUTF8StringEnumerator** aResult) michael@0: { michael@0: return GetList(NS_LITERAL_CSTRING("charset-detectors"), michael@0: NS_LITERAL_CSTRING("chardet."), aResult); michael@0: } michael@0: michael@0: // XXX Improve the implementation of this method. Right now, it is build on michael@0: // top of the nsCharsetAlias service. We can make the nsCharsetAlias michael@0: // better, with its own hash table (not the StringBundle anymore) and michael@0: // a nicer file format. michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetCharsetAlias(const char * aCharset, michael@0: nsACString& aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCharset); michael@0: michael@0: // We try to obtain the preferred name for this charset from the charset michael@0: // aliases. michael@0: nsresult rv; michael@0: michael@0: rv = nsCharsetAlias::GetPreferred(nsDependentCString(aCharset), aResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetCharsetTitle(const char * aCharset, michael@0: nsAString& aResult) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCharset); michael@0: michael@0: if (!sTitleBundle) { michael@0: nsresult rv = LoadExtensibleBundle(NS_TITLE_BUNDLE_CATEGORY, &sTitleBundle); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return GetBundleValue(sTitleBundle, aCharset, NS_LITERAL_STRING(".title"), aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetCharsetData(const char * aCharset, michael@0: const char16_t * aProp, michael@0: nsAString& aResult) michael@0: { michael@0: return GetCharsetDataImpl(aCharset, aProp, aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetCharsetLangGroup(const char * aCharset, michael@0: nsIAtom** aResult) michael@0: { michael@0: // resolve the charset first michael@0: nsAutoCString charset; michael@0: michael@0: nsresult rv = GetCharsetAlias(aCharset, charset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // fully qualify to possibly avoid vtable call michael@0: return nsCharsetConverterManager::GetCharsetLangGroupRaw(charset.get(), michael@0: aResult); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCharsetConverterManager::GetCharsetLangGroupRaw(const char * aCharset, michael@0: nsIAtom** aResult) michael@0: { michael@0: michael@0: *aResult = nullptr; michael@0: nsAutoString langGroup; michael@0: // fully qualify to possibly avoid vtable call michael@0: nsresult rv = nsCharsetConverterManager::GetCharsetData( michael@0: aCharset, MOZ_UTF16(".LangGroup"), langGroup); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ToLowerCase(langGroup); // use lowercase for all language atoms michael@0: *aResult = NS_NewAtom(langGroup).take(); michael@0: } michael@0: michael@0: return rv; michael@0: }