tor-browser

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

SharedFontList.cpp (50056B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "SharedFontList-impl.h"
      6 #include "gfxPlatformFontList.h"
      7 #include "gfxFontUtils.h"
      8 #include "gfxFont.h"
      9 #include "nsReadableUtils.h"
     10 #include "prerror.h"
     11 #include "mozilla/dom/ContentChild.h"
     12 #include "mozilla/dom/ContentParent.h"
     13 #include "mozilla/Logging.h"
     14 
     15 #define LOG_FONTLIST(args) \
     16  MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
     17 #define LOG_FONTLIST_ENABLED() \
     18  MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
     19 
     20 namespace mozilla {
     21 namespace fontlist {
     22 
     23 static double WSSDistance(const Face* aFace, const gfxFontStyle& aStyle) {
     24  double stretchDist = StretchDistance(aFace->mStretch, aStyle.stretch);
     25  double styleDist = StyleDistance(
     26      aFace->mStyle, aStyle.style,
     27      aStyle.synthesisStyle != StyleFontSynthesisStyle::ObliqueOnly);
     28  double weightDist = WeightDistance(aFace->mWeight, aStyle.weight);
     29 
     30  // Sanity-check that the distances are within the expected range
     31  // (update if implementation of the distance functions is changed).
     32  MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
     33  MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 900.0);
     34  MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
     35 
     36  // weight/style/stretch priority: stretch >> style >> weight
     37  // so we multiply the stretch and style values to make them dominate
     38  // the result
     39  return stretchDist * kStretchFactor + styleDist * kStyleFactor +
     40         weightDist * kWeightFactor;
     41 }
     42 
     43 void* Pointer::ToPtr(FontList* aFontList,
     44                     size_t aSize) const MOZ_NO_THREAD_SAFETY_ANALYSIS {
     45  // On failure, we'll return null; callers need to handle this appropriately
     46  // (e.g. via fallback).
     47  void* result = nullptr;
     48 
     49  if (IsNull()) {
     50    return result;
     51  }
     52 
     53  // Ensure the list doesn't get replaced out from under us. Font-list rebuild
     54  // happens on the main thread, so only non-main-thread callers need to lock
     55  // it here.
     56  bool isMainThread = NS_IsMainThread();
     57  if (!isMainThread) {
     58    gfxPlatformFontList::PlatformFontList()->Lock();
     59  }
     60 
     61  uint32_t blockIndex = Block();
     62 
     63  // If the Pointer refers to a block we have not yet mapped in this process,
     64  // we first need to retrieve new block handle(s) from the parent and update
     65  // our mBlocks list.
     66  auto& blocks = aFontList->mBlocks;
     67  if (blockIndex >= blocks.Length()) {
     68    if (MOZ_UNLIKELY(XRE_IsParentProcess())) {
     69      // Shouldn't happen! A content process tried to pass a bad Pointer?
     70      goto cleanup;
     71    }
     72    // If we're not on the main thread, we can't do the IPC involved in
     73    // UpdateShmBlocks; just let the lookup fail for now.
     74    if (!isMainThread) {
     75      goto cleanup;
     76    }
     77    // UpdateShmBlocks can fail, if the parent has replaced the font list with
     78    // a new generation. In that case we just return null, and whatever font
     79    // the content process was trying to use will appear unusable for now. It's
     80    // about to receive a notification of the new font list anyhow, at which
     81    // point it will flush its caches and reflow everything, so the temporary
     82    // failure of this font will be forgotten.
     83    // UpdateShmBlocks will take the platform font-list lock during the update.
     84    if (MOZ_UNLIKELY(!aFontList->UpdateShmBlocks(true))) {
     85      goto cleanup;
     86    }
     87    MOZ_ASSERT(blockIndex < blocks.Length(), "failure in UpdateShmBlocks?");
     88    // This is wallpapering bug 1667977; it's unclear if we will always survive
     89    // this, as the content process may be unable to shape/render text if all
     90    // font lookups are failing.
     91    // In at least some cases, however, this can occur transiently while the
     92    // font list is being rebuilt by the parent; content will then be notified
     93    // that the list has changed, and should refresh everything successfully.
     94    if (MOZ_UNLIKELY(blockIndex >= blocks.Length())) {
     95      goto cleanup;
     96    }
     97  }
     98 
     99  {
    100    // Don't create a pointer that's outside what the block has allocated!
    101    const auto& block = blocks[blockIndex];
    102    if (MOZ_LIKELY(Offset() + aSize <= block->Allocated())) {
    103      result = static_cast<char*>(block->Memory()) + Offset();
    104    }
    105  }
    106 
    107 cleanup:
    108  if (!isMainThread) {
    109    gfxPlatformFontList::PlatformFontList()->Unlock();
    110  }
    111 
    112  return result;
    113 }
    114 
    115 void String::Assign(const nsACString& aString, FontList* aList) {
    116  // We only assign to previously-empty strings; they are never modified
    117  // after initial assignment.
    118  MOZ_ASSERT(mPointer.IsNull());
    119  mLength = aString.Length();
    120  mPointer = aList->Alloc(mLength + 1);
    121  auto* p = mPointer.ToArray<char>(aList, mLength);
    122  std::memcpy(p, aString.BeginReading(), mLength);
    123  p[mLength] = '\0';
    124 }
    125 
    126 Family::Family(FontList* aList, const InitData& aData)
    127    : mFaceCount(0),
    128      mKey(aList, aData.mKey),
    129      mName(aList, aData.mName),
    130      mCharacterMap(Pointer::Null()),
    131      mFaces(Pointer::Null()),
    132      mIndex(aData.mIndex),
    133      mVisibility(aData.mVisibility),
    134      mIsSimple(false),
    135      mIsBundled(aData.mBundled),
    136      mIsBadUnderlineFamily(aData.mBadUnderline),
    137      mIsForceClassic(aData.mForceClassic),
    138      mIsAltLocale(aData.mAltLocale) {}
    139 
    140 class SetCharMapRunnable : public mozilla::Runnable {
    141 public:
    142  SetCharMapRunnable(uint32_t aListGeneration,
    143                     std::pair<uint32_t, bool> aFamilyIndex,
    144                     uint32_t aFaceIndex, gfxCharacterMap* aCharMap)
    145      : Runnable("SetCharMapRunnable"),
    146        mListGeneration(aListGeneration),
    147        mFamilyIndex(aFamilyIndex),
    148        mFaceIndex(aFaceIndex),
    149        mCharMap(aCharMap) {}
    150 
    151  NS_IMETHOD Run() override {
    152    auto* list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
    153    if (!list || list->GetGeneration() != mListGeneration) {
    154      return NS_OK;
    155    }
    156    dom::ContentChild::GetSingleton()->SendSetCharacterMap(
    157        mListGeneration, mFamilyIndex.first, mFamilyIndex.second, mFaceIndex,
    158        *mCharMap);
    159    return NS_OK;
    160  }
    161 
    162 private:
    163  uint32_t mListGeneration;
    164  std::pair<uint32_t, bool> mFamilyIndex;
    165  uint32_t mFaceIndex;
    166  RefPtr<gfxCharacterMap> mCharMap;
    167 };
    168 
    169 void Face::SetCharacterMap(FontList* aList, gfxCharacterMap* aCharMap,
    170                           const Family* aFamily) {
    171  if (!XRE_IsParentProcess()) {
    172    Maybe<std::pair<uint32_t, bool>> familyIndex = aFamily->FindIndex(aList);
    173    if (!familyIndex) {
    174      NS_WARNING("Family index not found! Ignoring SetCharacterMap");
    175      return;
    176    }
    177    const auto* faces = aFamily->Faces(aList);
    178    uint32_t faceIndex = 0;
    179    while (faceIndex < aFamily->NumFaces()) {
    180      if (faces[faceIndex].ToPtr<Face>(aList) == this) {
    181        break;
    182      }
    183      ++faceIndex;
    184    }
    185    if (faceIndex >= aFamily->NumFaces()) {
    186      NS_WARNING("Face not found in family! Ignoring SetCharacterMap");
    187      return;
    188    }
    189    if (NS_IsMainThread()) {
    190      dom::ContentChild::GetSingleton()->SendSetCharacterMap(
    191          aList->GetGeneration(), familyIndex->first, familyIndex->second,
    192          faceIndex, *aCharMap);
    193    } else {
    194      NS_DispatchToMainThread(new SetCharMapRunnable(
    195          aList->GetGeneration(), familyIndex.value(), faceIndex, aCharMap));
    196    }
    197    return;
    198  }
    199  auto pfl = gfxPlatformFontList::PlatformFontList();
    200  mCharacterMap = pfl->GetShmemCharMap(aCharMap);
    201 }
    202 
    203 void Family::AddFaces(FontList* aList, const nsTArray<Face::InitData>& aFaces) {
    204  MOZ_ASSERT(XRE_IsParentProcess());
    205  if (mFaceCount > 0) {
    206    // Already initialized!
    207    return;
    208  }
    209 
    210  uint32_t count = aFaces.Length();
    211  bool isSimple = false;
    212  // A family is "simple" (i.e. simplified style selection may be used instead
    213  // of the full CSS font-matching algorithm) if there is at maximum one normal,
    214  // bold, italic, and bold-italic face; in this case, they are stored at known
    215  // positions in the mFaces array.
    216  const Face::InitData* slots[4] = {nullptr, nullptr, nullptr, nullptr};
    217  if (count >= 2 && count <= 4) {
    218    // Check if this can be treated as a "simple" family
    219    isSimple = true;
    220    for (const auto& f : aFaces) {
    221      if (!f.mWeight.IsSingle() || !f.mStretch.IsSingle() ||
    222          !f.mStyle.IsSingle()) {
    223        isSimple = false;
    224        break;
    225      }
    226      if (!f.mStretch.Min().IsNormal()) {
    227        isSimple = false;
    228        break;
    229      }
    230      // Figure out which slot (0-3) this face belongs in
    231      size_t slot = 0;
    232      static_assert((kBoldMask | kItalicMask) == 0b11, "bad bold/italic bits");
    233      if (f.mWeight.Min().IsBold()) {
    234        slot |= kBoldMask;
    235      }
    236      if (!f.mStyle.Min().IsNormal()) {
    237        slot |= kItalicMask;
    238      }
    239      if (slots[slot]) {
    240        // More than one face mapped to the same slot - not a simple family!
    241        isSimple = false;
    242        break;
    243      }
    244      slots[slot] = &f;
    245    }
    246    if (isSimple) {
    247      // Ensure all 4 slots will exist, even if some are empty.
    248      count = 4;
    249    }
    250  }
    251 
    252  // Allocate space for the face records, and initialize them.
    253  // coverity[suspicious_sizeof]
    254  Pointer p = aList->Alloc(count * sizeof(Pointer));
    255  auto* facePtrs = p.ToArray<Pointer>(aList, count);
    256  for (size_t i = 0; i < count; i++) {
    257    if (isSimple && !slots[i]) {
    258      facePtrs[i] = Pointer::Null();
    259    } else {
    260      const auto* initData = isSimple ? slots[i] : &aFaces[i];
    261      Pointer fp = aList->Alloc(sizeof(Face));
    262      auto* face = fp.ToPtr<Face>(aList);
    263      (void)new (face) Face(aList, *initData);
    264      facePtrs[i] = fp;
    265      if (initData->mCharMap) {
    266        face->SetCharacterMap(aList, initData->mCharMap, this);
    267      }
    268    }
    269  }
    270 
    271  mIsSimple = isSimple;
    272  mFaces = p;
    273  mFaceCount.store(count);
    274 
    275  if (LOG_FONTLIST_ENABLED()) {
    276    const nsCString& fam = DisplayName().AsString(aList);
    277    for (unsigned j = 0; j < aFaces.Length(); j++) {
    278      nsAutoCString weight, style, stretch;
    279      aFaces[j].mWeight.ToString(weight);
    280      aFaces[j].mStyle.ToString(style);
    281      aFaces[j].mStretch.ToString(stretch);
    282      LOG_FONTLIST(
    283          ("(shared-fontlist) family (%s) added face (%s) index %u, weight "
    284           "%s, style %s, stretch %s",
    285           fam.get(), aFaces[j].mDescriptor.get(), aFaces[j].mIndex,
    286           weight.get(), style.get(), stretch.get()));
    287    }
    288  }
    289 }
    290 
    291 bool Family::FindAllFacesForStyleInternal(FontList* aList,
    292                                          const gfxFontStyle& aStyle,
    293                                          nsTArray<Face*>& aFaceList) const {
    294  MOZ_ASSERT(aFaceList.IsEmpty());
    295  if (!IsInitialized()) {
    296    return false;
    297  }
    298 
    299  Pointer* facePtrs = Faces(aList);
    300  if (!facePtrs) {
    301    return false;
    302  }
    303 
    304  // Depending on the kind of family, we have to do varying amounts of work
    305  // to figure out what face(s) to use for the requested style properties.
    306 
    307  // If the family has only one face, we simply use it; no further style
    308  // checking needed. (However, for bitmap fonts we may still need to check
    309  // whether the size is acceptable.)
    310  if (NumFaces() == 1) {
    311    MOZ_ASSERT(!facePtrs[0].IsNull());
    312    auto* face = facePtrs[0].ToPtr<Face>(aList);
    313    if (face && face->HasValidDescriptor()) {
    314      aFaceList.AppendElement(face);
    315 #ifdef MOZ_WIDGET_GTK
    316      if (face->mSize) {
    317        return true;
    318      }
    319 #endif
    320    }
    321    return false;
    322  }
    323 
    324  // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
    325  // or some subset of these. In this case, we have exactly 4 entries in
    326  // mAvailableFonts, stored in the above order; note that some of the entries
    327  // may be nullptr. We can then pick the required entry based on whether the
    328  // request is for bold or non-bold, italic or non-italic, without running
    329  // the more complex matching algorithm used for larger families with many
    330  // weights and/or widths.
    331 
    332  if (mIsSimple) {
    333    // Family has no more than the "standard" 4 faces, at fixed indexes;
    334    // calculate which one we want.
    335    // Note that we cannot simply return it as not all 4 faces are necessarily
    336    // present.
    337    bool wantBold = aStyle.weight.PreferBold();
    338    bool wantItalic = !aStyle.style.IsNormal();
    339    uint8_t faceIndex =
    340        (wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
    341 
    342    // If the desired style is available, use it directly.
    343    auto* face = facePtrs[faceIndex].ToPtr<Face>(aList);
    344    if (face && face->HasValidDescriptor()) {
    345      aFaceList.AppendElement(face);
    346 #ifdef MOZ_WIDGET_GTK
    347      if (face->mSize) {
    348        return true;
    349      }
    350 #endif
    351      return false;
    352    }
    353 
    354    // Order to check fallback faces in a simple family, depending on the
    355    // requested style.
    356    static const uint8_t simpleFallbacks[4][3] = {
    357        {kBoldFaceIndex, kItalicFaceIndex,
    358         kBoldItalicFaceIndex},  // fallback sequence for Regular
    359        {kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex},  // Bold
    360        {kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex},    // Italic
    361        {kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex}  // BoldItalic
    362    };
    363    const uint8_t* order = simpleFallbacks[faceIndex];
    364 
    365    for (uint8_t trial = 0; trial < 3; ++trial) {
    366      // check remaining faces in order of preference to find the first that
    367      // actually exists
    368      face = facePtrs[order[trial]].ToPtr<Face>(aList);
    369      if (face && face->HasValidDescriptor()) {
    370        aFaceList.AppendElement(face);
    371 #ifdef MOZ_WIDGET_GTK
    372        if (face->mSize) {
    373          return true;
    374        }
    375 #endif
    376        return false;
    377      }
    378    }
    379 
    380    // We can only reach here if we failed to resolve the face pointer, which
    381    // can happen if we're on a stylo thread and caught the font list being
    382    // updated; in that case we just fail quietly and let font fallback do
    383    // something for the time being.
    384    return false;
    385  }
    386 
    387  // Pick the font(s) that are closest to the desired weight, style, and
    388  // stretch. Iterate over all fonts, measuring the weight/style distance.
    389  // Because of unicode-range values, there may be more than one font for a
    390  // given but the 99% use case is only a single font entry per
    391  // weight/style/stretch distance value. To optimize this, only add entries
    392  // to the matched font array when another entry already has the same
    393  // weight/style/stretch distance and add the last matched font entry. For
    394  // normal platform fonts with a single font entry for each
    395  // weight/style/stretch combination, only the last matched font entry will
    396  // be added.
    397  double minDistance = INFINITY;
    398  Face* matched = nullptr;
    399  // Keep track of whether we've included any non-scalable font resources in
    400  // the selected set.
    401  bool anyNonScalable = false;
    402  for (uint32_t i = 0; i < NumFaces(); i++) {
    403    auto* face = facePtrs[i].ToPtr<Face>(aList);
    404    if (face) {
    405      // weight/style/stretch priority: stretch >> style >> weight
    406      double distance = WSSDistance(face, aStyle);
    407      if (distance < minDistance) {
    408        matched = face;
    409        if (!aFaceList.IsEmpty()) {
    410          aFaceList.Clear();
    411        }
    412        minDistance = distance;
    413      } else if (distance == minDistance) {
    414        if (matched) {
    415          aFaceList.AppendElement(matched);
    416 #ifdef MOZ_WIDGET_GTK
    417          if (matched->mSize) {
    418            anyNonScalable = true;
    419          }
    420 #endif
    421        }
    422        matched = face;
    423      }
    424    }
    425  }
    426 
    427  MOZ_ASSERT(matched, "didn't match a font within a family");
    428  if (matched) {
    429    aFaceList.AppendElement(matched);
    430 #ifdef MOZ_WIDGET_GTK
    431    if (matched->mSize) {
    432      anyNonScalable = true;
    433    }
    434 #endif
    435  }
    436 
    437  return anyNonScalable;
    438 }
    439 
    440 void Family::FindAllFacesForStyle(FontList* aList, const gfxFontStyle& aStyle,
    441                                  nsTArray<Face*>& aFaceList,
    442                                  bool aIgnoreSizeTolerance) const {
    443 #ifdef MOZ_WIDGET_GTK
    444  bool anyNonScalable =
    445 #else
    446  (void)
    447 #endif
    448      FindAllFacesForStyleInternal(aList, aStyle, aFaceList);
    449 
    450 #ifdef MOZ_WIDGET_GTK
    451  // aFaceList now contains whatever faces are the best style match for
    452  // the requested style. If specifically-sized bitmap faces are supported,
    453  // we need to additionally filter the list to choose the appropriate size.
    454  //
    455  // It would be slightly more efficient to integrate this directly into the
    456  // face-selection algorithm above, but it's a rare case that doesn't apply
    457  // at all to most font families.
    458  //
    459  // Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e.
    460  // when using the gfxFcPlatformFontList implementation), so this filtering is
    461  // not needed on other platforms.
    462  //
    463  // (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color
    464  // Emoji don't count here; they package multiple bitmap sizes into a single
    465  // OpenType wrapper, so they appear as a single "scalable" face in our list.)
    466  if (anyNonScalable) {
    467    uint16_t best = 0;
    468    gfxFloat dist = 0.0;
    469    for (const auto& f : aFaceList) {
    470      if (f->mSize == 0) {
    471        // Scalable face; no size distance to compute.
    472        continue;
    473      }
    474      gfxFloat d = fabs(gfxFloat(f->mSize) - aStyle.size);
    475      if (!aIgnoreSizeTolerance && (d * 5.0 > f->mSize)) {
    476        continue;  // Too far from the requested size, ignore.
    477      }
    478      // If we haven't found a "best" bitmap size yet, or if this is a better
    479      // match, remember it.
    480      if (!best || d < dist) {
    481        best = f->mSize;
    482        dist = d;
    483      }
    484    }
    485    // Discard all faces except the chosen "best" size; or if no pixel size was
    486    // chosen, all except scalable faces.
    487    // This may eliminate *all* faces in the family, if all were bitmaps and
    488    // none was a good enough size match, in which case we'll fall back to the
    489    // next font-family name.
    490    aFaceList.RemoveElementsBy([=](const auto& e) { return e->mSize != best; });
    491  }
    492 #endif
    493 }
    494 
    495 Face* Family::FindFaceForStyle(FontList* aList, const gfxFontStyle& aStyle,
    496                               bool aIgnoreSizeTolerance) const {
    497  AutoTArray<Face*, 4> faces;
    498  FindAllFacesForStyle(aList, aStyle, faces, aIgnoreSizeTolerance);
    499  return faces.IsEmpty() ? nullptr : faces[0];
    500 }
    501 
    502 void Family::SearchAllFontsForChar(FontList* aList,
    503                                   GlobalFontMatch* aMatchData) {
    504  auto* charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList);
    505  if (!charmap) {
    506    // If the face list is not yet initialized, or if character maps have
    507    // not been loaded, go ahead and do this now (by sending a message to the
    508    // parent process, if we're running in a child).
    509    // After this, all faces should have their mCharacterMap set up, and the
    510    // family's mCharacterMap should also be set; but in the code below we
    511    // don't assume this all succeeded, so it still checks.
    512    if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this,
    513                                                                   true)) {
    514      return;
    515    }
    516    charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList);
    517  }
    518  if (charmap && !charmap->test(aMatchData->mCh)) {
    519    return;
    520  }
    521 
    522  uint32_t numFaces = NumFaces();
    523  uint32_t charMapsLoaded = 0;  // number of faces whose charmap is loaded
    524  Pointer* facePtrs = Faces(aList);
    525  if (!facePtrs) {
    526    return;
    527  }
    528  for (uint32_t i = 0; i < numFaces; i++) {
    529    auto* face = facePtrs[i].ToPtr<Face>(aList);
    530    if (!face) {
    531      continue;
    532    }
    533    MOZ_ASSERT(face->HasValidDescriptor());
    534    // Get the face's character map, if available (may be null!)
    535    charmap = face->mCharacterMap.ToPtr<const SharedBitSet>(aList);
    536    if (charmap) {
    537      ++charMapsLoaded;
    538    }
    539    // Check style distance if the char is supported, or if charmap not known
    540    // (so that we don't trigger cmap-loading for faces that would be a worse
    541    // match than what we've already found).
    542    if (!charmap || charmap->test(aMatchData->mCh)) {
    543      double distance = WSSDistance(face, aMatchData->mStyle);
    544      if (distance < aMatchData->mMatchDistance) {
    545        // It's a better style match: get a fontEntry, and if we haven't
    546        // already checked character coverage, do it now (note that
    547        // HasCharacter() will trigger loading the fontEntry's cmap, if
    548        // needed).
    549        RefPtr<gfxFontEntry> fe =
    550            gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face,
    551                                                                          this);
    552        if (!fe) {
    553          continue;
    554        }
    555        if (!charmap && !fe->HasCharacter(aMatchData->mCh)) {
    556          continue;
    557        }
    558        if (aMatchData->mPresentation != FontPresentation::Any) {
    559          RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
    560          if (!font) {
    561            continue;
    562          }
    563          bool hasColorGlyph =
    564              font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
    565          if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
    566            distance += kPresentationMismatch;
    567            if (distance >= aMatchData->mMatchDistance) {
    568              continue;
    569            }
    570          }
    571        }
    572        aMatchData->mBestMatch = fe;
    573        aMatchData->mMatchDistance = distance;
    574        aMatchData->mMatchedSharedFamily = this;
    575      }
    576    }
    577  }
    578  if (mCharacterMap.IsNull() && charMapsLoaded == numFaces) {
    579    SetupFamilyCharMap(aList);
    580  }
    581 }
    582 
    583 void Family::SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces) {
    584  if (aFaces.Length() >= 2 && aFaces.Length() <= 4) {
    585    // Check whether the faces meet the criteria for a "simple" family: no more
    586    // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
    587    // them at the appropriate slots in mFaces and set the mIsSimple flag to
    588    // accelerate font-matching.
    589    bool isSimple = true;
    590    Pointer slots[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
    591                        Pointer::Null()};
    592    for (const Pointer& fp : aFaces) {
    593      auto* f = fp.ToPtr<const Face>(aList);
    594      if (!f->mWeight.IsSingle() || !f->mStyle.IsSingle() ||
    595          !f->mStretch.IsSingle()) {
    596        isSimple = false;
    597        break;
    598      }
    599      if (!f->mStretch.Min().IsNormal()) {
    600        isSimple = false;
    601        break;
    602      }
    603      size_t slot = 0;
    604      if (f->mWeight.Min().IsBold()) {
    605        slot |= kBoldMask;
    606      }
    607      if (!f->mStyle.Min().IsNormal()) {
    608        slot |= kItalicMask;
    609      }
    610      if (!slots[slot].IsNull()) {
    611        isSimple = false;
    612        break;
    613      }
    614      slots[slot] = fp;
    615    }
    616    if (isSimple) {
    617      size_t size = 4 * sizeof(Pointer);
    618      mFaces = aList->Alloc(size);
    619      memcpy(mFaces.ToPtr(aList, size), slots, size);
    620      mFaceCount.store(4);
    621      mIsSimple = true;
    622      return;
    623    }
    624  }
    625  size_t size = aFaces.Length() * sizeof(Pointer);
    626  mFaces = aList->Alloc(size);
    627  memcpy(mFaces.ToPtr(aList, size), aFaces.Elements(), size);
    628  mFaceCount.store(aFaces.Length());
    629 }
    630 
    631 void Family::SetupFamilyCharMap(FontList* aList) {
    632  // Set the character map of the family to the union of all the face cmaps,
    633  // to allow font fallback searches to more rapidly reject the family.
    634  if (!mCharacterMap.IsNull()) {
    635    return;
    636  }
    637  if (!XRE_IsParentProcess()) {
    638    // |this| could be a Family record in either the Families() or Aliases()
    639    // arrays; FindIndex will map it back to its index and which array.
    640    Maybe<std::pair<uint32_t, bool>> index = FindIndex(aList);
    641    if (!index) {
    642      NS_WARNING("Family index not found! Ignoring SetupFamilyCharMap");
    643      return;
    644    }
    645    if (NS_IsMainThread()) {
    646      dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
    647          aList->GetGeneration(), index->first, index->second);
    648      return;
    649    }
    650    NS_DispatchToMainThread(NS_NewRunnableFunction(
    651        "SetupFamilyCharMap callback",
    652        [gen = aList->GetGeneration(), idx = index->first,
    653         alias = index->second] {
    654          dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(gen, idx,
    655                                                                    alias);
    656        }));
    657    return;
    658  }
    659  gfxSparseBitSet familyMap;
    660  Pointer firstMapShmPointer;
    661  const SharedBitSet* firstMap = nullptr;
    662  bool merged = false;
    663  Pointer* faces = Faces(aList);
    664  if (!faces) {
    665    return;
    666  }
    667  for (size_t i = 0; i < NumFaces(); i++) {
    668    auto* f = faces[i].ToPtr<const Face>(aList);
    669    if (!f) {
    670      continue;  // Skip missing face (in an incomplete "simple" family)
    671    }
    672    auto* faceMap = f->mCharacterMap.ToPtr<const SharedBitSet>(aList);
    673    if (!faceMap) {
    674      continue;  // If there's a face where setting up the cmap failed, we skip
    675                 // it as unusable.
    676    }
    677    if (!firstMap) {
    678      firstMap = faceMap;
    679      firstMapShmPointer = f->mCharacterMap;
    680    } else if (faceMap != firstMap) {
    681      if (!merged) {
    682        familyMap.Union(*firstMap);
    683        merged = true;
    684      }
    685      familyMap.Union(*faceMap);
    686    }
    687  }
    688  // If we created a merged cmap, we need to save that on the family; or if we
    689  // found no usable cmaps at all, we need to store the empty familyMap so that
    690  // we won't repeatedly attempt this for an unusable family.
    691  if (merged || firstMapShmPointer.IsNull()) {
    692    mCharacterMap =
    693        gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap);
    694  } else {
    695    // If all [usable] faces had the same cmap, we can just share it.
    696    mCharacterMap = firstMapShmPointer;
    697  }
    698 }
    699 
    700 Maybe<std::pair<uint32_t, bool>> Family::FindIndex(FontList* aList) const {
    701  const auto* start = aList->Families();
    702  const auto* end = start + aList->NumFamilies();
    703  if (this >= start && this < end) {
    704    uint32_t index = this - start;
    705    MOZ_RELEASE_ASSERT(start + index == this, "misaligned Family ptr!");
    706    return Some(std::pair(index, false));
    707  }
    708 
    709  start = aList->AliasFamilies();
    710  end = start + aList->NumAliases();
    711  if (this >= start && this < end) {
    712    uint32_t index = this - start;
    713    MOZ_RELEASE_ASSERT(start + index == this, "misaligned AliasFamily ptr!");
    714    return Some(std::pair(index, true));
    715  }
    716 
    717  return Nothing();
    718 }
    719 
    720 FontList::FontList(uint32_t aGeneration) {
    721  if (XRE_IsParentProcess()) {
    722    // Create the initial shared block, and initialize Header
    723    if (AppendShmBlock(SHM_BLOCK_SIZE)) {
    724      Header& header = GetHeader();
    725      header.mBlockHeader.mAllocated.store(sizeof(Header));
    726      header.mGeneration = aGeneration;
    727      header.mFamilyCount = 0;
    728      header.mBlockCount.store(1);
    729      header.mAliasCount.store(0);
    730      header.mLocalFaceCount.store(0);
    731      header.mFamilies = Pointer::Null();
    732      header.mAliases = Pointer::Null();
    733      header.mLocalFaces = Pointer::Null();
    734    } else {
    735      MOZ_CRASH("parent: failed to initialize FontList");
    736    }
    737  } else {
    738    // Initialize using the list of shmem blocks passed by the parent via
    739    // SetXPCOMProcessAttributes.
    740    auto& blocks = dom::ContentChild::GetSingleton()->SharedFontListBlocks();
    741    for (auto& handle : blocks) {
    742      if (!handle) {
    743        // Bail out and let UpdateShmBlocks try to do its thing below.
    744        break;
    745      }
    746      if (handle.Size() < SHM_BLOCK_SIZE) {
    747        MOZ_CRASH("failed to map shared memory");
    748      }
    749      auto newShm = handle.Map();
    750      if (!newShm || !newShm.Address()) {
    751        MOZ_CRASH("failed to map shared memory");
    752      }
    753      uint32_t size = newShm.DataAs<BlockHeader>()->mBlockSize;
    754      MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
    755      if (newShm.Size() < size) {
    756        MOZ_CRASH("failed to map shared memory");
    757      }
    758      mBlocks.AppendElement(new ShmBlock(std::move(newShm)));
    759    }
    760    blocks.Clear();
    761    // Update in case of any changes since the initial message was sent.
    762    for (unsigned retryCount = 0; retryCount < 3; ++retryCount) {
    763      if (UpdateShmBlocks(false)) {
    764        return;
    765      }
    766      // The only reason for UpdateShmBlocks to fail is if the parent recreated
    767      // the list after we read its first block, but before we finished getting
    768      // them all, and so the generation check failed on a subsequent request.
    769      // Discarding whatever we've got and retrying should get us a new,
    770      // consistent set of memory blocks in this case. If this doesn't work
    771      // after a couple of retries, bail out.
    772      DetachShmBlocks();
    773    }
    774    NS_WARNING("child: failed to initialize shared FontList");
    775  }
    776 }
    777 
    778 FontList::~FontList() { DetachShmBlocks(); }
    779 
    780 FontList::Header& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS {
    781  // We only need to lock if we're not on the main thread.
    782  bool isMainThread = NS_IsMainThread();
    783  if (!isMainThread) {
    784    gfxPlatformFontList::PlatformFontList()->Lock();
    785  }
    786 
    787  // It's invalid to try and access this before the first block exists.
    788  MOZ_ASSERT(mBlocks.Length() > 0);
    789  auto& result = *static_cast<Header*>(mBlocks[0]->Memory());
    790 
    791  if (!isMainThread) {
    792    gfxPlatformFontList::PlatformFontList()->Unlock();
    793  }
    794 
    795  return result;
    796 }
    797 
    798 bool FontList::AppendShmBlock(uint32_t aSizeNeeded) {
    799  MOZ_ASSERT(XRE_IsParentProcess());
    800  uint32_t size = std::max(aSizeNeeded, SHM_BLOCK_SIZE);
    801  auto handle = ipc::shared_memory::CreateFreezable(size);
    802  if (!handle) {
    803    MOZ_CRASH("failed to create shared memory");
    804    return false;
    805  }
    806  auto [readOnly, newShm] = std::move(handle).Map().FreezeWithMutableMapping();
    807  if (!newShm || !newShm.Address()) {
    808    MOZ_CRASH("failed to map shared memory");
    809    return false;
    810  }
    811  if (!readOnly) {
    812    MOZ_CRASH("failed to create read-only copy");
    813    return false;
    814  }
    815 
    816  ShmBlock* block = new ShmBlock(std::move(newShm));
    817  block->StoreAllocated(sizeof(BlockHeader));
    818  block->BlockSize() = size;
    819 
    820  mBlocks.AppendElement(block);
    821  GetHeader().mBlockCount.store(mBlocks.Length());
    822 
    823  mReadOnlyShmems.AppendElement(std::move(readOnly));
    824 
    825  // We don't need to broadcast the addition of the initial block,
    826  // because child processes can't have initialized their list at all
    827  // prior to the first block being set up.
    828  if (mBlocks.Length() > 1) {
    829    if (NS_IsMainThread()) {
    830      dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(),
    831                                                 mBlocks.Length() - 1);
    832    } else {
    833      NS_DispatchToMainThread(NS_NewRunnableFunction(
    834          "ShmBlockAdded callback",
    835          [generation = GetGeneration(), index = mBlocks.Length() - 1] {
    836            dom::ContentParent::BroadcastShmBlockAdded(generation, index);
    837          }));
    838    }
    839  }
    840 
    841  return true;
    842 }
    843 
    844 void FontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
    845                             ipc::ReadOnlySharedMemoryHandle aHandle) {
    846  MOZ_ASSERT(!XRE_IsParentProcess());
    847  MOZ_ASSERT(mBlocks.Length() > 0);
    848 
    849  if (!aHandle) {
    850    return;
    851  }
    852  if (aIndex != mBlocks.Length()) {
    853    return;
    854  }
    855  if (aGeneration != GetGeneration()) {
    856    return;
    857  }
    858 
    859  auto newShm = aHandle.Map();
    860  if (!newShm || !newShm.Address() || newShm.Size() < SHM_BLOCK_SIZE) {
    861    MOZ_CRASH("failed to map shared memory");
    862  }
    863 
    864  uint32_t size = newShm.DataAs<BlockHeader>()->mBlockSize;
    865  MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
    866  if (newShm.Size() < size) {
    867    MOZ_CRASH("failed to map shared memory");
    868  }
    869 
    870  mBlocks.AppendElement(new ShmBlock(std::move(newShm)));
    871 }
    872 
    873 void FontList::DetachShmBlocks() {
    874  for (auto& i : mBlocks) {
    875    i->Clear();
    876  }
    877  mBlocks.Clear();
    878  mReadOnlyShmems.Clear();
    879 }
    880 
    881 FontList::ShmBlock* FontList::GetBlockFromParent(uint32_t aIndex) {
    882  MOZ_ASSERT(!XRE_IsParentProcess());
    883  // If we have no existing blocks, we don't want a generation check yet;
    884  // the header in the first block will define the generation of this list
    885  uint32_t generation = aIndex == 0 ? 0 : GetGeneration();
    886  ipc::ReadOnlySharedMemoryHandle handle;
    887  if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
    888          generation, aIndex, &handle)) {
    889    return nullptr;
    890  }
    891  if (!handle) {
    892    return nullptr;
    893  }
    894  auto newShm = handle.Map();
    895  if (!newShm || !newShm.Address() || newShm.Size() < SHM_BLOCK_SIZE) {
    896    MOZ_CRASH("failed to map shared memory");
    897  }
    898  uint32_t size = newShm.DataAs<BlockHeader>()->mBlockSize;
    899  MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
    900  if (newShm.Size() < size) {
    901    MOZ_CRASH("failed to map shared memory");
    902  }
    903  return new ShmBlock(std::move(newShm));
    904 }
    905 
    906 // We don't take the lock when called from the constructor, so disable thread-
    907 // safety analysis here.
    908 bool FontList::UpdateShmBlocks(bool aMustLock) MOZ_NO_THREAD_SAFETY_ANALYSIS {
    909  MOZ_ASSERT(!XRE_IsParentProcess());
    910  if (aMustLock) {
    911    gfxPlatformFontList::PlatformFontList()->Lock();
    912  }
    913  bool result = true;
    914  while (!mBlocks.Length() || mBlocks.Length() < GetHeader().mBlockCount) {
    915    ShmBlock* newBlock = GetBlockFromParent(mBlocks.Length());
    916    if (!newBlock) {
    917      result = false;
    918      break;
    919    }
    920    mBlocks.AppendElement(newBlock);
    921  }
    922  if (aMustLock) {
    923    gfxPlatformFontList::PlatformFontList()->Unlock();
    924  }
    925  return result;
    926 }
    927 
    928 void FontList::ShareBlocksToProcess(
    929    nsTArray<ipc::ReadOnlySharedMemoryHandle>* aBlocks, base::ProcessId aPid) {
    930  MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
    931  for (auto& shmem : mReadOnlyShmems) {
    932    auto handle = shmem.Clone();
    933    if (!handle) {
    934      // If something went wrong here, we just bail out; the child will need to
    935      // request the blocks as needed, at some performance cost. (Although in
    936      // practice this may mean resources are so constrained the child process
    937      // isn't really going to work at all. But that's not our problem here.)
    938      aBlocks->Clear();
    939      return;
    940    }
    941    aBlocks->AppendElement(std::move(handle));
    942  }
    943 }
    944 
    945 ipc::ReadOnlySharedMemoryHandle FontList::ShareBlockToProcess(
    946    uint32_t aIndex, base::ProcessId aPid) {
    947  MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
    948  MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
    949  MOZ_RELEASE_ASSERT(aIndex < mReadOnlyShmems.Length());
    950 
    951  return mReadOnlyShmems[aIndex].Clone();
    952 }
    953 
    954 Pointer FontList::Alloc(uint32_t aSize) {
    955  // Only the parent process does allocation.
    956  MOZ_ASSERT(XRE_IsParentProcess());
    957 
    958  // 4-byte alignment is good enough for anything we allocate in the font list,
    959  // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
    960  auto align = [](uint32_t aSize) -> size_t { return (aSize + 3u) & ~3u; };
    961 
    962  aSize = align(aSize);
    963 
    964  int32_t blockIndex = -1;
    965  uint32_t curAlloc, size;
    966 
    967  if (aSize < SHM_BLOCK_SIZE - sizeof(BlockHeader)) {
    968    // Try to allocate in the most recently added block first, as this is
    969    // highly likely to succeed; if not, try earlier blocks (to fill gaps).
    970    const int32_t blockCount = mBlocks.Length();
    971    for (blockIndex = blockCount - 1; blockIndex >= 0; --blockIndex) {
    972      size = mBlocks[blockIndex]->BlockSize();
    973      curAlloc = mBlocks[blockIndex]->Allocated();
    974      if (size - curAlloc >= aSize) {
    975        break;
    976      }
    977    }
    978  }
    979 
    980  if (blockIndex < 0) {
    981    // Couldn't find enough space (or the requested size is too large to use
    982    // a part of a block): create a new block.
    983    if (!AppendShmBlock(aSize + sizeof(BlockHeader))) {
    984      return Pointer::Null();
    985    }
    986    blockIndex = mBlocks.Length() - 1;
    987    curAlloc = mBlocks[blockIndex]->Allocated();
    988  }
    989 
    990  // We've found a block; allocate space from it, and return
    991  mBlocks[blockIndex]->StoreAllocated(curAlloc + aSize);
    992 
    993  return Pointer(blockIndex, curAlloc);
    994 }
    995 
    996 void FontList::SetFamilyNames(nsTArray<Family::InitData>& aFamilies) {
    997  // Only the parent process should ever assign the list of families.
    998  MOZ_ASSERT(XRE_IsParentProcess());
    999 
   1000  Header& header = GetHeader();
   1001  MOZ_ASSERT(!header.mFamilyCount);
   1002 
   1003  gfxPlatformFontList::PlatformFontList()->ApplyWhitelist(aFamilies);
   1004  aFamilies.Sort();
   1005 
   1006  size_t count = aFamilies.Length();
   1007 
   1008  // Any font resources with an empty family-name will have been collected in
   1009  // a family with empty name, and sorted to the start of the list. Such fonts
   1010  // are not generally usable, or recognized as "installed", so we drop them.
   1011  if (count > 1 && aFamilies[0].mKey.IsEmpty()) {
   1012    aFamilies.RemoveElementAt(0);
   1013    --count;
   1014  }
   1015 
   1016  // Check for duplicate family entries (can occur if there is a bundled font
   1017  // that has the same name as a system-installed one); in this case we keep
   1018  // the bundled one as it will always be exposed.
   1019  if (count > 1) {
   1020    for (size_t i = 1; i < count; ++i) {
   1021      if (aFamilies[i].mKey.Equals(aFamilies[i - 1].mKey)) {
   1022        // Decide whether to discard the current entry or the preceding one
   1023        size_t discard =
   1024            aFamilies[i].mBundled && !aFamilies[i - 1].mBundled ? i - 1 : i;
   1025        aFamilies.RemoveElementAt(discard);
   1026        --count;
   1027        --i;
   1028      }
   1029    }
   1030  }
   1031 
   1032  header.mFamilies = Alloc(count * sizeof(Family));
   1033  if (header.mFamilies.IsNull()) {
   1034    return;
   1035  }
   1036 
   1037  // We can't call Families() here because the mFamilyCount field has not yet
   1038  // been set!
   1039  auto* families = header.mFamilies.ToArray<Family>(this, count);
   1040  for (size_t i = 0; i < count; i++) {
   1041    (void)new (&families[i]) Family(this, aFamilies[i]);
   1042    LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i,
   1043                  aFamilies[i].mName.get()));
   1044  }
   1045 
   1046  header.mFamilyCount = count;
   1047 }
   1048 
   1049 void FontList::SetAliases(
   1050    nsClassHashtable<nsCStringHashKey, AliasData>& aAliasTable) {
   1051  MOZ_ASSERT(XRE_IsParentProcess());
   1052 
   1053  Header& header = GetHeader();
   1054 
   1055  // Build an array of Family::InitData records based on the entries in
   1056  // aAliasTable, then sort them and store into the fontlist.
   1057  nsTArray<Family::InitData> aliasArray;
   1058  aliasArray.SetCapacity(aAliasTable.Count());
   1059  for (const auto& entry : aAliasTable) {
   1060    aliasArray.AppendElement(Family::InitData(
   1061        entry.GetKey(), entry.GetData()->mBaseFamily, entry.GetData()->mIndex,
   1062        entry.GetData()->mVisibility, entry.GetData()->mBundled,
   1063        entry.GetData()->mBadUnderline, entry.GetData()->mForceClassic, true));
   1064  }
   1065  aliasArray.Sort();
   1066 
   1067  size_t count = aliasArray.Length();
   1068 
   1069  // Drop any entry with empty family-name as being unusable.
   1070  if (count && aliasArray[0].mKey.IsEmpty()) {
   1071    aliasArray.RemoveElementAt(0);
   1072    --count;
   1073  }
   1074 
   1075  if (count < header.mAliasCount) {
   1076    // This shouldn't happen, but handle it safely by just bailing out.
   1077    NS_WARNING("cannot reduce number of aliases");
   1078    return;
   1079  }
   1080  fontlist::Pointer ptr = Alloc(count * sizeof(Family));
   1081  auto* aliases = ptr.ToArray<Family>(this, count);
   1082  for (size_t i = 0; i < count; i++) {
   1083    (void)new (&aliases[i]) Family(this, aliasArray[i]);
   1084    LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i,
   1085                  aliasArray[i].mKey.get(), aliasArray[i].mName.get()));
   1086    aliases[i].SetFacePtrs(this, aAliasTable.Get(aliasArray[i].mKey)->mFaces);
   1087    if (LOG_FONTLIST_ENABLED()) {
   1088      const auto& faces = aAliasTable.Get(aliasArray[i].mKey)->mFaces;
   1089      for (unsigned j = 0; j < faces.Length(); j++) {
   1090        auto* face = faces[j].ToPtr<const Face>(this);
   1091        const nsCString& desc = face->mDescriptor.AsString(this);
   1092        nsAutoCString weight, style, stretch;
   1093        face->mWeight.ToString(weight);
   1094        face->mStyle.ToString(style);
   1095        face->mStretch.ToString(stretch);
   1096        LOG_FONTLIST(
   1097            ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
   1098             "stretch %s",
   1099             desc.get(), face->mIndex, weight.get(), style.get(),
   1100             stretch.get()));
   1101      }
   1102    }
   1103  }
   1104 
   1105  // Set the pointer before the count, so that any other process trying to read
   1106  // will not risk out-of-bounds access to the array.
   1107  header.mAliases = ptr;
   1108  header.mAliasCount.store(count);
   1109 }
   1110 
   1111 void FontList::SetLocalNames(
   1112    nsTHashMap<nsCStringHashKey, LocalFaceRec::InitData>& aLocalNameTable) {
   1113  MOZ_ASSERT(XRE_IsParentProcess());
   1114  Header& header = GetHeader();
   1115  if (header.mLocalFaceCount > 0) {
   1116    return;  // already been done!
   1117  }
   1118  auto faceArray = ToTArray<nsTArray<nsCString>>(aLocalNameTable.Keys());
   1119  faceArray.Sort();
   1120  size_t count = faceArray.Length();
   1121  Family* families = Families();
   1122  fontlist::Pointer ptr = Alloc(count * sizeof(LocalFaceRec));
   1123  auto* faces = ptr.ToArray<LocalFaceRec>(this, count);
   1124  for (size_t i = 0; i < count; i++) {
   1125    (void)new (&faces[i]) LocalFaceRec();
   1126    const auto& rec = aLocalNameTable.Get(faceArray[i]);
   1127    faces[i].mKey.Assign(faceArray[i], this);
   1128    // Local face name records will refer to the canonical family name; we don't
   1129    // need to search aliases here.
   1130    const auto* family = FindFamily(rec.mFamilyName, /*aPrimaryNameOnly*/ true);
   1131    if (!family) {
   1132      // Skip this record if the family was excluded by the font whitelist pref.
   1133      continue;
   1134    }
   1135    faces[i].mFamilyIndex = family - families;
   1136    if (rec.mFaceIndex == uint32_t(-1)) {
   1137      // The InitData record contains an mFaceDescriptor rather than an index,
   1138      // so now we need to look for the appropriate index in the family.
   1139      faces[i].mFaceIndex = 0;
   1140      const Pointer* faceList =
   1141          static_cast<const Pointer*>(family->Faces(this));
   1142      for (uint32_t j = 0; j < family->NumFaces(); j++) {
   1143        if (!faceList[j].IsNull()) {
   1144          auto* f = faceList[j].ToPtr<const Face>(this);
   1145          if (f && rec.mFaceDescriptor == f->mDescriptor.AsString(this)) {
   1146            faces[i].mFaceIndex = j;
   1147            break;
   1148          }
   1149        }
   1150      }
   1151    } else {
   1152      faces[i].mFaceIndex = rec.mFaceIndex;
   1153    }
   1154  }
   1155  header.mLocalFaces = ptr;
   1156  header.mLocalFaceCount.store(count);
   1157 }
   1158 
   1159 nsCString FontList::LocalizedFamilyName(const Family* aFamily) {
   1160  // If the given family was created for an alternate locale or legacy name,
   1161  // search for a standard family that corresponds to it. This is a linear
   1162  // search of the font list, but (a) this is only used to show names in
   1163  // Preferences, so is not performance-critical for layout etc.; and (b) few
   1164  // such family names are normally present anyway, the vast majority of fonts
   1165  // just have a single family name and we return it directly.
   1166  if (aFamily->IsAltLocaleFamily()) {
   1167    // Currently only the Windows backend actually does this; on other systems,
   1168    // the family index is unused and will be kNoIndex for all fonts.
   1169    if (aFamily->Index() != Family::kNoIndex) {
   1170      const Family* families = Families();
   1171      for (uint32_t i = 0; i < NumFamilies(); ++i) {
   1172        if (families[i].Index() == aFamily->Index() &&
   1173            families[i].IsBundled() == aFamily->IsBundled() &&
   1174            !families[i].IsAltLocaleFamily()) {
   1175          return families[i].DisplayName().AsString(this);
   1176        }
   1177      }
   1178    }
   1179  }
   1180 
   1181  // For standard families (or if we failed to find the expected standard
   1182  // family for some reason), just return the DisplayName.
   1183  return aFamily->DisplayName().AsString(this);
   1184 }
   1185 
   1186 Family* FontList::FindFamily(const nsCString& aName, bool aPrimaryNameOnly) {
   1187  struct FamilyNameComparator {
   1188    FamilyNameComparator(FontList* aList, const nsCString& aTarget)
   1189        : mList(aList), mTarget(aTarget) {}
   1190 
   1191    int operator()(const Family& aVal) const {
   1192      return Compare(mTarget,
   1193                     nsDependentCString(aVal.Key().BeginReading(mList)));
   1194    }
   1195 
   1196   private:
   1197    FontList* mList;
   1198    const nsCString& mTarget;
   1199  };
   1200 
   1201  const Header& header = GetHeader();
   1202 
   1203  Family* families = Families();
   1204  if (!families) {
   1205    return nullptr;
   1206  }
   1207 
   1208  size_t match;
   1209  if (BinarySearchIf(families, 0, header.mFamilyCount,
   1210                     FamilyNameComparator(this, aName), &match)) {
   1211    return &families[match];
   1212  }
   1213 
   1214  if (aPrimaryNameOnly) {
   1215    return nullptr;
   1216  }
   1217 
   1218  if (header.mAliasCount) {
   1219    Family* aliases = AliasFamilies();
   1220    size_t match;
   1221    if (aliases && BinarySearchIf(aliases, 0, header.mAliasCount,
   1222                                  FamilyNameComparator(this, aName), &match)) {
   1223      return &aliases[match];
   1224    }
   1225  }
   1226 
   1227 #ifdef XP_WIN
   1228  // For Windows only, because of how DWrite munges font family names in some
   1229  // cases (see
   1230  // https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/02/24/90/36/WPF%20Font%20Selection%20Model.pdf
   1231  // and discussion on the OpenType list), try stripping a possible style-name
   1232  // suffix from the end of the requested family name.
   1233  // After the deferred font loader has finished, this is no longer needed as
   1234  // the "real" family names will have been found in AliasFamilies() above.
   1235  if (aName.Contains(' ')) {
   1236    auto pfl = gfxPlatformFontList::PlatformFontList();
   1237    pfl->mLock.AssertCurrentThreadIn();
   1238    if (header.mAliasCount) {
   1239      // Aliases have been fully loaded by the parent process, so just discard
   1240      // any stray mAliasTable and mLocalNameTable entries from earlier calls
   1241      // to this code, and return.
   1242      pfl->mAliasTable.Clear();
   1243      pfl->mLocalNameTable.Clear();
   1244      mFaceNamesRead.Clear();
   1245      return nullptr;
   1246    }
   1247 
   1248    // Do we already have an aliasData record for this name? If so, we just
   1249    // return its base family.
   1250    if (auto lookup = pfl->mAliasTable.Lookup(aName)) {
   1251      return FindFamily(lookup.Data()->mBaseFamily, true);
   1252    }
   1253 
   1254    // Strip the style suffix (after last space in the name) to get a "base"
   1255    // family name.
   1256    const char* data = aName.BeginReading();
   1257    int32_t index = aName.Length();
   1258    while (--index > 0) {
   1259      if (data[index] == ' ') {
   1260        break;
   1261      }
   1262    }
   1263    if (index <= 0) {
   1264      return nullptr;
   1265    }
   1266    nsAutoCString base(Substring(aName, 0, index));
   1267    auto familyCount = header.mFamilyCount;
   1268    if (BinarySearchIf(families, 0, familyCount,
   1269                       FamilyNameComparator(this, base), &match)) {
   1270      // Check to see if we have already read the face names for this base
   1271      // family. Note: EnsureLengthAtLeast will default new entries to false.
   1272      mFaceNamesRead.EnsureLengthAtLeast(familyCount);
   1273      if (mFaceNamesRead[match]) {
   1274        return nullptr;
   1275      }
   1276      // This may be a possible base family to satisfy the search; call
   1277      // ReadFaceNamesForFamily and see if the desired name ends up in
   1278      // mAliasTable.
   1279      // Note that ReadFaceNamesForFamily may store entries in mAliasTable
   1280      // (and mLocalNameTable), but if this is happening in a content
   1281      // process (which is the common case) those entries will not be saved
   1282      // into the shared font list; they're just used here until the "real"
   1283      // alias list is ready, then discarded.
   1284      Family* baseFamily = &families[match];
   1285      pfl->ReadFaceNamesForFamily(baseFamily, false);
   1286      mFaceNamesRead[match] = true;
   1287      if (auto lookup = pfl->mAliasTable.Lookup(aName)) {
   1288        if (lookup.Data()->mFaces.Length() != baseFamily->NumFaces()) {
   1289          // If the alias family doesn't have all the faces of the base family,
   1290          // then style matching may end up resolving to a face that isn't
   1291          // supposed to be available in the legacy styled family. To ensure
   1292          // such mis-styling will get fixed, we start the async font info
   1293          // loader (if it hasn't yet been triggered), which will pull in the
   1294          // full metadata we need and then force a reflow.
   1295          pfl->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true);
   1296        }
   1297        return baseFamily;
   1298      }
   1299    }
   1300  }
   1301 #endif
   1302 
   1303  return nullptr;
   1304 }
   1305 
   1306 LocalFaceRec* FontList::FindLocalFace(const nsCString& aName) {
   1307  struct FaceNameComparator {
   1308    FaceNameComparator(FontList* aList, const nsCString& aTarget)
   1309        : mList(aList), mTarget(aTarget) {}
   1310 
   1311    int operator()(const LocalFaceRec& aVal) const {
   1312      return Compare(mTarget,
   1313                     nsDependentCString(aVal.mKey.BeginReading(mList)));
   1314    }
   1315 
   1316   private:
   1317    FontList* mList;
   1318    const nsCString& mTarget;
   1319  };
   1320 
   1321  Header& header = GetHeader();
   1322 
   1323  LocalFaceRec* faces = LocalFaces();
   1324  size_t match;
   1325  if (faces && BinarySearchIf(faces, 0, header.mLocalFaceCount,
   1326                              FaceNameComparator(this, aName), &match)) {
   1327    return &faces[match];
   1328  }
   1329 
   1330  return nullptr;
   1331 }
   1332 
   1333 void FontList::SearchForLocalFace(const nsACString& aName, Family** aFamily,
   1334                                  Face** aFace) {
   1335  Header& header = GetHeader();
   1336  MOZ_ASSERT(header.mLocalFaceCount == 0,
   1337             "do not use when local face names are already set up!");
   1338  LOG_FONTLIST(
   1339      ("(shared-fontlist) local face search for (%s)", aName.BeginReading()));
   1340  char initial = aName[0];
   1341  Family* families = Families();
   1342  if (!families) {
   1343    return;
   1344  }
   1345  for (uint32_t i = 0; i < header.mFamilyCount; i++) {
   1346    Family* family = &families[i];
   1347    if (family->Key().BeginReading(this)[0] != initial) {
   1348      continue;
   1349    }
   1350    LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
   1351                  family->Key().AsString(this).BeginReading()));
   1352    if (!family->IsInitialized()) {
   1353      if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family)) {
   1354        continue;
   1355      }
   1356    }
   1357    Pointer* faces = family->Faces(this);
   1358    if (!faces) {
   1359      continue;
   1360    }
   1361    for (uint32_t j = 0; j < family->NumFaces(); j++) {
   1362      auto* face = faces[j].ToPtr<Face>(this);
   1363      if (!face) {
   1364        continue;
   1365      }
   1366      nsAutoCString psname, fullname;
   1367      if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
   1368              family, face, psname, fullname)) {
   1369        LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
   1370                      psname.get(), fullname.get()));
   1371        ToLowerCase(psname);
   1372        ToLowerCase(fullname);
   1373        if (aName == psname || aName == fullname) {
   1374          *aFamily = family;
   1375          *aFace = face;
   1376          return;
   1377        }
   1378      }
   1379    }
   1380  }
   1381 }
   1382 
   1383 size_t FontList::SizeOfIncludingThis(
   1384    mozilla::MallocSizeOf aMallocSizeOf) const {
   1385  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1386 }
   1387 
   1388 size_t FontList::SizeOfExcludingThis(
   1389    mozilla::MallocSizeOf aMallocSizeOf) const {
   1390  size_t result = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
   1391  for (const auto& b : mBlocks) {
   1392    result += aMallocSizeOf(b.get());
   1393  }
   1394  return result;
   1395 }
   1396 
   1397 size_t FontList::AllocatedShmemSize() const {
   1398  size_t result = 0;
   1399  for (const auto& b : mBlocks) {
   1400    result += b->BlockSize();
   1401  }
   1402  return result;
   1403 }
   1404 
   1405 }  // namespace fontlist
   1406 }  // namespace mozilla