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: #include "nsEntityConverter.h" michael@0: #include "nsLiteralString.h" michael@0: #include "nsString.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsCRT.h" michael@0: michael@0: // michael@0: // implementation methods michael@0: // michael@0: nsEntityConverter::nsEntityConverter() : michael@0: mVersionList(nullptr), michael@0: mVersionListLength(0) michael@0: { michael@0: } michael@0: michael@0: nsEntityConverter::~nsEntityConverter() michael@0: { michael@0: if (mVersionList) michael@0: delete [] mVersionList; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsEntityConverter::LoadVersionPropertyFile() michael@0: { michael@0: NS_NAMED_LITERAL_CSTRING(url, "resource://gre/res/entityTables/htmlEntityVersions.properties"); michael@0: michael@0: nsCOMPtr bundleService = michael@0: mozilla::services::GetStringBundleService(); michael@0: if (!bundleService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr entities; michael@0: nsresult rv = bundleService->CreateBundle(url.get(), getter_AddRefs(entities)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsresult result; michael@0: michael@0: nsAutoString key; michael@0: nsXPIDLString value; michael@0: rv = entities->GetStringFromName(MOZ_UTF16("length"), michael@0: getter_Copies(value)); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv),"nsEntityConverter: malformed entity table\n"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mVersionListLength = nsAutoString(value).ToInteger(&result); michael@0: NS_ASSERTION(32 >= mVersionListLength,"nsEntityConverter: malformed entity table\n"); michael@0: if (32 < mVersionListLength) return NS_ERROR_FAILURE; michael@0: michael@0: mVersionList = new nsEntityVersionList[mVersionListLength]; michael@0: if (!mVersionList) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: for (uint32_t i = 0; i < mVersionListLength && NS_SUCCEEDED(rv); i++) { michael@0: key.SetLength(0); michael@0: key.AppendInt(i+1, 10); michael@0: rv = entities->GetStringFromName(key.get(), getter_Copies(value)); michael@0: uint32_t len = value.Length(); michael@0: if (kVERSION_STRING_LEN < len) return NS_ERROR_UNEXPECTED; michael@0: michael@0: memcpy(mVersionList[i].mEntityListName, value.get(), len*sizeof(char16_t)); michael@0: mVersionList[i].mEntityListName[len] = 0; michael@0: mVersionList[i].mVersion = (1 << i); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsEntityConverter::LoadEntityBundle(uint32_t version) michael@0: { michael@0: nsAutoCString url(NS_LITERAL_CSTRING("resource://gre/res/entityTables/")); michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr bundleService = michael@0: do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: const char16_t *versionName = GetVersionName(version); michael@0: NS_ENSURE_TRUE(versionName, nullptr); michael@0: michael@0: // all property file names are ASCII, like "html40Latin1" so this is safe michael@0: LossyAppendUTF16toASCII(versionName, url); michael@0: url.Append(".properties"); michael@0: michael@0: nsCOMPtr bundle; michael@0: rv = bundleService->CreateBundle(url.get(), getter_AddRefs(bundle)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: return bundle.forget(); michael@0: } michael@0: michael@0: const char16_t* michael@0: nsEntityConverter:: GetVersionName(uint32_t versionNumber) michael@0: { michael@0: for (uint32_t i = 0; i < mVersionListLength; i++) { michael@0: if (versionNumber == mVersionList[i].mVersion) michael@0: return mVersionList[i].mEntityListName; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIStringBundle* michael@0: nsEntityConverter:: GetVersionBundleInstance(uint32_t versionNumber) michael@0: { michael@0: if (!mVersionList) { michael@0: // load the property file which contains available version names michael@0: // and generate a list of version/name pair michael@0: if (NS_FAILED(LoadVersionPropertyFile())) michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t i; michael@0: for (i = 0; i < mVersionListLength; i++) { michael@0: if (versionNumber == mVersionList[i].mVersion) { michael@0: if (!mVersionList[i].mEntities) michael@0: { // not loaded michael@0: // load the property file michael@0: mVersionList[i].mEntities = LoadEntityBundle(versionNumber); michael@0: NS_ASSERTION(mVersionList[i].mEntities, "LoadEntityBundle failed"); michael@0: } michael@0: return mVersionList[i].mEntities.get(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: // michael@0: // nsISupports methods michael@0: // michael@0: NS_IMPL_ISUPPORTS(nsEntityConverter,nsIEntityConverter) michael@0: michael@0: michael@0: // michael@0: // nsIEntityConverter michael@0: // michael@0: NS_IMETHODIMP michael@0: nsEntityConverter::ConvertToEntity(char16_t character, uint32_t entityVersion, char **_retval) michael@0: { michael@0: return ConvertUTF32ToEntity((uint32_t)character, entityVersion, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsEntityConverter::ConvertUTF32ToEntity(uint32_t character, uint32_t entityVersion, char **_retval) michael@0: { michael@0: NS_ASSERTION(_retval, "null ptr- _retval"); michael@0: if(nullptr == _retval) michael@0: return NS_ERROR_NULL_POINTER; michael@0: *_retval = nullptr; michael@0: michael@0: for (uint32_t mask = 1, mask2 = 0xFFFFFFFFL; (0!=(entityVersion & mask2)); mask<<=1, mask2<<=1) { michael@0: if (0 == (entityVersion & mask)) michael@0: continue; michael@0: nsIStringBundle* entities = GetVersionBundleInstance(entityVersion & mask); michael@0: NS_ASSERTION(entities, "Cannot get the property file"); michael@0: michael@0: if (!entities) michael@0: continue; michael@0: michael@0: nsAutoString key(NS_LITERAL_STRING("entity.")); michael@0: key.AppendInt(character,10); michael@0: michael@0: nsXPIDLString value; michael@0: nsresult rv = entities->GetStringFromName(key.get(), getter_Copies(value)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: *_retval = ToNewCString(value); michael@0: if(nullptr == *_retval) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: else michael@0: return NS_OK; michael@0: } michael@0: } michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsEntityConverter::ConvertToEntities(const char16_t *inString, uint32_t entityVersion, char16_t **_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(inString); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: *_retval = nullptr; michael@0: michael@0: nsString outString; michael@0: michael@0: // per character look for the entity michael@0: uint32_t len = NS_strlen(inString); michael@0: for (uint32_t i = 0; i < len; i++) { michael@0: nsAutoString key(NS_LITERAL_STRING("entity.")); michael@0: if (NS_IS_HIGH_SURROGATE(inString[i]) && michael@0: i + 2 < len && michael@0: NS_IS_LOW_SURROGATE(inString[i + 1])) { michael@0: key.AppendInt(SURROGATE_TO_UCS4(inString[i], inString[i+1]), 10); michael@0: ++i; michael@0: } michael@0: else { michael@0: key.AppendInt(inString[i],10); michael@0: } michael@0: michael@0: nsXPIDLString value; michael@0: const char16_t *entity = nullptr; michael@0: michael@0: for (uint32_t mask = 1, mask2 = 0xFFFFFFFFL; (0!=(entityVersion & mask2)); mask<<=1, mask2<<=1) { michael@0: if (0 == (entityVersion & mask)) michael@0: continue; michael@0: nsIStringBundle* entities = GetVersionBundleInstance(entityVersion & mask); michael@0: NS_ASSERTION(entities, "Cannot get the property file"); michael@0: michael@0: if (!entities) michael@0: continue; michael@0: michael@0: nsresult rv = entities->GetStringFromName(key.get(), michael@0: getter_Copies(value)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: entity = value.get(); michael@0: break; michael@0: } michael@0: } michael@0: if (entity) { michael@0: outString.Append(entity); michael@0: } michael@0: else { michael@0: outString.Append(&inString[i], 1); michael@0: } michael@0: } michael@0: michael@0: *_retval = ToNewUnicode(outString); michael@0: if (!*_retval) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: return NS_OK; michael@0: }