tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsHyphenationManager.cpp (10979B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsHyphenationManager.h"
      7 #include "nsHyphenator.h"
      8 #include "nsAtom.h"
      9 #include "nsIFile.h"
     10 #include "nsIURI.h"
     11 #include "nsIJARURI.h"
     12 #include "nsIProperties.h"
     13 #include "nsIDirectoryEnumerator.h"
     14 #include "nsDirectoryServiceDefs.h"
     15 #include "nsNetUtil.h"
     16 #include "nsUnicharUtils.h"
     17 #include "mozilla/CountingAllocatorBase.h"
     18 #include "mozilla/Preferences.h"
     19 #include "nsZipArchive.h"
     20 #include "mozilla/Services.h"
     21 #include "nsIObserverService.h"
     22 #include "nsCRT.h"
     23 #include "nsAppDirectoryServiceDefs.h"
     24 #include "nsDirectoryServiceUtils.h"
     25 #include "nsXULAppAPI.h"
     26 
     27 using namespace mozilla;
     28 
     29 static const char kIntlHyphenationAliasPrefix[] = "intl.hyphenation-alias.";
     30 static const char kMemoryPressureNotification[] = "memory-pressure";
     31 
     32 class HyphenReporter final : public nsIMemoryReporter {
     33 private:
     34  ~HyphenReporter() = default;
     35 
     36 public:
     37  NS_DECL_ISUPPORTS
     38 
     39  // For telemetry, we report the memory rounded up to the nearest KB.
     40  static uint32_t MemoryAllocatedInKB() {
     41    size_t total = 0;
     42    if (nsHyphenationManager::Instance()) {
     43      total = nsHyphenationManager::Instance()->SizeOfIncludingThis(
     44          moz_malloc_size_of);
     45    }
     46    return (total + 1023) / 1024;
     47  }
     48 
     49  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
     50                            nsISupports* aData, bool aAnonymize) override {
     51    size_t total = 0;
     52    if (nsHyphenationManager::Instance()) {
     53      total = nsHyphenationManager::Instance()->SizeOfIncludingThis(
     54          moz_malloc_size_of);
     55    }
     56    MOZ_COLLECT_REPORT("explicit/hyphenation", KIND_HEAP, UNITS_BYTES, total,
     57                       "Memory used by hyphenation data.");
     58    return NS_OK;
     59  }
     60 };
     61 
     62 NS_IMPL_ISUPPORTS(HyphenReporter, nsIMemoryReporter)
     63 
     64 nsHyphenationManager* nsHyphenationManager::sInstance = nullptr;
     65 
     66 NS_IMPL_ISUPPORTS(nsHyphenationManager, nsIObserver)
     67 
     68 NS_IMETHODIMP
     69 nsHyphenationManager::Observe(nsISupports* aSubject, const char* aTopic,
     70                              const char16_t* aData) {
     71  if (!nsCRT::strcmp(aTopic, kMemoryPressureNotification)) {
     72    nsHyphenationManager::sInstance->mHyphenators.Clear();
     73  }
     74  return NS_OK;
     75 }
     76 
     77 nsHyphenationManager* nsHyphenationManager::Instance() {
     78  if (sInstance == nullptr) {
     79    sInstance = new nsHyphenationManager();
     80 
     81    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     82    if (obs) {
     83      obs->AddObserver(sInstance, kMemoryPressureNotification, false);
     84    }
     85 
     86    RegisterStrongMemoryReporter(new HyphenReporter());
     87  }
     88  return sInstance;
     89 }
     90 
     91 void nsHyphenationManager::Shutdown() {
     92  if (sInstance) {
     93    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     94    if (obs) {
     95      obs->RemoveObserver(sInstance, kMemoryPressureNotification);
     96    }
     97    delete sInstance;
     98    sInstance = nullptr;
     99  }
    100 }
    101 
    102 nsHyphenationManager::nsHyphenationManager() {
    103  LoadPatternList();
    104  LoadAliases();
    105 }
    106 
    107 nsHyphenationManager::~nsHyphenationManager() { sInstance = nullptr; }
    108 
    109 already_AddRefed<nsHyphenator> nsHyphenationManager::GetHyphenator(
    110    nsAtom* aLocale) {
    111  RefPtr<nsHyphenator> hyph;
    112  mHyphenators.Get(aLocale, getter_AddRefs(hyph));
    113  if (hyph) {
    114    return hyph.forget();
    115  }
    116  nsCOMPtr<nsIURI> uri = mPatternFiles.Get(aLocale);
    117  if (!uri) {
    118    RefPtr<nsAtom> alias = mHyphAliases.Get(aLocale);
    119    if (alias) {
    120      mHyphenators.Get(alias, getter_AddRefs(hyph));
    121      if (hyph) {
    122        return hyph.forget();
    123      }
    124      uri = mPatternFiles.Get(alias);
    125      if (uri) {
    126        aLocale = alias;
    127      }
    128    }
    129    if (!uri) {
    130      // In the case of a locale such as "de-DE-1996", we try replacing
    131      // successive trailing subtags with "-*" to find fallback patterns,
    132      // so "de-DE-1996" -> "de-DE-*" (and then recursively -> "de-*")
    133      nsAtomCString localeStr(aLocale);
    134      if (StringEndsWith(localeStr, "-*"_ns)) {
    135        localeStr.Truncate(localeStr.Length() - 2);
    136      }
    137      int32_t i = localeStr.RFindChar('-');
    138      if (i > 1) {
    139        localeStr.ReplaceLiteral(i, localeStr.Length() - i, "-*");
    140        RefPtr<nsAtom> fuzzyLocale = NS_Atomize(localeStr);
    141        return GetHyphenator(fuzzyLocale);
    142      }
    143      return nullptr;
    144    }
    145  }
    146  nsAutoCString hyphCapPref("intl.hyphenate-capitalized.");
    147  hyphCapPref.Append(nsAtomCString(aLocale));
    148  hyph = new nsHyphenator(uri, Preferences::GetBool(hyphCapPref.get()));
    149  if (hyph->IsValid()) {
    150    mHyphenators.InsertOrUpdate(aLocale, RefPtr{hyph});
    151    return hyph.forget();
    152  }
    153 #ifdef DEBUG
    154  nsCString msg("failed to load patterns from ");
    155  msg += uri->GetSpecOrDefault();
    156  NS_WARNING(msg.get());
    157 #endif
    158  mPatternFiles.Remove(aLocale);
    159  return nullptr;
    160 }
    161 
    162 void nsHyphenationManager::LoadPatternList() {
    163  mPatternFiles.Clear();
    164  mHyphenators.Clear();
    165 
    166  LoadPatternListFromOmnijar(Omnijar::GRE);
    167  LoadPatternListFromOmnijar(Omnijar::APP);
    168 
    169  nsCOMPtr<nsIProperties> dirSvc =
    170      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
    171  if (!dirSvc) {
    172    return;
    173  }
    174 
    175  nsresult rv;
    176  nsCOMPtr<nsIFile> greDir;
    177  rv = dirSvc->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
    178  if (NS_SUCCEEDED(rv)) {
    179    greDir->AppendNative("hyphenation"_ns);
    180    LoadPatternListFromDir(greDir);
    181  }
    182 
    183  nsCOMPtr<nsIFile> appDir;
    184  rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
    185                   getter_AddRefs(appDir));
    186  if (NS_SUCCEEDED(rv)) {
    187    appDir->AppendNative("hyphenation"_ns);
    188    bool equals;
    189    if (NS_SUCCEEDED(appDir->Equals(greDir, &equals)) && !equals) {
    190      LoadPatternListFromDir(appDir);
    191    }
    192  }
    193 
    194  nsCOMPtr<nsIFile> profileDir;
    195  rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
    196                              getter_AddRefs(profileDir));
    197  if (NS_SUCCEEDED(rv)) {
    198    profileDir->AppendNative("hyphenation"_ns);
    199    LoadPatternListFromDir(profileDir);
    200  }
    201 }
    202 
    203 // Extract the locale code we'll use to identify a given hyphenation resource
    204 // from the path name as found in omnijar or on disk.
    205 static already_AddRefed<nsAtom> LocaleAtomFromPath(const nsCString& aPath) {
    206  if (!StringEndsWith(aPath, ".hyf"_ns) && !StringEndsWith(aPath, ".dic"_ns)) {
    207    return nullptr;
    208  }
    209  nsCString locale(aPath);
    210  locale.Truncate(locale.Length() - 4);      // strip ".hyf" or ".dic"
    211  locale.Cut(0, locale.RFindChar('/') + 1);  // strip directory
    212  ToLowerCase(locale);
    213  if (StringBeginsWith(locale, "hyph_"_ns)) {
    214    locale.Cut(0, 5);
    215  }
    216  locale.ReplaceChar('_', '-');
    217  return NS_Atomize(locale);
    218 }
    219 
    220 void nsHyphenationManager::LoadPatternListFromOmnijar(Omnijar::Type aType) {
    221  nsCString base;
    222  nsresult rv = Omnijar::GetURIString(aType, base);
    223  if (NS_FAILED(rv)) {
    224    return;
    225  }
    226 
    227  RefPtr<nsZipArchive> zip = Omnijar::GetReader(aType);
    228  if (!zip) {
    229    return;
    230  }
    231 
    232  nsZipFind* find;
    233  zip->FindInit("hyphenation/hyph_*.*", &find);
    234  if (!find) {
    235    return;
    236  }
    237 
    238  const char* result;
    239  uint16_t len;
    240  while (NS_SUCCEEDED(find->FindNext(&result, &len))) {
    241    nsCString uriString(base);
    242    uriString.Append(result, len);
    243    nsCOMPtr<nsIURI> uri;
    244    rv = NS_NewURI(getter_AddRefs(uri), uriString);
    245    if (NS_FAILED(rv)) {
    246      continue;
    247    }
    248    nsCString locale;
    249    rv = uri->GetPathQueryRef(locale);
    250    if (NS_FAILED(rv)) {
    251      continue;
    252    }
    253    RefPtr<nsAtom> localeAtom = LocaleAtomFromPath(locale);
    254    if (!localeAtom) {
    255      continue;
    256    }
    257    mPatternFiles.InsertOrUpdate(localeAtom, uri);
    258  }
    259 
    260  delete find;
    261 }
    262 
    263 void nsHyphenationManager::LoadPatternListFromDir(nsIFile* aDir) {
    264  nsresult rv;
    265 
    266  bool check = false;
    267  rv = aDir->Exists(&check);
    268  if (NS_FAILED(rv) || !check) {
    269    return;
    270  }
    271 
    272  rv = aDir->IsDirectory(&check);
    273  if (NS_FAILED(rv) || !check) {
    274    return;
    275  }
    276 
    277  nsCOMPtr<nsIDirectoryEnumerator> files;
    278  rv = aDir->GetDirectoryEntries(getter_AddRefs(files));
    279  if (NS_FAILED(rv)) {
    280    return;
    281  }
    282 
    283  nsCOMPtr<nsIFile> file;
    284  while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(file))) && file) {
    285    nsAutoString dictName;
    286    file->GetLeafName(dictName);
    287    NS_ConvertUTF16toUTF8 path(dictName);
    288    if (!(StringEndsWith(path, ".hyf"_ns) || StringEndsWith(path, ".dic"_ns))) {
    289      continue;
    290    }
    291    RefPtr<nsAtom> localeAtom = LocaleAtomFromPath(path);
    292    if (!localeAtom) {
    293      continue;
    294    }
    295    nsCOMPtr<nsIURI> uri;
    296    nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file);
    297    if (NS_SUCCEEDED(rv)) {
    298 #ifdef DEBUG_hyph
    299      printf("adding hyphenation patterns for %s: %s\n",
    300             nsAtomCString(localeAtom).get(), path.get());
    301 #endif
    302      mPatternFiles.InsertOrUpdate(localeAtom, uri);
    303    }
    304  }
    305 }
    306 
    307 void nsHyphenationManager::LoadAliases() {
    308  nsIPrefBranch* prefRootBranch = Preferences::GetRootBranch();
    309  if (!prefRootBranch) {
    310    return;
    311  }
    312  nsTArray<nsCString> prefNames;
    313  nsresult rv =
    314      prefRootBranch->GetChildList(kIntlHyphenationAliasPrefix, prefNames);
    315  if (NS_SUCCEEDED(rv)) {
    316    for (auto& prefName : prefNames) {
    317      nsAutoCString value;
    318      rv = Preferences::GetCString(prefName.get(), value);
    319      if (NS_SUCCEEDED(rv)) {
    320        nsAutoCString alias(prefName);
    321        alias.Cut(0, sizeof(kIntlHyphenationAliasPrefix) - 1);
    322        ToLowerCase(alias);
    323        ToLowerCase(value);
    324        RefPtr<nsAtom> aliasAtom = NS_Atomize(alias);
    325        RefPtr<nsAtom> valueAtom = NS_Atomize(value);
    326        mHyphAliases.InsertOrUpdate(aliasAtom, std::move(valueAtom));
    327      }
    328    }
    329  }
    330 }
    331 
    332 void nsHyphenationManager::ShareHyphDictToProcess(
    333    nsIURI* aURI, base::ProcessId aPid,
    334    mozilla::ipc::ReadOnlySharedMemoryHandle* aOutHandle) {
    335  MOZ_ASSERT(XRE_IsParentProcess());
    336  // aURI will be referring to an omnijar resource (otherwise just bail).
    337  *aOutHandle = nullptr;
    338 
    339  // Extract the locale code from the URI, and get the corresponding
    340  // hyphenator (loading it into shared memory if necessary).
    341  nsCString path;
    342  nsCOMPtr<nsIJARURI> jar = do_QueryInterface(aURI);
    343  if (jar) {
    344    jar->GetJAREntry(path);
    345  } else {
    346    aURI->GetFilePath(path);
    347  }
    348 
    349  RefPtr<nsAtom> localeAtom = LocaleAtomFromPath(path);
    350  if (!localeAtom) {
    351    MOZ_ASSERT_UNREACHABLE("bad path");
    352    return;
    353  }
    354  RefPtr<nsHyphenator> hyph = GetHyphenator(localeAtom);
    355  if (!hyph) {
    356    MOZ_ASSERT_UNREACHABLE("failed to find hyphenator");
    357    return;
    358  }
    359 
    360  *aOutHandle = hyph->CloneHandle();
    361 }
    362 
    363 size_t nsHyphenationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
    364  size_t result = aMallocSizeOf(this);
    365 
    366  result += mHyphAliases.ShallowSizeOfExcludingThis(aMallocSizeOf);
    367 
    368  result += mPatternFiles.ShallowSizeOfExcludingThis(aMallocSizeOf);
    369  // Measurement of the URIs stored in mPatternFiles may be added later if DMD
    370  // finds it is worthwhile.
    371 
    372  result += mHyphenators.ShallowSizeOfExcludingThis(aMallocSizeOf);
    373 
    374  return result;
    375 }