tor-browser

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

gfxFT2FontList.cpp (69251B)


      1 /* -*- Mode: C++; tab-width: 20; 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 "mozilla/Base64.h"
      7 #include "mozilla/MemoryReporting.h"
      8 
      9 #include "mozilla/dom/ContentChild.h"
     10 #include "gfxAndroidPlatform.h"
     11 #include "mozilla/Omnijar.h"
     12 #include "mozilla/UniquePtr.h"
     13 #include "mozilla/UniquePtrExtensions.h"
     14 #include "nsReadableUtils.h"
     15 
     16 #include "nsXULAppAPI.h"
     17 #include <dirent.h>
     18 #include <android/log.h>
     19 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko", ##args)
     20 
     21 #include "ft2build.h"
     22 #include FT_FREETYPE_H
     23 #include FT_TRUETYPE_TAGS_H
     24 #include FT_TRUETYPE_TABLES_H
     25 #include FT_MULTIPLE_MASTERS_H
     26 #include "cairo-ft.h"
     27 
     28 #include "gfxFT2FontList.h"
     29 #include "gfxFT2Fonts.h"
     30 #include "gfxFT2Utils.h"
     31 #include "gfxUserFontSet.h"
     32 #include "gfxFontUtils.h"
     33 #include "SharedFontList-impl.h"
     34 #define StandardFonts
     35 #include "StandardFonts-android.inc"
     36 #undef StandardFonts
     37 #include "harfbuzz/hb-ot.h"  // for name ID constants
     38 
     39 #include "nsServiceManagerUtils.h"
     40 #include "nsIGfxInfo.h"
     41 #include "mozilla/Components.h"
     42 #include "nsIObserverService.h"
     43 #include "nsTArray.h"
     44 #include "nsUnicharUtils.h"
     45 #include "nsCRT.h"
     46 
     47 #include "nsDirectoryServiceUtils.h"
     48 #include "nsDirectoryServiceDefs.h"
     49 #include "nsAppDirectoryServiceDefs.h"
     50 #include "nsMemory.h"
     51 #include "nsPresContext.h"
     52 #include "gfxFontConstants.h"
     53 
     54 #include "mozilla/EndianUtils.h"
     55 #include "mozilla/Preferences.h"
     56 #include "mozilla/scache/StartupCache.h"
     57 #include "mozilla/glean/GfxMetrics.h"
     58 #include <fcntl.h>
     59 #include <sys/mman.h>
     60 #include <sys/stat.h>
     61 
     62 #ifdef MOZ_WIDGET_ANDROID
     63 #  include "AndroidBuild.h"
     64 #  include "AndroidSystemFontIterator.h"
     65 #  include "mozilla/jni/Utils.h"
     66 #endif
     67 
     68 using namespace mozilla;
     69 using namespace mozilla::gfx;
     70 
     71 static LazyLogModule sFontInfoLog("fontInfoLog");
     72 
     73 #undef LOG
     74 #define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args)
     75 #define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug)
     76 
     77 static __inline void BuildKeyNameFromFontName(nsACString& aName) {
     78  ToLowerCase(aName);
     79 }
     80 
     81 // Helper to access the FT_Face for a given FT2FontEntry,
     82 // creating a temporary face if the entry does not have one yet.
     83 // This allows us to read font names, tables, etc if necessary
     84 // without permanently instantiating a freetype face and consuming
     85 // memory long-term.
     86 // This may fail (resulting in a null FT_Face), e.g. if it fails to
     87 // allocate memory to uncompress a font from omnijar.
     88 already_AddRefed<SharedFTFace> FT2FontEntry::GetFTFace(bool aCommit) {
     89  if (mFTFace) {
     90    // Create a new reference, and return it.
     91    RefPtr<SharedFTFace> face(mFTFace);
     92    return face.forget();
     93  }
     94 
     95  NS_ASSERTION(!mFilename.IsEmpty(),
     96               "can't use GetFTFace for fonts without a filename");
     97 
     98  // A relative path (no initial "/") means this is a resource in
     99  // omnijar, not an installed font on the device.
    100  // The NS_ASSERTIONs here should never fail, as the resource must have
    101  // been read successfully during font-list initialization or we'd never
    102  // have created the font entry. The only legitimate runtime failure
    103  // here would be memory allocation, in which case mFace remains null.
    104  RefPtr<SharedFTFace> face;
    105  if (mFilename[0] != '/') {
    106    RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
    107    nsZipItem* item = reader->GetItem(mFilename);
    108    NS_ASSERTION(item, "failed to find zip entry");
    109 
    110    uint32_t bufSize = item->RealSize();
    111    uint8_t* fontDataBuf = static_cast<uint8_t*>(malloc(bufSize));
    112    if (fontDataBuf) {
    113      nsZipCursor cursor(item, reader, fontDataBuf, bufSize);
    114      cursor.Copy(&bufSize);
    115      NS_ASSERTION(bufSize == item->RealSize(), "error reading bundled font");
    116      RefPtr<FTUserFontData> ufd = new FTUserFontData(fontDataBuf, bufSize);
    117      face = ufd->CloneFace(mFTFontIndex);
    118      if (!face) {
    119        NS_WARNING("failed to create freetype face");
    120        return nullptr;
    121      }
    122    }
    123  } else {
    124    RefPtr<FTUserFontData> fd = new FTUserFontData(mFilename.get());
    125    face = fd->CloneFace(mFTFontIndex);
    126    if (!face) {
    127      NS_WARNING("failed to create freetype face");
    128      return nullptr;
    129    }
    130    if (FT_Err_Ok != FT_Select_Charmap(face->GetFace(), FT_ENCODING_UNICODE) &&
    131        FT_Err_Ok !=
    132            FT_Select_Charmap(face->GetFace(), FT_ENCODING_MS_SYMBOL)) {
    133      NS_WARNING("failed to select Unicode or symbol charmap");
    134    }
    135  }
    136 
    137  if (aCommit) {
    138    if (mFTFace.compareExchange(nullptr, face.get())) {
    139      // The reference we created is now owned by mFTFace.
    140      face.forget().leak();
    141    } else {
    142      // We lost a race! Just discard our new face and use the existing one.
    143    }
    144  }
    145 
    146  // Create a new reference, and return it.
    147  face = mFTFace;
    148  return face.forget();
    149 }
    150 
    151 FTUserFontData* FT2FontEntry::GetUserFontData() {
    152  if (SharedFTFace* face = mFTFace) {
    153    return static_cast<FTUserFontData*>(face->GetData());
    154  }
    155  return nullptr;
    156 }
    157 
    158 /*
    159 * FT2FontEntry
    160 * gfxFontEntry subclass corresponding to a specific face that can be
    161 * rendered by freetype. This is associated with a face index in a
    162 * file (normally a .ttf/.otf file holding a single face, but in principle
    163 * there could be .ttc files with multiple faces).
    164 * The FT2FontEntry can create the necessary FT_Face on demand, and can
    165 * then create a Cairo font_face and scaled_font for drawing.
    166 */
    167 
    168 FT2FontEntry::~FT2FontEntry() {
    169  if (mMMVar) {
    170    SharedFTFace* face = mFTFace;
    171    FT_Done_MM_Var(face->GetFace()->glyph->library, mMMVar);
    172  }
    173  if (mFTFace) {
    174    auto face = mFTFace.exchange(nullptr);
    175    NS_IF_RELEASE(face);
    176  }
    177 }
    178 
    179 gfxFontEntry* FT2FontEntry::Clone() const {
    180  MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
    181  FT2FontEntry* fe = new FT2FontEntry(Name());
    182  fe->mFilename = mFilename;
    183  fe->mFTFontIndex = mFTFontIndex;
    184  fe->mWeightRange = mWeightRange;
    185  fe->mStretchRange = mStretchRange;
    186  fe->mStyleRange = mStyleRange;
    187  return fe;
    188 }
    189 
    190 gfxFont* FT2FontEntry::CreateFontInstance(const gfxFontStyle* aStyle) {
    191  RefPtr<SharedFTFace> face = GetFTFace(true);
    192  if (!face) {
    193    return nullptr;
    194  }
    195 
    196  if (face->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
    197    // Resolve variations from entry (descriptor) and style (property)
    198    AutoTArray<gfxFontVariation, 8> settings;
    199    GetVariationsForStyle(settings, aStyle ? *aStyle : gfxFontStyle());
    200 
    201    // If variations are present, we will not use our cached mFTFace
    202    // but always create a new one as it will have custom variation
    203    // coordinates applied.
    204    if (!settings.IsEmpty()) {
    205      // Create a separate FT_Face because we need to apply custom
    206      // variation settings to it.
    207      RefPtr<SharedFTFace> varFace;
    208      if (!mFilename.IsEmpty() && mFilename[0] == '/') {
    209        varFace =
    210            Factory::NewSharedFTFace(nullptr, mFilename.get(), mFTFontIndex);
    211      } else {
    212        varFace = face->GetData()->CloneFace(mFTFontIndex);
    213      }
    214      if (varFace) {
    215        gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings,
    216                                       varFace->GetFace());
    217        face = std::move(varFace);
    218      }
    219    }
    220  }
    221 
    222  int loadFlags = gfxPlatform::GetPlatform()->FontHintingEnabled()
    223                      ? FT_LOAD_DEFAULT
    224                      : (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
    225  if (face->GetFace()->face_flags & FT_FACE_FLAG_TRICKY) {
    226    loadFlags &= ~FT_LOAD_NO_AUTOHINT;
    227  }
    228 
    229  RefPtr<UnscaledFontFreeType> unscaledFont(mUnscaledFont);
    230  if (!unscaledFont) {
    231    RefPtr<SharedFTFace> origFace(mFTFace);
    232    unscaledFont =
    233        !mFilename.IsEmpty() && mFilename[0] == '/'
    234            ? new UnscaledFontFreeType(mFilename.BeginReading(), mFTFontIndex,
    235                                       std::move(origFace))
    236            : new UnscaledFontFreeType(std::move(origFace));
    237    mUnscaledFont = unscaledFont;
    238  }
    239 
    240  gfxFont* font =
    241      new gfxFT2Font(unscaledFont, std::move(face), this, aStyle, loadFlags);
    242  return font;
    243 }
    244 
    245 /* static */
    246 FT2FontEntry* FT2FontEntry::CreateFontEntry(
    247    const nsACString& aFontName, WeightRange aWeight, StretchRange aStretch,
    248    SlantStyleRange aStyle, const uint8_t* aFontData, uint32_t aLength) {
    249  // Ownership of aFontData is passed in here; the fontEntry must
    250  // retain it as long as the FT_Face needs it, and ensure it is
    251  // eventually deleted.
    252  RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength);
    253  RefPtr<SharedFTFace> face = ufd->CloneFace();
    254  if (!face) {
    255    return nullptr;
    256  }
    257  // Create our FT2FontEntry, which inherits the name of the userfont entry
    258  // as it's not guaranteed that the face has valid names (bug 737315)
    259  FT2FontEntry* fe =
    260      FT2FontEntry::CreateFontEntry(aFontName, nullptr, 0, nullptr);
    261  if (fe) {
    262    fe->mFTFace = face.forget().take();  // mFTFace takes ownership.
    263    fe->mStyleRange = aStyle;
    264    fe->mWeightRange = aWeight;
    265    fe->mStretchRange = aStretch;
    266    fe->mIsDataUserFont = true;
    267  }
    268  return fe;
    269 }
    270 
    271 /* static */
    272 FT2FontEntry* FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE) {
    273  FT2FontEntry* fe = new FT2FontEntry(aFLE.faceName());
    274  fe->mFilename = aFLE.filepath();
    275  fe->mFTFontIndex = aFLE.index();
    276  fe->mWeightRange = WeightRange::FromScalar(aFLE.weightRange());
    277  fe->mStretchRange = StretchRange::FromScalar(aFLE.stretchRange());
    278  fe->mStyleRange = SlantStyleRange::FromScalar(aFLE.styleRange());
    279  return fe;
    280 }
    281 
    282 // Extract font entry properties from an hb_face_t
    283 static void SetPropertiesFromFace(gfxFontEntry* aFontEntry,
    284                                  const hb_face_t* aFace) {
    285  // OS2 width class to CSS 'stretch' mapping from
    286  // https://docs.microsoft.com/en-gb/typography/opentype/spec/os2#uswidthclass
    287  const float kOS2WidthToStretch[] = {
    288      100,    // (invalid, treat as normal)
    289      50,     // Ultra-condensed
    290      62.5,   // Extra-condensed
    291      75,     // Condensed
    292      87.5,   // Semi-condensed
    293      100,    // Normal
    294      112.5,  // Semi-expanded
    295      125,    // Expanded
    296      150,    // Extra-expanded
    297      200     // Ultra-expanded
    298  };
    299 
    300  // Get the macStyle field from the 'head' table
    301  gfxFontUtils::AutoHBBlob headBlob(
    302      hb_face_reference_table(aFace, HB_TAG('h', 'e', 'a', 'd')));
    303  unsigned int len;
    304  const char* data = hb_blob_get_data(headBlob, &len);
    305  uint16_t style = 0;
    306  if (len >= sizeof(HeadTable)) {
    307    const HeadTable* head = reinterpret_cast<const HeadTable*>(data);
    308    style = head->macStyle;
    309  }
    310 
    311  // Get the OS/2 table for weight & width fields
    312  gfxFontUtils::AutoHBBlob os2blob(
    313      hb_face_reference_table(aFace, HB_TAG('O', 'S', '/', '2')));
    314  data = hb_blob_get_data(os2blob, &len);
    315  uint16_t os2weight = 400;
    316  float stretch = 100.0;
    317  if (len >= offsetof(OS2Table, fsType)) {
    318    const OS2Table* os2 = reinterpret_cast<const OS2Table*>(data);
    319    os2weight = os2->usWeightClass;
    320    uint16_t os2width = os2->usWidthClass;
    321    if (os2width < std::size(kOS2WidthToStretch)) {
    322      stretch = kOS2WidthToStretch[os2width];
    323    }
    324  }
    325 
    326  aFontEntry->mStyleRange = SlantStyleRange(
    327      (style & 2) ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL);
    328  aFontEntry->mWeightRange = WeightRange(FontWeight::FromInt(int(os2weight)));
    329  aFontEntry->mStretchRange = StretchRange(FontStretch::FromFloat(stretch));
    330 
    331  // For variable fonts, update the style/weight/stretch attributes if the
    332  // corresponding variation axes are present.
    333  aFontEntry->SetupVariationRanges();
    334 }
    335 
    336 // Used to create the font entry for installed faces on the device,
    337 // when iterating over the fonts directories.
    338 // We use the hb_face_t to retrieve the details needed for the font entry,
    339 // but do *not* save a reference to it, nor create a cairo face.
    340 /* static */
    341 FT2FontEntry* FT2FontEntry::CreateFontEntry(const nsACString& aName,
    342                                            const char* aFilename,
    343                                            uint8_t aIndex,
    344                                            const hb_face_t* aFace) {
    345  FT2FontEntry* fe = new FT2FontEntry(aName);
    346  fe->mFilename = aFilename;
    347  fe->mFTFontIndex = aIndex;
    348 
    349  if (aFace) {
    350    SetPropertiesFromFace(fe, aFace);
    351  } else {
    352    // If nullptr is passed for aFace, the caller is intending to override
    353    // these attributes anyway. We just set defaults here to be safe.
    354    fe->mStyleRange = SlantStyleRange(FontSlantStyle::NORMAL);
    355    fe->mWeightRange = WeightRange(FontWeight::NORMAL);
    356    fe->mStretchRange = StretchRange(FontStretch::NORMAL);
    357  }
    358 
    359  return fe;
    360 }
    361 
    362 FT2FontEntry* gfxFT2Font::GetFontEntry() {
    363  return static_cast<FT2FontEntry*>(mFontEntry.get());
    364 }
    365 
    366 // Copied/modified from similar code in gfxMacPlatformFontList.mm:
    367 // Complex scripts will not render correctly unless Graphite or OT
    368 // layout tables are present.
    369 // For OpenType, we also check that the GSUB table supports the relevant
    370 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
    371 // the characters, but lacks OpenType support).
    372 
    373 // TODO: consider whether we should move this to gfxFontEntry and do similar
    374 // cmap-masking on all platforms to avoid using fonts that won't shape
    375 // properly.
    376 
    377 nsresult FT2FontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
    378  if (mCharacterMap || mShmemCharacterMap) {
    379    return NS_OK;
    380  }
    381 
    382  RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256);
    383 
    384  nsresult rv = NS_ERROR_NOT_AVAILABLE;
    385  uint32_t uvsOffset = 0;
    386  gfxFontUtils::AutoHBBlob cmapBlob(GetFontTable(TTAG_cmap));
    387  if (cmapBlob) {
    388    unsigned int length;
    389    const char* data = hb_blob_get_data(cmapBlob, &length);
    390    rv = gfxFontUtils::ReadCMAP((const uint8_t*)data, length, *charmap,
    391                                uvsOffset);
    392  }
    393  mUVSOffset.exchange(uvsOffset);
    394 
    395  if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) {
    396    // For downloadable fonts, trust the author and don't
    397    // try to munge the cmap based on script shaping support.
    398 
    399    // We also assume a Graphite font knows what it's doing,
    400    // and provides whatever shaping is needed for the
    401    // characters it supports, so only check/clear the
    402    // complex-script ranges for non-Graphite fonts
    403 
    404    // for layout support, check for the presence of opentype layout tables
    405    bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B'));
    406 
    407    for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
    408         sr->rangeStart; sr++) {
    409      // check to see if the cmap includes complex script codepoints
    410      if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
    411        // We check for GSUB here, as GPOS alone would not be ok.
    412        if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) {
    413          continue;
    414        }
    415        charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
    416      }
    417    }
    418 
    419    // Bug 1980258: the Cutive Mono font, widely present on Android, has
    420    // spurious blank glyphs for several codepoints. Mask them out, to avoid
    421    // the risk of font fallback using it (resulting in blank characters).
    422    if (FamilyName().EqualsLiteral("Cutive Mono")) {
    423      charmap->ClearRange(0x0080, 0x009f);  // C1 controls: not all present,
    424                                            // but let's just mask the block.
    425      charmap->clear(0x2074);               // superscript four
    426      charmap->clear(0xfb00);               // ff ligature
    427      charmap->ClearRange(0xfb03, 0xfb04);  // ffi, ffl
    428    }
    429  }
    430 
    431 #if defined(MOZ_WIDGET_ANDROID) && !defined(NIGHTLY_BUILD)
    432  // Hack for the SamsungDevanagari font, bug 1012365:
    433  // pretend the font supports U+0972.
    434  if (!charmap->test(0x0972) && charmap->test(0x0905) &&
    435      charmap->test(0x0945)) {
    436    charmap->set(0x0972);
    437  }
    438 #endif
    439 
    440  bool setCharMap = true;
    441  if (NS_SUCCEEDED(rv)) {
    442    gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
    443    fontlist::FontList* sharedFontList = pfl->SharedFontList();
    444    if (!IsUserFont() && mShmemFace) {
    445      mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily);
    446      if (TrySetShmemCharacterMap()) {
    447        setCharMap = false;
    448      }
    449    } else {
    450      charmap = pfl->FindCharMap(charmap);
    451    }
    452    mHasCmapTable = true;
    453  } else {
    454    // if error occurred, initialize to null cmap
    455    charmap = new gfxCharacterMap(0);
    456    mHasCmapTable = false;
    457  }
    458  if (setCharMap) {
    459    if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
    460      // We forget rather than addref because we don't use the charmap below.
    461      charmap.forget().leak();
    462    }
    463  }
    464 
    465  return rv;
    466 }
    467 
    468 hb_face_t* FT2FontEntry::CreateHBFace() const {
    469  hb_face_t* result = nullptr;
    470 
    471  if (mFilename[0] == '/') {
    472    // An absolute path means a normal file in the filesystem, so we can use
    473    // hb_blob_create_from_file to read it.
    474    gfxFontUtils::AutoHBBlob fileBlob(
    475        hb_blob_create_from_file(mFilename.get()));
    476    if (hb_blob_get_length(fileBlob) > 0) {
    477      result = hb_face_create(fileBlob, mFTFontIndex);
    478    }
    479  } else {
    480    // A relative path means an omnijar resource, which we may need to
    481    // decompress to a temporary buffer.
    482    RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
    483    nsZipItem* item = reader->GetItem(mFilename);
    484    MOZ_ASSERT(item, "failed to find zip entry");
    485    if (item) {
    486      // TODO(jfkthame):
    487      // Check whether the item is compressed; if not, we could just get a
    488      // pointer without needing to allocate a buffer and copy the data.
    489      // (Currently this configuration isn't used for Gecko on Android.)
    490      uint32_t length = item->RealSize();
    491      uint8_t* buffer = static_cast<uint8_t*>(malloc(length));
    492      if (buffer) {
    493        nsZipCursor cursor(item, reader, buffer, length);
    494        cursor.Copy(&length);
    495        MOZ_ASSERT(length == item->RealSize(), "error reading font");
    496        if (length == item->RealSize()) {
    497          gfxFontUtils::AutoHBBlob blob(
    498              hb_blob_create((const char*)buffer, length,
    499                             HB_MEMORY_MODE_READONLY, buffer, free));
    500          result = hb_face_create(blob, mFTFontIndex);
    501        }
    502      }
    503    }
    504  }
    505 
    506  return result;
    507 }
    508 
    509 bool FT2FontEntry::HasFontTable(uint32_t aTableTag) {
    510  // If we already have a FreeType face, we can just use that.
    511  if (mFTFace) {
    512    RefPtr<SharedFTFace> face = GetFTFace();
    513    return gfxFT2FontEntryBase::FaceHasTable(face, aTableTag);
    514  }
    515 
    516  {
    517    // If we have a cached set of tables, query that.
    518    AutoReadLock lock(mLock);
    519    if (mAvailableTables.Count() > 0) {
    520      return mAvailableTables.Contains(aTableTag);
    521    }
    522  }
    523 
    524  // If we haven't created a FreeType face already, try to avoid that by
    525  // reading the available table tags via harfbuzz and caching in a hashset.
    526  AutoWriteLock lock(mLock);
    527  if (!mFTFace && !mFilename.IsEmpty()) {
    528    hb_face_t* face = CreateHBFace();
    529    if (face) {
    530      // Read table tags in batches; 32 should be enough for most fonts in a
    531      // single operation.
    532      const unsigned TAG_BUF_LENGTH = 32;
    533      hb_tag_t tags[TAG_BUF_LENGTH];
    534      unsigned int startOffset = 0;
    535      unsigned int totalTables = 0;
    536      do {
    537        unsigned int count = TAG_BUF_LENGTH;
    538        // Updates count to the number of table tags actually retrieved
    539        totalTables = hb_face_get_table_tags(face, startOffset, &count, tags);
    540        startOffset += count;
    541        while (count-- > 0) {
    542          mAvailableTables.Insert(tags[count]);
    543        }
    544      } while (startOffset < totalTables);
    545      hb_face_destroy(face);
    546    } else {
    547      // Failed to create the HarfBuzz face! The font is probably broken.
    548      // Put a dummy entry in mAvailableTables so that we don't bother
    549      // re-trying here.
    550      mAvailableTables.Insert(uint32_t(-1));
    551    }
    552    return mAvailableTables.Contains(aTableTag);
    553  }
    554 
    555  // Last resort: we'll have to create a (temporary) FreeType face to query
    556  // for table presence.
    557  RefPtr<SharedFTFace> face = GetFTFace();
    558  return gfxFT2FontEntryBase::FaceHasTable(face, aTableTag);
    559 }
    560 
    561 nsresult FT2FontEntry::CopyFontTable(uint32_t aTableTag,
    562                                     nsTArray<uint8_t>& aBuffer) {
    563  RefPtr<SharedFTFace> face = GetFTFace();
    564  return gfxFT2FontEntryBase::CopyFaceTable(face, aTableTag, aBuffer);
    565 }
    566 
    567 hb_blob_t* FT2FontEntry::GetFontTable(uint32_t aTableTag) {
    568  if (FTUserFontData* userFontData = GetUserFontData()) {
    569    // If there's a cairo font face, we may be able to return a blob
    570    // that just wraps a range of the attached user font data
    571    if (userFontData->FontData()) {
    572      return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
    573                                                aTableTag);
    574    }
    575  }
    576 
    577  // If the FT_Face hasn't been instantiated, try to read table directly
    578  // via harfbuzz API to avoid expensive FT_Face creation.
    579  if (!mFTFace && !mFilename.IsEmpty()) {
    580    hb_face_t* face = CreateHBFace();
    581    if (face) {
    582      hb_blob_t* result = hb_face_reference_table(face, aTableTag);
    583      hb_face_destroy(face);
    584      return result;
    585    }
    586  }
    587 
    588  // Otherwise, use the default method (which in turn will call our
    589  // implementation of CopyFontTable).
    590  return gfxFontEntry::GetFontTable(aTableTag);
    591 }
    592 
    593 bool FT2FontEntry::HasVariations() {
    594  switch (mHasVariations) {
    595    case HasVariationsState::No:
    596      return false;
    597    case HasVariationsState::Yes:
    598      return true;
    599    case HasVariationsState::Uninitialized:
    600      break;
    601  }
    602 
    603  SharedFTFace* face = mFTFace;
    604  bool hasVariations =
    605      face ? face->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS
    606           : gfxPlatform::HasVariationFontSupport() &&
    607                 HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r'));
    608  mHasVariations =
    609      hasVariations ? HasVariationsState::Yes : HasVariationsState::No;
    610  return hasVariations;
    611 }
    612 
    613 void FT2FontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes) {
    614  if (!HasVariations()) {
    615    return;
    616  }
    617  if (FT_MM_Var* mmVar = GetMMVar()) {
    618    gfxFT2Utils::GetVariationAxes(mmVar, aAxes);
    619  }
    620 }
    621 
    622 void FT2FontEntry::GetVariationInstances(
    623    nsTArray<gfxFontVariationInstance>& aInstances) {
    624  if (!HasVariations()) {
    625    return;
    626  }
    627  if (FT_MM_Var* mmVar = GetMMVar()) {
    628    gfxFT2Utils::GetVariationInstances(this, mmVar, aInstances);
    629  }
    630 }
    631 
    632 FT_MM_Var* FT2FontEntry::GetMMVar() {
    633  if (mMMVarInitialized) {
    634    return mMMVar;
    635  }
    636  AutoWriteLock lock(mLock);
    637  RefPtr<SharedFTFace> face = GetFTFace(true);
    638  if (!face) {
    639    return nullptr;
    640  }
    641  if (FT_Err_Ok != FT_Get_MM_Var(face->GetFace(), &mMMVar)) {
    642    mMMVar = nullptr;
    643  }
    644  mMMVarInitialized = true;
    645  return mMMVar;
    646 }
    647 
    648 void FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    649                                          FontListSizes* aSizes) const {
    650  gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    651  aSizes->mFontListSize +=
    652      mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    653 }
    654 
    655 void FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    656                                          FontListSizes* aSizes) const {
    657  aSizes->mFontListSize += aMallocSizeOf(this);
    658  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    659 }
    660 
    661 /*
    662 * FT2FontFamily
    663 * A standard gfxFontFamily; just adds a method used to support sending
    664 * the font list from chrome to content via IPC.
    665 */
    666 
    667 void FT2FontFamily::AddFacesToFontList(nsTArray<FontListEntry>* aFontList) {
    668  AutoReadLock lock(mLock);
    669  for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
    670    const FT2FontEntry* fe =
    671        static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
    672    if (!fe) {
    673      continue;
    674    }
    675 
    676    aFontList->AppendElement(FontListEntry(
    677        Name(), fe->Name(), fe->mFilename, fe->Weight().AsScalar(),
    678        fe->Stretch().AsScalar(), fe->SlantStyle().AsScalar(), fe->mFTFontIndex,
    679        Visibility()));
    680  }
    681 }
    682 
    683 /*
    684 * Startup cache support for the font list:
    685 * We store the list of families and faces, with their style attributes and the
    686 * corresponding font files, in the startup cache.
    687 * This allows us to recreate the gfxFT2FontList collection of families and
    688 * faces without instantiating Freetype faces for each font file (in order to
    689 * find their attributes), leading to significantly quicker startup.
    690 */
    691 
    692 #define CACHE_KEY "font.cached-list"
    693 
    694 void gfxFT2FontList::CollectInitData(const FontListEntry& aFLE,
    695                                     const nsCString& aPSName,
    696                                     const nsCString& aFullName,
    697                                     StandardFile aStdFile) {
    698  nsAutoCString key(aFLE.familyName());
    699  BuildKeyNameFromFontName(key);
    700  mFaceInitData
    701      .LookupOrInsertWith(
    702          key,
    703          [&] {
    704            mFamilyInitData.AppendElement(fontlist::Family::InitData{
    705                key, aFLE.familyName(), fontlist::Family::kNoIndex,
    706                aFLE.visibility()});
    707            return MakeUnique<nsTArray<fontlist::Face::InitData>>();
    708          })
    709      ->AppendElement(fontlist::Face::InitData{
    710          aFLE.filepath(), aFLE.index(), false,
    711          WeightRange::FromScalar(aFLE.weightRange()),
    712          StretchRange::FromScalar(aFLE.stretchRange()),
    713          SlantStyleRange::FromScalar(aFLE.styleRange())});
    714  nsAutoCString psname(aPSName), fullname(aFullName);
    715  if (!psname.IsEmpty()) {
    716    ToLowerCase(psname);
    717    MaybeAddToLocalNameTable(
    718        psname, fontlist::LocalFaceRec::InitData(key, aFLE.filepath()));
    719  }
    720  if (!fullname.IsEmpty()) {
    721    ToLowerCase(fullname);
    722    if (fullname != psname) {
    723      MaybeAddToLocalNameTable(
    724          fullname, fontlist::LocalFaceRec::InitData(key, aFLE.filepath()));
    725    }
    726  }
    727 }
    728 
    729 class FontNameCache {
    730 public:
    731  // Delimiters used in the cached font-list records we store in startupCache
    732  static const char kFileSep = 0x1c;
    733  static const char kGroupSep = 0x1d;
    734  static const char kRecordSep = 0x1e;
    735  static const char kFieldSep = 0x1f;
    736 
    737  // Separator for font property ranges; we only look for this within a
    738  // field that holds a serialized FontPropertyValue or Range, so there's no
    739  // risk of conflicting with printable characters in font names.
    740  // Note that this must be a character that will terminate strtof() parsing
    741  // of a number.
    742  static const char kRangeSep = ':';
    743 
    744  // Creates the object but does NOT load the cached data from the startup
    745  // cache; call Init() after creation to do that.
    746  FontNameCache() : mMap(&mOps, sizeof(FNCMapEntry), 0), mWriteNeeded(false) {
    747    // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
    748    // it to |mMap|'s constructor. A more normal approach here would be to
    749    // have a static |sOps| member. Unfortunately, this mysteriously but
    750    // consistently makes Fennec start-up slower, so we take this
    751    // unorthodox approach instead. It's safe because PLDHashTable's
    752    // constructor doesn't dereference the pointer; it just makes a copy of
    753    // it.
    754    mOps = (PLDHashTableOps){StringHash, HashMatchEntry, MoveEntry,
    755                             PLDHashTable::ClearEntryStub, nullptr};
    756 
    757    MOZ_ASSERT(XRE_IsParentProcess(),
    758               "FontNameCache should only be used in chrome process");
    759    mCache = mozilla::scache::StartupCache::GetSingleton();
    760  }
    761 
    762  ~FontNameCache() { WriteCache(); }
    763 
    764  size_t EntryCount() const { return mMap.EntryCount(); }
    765 
    766  void DropStaleEntries() {
    767    for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
    768      auto entry = static_cast<FNCMapEntry*>(iter.Get());
    769      if (!entry->mFileExists) {
    770        iter.Remove();
    771      }
    772    }
    773  }
    774 
    775  void WriteCache() {
    776    if (!mWriteNeeded || !mCache) {
    777      return;
    778    }
    779 
    780    LOG(("Writing FontNameCache:"));
    781    nsAutoCString buf;
    782    for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
    783      auto entry = static_cast<FNCMapEntry*>(iter.Get());
    784      MOZ_ASSERT(entry->mFileExists);
    785      buf.Append(entry->mFilename);
    786      buf.Append(kGroupSep);
    787      buf.Append(entry->mFaces);
    788      buf.Append(kGroupSep);
    789      buf.AppendInt(entry->mTimestamp);
    790      buf.Append(kGroupSep);
    791      buf.AppendInt(entry->mFilesize);
    792      buf.Append(kFileSep);
    793    }
    794 
    795    LOG(("putting FontNameCache to " CACHE_KEY ", length %zu",
    796         buf.Length() + 1));
    797    mCache->PutBuffer(CACHE_KEY, UniqueFreePtr<char[]>(ToNewCString(buf)),
    798                      buf.Length() + 1);
    799    mWriteNeeded = false;
    800  }
    801 
    802  // This may be called more than once (if we re-load the font list).
    803  void Init() {
    804    if (!mCache) {
    805      return;
    806    }
    807 
    808    uint32_t size;
    809    const char* cur;
    810    if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &cur, &size))) {
    811      LOG(("no cache of " CACHE_KEY));
    812      return;
    813    }
    814 
    815    LOG(("got: %u bytes from the cache " CACHE_KEY, size));
    816 
    817    mMap.Clear();
    818    mWriteNeeded = false;
    819 
    820    while (const char* fileEnd = strchr(cur, kFileSep)) {
    821      // The cached record for one file is at [cur, fileEnd].
    822 
    823      // Find end of field that starts at aStart, terminated by kGroupSep or
    824      // end of record.
    825      auto endOfField = [=](const char* aStart) -> const char* {
    826        MOZ_ASSERT(aStart <= fileEnd);
    827        const char* end = static_cast<const char*>(
    828            memchr(aStart, kGroupSep, fileEnd - aStart));
    829        if (end) {
    830          return end;
    831        }
    832        return fileEnd;
    833      };
    834 
    835      // Advance aStart and aEnd to indicate the range of the next field and
    836      // return true, or just return false if already at end of record.
    837      auto nextField = [=](const char*& aStart, const char*& aEnd) -> bool {
    838        if (aEnd < fileEnd) {
    839          aStart = aEnd + 1;
    840          aEnd = endOfField(aStart);
    841          return true;
    842        }
    843        return false;
    844      };
    845 
    846      const char* end = endOfField(cur);
    847      nsCString filename(cur, end - cur);
    848      if (!nextField(cur, end)) {
    849        break;
    850      }
    851      nsCString faceList(cur, end - cur);
    852      if (!nextField(cur, end)) {
    853        break;
    854      }
    855      uint32_t timestamp = strtoul(cur, nullptr, 10);
    856      if (!nextField(cur, end)) {
    857        break;
    858      }
    859      uint32_t filesize = strtoul(cur, nullptr, 10);
    860 
    861      auto mapEntry =
    862          static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible));
    863      if (mapEntry) {
    864        mapEntry->mFilename.Assign(filename);
    865        mapEntry->mTimestamp = timestamp;
    866        mapEntry->mFilesize = filesize;
    867        mapEntry->mFaces.Assign(faceList);
    868        // entries from the startupcache are marked "non-existing"
    869        // until we have confirmed that the file still exists
    870        mapEntry->mFileExists = false;
    871      }
    872 
    873      cur = fileEnd + 1;
    874    }
    875  }
    876 
    877  void GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
    878                      uint32_t* aTimestamp, uint32_t* aFilesize) {
    879    auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get()));
    880    if (entry) {
    881      *aTimestamp = entry->mTimestamp;
    882      *aFilesize = entry->mFilesize;
    883      aFaceList.Assign(entry->mFaces);
    884      // this entry does correspond to an existing file
    885      // (although it might not be up-to-date, in which case
    886      // it will get overwritten via CacheFileInfo)
    887      entry->mFileExists = true;
    888    }
    889  }
    890 
    891  void CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
    892                     uint32_t aTimestamp, uint32_t aFilesize) {
    893    auto entry = static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible));
    894    if (entry) {
    895      entry->mFilename.Assign(aFileName);
    896      entry->mTimestamp = aTimestamp;
    897      entry->mFilesize = aFilesize;
    898      entry->mFaces.Assign(aFaceList);
    899      entry->mFileExists = true;
    900    }
    901    mWriteNeeded = true;
    902  }
    903 
    904 private:
    905  mozilla::scache::StartupCache* mCache;
    906  PLDHashTable mMap;
    907  bool mWriteNeeded;
    908 
    909  PLDHashTableOps mOps;
    910 
    911  struct FNCMapEntry : public PLDHashEntryHdr {
    912   public:
    913    nsCString mFilename;
    914    uint32_t mTimestamp;
    915    uint32_t mFilesize;
    916    nsCString mFaces;
    917    bool mFileExists;
    918  };
    919 
    920  static PLDHashNumber StringHash(const void* key) {
    921    return HashString(reinterpret_cast<const char*>(key));
    922  }
    923 
    924  static bool HashMatchEntry(const PLDHashEntryHdr* aHdr, const void* key) {
    925    const FNCMapEntry* entry = static_cast<const FNCMapEntry*>(aHdr);
    926    return entry->mFilename.Equals(reinterpret_cast<const char*>(key));
    927  }
    928 
    929  static void MoveEntry(PLDHashTable* table, const PLDHashEntryHdr* aFrom,
    930                        PLDHashEntryHdr* aTo) {
    931    FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo);
    932    const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom);
    933    to->mFilename.Assign(from->mFilename);
    934    to->mTimestamp = from->mTimestamp;
    935    to->mFilesize = from->mFilesize;
    936    to->mFaces.Assign(from->mFaces);
    937    to->mFileExists = from->mFileExists;
    938  }
    939 };
    940 
    941 /***************************************************************
    942 *
    943 * gfxFT2FontList
    944 *
    945 */
    946 
    947 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly
    948 // scanning the system's Fonts directory for OpenType and TrueType files.
    949 
    950 #define JAR_LAST_MODIFED_TIME "jar-last-modified-time"
    951 
    952 class WillShutdownObserver : public nsIObserver {
    953 public:
    954  NS_DECL_ISUPPORTS
    955  NS_DECL_NSIOBSERVER
    956 
    957  explicit WillShutdownObserver(gfxFT2FontList* aFontList)
    958      : mFontList(aFontList) {}
    959 
    960  void Remove() {
    961    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    962    if (obs) {
    963      obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
    964    }
    965    mFontList = nullptr;
    966  }
    967 
    968 protected:
    969  virtual ~WillShutdownObserver() = default;
    970 
    971  gfxFT2FontList* mFontList;
    972 };
    973 
    974 NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver)
    975 
    976 NS_IMETHODIMP
    977 WillShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
    978                              const char16_t* aData) {
    979  if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
    980    mFontList->WillShutdown();
    981  } else {
    982    MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
    983  }
    984  return NS_OK;
    985 }
    986 
    987 gfxFT2FontList::gfxFT2FontList() : mJarModifiedTime(0) {
    988  CheckFamilyList(kBaseFonts_Android);
    989  CheckFamilyList(kBaseFonts_Android5_8);
    990  CheckFamilyList(kBaseFonts_Android9_Higher);
    991  CheckFamilyList(kBaseFonts_Android9_11);
    992  CheckFamilyList(kBaseFonts_Android12_Higher);
    993  CheckFamilyList(kLangPack_MFR_Android12_Higher);
    994 
    995  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    996  if (obs) {
    997    mObserver = new WillShutdownObserver(this);
    998    obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
    999  }
   1000 }
   1001 
   1002 gfxFT2FontList::~gfxFT2FontList() {
   1003  AutoLock lock(mLock);
   1004  if (mObserver) {
   1005    mObserver->Remove();
   1006  }
   1007 }
   1008 
   1009 bool gfxFT2FontList::AppendFacesFromCachedFaceList(CollectFunc aCollectFace,
   1010                                                   const nsCString& aFileName,
   1011                                                   const nsCString& aFaceList,
   1012                                                   StandardFile aStdFile) {
   1013  const char* start = aFaceList.get();
   1014  int count = 0;
   1015 
   1016  while (const char* recEnd = strchr(start, FontNameCache::kRecordSep)) {
   1017    auto endOfField = [=](const char* aStart) -> const char* {
   1018      MOZ_ASSERT(aStart <= recEnd);
   1019      const char* end = static_cast<const char*>(
   1020          memchr(aStart, FontNameCache::kFieldSep, recEnd - aStart));
   1021      if (end) {
   1022        return end;
   1023      }
   1024      return recEnd;
   1025    };
   1026 
   1027    auto nextField = [=](const char*& aStart, const char*& aEnd) -> bool {
   1028      if (aEnd < recEnd) {
   1029        aStart = aEnd + 1;
   1030        aEnd = endOfField(aStart);
   1031        return true;
   1032      }
   1033      return false;
   1034    };
   1035 
   1036    const char* end = endOfField(start);
   1037    nsAutoCString familyName(start, end - start);
   1038    nsAutoCString key(familyName);
   1039    ToLowerCase(key);
   1040 
   1041    if (!nextField(start, end)) {
   1042      break;
   1043    }
   1044    nsAutoCString faceName(start, end - start);
   1045 
   1046    if (!nextField(start, end)) {
   1047      break;
   1048    }
   1049    uint32_t index = strtoul(start, nullptr, 10);
   1050 
   1051    if (!nextField(start, end)) {
   1052      break;
   1053    }
   1054 
   1055    auto readIntPair = [&](int32_t& aStart, int32_t& aEnd) {
   1056      char* limit = nullptr;
   1057      aStart = strtol(start, &limit, 10);
   1058      if (*limit == FontNameCache::kRangeSep && limit + 1 < end) {
   1059        aEnd = strtof(limit + 1, nullptr);
   1060      }
   1061    };
   1062 
   1063    int32_t minStyle, maxStyle;
   1064    readIntPair(minStyle, maxStyle);
   1065 
   1066    if (!nextField(start, end)) {
   1067      break;
   1068    }
   1069 
   1070    int32_t minWeight, maxWeight;
   1071    readIntPair(minWeight, maxWeight);
   1072 
   1073    if (!nextField(start, end)) {
   1074      break;
   1075    }
   1076 
   1077    int32_t minStretch, maxStretch;
   1078    readIntPair(minStretch, maxStretch);
   1079 
   1080    if (!nextField(start, end)) {
   1081      break;
   1082    }
   1083    nsAutoCString psname(start, end - start);
   1084 
   1085    if (!nextField(start, end)) {
   1086      break;
   1087    }
   1088    nsAutoCString fullname(start, end - start);
   1089 
   1090    if (!nextField(start, end)) {
   1091      break;
   1092    }
   1093    FontVisibility visibility = FontVisibility(strtoul(start, nullptr, 10));
   1094 
   1095    FontListEntry fle(familyName, faceName, aFileName,
   1096                      WeightRange(FontWeight::FromRaw(minWeight),
   1097                                  FontWeight::FromRaw(maxWeight))
   1098                          .AsScalar(),
   1099                      StretchRange(FontStretch::FromRaw(minStretch),
   1100                                   FontStretch::FromRaw(maxStretch))
   1101                          .AsScalar(),
   1102                      SlantStyleRange(FontSlantStyle::FromRaw(minStyle),
   1103                                      FontSlantStyle::FromRaw(maxStyle))
   1104                          .AsScalar(),
   1105                      index, visibility);
   1106 
   1107    aCollectFace(fle, psname, fullname, aStdFile);
   1108    count++;
   1109 
   1110    start = recEnd + 1;
   1111  }
   1112 
   1113  return count > 0;
   1114 }
   1115 
   1116 void FT2FontEntry::AppendToFaceList(nsCString& aFaceList,
   1117                                    const nsACString& aFamilyName,
   1118                                    const nsACString& aPSName,
   1119                                    const nsACString& aFullName,
   1120                                    FontVisibility aVisibility) {
   1121  aFaceList.Append(aFamilyName);
   1122  aFaceList.Append(FontNameCache::kFieldSep);
   1123  aFaceList.Append(Name());
   1124  aFaceList.Append(FontNameCache::kFieldSep);
   1125  aFaceList.AppendInt(mFTFontIndex);
   1126  aFaceList.Append(FontNameCache::kFieldSep);
   1127  aFaceList.AppendInt(SlantStyle().Min().Raw());
   1128  aFaceList.Append(FontNameCache::kRangeSep);
   1129  aFaceList.AppendInt(SlantStyle().Max().Raw());
   1130  aFaceList.Append(FontNameCache::kFieldSep);
   1131  aFaceList.AppendInt(Weight().Min().Raw());
   1132  aFaceList.Append(FontNameCache::kRangeSep);
   1133  aFaceList.AppendInt(Weight().Max().Raw());
   1134  aFaceList.Append(FontNameCache::kFieldSep);
   1135  aFaceList.AppendInt(Stretch().Min().Raw());
   1136  aFaceList.Append(FontNameCache::kRangeSep);
   1137  aFaceList.AppendInt(Stretch().Max().Raw());
   1138  aFaceList.Append(FontNameCache::kFieldSep);
   1139  aFaceList.Append(aPSName);
   1140  aFaceList.Append(FontNameCache::kFieldSep);
   1141  aFaceList.Append(aFullName);
   1142  aFaceList.Append(FontNameCache::kFieldSep);
   1143  aFaceList.AppendInt(int(aVisibility));
   1144  aFaceList.Append(FontNameCache::kRecordSep);
   1145 }
   1146 
   1147 void FT2FontEntry::CheckForBrokenFont(gfxFontFamily* aFamily) {
   1148  // note if the family is in the "bad underline" blocklist
   1149  if (aFamily->IsBadUnderlineFamily()) {
   1150    mIsBadUnderlineFont = true;
   1151  }
   1152  nsAutoCString familyKey(aFamily->Name());
   1153  BuildKeyNameFromFontName(familyKey);
   1154  CheckForBrokenFont(familyKey);
   1155 }
   1156 
   1157 void FT2FontEntry::CheckForBrokenFont(const nsACString& aFamilyKey) {
   1158  // bug 721719 - set the IgnoreGSUB flag on entries for Roboto
   1159  // because of unwanted on-by-default "ae" ligature.
   1160  // (See also AppendFaceFromFontListEntry.)
   1161  if (aFamilyKey.EqualsLiteral("roboto")) {
   1162    mIgnoreGSUB = true;
   1163    return;
   1164  }
   1165 
   1166  // bug 706888 - set the IgnoreGSUB flag on the broken version of
   1167  // Droid Sans Arabic from certain phones, as identified by the
   1168  // font checksum in the 'head' table
   1169  if (aFamilyKey.EqualsLiteral("droid sans arabic")) {
   1170    RefPtr<SharedFTFace> face = GetFTFace();
   1171    if (face) {
   1172      const TT_Header* head = static_cast<const TT_Header*>(
   1173          FT_Get_Sfnt_Table(face->GetFace(), ft_sfnt_head));
   1174      if (head && head->CheckSum_Adjust == 0xe445242) {
   1175        mIgnoreGSUB = true;
   1176      }
   1177    }
   1178  }
   1179 }
   1180 
   1181 void gfxFT2FontList::AppendFacesFromBlob(
   1182    const nsCString& aFileName, StandardFile aStdFile, hb_blob_t* aBlob,
   1183    FontNameCache* aCache, uint32_t aTimestamp, uint32_t aFilesize) {
   1184  nsCString newFaceList;
   1185  uint32_t numFaces = 1;
   1186  unsigned int length;
   1187  const char* data = hb_blob_get_data(aBlob, &length);
   1188  // Check for a possible TrueType Collection
   1189  if (length >= sizeof(TTCHeader)) {
   1190    const TTCHeader* ttc = reinterpret_cast<const TTCHeader*>(data);
   1191    if (ttc->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
   1192      numFaces = ttc->numFonts;
   1193    }
   1194  }
   1195  for (unsigned int index = 0; index < numFaces; index++) {
   1196    hb_face_t* face = hb_face_create(aBlob, index);
   1197    if (face != hb_face_get_empty()) {
   1198      AddFaceToList(aFileName, index, aStdFile, face, newFaceList);
   1199    }
   1200    hb_face_destroy(face);
   1201  }
   1202  if (aCache && !newFaceList.IsEmpty()) {
   1203    aCache->CacheFileInfo(aFileName, newFaceList, aTimestamp, aFilesize);
   1204  }
   1205 }
   1206 
   1207 void gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName,
   1208                                             FontNameCache* aCache,
   1209                                             StandardFile aStdFile) {
   1210  nsCString cachedFaceList;
   1211  uint32_t filesize = 0, timestamp = 0;
   1212  if (aCache) {
   1213    aCache->GetInfoForFile(aFileName, cachedFaceList, &timestamp, &filesize);
   1214  }
   1215 
   1216  struct stat s;
   1217  int statRetval = stat(aFileName.get(), &s);
   1218  if (!cachedFaceList.IsEmpty() && 0 == statRetval &&
   1219      uint32_t(s.st_mtime) == timestamp && s.st_size == filesize) {
   1220    CollectFunc unshared =
   1221        [](const FontListEntry& aFLE, const nsCString& aPSName,
   1222           const nsCString& aFullName, StandardFile aStdFile)
   1223            MOZ_REQUIRES(PlatformFontList()->mLock) {
   1224              auto* pfl = PlatformFontList();
   1225              pfl->mLock.AssertCurrentThreadIn();
   1226              pfl->AppendFaceFromFontListEntry(aFLE, aStdFile);
   1227            };
   1228    CollectFunc shared = [](const FontListEntry& aFLE, const nsCString& aPSName,
   1229                            const nsCString& aFullName, StandardFile aStdFile)
   1230                             MOZ_REQUIRES(PlatformFontList()->mLock) {
   1231                               PlatformFontList()->CollectInitData(
   1232                                   aFLE, aPSName, aFullName, aStdFile);
   1233                             };
   1234    if (AppendFacesFromCachedFaceList(SharedFontList() ? shared : unshared,
   1235                                      aFileName, cachedFaceList, aStdFile)) {
   1236      LOG(("using cached font info for %s", aFileName.get()));
   1237      return;
   1238    }
   1239  }
   1240 
   1241  gfxFontUtils::AutoHBBlob fileBlob(hb_blob_create_from_file(aFileName.get()));
   1242  if (hb_blob_get_length(fileBlob) > 0) {
   1243    LOG(("reading font info via harfbuzz for %s", aFileName.get()));
   1244    AppendFacesFromBlob(aFileName, aStdFile, fileBlob,
   1245                        0 == statRetval ? aCache : nullptr, s.st_mtime,
   1246                        s.st_size);
   1247  }
   1248 }
   1249 
   1250 void gfxFT2FontList::FindFontsInOmnijar(FontNameCache* aCache) {
   1251  bool jarChanged = false;
   1252 
   1253  mozilla::scache::StartupCache* cache =
   1254      mozilla::scache::StartupCache::GetSingleton();
   1255  const char* cachedModifiedTimeBuf;
   1256  uint32_t longSize;
   1257  if (cache &&
   1258      NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME,
   1259                                    &cachedModifiedTimeBuf, &longSize)) &&
   1260      longSize == sizeof(int64_t)) {
   1261    nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE);
   1262    jarFile->GetLastModifiedTime(&mJarModifiedTime);
   1263    if (mJarModifiedTime > LittleEndian::readInt64(cachedModifiedTimeBuf)) {
   1264      jarChanged = true;
   1265    }
   1266  }
   1267 
   1268  static const char* sJarSearchPaths[] = {
   1269      "res/fonts/*.ttf$",
   1270  };
   1271  RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
   1272  for (unsigned i = 0; i < std::size(sJarSearchPaths); i++) {
   1273    nsZipFind* find;
   1274    if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) {
   1275      const char* path;
   1276      uint16_t len;
   1277      while (NS_SUCCEEDED(find->FindNext(&path, &len))) {
   1278        nsCString entryName(path, len);
   1279        AppendFacesFromOmnijarEntry(reader, entryName, aCache, jarChanged);
   1280      }
   1281      delete find;
   1282    }
   1283  }
   1284 }
   1285 
   1286 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
   1287 FontVisibility gfxFT2FontList::GetVisibilityForFamily(
   1288    const nsACString& aName) const {
   1289  static Device fontVisibilityDevice = Device::Unassigned;
   1290  if (fontVisibilityDevice == Device::Unassigned) {
   1291    nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
   1292    NS_ENSURE_SUCCESS(
   1293        gfxInfo->GetFontVisibilityDetermination(&fontVisibilityDevice),
   1294        FontVisibility::Unknown);
   1295  }
   1296 
   1297  if (fontVisibilityDevice == Device::Android_Unknown_Release_Version ||
   1298      fontVisibilityDevice == Device::Android_Unknown_Peloton ||
   1299      fontVisibilityDevice == Device::Android_Unknown_vbox ||
   1300      fontVisibilityDevice == Device::Android_Unknown_mitv ||
   1301      fontVisibilityDevice == Device::Android_Chromebook ||
   1302      fontVisibilityDevice == Device::Android_Amazon) {
   1303    return FontVisibility::Unknown;
   1304  }
   1305 
   1306  // Sanity Check
   1307  if (fontVisibilityDevice != Device::Android_sub_9 &&
   1308      fontVisibilityDevice != Device::Android_9_11 &&
   1309      fontVisibilityDevice != Device::Android_12_plus) {
   1310    return FontVisibility::Unknown;
   1311  }
   1312 
   1313  if (FamilyInList(aName, kBaseFonts_Android)) {
   1314    return FontVisibility::Base;
   1315  }
   1316 
   1317  if (fontVisibilityDevice == Device::Android_sub_9) {
   1318    if (FamilyInList(aName, kBaseFonts_Android5_8)) {
   1319      return FontVisibility::Base;
   1320    }
   1321  } else {
   1322    if (FamilyInList(aName, kBaseFonts_Android9_Higher)) {
   1323      return FontVisibility::Base;
   1324    }
   1325 
   1326    if (fontVisibilityDevice == Device::Android_9_11) {
   1327      if (FamilyInList(aName, kBaseFonts_Android9_11)) {
   1328        return FontVisibility::Base;
   1329      }
   1330    } else {
   1331      if (FamilyInList(aName, kBaseFonts_Android12_Higher)) {
   1332        return FontVisibility::Base;
   1333      }
   1334 
   1335      if (FamilyInList(aName, kLangPack_MFR_Android12_Higher)) {
   1336        return FontVisibility::LangPack;
   1337      }
   1338    }
   1339  }
   1340 
   1341  return FontVisibility::User;
   1342 }
   1343 
   1344 nsTArray<std::pair<const char**, uint32_t>>
   1345 gfxFT2FontList::GetFilteredPlatformFontLists() {
   1346  static Device fontVisibilityDevice = Device::Unassigned;
   1347  if (fontVisibilityDevice == Device::Unassigned) {
   1348    nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
   1349    (void)gfxInfo->GetFontVisibilityDetermination(&fontVisibilityDevice);
   1350  }
   1351 
   1352  nsTArray<std::pair<const char**, uint32_t>> fontLists;
   1353 
   1354  if (fontVisibilityDevice == Device::Android_Unknown_Release_Version ||
   1355      fontVisibilityDevice == Device::Android_Unknown_Peloton ||
   1356      fontVisibilityDevice == Device::Android_Unknown_vbox ||
   1357      fontVisibilityDevice == Device::Android_Unknown_mitv ||
   1358      fontVisibilityDevice == Device::Android_Chromebook ||
   1359      fontVisibilityDevice == Device::Android_Amazon) {
   1360    return fontLists;
   1361  }
   1362 
   1363  // Sanity Check
   1364  if (fontVisibilityDevice != Device::Android_sub_9 &&
   1365      fontVisibilityDevice != Device::Android_9_11 &&
   1366      fontVisibilityDevice != Device::Android_12_plus) {
   1367    return fontLists;
   1368  }
   1369 
   1370  fontLists.AppendElement(
   1371      std::make_pair(kBaseFonts_Android, std::size(kBaseFonts_Android)));
   1372 
   1373  if (fontVisibilityDevice == Device::Android_sub_9) {
   1374    fontLists.AppendElement(std::make_pair(kBaseFonts_Android5_8,
   1375                                           std::size(kBaseFonts_Android5_8)));
   1376  } else {
   1377    fontLists.AppendElement(std::make_pair(
   1378        kBaseFonts_Android9_Higher, std::size(kBaseFonts_Android9_Higher)));
   1379 
   1380    if (fontVisibilityDevice == Device::Android_9_11) {
   1381      fontLists.AppendElement(std::make_pair(
   1382          kBaseFonts_Android9_11, std::size(kBaseFonts_Android9_11)));
   1383    } else {
   1384      fontLists.AppendElement(std::make_pair(
   1385          kBaseFonts_Android12_Higher, std::size(kBaseFonts_Android12_Higher)));
   1386      fontLists.AppendElement(
   1387          std::make_pair(kLangPack_MFR_Android12_Higher,
   1388                         std::size(kLangPack_MFR_Android12_Higher)));
   1389    }
   1390  }
   1391 
   1392  return fontLists;
   1393 }
   1394 
   1395 static void GetName(hb_face_t* aFace, hb_ot_name_id_t aNameID,
   1396                    nsACString& aName) {
   1397  unsigned int n = 0;
   1398  n = hb_ot_name_get_utf8(aFace, aNameID, HB_LANGUAGE_INVALID, &n, nullptr);
   1399  if (n) {
   1400    aName.SetLength(n++);  // increment n to account for NUL terminator
   1401    n = hb_ot_name_get_utf8(aFace, aNameID, HB_LANGUAGE_INVALID, &n,
   1402                            aName.BeginWriting());
   1403  }
   1404 }
   1405 
   1406 // Given the harfbuzz face corresponding to an entryName and face index,
   1407 // add the face to the available font list and to the faceList string
   1408 void gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
   1409                                   StandardFile aStdFile, hb_face_t* aFace,
   1410                                   nsCString& aFaceList) {
   1411  nsAutoCString familyName;
   1412  bool preferTypographicNames = true;
   1413  GetName(aFace, HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY, familyName);
   1414  if (familyName.IsEmpty()) {
   1415    preferTypographicNames = false;
   1416    GetName(aFace, HB_OT_NAME_ID_FONT_FAMILY, familyName);
   1417  }
   1418  if (familyName.IsEmpty()) {
   1419    return;
   1420  }
   1421 
   1422  nsAutoCString fullname;
   1423  GetName(aFace, HB_OT_NAME_ID_FULL_NAME, fullname);
   1424  if (fullname.IsEmpty()) {
   1425    // Construct fullname from family + style
   1426    fullname = familyName;
   1427    nsAutoCString styleName;
   1428    if (preferTypographicNames) {
   1429      GetName(aFace, HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, styleName);
   1430    }
   1431    if (styleName.IsEmpty()) {
   1432      GetName(aFace, HB_OT_NAME_ID_FONT_SUBFAMILY, styleName);
   1433    }
   1434    if (!styleName.IsEmpty() && !styleName.EqualsLiteral("Regular")) {
   1435      fullname.Append(' ');
   1436      fullname.Append(styleName);
   1437    }
   1438  }
   1439 
   1440  // Build the font entry name and create an FT2FontEntry,
   1441  // but do -not- keep a reference to the FT_Face.
   1442  // (When using the shared font list, this entry will not be retained,
   1443  // it is used only to call AppendToFaceList.)
   1444  RefPtr<FT2FontEntry> fe =
   1445      FT2FontEntry::CreateFontEntry(fullname, aEntryName.get(), aIndex, aFace);
   1446 
   1447  if (fe) {
   1448    fe->mStandardFace = (aStdFile == kStandard);
   1449    nsAutoCString familyKey(familyName);
   1450    BuildKeyNameFromFontName(familyKey);
   1451 
   1452    FontVisibility visibility = GetVisibilityForFamily(familyName);
   1453 
   1454    nsAutoCString psname;
   1455    GetName(aFace, HB_OT_NAME_ID_POSTSCRIPT_NAME, psname);
   1456 
   1457    if (SharedFontList()) {
   1458      FontListEntry fle(familyName, fe->Name(), fe->mFilename,
   1459                        fe->Weight().AsScalar(), fe->Stretch().AsScalar(),
   1460                        fe->SlantStyle().AsScalar(), fe->mFTFontIndex,
   1461                        visibility);
   1462      CollectInitData(fle, psname, fullname, aStdFile);
   1463    } else {
   1464      RefPtr<gfxFontFamily> family =
   1465          mFontFamilies.LookupOrInsertWith(familyKey, [&] {
   1466            auto family = MakeRefPtr<FT2FontFamily>(familyName, visibility);
   1467            if (mSkipSpaceLookupCheckFamilies.Contains(familyKey)) {
   1468              family->SetSkipSpaceFeatureCheck(true);
   1469            }
   1470            if (mBadUnderlineFamilyNames.ContainsSorted(familyKey)) {
   1471              family->SetBadUnderlineFamily();
   1472            }
   1473            return family;
   1474          });
   1475      family->AddFontEntry(fe);
   1476      fe->CheckForBrokenFont(family);
   1477    }
   1478 
   1479    fe->AppendToFaceList(aFaceList, familyName, psname, fullname, visibility);
   1480    if (LOG_ENABLED()) {
   1481      nsAutoCString weightString;
   1482      fe->Weight().ToString(weightString);
   1483      nsAutoCString stretchString;
   1484      fe->Stretch().ToString(stretchString);
   1485      LOG(
   1486          ("(fontinit) added (%s) to family (%s)"
   1487           " with style: %s weight: %s stretch: %s",
   1488           fe->Name().get(), familyName.get(),
   1489           fe->IsItalic() ? "italic" : "normal", weightString.get(),
   1490           stretchString.get()));
   1491    }
   1492  }
   1493 }
   1494 
   1495 void gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
   1496                                                 const nsCString& aEntryName,
   1497                                                 FontNameCache* aCache,
   1498                                                 bool aJarChanged) {
   1499  nsCString faceList;
   1500  if (aCache && !aJarChanged) {
   1501    uint32_t filesize, timestamp;
   1502    aCache->GetInfoForFile(aEntryName, faceList, &timestamp, &filesize);
   1503    if (faceList.Length() > 0) {
   1504      CollectFunc unshared =
   1505          [](const FontListEntry& aFLE, const nsCString& aPSName,
   1506             const nsCString& aFullName, StandardFile aStdFile) {
   1507            auto* pfl = PlatformFontList();
   1508            pfl->mLock.AssertCurrentThreadIn();
   1509            pfl->AppendFaceFromFontListEntry(aFLE, aStdFile);
   1510          };
   1511      CollectFunc shared = [](const FontListEntry& aFLE,
   1512                              const nsCString& aPSName,
   1513                              const nsCString& aFullName, StandardFile aStdFile)
   1514                               MOZ_REQUIRES(PlatformFontList()->mLock) {
   1515                                 PlatformFontList()->CollectInitData(
   1516                                     aFLE, aPSName, aFullName, aStdFile);
   1517                               };
   1518      if (AppendFacesFromCachedFaceList(SharedFontList() ? shared : unshared,
   1519                                        aEntryName, faceList, kStandard)) {
   1520        return;
   1521      }
   1522    }
   1523  }
   1524 
   1525  nsZipItem* item = aArchive->GetItem(aEntryName);
   1526  NS_ASSERTION(item, "failed to find zip entry");
   1527 
   1528  uint32_t bufSize = item->RealSize();
   1529 
   1530  // We use fallible allocation here; if there's not enough RAM, we'll simply
   1531  // ignore the bundled fonts and fall back to the device's installed fonts.
   1532  char* buffer = static_cast<char*>(malloc(bufSize));
   1533  if (!buffer) {
   1534    return;
   1535  }
   1536 
   1537  nsZipCursor cursor(item, aArchive, (uint8_t*)buffer, bufSize);
   1538  uint8_t* data = cursor.Copy(&bufSize);
   1539  MOZ_ASSERT(data && bufSize == item->RealSize(), "error reading bundled font");
   1540  if (!data) {
   1541    return;
   1542  }
   1543 
   1544  gfxFontUtils::AutoHBBlob blob(
   1545      hb_blob_create(buffer, bufSize, HB_MEMORY_MODE_READONLY, buffer, free));
   1546  AppendFacesFromBlob(aEntryName, kStandard, blob, aCache, 0, bufSize);
   1547 }
   1548 
   1549 // Called on each family after all fonts are added to the list;
   1550 // if aSortFaces is true this will sort faces to give priority to "standard"
   1551 // font files.
   1552 void FT2FontFamily::FinalizeMemberList(bool aSortFaces) {
   1553  AutoWriteLock lock(mLock);
   1554 
   1555  SetHasStyles(true);
   1556 
   1557  if (aSortFaces) {
   1558    SortAvailableFonts();
   1559  }
   1560  CheckForSimpleFamily();
   1561 }
   1562 
   1563 void gfxFT2FontList::FindFonts() {
   1564  MOZ_ASSERT(XRE_IsParentProcess());
   1565 
   1566  // Chrome process: get the cached list (if any)
   1567  if (!mFontNameCache) {
   1568    mFontNameCache = MakeUnique<FontNameCache>();
   1569  }
   1570  mFontNameCache->Init();
   1571 
   1572 #if defined(MOZ_WIDGET_ANDROID)
   1573  // Android API 29+ provides system font and font matcher API for native code.
   1574 
   1575  nsAutoCString androidFontsRoot = [&] {
   1576    // ANDROID_ROOT is the root of the android system, typically /system;
   1577    // font files are in /$ANDROID_ROOT/fonts/
   1578    nsAutoCString root;
   1579    char* androidRoot = PR_GetEnv("ANDROID_ROOT");
   1580    if (androidRoot) {
   1581      root = androidRoot;
   1582    } else {
   1583      root = "/system"_ns;
   1584    }
   1585    root.AppendLiteral("/fonts");
   1586    return root;
   1587  }();
   1588 
   1589  bool useSystemFontAPI = !gfxAndroidPlatform::IsFontAPIDisabled();
   1590 
   1591  if (useSystemFontAPI) {
   1592    bool noFontByFontAPI = true;
   1593    if (__builtin_available(android 29, *)) {
   1594      gfxAndroidPlatform::WaitForInitializeFontAPI();
   1595      AndroidSystemFontIterator iter;
   1596      while (Maybe<AndroidFont> androidFont = iter.Next()) {
   1597        const char* _Nonnull fontPath = androidFont->GetFontFilePath();
   1598        noFontByFontAPI = false;
   1599        AppendFacesFromFontFile(nsDependentCString(fontPath),
   1600                                mFontNameCache.get(), kStandard);
   1601      }
   1602    }
   1603 
   1604    if (noFontByFontAPI) {
   1605      // Font API doesn't seem to work. Use legacy way.
   1606      useSystemFontAPI = false;
   1607    }
   1608 
   1609    if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) {
   1610      // We turn off COLRv1 fonts support. Newer android versions have
   1611      // COLRv1 emoji font, and a legacy and hidden CBDT font we understand,
   1612      // so try to find NotoColorEmojiLegacy.ttf explicitly for now.
   1613      nsAutoCString legacyEmojiFont(androidFontsRoot);
   1614      legacyEmojiFont.Append("/NotoColorEmojiLegacy.ttf");
   1615      AppendFacesFromFontFile(legacyEmojiFont, mFontNameCache.get(), kStandard);
   1616    }
   1617  }
   1618 
   1619  if (!useSystemFontAPI)
   1620 #endif
   1621  {
   1622    FindFontsInDir(androidFontsRoot, mFontNameCache.get());
   1623  }
   1624 
   1625  // Look for fonts stored in omnijar, unless we're on a low-memory
   1626  // device where we don't want to spend the RAM to decompress them.
   1627  // (Prefs may disable this, or force-enable it even with low memory.)
   1628  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() > 0 ||
   1629      (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() < 0 &&
   1630       !nsMemory::IsLowMemoryPlatform())) {
   1631    auto timer = glean::fontlist::bundledfonts_activate.Measure();
   1632    FindFontsInOmnijar(mFontNameCache.get());
   1633  }
   1634 
   1635  // Look for downloaded fonts in a profile-agnostic "fonts" directory.
   1636  nsCOMPtr<nsIProperties> dirSvc =
   1637      do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   1638  if (dirSvc) {
   1639    nsCOMPtr<nsIFile> appDir;
   1640    nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
   1641                              getter_AddRefs(appDir));
   1642    if (NS_SUCCEEDED(rv)) {
   1643      appDir->AppendNative("fonts"_ns);
   1644      nsCString localPath;
   1645      if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
   1646        FindFontsInDir(localPath, mFontNameCache.get());
   1647      }
   1648    }
   1649  }
   1650 
   1651  // look for locally-added fonts in a "fonts" subdir of the profile
   1652  nsCOMPtr<nsIFile> localDir;
   1653  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
   1654                                       getter_AddRefs(localDir));
   1655  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(localDir->Append(u"fonts"_ns))) {
   1656    nsCString localPath;
   1657    rv = localDir->GetNativePath(localPath);
   1658    if (NS_SUCCEEDED(rv)) {
   1659      FindFontsInDir(localPath, mFontNameCache.get());
   1660    }
   1661  }
   1662 
   1663  mFontNameCache->DropStaleEntries();
   1664  if (!mFontNameCache->EntryCount()) {
   1665    // if we can't find any usable fonts, we are doomed!
   1666    MOZ_CRASH("No font files found");
   1667  }
   1668 
   1669  // Write out FontCache data if needed
   1670  WriteCache();
   1671 }
   1672 
   1673 void gfxFT2FontList::WriteCache() {
   1674  if (mFontNameCache) {
   1675    mFontNameCache->WriteCache();
   1676  }
   1677  mozilla::scache::StartupCache* cache =
   1678      mozilla::scache::StartupCache::GetSingleton();
   1679  if (cache && mJarModifiedTime > 0) {
   1680    const size_t bufSize = sizeof(mJarModifiedTime);
   1681    auto buf = UniqueFreePtr<char[]>(
   1682        reinterpret_cast<char*>(malloc(sizeof(char) * bufSize)));
   1683    LittleEndian::writeInt64(buf.get(), mJarModifiedTime);
   1684 
   1685    LOG(("WriteCache: putting Jar, length %zu", bufSize));
   1686    cache->PutBuffer(JAR_LAST_MODIFED_TIME, std::move(buf), bufSize);
   1687  }
   1688  LOG(("Done with writecache"));
   1689 }
   1690 
   1691 void gfxFT2FontList::FindFontsInDir(const nsCString& aDir,
   1692                                    FontNameCache* aFNC) {
   1693  static const char* sStandardFonts[] = {"DroidSans.ttf",
   1694                                         "DroidSans-Bold.ttf",
   1695                                         "DroidSerif-Regular.ttf",
   1696                                         "DroidSerif-Bold.ttf",
   1697                                         "DroidSerif-Italic.ttf",
   1698                                         "DroidSerif-BoldItalic.ttf",
   1699                                         "DroidSansMono.ttf",
   1700                                         "DroidSansArabic.ttf",
   1701                                         "DroidSansHebrew.ttf",
   1702                                         "DroidSansThai.ttf",
   1703                                         "MTLmr3m.ttf",
   1704                                         "MTLc3m.ttf",
   1705                                         "NanumGothic.ttf",
   1706                                         "NotoColorEmoji.ttf",
   1707                                         "NotoColorEmojiFlags.ttf",
   1708                                         "NotoColorEmojiLegacy.ttf",
   1709                                         "DroidSansJapanese.ttf",
   1710                                         "DroidSansFallback.ttf"};
   1711 
   1712  DIR* d = opendir(aDir.get());
   1713  if (!d) {
   1714    return;
   1715  }
   1716 
   1717  struct dirent* ent = nullptr;
   1718  while ((ent = readdir(d)) != nullptr) {
   1719    const char* ext = strrchr(ent->d_name, '.');
   1720    if (!ext) {
   1721      continue;
   1722    }
   1723    if (strcasecmp(ext, ".ttf") == 0 || strcasecmp(ext, ".otf") == 0 ||
   1724        strcasecmp(ext, ".woff") == 0 || strcasecmp(ext, ".ttc") == 0) {
   1725      bool isStdFont = false;
   1726      for (unsigned int i = 0; i < std::size(sStandardFonts) && !isStdFont;
   1727           i++) {
   1728        isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0;
   1729      }
   1730 
   1731      nsAutoCString s(aDir);
   1732      s.Append('/');
   1733      s.Append(ent->d_name);
   1734 
   1735      // Add the face(s) from this file to our font list;
   1736      // note that if we have cached info for this file in fnc,
   1737      // and the file is unchanged, we won't actually need to read it.
   1738      // If the file is new/changed, this will update the FontNameCache.
   1739      AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown);
   1740    }
   1741  }
   1742 
   1743  closedir(d);
   1744 }
   1745 
   1746 void gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
   1747                                                 StandardFile aStdFile) {
   1748  FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE);
   1749  if (fe) {
   1750    nsAutoCString key(aFLE.familyName());
   1751    BuildKeyNameFromFontName(key);
   1752    fe->mStandardFace = (aStdFile == kStandard);
   1753    RefPtr<gfxFontFamily> family = mFontFamilies.LookupOrInsertWith(key, [&] {
   1754      auto family =
   1755          MakeRefPtr<FT2FontFamily>(aFLE.familyName(), aFLE.visibility());
   1756      if (mSkipSpaceLookupCheckFamilies.Contains(key)) {
   1757        family->SetSkipSpaceFeatureCheck(true);
   1758      }
   1759      if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
   1760        family->SetBadUnderlineFamily();
   1761      }
   1762      return family;
   1763    });
   1764    family->AddFontEntry(fe);
   1765 
   1766    fe->CheckForBrokenFont(family);
   1767  }
   1768 }
   1769 
   1770 void gfxFT2FontList::ReadSystemFontList(dom::SystemFontList* aList) {
   1771  AutoLock lock(mLock);
   1772  for (const auto& entry : mFontFamilies) {
   1773    auto family = static_cast<FT2FontFamily*>(entry.GetData().get());
   1774    family->AddFacesToFontList(&aList->entries());
   1775  }
   1776 }
   1777 
   1778 static void LoadSkipSpaceLookupCheck(
   1779    nsTHashSet<nsCString>& aSkipSpaceLookupCheck) {
   1780  AutoTArray<nsCString, 5> skiplist;
   1781  gfxFontUtils::GetPrefsFontList(
   1782      "font.whitelist.skip_default_features_space_check", skiplist);
   1783  uint32_t numFonts = skiplist.Length();
   1784  for (uint32_t i = 0; i < numFonts; i++) {
   1785    ToLowerCase(skiplist[i]);
   1786    aSkipSpaceLookupCheck.Insert(skiplist[i]);
   1787  }
   1788 }
   1789 
   1790 nsresult gfxFT2FontList::InitFontListForPlatform() {
   1791  LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies);
   1792 
   1793  if (XRE_IsParentProcess()) {
   1794    // This will populate/update mFontNameCache and store it in the
   1795    // startupCache for future startups.
   1796    FindFonts();
   1797 
   1798    // Finalize the families by sorting faces into standard order
   1799    // and marking "simple" families.
   1800    for (const auto& entry : mFontFamilies) {
   1801      auto* family = static_cast<FT2FontFamily*>(entry.GetData().get());
   1802      family->FinalizeMemberList(/* aSortFaces */ true);
   1803    }
   1804 
   1805    return NS_OK;
   1806  }
   1807 
   1808  // Content process: use font list passed from the chrome process via
   1809  // the GetXPCOMProcessAttributes message.
   1810  auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
   1811  for (FontListEntry& fle : fontList.entries()) {
   1812    // We don't need to identify "standard" font files here,
   1813    // as the faces are already sorted.
   1814    AppendFaceFromFontListEntry(fle, kUnknown);
   1815  }
   1816 
   1817  // We don't need to sort faces (because they were already sorted by the
   1818  // chrome process, so we just maintain the existing order)
   1819  for (const auto& entry : mFontFamilies) {
   1820    auto* family = static_cast<FT2FontFamily*>(entry.GetData().get());
   1821    family->FinalizeMemberList(/* aSortFaces */ false);
   1822  }
   1823 
   1824  LOG(("got font list from chrome process: %" PRIdPTR " faces in %" PRIu32
   1825       " families",
   1826       fontList.entries().Length(), mFontFamilies.Count()));
   1827  fontList.entries().Clear();
   1828 
   1829  return NS_OK;
   1830 }
   1831 
   1832 void gfxFT2FontList::InitSharedFontListForPlatform() {
   1833  if (!XRE_IsParentProcess()) {
   1834    // Content processes will access the shared-memory data created by the
   1835    // parent, so don't need to scan for available fonts themselves.
   1836    return;
   1837  }
   1838 
   1839  // This will populate mFontNameCache with entries for all the available font
   1840  // files, and record them in mFamilies (unshared list) or mFamilyInitData and
   1841  // mFaceInitData (shared font list).
   1842  FindFonts();
   1843 
   1844  mozilla::fontlist::FontList* list = SharedFontList();
   1845  list->SetFamilyNames(mFamilyInitData);
   1846 
   1847  auto families = list->Families();
   1848  for (uint32_t i = 0; i < mFamilyInitData.Length(); i++) {
   1849    auto faceList = mFaceInitData.Get(mFamilyInitData[i].mKey);
   1850    MOZ_ASSERT(faceList);
   1851    families[i].AddFaces(list, *faceList);
   1852  }
   1853 
   1854  mFamilyInitData.Clear();
   1855  mFaceInitData.Clear();
   1856 }
   1857 
   1858 gfxFontEntry* gfxFT2FontList::CreateFontEntry(fontlist::Face* aFace,
   1859                                              const fontlist::Family* aFamily) {
   1860  fontlist::FontList* list = SharedFontList();
   1861  nsAutoCString desc(aFace->mDescriptor.AsString(list));
   1862  FT2FontEntry* fe =
   1863      FT2FontEntry::CreateFontEntry(desc, desc.get(), aFace->mIndex, nullptr);
   1864  fe->InitializeFrom(aFace, aFamily);
   1865  fe->CheckForBrokenFont(aFamily->Key().AsString(list));
   1866  return fe;
   1867 }
   1868 
   1869 // called for each family name, based on the assumption that the
   1870 // first part of the full name is the family name
   1871 
   1872 gfxFontEntry* gfxFT2FontList::LookupLocalFont(
   1873    FontVisibilityProvider* aFontVisibilityProvider,
   1874    const nsACString& aFontName, WeightRange aWeightForEntry,
   1875    StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
   1876  AutoLock lock(mLock);
   1877 
   1878  if (SharedFontList()) {
   1879    return LookupInSharedFaceNameList(aFontVisibilityProvider, aFontName,
   1880                                      aWeightForEntry, aStretchForEntry,
   1881                                      aStyleForEntry);
   1882  }
   1883 
   1884  // walk over list of names
   1885  FT2FontEntry* fontEntry = nullptr;
   1886  FontVisibility level = aFontVisibilityProvider
   1887                             ? aFontVisibilityProvider->GetFontVisibility()
   1888                             : FontVisibility::User;
   1889 
   1890  for (const RefPtr<gfxFontFamily>& fontFamily : mFontFamilies.Values()) {
   1891    if (!IsVisibleToCSS(*fontFamily, level)) {
   1892      continue;
   1893    }
   1894 
   1895    // Check family name, based on the assumption that the
   1896    // first part of the full name is the family name
   1897 
   1898    // does the family name match up to the length of the family name?
   1899    const nsCString& family = fontFamily->Name();
   1900 
   1901    const nsAutoCString fullNameFamily(
   1902        Substring(aFontName, 0, family.Length()));
   1903 
   1904    // if so, iterate over faces in this family to see if there is a match
   1905    if (family.Equals(fullNameFamily, nsCaseInsensitiveCStringComparator)) {
   1906      gfxFontEntry* fe =
   1907          fontFamily->FindFont(aFontName, nsCaseInsensitiveCStringComparator);
   1908      if (fe) {
   1909        fontEntry = static_cast<FT2FontEntry*>(fe);
   1910        goto searchDone;
   1911      }
   1912    }
   1913  }
   1914 
   1915 searchDone:
   1916  if (!fontEntry) {
   1917    return nullptr;
   1918  }
   1919 
   1920  // Clone the font entry so that we can then set its style descriptors
   1921  // from the userfont entry rather than the actual font.
   1922 
   1923  // Ensure existence of mFTFace in the original entry
   1924  RefPtr<SharedFTFace> face = fontEntry->GetFTFace(true);
   1925  if (!face) {
   1926    return nullptr;
   1927  }
   1928 
   1929  FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(
   1930      fontEntry->Name(), fontEntry->mFilename.get(), fontEntry->mFTFontIndex,
   1931      nullptr);
   1932  if (fe) {
   1933    fe->mStyleRange = aStyleForEntry;
   1934    fe->mWeightRange = aWeightForEntry;
   1935    fe->mStretchRange = aStretchForEntry;
   1936    fe->mIsLocalUserFont = true;
   1937  }
   1938 
   1939  return fe;
   1940 }
   1941 
   1942 FontFamily gfxFT2FontList::GetDefaultFontForPlatform(
   1943    FontVisibilityProvider* aFontVisibilityProvider, const gfxFontStyle* aStyle,
   1944    nsAtom* aLanguage) {
   1945  FontFamily ff;
   1946 #if defined(MOZ_WIDGET_ANDROID)
   1947  ff = FindFamily(aFontVisibilityProvider, "Roboto"_ns);
   1948  if (ff.IsNull()) {
   1949    ff = FindFamily(aFontVisibilityProvider, "Droid Sans"_ns);
   1950  }
   1951 #endif
   1952  /* TODO: what about Qt or other platforms that may use this? */
   1953  return ff;
   1954 }
   1955 
   1956 gfxFontEntry* gfxFT2FontList::MakePlatformFont(const nsACString& aFontName,
   1957                                               WeightRange aWeightForEntry,
   1958                                               StretchRange aStretchForEntry,
   1959                                               SlantStyleRange aStyleForEntry,
   1960                                               const uint8_t* aFontData,
   1961                                               uint32_t aLength) {
   1962  // The FT2 font needs the font data to persist, so we do NOT free it here
   1963  // but instead pass ownership to the font entry.
   1964  // Deallocation will happen later, when the font face is destroyed.
   1965  return FT2FontEntry::CreateFontEntry(aFontName, aWeightForEntry,
   1966                                       aStretchForEntry, aStyleForEntry,
   1967                                       aFontData, aLength);
   1968 }
   1969 
   1970 gfxFontFamily* gfxFT2FontList::CreateFontFamily(
   1971    const nsACString& aName, FontVisibility aVisibility) const {
   1972  return new FT2FontFamily(aName, aVisibility);
   1973 }
   1974 
   1975 void gfxFT2FontList::WillShutdown() {
   1976  LOG(("WillShutdown"));
   1977  WriteCache();
   1978  mFontNameCache = nullptr;
   1979 }