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