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/. */
5 #include "SharedFontList-impl.h"
6 #include "gfxPlatformFontList.h"
7 #include "gfxFontUtils.h"
9 #include "nsReadableUtils.h"
11 #include "mozilla/dom/ContentChild.h"
12 #include "mozilla/dom/ContentParent.h"
13 #include "mozilla/Logging.h"
14 #include "mozilla/Unused.h"
16 #define LOG_FONTLIST(args) \
17 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
18 #define LOG_FONTLIST_ENABLED() \
19 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
24 static double WSSDistance(const Face
* aFace
, const gfxFontStyle
& aStyle
) {
25 double stretchDist
= StretchDistance(aFace
->mStretch
, aStyle
.stretch
);
26 double styleDist
= StyleDistance(aFace
->mStyle
, aStyle
.style
);
27 double weightDist
= WeightDistance(aFace
->mWeight
, aStyle
.weight
);
29 // Sanity-check that the distances are within the expected range
30 // (update if implementation of the distance functions is changed).
31 MOZ_ASSERT(stretchDist
>= 0.0 && stretchDist
<= 2000.0);
32 MOZ_ASSERT(styleDist
>= 0.0 && styleDist
<= 500.0);
33 MOZ_ASSERT(weightDist
>= 0.0 && weightDist
<= 1600.0);
35 // weight/style/stretch priority: stretch >> style >> weight
36 // so we multiply the stretch and style values to make them dominate
38 return stretchDist
* kStretchFactor
+ styleDist
* kStyleFactor
+
39 weightDist
* kWeightFactor
;
42 void* Pointer::ToPtr(FontList
* aFontList
,
43 size_t aSize
) const MOZ_NO_THREAD_SAFETY_ANALYSIS
{
44 // On failure, we'll return null; callers need to handle this appropriately
45 // (e.g. via fallback).
46 void* result
= nullptr;
52 // Ensure the list doesn't get replaced out from under us. Font-list rebuild
53 // happens on the main thread, so only non-main-thread callers need to lock
55 bool isMainThread
= NS_IsMainThread();
57 gfxPlatformFontList::PlatformFontList()->Lock();
60 uint32_t blockIndex
= Block();
62 // If the Pointer refers to a block we have not yet mapped in this process,
63 // we first need to retrieve new block handle(s) from the parent and update
65 auto& blocks
= aFontList
->mBlocks
;
66 if (blockIndex
>= blocks
.Length()) {
67 if (MOZ_UNLIKELY(XRE_IsParentProcess())) {
68 // Shouldn't happen! A content process tried to pass a bad Pointer?
71 // If we're not on the main thread, we can't do the IPC involved in
72 // UpdateShmBlocks; just let the lookup fail for now.
76 // UpdateShmBlocks can fail, if the parent has replaced the font list with
77 // a new generation. In that case we just return null, and whatever font
78 // the content process was trying to use will appear unusable for now. It's
79 // about to receive a notification of the new font list anyhow, at which
80 // point it will flush its caches and reflow everything, so the temporary
81 // failure of this font will be forgotten.
82 // UpdateShmBlocks will take the platform font-list lock during the update.
83 if (MOZ_UNLIKELY(!aFontList
->UpdateShmBlocks(true))) {
86 MOZ_ASSERT(blockIndex
< blocks
.Length(), "failure in UpdateShmBlocks?");
87 // This is wallpapering bug 1667977; it's unclear if we will always survive
88 // this, as the content process may be unable to shape/render text if all
89 // font lookups are failing.
90 // In at least some cases, however, this can occur transiently while the
91 // font list is being rebuilt by the parent; content will then be notified
92 // that the list has changed, and should refresh everything successfully.
93 if (MOZ_UNLIKELY(blockIndex
>= blocks
.Length())) {
99 // Don't create a pointer that's outside what the block has allocated!
100 const auto& block
= blocks
[blockIndex
];
101 if (MOZ_LIKELY(Offset() + aSize
<= block
->Allocated())) {
102 result
= static_cast<char*>(block
->Memory()) + Offset();
108 gfxPlatformFontList::PlatformFontList()->Unlock();
114 void String::Assign(const nsACString
& aString
, FontList
* aList
) {
115 // We only assign to previously-empty strings; they are never modified
116 // after initial assignment.
117 MOZ_ASSERT(mPointer
.IsNull());
118 mLength
= aString
.Length();
119 mPointer
= aList
->Alloc(mLength
+ 1);
120 auto* p
= mPointer
.ToArray
<char>(aList
, mLength
);
121 std::memcpy(p
, aString
.BeginReading(), mLength
);
125 Family::Family(FontList
* aList
, const InitData
& aData
)
127 mKey(aList
, aData
.mKey
),
128 mName(aList
, aData
.mName
),
129 mCharacterMap(Pointer::Null()),
130 mFaces(Pointer::Null()),
131 mIndex(aData
.mIndex
),
132 mVisibility(aData
.mVisibility
),
134 mIsBundled(aData
.mBundled
),
135 mIsBadUnderlineFamily(aData
.mBadUnderline
),
136 mIsForceClassic(aData
.mForceClassic
),
137 mIsAltLocale(aData
.mAltLocale
) {}
139 class SetCharMapRunnable
: public mozilla::Runnable
{
141 SetCharMapRunnable(uint32_t aListGeneration
,
142 std::pair
<uint32_t, bool> aFamilyIndex
,
143 uint32_t aFaceIndex
, gfxCharacterMap
* aCharMap
)
144 : Runnable("SetCharMapRunnable"),
145 mListGeneration(aListGeneration
),
146 mFamilyIndex(aFamilyIndex
),
147 mFaceIndex(aFaceIndex
),
148 mCharMap(aCharMap
) {}
150 NS_IMETHOD
Run() override
{
151 auto* list
= gfxPlatformFontList::PlatformFontList()->SharedFontList();
152 if (!list
|| list
->GetGeneration() != mListGeneration
) {
155 dom::ContentChild::GetSingleton()->SendSetCharacterMap(
156 mListGeneration
, mFamilyIndex
.first
, mFamilyIndex
.second
, mFaceIndex
,
162 uint32_t mListGeneration
;
163 std::pair
<uint32_t, bool> mFamilyIndex
;
165 RefPtr
<gfxCharacterMap
> mCharMap
;
168 void Face::SetCharacterMap(FontList
* aList
, gfxCharacterMap
* aCharMap
,
169 const Family
* aFamily
) {
170 if (!XRE_IsParentProcess()) {
171 Maybe
<std::pair
<uint32_t, bool>> familyIndex
= aFamily
->FindIndex(aList
);
173 NS_WARNING("Family index not found! Ignoring SetCharacterMap");
176 const auto* faces
= aFamily
->Faces(aList
);
177 uint32_t faceIndex
= 0;
178 while (faceIndex
< aFamily
->NumFaces()) {
179 if (faces
[faceIndex
].ToPtr
<Face
>(aList
) == this) {
184 if (faceIndex
>= aFamily
->NumFaces()) {
185 NS_WARNING("Face not found in family! Ignoring SetCharacterMap");
188 if (NS_IsMainThread()) {
189 dom::ContentChild::GetSingleton()->SendSetCharacterMap(
190 aList
->GetGeneration(), familyIndex
->first
, familyIndex
->second
,
191 faceIndex
, *aCharMap
);
193 NS_DispatchToMainThread(new SetCharMapRunnable(
194 aList
->GetGeneration(), familyIndex
.value(), faceIndex
, aCharMap
));
198 auto pfl
= gfxPlatformFontList::PlatformFontList();
199 mCharacterMap
= pfl
->GetShmemCharMap(aCharMap
);
202 void Family::AddFaces(FontList
* aList
, const nsTArray
<Face::InitData
>& aFaces
) {
203 MOZ_ASSERT(XRE_IsParentProcess());
204 if (mFaceCount
> 0) {
205 // Already initialized!
209 uint32_t count
= aFaces
.Length();
210 bool isSimple
= false;
211 // A family is "simple" (i.e. simplified style selection may be used instead
212 // of the full CSS font-matching algorithm) if there is at maximum one normal,
213 // bold, italic, and bold-italic face; in this case, they are stored at known
214 // positions in the mFaces array.
215 const Face::InitData
* slots
[4] = {nullptr, nullptr, nullptr, nullptr};
216 if (count
>= 2 && count
<= 4) {
217 // Check if this can be treated as a "simple" family
219 for (const auto& f
: aFaces
) {
220 if (!f
.mWeight
.IsSingle() || !f
.mStretch
.IsSingle() ||
221 !f
.mStyle
.IsSingle()) {
225 if (!f
.mStretch
.Min().IsNormal()) {
229 // Figure out which slot (0-3) this face belongs in
231 static_assert((kBoldMask
| kItalicMask
) == 0b11, "bad bold/italic bits");
232 if (f
.mWeight
.Min().IsBold()) {
235 if (f
.mStyle
.Min().IsItalic() || f
.mStyle
.Min().IsOblique()) {
239 // More than one face mapped to the same slot - not a simple family!
246 // Ensure all 4 slots will exist, even if some are empty.
251 // Allocate space for the face records, and initialize them.
252 // coverity[suspicious_sizeof]
253 Pointer p
= aList
->Alloc(count
* sizeof(Pointer
));
254 auto* facePtrs
= p
.ToArray
<Pointer
>(aList
, count
);
255 for (size_t i
= 0; i
< count
; i
++) {
256 if (isSimple
&& !slots
[i
]) {
257 facePtrs
[i
] = Pointer::Null();
259 const auto* initData
= isSimple
? slots
[i
] : &aFaces
[i
];
260 Pointer fp
= aList
->Alloc(sizeof(Face
));
261 auto* face
= fp
.ToPtr
<Face
>(aList
);
262 (void)new (face
) Face(aList
, *initData
);
264 if (initData
->mCharMap
) {
265 face
->SetCharacterMap(aList
, initData
->mCharMap
, this);
270 mIsSimple
= isSimple
;
272 mFaceCount
.store(count
);
274 if (LOG_FONTLIST_ENABLED()) {
275 const nsCString
& fam
= DisplayName().AsString(aList
);
276 for (unsigned j
= 0; j
< aFaces
.Length(); j
++) {
277 nsAutoCString weight
, style
, stretch
;
278 aFaces
[j
].mWeight
.ToString(weight
);
279 aFaces
[j
].mStyle
.ToString(style
);
280 aFaces
[j
].mStretch
.ToString(stretch
);
282 ("(shared-fontlist) family (%s) added face (%s) index %u, weight "
283 "%s, style %s, stretch %s",
284 fam
.get(), aFaces
[j
].mDescriptor
.get(), aFaces
[j
].mIndex
,
285 weight
.get(), style
.get(), stretch
.get()));
290 bool Family::FindAllFacesForStyleInternal(FontList
* aList
,
291 const gfxFontStyle
& aStyle
,
292 nsTArray
<Face
*>& aFaceList
) const {
293 MOZ_ASSERT(aFaceList
.IsEmpty());
294 if (!IsInitialized()) {
298 Pointer
* facePtrs
= Faces(aList
);
303 // Depending on the kind of family, we have to do varying amounts of work
304 // to figure out what face(s) to use for the requested style properties.
306 // If the family has only one face, we simply use it; no further style
307 // checking needed. (However, for bitmap fonts we may still need to check
308 // whether the size is acceptable.)
309 if (NumFaces() == 1) {
310 MOZ_ASSERT(!facePtrs
[0].IsNull());
311 auto* face
= facePtrs
[0].ToPtr
<Face
>(aList
);
312 if (face
&& face
->HasValidDescriptor()) {
313 aFaceList
.AppendElement(face
);
314 #ifdef MOZ_WIDGET_GTK
323 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
324 // or some subset of these. In this case, we have exactly 4 entries in
325 // mAvailableFonts, stored in the above order; note that some of the entries
326 // may be nullptr. We can then pick the required entry based on whether the
327 // request is for bold or non-bold, italic or non-italic, without running
328 // the more complex matching algorithm used for larger families with many
329 // weights and/or widths.
332 // Family has no more than the "standard" 4 faces, at fixed indexes;
333 // calculate which one we want.
334 // Note that we cannot simply return it as not all 4 faces are necessarily
336 bool wantBold
= aStyle
.weight
.IsBold();
337 bool wantItalic
= !aStyle
.style
.IsNormal();
339 (wantItalic
? kItalicMask
: 0) | (wantBold
? kBoldMask
: 0);
341 // If the desired style is available, use it directly.
342 auto* face
= facePtrs
[faceIndex
].ToPtr
<Face
>(aList
);
343 if (face
&& face
->HasValidDescriptor()) {
344 aFaceList
.AppendElement(face
);
345 #ifdef MOZ_WIDGET_GTK
353 // Order to check fallback faces in a simple family, depending on the
355 static const uint8_t simpleFallbacks
[4][3] = {
356 {kBoldFaceIndex
, kItalicFaceIndex
,
357 kBoldItalicFaceIndex
}, // fallback sequence for Regular
358 {kRegularFaceIndex
, kBoldItalicFaceIndex
, kItalicFaceIndex
}, // Bold
359 {kBoldItalicFaceIndex
, kRegularFaceIndex
, kBoldFaceIndex
}, // Italic
360 {kItalicFaceIndex
, kBoldFaceIndex
, kRegularFaceIndex
} // BoldItalic
362 const uint8_t* order
= simpleFallbacks
[faceIndex
];
364 for (uint8_t trial
= 0; trial
< 3; ++trial
) {
365 // check remaining faces in order of preference to find the first that
367 face
= facePtrs
[order
[trial
]].ToPtr
<Face
>(aList
);
368 if (face
&& face
->HasValidDescriptor()) {
369 aFaceList
.AppendElement(face
);
370 #ifdef MOZ_WIDGET_GTK
379 // We can only reach here if we failed to resolve the face pointer, which
380 // can happen if we're on a stylo thread and caught the font list being
381 // updated; in that case we just fail quietly and let font fallback do
382 // something for the time being.
386 // Pick the font(s) that are closest to the desired weight, style, and
387 // stretch. Iterate over all fonts, measuring the weight/style distance.
388 // Because of unicode-range values, there may be more than one font for a
389 // given but the 99% use case is only a single font entry per
390 // weight/style/stretch distance value. To optimize this, only add entries
391 // to the matched font array when another entry already has the same
392 // weight/style/stretch distance and add the last matched font entry. For
393 // normal platform fonts with a single font entry for each
394 // weight/style/stretch combination, only the last matched font entry will
396 double minDistance
= INFINITY
;
397 Face
* matched
= nullptr;
398 // Keep track of whether we've included any non-scalable font resources in
400 bool anyNonScalable
= false;
401 for (uint32_t i
= 0; i
< NumFaces(); i
++) {
402 auto* face
= facePtrs
[i
].ToPtr
<Face
>(aList
);
404 // weight/style/stretch priority: stretch >> style >> weight
405 double distance
= WSSDistance(face
, aStyle
);
406 if (distance
< minDistance
) {
408 if (!aFaceList
.IsEmpty()) {
411 minDistance
= distance
;
412 } else if (distance
== minDistance
) {
414 aFaceList
.AppendElement(matched
);
415 #ifdef MOZ_WIDGET_GTK
416 if (matched
->mSize
) {
417 anyNonScalable
= true;
426 MOZ_ASSERT(matched
, "didn't match a font within a family");
428 aFaceList
.AppendElement(matched
);
429 #ifdef MOZ_WIDGET_GTK
430 if (matched
->mSize
) {
431 anyNonScalable
= true;
436 return anyNonScalable
;
439 void Family::FindAllFacesForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
440 nsTArray
<Face
*>& aFaceList
,
441 bool aIgnoreSizeTolerance
) const {
442 #ifdef MOZ_WIDGET_GTK
443 bool anyNonScalable
=
447 FindAllFacesForStyleInternal(aList
, aStyle
, aFaceList
);
449 #ifdef MOZ_WIDGET_GTK
450 // aFaceList now contains whatever faces are the best style match for
451 // the requested style. If specifically-sized bitmap faces are supported,
452 // we need to additionally filter the list to choose the appropriate size.
454 // It would be slightly more efficient to integrate this directly into the
455 // face-selection algorithm above, but it's a rare case that doesn't apply
456 // at all to most font families.
458 // Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e.
459 // when using the gfxFcPlatformFontList implementation), so this filtering is
460 // not needed on other platforms.
462 // (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color
463 // Emoji don't count here; they package multiple bitmap sizes into a single
464 // OpenType wrapper, so they appear as a single "scalable" face in our list.)
465 if (anyNonScalable
) {
468 for (const auto& f
: aFaceList
) {
470 // Scalable face; no size distance to compute.
473 gfxFloat d
= fabs(gfxFloat(f
->mSize
) - aStyle
.size
);
474 if (!aIgnoreSizeTolerance
&& (d
* 5.0 > f
->mSize
)) {
475 continue; // Too far from the requested size, ignore.
477 // If we haven't found a "best" bitmap size yet, or if this is a better
478 // match, remember it.
479 if (!best
|| d
< dist
) {
484 // Discard all faces except the chosen "best" size; or if no pixel size was
485 // chosen, all except scalable faces.
486 // This may eliminate *all* faces in the family, if all were bitmaps and
487 // none was a good enough size match, in which case we'll fall back to the
488 // next font-family name.
489 aFaceList
.RemoveElementsBy([=](const auto& e
) { return e
->mSize
!= best
; });
494 Face
* Family::FindFaceForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
495 bool aIgnoreSizeTolerance
) const {
496 AutoTArray
<Face
*, 4> faces
;
497 FindAllFacesForStyle(aList
, aStyle
, faces
, aIgnoreSizeTolerance
);
498 return faces
.IsEmpty() ? nullptr : faces
[0];
501 void Family::SearchAllFontsForChar(FontList
* aList
,
502 GlobalFontMatch
* aMatchData
) {
503 auto* charmap
= mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
505 // If the face list is not yet initialized, or if character maps have
506 // not been loaded, go ahead and do this now (by sending a message to the
507 // parent process, if we're running in a child).
508 // After this, all faces should have their mCharacterMap set up, and the
509 // family's mCharacterMap should also be set; but in the code below we
510 // don't assume this all succeeded, so it still checks.
511 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this,
515 charmap
= mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
517 if (charmap
&& !charmap
->test(aMatchData
->mCh
)) {
521 uint32_t numFaces
= NumFaces();
522 uint32_t charMapsLoaded
= 0; // number of faces whose charmap is loaded
523 Pointer
* facePtrs
= Faces(aList
);
527 for (uint32_t i
= 0; i
< numFaces
; i
++) {
528 auto* face
= facePtrs
[i
].ToPtr
<Face
>(aList
);
532 MOZ_ASSERT(face
->HasValidDescriptor());
533 // Get the face's character map, if available (may be null!)
534 charmap
= face
->mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
538 // Check style distance if the char is supported, or if charmap not known
539 // (so that we don't trigger cmap-loading for faces that would be a worse
540 // match than what we've already found).
541 if (!charmap
|| charmap
->test(aMatchData
->mCh
)) {
542 double distance
= WSSDistance(face
, aMatchData
->mStyle
);
543 if (distance
< aMatchData
->mMatchDistance
) {
544 // It's a better style match: get a fontEntry, and if we haven't
545 // already checked character coverage, do it now (note that
546 // HasCharacter() will trigger loading the fontEntry's cmap, if
548 RefPtr
<gfxFontEntry
> fe
=
549 gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face
,
554 if (!charmap
&& !fe
->HasCharacter(aMatchData
->mCh
)) {
557 if (aMatchData
->mPresentation
!= eFontPresentation::Any
) {
558 RefPtr
<gfxFont
> font
= fe
->FindOrMakeFont(&aMatchData
->mStyle
);
563 font
->HasColorGlyphFor(aMatchData
->mCh
, aMatchData
->mNextCh
);
564 if (hasColorGlyph
!= PrefersColor(aMatchData
->mPresentation
)) {
565 distance
+= kPresentationMismatch
;
566 if (distance
>= aMatchData
->mMatchDistance
) {
571 aMatchData
->mBestMatch
= fe
;
572 aMatchData
->mMatchDistance
= distance
;
573 aMatchData
->mMatchedSharedFamily
= this;
577 if (mCharacterMap
.IsNull() && charMapsLoaded
== numFaces
) {
578 SetupFamilyCharMap(aList
);
582 void Family::SetFacePtrs(FontList
* aList
, nsTArray
<Pointer
>& aFaces
) {
583 if (aFaces
.Length() >= 2 && aFaces
.Length() <= 4) {
584 // Check whether the faces meet the criteria for a "simple" family: no more
585 // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
586 // them at the appropriate slots in mFaces and set the mIsSimple flag to
587 // accelerate font-matching.
588 bool isSimple
= true;
589 Pointer slots
[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
591 for (const Pointer
& fp
: aFaces
) {
592 auto* f
= fp
.ToPtr
<const Face
>(aList
);
593 if (!f
->mWeight
.IsSingle() || !f
->mStyle
.IsSingle() ||
594 !f
->mStretch
.IsSingle()) {
598 if (!f
->mStretch
.Min().IsNormal()) {
603 if (f
->mWeight
.Min().IsBold()) {
606 if (f
->mStyle
.Min().IsItalic() || f
->mStyle
.Min().IsOblique()) {
609 if (!slots
[slot
].IsNull()) {
616 size_t size
= 4 * sizeof(Pointer
);
617 mFaces
= aList
->Alloc(size
);
618 memcpy(mFaces
.ToPtr(aList
, size
), slots
, size
);
624 size_t size
= aFaces
.Length() * sizeof(Pointer
);
625 mFaces
= aList
->Alloc(size
);
626 memcpy(mFaces
.ToPtr(aList
, size
), aFaces
.Elements(), size
);
627 mFaceCount
.store(aFaces
.Length());
630 void Family::SetupFamilyCharMap(FontList
* aList
) {
631 // Set the character map of the family to the union of all the face cmaps,
632 // to allow font fallback searches to more rapidly reject the family.
633 if (!mCharacterMap
.IsNull()) {
636 if (!XRE_IsParentProcess()) {
637 // |this| could be a Family record in either the Families() or Aliases()
638 // arrays; FindIndex will map it back to its index and which array.
639 Maybe
<std::pair
<uint32_t, bool>> index
= FindIndex(aList
);
641 NS_WARNING("Family index not found! Ignoring SetupFamilyCharMap");
644 if (NS_IsMainThread()) {
645 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
646 aList
->GetGeneration(), index
->first
, index
->second
);
649 NS_DispatchToMainThread(NS_NewRunnableFunction(
650 "SetupFamilyCharMap callback",
651 [gen
= aList
->GetGeneration(), idx
= index
->first
,
652 alias
= index
->second
] {
653 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(gen
, idx
,
658 gfxSparseBitSet familyMap
;
659 Pointer firstMapShmPointer
;
660 const SharedBitSet
* firstMap
= nullptr;
662 Pointer
* faces
= Faces(aList
);
666 for (size_t i
= 0; i
< NumFaces(); i
++) {
667 auto* f
= faces
[i
].ToPtr
<const Face
>(aList
);
669 continue; // Skip missing face (in an incomplete "simple" family)
671 auto* faceMap
= f
->mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
673 continue; // If there's a face where setting up the cmap failed, we skip
678 firstMapShmPointer
= f
->mCharacterMap
;
679 } else if (faceMap
!= firstMap
) {
681 familyMap
.Union(*firstMap
);
684 familyMap
.Union(*faceMap
);
687 // If we created a merged cmap, we need to save that on the family; or if we
688 // found no usable cmaps at all, we need to store the empty familyMap so that
689 // we won't repeatedly attempt this for an unusable family.
690 if (merged
|| firstMapShmPointer
.IsNull()) {
692 gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap
);
694 // If all [usable] faces had the same cmap, we can just share it.
695 mCharacterMap
= firstMapShmPointer
;
699 Maybe
<std::pair
<uint32_t, bool>> Family::FindIndex(FontList
* aList
) const {
700 const auto* start
= aList
->Families();
701 const auto* end
= start
+ aList
->NumFamilies();
702 if (this >= start
&& this < end
) {
703 uint32_t index
= this - start
;
704 MOZ_RELEASE_ASSERT(start
+ index
== this, "misaligned Family ptr!");
705 return Some(std::pair(index
, false));
708 start
= aList
->AliasFamilies();
709 end
= start
+ aList
->NumAliases();
710 if (this >= start
&& this < end
) {
711 uint32_t index
= this - start
;
712 MOZ_RELEASE_ASSERT(start
+ index
== this, "misaligned AliasFamily ptr!");
713 return Some(std::pair(index
, true));
719 FontList::FontList(uint32_t aGeneration
) {
720 if (XRE_IsParentProcess()) {
721 // Create the initial shared block, and initialize Header
722 if (AppendShmBlock(SHM_BLOCK_SIZE
)) {
723 Header
& header
= GetHeader();
724 header
.mBlockHeader
.mAllocated
.store(sizeof(Header
));
725 header
.mGeneration
= aGeneration
;
726 header
.mFamilyCount
= 0;
727 header
.mBlockCount
.store(1);
728 header
.mAliasCount
.store(0);
729 header
.mLocalFaceCount
.store(0);
730 header
.mFamilies
= Pointer::Null();
731 header
.mAliases
= Pointer::Null();
732 header
.mLocalFaces
= Pointer::Null();
734 MOZ_CRASH("parent: failed to initialize FontList");
737 // Initialize using the list of shmem blocks passed by the parent via
738 // SetXPCOMProcessAttributes.
739 auto& blocks
= dom::ContentChild::GetSingleton()->SharedFontListBlocks();
740 for (auto& handle
: blocks
) {
741 auto newShm
= MakeUnique
<base::SharedMemory
>();
742 if (!newShm
->IsHandleValid(handle
)) {
743 // Bail out and let UpdateShmBlocks try to do its thing below.
746 if (!newShm
->SetHandle(std::move(handle
), true)) {
747 MOZ_CRASH("failed to set shm handle");
749 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
750 MOZ_CRASH("failed to map shared memory");
752 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
753 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
754 if (size
!= SHM_BLOCK_SIZE
) {
756 if (!newShm
->Map(size
) || !newShm
->memory()) {
757 MOZ_CRASH("failed to map shared memory");
760 mBlocks
.AppendElement(new ShmBlock(std::move(newShm
)));
763 // Update in case of any changes since the initial message was sent.
764 for (unsigned retryCount
= 0; retryCount
< 3; ++retryCount
) {
765 if (UpdateShmBlocks(false)) {
768 // The only reason for UpdateShmBlocks to fail is if the parent recreated
769 // the list after we read its first block, but before we finished getting
770 // them all, and so the generation check failed on a subsequent request.
771 // Discarding whatever we've got and retrying should get us a new,
772 // consistent set of memory blocks in this case. If this doesn't work
773 // after a couple of retries, bail out.
776 NS_WARNING("child: failed to initialize shared FontList");
780 FontList::~FontList() { DetachShmBlocks(); }
782 FontList::Header
& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS
{
783 // We only need to lock if we're not on the main thread.
784 bool isMainThread
= NS_IsMainThread();
786 gfxPlatformFontList::PlatformFontList()->Lock();
789 // It's invalid to try and access this before the first block exists.
790 MOZ_ASSERT(mBlocks
.Length() > 0);
791 auto& result
= *static_cast<Header
*>(mBlocks
[0]->Memory());
794 gfxPlatformFontList::PlatformFontList()->Unlock();
800 bool FontList::AppendShmBlock(uint32_t aSizeNeeded
) {
801 MOZ_ASSERT(XRE_IsParentProcess());
802 uint32_t size
= std::max(aSizeNeeded
, SHM_BLOCK_SIZE
);
803 auto newShm
= MakeUnique
<base::SharedMemory
>();
804 if (!newShm
->CreateFreezeable(size
)) {
805 MOZ_CRASH("failed to create shared memory");
808 if (!newShm
->Map(size
) || !newShm
->memory()) {
809 MOZ_CRASH("failed to map shared memory");
812 auto readOnly
= MakeUnique
<base::SharedMemory
>();
813 if (!newShm
->ReadOnlyCopy(readOnly
.get())) {
814 MOZ_CRASH("failed to create read-only copy");
818 ShmBlock
* block
= new ShmBlock(std::move(newShm
));
819 block
->StoreAllocated(sizeof(BlockHeader
));
820 block
->BlockSize() = size
;
822 mBlocks
.AppendElement(block
);
823 GetHeader().mBlockCount
.store(mBlocks
.Length());
825 mReadOnlyShmems
.AppendElement(std::move(readOnly
));
827 // We don't need to broadcast the addition of the initial block,
828 // because child processes can't have initialized their list at all
829 // prior to the first block being set up.
830 if (mBlocks
.Length() > 1) {
831 if (NS_IsMainThread()) {
832 dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(),
833 mBlocks
.Length() - 1);
835 NS_DispatchToMainThread(NS_NewRunnableFunction(
836 "ShmBlockAdded callback",
837 [generation
= GetGeneration(), index
= mBlocks
.Length() - 1] {
838 dom::ContentParent::BroadcastShmBlockAdded(generation
, index
);
846 void FontList::ShmBlockAdded(uint32_t aGeneration
, uint32_t aIndex
,
847 base::SharedMemoryHandle aHandle
) {
848 MOZ_ASSERT(!XRE_IsParentProcess());
849 MOZ_ASSERT(mBlocks
.Length() > 0);
851 auto newShm
= MakeUnique
<base::SharedMemory
>();
852 if (!newShm
->IsHandleValid(aHandle
)) {
855 if (!newShm
->SetHandle(std::move(aHandle
), true)) {
856 MOZ_CRASH("failed to set shm handle");
859 if (aIndex
!= mBlocks
.Length()) {
862 if (aGeneration
!= GetGeneration()) {
866 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
867 MOZ_CRASH("failed to map shared memory");
870 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
871 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
872 if (size
!= SHM_BLOCK_SIZE
) {
874 if (!newShm
->Map(size
) || !newShm
->memory()) {
875 MOZ_CRASH("failed to map shared memory");
879 mBlocks
.AppendElement(new ShmBlock(std::move(newShm
)));
882 void FontList::DetachShmBlocks() {
883 for (auto& i
: mBlocks
) {
887 mReadOnlyShmems
.Clear();
890 FontList::ShmBlock
* FontList::GetBlockFromParent(uint32_t aIndex
) {
891 MOZ_ASSERT(!XRE_IsParentProcess());
892 // If we have no existing blocks, we don't want a generation check yet;
893 // the header in the first block will define the generation of this list
894 uint32_t generation
= aIndex
== 0 ? 0 : GetGeneration();
895 base::SharedMemoryHandle handle
= base::SharedMemory::NULLHandle();
896 if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
897 generation
, aIndex
, &handle
)) {
900 auto newShm
= MakeUnique
<base::SharedMemory
>();
901 if (!newShm
->IsHandleValid(handle
)) {
904 if (!newShm
->SetHandle(std::move(handle
), true)) {
905 MOZ_CRASH("failed to set shm handle");
907 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
908 MOZ_CRASH("failed to map shared memory");
910 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
911 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
912 if (size
!= SHM_BLOCK_SIZE
) {
914 if (!newShm
->Map(size
) || !newShm
->memory()) {
915 MOZ_CRASH("failed to map shared memory");
918 return new ShmBlock(std::move(newShm
));
921 // We don't take the lock when called from the constructor, so disable thread-
922 // safety analysis here.
923 bool FontList::UpdateShmBlocks(bool aMustLock
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
924 MOZ_ASSERT(!XRE_IsParentProcess());
926 gfxPlatformFontList::PlatformFontList()->Lock();
929 while (!mBlocks
.Length() || mBlocks
.Length() < GetHeader().mBlockCount
) {
930 ShmBlock
* newBlock
= GetBlockFromParent(mBlocks
.Length());
935 mBlocks
.AppendElement(newBlock
);
938 gfxPlatformFontList::PlatformFontList()->Unlock();
943 void FontList::ShareBlocksToProcess(nsTArray
<base::SharedMemoryHandle
>* aBlocks
,
944 base::ProcessId aPid
) {
945 MOZ_RELEASE_ASSERT(mReadOnlyShmems
.Length() == mBlocks
.Length());
946 for (auto& shmem
: mReadOnlyShmems
) {
947 auto handle
= shmem
->CloneHandle();
949 // If something went wrong here, we just bail out; the child will need to
950 // request the blocks as needed, at some performance cost. (Although in
951 // practice this may mean resources are so constrained the child process
952 // isn't really going to work at all. But that's not our problem here.)
956 aBlocks
->AppendElement(std::move(handle
));
960 base::SharedMemoryHandle
FontList::ShareBlockToProcess(uint32_t aIndex
,
961 base::ProcessId aPid
) {
962 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
963 MOZ_RELEASE_ASSERT(mReadOnlyShmems
.Length() == mBlocks
.Length());
964 MOZ_RELEASE_ASSERT(aIndex
< mReadOnlyShmems
.Length());
966 return mReadOnlyShmems
[aIndex
]->CloneHandle();
969 Pointer
FontList::Alloc(uint32_t aSize
) {
970 // Only the parent process does allocation.
971 MOZ_ASSERT(XRE_IsParentProcess());
973 // 4-byte alignment is good enough for anything we allocate in the font list,
974 // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
975 auto align
= [](uint32_t aSize
) -> size_t { return (aSize
+ 3u) & ~3u; };
977 aSize
= align(aSize
);
979 int32_t blockIndex
= -1;
980 uint32_t curAlloc
, size
;
982 if (aSize
< SHM_BLOCK_SIZE
- sizeof(BlockHeader
)) {
983 // Try to allocate in the most recently added block first, as this is
984 // highly likely to succeed; if not, try earlier blocks (to fill gaps).
985 const int32_t blockCount
= mBlocks
.Length();
986 for (blockIndex
= blockCount
- 1; blockIndex
>= 0; --blockIndex
) {
987 size
= mBlocks
[blockIndex
]->BlockSize();
988 curAlloc
= mBlocks
[blockIndex
]->Allocated();
989 if (size
- curAlloc
>= aSize
) {
995 if (blockIndex
< 0) {
996 // Couldn't find enough space (or the requested size is too large to use
997 // a part of a block): create a new block.
998 if (!AppendShmBlock(aSize
+ sizeof(BlockHeader
))) {
999 return Pointer::Null();
1001 blockIndex
= mBlocks
.Length() - 1;
1002 curAlloc
= mBlocks
[blockIndex
]->Allocated();
1005 // We've found a block; allocate space from it, and return
1006 mBlocks
[blockIndex
]->StoreAllocated(curAlloc
+ aSize
);
1008 return Pointer(blockIndex
, curAlloc
);
1011 void FontList::SetFamilyNames(nsTArray
<Family::InitData
>& aFamilies
) {
1012 // Only the parent process should ever assign the list of families.
1013 MOZ_ASSERT(XRE_IsParentProcess());
1015 Header
& header
= GetHeader();
1016 MOZ_ASSERT(!header
.mFamilyCount
);
1018 gfxPlatformFontList::PlatformFontList()->ApplyWhitelist(aFamilies
);
1021 size_t count
= aFamilies
.Length();
1023 // Any font resources with an empty family-name will have been collected in
1024 // a family with empty name, and sorted to the start of the list. Such fonts
1025 // are not generally usable, or recognized as "installed", so we drop them.
1026 if (count
> 1 && aFamilies
[0].mKey
.IsEmpty()) {
1027 aFamilies
.RemoveElementAt(0);
1031 // Check for duplicate family entries (can occur if there is a bundled font
1032 // that has the same name as a system-installed one); in this case we keep
1033 // the bundled one as it will always be exposed.
1035 for (size_t i
= 1; i
< count
; ++i
) {
1036 if (aFamilies
[i
].mKey
.Equals(aFamilies
[i
- 1].mKey
)) {
1037 // Decide whether to discard the current entry or the preceding one
1039 aFamilies
[i
].mBundled
&& !aFamilies
[i
- 1].mBundled
? i
- 1 : i
;
1040 aFamilies
.RemoveElementAt(discard
);
1047 header
.mFamilies
= Alloc(count
* sizeof(Family
));
1048 if (header
.mFamilies
.IsNull()) {
1052 // We can't call Families() here because the mFamilyCount field has not yet
1054 auto* families
= header
.mFamilies
.ToArray
<Family
>(this, count
);
1055 for (size_t i
= 0; i
< count
; i
++) {
1056 (void)new (&families
[i
]) Family(this, aFamilies
[i
]);
1057 LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i
,
1058 aFamilies
[i
].mName
.get()));
1061 header
.mFamilyCount
= count
;
1064 void FontList::SetAliases(
1065 nsClassHashtable
<nsCStringHashKey
, AliasData
>& aAliasTable
) {
1066 MOZ_ASSERT(XRE_IsParentProcess());
1068 Header
& header
= GetHeader();
1070 // Build an array of Family::InitData records based on the entries in
1071 // aAliasTable, then sort them and store into the fontlist.
1072 nsTArray
<Family::InitData
> aliasArray
;
1073 aliasArray
.SetCapacity(aAliasTable
.Count());
1074 for (const auto& entry
: aAliasTable
) {
1075 aliasArray
.AppendElement(Family::InitData(
1076 entry
.GetKey(), entry
.GetData()->mBaseFamily
, entry
.GetData()->mIndex
,
1077 entry
.GetData()->mVisibility
, entry
.GetData()->mBundled
,
1078 entry
.GetData()->mBadUnderline
, entry
.GetData()->mForceClassic
, true));
1082 size_t count
= aliasArray
.Length();
1084 // Drop any entry with empty family-name as being unusable.
1085 if (count
&& aliasArray
[0].mKey
.IsEmpty()) {
1086 aliasArray
.RemoveElementAt(0);
1090 if (count
< header
.mAliasCount
) {
1091 // This shouldn't happen, but handle it safely by just bailing out.
1092 NS_WARNING("cannot reduce number of aliases");
1095 fontlist::Pointer ptr
= Alloc(count
* sizeof(Family
));
1096 auto* aliases
= ptr
.ToArray
<Family
>(this, count
);
1097 for (size_t i
= 0; i
< count
; i
++) {
1098 (void)new (&aliases
[i
]) Family(this, aliasArray
[i
]);
1099 LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i
,
1100 aliasArray
[i
].mKey
.get(), aliasArray
[i
].mName
.get()));
1101 aliases
[i
].SetFacePtrs(this, aAliasTable
.Get(aliasArray
[i
].mKey
)->mFaces
);
1102 if (LOG_FONTLIST_ENABLED()) {
1103 const auto& faces
= aAliasTable
.Get(aliasArray
[i
].mKey
)->mFaces
;
1104 for (unsigned j
= 0; j
< faces
.Length(); j
++) {
1105 auto* face
= faces
[j
].ToPtr
<const Face
>(this);
1106 const nsCString
& desc
= face
->mDescriptor
.AsString(this);
1107 nsAutoCString weight
, style
, stretch
;
1108 face
->mWeight
.ToString(weight
);
1109 face
->mStyle
.ToString(style
);
1110 face
->mStretch
.ToString(stretch
);
1112 ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
1114 desc
.get(), face
->mIndex
, weight
.get(), style
.get(),
1120 // Set the pointer before the count, so that any other process trying to read
1121 // will not risk out-of-bounds access to the array.
1122 header
.mAliases
= ptr
;
1123 header
.mAliasCount
.store(count
);
1126 void FontList::SetLocalNames(
1127 nsTHashMap
<nsCStringHashKey
, LocalFaceRec::InitData
>& aLocalNameTable
) {
1128 MOZ_ASSERT(XRE_IsParentProcess());
1129 Header
& header
= GetHeader();
1130 if (header
.mLocalFaceCount
> 0) {
1131 return; // already been done!
1133 auto faceArray
= ToTArray
<nsTArray
<nsCString
>>(aLocalNameTable
.Keys());
1135 size_t count
= faceArray
.Length();
1136 Family
* families
= Families();
1137 fontlist::Pointer ptr
= Alloc(count
* sizeof(LocalFaceRec
));
1138 auto* faces
= ptr
.ToArray
<LocalFaceRec
>(this, count
);
1139 for (size_t i
= 0; i
< count
; i
++) {
1140 (void)new (&faces
[i
]) LocalFaceRec();
1141 const auto& rec
= aLocalNameTable
.Get(faceArray
[i
]);
1142 faces
[i
].mKey
.Assign(faceArray
[i
], this);
1143 // Local face name records will refer to the canonical family name; we don't
1144 // need to search aliases here.
1145 const auto* family
= FindFamily(rec
.mFamilyName
, /*aPrimaryNameOnly*/ true);
1147 // Skip this record if the family was excluded by the font whitelist pref.
1150 faces
[i
].mFamilyIndex
= family
- families
;
1151 if (rec
.mFaceIndex
== uint32_t(-1)) {
1152 // The InitData record contains an mFaceDescriptor rather than an index,
1153 // so now we need to look for the appropriate index in the family.
1154 faces
[i
].mFaceIndex
= 0;
1155 const Pointer
* faceList
=
1156 static_cast<const Pointer
*>(family
->Faces(this));
1157 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
1158 if (!faceList
[j
].IsNull()) {
1159 auto* f
= faceList
[j
].ToPtr
<const Face
>(this);
1160 if (f
&& rec
.mFaceDescriptor
== f
->mDescriptor
.AsString(this)) {
1161 faces
[i
].mFaceIndex
= j
;
1167 faces
[i
].mFaceIndex
= rec
.mFaceIndex
;
1170 header
.mLocalFaces
= ptr
;
1171 header
.mLocalFaceCount
.store(count
);
1174 nsCString
FontList::LocalizedFamilyName(const Family
* aFamily
) {
1175 // If the given family was created for an alternate locale or legacy name,
1176 // search for a standard family that corresponds to it. This is a linear
1177 // search of the font list, but (a) this is only used to show names in
1178 // Preferences, so is not performance-critical for layout etc.; and (b) few
1179 // such family names are normally present anyway, the vast majority of fonts
1180 // just have a single family name and we return it directly.
1181 if (aFamily
->IsAltLocaleFamily()) {
1182 // Currently only the Windows backend actually does this; on other systems,
1183 // the family index is unused and will be kNoIndex for all fonts.
1184 if (aFamily
->Index() != Family::kNoIndex
) {
1185 const Family
* families
= Families();
1186 for (uint32_t i
= 0; i
< NumFamilies(); ++i
) {
1187 if (families
[i
].Index() == aFamily
->Index() &&
1188 families
[i
].IsBundled() == aFamily
->IsBundled() &&
1189 !families
[i
].IsAltLocaleFamily()) {
1190 return families
[i
].DisplayName().AsString(this);
1196 // For standard families (or if we failed to find the expected standard
1197 // family for some reason), just return the DisplayName.
1198 return aFamily
->DisplayName().AsString(this);
1201 Family
* FontList::FindFamily(const nsCString
& aName
, bool aPrimaryNameOnly
) {
1202 struct FamilyNameComparator
{
1203 FamilyNameComparator(FontList
* aList
, const nsCString
& aTarget
)
1204 : mList(aList
), mTarget(aTarget
) {}
1206 int operator()(const Family
& aVal
) const {
1207 return Compare(mTarget
,
1208 nsDependentCString(aVal
.Key().BeginReading(mList
)));
1213 const nsCString
& mTarget
;
1216 const Header
& header
= GetHeader();
1218 Family
* families
= Families();
1224 if (BinarySearchIf(families
, 0, header
.mFamilyCount
,
1225 FamilyNameComparator(this, aName
), &match
)) {
1226 return &families
[match
];
1229 if (aPrimaryNameOnly
) {
1233 if (header
.mAliasCount
) {
1234 Family
* aliases
= AliasFamilies();
1236 if (aliases
&& BinarySearchIf(aliases
, 0, header
.mAliasCount
,
1237 FamilyNameComparator(this, aName
), &match
)) {
1238 return &aliases
[match
];
1243 // For Windows only, because of how DWrite munges font family names in some
1245 // 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
1246 // and discussion on the OpenType list), try stripping a possible style-name
1247 // suffix from the end of the requested family name.
1248 // After the deferred font loader has finished, this is no longer needed as
1249 // the "real" family names will have been found in AliasFamilies() above.
1250 if (aName
.Contains(' ')) {
1251 auto pfl
= gfxPlatformFontList::PlatformFontList();
1252 pfl
->mLock
.AssertCurrentThreadIn();
1253 if (header
.mAliasCount
) {
1254 // Aliases have been fully loaded by the parent process, so just discard
1255 // any stray mAliasTable and mLocalNameTable entries from earlier calls
1256 // to this code, and return.
1257 pfl
->mAliasTable
.Clear();
1258 pfl
->mLocalNameTable
.Clear();
1259 mFaceNamesRead
.Clear();
1263 // Do we already have an aliasData record for this name? If so, we just
1264 // return its base family.
1265 if (auto lookup
= pfl
->mAliasTable
.Lookup(aName
)) {
1266 return FindFamily(lookup
.Data()->mBaseFamily
, true);
1269 // Strip the style suffix (after last space in the name) to get a "base"
1271 const char* data
= aName
.BeginReading();
1272 int32_t index
= aName
.Length();
1273 while (--index
> 0) {
1274 if (data
[index
] == ' ') {
1281 nsAutoCString
base(Substring(aName
, 0, index
));
1282 auto familyCount
= header
.mFamilyCount
;
1283 if (BinarySearchIf(families
, 0, familyCount
,
1284 FamilyNameComparator(this, base
), &match
)) {
1285 // Check to see if we have already read the face names for this base
1286 // family. Note: EnsureLengthAtLeast will default new entries to false.
1287 mFaceNamesRead
.EnsureLengthAtLeast(familyCount
);
1288 if (mFaceNamesRead
[match
]) {
1291 // This may be a possible base family to satisfy the search; call
1292 // ReadFaceNamesForFamily and see if the desired name ends up in
1294 // Note that ReadFaceNamesForFamily may store entries in mAliasTable
1295 // (and mLocalNameTable), but if this is happening in a content
1296 // process (which is the common case) those entries will not be saved
1297 // into the shared font list; they're just used here until the "real"
1298 // alias list is ready, then discarded.
1299 Family
* baseFamily
= &families
[match
];
1300 pfl
->ReadFaceNamesForFamily(baseFamily
, false);
1301 mFaceNamesRead
[match
] = true;
1302 if (auto lookup
= pfl
->mAliasTable
.Lookup(aName
)) {
1303 if (lookup
.Data()->mFaces
.Length() != baseFamily
->NumFaces()) {
1304 // If the alias family doesn't have all the faces of the base family,
1305 // then style matching may end up resolving to a face that isn't
1306 // supposed to be available in the legacy styled family. To ensure
1307 // such mis-styling will get fixed, we start the async font info
1308 // loader (if it hasn't yet been triggered), which will pull in the
1309 // full metadata we need and then force a reflow.
1310 pfl
->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true);
1321 LocalFaceRec
* FontList::FindLocalFace(const nsCString
& aName
) {
1322 struct FaceNameComparator
{
1323 FaceNameComparator(FontList
* aList
, const nsCString
& aTarget
)
1324 : mList(aList
), mTarget(aTarget
) {}
1326 int operator()(const LocalFaceRec
& aVal
) const {
1327 return Compare(mTarget
,
1328 nsDependentCString(aVal
.mKey
.BeginReading(mList
)));
1333 const nsCString
& mTarget
;
1336 Header
& header
= GetHeader();
1338 LocalFaceRec
* faces
= LocalFaces();
1340 if (faces
&& BinarySearchIf(faces
, 0, header
.mLocalFaceCount
,
1341 FaceNameComparator(this, aName
), &match
)) {
1342 return &faces
[match
];
1348 void FontList::SearchForLocalFace(const nsACString
& aName
, Family
** aFamily
,
1350 Header
& header
= GetHeader();
1351 MOZ_ASSERT(header
.mLocalFaceCount
== 0,
1352 "do not use when local face names are already set up!");
1354 ("(shared-fontlist) local face search for (%s)", aName
.BeginReading()));
1355 char initial
= aName
[0];
1356 Family
* families
= Families();
1360 for (uint32_t i
= 0; i
< header
.mFamilyCount
; i
++) {
1361 Family
* family
= &families
[i
];
1362 if (family
->Key().BeginReading(this)[0] != initial
) {
1365 LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
1366 family
->Key().AsString(this).BeginReading()));
1367 if (!family
->IsInitialized()) {
1368 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family
)) {
1372 Pointer
* faces
= family
->Faces(this);
1376 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
1377 auto* face
= faces
[j
].ToPtr
<Face
>(this);
1381 nsAutoCString psname
, fullname
;
1382 if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
1383 family
, face
, psname
, fullname
)) {
1384 LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
1385 psname
.get(), fullname
.get()));
1386 ToLowerCase(psname
);
1387 ToLowerCase(fullname
);
1388 if (aName
== psname
|| aName
== fullname
) {
1398 size_t FontList::SizeOfIncludingThis(
1399 mozilla::MallocSizeOf aMallocSizeOf
) const {
1400 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
1403 size_t FontList::SizeOfExcludingThis(
1404 mozilla::MallocSizeOf aMallocSizeOf
) const {
1405 size_t result
= mBlocks
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1406 for (const auto& b
: mBlocks
) {
1407 result
+= aMallocSizeOf(b
.get()) + aMallocSizeOf(b
->mShmem
.get());
1412 size_t FontList::AllocatedShmemSize() const {
1414 for (const auto& b
: mBlocks
) {
1415 result
+= b
->BlockSize();
1420 } // namespace fontlist
1421 } // namespace mozilla