diff options
Diffstat (limited to 'intl/hyphenation/glue/nsHyphenationManager.cpp')
-rw-r--r-- | intl/hyphenation/glue/nsHyphenationManager.cpp | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/intl/hyphenation/glue/nsHyphenationManager.cpp b/intl/hyphenation/glue/nsHyphenationManager.cpp new file mode 100644 index 0000000000..90d40a733c --- /dev/null +++ b/intl/hyphenation/glue/nsHyphenationManager.cpp @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsHyphenationManager.h" +#include "nsHyphenator.h" +#include "nsAtom.h" +#include "nsIFile.h" +#include "nsIURI.h" +#include "nsIJARURI.h" +#include "nsIProperties.h" +#include "nsIDirectoryEnumerator.h" +#include "nsDirectoryServiceDefs.h" +#include "nsNetUtil.h" +#include "nsUnicharUtils.h" +#include "mozilla/CountingAllocatorBase.h" +#include "mozilla/Preferences.h" +#include "nsZipArchive.h" +#include "mozilla/Services.h" +#include "nsIObserverService.h" +#include "nsCRT.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" +#include "nsXULAppAPI.h" + +using namespace mozilla; + +static const char kIntlHyphenationAliasPrefix[] = "intl.hyphenation-alias."; +static const char kMemoryPressureNotification[] = "memory-pressure"; + +class HyphenReporter final : public nsIMemoryReporter { + private: + ~HyphenReporter() = default; + + public: + NS_DECL_ISUPPORTS + + // For telemetry, we report the memory rounded up to the nearest KB. + static uint32_t MemoryAllocatedInKB() { + size_t total = 0; + if (nsHyphenationManager::Instance()) { + total = nsHyphenationManager::Instance()->SizeOfIncludingThis( + moz_malloc_size_of); + } + return (total + 1023) / 1024; + } + + NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) override { + size_t total = 0; + if (nsHyphenationManager::Instance()) { + total = nsHyphenationManager::Instance()->SizeOfIncludingThis( + moz_malloc_size_of); + } + MOZ_COLLECT_REPORT("explicit/hyphenation", KIND_HEAP, UNITS_BYTES, total, + "Memory used by hyphenation data."); + return NS_OK; + } +}; + +NS_IMPL_ISUPPORTS(HyphenReporter, nsIMemoryReporter) + +nsHyphenationManager* nsHyphenationManager::sInstance = nullptr; + +NS_IMPL_ISUPPORTS(nsHyphenationManager, nsIObserver) + +NS_IMETHODIMP +nsHyphenationManager::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!nsCRT::strcmp(aTopic, kMemoryPressureNotification)) { + nsHyphenationManager::sInstance->mHyphenators.Clear(); + } + return NS_OK; +} + +nsHyphenationManager* nsHyphenationManager::Instance() { + if (sInstance == nullptr) { + sInstance = new nsHyphenationManager(); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(sInstance, kMemoryPressureNotification, false); + } + + RegisterStrongMemoryReporter(new HyphenReporter()); + } + return sInstance; +} + +void nsHyphenationManager::Shutdown() { + if (sInstance) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(sInstance, kMemoryPressureNotification); + } + delete sInstance; + sInstance = nullptr; + } +} + +nsHyphenationManager::nsHyphenationManager() { + LoadPatternList(); + LoadAliases(); +} + +nsHyphenationManager::~nsHyphenationManager() { sInstance = nullptr; } + +already_AddRefed<nsHyphenator> nsHyphenationManager::GetHyphenator( + nsAtom* aLocale) { + RefPtr<nsHyphenator> hyph; + mHyphenators.Get(aLocale, getter_AddRefs(hyph)); + if (hyph) { + return hyph.forget(); + } + nsCOMPtr<nsIURI> uri = mPatternFiles.Get(aLocale); + if (!uri) { + RefPtr<nsAtom> alias = mHyphAliases.Get(aLocale); + if (alias) { + mHyphenators.Get(alias, getter_AddRefs(hyph)); + if (hyph) { + return hyph.forget(); + } + uri = mPatternFiles.Get(alias); + if (uri) { + aLocale = alias; + } + } + if (!uri) { + // In the case of a locale such as "de-DE-1996", we try replacing + // successive trailing subtags with "-*" to find fallback patterns, + // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*") + nsAtomCString localeStr(aLocale); + if (StringEndsWith(localeStr, "-*"_ns)) { + localeStr.Truncate(localeStr.Length() - 2); + } + int32_t i = localeStr.RFindChar('-'); + if (i > 1) { + localeStr.ReplaceLiteral(i, localeStr.Length() - i, "-*"); + RefPtr<nsAtom> fuzzyLocale = NS_Atomize(localeStr); + return GetHyphenator(fuzzyLocale); + } + return nullptr; + } + } + nsAutoCString hyphCapPref("intl.hyphenate-capitalized."); + hyphCapPref.Append(nsAtomCString(aLocale)); + hyph = new nsHyphenator(uri, Preferences::GetBool(hyphCapPref.get())); + if (hyph->IsValid()) { + mHyphenators.InsertOrUpdate(aLocale, RefPtr{hyph}); + return hyph.forget(); + } +#ifdef DEBUG + nsCString msg("failed to load patterns from "); + msg += uri->GetSpecOrDefault(); + NS_WARNING(msg.get()); +#endif + mPatternFiles.Remove(aLocale); + return nullptr; +} + +void nsHyphenationManager::LoadPatternList() { + mPatternFiles.Clear(); + mHyphenators.Clear(); + + LoadPatternListFromOmnijar(Omnijar::GRE); + LoadPatternListFromOmnijar(Omnijar::APP); + + nsCOMPtr<nsIProperties> dirSvc = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); + if (!dirSvc) { + return; + } + + nsresult rv; + nsCOMPtr<nsIFile> greDir; + rv = dirSvc->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir)); + if (NS_SUCCEEDED(rv)) { + greDir->AppendNative("hyphenation"_ns); + LoadPatternListFromDir(greDir); + } + + nsCOMPtr<nsIFile> appDir; + rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(appDir)); + if (NS_SUCCEEDED(rv)) { + appDir->AppendNative("hyphenation"_ns); + bool equals; + if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) { + LoadPatternListFromDir(appDir); + } + } + + nsCOMPtr<nsIFile> profileDir; + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, + getter_AddRefs(profileDir)); + if (NS_SUCCEEDED(rv)) { + profileDir->AppendNative("hyphenation"_ns); + LoadPatternListFromDir(profileDir); + } +} + +// Extract the locale code we'll use to identify a given hyphenation resource +// from the path name as found in omnijar or on disk. +static already_AddRefed<nsAtom> LocaleAtomFromPath(const nsCString& aPath) { + if (!StringEndsWith(aPath, ".hyf"_ns) && !StringEndsWith(aPath, ".dic"_ns)) { + return nullptr; + } + nsCString locale(aPath); + locale.Truncate(locale.Length() - 4); // strip ".hyf" or ".dic" + locale.Cut(0, locale.RFindChar('/') + 1); // strip directory + ToLowerCase(locale); + if (StringBeginsWith(locale, "hyph_"_ns)) { + locale.Cut(0, 5); + } + locale.ReplaceChar('_', '-'); + return NS_Atomize(locale); +} + +void nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType) { + nsCString base; + nsresult rv = Omnijar::GetURIString(aType, base); + if (NS_FAILED(rv)) { + return; + } + + RefPtr<nsZipArchive> zip = Omnijar::GetReader(aType); + if (!zip) { + return; + } + + nsZipFind* find; + zip->FindInit("hyphenation/hyph_*.*", &find); + if (!find) { + return; + } + + const char* result; + uint16_t len; + while (NS_SUCCEEDED(find->FindNext(&result, &len))) { + nsCString uriString(base); + uriString.Append(result, len); + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI(getter_AddRefs(uri), uriString); + if (NS_FAILED(rv)) { + continue; + } + nsCString locale; + rv = uri->GetPathQueryRef(locale); + if (NS_FAILED(rv)) { + continue; + } + RefPtr<nsAtom> localeAtom = LocaleAtomFromPath(locale); + if (!localeAtom) { + continue; + } + mPatternFiles.InsertOrUpdate(localeAtom, uri); + } + + delete find; +} + +void nsHyphenationManager::LoadPatternListFromDir(nsIFile* aDir) { + nsresult rv; + + bool check = false; + rv = aDir->Exists(&check); + if (NS_FAILED(rv) || !check) { + return; + } + + rv = aDir->IsDirectory(&check); + if (NS_FAILED(rv) || !check) { + return; + } + + nsCOMPtr<nsIDirectoryEnumerator> files; + rv = aDir->GetDirectoryEntries(getter_AddRefs(files)); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIFile> file; + while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) { + nsAutoString dictName; + file->GetLeafName(dictName); + NS_ConvertUTF16toUTF8 path(dictName); + if (!(StringEndsWith(path, ".hyf"_ns) || StringEndsWith(path, ".dic"_ns))) { + continue; + } + RefPtr<nsAtom> localeAtom = LocaleAtomFromPath(path); + if (!localeAtom) { + continue; + } + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file); + if (NS_SUCCEEDED(rv)) { +#ifdef DEBUG_hyph + printf("adding hyphenation patterns for %s: %s\n", + nsAtomCString(localeAtom).get(), path.get()); +#endif + mPatternFiles.InsertOrUpdate(localeAtom, uri); + } + } +} + +void nsHyphenationManager::LoadAliases() { + nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch(); + if (!prefRootBranch) { + return; + } + nsTArray<nsCString> prefNames; + nsresult rv = + prefRootBranch->GetChildList(kIntlHyphenationAliasPrefix, prefNames); + if (NS_SUCCEEDED(rv)) { + for (auto& prefName : prefNames) { + nsAutoCString value; + rv = Preferences::GetCString(prefName.get(), value); + if (NS_SUCCEEDED(rv)) { + nsAutoCString alias(prefName); + alias.Cut(0, sizeof(kIntlHyphenationAliasPrefix) - 1); + ToLowerCase(alias); + ToLowerCase(value); + RefPtr<nsAtom> aliasAtom = NS_Atomize(alias); + RefPtr<nsAtom> valueAtom = NS_Atomize(value); + mHyphAliases.InsertOrUpdate(aliasAtom, std::move(valueAtom)); + } + } + } +} + +void nsHyphenationManager::ShareHyphDictToProcess( + nsIURI* aURI, base::ProcessId aPid, base::SharedMemoryHandle* aOutHandle, + uint32_t* aOutSize) { + MOZ_ASSERT(XRE_IsParentProcess()); + // aURI will be referring to an omnijar resource (otherwise just bail). + *aOutHandle = base::SharedMemory::NULLHandle(); + *aOutSize = 0; + + // Extract the locale code from the URI, and get the corresponding + // hyphenator (loading it into shared memory if necessary). + nsCString path; + nsCOMPtr<nsIJARURI> jar = do_QueryInterface(aURI); + if (jar) { + jar->GetJAREntry(path); + } else { + aURI->GetFilePath(path); + } + + RefPtr<nsAtom> localeAtom = LocaleAtomFromPath(path); + if (!localeAtom) { + MOZ_ASSERT_UNREACHABLE("bad path"); + return; + } + RefPtr<nsHyphenator> hyph = GetHyphenator(localeAtom); + if (!hyph) { + MOZ_ASSERT_UNREACHABLE("failed to find hyphenator"); + return; + } + + hyph->CloneHandle(aOutHandle, aOutSize); +} + +size_t nsHyphenationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { + size_t result = aMallocSizeOf(this); + + result += mHyphAliases.ShallowSizeOfExcludingThis(aMallocSizeOf); + + result += mPatternFiles.ShallowSizeOfExcludingThis(aMallocSizeOf); + // Measurement of the URIs stored in mPatternFiles may be added later if DMD + // finds it is worthwhile. + + result += mHyphenators.ShallowSizeOfExcludingThis(aMallocSizeOf); + + return result; +} |