Files
tubestation/gfx/thebes/SharedFontList.cpp
Sandor Molnar abe7f841ca Backed out 4 changesets (bug 1454816) for causing build bustages. CLOSED TREE
Backed out changeset 3e8d2c47138c (bug 1454816)
Backed out changeset 80ff20241831 (bug 1454816)
Backed out changeset 28c2d6d2a683 (bug 1454816)
Backed out changeset 236943ab4142 (bug 1454816)
2024-10-21 20:15:13 +03:00

1422 lines
50 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SharedFontList-impl.h"
#include "gfxPlatformFontList.h"
#include "gfxFontUtils.h"
#include "gfxFont.h"
#include "nsReadableUtils.h"
#include "prerror.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/Logging.h"
#include "mozilla/Unused.h"
#define LOG_FONTLIST(args) \
MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
#define LOG_FONTLIST_ENABLED() \
MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
namespace mozilla {
namespace fontlist {
static double WSSDistance(const Face* aFace, const gfxFontStyle& aStyle) {
double stretchDist = StretchDistance(aFace->mStretch, aStyle.stretch);
double styleDist = StyleDistance(aFace->mStyle, aStyle.style);
double weightDist = WeightDistance(aFace->mWeight, aStyle.weight);
// Sanity-check that the distances are within the expected range
// (update if implementation of the distance functions is changed).
MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
// weight/style/stretch priority: stretch >> style >> weight
// so we multiply the stretch and style values to make them dominate
// the result
return stretchDist * kStretchFactor + styleDist * kStyleFactor +
weightDist * kWeightFactor;
}
void* Pointer::ToPtr(FontList* aFontList,
size_t aSize) const MOZ_NO_THREAD_SAFETY_ANALYSIS {
// On failure, we'll return null; callers need to handle this appropriately
// (e.g. via fallback).
void* result = nullptr;
if (IsNull()) {
return result;
}
// Ensure the list doesn't get replaced out from under us. Font-list rebuild
// happens on the main thread, so only non-main-thread callers need to lock
// it here.
bool isMainThread = NS_IsMainThread();
if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Lock();
}
uint32_t blockIndex = Block();
// If the Pointer refers to a block we have not yet mapped in this process,
// we first need to retrieve new block handle(s) from the parent and update
// our mBlocks list.
auto& blocks = aFontList->mBlocks;
if (blockIndex >= blocks.Length()) {
if (MOZ_UNLIKELY(XRE_IsParentProcess())) {
// Shouldn't happen! A content process tried to pass a bad Pointer?
goto cleanup;
}
// If we're not on the main thread, we can't do the IPC involved in
// UpdateShmBlocks; just let the lookup fail for now.
if (!isMainThread) {
goto cleanup;
}
// UpdateShmBlocks can fail, if the parent has replaced the font list with
// a new generation. In that case we just return null, and whatever font
// the content process was trying to use will appear unusable for now. It's
// about to receive a notification of the new font list anyhow, at which
// point it will flush its caches and reflow everything, so the temporary
// failure of this font will be forgotten.
// UpdateShmBlocks will take the platform font-list lock during the update.
if (MOZ_UNLIKELY(!aFontList->UpdateShmBlocks(true))) {
goto cleanup;
}
MOZ_ASSERT(blockIndex < blocks.Length(), "failure in UpdateShmBlocks?");
// This is wallpapering bug 1667977; it's unclear if we will always survive
// this, as the content process may be unable to shape/render text if all
// font lookups are failing.
// In at least some cases, however, this can occur transiently while the
// font list is being rebuilt by the parent; content will then be notified
// that the list has changed, and should refresh everything successfully.
if (MOZ_UNLIKELY(blockIndex >= blocks.Length())) {
goto cleanup;
}
}
{
// Don't create a pointer that's outside what the block has allocated!
const auto& block = blocks[blockIndex];
if (MOZ_LIKELY(Offset() + aSize <= block->Allocated())) {
result = static_cast<char*>(block->Memory()) + Offset();
}
}
cleanup:
if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Unlock();
}
return result;
}
void String::Assign(const nsACString& aString, FontList* aList) {
// We only assign to previously-empty strings; they are never modified
// after initial assignment.
MOZ_ASSERT(mPointer.IsNull());
mLength = aString.Length();
mPointer = aList->Alloc(mLength + 1);
auto* p = mPointer.ToArray<char>(aList, mLength);
std::memcpy(p, aString.BeginReading(), mLength);
p[mLength] = '\0';
}
Family::Family(FontList* aList, const InitData& aData)
: mFaceCount(0),
mKey(aList, aData.mKey),
mName(aList, aData.mName),
mCharacterMap(Pointer::Null()),
mFaces(Pointer::Null()),
mIndex(aData.mIndex),
mVisibility(aData.mVisibility),
mIsSimple(false),
mIsBundled(aData.mBundled),
mIsBadUnderlineFamily(aData.mBadUnderline),
mIsForceClassic(aData.mForceClassic),
mIsAltLocale(aData.mAltLocale) {}
class SetCharMapRunnable : public mozilla::Runnable {
public:
SetCharMapRunnable(uint32_t aListGeneration,
std::pair<uint32_t, bool> aFamilyIndex,
uint32_t aFaceIndex, gfxCharacterMap* aCharMap)
: Runnable("SetCharMapRunnable"),
mListGeneration(aListGeneration),
mFamilyIndex(aFamilyIndex),
mFaceIndex(aFaceIndex),
mCharMap(aCharMap) {}
NS_IMETHOD Run() override {
auto* list = gfxPlatformFontList::PlatformFontList()->SharedFontList();
if (!list || list->GetGeneration() != mListGeneration) {
return NS_OK;
}
dom::ContentChild::GetSingleton()->SendSetCharacterMap(
mListGeneration, mFamilyIndex.first, mFamilyIndex.second, mFaceIndex,
*mCharMap);
return NS_OK;
}
private:
uint32_t mListGeneration;
std::pair<uint32_t, bool> mFamilyIndex;
uint32_t mFaceIndex;
RefPtr<gfxCharacterMap> mCharMap;
};
void Face::SetCharacterMap(FontList* aList, gfxCharacterMap* aCharMap,
const Family* aFamily) {
if (!XRE_IsParentProcess()) {
Maybe<std::pair<uint32_t, bool>> familyIndex = aFamily->FindIndex(aList);
if (!familyIndex) {
NS_WARNING("Family index not found! Ignoring SetCharacterMap");
return;
}
const auto* faces = aFamily->Faces(aList);
uint32_t faceIndex = 0;
while (faceIndex < aFamily->NumFaces()) {
if (faces[faceIndex].ToPtr<Face>(aList) == this) {
break;
}
++faceIndex;
}
if (faceIndex >= aFamily->NumFaces()) {
NS_WARNING("Face not found in family! Ignoring SetCharacterMap");
return;
}
if (NS_IsMainThread()) {
dom::ContentChild::GetSingleton()->SendSetCharacterMap(
aList->GetGeneration(), familyIndex->first, familyIndex->second,
faceIndex, *aCharMap);
} else {
NS_DispatchToMainThread(new SetCharMapRunnable(
aList->GetGeneration(), familyIndex.value(), faceIndex, aCharMap));
}
return;
}
auto pfl = gfxPlatformFontList::PlatformFontList();
mCharacterMap = pfl->GetShmemCharMap(aCharMap);
}
void Family::AddFaces(FontList* aList, const nsTArray<Face::InitData>& aFaces) {
MOZ_ASSERT(XRE_IsParentProcess());
if (mFaceCount > 0) {
// Already initialized!
return;
}
uint32_t count = aFaces.Length();
bool isSimple = false;
// A family is "simple" (i.e. simplified style selection may be used instead
// of the full CSS font-matching algorithm) if there is at maximum one normal,
// bold, italic, and bold-italic face; in this case, they are stored at known
// positions in the mFaces array.
const Face::InitData* slots[4] = {nullptr, nullptr, nullptr, nullptr};
if (count >= 2 && count <= 4) {
// Check if this can be treated as a "simple" family
isSimple = true;
for (const auto& f : aFaces) {
if (!f.mWeight.IsSingle() || !f.mStretch.IsSingle() ||
!f.mStyle.IsSingle()) {
isSimple = false;
break;
}
if (!f.mStretch.Min().IsNormal()) {
isSimple = false;
break;
}
// Figure out which slot (0-3) this face belongs in
size_t slot = 0;
static_assert((kBoldMask | kItalicMask) == 0b11, "bad bold/italic bits");
if (f.mWeight.Min().IsBold()) {
slot |= kBoldMask;
}
if (f.mStyle.Min().IsItalic() || f.mStyle.Min().IsOblique()) {
slot |= kItalicMask;
}
if (slots[slot]) {
// More than one face mapped to the same slot - not a simple family!
isSimple = false;
break;
}
slots[slot] = &f;
}
if (isSimple) {
// Ensure all 4 slots will exist, even if some are empty.
count = 4;
}
}
// Allocate space for the face records, and initialize them.
// coverity[suspicious_sizeof]
Pointer p = aList->Alloc(count * sizeof(Pointer));
auto* facePtrs = p.ToArray<Pointer>(aList, count);
for (size_t i = 0; i < count; i++) {
if (isSimple && !slots[i]) {
facePtrs[i] = Pointer::Null();
} else {
const auto* initData = isSimple ? slots[i] : &aFaces[i];
Pointer fp = aList->Alloc(sizeof(Face));
auto* face = fp.ToPtr<Face>(aList);
(void)new (face) Face(aList, *initData);
facePtrs[i] = fp;
if (initData->mCharMap) {
face->SetCharacterMap(aList, initData->mCharMap, this);
}
}
}
mIsSimple = isSimple;
mFaces = p;
mFaceCount.store(count);
if (LOG_FONTLIST_ENABLED()) {
const nsCString& fam = DisplayName().AsString(aList);
for (unsigned j = 0; j < aFaces.Length(); j++) {
nsAutoCString weight, style, stretch;
aFaces[j].mWeight.ToString(weight);
aFaces[j].mStyle.ToString(style);
aFaces[j].mStretch.ToString(stretch);
LOG_FONTLIST(
("(shared-fontlist) family (%s) added face (%s) index %u, weight "
"%s, style %s, stretch %s",
fam.get(), aFaces[j].mDescriptor.get(), aFaces[j].mIndex,
weight.get(), style.get(), stretch.get()));
}
}
}
bool Family::FindAllFacesForStyleInternal(FontList* aList,
const gfxFontStyle& aStyle,
nsTArray<Face*>& aFaceList) const {
MOZ_ASSERT(aFaceList.IsEmpty());
if (!IsInitialized()) {
return false;
}
Pointer* facePtrs = Faces(aList);
if (!facePtrs) {
return false;
}
// Depending on the kind of family, we have to do varying amounts of work
// to figure out what face(s) to use for the requested style properties.
// If the family has only one face, we simply use it; no further style
// checking needed. (However, for bitmap fonts we may still need to check
// whether the size is acceptable.)
if (NumFaces() == 1) {
MOZ_ASSERT(!facePtrs[0].IsNull());
auto* face = facePtrs[0].ToPtr<Face>(aList);
if (face && face->HasValidDescriptor()) {
aFaceList.AppendElement(face);
#ifdef MOZ_WIDGET_GTK
if (face->mSize) {
return true;
}
#endif
}
return false;
}
// Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
// or some subset of these. In this case, we have exactly 4 entries in
// mAvailableFonts, stored in the above order; note that some of the entries
// may be nullptr. We can then pick the required entry based on whether the
// request is for bold or non-bold, italic or non-italic, without running
// the more complex matching algorithm used for larger families with many
// weights and/or widths.
if (mIsSimple) {
// Family has no more than the "standard" 4 faces, at fixed indexes;
// calculate which one we want.
// Note that we cannot simply return it as not all 4 faces are necessarily
// present.
bool wantBold = aStyle.weight.IsBold();
bool wantItalic = !aStyle.style.IsNormal();
uint8_t faceIndex =
(wantItalic ? kItalicMask : 0) | (wantBold ? kBoldMask : 0);
// If the desired style is available, use it directly.
auto* face = facePtrs[faceIndex].ToPtr<Face>(aList);
if (face && face->HasValidDescriptor()) {
aFaceList.AppendElement(face);
#ifdef MOZ_WIDGET_GTK
if (face->mSize) {
return true;
}
#endif
return false;
}
// Order to check fallback faces in a simple family, depending on the
// requested style.
static const uint8_t simpleFallbacks[4][3] = {
{kBoldFaceIndex, kItalicFaceIndex,
kBoldItalicFaceIndex}, // fallback sequence for Regular
{kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex}, // Bold
{kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex}, // Italic
{kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex} // BoldItalic
};
const uint8_t* order = simpleFallbacks[faceIndex];
for (uint8_t trial = 0; trial < 3; ++trial) {
// check remaining faces in order of preference to find the first that
// actually exists
face = facePtrs[order[trial]].ToPtr<Face>(aList);
if (face && face->HasValidDescriptor()) {
aFaceList.AppendElement(face);
#ifdef MOZ_WIDGET_GTK
if (face->mSize) {
return true;
}
#endif
return false;
}
}
// We can only reach here if we failed to resolve the face pointer, which
// can happen if we're on a stylo thread and caught the font list being
// updated; in that case we just fail quietly and let font fallback do
// something for the time being.
return false;
}
// Pick the font(s) that are closest to the desired weight, style, and
// stretch. Iterate over all fonts, measuring the weight/style distance.
// Because of unicode-range values, there may be more than one font for a
// given but the 99% use case is only a single font entry per
// weight/style/stretch distance value. To optimize this, only add entries
// to the matched font array when another entry already has the same
// weight/style/stretch distance and add the last matched font entry. For
// normal platform fonts with a single font entry for each
// weight/style/stretch combination, only the last matched font entry will
// be added.
double minDistance = INFINITY;
Face* matched = nullptr;
// Keep track of whether we've included any non-scalable font resources in
// the selected set.
bool anyNonScalable = false;
for (uint32_t i = 0; i < NumFaces(); i++) {
auto* face = facePtrs[i].ToPtr<Face>(aList);
if (face) {
// weight/style/stretch priority: stretch >> style >> weight
double distance = WSSDistance(face, aStyle);
if (distance < minDistance) {
matched = face;
if (!aFaceList.IsEmpty()) {
aFaceList.Clear();
}
minDistance = distance;
} else if (distance == minDistance) {
if (matched) {
aFaceList.AppendElement(matched);
#ifdef MOZ_WIDGET_GTK
if (matched->mSize) {
anyNonScalable = true;
}
#endif
}
matched = face;
}
}
}
MOZ_ASSERT(matched, "didn't match a font within a family");
if (matched) {
aFaceList.AppendElement(matched);
#ifdef MOZ_WIDGET_GTK
if (matched->mSize) {
anyNonScalable = true;
}
#endif
}
return anyNonScalable;
}
void Family::FindAllFacesForStyle(FontList* aList, const gfxFontStyle& aStyle,
nsTArray<Face*>& aFaceList,
bool aIgnoreSizeTolerance) const {
#ifdef MOZ_WIDGET_GTK
bool anyNonScalable =
#else
Unused <<
#endif
FindAllFacesForStyleInternal(aList, aStyle, aFaceList);
#ifdef MOZ_WIDGET_GTK
// aFaceList now contains whatever faces are the best style match for
// the requested style. If specifically-sized bitmap faces are supported,
// we need to additionally filter the list to choose the appropriate size.
//
// It would be slightly more efficient to integrate this directly into the
// face-selection algorithm above, but it's a rare case that doesn't apply
// at all to most font families.
//
// Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e.
// when using the gfxFcPlatformFontList implementation), so this filtering is
// not needed on other platforms.
//
// (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color
// Emoji don't count here; they package multiple bitmap sizes into a single
// OpenType wrapper, so they appear as a single "scalable" face in our list.)
if (anyNonScalable) {
uint16_t best = 0;
gfxFloat dist = 0.0;
for (const auto& f : aFaceList) {
if (f->mSize == 0) {
// Scalable face; no size distance to compute.
continue;
}
gfxFloat d = fabs(gfxFloat(f->mSize) - aStyle.size);
if (!aIgnoreSizeTolerance && (d * 5.0 > f->mSize)) {
continue; // Too far from the requested size, ignore.
}
// If we haven't found a "best" bitmap size yet, or if this is a better
// match, remember it.
if (!best || d < dist) {
best = f->mSize;
dist = d;
}
}
// Discard all faces except the chosen "best" size; or if no pixel size was
// chosen, all except scalable faces.
// This may eliminate *all* faces in the family, if all were bitmaps and
// none was a good enough size match, in which case we'll fall back to the
// next font-family name.
aFaceList.RemoveElementsBy([=](const auto& e) { return e->mSize != best; });
}
#endif
}
Face* Family::FindFaceForStyle(FontList* aList, const gfxFontStyle& aStyle,
bool aIgnoreSizeTolerance) const {
AutoTArray<Face*, 4> faces;
FindAllFacesForStyle(aList, aStyle, faces, aIgnoreSizeTolerance);
return faces.IsEmpty() ? nullptr : faces[0];
}
void Family::SearchAllFontsForChar(FontList* aList,
GlobalFontMatch* aMatchData) {
auto* charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList);
if (!charmap) {
// If the face list is not yet initialized, or if character maps have
// not been loaded, go ahead and do this now (by sending a message to the
// parent process, if we're running in a child).
// After this, all faces should have their mCharacterMap set up, and the
// family's mCharacterMap should also be set; but in the code below we
// don't assume this all succeeded, so it still checks.
if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this,
true)) {
return;
}
charmap = mCharacterMap.ToPtr<const SharedBitSet>(aList);
}
if (charmap && !charmap->test(aMatchData->mCh)) {
return;
}
uint32_t numFaces = NumFaces();
uint32_t charMapsLoaded = 0; // number of faces whose charmap is loaded
Pointer* facePtrs = Faces(aList);
if (!facePtrs) {
return;
}
for (uint32_t i = 0; i < numFaces; i++) {
auto* face = facePtrs[i].ToPtr<Face>(aList);
if (!face) {
continue;
}
MOZ_ASSERT(face->HasValidDescriptor());
// Get the face's character map, if available (may be null!)
charmap = face->mCharacterMap.ToPtr<const SharedBitSet>(aList);
if (charmap) {
++charMapsLoaded;
}
// Check style distance if the char is supported, or if charmap not known
// (so that we don't trigger cmap-loading for faces that would be a worse
// match than what we've already found).
if (!charmap || charmap->test(aMatchData->mCh)) {
double distance = WSSDistance(face, aMatchData->mStyle);
if (distance < aMatchData->mMatchDistance) {
// It's a better style match: get a fontEntry, and if we haven't
// already checked character coverage, do it now (note that
// HasCharacter() will trigger loading the fontEntry's cmap, if
// needed).
RefPtr<gfxFontEntry> fe =
gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face,
this);
if (!fe) {
continue;
}
if (!charmap && !fe->HasCharacter(aMatchData->mCh)) {
continue;
}
if (aMatchData->mPresentation != eFontPresentation::Any) {
RefPtr<gfxFont> font = fe->FindOrMakeFont(&aMatchData->mStyle);
if (!font) {
continue;
}
bool hasColorGlyph =
font->HasColorGlyphFor(aMatchData->mCh, aMatchData->mNextCh);
if (hasColorGlyph != PrefersColor(aMatchData->mPresentation)) {
distance += kPresentationMismatch;
if (distance >= aMatchData->mMatchDistance) {
continue;
}
}
}
aMatchData->mBestMatch = fe;
aMatchData->mMatchDistance = distance;
aMatchData->mMatchedSharedFamily = this;
}
}
}
if (mCharacterMap.IsNull() && charMapsLoaded == numFaces) {
SetupFamilyCharMap(aList);
}
}
void Family::SetFacePtrs(FontList* aList, nsTArray<Pointer>& aFaces) {
if (aFaces.Length() >= 2 && aFaces.Length() <= 4) {
// Check whether the faces meet the criteria for a "simple" family: no more
// than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
// them at the appropriate slots in mFaces and set the mIsSimple flag to
// accelerate font-matching.
bool isSimple = true;
Pointer slots[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
Pointer::Null()};
for (const Pointer& fp : aFaces) {
auto* f = fp.ToPtr<const Face>(aList);
if (!f->mWeight.IsSingle() || !f->mStyle.IsSingle() ||
!f->mStretch.IsSingle()) {
isSimple = false;
break;
}
if (!f->mStretch.Min().IsNormal()) {
isSimple = false;
break;
}
size_t slot = 0;
if (f->mWeight.Min().IsBold()) {
slot |= kBoldMask;
}
if (f->mStyle.Min().IsItalic() || f->mStyle.Min().IsOblique()) {
slot |= kItalicMask;
}
if (!slots[slot].IsNull()) {
isSimple = false;
break;
}
slots[slot] = fp;
}
if (isSimple) {
size_t size = 4 * sizeof(Pointer);
mFaces = aList->Alloc(size);
memcpy(mFaces.ToPtr(aList, size), slots, size);
mFaceCount.store(4);
mIsSimple = true;
return;
}
}
size_t size = aFaces.Length() * sizeof(Pointer);
mFaces = aList->Alloc(size);
memcpy(mFaces.ToPtr(aList, size), aFaces.Elements(), size);
mFaceCount.store(aFaces.Length());
}
void Family::SetupFamilyCharMap(FontList* aList) {
// Set the character map of the family to the union of all the face cmaps,
// to allow font fallback searches to more rapidly reject the family.
if (!mCharacterMap.IsNull()) {
return;
}
if (!XRE_IsParentProcess()) {
// |this| could be a Family record in either the Families() or Aliases()
// arrays; FindIndex will map it back to its index and which array.
Maybe<std::pair<uint32_t, bool>> index = FindIndex(aList);
if (!index) {
NS_WARNING("Family index not found! Ignoring SetupFamilyCharMap");
return;
}
if (NS_IsMainThread()) {
dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
aList->GetGeneration(), index->first, index->second);
return;
}
NS_DispatchToMainThread(NS_NewRunnableFunction(
"SetupFamilyCharMap callback",
[gen = aList->GetGeneration(), idx = index->first,
alias = index->second] {
dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(gen, idx,
alias);
}));
return;
}
gfxSparseBitSet familyMap;
Pointer firstMapShmPointer;
const SharedBitSet* firstMap = nullptr;
bool merged = false;
Pointer* faces = Faces(aList);
if (!faces) {
return;
}
for (size_t i = 0; i < NumFaces(); i++) {
auto* f = faces[i].ToPtr<const Face>(aList);
if (!f) {
continue; // Skip missing face (in an incomplete "simple" family)
}
auto* faceMap = f->mCharacterMap.ToPtr<const SharedBitSet>(aList);
if (!faceMap) {
continue; // If there's a face where setting up the cmap failed, we skip
// it as unusable.
}
if (!firstMap) {
firstMap = faceMap;
firstMapShmPointer = f->mCharacterMap;
} else if (faceMap != firstMap) {
if (!merged) {
familyMap.Union(*firstMap);
merged = true;
}
familyMap.Union(*faceMap);
}
}
// If we created a merged cmap, we need to save that on the family; or if we
// found no usable cmaps at all, we need to store the empty familyMap so that
// we won't repeatedly attempt this for an unusable family.
if (merged || firstMapShmPointer.IsNull()) {
mCharacterMap =
gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap);
} else {
// If all [usable] faces had the same cmap, we can just share it.
mCharacterMap = firstMapShmPointer;
}
}
Maybe<std::pair<uint32_t, bool>> Family::FindIndex(FontList* aList) const {
const auto* start = aList->Families();
const auto* end = start + aList->NumFamilies();
if (this >= start && this < end) {
uint32_t index = this - start;
MOZ_RELEASE_ASSERT(start + index == this, "misaligned Family ptr!");
return Some(std::pair(index, false));
}
start = aList->AliasFamilies();
end = start + aList->NumAliases();
if (this >= start && this < end) {
uint32_t index = this - start;
MOZ_RELEASE_ASSERT(start + index == this, "misaligned AliasFamily ptr!");
return Some(std::pair(index, true));
}
return Nothing();
}
FontList::FontList(uint32_t aGeneration) {
if (XRE_IsParentProcess()) {
// Create the initial shared block, and initialize Header
if (AppendShmBlock(SHM_BLOCK_SIZE)) {
Header& header = GetHeader();
header.mBlockHeader.mAllocated.store(sizeof(Header));
header.mGeneration = aGeneration;
header.mFamilyCount = 0;
header.mBlockCount.store(1);
header.mAliasCount.store(0);
header.mLocalFaceCount.store(0);
header.mFamilies = Pointer::Null();
header.mAliases = Pointer::Null();
header.mLocalFaces = Pointer::Null();
} else {
MOZ_CRASH("parent: failed to initialize FontList");
}
} else {
// Initialize using the list of shmem blocks passed by the parent via
// SetXPCOMProcessAttributes.
auto& blocks = dom::ContentChild::GetSingleton()->SharedFontListBlocks();
for (auto& handle : blocks) {
auto newShm = MakeUnique<base::SharedMemory>();
if (!newShm->IsHandleValid(handle)) {
// Bail out and let UpdateShmBlocks try to do its thing below.
break;
}
if (!newShm->SetHandle(std::move(handle), true)) {
MOZ_CRASH("failed to set shm handle");
}
if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
}
uint32_t size = static_cast<BlockHeader*>(newShm->memory())->mBlockSize;
MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
if (size != SHM_BLOCK_SIZE) {
newShm->Unmap();
if (!newShm->Map(size) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
}
}
mBlocks.AppendElement(new ShmBlock(std::move(newShm)));
}
blocks.Clear();
// Update in case of any changes since the initial message was sent.
for (unsigned retryCount = 0; retryCount < 3; ++retryCount) {
if (UpdateShmBlocks(false)) {
return;
}
// The only reason for UpdateShmBlocks to fail is if the parent recreated
// the list after we read its first block, but before we finished getting
// them all, and so the generation check failed on a subsequent request.
// Discarding whatever we've got and retrying should get us a new,
// consistent set of memory blocks in this case. If this doesn't work
// after a couple of retries, bail out.
DetachShmBlocks();
}
NS_WARNING("child: failed to initialize shared FontList");
}
}
FontList::~FontList() { DetachShmBlocks(); }
FontList::Header& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS {
// We only need to lock if we're not on the main thread.
bool isMainThread = NS_IsMainThread();
if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Lock();
}
// It's invalid to try and access this before the first block exists.
MOZ_ASSERT(mBlocks.Length() > 0);
auto& result = *static_cast<Header*>(mBlocks[0]->Memory());
if (!isMainThread) {
gfxPlatformFontList::PlatformFontList()->Unlock();
}
return result;
}
bool FontList::AppendShmBlock(uint32_t aSizeNeeded) {
MOZ_ASSERT(XRE_IsParentProcess());
uint32_t size = std::max(aSizeNeeded, SHM_BLOCK_SIZE);
auto newShm = MakeUnique<base::SharedMemory>();
if (!newShm->CreateFreezeable(size)) {
MOZ_CRASH("failed to create shared memory");
return false;
}
if (!newShm->Map(size) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
return false;
}
auto readOnly = MakeUnique<base::SharedMemory>();
if (!newShm->ReadOnlyCopy(readOnly.get())) {
MOZ_CRASH("failed to create read-only copy");
return false;
}
ShmBlock* block = new ShmBlock(std::move(newShm));
block->StoreAllocated(sizeof(BlockHeader));
block->BlockSize() = size;
mBlocks.AppendElement(block);
GetHeader().mBlockCount.store(mBlocks.Length());
mReadOnlyShmems.AppendElement(std::move(readOnly));
// We don't need to broadcast the addition of the initial block,
// because child processes can't have initialized their list at all
// prior to the first block being set up.
if (mBlocks.Length() > 1) {
if (NS_IsMainThread()) {
dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(),
mBlocks.Length() - 1);
} else {
NS_DispatchToMainThread(NS_NewRunnableFunction(
"ShmBlockAdded callback",
[generation = GetGeneration(), index = mBlocks.Length() - 1] {
dom::ContentParent::BroadcastShmBlockAdded(generation, index);
}));
}
}
return true;
}
void FontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
base::SharedMemoryHandle aHandle) {
MOZ_ASSERT(!XRE_IsParentProcess());
MOZ_ASSERT(mBlocks.Length() > 0);
auto newShm = MakeUnique<base::SharedMemory>();
if (!newShm->IsHandleValid(aHandle)) {
return;
}
if (!newShm->SetHandle(std::move(aHandle), true)) {
MOZ_CRASH("failed to set shm handle");
}
if (aIndex != mBlocks.Length()) {
return;
}
if (aGeneration != GetGeneration()) {
return;
}
if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
}
uint32_t size = static_cast<BlockHeader*>(newShm->memory())->mBlockSize;
MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
if (size != SHM_BLOCK_SIZE) {
newShm->Unmap();
if (!newShm->Map(size) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
}
}
mBlocks.AppendElement(new ShmBlock(std::move(newShm)));
}
void FontList::DetachShmBlocks() {
for (auto& i : mBlocks) {
i->mShmem = nullptr;
}
mBlocks.Clear();
mReadOnlyShmems.Clear();
}
FontList::ShmBlock* FontList::GetBlockFromParent(uint32_t aIndex) {
MOZ_ASSERT(!XRE_IsParentProcess());
// If we have no existing blocks, we don't want a generation check yet;
// the header in the first block will define the generation of this list
uint32_t generation = aIndex == 0 ? 0 : GetGeneration();
base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
generation, aIndex, &handle)) {
return nullptr;
}
auto newShm = MakeUnique<base::SharedMemory>();
if (!newShm->IsHandleValid(handle)) {
return nullptr;
}
if (!newShm->SetHandle(std::move(handle), true)) {
MOZ_CRASH("failed to set shm handle");
}
if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
}
uint32_t size = static_cast<BlockHeader*>(newShm->memory())->mBlockSize;
MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
if (size != SHM_BLOCK_SIZE) {
newShm->Unmap();
if (!newShm->Map(size) || !newShm->memory()) {
MOZ_CRASH("failed to map shared memory");
}
}
return new ShmBlock(std::move(newShm));
}
// We don't take the lock when called from the constructor, so disable thread-
// safety analysis here.
bool FontList::UpdateShmBlocks(bool aMustLock) MOZ_NO_THREAD_SAFETY_ANALYSIS {
MOZ_ASSERT(!XRE_IsParentProcess());
if (aMustLock) {
gfxPlatformFontList::PlatformFontList()->Lock();
}
bool result = true;
while (!mBlocks.Length() || mBlocks.Length() < GetHeader().mBlockCount) {
ShmBlock* newBlock = GetBlockFromParent(mBlocks.Length());
if (!newBlock) {
result = false;
break;
}
mBlocks.AppendElement(newBlock);
}
if (aMustLock) {
gfxPlatformFontList::PlatformFontList()->Unlock();
}
return result;
}
void FontList::ShareBlocksToProcess(nsTArray<base::SharedMemoryHandle>* aBlocks,
base::ProcessId aPid) {
MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
for (auto& shmem : mReadOnlyShmems) {
auto handle = shmem->CloneHandle();
if (!handle) {
// If something went wrong here, we just bail out; the child will need to
// request the blocks as needed, at some performance cost. (Although in
// practice this may mean resources are so constrained the child process
// isn't really going to work at all. But that's not our problem here.)
aBlocks->Clear();
return;
}
aBlocks->AppendElement(std::move(handle));
}
}
base::SharedMemoryHandle FontList::ShareBlockToProcess(uint32_t aIndex,
base::ProcessId aPid) {
MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
MOZ_RELEASE_ASSERT(aIndex < mReadOnlyShmems.Length());
return mReadOnlyShmems[aIndex]->CloneHandle();
}
Pointer FontList::Alloc(uint32_t aSize) {
// Only the parent process does allocation.
MOZ_ASSERT(XRE_IsParentProcess());
// 4-byte alignment is good enough for anything we allocate in the font list,
// as our "Pointer" (block index/offset) is a 32-bit value even on x64.
auto align = [](uint32_t aSize) -> size_t { return (aSize + 3u) & ~3u; };
aSize = align(aSize);
int32_t blockIndex = -1;
uint32_t curAlloc, size;
if (aSize < SHM_BLOCK_SIZE - sizeof(BlockHeader)) {
// Try to allocate in the most recently added block first, as this is
// highly likely to succeed; if not, try earlier blocks (to fill gaps).
const int32_t blockCount = mBlocks.Length();
for (blockIndex = blockCount - 1; blockIndex >= 0; --blockIndex) {
size = mBlocks[blockIndex]->BlockSize();
curAlloc = mBlocks[blockIndex]->Allocated();
if (size - curAlloc >= aSize) {
break;
}
}
}
if (blockIndex < 0) {
// Couldn't find enough space (or the requested size is too large to use
// a part of a block): create a new block.
if (!AppendShmBlock(aSize + sizeof(BlockHeader))) {
return Pointer::Null();
}
blockIndex = mBlocks.Length() - 1;
curAlloc = mBlocks[blockIndex]->Allocated();
}
// We've found a block; allocate space from it, and return
mBlocks[blockIndex]->StoreAllocated(curAlloc + aSize);
return Pointer(blockIndex, curAlloc);
}
void FontList::SetFamilyNames(nsTArray<Family::InitData>& aFamilies) {
// Only the parent process should ever assign the list of families.
MOZ_ASSERT(XRE_IsParentProcess());
Header& header = GetHeader();
MOZ_ASSERT(!header.mFamilyCount);
gfxPlatformFontList::PlatformFontList()->ApplyWhitelist(aFamilies);
aFamilies.Sort();
size_t count = aFamilies.Length();
// Any font resources with an empty family-name will have been collected in
// a family with empty name, and sorted to the start of the list. Such fonts
// are not generally usable, or recognized as "installed", so we drop them.
if (count > 1 && aFamilies[0].mKey.IsEmpty()) {
aFamilies.RemoveElementAt(0);
--count;
}
// Check for duplicate family entries (can occur if there is a bundled font
// that has the same name as a system-installed one); in this case we keep
// the bundled one as it will always be exposed.
if (count > 1) {
for (size_t i = 1; i < count; ++i) {
if (aFamilies[i].mKey.Equals(aFamilies[i - 1].mKey)) {
// Decide whether to discard the current entry or the preceding one
size_t discard =
aFamilies[i].mBundled && !aFamilies[i - 1].mBundled ? i - 1 : i;
aFamilies.RemoveElementAt(discard);
--count;
--i;
}
}
}
header.mFamilies = Alloc(count * sizeof(Family));
if (header.mFamilies.IsNull()) {
return;
}
// We can't call Families() here because the mFamilyCount field has not yet
// been set!
auto* families = header.mFamilies.ToArray<Family>(this, count);
for (size_t i = 0; i < count; i++) {
(void)new (&families[i]) Family(this, aFamilies[i]);
LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i,
aFamilies[i].mName.get()));
}
header.mFamilyCount = count;
}
void FontList::SetAliases(
nsClassHashtable<nsCStringHashKey, AliasData>& aAliasTable) {
MOZ_ASSERT(XRE_IsParentProcess());
Header& header = GetHeader();
// Build an array of Family::InitData records based on the entries in
// aAliasTable, then sort them and store into the fontlist.
nsTArray<Family::InitData> aliasArray;
aliasArray.SetCapacity(aAliasTable.Count());
for (const auto& entry : aAliasTable) {
aliasArray.AppendElement(Family::InitData(
entry.GetKey(), entry.GetData()->mBaseFamily, entry.GetData()->mIndex,
entry.GetData()->mVisibility, entry.GetData()->mBundled,
entry.GetData()->mBadUnderline, entry.GetData()->mForceClassic, true));
}
aliasArray.Sort();
size_t count = aliasArray.Length();
// Drop any entry with empty family-name as being unusable.
if (count && aliasArray[0].mKey.IsEmpty()) {
aliasArray.RemoveElementAt(0);
--count;
}
if (count < header.mAliasCount) {
// This shouldn't happen, but handle it safely by just bailing out.
NS_WARNING("cannot reduce number of aliases");
return;
}
fontlist::Pointer ptr = Alloc(count * sizeof(Family));
auto* aliases = ptr.ToArray<Family>(this, count);
for (size_t i = 0; i < count; i++) {
(void)new (&aliases[i]) Family(this, aliasArray[i]);
LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i,
aliasArray[i].mKey.get(), aliasArray[i].mName.get()));
aliases[i].SetFacePtrs(this, aAliasTable.Get(aliasArray[i].mKey)->mFaces);
if (LOG_FONTLIST_ENABLED()) {
const auto& faces = aAliasTable.Get(aliasArray[i].mKey)->mFaces;
for (unsigned j = 0; j < faces.Length(); j++) {
auto* face = faces[j].ToPtr<const Face>(this);
const nsCString& desc = face->mDescriptor.AsString(this);
nsAutoCString weight, style, stretch;
face->mWeight.ToString(weight);
face->mStyle.ToString(style);
face->mStretch.ToString(stretch);
LOG_FONTLIST(
("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
"stretch %s",
desc.get(), face->mIndex, weight.get(), style.get(),
stretch.get()));
}
}
}
// Set the pointer before the count, so that any other process trying to read
// will not risk out-of-bounds access to the array.
header.mAliases = ptr;
header.mAliasCount.store(count);
}
void FontList::SetLocalNames(
nsTHashMap<nsCStringHashKey, LocalFaceRec::InitData>& aLocalNameTable) {
MOZ_ASSERT(XRE_IsParentProcess());
Header& header = GetHeader();
if (header.mLocalFaceCount > 0) {
return; // already been done!
}
auto faceArray = ToTArray<nsTArray<nsCString>>(aLocalNameTable.Keys());
faceArray.Sort();
size_t count = faceArray.Length();
Family* families = Families();
fontlist::Pointer ptr = Alloc(count * sizeof(LocalFaceRec));
auto* faces = ptr.ToArray<LocalFaceRec>(this, count);
for (size_t i = 0; i < count; i++) {
(void)new (&faces[i]) LocalFaceRec();
const auto& rec = aLocalNameTable.Get(faceArray[i]);
faces[i].mKey.Assign(faceArray[i], this);
// Local face name records will refer to the canonical family name; we don't
// need to search aliases here.
const auto* family = FindFamily(rec.mFamilyName, /*aPrimaryNameOnly*/ true);
if (!family) {
// Skip this record if the family was excluded by the font whitelist pref.
continue;
}
faces[i].mFamilyIndex = family - families;
if (rec.mFaceIndex == uint32_t(-1)) {
// The InitData record contains an mFaceDescriptor rather than an index,
// so now we need to look for the appropriate index in the family.
faces[i].mFaceIndex = 0;
const Pointer* faceList =
static_cast<const Pointer*>(family->Faces(this));
for (uint32_t j = 0; j < family->NumFaces(); j++) {
if (!faceList[j].IsNull()) {
auto* f = faceList[j].ToPtr<const Face>(this);
if (f && rec.mFaceDescriptor == f->mDescriptor.AsString(this)) {
faces[i].mFaceIndex = j;
break;
}
}
}
} else {
faces[i].mFaceIndex = rec.mFaceIndex;
}
}
header.mLocalFaces = ptr;
header.mLocalFaceCount.store(count);
}
nsCString FontList::LocalizedFamilyName(const Family* aFamily) {
// If the given family was created for an alternate locale or legacy name,
// search for a standard family that corresponds to it. This is a linear
// search of the font list, but (a) this is only used to show names in
// Preferences, so is not performance-critical for layout etc.; and (b) few
// such family names are normally present anyway, the vast majority of fonts
// just have a single family name and we return it directly.
if (aFamily->IsAltLocaleFamily()) {
// Currently only the Windows backend actually does this; on other systems,
// the family index is unused and will be kNoIndex for all fonts.
if (aFamily->Index() != Family::kNoIndex) {
const Family* families = Families();
for (uint32_t i = 0; i < NumFamilies(); ++i) {
if (families[i].Index() == aFamily->Index() &&
families[i].IsBundled() == aFamily->IsBundled() &&
!families[i].IsAltLocaleFamily()) {
return families[i].DisplayName().AsString(this);
}
}
}
}
// For standard families (or if we failed to find the expected standard
// family for some reason), just return the DisplayName.
return aFamily->DisplayName().AsString(this);
}
Family* FontList::FindFamily(const nsCString& aName, bool aPrimaryNameOnly) {
struct FamilyNameComparator {
FamilyNameComparator(FontList* aList, const nsCString& aTarget)
: mList(aList), mTarget(aTarget) {}
int operator()(const Family& aVal) const {
return Compare(mTarget,
nsDependentCString(aVal.Key().BeginReading(mList)));
}
private:
FontList* mList;
const nsCString& mTarget;
};
const Header& header = GetHeader();
Family* families = Families();
if (!families) {
return nullptr;
}
size_t match;
if (BinarySearchIf(families, 0, header.mFamilyCount,
FamilyNameComparator(this, aName), &match)) {
return &families[match];
}
if (aPrimaryNameOnly) {
return nullptr;
}
if (header.mAliasCount) {
Family* aliases = AliasFamilies();
size_t match;
if (aliases && BinarySearchIf(aliases, 0, header.mAliasCount,
FamilyNameComparator(this, aName), &match)) {
return &aliases[match];
}
}
#ifdef XP_WIN
// For Windows only, because of how DWrite munges font family names in some
// cases (see
// 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
// and discussion on the OpenType list), try stripping a possible style-name
// suffix from the end of the requested family name.
// After the deferred font loader has finished, this is no longer needed as
// the "real" family names will have been found in AliasFamilies() above.
if (aName.Contains(' ')) {
auto pfl = gfxPlatformFontList::PlatformFontList();
pfl->mLock.AssertCurrentThreadIn();
if (header.mAliasCount) {
// Aliases have been fully loaded by the parent process, so just discard
// any stray mAliasTable and mLocalNameTable entries from earlier calls
// to this code, and return.
pfl->mAliasTable.Clear();
pfl->mLocalNameTable.Clear();
mFaceNamesRead.Clear();
return nullptr;
}
// Do we already have an aliasData record for this name? If so, we just
// return its base family.
if (auto lookup = pfl->mAliasTable.Lookup(aName)) {
return FindFamily(lookup.Data()->mBaseFamily, true);
}
// Strip the style suffix (after last space in the name) to get a "base"
// family name.
const char* data = aName.BeginReading();
int32_t index = aName.Length();
while (--index > 0) {
if (data[index] == ' ') {
break;
}
}
if (index <= 0) {
return nullptr;
}
nsAutoCString base(Substring(aName, 0, index));
auto familyCount = header.mFamilyCount;
if (BinarySearchIf(families, 0, familyCount,
FamilyNameComparator(this, base), &match)) {
// Check to see if we have already read the face names for this base
// family. Note: EnsureLengthAtLeast will default new entries to false.
mFaceNamesRead.EnsureLengthAtLeast(familyCount);
if (mFaceNamesRead[match]) {
return nullptr;
}
// This may be a possible base family to satisfy the search; call
// ReadFaceNamesForFamily and see if the desired name ends up in
// mAliasTable.
// Note that ReadFaceNamesForFamily may store entries in mAliasTable
// (and mLocalNameTable), but if this is happening in a content
// process (which is the common case) those entries will not be saved
// into the shared font list; they're just used here until the "real"
// alias list is ready, then discarded.
Family* baseFamily = &families[match];
pfl->ReadFaceNamesForFamily(baseFamily, false);
mFaceNamesRead[match] = true;
if (auto lookup = pfl->mAliasTable.Lookup(aName)) {
if (lookup.Data()->mFaces.Length() != baseFamily->NumFaces()) {
// If the alias family doesn't have all the faces of the base family,
// then style matching may end up resolving to a face that isn't
// supposed to be available in the legacy styled family. To ensure
// such mis-styling will get fixed, we start the async font info
// loader (if it hasn't yet been triggered), which will pull in the
// full metadata we need and then force a reflow.
pfl->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true);
}
return baseFamily;
}
}
}
#endif
return nullptr;
}
LocalFaceRec* FontList::FindLocalFace(const nsCString& aName) {
struct FaceNameComparator {
FaceNameComparator(FontList* aList, const nsCString& aTarget)
: mList(aList), mTarget(aTarget) {}
int operator()(const LocalFaceRec& aVal) const {
return Compare(mTarget,
nsDependentCString(aVal.mKey.BeginReading(mList)));
}
private:
FontList* mList;
const nsCString& mTarget;
};
Header& header = GetHeader();
LocalFaceRec* faces = LocalFaces();
size_t match;
if (faces && BinarySearchIf(faces, 0, header.mLocalFaceCount,
FaceNameComparator(this, aName), &match)) {
return &faces[match];
}
return nullptr;
}
void FontList::SearchForLocalFace(const nsACString& aName, Family** aFamily,
Face** aFace) {
Header& header = GetHeader();
MOZ_ASSERT(header.mLocalFaceCount == 0,
"do not use when local face names are already set up!");
LOG_FONTLIST(
("(shared-fontlist) local face search for (%s)", aName.BeginReading()));
char initial = aName[0];
Family* families = Families();
if (!families) {
return;
}
for (uint32_t i = 0; i < header.mFamilyCount; i++) {
Family* family = &families[i];
if (family->Key().BeginReading(this)[0] != initial) {
continue;
}
LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
family->Key().AsString(this).BeginReading()));
if (!family->IsInitialized()) {
if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family)) {
continue;
}
}
Pointer* faces = family->Faces(this);
if (!faces) {
continue;
}
for (uint32_t j = 0; j < family->NumFaces(); j++) {
auto* face = faces[j].ToPtr<Face>(this);
if (!face) {
continue;
}
nsAutoCString psname, fullname;
if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
family, face, psname, fullname)) {
LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
psname.get(), fullname.get()));
ToLowerCase(psname);
ToLowerCase(fullname);
if (aName == psname || aName == fullname) {
*aFamily = family;
*aFace = face;
return;
}
}
}
}
}
size_t FontList::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
size_t FontList::SizeOfExcludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t result = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (const auto& b : mBlocks) {
result += aMallocSizeOf(b.get()) + aMallocSizeOf(b->mShmem.get());
}
return result;
}
size_t FontList::AllocatedShmemSize() const {
size_t result = 0;
for (const auto& b : mBlocks) {
result += b->BlockSize();
}
return result;
}
} // namespace fontlist
} // namespace mozilla