tor-browser

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

gfxDWriteFontList.cpp (92883B)


      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/FontPropertyTypes.h"
      7 #include "mozilla/MemoryReporting.h"
      8 #include "mozilla/intl/OSPreferences.h"
      9 
     10 #include "gfxDWriteFontList.h"
     11 #include "gfxDWriteFonts.h"
     12 #include "gfxDWriteCommon.h"
     13 #include "nsUnicharUtils.h"
     14 #include "nsPresContext.h"
     15 #include "nsServiceManagerUtils.h"
     16 #include "nsCharSeparatedTokenizer.h"
     17 #include "mozilla/dom/Document.h"
     18 #include "mozilla/gfx/Logging.h"
     19 #include "mozilla/LookAndFeel.h"
     20 #include "mozilla/Preferences.h"
     21 #include "mozilla/ProfilerLabels.h"
     22 #include "mozilla/Sprintf.h"
     23 #include "mozilla/StaticPrefs_gfx.h"
     24 #include "mozilla/glean/GfxMetrics.h"
     25 #include "mozilla/WindowsProcessMitigations.h"
     26 #include "nsDirectoryServiceUtils.h"
     27 #include "nsDirectoryServiceDefs.h"
     28 #include "nsAppDirectoryServiceDefs.h"
     29 
     30 #include "../2d/AutoHelpersWin.h"
     31 #include "gfxRect.h"
     32 #include "SharedFontList-impl.h"
     33 
     34 #include "harfbuzz/hb.h"
     35 
     36 #define StandardFonts
     37 #include "StandardFonts-win10.inc"
     38 #undef StandardFonts
     39 #include "StandardFonts-win10-bb.inc"
     40 
     41 using namespace mozilla;
     42 using namespace mozilla::gfx;
     43 using mozilla::intl::OSPreferences;
     44 
     45 #define LOG_FONTLIST(args) \
     46  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
     47 #define LOG_FONTLIST_ENABLED() \
     48  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
     49 
     50 #define LOG_FONTINIT(args) \
     51  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
     52 #define LOG_FONTINIT_ENABLED() \
     53  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
     54 
     55 #define LOG_CMAPDATA_ENABLED() \
     56  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
     57 
     58 static __inline void BuildKeyNameFromFontName(nsACString& aName) {
     59  ToLowerCase(aName);
     60 }
     61 
     62 ////////////////////////////////////////////////////////////////////////////////
     63 // gfxDWriteFontFamily
     64 
     65 gfxDWriteFontFamily::~gfxDWriteFontFamily() {}
     66 
     67 static bool GetNameAsUtf8(nsACString& aName, IDWriteLocalizedStrings* aStrings,
     68                          UINT32 aIndex) {
     69  AutoTArray<WCHAR, 32> name;
     70  UINT32 length;
     71  HRESULT hr = aStrings->GetStringLength(aIndex, &length);
     72  if (FAILED(hr)) {
     73    return false;
     74  }
     75  if (!name.SetLength(length + 1, fallible)) {
     76    return false;
     77  }
     78  hr = aStrings->GetString(aIndex, name.Elements(), length + 1);
     79  if (FAILED(hr)) {
     80    return false;
     81  }
     82  aName.Truncate();
     83  AppendUTF16toUTF8(
     84      Substring(reinterpret_cast<const char16_t*>(name.Elements()),
     85                name.Length() - 1),
     86      aName);
     87  return true;
     88 }
     89 
     90 static bool GetEnglishOrFirstName(nsACString& aName,
     91                                  IDWriteLocalizedStrings* aStrings) {
     92  UINT32 englishIdx = 0;
     93  BOOL exists;
     94  HRESULT hr = aStrings->FindLocaleName(L"en-us", &englishIdx, &exists);
     95  if (FAILED(hr) || !exists) {
     96    // Use 0 index if english is not found.
     97    englishIdx = 0;
     98  }
     99  return GetNameAsUtf8(aName, aStrings, englishIdx);
    100 }
    101 
    102 static HRESULT GetDirectWriteFontName(IDWriteFont* aFont,
    103                                      nsACString& aFontName) {
    104  HRESULT hr;
    105 
    106  RefPtr<IDWriteLocalizedStrings> names;
    107  hr = aFont->GetFaceNames(getter_AddRefs(names));
    108  if (FAILED(hr)) {
    109    return hr;
    110  }
    111 
    112  if (!GetEnglishOrFirstName(aFontName, names)) {
    113    return E_FAIL;
    114  }
    115 
    116  return S_OK;
    117 }
    118 
    119 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME
    120 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME
    121 
    122 // for use in reading postscript or fullname
    123 static HRESULT GetDirectWriteFaceName(IDWriteFont* aFont,
    124                                      DWRITE_INFORMATIONAL_STRING_ID aWhichName,
    125                                      nsACString& aFontName) {
    126  HRESULT hr;
    127 
    128  BOOL exists;
    129  RefPtr<IDWriteLocalizedStrings> infostrings;
    130  hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings),
    131                                      &exists);
    132  if (FAILED(hr) || !exists) {
    133    return E_FAIL;
    134  }
    135 
    136  if (!GetEnglishOrFirstName(aFontName, infostrings)) {
    137    return E_FAIL;
    138  }
    139 
    140  return S_OK;
    141 }
    142 
    143 void gfxDWriteFontFamily::FindStyleVariationsLocked(
    144    FontInfoData* aFontInfoData) {
    145  HRESULT hr;
    146  if (mHasStyles) {
    147    return;
    148  }
    149 
    150  mHasStyles = true;
    151 
    152  gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
    153 
    154  bool skipFaceNames =
    155      mFaceNamesInitialized || !fp->NeedFullnamePostscriptNames();
    156  bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
    157                                     fp->NeedFullnamePostscriptNames() &&
    158                                     aFontInfoData;
    159 
    160  for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
    161    RefPtr<IDWriteFont> font;
    162    hr = mDWFamily->GetFont(i, getter_AddRefs(font));
    163    if (FAILED(hr)) {
    164      // This should never happen.
    165      NS_WARNING("Failed to get existing font from family.");
    166      continue;
    167    }
    168 
    169    if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
    170      // We don't want these in the font list; we'll apply simulations
    171      // on the fly when appropriate.
    172      continue;
    173    }
    174 
    175    // name
    176    nsCString fullID(mName);
    177    nsAutoCString faceName;
    178    hr = GetDirectWriteFontName(font, faceName);
    179    if (FAILED(hr)) {
    180      continue;
    181    }
    182    fullID.Append(' ');
    183    fullID.Append(faceName);
    184 
    185    // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
    186    // non-italic style glyphs as Japanese characters.  However, using it
    187    // causes serious problem if web pages wants some elements to be
    188    // different style from others only with font-style.  For example,
    189    // <em> and <i> should be rendered as italic in the default style.
    190    if (fullID.EqualsLiteral("Meiryo Italic") ||
    191        fullID.EqualsLiteral("Meiryo Bold Italic")) {
    192      continue;
    193    }
    194 
    195    gfxDWriteFontEntry* fe =
    196        new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
    197    fe->SetForceGDIClassic(mForceGDIClassic);
    198 
    199    fe->SetupVariationRanges();
    200 
    201    AddFontEntryLocked(fe);
    202 
    203    // postscript/fullname if needed
    204    nsAutoCString psname, fullname;
    205    if (fontInfoShouldHaveFaceNames) {
    206      aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
    207      if (!fullname.IsEmpty()) {
    208        fp->AddFullname(fe, fullname);
    209      }
    210      if (!psname.IsEmpty()) {
    211        fp->AddPostscriptName(fe, psname);
    212      }
    213    } else if (!skipFaceNames) {
    214      hr = GetDirectWriteFaceName(font, PSNAME_ID, psname);
    215      if (FAILED(hr)) {
    216        skipFaceNames = true;
    217      } else if (psname.Length() > 0) {
    218        fp->AddPostscriptName(fe, psname);
    219      }
    220 
    221      hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname);
    222      if (FAILED(hr)) {
    223        skipFaceNames = true;
    224      } else if (fullname.Length() > 0) {
    225        fp->AddFullname(fe, fullname);
    226      }
    227    }
    228 
    229    if (LOG_FONTLIST_ENABLED()) {
    230      nsAutoCString weightString;
    231      fe->Weight().ToString(weightString);
    232      LOG_FONTLIST(
    233          ("(fontlist) added (%s) to family (%s)"
    234           " with style: %s weight: %s stretch: %d psname: %s fullname: %s",
    235           fe->Name().get(), Name().get(),
    236           (fe->IsItalic()) ? "italic"
    237                            : (fe->IsOblique() ? "oblique" : "normal"),
    238           weightString.get(), fe->Stretch().AsScalar(), psname.get(),
    239           fullname.get()));
    240    }
    241  }
    242 
    243  // assume that if no error, all postscript/fullnames were initialized
    244  if (!skipFaceNames) {
    245    mFaceNamesInitialized = true;
    246  }
    247 
    248  if (!mAvailableFonts.Length()) {
    249    NS_WARNING("Family with no font faces in it.");
    250  }
    251 
    252  if (mIsBadUnderlineFamily) {
    253    SetBadUnderlineFonts();
    254  }
    255 
    256  CheckForSimpleFamily();
    257  if (mIsSimpleFamily) {
    258    for (auto& f : mAvailableFonts) {
    259      if (f) {
    260        static_cast<gfxDWriteFontEntry*>(f.get())->mMayUseGDIAccess = true;
    261      }
    262    }
    263  }
    264 }
    265 
    266 void gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
    267                                        bool aNeedFullnamePostscriptNames,
    268                                        FontInfoData* aFontInfoData) {
    269  // if all needed names have already been read, skip
    270  if (mOtherFamilyNamesInitialized &&
    271      (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
    272    return;
    273  }
    274 
    275  // If we've been passed a FontInfoData, we skip the DWrite implementation
    276  // here and fall back to the generic code which will use that info.
    277  if (!aFontInfoData) {
    278    // DirectWrite version of this will try to read
    279    // postscript/fullnames via DirectWrite API
    280    FindStyleVariations();
    281  }
    282 
    283  // fallback to looking up via name table
    284  if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) {
    285    gfxFontFamily::ReadFaceNames(aPlatformFontList,
    286                                 aNeedFullnamePostscriptNames, aFontInfoData);
    287  }
    288 }
    289 
    290 void gfxDWriteFontFamily::LocalizedName(nsACString& aLocalizedName) {
    291  aLocalizedName = Name();  // just return canonical name in case of failure
    292 
    293  if (!mDWFamily) {
    294    return;
    295  }
    296 
    297  HRESULT hr;
    298  nsAutoCString locale;
    299  // We use system locale here because it's what user expects to see.
    300  // See bug 1349454 for details.
    301  RefPtr<OSPreferences> osprefs = OSPreferences::GetInstanceAddRefed();
    302  if (!osprefs) {
    303    return;
    304  }
    305  osprefs->GetSystemLocale(locale);
    306 
    307  RefPtr<IDWriteLocalizedStrings> names;
    308 
    309  hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
    310  if (FAILED(hr)) {
    311    return;
    312  }
    313  UINT32 idx = 0;
    314  BOOL exists;
    315  hr =
    316      names->FindLocaleName(NS_ConvertUTF8toUTF16(locale).get(), &idx, &exists);
    317  if (FAILED(hr)) {
    318    return;
    319  }
    320  if (!exists) {
    321    // Use english is localized is not found.
    322    hr = names->FindLocaleName(L"en-us", &idx, &exists);
    323    if (FAILED(hr)) {
    324      return;
    325    }
    326    if (!exists) {
    327      // Use 0 index if english is not found.
    328      idx = 0;
    329    }
    330  }
    331  AutoTArray<WCHAR, 32> famName;
    332  UINT32 length;
    333 
    334  hr = names->GetStringLength(idx, &length);
    335  if (FAILED(hr)) {
    336    return;
    337  }
    338 
    339  if (!famName.SetLength(length + 1, fallible)) {
    340    // Eeep - running out of memory. Unlikely to end well.
    341    return;
    342  }
    343 
    344  hr = names->GetString(idx, famName.Elements(), length + 1);
    345  if (FAILED(hr)) {
    346    return;
    347  }
    348 
    349  aLocalizedName = NS_ConvertUTF16toUTF8((const char16_t*)famName.Elements(),
    350                                         famName.Length() - 1);
    351 }
    352 
    353 bool gfxDWriteFontFamily::IsSymbolFontFamily() const {
    354  // Just check the first font in the family
    355  if (mDWFamily->GetFontCount() > 0) {
    356    RefPtr<IDWriteFont> font;
    357    if (SUCCEEDED(mDWFamily->GetFont(0, getter_AddRefs(font)))) {
    358      return font->IsSymbolFont();
    359    }
    360  }
    361  return false;
    362 }
    363 
    364 void gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    365                                                 FontListSizes* aSizes) const {
    366  gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    367  // TODO:
    368  // This doesn't currently account for |mDWFamily|
    369 }
    370 
    371 void gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    372                                                 FontListSizes* aSizes) const {
    373  aSizes->mFontListSize += aMallocSizeOf(this);
    374  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    375 }
    376 
    377 ////////////////////////////////////////////////////////////////////////////////
    378 // gfxDWriteFontEntry
    379 
    380 gfxFontEntry* gfxDWriteFontEntry::Clone() const {
    381  MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
    382  gfxDWriteFontEntry* fe = new gfxDWriteFontEntry(Name(), mFont);
    383  fe->mWeightRange = mWeightRange;
    384  fe->mStretchRange = mStretchRange;
    385  fe->mStyleRange = mStyleRange;
    386  return fe;
    387 }
    388 
    389 gfxDWriteFontEntry::~gfxDWriteFontEntry() {}
    390 
    391 static bool UsingArabicOrHebrewScriptSystemLocale() {
    392  LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID());
    393  switch (langid) {
    394    case LANG_ARABIC:
    395    case LANG_DARI:
    396    case LANG_PASHTO:
    397    case LANG_PERSIAN:
    398    case LANG_SINDHI:
    399    case LANG_UIGHUR:
    400    case LANG_URDU:
    401    case LANG_HEBREW:
    402      return true;
    403    default:
    404      return false;
    405  }
    406 }
    407 
    408 nsresult gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag,
    409                                           nsTArray<uint8_t>& aBuffer) {
    410  gfxDWriteFontList* pFontList = gfxDWriteFontList::PlatformFontList();
    411  const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag);
    412 
    413  // Don't use GDI table loading for symbol fonts or for
    414  // italic fonts in Arabic-script system locales because of
    415  // potential cmap discrepancies, see bug 629386.
    416  // Ditto for Hebrew, bug 837498.
    417  if (mFont && mMayUseGDIAccess && pFontList->UseGDIFontTableAccess() &&
    418      !(!IsUpright() && UsingArabicOrHebrewScriptSystemLocale()) &&
    419      !mFont->IsSymbolFont()) {
    420    LOGFONTW logfont = {0};
    421    if (InitLogFont(mFont, &logfont)) {
    422      AutoDC dc;
    423      AutoSelectFont font(dc.GetDC(), &logfont);
    424      if (font.IsValid()) {
    425        uint32_t tableSize = ::GetFontData(dc.GetDC(), tagBE, 0, nullptr, 0);
    426        if (tableSize != GDI_ERROR) {
    427          if (aBuffer.SetLength(tableSize, fallible)) {
    428            ::GetFontData(dc.GetDC(), tagBE, 0, aBuffer.Elements(),
    429                          aBuffer.Length());
    430            return NS_OK;
    431          }
    432          return NS_ERROR_OUT_OF_MEMORY;
    433        }
    434      }
    435    }
    436  }
    437 
    438  RefPtr<IDWriteFontFace> fontFace;
    439  nsresult rv = CreateFontFace(getter_AddRefs(fontFace));
    440  if (NS_FAILED(rv)) {
    441    return rv;
    442  }
    443 
    444  uint8_t* tableData;
    445  uint32_t len;
    446  void* tableContext = nullptr;
    447  BOOL exists;
    448  HRESULT hr = fontFace->TryGetFontTable(tagBE, (const void**)&tableData, &len,
    449                                         &tableContext, &exists);
    450  if (FAILED(hr) || !exists) {
    451    return NS_ERROR_FAILURE;
    452  }
    453 
    454  if (aBuffer.SetLength(len, fallible)) {
    455    memcpy(aBuffer.Elements(), tableData, len);
    456    rv = NS_OK;
    457  } else {
    458    rv = NS_ERROR_OUT_OF_MEMORY;
    459  }
    460 
    461  if (tableContext) {
    462    fontFace->ReleaseFontTable(&tableContext);
    463  }
    464 
    465  return rv;
    466 }
    467 
    468 // Access to font tables packaged in hb_blob_t form
    469 
    470 // object attached to the Harfbuzz blob, used to release
    471 // the table when the blob is destroyed
    472 class FontTableRec {
    473 public:
    474  FontTableRec(IDWriteFontFace* aFontFace, void* aContext)
    475      : mFontFace(aFontFace), mContext(aContext) {
    476    MOZ_COUNT_CTOR(FontTableRec);
    477  }
    478 
    479  ~FontTableRec() {
    480    MOZ_COUNT_DTOR(FontTableRec);
    481    mFontFace->ReleaseFontTable(mContext);
    482  }
    483 
    484 private:
    485  RefPtr<IDWriteFontFace> mFontFace;
    486  void* mContext;
    487 };
    488 
    489 static void DestroyBlobFunc(void* aUserData) {
    490  FontTableRec* ftr = static_cast<FontTableRec*>(aUserData);
    491  delete ftr;
    492 }
    493 
    494 hb_blob_t* gfxDWriteFontEntry::GetFontTable(uint32_t aTag) {
    495  // try to avoid potentially expensive DWrite call if we haven't actually
    496  // created the font face yet, by using the gfxFontEntry method that will
    497  // use CopyFontTable and then cache the data
    498  if (!mFontFace) {
    499    return gfxFontEntry::GetFontTable(aTag);
    500  }
    501 
    502  const void* data;
    503  UINT32 size;
    504  void* context;
    505  BOOL exists;
    506  HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag),
    507                                          &data, &size, &context, &exists);
    508  if (SUCCEEDED(hr) && exists) {
    509    FontTableRec* ftr = new FontTableRec(mFontFace, context);
    510    return hb_blob_create(static_cast<const char*>(data), size,
    511                          HB_MEMORY_MODE_READONLY, ftr, DestroyBlobFunc);
    512  }
    513 
    514  return nullptr;
    515 }
    516 
    517 nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
    518  AUTO_PROFILER_LABEL("gfxDWriteFontEntry::ReadCMAP", GRAPHICS);
    519 
    520  // attempt this once, if errors occur leave a blank cmap
    521  if (mCharacterMap || mShmemCharacterMap) {
    522    return NS_OK;
    523  }
    524 
    525  RefPtr<gfxCharacterMap> charmap;
    526  nsresult rv;
    527 
    528  uint32_t uvsOffset = 0;
    529  if (aFontInfoData &&
    530      (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
    531    rv = NS_OK;
    532  } else {
    533    uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
    534    charmap = new gfxCharacterMap(256);
    535    AutoTable cmapTable(this, kCMAP);
    536 
    537    if (cmapTable) {
    538      uint32_t cmapLen;
    539      const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
    540          hb_blob_get_data(cmapTable, &cmapLen));
    541      rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset);
    542    } else {
    543      rv = NS_ERROR_NOT_AVAILABLE;
    544    }
    545  }
    546  mUVSOffset.exchange(uvsOffset);
    547 
    548  bool setCharMap = true;
    549  if (NS_SUCCEEDED(rv)) {
    550    // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used
    551    // by sites to represent a "Play" icon, but the glyph in Segoe UI Light
    552    // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.)
    553    // Fallback to Segoe UI Symbol is preferred.
    554    if (FamilyName().EqualsLiteral("Segoe UI")) {
    555      charmap->clear(0x25b6);
    556      charmap->clear(0x25c0);
    557    }
    558    gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
    559    fontlist::FontList* sharedFontList = pfl->SharedFontList();
    560    if (!IsUserFont() && mShmemFace) {
    561      mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily);
    562      if (TrySetShmemCharacterMap()) {
    563        setCharMap = false;
    564      }
    565    } else {
    566      charmap = pfl->FindCharMap(charmap);
    567    }
    568    mHasCmapTable = true;
    569  } else {
    570    // if error occurred, initialize to null cmap
    571    charmap = new gfxCharacterMap(0);
    572    mHasCmapTable = false;
    573  }
    574  if (setCharMap) {
    575    // Temporarily retain charmap, until the shared version is
    576    // ready for use.
    577    if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
    578      charmap.get()->AddRef();
    579    }
    580  }
    581 
    582  LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
    583                mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
    584                charmap->mHash, mCharacterMap == charmap ? " new" : ""));
    585  if (LOG_CMAPDATA_ENABLED()) {
    586    char prefix[256];
    587    SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
    588    charmap->Dump(prefix, eGfxLog_cmapdata);
    589  }
    590 
    591  return rv;
    592 }
    593 
    594 bool gfxDWriteFontEntry::HasVariations() {
    595  if (mHasVariationsInitialized) {
    596    return mHasVariations;
    597  }
    598  mHasVariationsInitialized = true;
    599  mHasVariations = false;
    600 
    601  if (!gfxPlatform::HasVariationFontSupport()) {
    602    return mHasVariations;
    603  }
    604 
    605  if (!mFontFace) {
    606    // CreateFontFace will initialize the mFontFace field, and also
    607    // mFontFace5 if available on the current DWrite version.
    608    RefPtr<IDWriteFontFace> fontFace;
    609    if (NS_FAILED(CreateFontFace(getter_AddRefs(fontFace)))) {
    610      return mHasVariations;
    611    }
    612  }
    613  if (mFontFace5) {
    614    mHasVariations = mFontFace5->HasVariations();
    615  }
    616  return mHasVariations;
    617 }
    618 
    619 void gfxDWriteFontEntry::GetVariationAxes(
    620    nsTArray<gfxFontVariationAxis>& aAxes) {
    621  if (!HasVariations()) {
    622    return;
    623  }
    624  // HasVariations() will have ensured the mFontFace5 interface is available;
    625  // so we can get an IDWriteFontResource and ask it for the axis info.
    626  RefPtr<IDWriteFontResource> resource;
    627  HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
    628  if (FAILED(hr) || !resource) {
    629    return;
    630  }
    631 
    632  uint32_t count = resource->GetFontAxisCount();
    633  AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> defaultValues;
    634  AutoTArray<DWRITE_FONT_AXIS_RANGE, 4> ranges;
    635  defaultValues.SetLength(count);
    636  ranges.SetLength(count);
    637  resource->GetDefaultFontAxisValues(defaultValues.Elements(), count);
    638  resource->GetFontAxisRanges(ranges.Elements(), count);
    639  for (uint32_t i = 0; i < count; ++i) {
    640    gfxFontVariationAxis axis;
    641    MOZ_ASSERT(ranges[i].axisTag == defaultValues[i].axisTag);
    642    DWRITE_FONT_AXIS_ATTRIBUTES attrs = resource->GetFontAxisAttributes(i);
    643    if (attrs & DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN) {
    644      continue;
    645    }
    646    if (!(attrs & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE)) {
    647      continue;
    648    }
    649    // Extract the 4 chars of the tag from DWrite's packed version,
    650    // and reassemble them in the order we use for TRUETYPE_TAG.
    651    uint32_t t = defaultValues[i].axisTag;
    652    axis.mTag = TRUETYPE_TAG(t & 0xff, (t >> 8) & 0xff, (t >> 16) & 0xff,
    653                             (t >> 24) & 0xff);
    654    // Try to get a human-friendly name (may not be present)
    655    RefPtr<IDWriteLocalizedStrings> names;
    656    resource->GetAxisNames(i, getter_AddRefs(names));
    657    if (names) {
    658      GetEnglishOrFirstName(axis.mName, names);
    659    }
    660    axis.mMinValue = ranges[i].minValue;
    661    axis.mMaxValue = ranges[i].maxValue;
    662    axis.mDefaultValue = defaultValues[i].value;
    663    aAxes.AppendElement(axis);
    664  }
    665 }
    666 
    667 void gfxDWriteFontEntry::GetVariationInstances(
    668    nsTArray<gfxFontVariationInstance>& aInstances) {
    669  gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
    670 }
    671 
    672 gfxFont* gfxDWriteFontEntry::CreateFontInstance(
    673    const gfxFontStyle* aFontStyle) {
    674  // We use the DirectWrite bold simulation for installed fonts, but NOT for
    675  // webfonts; those will use multi-strike synthetic bold instead.
    676  bool useBoldSim = false;
    677  if (aFontStyle->NeedsSyntheticBold(this)) {
    678    switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) {
    679      case 0:  // never use the DWrite simulation
    680        break;
    681      case 1:  // use DWrite simulation for installed fonts except COLR fonts,
    682               // but not webfonts
    683        useBoldSim =
    684            !mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
    685        break;
    686      default:  // always use DWrite bold simulation, except for COLR fonts
    687        useBoldSim = !HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
    688        break;
    689    }
    690  }
    691  DWRITE_FONT_SIMULATIONS sims =
    692      useBoldSim ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
    693  ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
    694      useBoldSim ? mUnscaledFontBold : mUnscaledFont;
    695  RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
    696  if (!unscaledFont) {
    697    RefPtr<IDWriteFontFace> fontFace;
    698    nsresult rv =
    699        CreateFontFace(getter_AddRefs(fontFace), nullptr, sims, nullptr);
    700    if (NS_FAILED(rv)) {
    701      return nullptr;
    702    }
    703    // Only pass in the underlying IDWriteFont if the unscaled font doesn't
    704    // reflect a data font. This signals whether or not we can safely query
    705    // a descriptor to represent the font for various transport use-cases.
    706    unscaledFont =
    707        new UnscaledFontDWrite(fontFace, !mIsDataUserFont ? mFont : nullptr);
    708    unscaledFontPtr = unscaledFont;
    709  }
    710  RefPtr<IDWriteFontFace> fontFace;
    711  if (HasVariations()) {
    712    // Get the variation settings needed to instantiate the fontEntry
    713    // for a particular fontStyle.
    714    AutoTArray<gfxFontVariation, 4> vars;
    715    GetVariationsForStyle(vars, *aFontStyle);
    716 
    717    if (!vars.IsEmpty()) {
    718      nsresult rv =
    719          CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims, &vars);
    720      if (NS_FAILED(rv)) {
    721        return nullptr;
    722      }
    723    }
    724  }
    725  return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace);
    726 }
    727 
    728 nsresult gfxDWriteFontEntry::CreateFontFace(
    729    IDWriteFontFace** aFontFace, const gfxFontStyle* aFontStyle,
    730    DWRITE_FONT_SIMULATIONS aSimulations,
    731    const nsTArray<gfxFontVariation>* aVariations) {
    732  // Convert an OpenType font tag from our uint32_t representation
    733  // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants.
    734  auto makeDWriteAxisTag = [](uint32_t aTag) {
    735    return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff, (aTag >> 16) & 0xff,
    736                                     (aTag >> 8) & 0xff, aTag & 0xff);
    737  };
    738 
    739  MOZ_SEH_TRY {
    740    // initialize mFontFace if this hasn't been done before
    741    if (!mFontFace) {
    742      HRESULT hr;
    743      if (mFont) {
    744        hr = mFont->CreateFontFace(getter_AddRefs(mFontFace));
    745      } else if (mFontFile) {
    746        IDWriteFontFile* fontFile = mFontFile.get();
    747        hr = Factory::GetDWriteFactory()->CreateFontFace(
    748            mFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE,
    749            getter_AddRefs(mFontFace));
    750      } else {
    751        MOZ_ASSERT_UNREACHABLE("invalid font entry");
    752        return NS_ERROR_FAILURE;
    753      }
    754      if (FAILED(hr)) {
    755        return NS_ERROR_FAILURE;
    756      }
    757      // Also get the IDWriteFontFace5 interface if we're running on a
    758      // sufficiently new DWrite version where it is available.
    759      if (mFontFace) {
    760        mFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
    761                                  (void**)getter_AddRefs(mFontFace5));
    762        if (!mVariationSettings.IsEmpty()) {
    763          // If the font entry has variations specified, mFontFace5 will
    764          // be a distinct face that has the variations applied.
    765          RefPtr<IDWriteFontResource> resource;
    766          HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
    767          if (SUCCEEDED(hr) && resource) {
    768            AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
    769            for (const auto& v : mVariationSettings) {
    770              DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
    771                                                  v.mValue};
    772              fontAxisValues.AppendElement(axisValue);
    773            }
    774            resource->CreateFontFace(
    775                mFontFace->GetSimulations(), fontAxisValues.Elements(),
    776                fontAxisValues.Length(), getter_AddRefs(mFontFace5));
    777          }
    778        }
    779      }
    780    }
    781 
    782    // Do we need to modify DWrite simulations from what mFontFace has?
    783    bool needSimulations =
    784        (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
    785        !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
    786 
    787    // If the IDWriteFontFace5 interface is available, we can try using
    788    // IDWriteFontResource to create a new modified face.
    789    if (mFontFace5 && (HasVariations() || needSimulations)) {
    790      RefPtr<IDWriteFontResource> resource;
    791      HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
    792      if (SUCCEEDED(hr) && resource) {
    793        AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
    794 
    795        // Copy variation settings to DWrite's type.
    796        if (aVariations) {
    797          for (const auto& v : *aVariations) {
    798            DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
    799                                                v.mValue};
    800            fontAxisValues.AppendElement(axisValue);
    801          }
    802        }
    803 
    804        IDWriteFontFace5* ff5;
    805        resource->CreateFontFace(aSimulations, fontAxisValues.Elements(),
    806                                 fontAxisValues.Length(), &ff5);
    807        if (ff5) {
    808          *aFontFace = ff5;
    809          return NS_OK;
    810        }
    811      }
    812    }
    813 
    814    // Do we need to add DWrite simulations to the face?
    815    if (needSimulations) {
    816      // if so, we need to return not mFontFace itself but a version that
    817      // has the Bold simulation - unfortunately, old DWrite doesn't provide
    818      // a simple API for this
    819      UINT32 numberOfFiles = 0;
    820      if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) {
    821        return NS_ERROR_FAILURE;
    822      }
    823      AutoTArray<IDWriteFontFile*, 1> files;
    824      files.AppendElements(numberOfFiles);
    825      if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
    826        return NS_ERROR_FAILURE;
    827      }
    828      HRESULT hr = Factory::GetDWriteFactory()->CreateFontFace(
    829          mFontFace->GetType(), numberOfFiles, files.Elements(),
    830          mFontFace->GetIndex(), aSimulations, aFontFace);
    831      for (UINT32 i = 0; i < numberOfFiles; ++i) {
    832        files[i]->Release();
    833      }
    834      return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
    835    }
    836  }
    837  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
    838    gfxCriticalNote << "Exception occurred creating font face for "
    839                    << mName.get();
    840  }
    841 
    842  // no simulation: we can just add a reference to mFontFace5 (if present)
    843  // or mFontFace (otherwise) and return that
    844  if (mFontFace5) {
    845    *aFontFace = mFontFace5;
    846  } else {
    847    *aFontFace = mFontFace;
    848  }
    849  (*aFontFace)->AddRef();
    850  return NS_OK;
    851 }
    852 
    853 bool gfxDWriteFontEntry::InitLogFont(IDWriteFont* aFont, LOGFONTW* aLogFont) {
    854  HRESULT hr;
    855 
    856  BOOL isInSystemCollection;
    857  IDWriteGdiInterop* gdi =
    858      gfxDWriteFontList::PlatformFontList()->GetGDIInterop();
    859  hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection);
    860  // If the font is not in the system collection, GDI will be unable to
    861  // select it and load its tables, so we return false here to indicate
    862  // failure, and let CopyFontTable fall back to DWrite native methods.
    863  return (SUCCEEDED(hr) && isInSystemCollection);
    864 }
    865 
    866 bool gfxDWriteFontEntry::IsCJKFont() {
    867  if (mIsCJK != UNINITIALIZED_VALUE) {
    868    return mIsCJK;
    869  }
    870 
    871  mIsCJK = false;
    872 
    873  const uint32_t kOS2Tag = TRUETYPE_TAG('O', 'S', '/', '2');
    874  gfxFontUtils::AutoHBBlob blob(GetFontTable(kOS2Tag));
    875  if (!blob) {
    876    return mIsCJK;
    877  }
    878 
    879  uint32_t len;
    880  const OS2Table* os2 =
    881      reinterpret_cast<const OS2Table*>(hb_blob_get_data(blob, &len));
    882  // ulCodePageRange bit definitions for the CJK codepages,
    883  // from http://www.microsoft.com/typography/otspec/os2.htm#cpr
    884  const uint32_t CJK_CODEPAGE_BITS =
    885      (1 << 17) |  // codepage 932 - JIS/Japan
    886      (1 << 18) |  // codepage 936 - Chinese (simplified)
    887      (1 << 19) |  // codepage 949 - Korean Wansung
    888      (1 << 20) |  // codepage 950 - Chinese (traditional)
    889      (1 << 21);   // codepage 1361 - Korean Johab
    890  if (len >= offsetof(OS2Table, sxHeight)) {
    891    if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) {
    892      mIsCJK = true;
    893    }
    894  }
    895 
    896  return mIsCJK;
    897 }
    898 
    899 void gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
    900                                                FontListSizes* aSizes) const {
    901  gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    902  // TODO:
    903  // This doesn't currently account for the |mFont| and |mFontFile| members
    904 }
    905 
    906 void gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
    907                                                FontListSizes* aSizes) const {
    908  aSizes->mFontListSize += aMallocSizeOf(this);
    909  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
    910 }
    911 
    912 size_t gfxDWriteFontEntry::ComputedSizeOfExcludingThis(
    913    mozilla::MallocSizeOf aMallocSizeOf) {
    914  size_t result = gfxFontEntry::ComputedSizeOfExcludingThis(aMallocSizeOf);
    915  if (mFontFileStream) {
    916    result += mFontFileStream->SizeOfExcludingThis(aMallocSizeOf);
    917  }
    918  return result;
    919 }
    920 
    921 ////////////////////////////////////////////////////////////////////////////////
    922 // gfxDWriteFontList
    923 
    924 gfxDWriteFontList::gfxDWriteFontList() : mForceGDIClassicMaxFontSize(0.0) {
    925  CheckFamilyList(kBaseFonts);
    926 #ifndef BASE_BROWSER_VERSION
    927  CheckFamilyList(kLangPackFonts);
    928 #endif
    929 }
    930 
    931 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
    932 //   I/O strain during cold startup due to dwrite caching bugs.  Default to
    933 //   Arial to avoid this.
    934 
    935 FontFamily gfxDWriteFontList::GetDefaultFontForPlatform(
    936    FontVisibilityProvider* aFontVisibilityProvider, const gfxFontStyle* aStyle,
    937    nsAtom* aLanguage) {
    938  // try Arial first
    939  FontFamily ff;
    940  ff = FindFamily(aFontVisibilityProvider, "Arial"_ns);
    941  if (!ff.IsNull()) {
    942    return ff;
    943  }
    944 
    945  // otherwise, use local default
    946  gfxFontStyle fontStyle;
    947  nsAutoString systemFontName;
    948  if (!mozilla::LookAndFeel::GetFont(mozilla::StyleSystemFont::MessageBox,
    949                                     systemFontName, fontStyle)) {
    950    return ff;
    951  }
    952 
    953  ff = FindFamily(aFontVisibilityProvider,
    954                  NS_ConvertUTF16toUTF8(systemFontName));
    955  return ff;
    956 }
    957 
    958 gfxFontEntry* gfxDWriteFontList::LookupLocalFont(
    959    FontVisibilityProvider* aFontVisibilityProvider,
    960    const nsACString& aFontName, WeightRange aWeightForEntry,
    961    StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
    962  AutoLock lock(mLock);
    963 
    964  if (SharedFontList()) {
    965    return LookupInSharedFaceNameList(aFontVisibilityProvider, aFontName,
    966                                      aWeightForEntry, aStretchForEntry,
    967                                      aStyleForEntry);
    968  }
    969 
    970  gfxFontEntry* lookup;
    971 
    972  lookup = LookupInFaceNameLists(aFontName);
    973  if (!lookup) {
    974    return nullptr;
    975  }
    976 
    977  gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
    978  gfxDWriteFontEntry* fe =
    979      new gfxDWriteFontEntry(lookup->Name(), dwriteLookup->mFont,
    980                             aWeightForEntry, aStretchForEntry, aStyleForEntry);
    981  fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
    982  return fe;
    983 }
    984 
    985 gfxFontEntry* gfxDWriteFontList::MakePlatformFont(
    986    const nsACString& aFontName, WeightRange aWeightForEntry,
    987    StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
    988    const uint8_t* aFontData, uint32_t aLength) {
    989  RefPtr<gfxDWriteFontFileStream> fontFileStream;
    990  RefPtr<IDWriteFontFile> fontFile;
    991  HRESULT hr = gfxDWriteFontFileLoader::CreateCustomFontFile(
    992      aFontData, aLength, getter_AddRefs(fontFile),
    993      getter_AddRefs(fontFileStream));
    994  free((void*)aFontData);
    995  NS_ASSERTION(SUCCEEDED(hr), "Failed to create font file reference");
    996  if (FAILED(hr)) {
    997    return nullptr;
    998  }
    999 
   1000  nsAutoString uniqueName;
   1001  nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
   1002  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to make unique user font name");
   1003  if (NS_FAILED(rv)) {
   1004    return nullptr;
   1005  }
   1006 
   1007  BOOL isSupported;
   1008  DWRITE_FONT_FILE_TYPE fileType;
   1009  UINT32 numFaces;
   1010 
   1011  auto entry = MakeUnique<gfxDWriteFontEntry>(
   1012      NS_ConvertUTF16toUTF8(uniqueName), fontFile, fontFileStream,
   1013      aWeightForEntry, aStretchForEntry, aStyleForEntry);
   1014 
   1015  hr = fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
   1016  NS_ASSERTION(SUCCEEDED(hr), "IDWriteFontFile::Analyze failed");
   1017  if (FAILED(hr)) {
   1018    return nullptr;
   1019  }
   1020  NS_ASSERTION(isSupported, "Unsupported font file");
   1021  if (!isSupported) {
   1022    return nullptr;
   1023  }
   1024  NS_ASSERTION(numFaces == 1, "Font file does not contain exactly 1 face");
   1025  if (numFaces != 1) {
   1026    // We don't know how to deal with 0 faces either.
   1027    return nullptr;
   1028  }
   1029 
   1030  return entry.release();
   1031 }
   1032 
   1033 bool gfxDWriteFontList::UseGDIFontTableAccess() const {
   1034  // Using GDI font table access for DWrite is controlled by a pref, but also we
   1035  // must be able to make win32k calls.
   1036  return mGDIFontTableAccess && !IsWin32kLockedDown();
   1037 }
   1038 
   1039 static void GetPostScriptNameFromNameTable(IDWriteFontFace* aFace,
   1040                                           nsCString& aName) {
   1041  const auto kNAME =
   1042      NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
   1043  const char* data;
   1044  UINT32 size;
   1045  void* context;
   1046  BOOL exists;
   1047  if (SUCCEEDED(aFace->TryGetFontTable(kNAME, (const void**)&data, &size,
   1048                                       &context, &exists)) &&
   1049      exists) {
   1050    if (NS_FAILED(gfxFontUtils::ReadCanonicalName(
   1051            data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aName))) {
   1052      aName.Truncate(0);
   1053    }
   1054    aFace->ReleaseFontTable(context);
   1055  }
   1056 }
   1057 
   1058 gfxFontEntry* gfxDWriteFontList::CreateFontEntry(
   1059    fontlist::Face* aFace, const fontlist::Family* aFamily) {
   1060  IDWriteFontCollection* collection =
   1061 #ifdef MOZ_BUNDLED_FONTS
   1062      aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
   1063 #else
   1064      mSystemFonts;
   1065 #endif
   1066  RefPtr<IDWriteFontFamily> family;
   1067  bool foundExpectedFamily = false;
   1068  const nsCString& familyName =
   1069      aFamily->DisplayName().AsString(SharedFontList());
   1070 
   1071  // The DirectWrite calls here might throw exceptions, e.g. in case of disk
   1072  // errors when trying to read the font file.
   1073  MOZ_SEH_TRY {
   1074    if (aFamily->Index() < collection->GetFontFamilyCount()) {
   1075      HRESULT hr =
   1076          collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
   1077      // Check that the family name is what we expected; if not, fall back to
   1078      // search by name. It's sad we have to do this, but it is possible for
   1079      // Windows to have given different versions of the system font collection
   1080      // to the parent and child processes.
   1081      if (SUCCEEDED(hr) && family) {
   1082        RefPtr<IDWriteLocalizedStrings> names;
   1083        hr = family->GetFamilyNames(getter_AddRefs(names));
   1084        if (SUCCEEDED(hr) && names) {
   1085          nsAutoCString name;
   1086          if (GetEnglishOrFirstName(name, names)) {
   1087            foundExpectedFamily = name.Equals(familyName);
   1088          }
   1089        }
   1090      }
   1091    }
   1092    if (!foundExpectedFamily) {
   1093      // Try to get family by name instead of index (to deal with the case of
   1094      // collection mismatch).
   1095      UINT32 index;
   1096      BOOL exists;
   1097      NS_ConvertUTF8toUTF16 name16(familyName);
   1098      HRESULT hr = collection->FindFamilyName(
   1099          reinterpret_cast<const WCHAR*>(name16.BeginReading()), &index,
   1100          &exists);
   1101      if (FAILED(hr) || !exists || index == UINT_MAX ||
   1102          FAILED(collection->GetFontFamily(index, getter_AddRefs(family))) ||
   1103          !family) {
   1104        return nullptr;
   1105      }
   1106    }
   1107 
   1108    // Retrieve the required face by index within the family.
   1109    RefPtr<IDWriteFont> font;
   1110    if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(font))) || !font) {
   1111      return nullptr;
   1112    }
   1113 
   1114    // Retrieve the psName from the font, so we can check we've found the
   1115    // expected face.
   1116    nsAutoCString psName;
   1117    if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
   1118      RefPtr<IDWriteFontFace> dwFontFace;
   1119      if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
   1120        GetPostScriptNameFromNameTable(dwFontFace, psName);
   1121      }
   1122    }
   1123 
   1124    // If it doesn't match, DirectWrite must have shuffled the order of faces
   1125    // returned for the family; search by name as a fallback.
   1126    nsCString faceName = aFace->mDescriptor.AsString(SharedFontList());
   1127    if (psName != faceName) {
   1128      gfxWarning() << "Face name mismatch for index " << aFace->mIndex
   1129                   << " in family " << familyName.get() << ": expected "
   1130                   << faceName.get() << ", found " << psName.get();
   1131      for (uint32_t i = 0; i < family->GetFontCount(); ++i) {
   1132        if (i == aFace->mIndex) {
   1133          continue;  // this was the face we already tried
   1134        }
   1135        if (FAILED(family->GetFont(i, getter_AddRefs(font))) || !font) {
   1136          return nullptr;  // this font family is broken!
   1137        }
   1138        if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
   1139          RefPtr<IDWriteFontFace> dwFontFace;
   1140          if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
   1141            GetPostScriptNameFromNameTable(dwFontFace, psName);
   1142          }
   1143        }
   1144        if (psName == faceName) {
   1145          break;
   1146        }
   1147      }
   1148    }
   1149    if (psName != faceName) {
   1150      return nullptr;
   1151    }
   1152 
   1153    auto fe = new gfxDWriteFontEntry(faceName, font, !aFamily->IsBundled());
   1154    fe->InitializeFrom(aFace, aFamily);
   1155    fe->mForceGDIClassic = aFamily->IsForceClassic();
   1156    fe->mMayUseGDIAccess = aFamily->IsSimple();
   1157    return fe;
   1158  }
   1159  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   1160    gfxCriticalNote << "Exception occurred while creating font entry for "
   1161                    << familyName.get();
   1162  }
   1163  return nullptr;
   1164 }
   1165 
   1166 FontVisibility gfxDWriteFontList::GetVisibilityForFamily(
   1167    const nsACString& aName) const {
   1168  if (FamilyInList(aName, kBaseFonts)) {
   1169    return FontVisibility::Base;
   1170  }
   1171 #ifndef BASE_BROWSER_VERSION
   1172  if (FamilyInList(aName, kLangPackFonts)) {
   1173    return FontVisibility::LangPack;
   1174  }
   1175  if (nsRFPService::FontIsAllowedByLocale(aName)) {
   1176    return FontVisibility::LangPack;
   1177  }
   1178 #endif
   1179  return FontVisibility::User;
   1180 }
   1181 
   1182 nsTArray<std::pair<const char**, uint32_t>>
   1183 gfxDWriteFontList::GetFilteredPlatformFontLists() {
   1184  nsTArray<std::pair<const char**, uint32_t>> fontLists;
   1185 
   1186  fontLists.AppendElement(std::make_pair(kBaseFonts, std::size(kBaseFonts)));
   1187 #ifndef BASE_BROWSER_VERSION
   1188  fontLists.AppendElement(
   1189      std::make_pair(kLangPackFonts, std::size(kLangPackFonts)));
   1190 #endif
   1191 
   1192  return fontLists;
   1193 }
   1194 
   1195 void gfxDWriteFontList::AppendFamiliesFromCollection(
   1196    IDWriteFontCollection* aCollection,
   1197    nsTArray<fontlist::Family::InitData>& aFamilies,
   1198    const nsTArray<nsCString>* aForceClassicFams) {
   1199  auto allFacesUltraBold = [](IDWriteFontFamily* aFamily) -> bool {
   1200    for (UINT32 i = 0; i < aFamily->GetFontCount(); i++) {
   1201      RefPtr<IDWriteFont> font;
   1202      HRESULT hr = aFamily->GetFont(i, getter_AddRefs(font));
   1203      if (FAILED(hr)) {
   1204        NS_WARNING("Failed to get existing font from family.");
   1205        continue;
   1206      }
   1207      nsAutoCString faceName;
   1208      hr = GetDirectWriteFontName(font, faceName);
   1209      if (FAILED(hr)) {
   1210        continue;
   1211      }
   1212      if (faceName.Find("Ultra Bold"_ns) == kNotFound) {
   1213        return false;
   1214      }
   1215    }
   1216    return true;
   1217  };
   1218 
   1219  nsAutoCString locale;
   1220  OSPreferences::GetInstance()->GetSystemLocale(locale);
   1221  ToLowerCase(locale);
   1222  NS_ConvertUTF8toUTF16 loc16(locale);
   1223 
   1224  for (unsigned i = 0; i < aCollection->GetFontFamilyCount(); ++i) {
   1225    RefPtr<IDWriteFontFamily> family;
   1226    aCollection->GetFontFamily(i, getter_AddRefs(family));
   1227    RefPtr<IDWriteLocalizedStrings> localizedNames;
   1228    HRESULT hr = family->GetFamilyNames(getter_AddRefs(localizedNames));
   1229    if (FAILED(hr)) {
   1230      gfxWarning() << "Failed to get names for font-family " << i;
   1231      continue;
   1232    }
   1233 
   1234    auto addFamily = [&](const nsACString& name, FontVisibility visibility,
   1235                         bool altLocale = false) {
   1236      nsAutoCString key;
   1237      key = name;
   1238      BuildKeyNameFromFontName(key);
   1239      bool bad = mBadUnderlineFamilyNames.ContainsSorted(key);
   1240      bool classic =
   1241          aForceClassicFams && aForceClassicFams->ContainsSorted(key);
   1242      aFamilies.AppendElement(fontlist::Family::InitData(
   1243          key, name, i, visibility, aCollection != mSystemFonts, bad, classic,
   1244          altLocale));
   1245    };
   1246 
   1247    auto visibilityForName = [&](const nsACString& aName) -> FontVisibility {
   1248      // Special case: hide the "Gill Sans" family that contains only UltraBold
   1249      // faces, as this leads to breakage on sites with CSS that targeted the
   1250      // Gill Sans family as found on macOS. (Bug 551313, bug 1632738)
   1251      // TODO (jfkthame): the ultrabold faces from Gill Sans should be treated
   1252      // as belonging to the Gill Sans MT family.
   1253      if (aName.EqualsLiteral("Gill Sans") && allFacesUltraBold(family)) {
   1254        return FontVisibility::Hidden;
   1255      }
   1256      // Bundled fonts are always available, so only system fonts are checked
   1257      // against the standard font names list.
   1258      return aCollection == mSystemFonts ? GetVisibilityForFamily(aName)
   1259                                         : FontVisibility::Base;
   1260    };
   1261 
   1262    unsigned count = localizedNames->GetCount();
   1263    if (count == 1) {
   1264      // This is the common case: the great majority of fonts only provide an
   1265      // en-US family name.
   1266      nsAutoCString name;
   1267      if (!GetNameAsUtf8(name, localizedNames, 0)) {
   1268        gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i;
   1269        continue;
   1270      }
   1271      addFamily(name, visibilityForName(name));
   1272    } else {
   1273      AutoTArray<nsCString, 4> names;
   1274      int sysLocIndex = -1;
   1275      FontVisibility visibility = FontVisibility::User;
   1276      for (unsigned index = 0; index < count; ++index) {
   1277        nsAutoCString name;
   1278        if (!GetNameAsUtf8(name, localizedNames, index)) {
   1279          gfxWarning() << "GetNameAsUtf8 failed for index " << index
   1280                       << " in font-family " << i;
   1281          continue;
   1282        }
   1283        if (names.Contains(name)) {
   1284          continue;
   1285        }
   1286        if (sysLocIndex == -1) {
   1287          WCHAR buf[32];
   1288          if (SUCCEEDED(localizedNames->GetLocaleName(index, buf, 32))) {
   1289            if (loc16.Equals(buf)) {
   1290              sysLocIndex = names.Length();
   1291            }
   1292          }
   1293        }
   1294        names.AppendElement(name);
   1295        // We give the family the least-restrictive visibility of all its
   1296        // localized names, so that the used visibility will not depend on
   1297        // locale; with the exception that if any name is explicitly Hidden,
   1298        // this hides the family as a whole.
   1299        if (visibility != FontVisibility::Hidden) {
   1300          FontVisibility v = visibilityForName(name);
   1301          if (v == FontVisibility::Hidden) {
   1302            visibility = FontVisibility::Hidden;
   1303          } else {
   1304            visibility = std::min(visibility, v);
   1305          }
   1306        }
   1307      }
   1308      // If we didn't find a name that matched the system locale, use the
   1309      // first (which is most often en-US).
   1310      if (sysLocIndex == -1) {
   1311        sysLocIndex = 0;
   1312      }
   1313      // Hack to work around EPSON fonts with bad names (tagged as MacRoman
   1314      // but actually contain MacJapanese data): if we've chosen the first
   1315      // name, *and* it is non-ASCII, *and* there is an alternative present,
   1316      // use the next option instead as being more likely to be valid.
   1317      if (sysLocIndex == 0 && names.Length() > 1 && !IsAscii(names[0])) {
   1318        sysLocIndex = 1;
   1319      }
   1320      for (unsigned index = 0; index < names.Length(); ++index) {
   1321        addFamily(names[index], visibility,
   1322                  index != static_cast<unsigned>(sysLocIndex));
   1323      }
   1324    }
   1325  }
   1326 }
   1327 
   1328 void gfxDWriteFontList::GetFacesInitDataForFamily(
   1329    const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces,
   1330    bool aLoadCmaps) const {
   1331  IDWriteFontCollection* collection =
   1332 #ifdef MOZ_BUNDLED_FONTS
   1333      aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
   1334 #else
   1335      mSystemFonts;
   1336 #endif
   1337  if (!collection) {
   1338    return;
   1339  }
   1340  RefPtr<IDWriteFontFamily> family;
   1341  collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
   1342  for (unsigned i = 0; i < family->GetFontCount(); ++i) {
   1343    RefPtr<IDWriteFont> dwFont;
   1344    family->GetFont(i, getter_AddRefs(dwFont));
   1345    if (!dwFont || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
   1346      continue;
   1347    }
   1348    DWRITE_FONT_STYLE dwstyle = dwFont->GetStyle();
   1349    // Ignore italic styles of Meiryo because "Meiryo (Bold) Italic" has
   1350    // non-italic style glyphs as Japanese characters.  However, using it
   1351    // causes serious problem if web pages wants some elements to be
   1352    // different style from others only with font-style.  For example,
   1353    // <em> and <i> should be rendered as italic in the default style.
   1354    if (dwstyle != DWRITE_FONT_STYLE_NORMAL &&
   1355        aFamily->Key().AsString(SharedFontList()).EqualsLiteral("meiryo")) {
   1356      continue;
   1357    }
   1358    WeightRange weight(FontWeight::FromInt(dwFont->GetWeight()));
   1359    StretchRange stretch(FontStretchFromDWriteStretch(dwFont->GetStretch()));
   1360    // Try to read PSName as a unique face identifier; if this fails we'll get
   1361    // it directly from the 'name' table, and if that also fails we consider
   1362    // the face unusable.
   1363    MOZ_SEH_TRY {
   1364      nsAutoCString name;
   1365      RefPtr<gfxCharacterMap> charmap;
   1366      if (FAILED(GetDirectWriteFaceName(dwFont, PSNAME_ID, name)) ||
   1367          aLoadCmaps) {
   1368        RefPtr<IDWriteFontFace> dwFontFace;
   1369        if (SUCCEEDED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
   1370          if (name.IsEmpty()) {
   1371            GetPostScriptNameFromNameTable(dwFontFace, name);
   1372          }
   1373          const auto kCMAP =
   1374              NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
   1375          const char* data;
   1376          UINT32 size;
   1377          void* context;
   1378          BOOL exists;
   1379          if (aLoadCmaps) {
   1380            if (SUCCEEDED(dwFontFace->TryGetFontTable(
   1381                    kCMAP, (const void**)&data, &size, &context, &exists)) &&
   1382                exists) {
   1383              charmap = new gfxCharacterMap(256);
   1384              uint32_t offset;
   1385              gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap,
   1386                                     offset);
   1387              dwFontFace->ReleaseFontTable(context);
   1388            }
   1389          }
   1390        }
   1391      }
   1392      if (name.IsEmpty()) {
   1393        gfxWarning() << "Failed to get name for face " << i << " in family "
   1394                     << aFamily->Key().AsString(SharedFontList()).get();
   1395        continue;
   1396      }
   1397      SlantStyleRange slant(
   1398          dwstyle == DWRITE_FONT_STYLE_NORMAL   ? FontSlantStyle::NORMAL
   1399          : dwstyle == DWRITE_FONT_STYLE_ITALIC ? FontSlantStyle::ITALIC
   1400                                                : FontSlantStyle::OBLIQUE);
   1401      aFaces.AppendElement(fontlist::Face::InitData{
   1402          name, uint16_t(i), false, weight, stretch, slant, charmap});
   1403    }
   1404    MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   1405      // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
   1406      // the font resource. We'll just skip the bad face.
   1407      gfxCriticalNote << "Exception occurred reading faces for "
   1408                      << aFamily->Key().AsString(SharedFontList()).get();
   1409    }
   1410  }
   1411 }
   1412 
   1413 bool gfxDWriteFontList::ReadFaceNames(const fontlist::Family* aFamily,
   1414                                      const fontlist::Face* aFace,
   1415                                      nsCString& aPSName,
   1416                                      nsCString& aFullName) {
   1417  IDWriteFontCollection* collection =
   1418 #ifdef MOZ_BUNDLED_FONTS
   1419      aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
   1420 #else
   1421      mSystemFonts;
   1422 #endif
   1423  RefPtr<IDWriteFontFamily> family;
   1424  if (FAILED(collection->GetFontFamily(aFamily->Index(),
   1425                                       getter_AddRefs(family)))) {
   1426    MOZ_ASSERT_UNREACHABLE("failed to get font-family");
   1427    return false;
   1428  }
   1429  RefPtr<IDWriteFont> dwFont;
   1430  if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(dwFont)))) {
   1431    MOZ_ASSERT_UNREACHABLE("failed to get font from family");
   1432    return false;
   1433  }
   1434  HRESULT ps = GetDirectWriteFaceName(dwFont, PSNAME_ID, aPSName);
   1435  HRESULT full = GetDirectWriteFaceName(dwFont, FULLNAME_ID, aFullName);
   1436  if (FAILED(ps) || FAILED(full) || aPSName.IsEmpty() || aFullName.IsEmpty()) {
   1437    // We'll return true if either name was found, false if both fail.
   1438    // Note that on older Win7 systems, GetDirectWriteFaceName may "succeed"
   1439    // but return an empty string, so we have to check for non-empty strings
   1440    // to be sure we actually got a usable name.
   1441 
   1442    // Initialize result to true if either name was already found.
   1443    bool result = (SUCCEEDED(ps) && !aPSName.IsEmpty()) ||
   1444                  (SUCCEEDED(full) && !aFullName.IsEmpty());
   1445    RefPtr<IDWriteFontFace> dwFontFace;
   1446    if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
   1447      NS_WARNING("failed to create font face");
   1448      return result;
   1449    }
   1450    void* context;
   1451    const char* data;
   1452    UINT32 size;
   1453    BOOL exists;
   1454    if (FAILED(dwFontFace->TryGetFontTable(
   1455            NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
   1456            (const void**)&data, &size, &context, &exists)) ||
   1457        !exists) {
   1458      NS_WARNING("failed to get name table");
   1459      return result;
   1460    }
   1461    MOZ_SEH_TRY {
   1462      // Try to read the name table entries, and ensure result is true if either
   1463      // one succeeds.
   1464      if (FAILED(ps) || aPSName.IsEmpty()) {
   1465        if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
   1466                data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aPSName))) {
   1467          result = true;
   1468        } else {
   1469          NS_WARNING("failed to read psname");
   1470        }
   1471      }
   1472      if (FAILED(full) || aFullName.IsEmpty()) {
   1473        if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
   1474                data, size, gfxFontUtils::NAME_ID_FULL, aFullName))) {
   1475          result = true;
   1476        } else {
   1477          NS_WARNING("failed to read fullname");
   1478        }
   1479      }
   1480    }
   1481    MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   1482      gfxCriticalNote << "Exception occurred reading face names for "
   1483                      << aFamily->Key().AsString(SharedFontList()).get();
   1484    }
   1485    if (dwFontFace && context) {
   1486      dwFontFace->ReleaseFontTable(context);
   1487    }
   1488    return result;
   1489  }
   1490  return true;
   1491 }
   1492 
   1493 void gfxDWriteFontList::ReadFaceNamesForFamily(
   1494    fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) {
   1495  if (!aFamily->IsInitialized()) {
   1496    if (!InitializeFamily(aFamily)) {
   1497      return;
   1498    }
   1499  }
   1500  IDWriteFontCollection* collection =
   1501 #ifdef MOZ_BUNDLED_FONTS
   1502      aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
   1503 #else
   1504      mSystemFonts;
   1505 #endif
   1506  RefPtr<IDWriteFontFamily> family;
   1507  if (FAILED(collection->GetFontFamily(aFamily->Index(),
   1508                                       getter_AddRefs(family)))) {
   1509    return;
   1510  }
   1511  fontlist::FontList* list = SharedFontList();
   1512  const fontlist::Pointer* facePtrs = aFamily->Faces(list);
   1513  nsAutoCString familyName(aFamily->DisplayName().AsString(list));
   1514  nsAutoCString key(aFamily->Key().AsString(list));
   1515 
   1516  MOZ_SEH_TRY {
   1517    // Read PS-names and fullnames of the faces, and any alternate family names
   1518    // (either localizations or legacy subfamily names)
   1519    for (unsigned i = 0; i < aFamily->NumFaces(); ++i) {
   1520      auto* face = facePtrs[i].ToPtr<fontlist::Face>(list);
   1521      if (!face) {
   1522        continue;
   1523      }
   1524      RefPtr<IDWriteFont> dwFont;
   1525      if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) {
   1526        continue;
   1527      }
   1528      RefPtr<IDWriteFontFace> dwFontFace;
   1529      if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
   1530        continue;
   1531      }
   1532 
   1533      const char* data;
   1534      UINT32 size;
   1535      void* context;
   1536      BOOL exists;
   1537      if (FAILED(dwFontFace->TryGetFontTable(
   1538              NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
   1539              (const void**)&data, &size, &context, &exists)) ||
   1540          !exists) {
   1541        continue;
   1542      }
   1543 
   1544      AutoTArray<nsCString, 4> otherFamilyNames;
   1545      gfxFontUtils::ReadOtherFamilyNamesForFace(familyName, data, size,
   1546                                                otherFamilyNames, false);
   1547      for (const auto& alias : otherFamilyNames) {
   1548        nsAutoCString key(alias);
   1549        ToLowerCase(key);
   1550        auto aliasData = mAliasTable.GetOrInsertNew(key);
   1551        aliasData->InitFromFamily(aFamily, familyName);
   1552        aliasData->mFaces.AppendElement(facePtrs[i]);
   1553      }
   1554 
   1555      nsAutoCString psname, fullname;
   1556      // Bug 1854090: don't load PSname if the family name ends with ".tmp",
   1557      // as some PDF-related software appears to pollute the font collection
   1558      // with spurious re-encoded versions of standard fonts like Arial, fails
   1559      // to alter the PSname, and thus can result in garbled rendering because
   1560      // the wrong resource may be found via src:local(...).
   1561      if (!StringEndsWith(key, ".tmp"_ns)) {
   1562        if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
   1563                data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, psname))) {
   1564          ToLowerCase(psname);
   1565          MaybeAddToLocalNameTable(psname,
   1566                                   fontlist::LocalFaceRec::InitData(key, i));
   1567        }
   1568      }
   1569      if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
   1570              data, size, gfxFontUtils::NAME_ID_FULL, fullname))) {
   1571        ToLowerCase(fullname);
   1572        if (fullname != psname) {
   1573          MaybeAddToLocalNameTable(fullname,
   1574                                   fontlist::LocalFaceRec::InitData(key, i));
   1575        }
   1576      }
   1577 
   1578      dwFontFace->ReleaseFontTable(context);
   1579    }
   1580  }
   1581  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   1582    gfxCriticalNote << "Exception occurred reading names for "
   1583                    << familyName.get();
   1584  }
   1585 }
   1586 
   1587 enum DWriteInitError {
   1588  errGDIInterop = 1,
   1589  errSystemFontCollection = 2,
   1590  errNoFonts = 3
   1591 };
   1592 
   1593 void gfxDWriteFontList::InitSharedFontListForPlatform() {
   1594  mGDIFontTableAccess = Preferences::GetBool(
   1595      "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
   1596  mForceGDIClassicMaxFontSize = Preferences::GetInt(
   1597      "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
   1598      mForceGDIClassicMaxFontSize);
   1599 
   1600  mSubstitutions.Clear();
   1601  mHardcodedSubstitutions.Clear();
   1602  mNonExistingFonts.Clear();
   1603 
   1604  RefPtr<IDWriteFactory> factory = Factory::EnsureDWriteFactory();
   1605  HRESULT hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
   1606  if (FAILED(hr)) {
   1607    glean::fontlist::dwritefont_init_problem.AccumulateSingleSample(
   1608        uint32_t(errGDIInterop));
   1609    mSharedFontList.reset(nullptr);
   1610    return;
   1611  }
   1612 
   1613  mSystemFonts = Factory::GetDWriteSystemFonts(true);
   1614  NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
   1615  if (!mSystemFonts) {
   1616    glean::fontlist::dwritefont_init_problem.AccumulateSingleSample(
   1617        uint32_t(errSystemFontCollection));
   1618    mSharedFontList.reset(nullptr);
   1619    return;
   1620  }
   1621 #ifdef MOZ_BUNDLED_FONTS
   1622  // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
   1623  // explicit value of 0 (off) will disable them.
   1624  TimeStamp start1 = TimeStamp::Now();
   1625  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
   1626    mBundledFonts = CreateBundledFontsCollection(factory);
   1627  }
   1628  TimeStamp end1 = TimeStamp::Now();
   1629 #endif
   1630 
   1631  if (XRE_IsParentProcess()) {
   1632    nsAutoCString classicFamilies;
   1633    AutoTArray<nsCString, 16> forceClassicFams;
   1634    nsresult rv = Preferences::GetCString(
   1635        "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
   1636        classicFamilies);
   1637    if (NS_SUCCEEDED(rv)) {
   1638      for (auto name :
   1639           nsCCharSeparatedTokenizer(classicFamilies, ',').ToRange()) {
   1640        BuildKeyNameFromFontName(name);
   1641        forceClassicFams.AppendElement(name);
   1642      }
   1643      forceClassicFams.Sort();
   1644    }
   1645    nsTArray<fontlist::Family::InitData> families;
   1646    AppendFamiliesFromCollection(mSystemFonts, families, &forceClassicFams);
   1647 #ifdef MOZ_BUNDLED_FONTS
   1648    if (mBundledFonts) {
   1649      TimeStamp start2 = TimeStamp::Now();
   1650      AppendFamiliesFromCollection(mBundledFonts, families);
   1651      TimeStamp end2 = TimeStamp::Now();
   1652      glean::fontlist::bundledfonts_activate.AccumulateRawDuration(
   1653          (end1 - start1) + (end2 - start2));
   1654    }
   1655 #endif
   1656    SharedFontList()->SetFamilyNames(families);
   1657    GetPrefsAndStartLoader();
   1658  }
   1659 
   1660  if (!SharedFontList()->Initialized()) {
   1661    return;
   1662  }
   1663 
   1664  GetDirectWriteSubstitutes();
   1665  GetFontSubstitutes();
   1666 }
   1667 
   1668 nsresult gfxDWriteFontList::InitFontListForPlatform() {
   1669  LARGE_INTEGER frequency;           // ticks per second
   1670  LARGE_INTEGER t1, t2, t3, t4, t5;  // ticks
   1671  double elapsedTime, upTime;
   1672  char nowTime[256], nowDate[256];
   1673 
   1674  if (LOG_FONTINIT_ENABLED()) {
   1675    GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, nullptr, nullptr,
   1676                   nowTime, 256);
   1677    GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
   1678    upTime = (double)GetTickCount();
   1679  }
   1680  QueryPerformanceFrequency(&frequency);
   1681  QueryPerformanceCounter(&t1);  // start
   1682 
   1683  HRESULT hr;
   1684  mGDIFontTableAccess = Preferences::GetBool(
   1685      "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
   1686 
   1687  mFontSubstitutes.Clear();
   1688  mHardcodedSubstitutes.Clear();
   1689  mNonExistingFonts.Clear();
   1690 
   1691  RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
   1692 
   1693  hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
   1694  if (FAILED(hr)) {
   1695    glean::fontlist::dwritefont_init_problem.AccumulateSingleSample(
   1696        uint32_t(errGDIInterop));
   1697    return NS_ERROR_FAILURE;
   1698  }
   1699 
   1700  QueryPerformanceCounter(&t2);  // base-class/interop initialization
   1701 
   1702  mSystemFonts = Factory::GetDWriteSystemFonts(true);
   1703  NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
   1704 
   1705  if (!mSystemFonts) {
   1706    glean::fontlist::dwritefont_init_problem.AccumulateSingleSample(
   1707        uint32_t(errSystemFontCollection));
   1708    return NS_ERROR_FAILURE;
   1709  }
   1710 
   1711 #ifdef MOZ_BUNDLED_FONTS
   1712  // Get bundled fonts before the system collection, so that in the case of
   1713  // duplicate names, we have recorded the family as bundled (and therefore
   1714  // available regardless of visibility settings).
   1715  // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
   1716  // explicit value of 0 (off) will disable them.
   1717  if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
   1718    auto timerId = glean::fontlist::bundledfonts_activate.Start();
   1719    mBundledFonts = CreateBundledFontsCollection(factory);
   1720    if (mBundledFonts) {
   1721      GetFontsFromCollection(mBundledFonts);
   1722    }
   1723    glean::fontlist::bundledfonts_activate.StopAndAccumulate(
   1724        std::move(timerId));
   1725  }
   1726 #endif
   1727  const uint32_t kBundledCount = mFontFamilies.Count();
   1728 
   1729  QueryPerformanceCounter(&t3);  // system font collection
   1730 
   1731  GetFontsFromCollection(mSystemFonts);
   1732 
   1733  // if no fonts found, something is out of whack, bail and use GDI backend
   1734  NS_ASSERTION(mFontFamilies.Count() > kBundledCount,
   1735               "no fonts found in the system fontlist -- holy crap batman!");
   1736  if (mFontFamilies.Count() == kBundledCount) {
   1737    glean::fontlist::dwritefont_init_problem.AccumulateSingleSample(
   1738        uint32_t(errNoFonts));
   1739    return NS_ERROR_FAILURE;
   1740  }
   1741 
   1742  QueryPerformanceCounter(&t4);  // iterate over system fonts
   1743 
   1744  mOtherFamilyNamesInitialized = true;
   1745  GetFontSubstitutes();
   1746 
   1747  // bug 642093 - DirectWrite does not support old bitmap (.fon)
   1748  // font files, but a few of these such as "Courier" and "MS Sans Serif"
   1749  // are frequently specified in shoddy CSS, without appropriate fallbacks.
   1750  // By mapping these to TrueType equivalents, we provide better consistency
   1751  // with both pre-DW systems and with IE9, which appears to do the same.
   1752  GetDirectWriteSubstitutes();
   1753 
   1754  // bug 551313 - DirectWrite creates a Gill Sans family out of
   1755  // poorly named members of the Gill Sans MT family containing
   1756  // only Ultra Bold weights.  This causes big problems for pages
   1757  // using Gill Sans which is usually only available on OSX
   1758 
   1759  nsAutoCString nameGillSans("Gill Sans");
   1760  nsAutoCString nameGillSansMT("Gill Sans MT");
   1761  BuildKeyNameFromFontName(nameGillSans);
   1762  BuildKeyNameFromFontName(nameGillSansMT);
   1763 
   1764  gfxFontFamily* gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
   1765  gfxFontFamily* gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
   1766 
   1767  if (gillSansFamily && gillSansMTFamily) {
   1768    gillSansFamily->FindStyleVariations();
   1769 
   1770    gillSansFamily->ReadLock();
   1771    const auto& faces = gillSansFamily->GetFontList();
   1772 
   1773    bool allUltraBold = true;
   1774    for (const auto& face : faces) {
   1775      // does the face have 'Ultra Bold' in the name?
   1776      if (face->Name().Find("Ultra Bold"_ns) == -1) {
   1777        allUltraBold = false;
   1778        break;
   1779      }
   1780    }
   1781 
   1782    // if all the Gill Sans faces are Ultra Bold ==> move faces
   1783    // for Gill Sans into Gill Sans MT family
   1784    if (allUltraBold) {
   1785      // add faces to Gill Sans MT
   1786      for (const auto& face : faces) {
   1787        // change the entry's family name to match its adoptive family
   1788        face->mFamilyName = gillSansMTFamily->Name();
   1789        gillSansMTFamily->AddFontEntry(face);
   1790 
   1791        if (LOG_FONTLIST_ENABLED()) {
   1792          nsAutoCString weightString;
   1793          face->Weight().ToString(weightString);
   1794          LOG_FONTLIST(
   1795              ("(fontlist) moved (%s) to family (%s)"
   1796               " with style: %s weight: %s stretch: %d",
   1797               face->Name().get(), gillSansMTFamily->Name().get(),
   1798               (face->IsItalic()) ? "italic"
   1799                                  : (face->IsOblique() ? "oblique" : "normal"),
   1800               weightString.get(), face->Stretch().AsScalar()));
   1801        }
   1802      }
   1803      gillSansFamily->ReadUnlock();
   1804 
   1805      // remove Gill Sans
   1806      mFontFamilies.Remove(nameGillSans);
   1807    } else {
   1808      gillSansFamily->ReadUnlock();
   1809    }
   1810  }
   1811 
   1812  nsAutoCString classicFamilies;
   1813  nsresult rv = Preferences::GetCString(
   1814      "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
   1815      classicFamilies);
   1816  if (NS_SUCCEEDED(rv)) {
   1817    nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
   1818    while (tokenizer.hasMoreTokens()) {
   1819      nsAutoCString name(tokenizer.nextToken());
   1820      BuildKeyNameFromFontName(name);
   1821      gfxFontFamily* family = mFontFamilies.GetWeak(name);
   1822      if (family) {
   1823        static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true);
   1824      }
   1825    }
   1826  }
   1827  mForceGDIClassicMaxFontSize = Preferences::GetInt(
   1828      "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
   1829      mForceGDIClassicMaxFontSize);
   1830 
   1831  GetPrefsAndStartLoader();
   1832 
   1833  QueryPerformanceCounter(&t5);  // misc initialization
   1834 
   1835  if (LOG_FONTINIT_ENABLED()) {
   1836    // determine dwrite version
   1837    nsAutoString dwriteVers;
   1838    gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
   1839    LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime));
   1840    LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime / 1000));
   1841    LOG_FONTINIT(("(fontinit) dwrite version: %s\n",
   1842                  NS_ConvertUTF16toUTF8(dwriteVers).get()));
   1843  }
   1844 
   1845  elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
   1846  glean::fontlist::dwritefont_delayedinit_total.AccumulateRawDuration(
   1847      TimeDuration::FromMilliseconds(elapsedTime));
   1848  glean::fontlist::dwritefont_delayedinit_count.AccumulateSingleSample(
   1849      mSystemFonts->GetFontFamilyCount());
   1850  LOG_FONTINIT((
   1851      "(fontinit) Total time in InitFontList:    %9.3f ms (families: %d, %s)\n",
   1852      elapsedTime, mSystemFonts->GetFontFamilyCount(),
   1853      (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
   1854 
   1855  elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
   1856  LOG_FONTINIT(
   1857      ("(fontinit)  --- base/interop obj initialization init: %9.3f ms\n",
   1858       elapsedTime));
   1859 
   1860  elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
   1861  glean::fontlist::dwritefont_delayedinit_collect.AccumulateRawDuration(
   1862      TimeDuration::FromMilliseconds(elapsedTime));
   1863  LOG_FONTINIT(
   1864      ("(fontinit)  --- GetSystemFontCollection:  %9.3f ms\n", elapsedTime));
   1865 
   1866  elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart;
   1867  LOG_FONTINIT(
   1868      ("(fontinit)  --- iterate over families:    %9.3f ms\n", elapsedTime));
   1869 
   1870  elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart;
   1871  LOG_FONTINIT(
   1872      ("(fontinit)  --- misc initialization:    %9.3f ms\n", elapsedTime));
   1873 
   1874  return NS_OK;
   1875 }
   1876 
   1877 void gfxDWriteFontList::GetFontsFromCollection(
   1878    IDWriteFontCollection* aCollection) {
   1879  for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) {
   1880    RefPtr<IDWriteFontFamily> family;
   1881    aCollection->GetFontFamily(i, getter_AddRefs(family));
   1882 
   1883    RefPtr<IDWriteLocalizedStrings> names;
   1884    HRESULT hr = family->GetFamilyNames(getter_AddRefs(names));
   1885    if (FAILED(hr)) {
   1886      continue;
   1887    }
   1888 
   1889    nsAutoCString name;
   1890    if (!GetEnglishOrFirstName(name, names)) {
   1891      continue;
   1892    }
   1893    nsAutoCString familyName(
   1894        name);  // keep a copy before we lowercase it as a key
   1895 
   1896    BuildKeyNameFromFontName(name);
   1897 
   1898    RefPtr<gfxFontFamily> fam;
   1899 
   1900    if (mFontFamilies.GetWeak(name)) {
   1901      continue;
   1902    }
   1903 
   1904    FontVisibility visibility = aCollection == mSystemFonts
   1905                                    ? GetVisibilityForFamily(familyName)
   1906                                    : FontVisibility::Base;
   1907 
   1908    fam = new gfxDWriteFontFamily(familyName, visibility, family,
   1909                                  aCollection == mSystemFonts);
   1910    if (!fam) {
   1911      continue;
   1912    }
   1913 
   1914    if (mBadUnderlineFamilyNames.ContainsSorted(name)) {
   1915      fam->SetBadUnderlineFamily();
   1916    }
   1917    mFontFamilies.InsertOrUpdate(name, RefPtr{fam});
   1918 
   1919    // now add other family name localizations, if present
   1920    uint32_t nameCount = names->GetCount();
   1921    uint32_t nameIndex;
   1922 
   1923    if (nameCount > 1) {
   1924      UINT32 englishIdx = 0;
   1925      BOOL exists;
   1926      // if this fails/doesn't exist, we'll have used name index 0,
   1927      // so that's the one we'll want to skip here
   1928      names->FindLocaleName(L"en-us", &englishIdx, &exists);
   1929      AutoTArray<nsCString, 4> otherFamilyNames;
   1930      for (nameIndex = 0; nameIndex < nameCount; nameIndex++) {
   1931        UINT32 nameLen;
   1932        AutoTArray<WCHAR, 32> localizedName;
   1933 
   1934        // only add other names
   1935        if (nameIndex == englishIdx) {
   1936          continue;
   1937        }
   1938 
   1939        hr = names->GetStringLength(nameIndex, &nameLen);
   1940        if (FAILED(hr)) {
   1941          continue;
   1942        }
   1943 
   1944        if (!localizedName.SetLength(nameLen + 1, fallible)) {
   1945          continue;
   1946        }
   1947 
   1948        hr = names->GetString(nameIndex, localizedName.Elements(), nameLen + 1);
   1949        if (FAILED(hr)) {
   1950          continue;
   1951        }
   1952 
   1953        NS_ConvertUTF16toUTF8 locName(localizedName.Elements());
   1954 
   1955        if (!familyName.Equals(locName)) {
   1956          otherFamilyNames.AppendElement(locName);
   1957        }
   1958      }
   1959      if (!otherFamilyNames.IsEmpty()) {
   1960        AddOtherFamilyNames(fam, otherFamilyNames);
   1961      }
   1962    }
   1963 
   1964    // at this point, all family names have been read in
   1965    fam->SetOtherFamilyNamesInitialized();
   1966  }
   1967 }
   1968 
   1969 static void RemoveCharsetFromFontSubstitute(nsACString& aName) {
   1970  int32_t comma = aName.FindChar(',');
   1971  if (comma >= 0) aName.Truncate(comma);
   1972 }
   1973 
   1974 #define MAX_VALUE_NAME 512
   1975 #define MAX_VALUE_DATA 512
   1976 
   1977 nsresult gfxDWriteFontList::GetFontSubstitutes() {
   1978  for (const FontSubstitute& fs : kFontSubstitutes) {
   1979    nsAutoCString substituteName(fs.substituteName);
   1980    nsAutoCString actualFontName(fs.actualFontName);
   1981    BuildKeyNameFromFontName(substituteName);
   1982    BuildKeyNameFromFontName(actualFontName);
   1983    AddSubstitute(substituteName, actualFontName, true);
   1984  }
   1985 
   1986  HKEY hKey;
   1987  DWORD i, rv, lenAlias, lenActual, valueType;
   1988  WCHAR aliasName[MAX_VALUE_NAME];
   1989  WCHAR actualName[MAX_VALUE_DATA];
   1990 
   1991  if (RegOpenKeyExW(
   1992          HKEY_LOCAL_MACHINE,
   1993          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
   1994          0, KEY_READ, &hKey) != ERROR_SUCCESS) {
   1995    return NS_ERROR_FAILURE;
   1996  }
   1997 
   1998  for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
   1999    aliasName[0] = 0;
   2000    lenAlias = std::size(aliasName);
   2001    actualName[0] = 0;
   2002    lenActual = sizeof(actualName);
   2003    rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
   2004                       (LPBYTE)actualName, &lenActual);
   2005 
   2006    if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
   2007      continue;
   2008    }
   2009 
   2010    if (aliasName[0] == WCHAR('@')) {
   2011      continue;
   2012    }
   2013 
   2014    NS_ConvertUTF16toUTF8 substituteName((char16_t*)aliasName);
   2015    NS_ConvertUTF16toUTF8 actualFontName((char16_t*)actualName);
   2016    RemoveCharsetFromFontSubstitute(substituteName);
   2017    BuildKeyNameFromFontName(substituteName);
   2018    RemoveCharsetFromFontSubstitute(actualFontName);
   2019    BuildKeyNameFromFontName(actualFontName);
   2020    AddSubstitute(substituteName, actualFontName, false);
   2021  }
   2022 
   2023  return NS_OK;
   2024 }
   2025 
   2026 void gfxDWriteFontList::AddSubstitute(const nsCString& aSubstituteName,
   2027                                      const nsCString& aActualFontName,
   2028                                      bool aIsHardcoded) {
   2029  if (SharedFontList()) {
   2030    auto& substitutions =
   2031        aIsHardcoded ? mHardcodedSubstitutions : mSubstitutions;
   2032    // Skip substitution if the original font is available, unless the
   2033    // option to apply substitutions unconditionally is enabled.
   2034    if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
   2035      // Font substitutions are recorded for the canonical family names;
   2036      // we don't need FindFamily to consider localized aliases when
   2037      // searching.
   2038      if (SharedFontList()->FindFamily(aSubstituteName,
   2039                                       /*aPrimaryNameOnly*/ true)) {
   2040        return;
   2041      }
   2042    }
   2043    if (SharedFontList()->FindFamily(aActualFontName,
   2044                                     /*aPrimaryNameOnly*/ true)) {
   2045      substitutions.InsertOrUpdate(aSubstituteName,
   2046                                   MakeUnique<nsCString>(aActualFontName));
   2047    } else if (substitutions.Get(aActualFontName)) {
   2048      substitutions.InsertOrUpdate(
   2049          aSubstituteName,
   2050          MakeUnique<nsCString>(*substitutions.Get(aActualFontName)));
   2051    } else {
   2052      mNonExistingFonts.AppendElement(aSubstituteName);
   2053    }
   2054  } else {
   2055    gfxFontFamily* ff;
   2056    if (!aActualFontName.IsEmpty() &&
   2057        (ff = mFontFamilies.GetWeak(aActualFontName))) {
   2058      if (aIsHardcoded) {
   2059        mHardcodedSubstitutes.InsertOrUpdate(aSubstituteName, RefPtr{ff});
   2060      } else {
   2061        mFontSubstitutes.InsertOrUpdate(aSubstituteName, RefPtr{ff});
   2062      }
   2063    } else {
   2064      mNonExistingFonts.AppendElement(aSubstituteName);
   2065    }
   2066  }
   2067 }
   2068 
   2069 struct FontSubstitution {
   2070  const char* aliasName;
   2071  const char* actualName;
   2072 };
   2073 
   2074 static const FontSubstitution sDirectWriteSubs[] = {
   2075    {"MS Sans Serif", "Microsoft Sans Serif"},
   2076    {"MS Serif", "Times New Roman"},
   2077    {"Courier", "Courier New"},
   2078    {"Small Fonts", "Arial"},
   2079    {"Roman", "Times New Roman"},
   2080    {"Script", "Mistral"}};
   2081 
   2082 void gfxDWriteFontList::GetDirectWriteSubstitutes() {
   2083  for (uint32_t i = 0; i < std::size(sDirectWriteSubs); ++i) {
   2084    const FontSubstitution& sub(sDirectWriteSubs[i]);
   2085    nsAutoCString substituteName(sub.aliasName);
   2086    BuildKeyNameFromFontName(substituteName);
   2087    if (SharedFontList()) {
   2088      // Skip substitution if the original font is available, unless the option
   2089      // to apply substitutions unconditionally is enabled.
   2090      if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
   2091        // We don't need FindFamily to consider localized aliases when searching
   2092        // for the DirectWrite substitutes, we know the canonical names.
   2093        if (SharedFontList()->FindFamily(substituteName,
   2094                                         /*aPrimaryNameOnly*/ true)) {
   2095          continue;
   2096        }
   2097      }
   2098      nsAutoCString actualFontName(sub.actualName);
   2099      BuildKeyNameFromFontName(actualFontName);
   2100      if (SharedFontList()->FindFamily(actualFontName,
   2101                                       /*aPrimaryNameOnly*/ true)) {
   2102        mSubstitutions.InsertOrUpdate(substituteName,
   2103                                      MakeUnique<nsCString>(actualFontName));
   2104        mHardcodedSubstitutions.InsertOrUpdate(
   2105            substituteName, MakeUnique<nsCString>(actualFontName));
   2106      } else {
   2107        mNonExistingFonts.AppendElement(substituteName);
   2108      }
   2109    } else {
   2110      if (nullptr != mFontFamilies.GetWeak(substituteName)) {
   2111        // don't do the substitution if user actually has a usable font
   2112        // with this name installed
   2113        continue;
   2114      }
   2115      nsAutoCString actualFontName(sub.actualName);
   2116      BuildKeyNameFromFontName(actualFontName);
   2117      gfxFontFamily* ff;
   2118      if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
   2119        mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
   2120        mHardcodedSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
   2121      } else {
   2122        mNonExistingFonts.AppendElement(substituteName);
   2123      }
   2124    }
   2125  }
   2126 }
   2127 
   2128 bool gfxDWriteFontList::FindAndAddFamiliesLocked(
   2129    FontVisibilityProvider* aFontVisibilityProvider,
   2130    StyleGenericFontFamily aGeneric, const nsACString& aFamily,
   2131    nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
   2132    gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
   2133  nsAutoCString keyName(aFamily);
   2134  BuildKeyNameFromFontName(keyName);
   2135 
   2136  const bool useHardcodedList =
   2137      aFontVisibilityProvider
   2138          ? aFontVisibilityProvider->ShouldResistFingerprinting(
   2139                RFPTarget::UseHardcodedFontSubstitutes)
   2140          : nsContentUtils::ShouldResistFingerprinting(
   2141                "aFontVisibilityProvider is not available",
   2142                RFPTarget::UseHardcodedFontSubstitutes);
   2143  if (SharedFontList()) {
   2144    nsACString* subst = useHardcodedList ? mHardcodedSubstitutions.Get(keyName)
   2145                                         : mSubstitutions.Get(keyName);
   2146    if (subst) {
   2147      keyName = *subst;
   2148    }
   2149  } else {
   2150    gfxFontFamily* ff = useHardcodedList
   2151                            ? mHardcodedSubstitutes.GetWeak(keyName)
   2152                            : mFontSubstitutes.GetWeak(keyName);
   2153    FontVisibility level = aFontVisibilityProvider
   2154                               ? aFontVisibilityProvider->GetFontVisibility()
   2155                               : FontVisibility::User;
   2156    if (ff && IsVisibleToCSS(*ff, level)) {
   2157      aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
   2158      return true;
   2159    }
   2160  }
   2161 
   2162  if (mNonExistingFonts.Contains(keyName)) {
   2163    return false;
   2164  }
   2165 
   2166  return gfxPlatformFontList::FindAndAddFamiliesLocked(
   2167      aFontVisibilityProvider, aGeneric, keyName, aOutput, aFlags, aStyle,
   2168      aLanguage, aDevToCssSize);
   2169 }
   2170 
   2171 void gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
   2172                                               FontListSizes* aSizes) const {
   2173  gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   2174 
   2175  AutoLock lock(mLock);
   2176 
   2177  // We are a singleton, so include the font loader singleton's memory.
   2178  MOZ_ASSERT(static_cast<const gfxPlatformFontList*>(this) ==
   2179             gfxPlatformFontList::PlatformFontList());
   2180  gfxDWriteFontFileLoader* loader = static_cast<gfxDWriteFontFileLoader*>(
   2181      gfxDWriteFontFileLoader::Instance());
   2182  aSizes->mLoaderSize += loader->SizeOfIncludingThis(aMallocSizeOf);
   2183 
   2184  aSizes->mFontListSize +=
   2185      SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
   2186  aSizes->mFontListSize +=
   2187      SizeOfFontFamilyTableExcludingThis(mHardcodedSubstitutes, aMallocSizeOf);
   2188 
   2189  aSizes->mFontListSize +=
   2190      mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2191  for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
   2192    aSizes->mFontListSize +=
   2193        mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   2194  }
   2195 }
   2196 
   2197 void gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   2198                                               FontListSizes* aSizes) const {
   2199  aSizes->mFontListSize += aMallocSizeOf(this);
   2200  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   2201 }
   2202 
   2203 static HRESULT GetFamilyName(IDWriteFont* aFont, nsCString& aFamilyName) {
   2204  HRESULT hr;
   2205  RefPtr<IDWriteFontFamily> family;
   2206 
   2207  // clean out previous value
   2208  aFamilyName.Truncate();
   2209 
   2210  hr = aFont->GetFontFamily(getter_AddRefs(family));
   2211  if (FAILED(hr)) {
   2212    return hr;
   2213  }
   2214 
   2215  RefPtr<IDWriteLocalizedStrings> familyNames;
   2216 
   2217  hr = family->GetFamilyNames(getter_AddRefs(familyNames));
   2218  if (FAILED(hr)) {
   2219    return hr;
   2220  }
   2221 
   2222  if (!GetEnglishOrFirstName(aFamilyName, familyNames)) {
   2223    return E_FAIL;
   2224  }
   2225 
   2226  return S_OK;
   2227 }
   2228 
   2229 // bug 705594 - the method below doesn't actually do any "drawing", it's only
   2230 // used to invoke the DirectWrite layout engine to determine the fallback font
   2231 // for a given character.
   2232 
   2233 IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun(
   2234    void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY,
   2235    DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun,
   2236    DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
   2237    IUnknown* clientDrawingEffect) {
   2238  if (!mSystemFonts) {
   2239    return E_FAIL;
   2240  }
   2241 
   2242  HRESULT hr = S_OK;
   2243 
   2244  RefPtr<IDWriteFont> font;
   2245  hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
   2246                                         getter_AddRefs(font));
   2247  if (FAILED(hr)) {
   2248    return hr;
   2249  }
   2250 
   2251  // copy the family name
   2252  hr = GetFamilyName(font, mFamilyName);
   2253  if (FAILED(hr)) {
   2254    return hr;
   2255  }
   2256 
   2257  // Arial is used as the default fallback font
   2258  // so if it matches ==> no font found
   2259  if (mFamilyName.EqualsLiteral("Arial")) {
   2260    mFamilyName.Truncate();
   2261    return E_FAIL;
   2262  }
   2263  return hr;
   2264 }
   2265 
   2266 gfxFontEntry* gfxDWriteFontList::PlatformGlobalFontFallback(
   2267    FontVisibilityProvider* aFontVisibilityProvider, const uint32_t aCh,
   2268    Script aRunScript, const gfxFontStyle* aMatchStyle,
   2269    FontFamily& aMatchedFamily) {
   2270  HRESULT hr;
   2271 
   2272  RefPtr<IDWriteFactory> dwFactory = Factory::GetDWriteFactory();
   2273  if (!dwFactory) {
   2274    return nullptr;
   2275  }
   2276 
   2277  // initialize fallback renderer
   2278  if (!mFallbackRenderer) {
   2279    mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory);
   2280  }
   2281  if (!mFallbackRenderer->IsValid()) {
   2282    return nullptr;
   2283  }
   2284 
   2285  // initialize text format
   2286  if (!mFallbackFormat) {
   2287    hr = dwFactory->CreateTextFormat(
   2288        L"Arial", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL,
   2289        DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us",
   2290        getter_AddRefs(mFallbackFormat));
   2291    if (FAILED(hr)) {
   2292      return nullptr;
   2293    }
   2294  }
   2295 
   2296  // set up string with fallback character
   2297  wchar_t str[16];
   2298  uint32_t strLen;
   2299 
   2300  if (IS_IN_BMP(aCh)) {
   2301    str[0] = static_cast<wchar_t>(aCh);
   2302    str[1] = 0;
   2303    strLen = 1;
   2304  } else {
   2305    str[0] = static_cast<wchar_t>(H_SURROGATE(aCh));
   2306    str[1] = static_cast<wchar_t>(L_SURROGATE(aCh));
   2307    str[2] = 0;
   2308    strLen = 2;
   2309  }
   2310 
   2311  // set up layout
   2312  RefPtr<IDWriteTextLayout> fallbackLayout;
   2313 
   2314  hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, 200.0f, 200.0f,
   2315                                   getter_AddRefs(fallbackLayout));
   2316  if (FAILED(hr)) {
   2317    return nullptr;
   2318  }
   2319 
   2320  // call the draw method to invoke the DirectWrite layout functions
   2321  // which determine the fallback font
   2322  MOZ_SEH_TRY {
   2323    hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
   2324    if (FAILED(hr)) {
   2325      return nullptr;
   2326    }
   2327  }
   2328  MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   2329    gfxCriticalNote << "Exception occurred during DWrite font fallback";
   2330    return nullptr;
   2331  }
   2332 
   2333  FontFamily family = FindFamily(aFontVisibilityProvider,
   2334                                 mFallbackRenderer->FallbackFamilyName());
   2335  if (!family.IsNull()) {
   2336    gfxFontEntry* fontEntry = nullptr;
   2337    if (family.mShared) {
   2338      auto face =
   2339          family.mShared->FindFaceForStyle(SharedFontList(), *aMatchStyle);
   2340      if (face) {
   2341        fontEntry = GetOrCreateFontEntry(face, family.mShared);
   2342      }
   2343    } else {
   2344      fontEntry = family.mUnshared->FindFontForStyle(*aMatchStyle);
   2345    }
   2346    if (fontEntry && fontEntry->HasCharacter(aCh)) {
   2347      aMatchedFamily = family;
   2348      return fontEntry;
   2349    }
   2350    glean::fontlist::bad_fallback_font
   2351        .EnumGet(glean::fontlist::BadFallbackFontLabel::eTrue)
   2352        .Add();
   2353  }
   2354 
   2355  return nullptr;
   2356 }
   2357 
   2358 // used to load system-wide font info on off-main thread
   2359 class DirectWriteFontInfo : public FontInfoData {
   2360 public:
   2361  DirectWriteFontInfo(bool aLoadOtherNames, bool aLoadFaceNames,
   2362                      bool aLoadCmaps, IDWriteFontCollection* aSystemFonts
   2363 #ifdef MOZ_BUNDLED_FONTS
   2364                      ,
   2365                      IDWriteFontCollection* aBundledFonts
   2366 #endif
   2367                      )
   2368      : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps),
   2369        mSystemFonts(aSystemFonts)
   2370 #ifdef MOZ_BUNDLED_FONTS
   2371        ,
   2372        mBundledFonts(aBundledFonts)
   2373 #endif
   2374  {
   2375  }
   2376 
   2377  virtual ~DirectWriteFontInfo() = default;
   2378 
   2379  // loads font data for all members of a given family
   2380  virtual void LoadFontFamilyData(const nsACString& aFamilyName);
   2381 
   2382 private:
   2383  RefPtr<IDWriteFontCollection> mSystemFonts;
   2384 #ifdef MOZ_BUNDLED_FONTS
   2385  RefPtr<IDWriteFontCollection> mBundledFonts;
   2386 #endif
   2387 };
   2388 
   2389 void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
   2390  // lookup the family
   2391  NS_ConvertUTF8toUTF16 famName(aFamilyName);
   2392 
   2393  HRESULT hr;
   2394  BOOL exists = false;
   2395 
   2396  uint32_t index;
   2397  RefPtr<IDWriteFontFamily> family;
   2398  hr = mSystemFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
   2399                                    &exists);
   2400  if (SUCCEEDED(hr) && exists) {
   2401    mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
   2402    if (!family) {
   2403      return;
   2404    }
   2405  }
   2406 
   2407 #ifdef MOZ_BUNDLED_FONTS
   2408  if (!family && mBundledFonts) {
   2409    hr = mBundledFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
   2410                                       &exists);
   2411    if (SUCCEEDED(hr) && exists) {
   2412      mBundledFonts->GetFontFamily(index, getter_AddRefs(family));
   2413    }
   2414  }
   2415 #endif
   2416 
   2417  if (!family) {
   2418    return;
   2419  }
   2420 
   2421  // later versions of DirectWrite support querying the fullname/psname
   2422  bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
   2423 
   2424  for (uint32_t i = 0; i < family->GetFontCount(); i++) {
   2425    // get the font
   2426    RefPtr<IDWriteFont> dwFont;
   2427    hr = family->GetFont(i, getter_AddRefs(dwFont));
   2428    if (FAILED(hr)) {
   2429      // This should never happen.
   2430      NS_WARNING("Failed to get existing font from family.");
   2431      continue;
   2432    }
   2433 
   2434    if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
   2435      // We don't want these in the font list; we'll apply simulations
   2436      // on the fly when appropriate.
   2437      continue;
   2438    }
   2439 
   2440    mLoadStats.fonts++;
   2441 
   2442    // get the name of the face
   2443    nsCString fullID(aFamilyName);
   2444    nsAutoCString fontName;
   2445    hr = GetDirectWriteFontName(dwFont, fontName);
   2446    if (FAILED(hr)) {
   2447      continue;
   2448    }
   2449    fullID.Append(' ');
   2450    fullID.Append(fontName);
   2451 
   2452    FontFaceData fontData;
   2453    bool haveData = true;
   2454    RefPtr<IDWriteFontFace> dwFontFace;
   2455 
   2456    if (mLoadFaceNames) {
   2457      // try to load using DirectWrite first
   2458      if (loadFaceNamesUsingDirectWrite) {
   2459        hr =
   2460            GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
   2461        if (FAILED(hr)) {
   2462          loadFaceNamesUsingDirectWrite = false;
   2463        }
   2464        hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
   2465        if (FAILED(hr)) {
   2466          loadFaceNamesUsingDirectWrite = false;
   2467        }
   2468      }
   2469 
   2470      // if DirectWrite read fails, load directly from name table
   2471      if (!loadFaceNamesUsingDirectWrite) {
   2472        hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
   2473        if (SUCCEEDED(hr)) {
   2474          uint32_t kNAME =
   2475              NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
   2476          const char* nameData;
   2477          BOOL exists;
   2478          void* ctx;
   2479          uint32_t nameSize;
   2480 
   2481          hr = dwFontFace->TryGetFontTable(kNAME, (const void**)&nameData,
   2482                                           &nameSize, &ctx, &exists);
   2483          if (SUCCEEDED(hr) && nameData && nameSize > 0) {
   2484            MOZ_SEH_TRY {
   2485              gfxFontUtils::ReadCanonicalName(nameData, nameSize,
   2486                                              gfxFontUtils::NAME_ID_FULL,
   2487                                              fontData.mFullName);
   2488              gfxFontUtils::ReadCanonicalName(nameData, nameSize,
   2489                                              gfxFontUtils::NAME_ID_POSTSCRIPT,
   2490                                              fontData.mPostscriptName);
   2491            }
   2492            MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   2493              gfxCriticalNote << "Exception occurred reading names for "
   2494                              << PromiseFlatCString(aFamilyName).get();
   2495            }
   2496            dwFontFace->ReleaseFontTable(ctx);
   2497          }
   2498        }
   2499      }
   2500 
   2501      haveData =
   2502          !fontData.mPostscriptName.IsEmpty() || !fontData.mFullName.IsEmpty();
   2503      if (haveData) {
   2504        mLoadStats.facenames++;
   2505      }
   2506    }
   2507 
   2508    // cmaps
   2509    if (mLoadCmaps) {
   2510      if (!dwFontFace) {
   2511        hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
   2512        if (!SUCCEEDED(hr)) {
   2513          continue;
   2514        }
   2515      }
   2516 
   2517      uint32_t kCMAP =
   2518          NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
   2519      const uint8_t* cmapData;
   2520      BOOL exists;
   2521      void* ctx;
   2522      uint32_t cmapSize;
   2523 
   2524      hr = dwFontFace->TryGetFontTable(kCMAP, (const void**)&cmapData,
   2525                                       &cmapSize, &ctx, &exists);
   2526 
   2527      if (SUCCEEDED(hr) && exists) {
   2528        bool cmapLoaded = false;
   2529        RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256);
   2530        uint32_t offset;
   2531        MOZ_SEH_TRY {
   2532          if (cmapData && cmapSize > 0 &&
   2533              NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
   2534                                                  offset))) {
   2535            fontData.mCharacterMap = charmap;
   2536            fontData.mUVSOffset = offset;
   2537            cmapLoaded = true;
   2538            mLoadStats.cmaps++;
   2539          }
   2540        }
   2541        MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
   2542          gfxCriticalNote << "Exception occurred reading cmaps for "
   2543                          << PromiseFlatCString(aFamilyName).get();
   2544        }
   2545        dwFontFace->ReleaseFontTable(ctx);
   2546        haveData = haveData || cmapLoaded;
   2547      }
   2548    }
   2549 
   2550    // if have data, load
   2551    if (haveData) {
   2552      mFontFaceData.InsertOrUpdate(fullID, fontData);
   2553    }
   2554  }
   2555 }
   2556 
   2557 already_AddRefed<FontInfoData> gfxDWriteFontList::CreateFontInfoData() {
   2558  bool loadCmaps = !UsesSystemFallback() ||
   2559                   gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
   2560 
   2561  RefPtr<DirectWriteFontInfo> fi = new DirectWriteFontInfo(
   2562      false, NeedFullnamePostscriptNames(), loadCmaps, mSystemFonts
   2563 #ifdef MOZ_BUNDLED_FONTS
   2564      ,
   2565      mBundledFonts
   2566 #endif
   2567  );
   2568 
   2569  return fi.forget();
   2570 }
   2571 
   2572 gfxFontFamily* gfxDWriteFontList::CreateFontFamily(
   2573    const nsACString& aName, FontVisibility aVisibility) const {
   2574  return new gfxDWriteFontFamily(aName, aVisibility, nullptr);
   2575 }
   2576 
   2577 #ifdef MOZ_BUNDLED_FONTS
   2578 
   2579 #  define IMPL_QI_FOR_DWRITE(_interface)                             \
   2580   public:                                                           \
   2581    IFACEMETHOD(QueryInterface)(IID const& riid, void** ppvObject) { \
   2582      if (__uuidof(_interface) == riid) {                            \
   2583        *ppvObject = this;                                           \
   2584      } else if (__uuidof(IUnknown) == riid) {                       \
   2585        *ppvObject = this;                                           \
   2586      } else {                                                       \
   2587        *ppvObject = nullptr;                                        \
   2588        return E_NOINTERFACE;                                        \
   2589      }                                                              \
   2590      this->AddRef();                                                \
   2591      return S_OK;                                                   \
   2592    }
   2593 
   2594 class BundledFontFileEnumerator : public IDWriteFontFileEnumerator {
   2595  IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator)
   2596 
   2597  NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator)
   2598 
   2599 public:
   2600  BundledFontFileEnumerator(IDWriteFactory* aFactory, nsIFile* aFontDir);
   2601 
   2602  IFACEMETHODIMP MoveNext(BOOL* hasCurrentFile);
   2603 
   2604  IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile);
   2605 
   2606 private:
   2607  BundledFontFileEnumerator() = delete;
   2608  BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete;
   2609  BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) =
   2610      delete;
   2611  virtual ~BundledFontFileEnumerator() = default;
   2612 
   2613  RefPtr<IDWriteFactory> mFactory;
   2614 
   2615  nsCOMPtr<nsIFile> mFontDir;
   2616  nsCOMPtr<nsIDirectoryEnumerator> mEntries;
   2617  nsCOMPtr<nsISupports> mCurrent;
   2618 };
   2619 
   2620 BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory* aFactory,
   2621                                                     nsIFile* aFontDir)
   2622    : mFactory(aFactory), mFontDir(aFontDir) {
   2623  mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries));
   2624 }
   2625 
   2626 IFACEMETHODIMP
   2627 BundledFontFileEnumerator::MoveNext(BOOL* aHasCurrentFile) {
   2628  bool hasMore = false;
   2629  if (mEntries) {
   2630    if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) {
   2631      if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) {
   2632        hasMore = true;
   2633      }
   2634    }
   2635  }
   2636  *aHasCurrentFile = hasMore;
   2637  return S_OK;
   2638 }
   2639 
   2640 IFACEMETHODIMP
   2641 BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** aFontFile) {
   2642  nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent);
   2643  if (!file) {
   2644    return E_FAIL;
   2645  }
   2646  nsString path;
   2647  if (NS_FAILED(file->GetPath(path))) {
   2648    return E_FAIL;
   2649  }
   2650  return mFactory->CreateFontFileReference((const WCHAR*)path.get(), nullptr,
   2651                                           aFontFile);
   2652 }
   2653 
   2654 class BundledFontLoader : public IDWriteFontCollectionLoader {
   2655  IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader)
   2656 
   2657  NS_INLINE_DECL_REFCOUNTING(BundledFontLoader)
   2658 
   2659 public:
   2660  BundledFontLoader() {}
   2661 
   2662  IFACEMETHODIMP CreateEnumeratorFromKey(
   2663      IDWriteFactory* aFactory, const void* aCollectionKey,
   2664      UINT32 aCollectionKeySize,
   2665      IDWriteFontFileEnumerator** aFontFileEnumerator);
   2666 
   2667 private:
   2668  BundledFontLoader(const BundledFontLoader&) = delete;
   2669  BundledFontLoader& operator=(const BundledFontLoader&) = delete;
   2670  virtual ~BundledFontLoader() = default;
   2671 };
   2672 
   2673 IFACEMETHODIMP
   2674 BundledFontLoader::CreateEnumeratorFromKey(
   2675    IDWriteFactory* aFactory, const void* aCollectionKey,
   2676    UINT32 aCollectionKeySize,
   2677    IDWriteFontFileEnumerator** aFontFileEnumerator) {
   2678  nsIFile* fontDir = *(nsIFile**)aCollectionKey;
   2679  *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir);
   2680  NS_ADDREF(*aFontFileEnumerator);
   2681  return S_OK;
   2682 }
   2683 
   2684 already_AddRefed<IDWriteFontCollection>
   2685 gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory) {
   2686  nsCOMPtr<nsIFile> localDir;
   2687  nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
   2688  if (NS_FAILED(rv)) {
   2689    return nullptr;
   2690  }
   2691  if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
   2692    return nullptr;
   2693  }
   2694  bool isDir;
   2695  if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
   2696    return nullptr;
   2697  }
   2698 
   2699  RefPtr<BundledFontLoader> loader = new BundledFontLoader();
   2700  if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) {
   2701    return nullptr;
   2702  }
   2703 
   2704  const void* key = localDir.get();
   2705  RefPtr<IDWriteFontCollection> collection;
   2706  HRESULT hr = aFactory->CreateCustomFontCollection(loader, &key, sizeof(key),
   2707                                                    getter_AddRefs(collection));
   2708 
   2709  aFactory->UnregisterFontCollectionLoader(loader);
   2710 
   2711  if (FAILED(hr)) {
   2712    return nullptr;
   2713  } else {
   2714    return collection.forget();
   2715  }
   2716 }
   2717 
   2718 #endif