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 std::pair
<uint32_t, bool> familyIndex
= aFamily
->FindIndex(aList
);
172 const auto* faces
= aFamily
->Faces(aList
);
173 uint32_t faceIndex
= 0;
174 while (faceIndex
< aFamily
->NumFaces()) {
175 if (faces
[faceIndex
].ToPtr
<Face
>(aList
) == this) {
180 MOZ_RELEASE_ASSERT(faceIndex
< aFamily
->NumFaces(), "Face ptr not found!");
181 if (NS_IsMainThread()) {
182 dom::ContentChild::GetSingleton()->SendSetCharacterMap(
183 aList
->GetGeneration(), familyIndex
.first
, familyIndex
.second
,
184 faceIndex
, *aCharMap
);
186 NS_DispatchToMainThread(new SetCharMapRunnable(
187 aList
->GetGeneration(), familyIndex
, faceIndex
, aCharMap
));
191 auto pfl
= gfxPlatformFontList::PlatformFontList();
192 mCharacterMap
= pfl
->GetShmemCharMap(aCharMap
);
195 void Family::AddFaces(FontList
* aList
, const nsTArray
<Face::InitData
>& aFaces
) {
196 MOZ_ASSERT(XRE_IsParentProcess());
197 if (mFaceCount
> 0) {
198 // Already initialized!
202 uint32_t count
= aFaces
.Length();
203 bool isSimple
= false;
204 // A family is "simple" (i.e. simplified style selection may be used instead
205 // of the full CSS font-matching algorithm) if there is at maximum one normal,
206 // bold, italic, and bold-italic face; in this case, they are stored at known
207 // positions in the mFaces array.
208 const Face::InitData
* slots
[4] = {nullptr, nullptr, nullptr, nullptr};
209 if (count
>= 2 && count
<= 4) {
210 // Check if this can be treated as a "simple" family
212 for (const auto& f
: aFaces
) {
213 if (!f
.mWeight
.IsSingle() || !f
.mStretch
.IsSingle() ||
214 !f
.mStyle
.IsSingle()) {
218 if (!f
.mStretch
.Min().IsNormal()) {
222 // Figure out which slot (0-3) this face belongs in
224 static_assert((kBoldMask
| kItalicMask
) == 0b11, "bad bold/italic bits");
225 if (f
.mWeight
.Min().IsBold()) {
228 if (f
.mStyle
.Min().IsItalic() || f
.mStyle
.Min().IsOblique()) {
232 // More than one face mapped to the same slot - not a simple family!
239 // Ensure all 4 slots will exist, even if some are empty.
244 // Allocate space for the face records, and initialize them.
245 // coverity[suspicious_sizeof]
246 Pointer p
= aList
->Alloc(count
* sizeof(Pointer
));
247 auto* facePtrs
= p
.ToArray
<Pointer
>(aList
, count
);
248 for (size_t i
= 0; i
< count
; i
++) {
249 if (isSimple
&& !slots
[i
]) {
250 facePtrs
[i
] = Pointer::Null();
252 const auto* initData
= isSimple
? slots
[i
] : &aFaces
[i
];
253 Pointer fp
= aList
->Alloc(sizeof(Face
));
254 auto* face
= fp
.ToPtr
<Face
>(aList
);
255 (void)new (face
) Face(aList
, *initData
);
257 if (initData
->mCharMap
) {
258 face
->SetCharacterMap(aList
, initData
->mCharMap
, this);
263 mIsSimple
= isSimple
;
265 mFaceCount
.store(count
);
267 if (LOG_FONTLIST_ENABLED()) {
268 const nsCString
& fam
= DisplayName().AsString(aList
);
269 for (unsigned j
= 0; j
< aFaces
.Length(); j
++) {
270 nsAutoCString weight
, style
, stretch
;
271 aFaces
[j
].mWeight
.ToString(weight
);
272 aFaces
[j
].mStyle
.ToString(style
);
273 aFaces
[j
].mStretch
.ToString(stretch
);
275 ("(shared-fontlist) family (%s) added face (%s) index %u, weight "
276 "%s, style %s, stretch %s",
277 fam
.get(), aFaces
[j
].mDescriptor
.get(), aFaces
[j
].mIndex
,
278 weight
.get(), style
.get(), stretch
.get()));
283 bool Family::FindAllFacesForStyleInternal(FontList
* aList
,
284 const gfxFontStyle
& aStyle
,
285 nsTArray
<Face
*>& aFaceList
) const {
286 MOZ_ASSERT(aFaceList
.IsEmpty());
287 if (!IsInitialized()) {
291 Pointer
* facePtrs
= Faces(aList
);
296 // Depending on the kind of family, we have to do varying amounts of work
297 // to figure out what face(s) to use for the requested style properties.
299 // If the family has only one face, we simply use it; no further style
300 // checking needed. (However, for bitmap fonts we may still need to check
301 // whether the size is acceptable.)
302 if (NumFaces() == 1) {
303 MOZ_ASSERT(!facePtrs
[0].IsNull());
304 auto* face
= facePtrs
[0].ToPtr
<Face
>(aList
);
305 if (face
&& face
->HasValidDescriptor()) {
306 aFaceList
.AppendElement(face
);
307 #ifdef MOZ_WIDGET_GTK
316 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
317 // or some subset of these. In this case, we have exactly 4 entries in
318 // mAvailableFonts, stored in the above order; note that some of the entries
319 // may be nullptr. We can then pick the required entry based on whether the
320 // request is for bold or non-bold, italic or non-italic, without running
321 // the more complex matching algorithm used for larger families with many
322 // weights and/or widths.
325 // Family has no more than the "standard" 4 faces, at fixed indexes;
326 // calculate which one we want.
327 // Note that we cannot simply return it as not all 4 faces are necessarily
329 bool wantBold
= aStyle
.weight
.IsBold();
330 bool wantItalic
= !aStyle
.style
.IsNormal();
332 (wantItalic
? kItalicMask
: 0) | (wantBold
? kBoldMask
: 0);
334 // If the desired style is available, use it directly.
335 auto* face
= facePtrs
[faceIndex
].ToPtr
<Face
>(aList
);
336 if (face
&& face
->HasValidDescriptor()) {
337 aFaceList
.AppendElement(face
);
338 #ifdef MOZ_WIDGET_GTK
346 // Order to check fallback faces in a simple family, depending on the
348 static const uint8_t simpleFallbacks
[4][3] = {
349 {kBoldFaceIndex
, kItalicFaceIndex
,
350 kBoldItalicFaceIndex
}, // fallback sequence for Regular
351 {kRegularFaceIndex
, kBoldItalicFaceIndex
, kItalicFaceIndex
}, // Bold
352 {kBoldItalicFaceIndex
, kRegularFaceIndex
, kBoldFaceIndex
}, // Italic
353 {kItalicFaceIndex
, kBoldFaceIndex
, kRegularFaceIndex
} // BoldItalic
355 const uint8_t* order
= simpleFallbacks
[faceIndex
];
357 for (uint8_t trial
= 0; trial
< 3; ++trial
) {
358 // check remaining faces in order of preference to find the first that
360 face
= facePtrs
[order
[trial
]].ToPtr
<Face
>(aList
);
361 if (face
&& face
->HasValidDescriptor()) {
362 aFaceList
.AppendElement(face
);
363 #ifdef MOZ_WIDGET_GTK
372 // We can only reach here if we failed to resolve the face pointer, which
373 // can happen if we're on a stylo thread and caught the font list being
374 // updated; in that case we just fail quietly and let font fallback do
375 // something for the time being.
379 // Pick the font(s) that are closest to the desired weight, style, and
380 // stretch. Iterate over all fonts, measuring the weight/style distance.
381 // Because of unicode-range values, there may be more than one font for a
382 // given but the 99% use case is only a single font entry per
383 // weight/style/stretch distance value. To optimize this, only add entries
384 // to the matched font array when another entry already has the same
385 // weight/style/stretch distance and add the last matched font entry. For
386 // normal platform fonts with a single font entry for each
387 // weight/style/stretch combination, only the last matched font entry will
389 double minDistance
= INFINITY
;
390 Face
* matched
= nullptr;
391 // Keep track of whether we've included any non-scalable font resources in
393 bool anyNonScalable
= false;
394 for (uint32_t i
= 0; i
< NumFaces(); i
++) {
395 auto* face
= facePtrs
[i
].ToPtr
<Face
>(aList
);
397 // weight/style/stretch priority: stretch >> style >> weight
398 double distance
= WSSDistance(face
, aStyle
);
399 if (distance
< minDistance
) {
401 if (!aFaceList
.IsEmpty()) {
404 minDistance
= distance
;
405 } else if (distance
== minDistance
) {
407 aFaceList
.AppendElement(matched
);
408 #ifdef MOZ_WIDGET_GTK
409 if (matched
->mSize
) {
410 anyNonScalable
= true;
419 MOZ_ASSERT(matched
, "didn't match a font within a family");
421 aFaceList
.AppendElement(matched
);
422 #ifdef MOZ_WIDGET_GTK
423 if (matched
->mSize
) {
424 anyNonScalable
= true;
429 return anyNonScalable
;
432 void Family::FindAllFacesForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
433 nsTArray
<Face
*>& aFaceList
,
434 bool aIgnoreSizeTolerance
) const {
435 #ifdef MOZ_WIDGET_GTK
436 bool anyNonScalable
=
440 FindAllFacesForStyleInternal(aList
, aStyle
, aFaceList
);
442 #ifdef MOZ_WIDGET_GTK
443 // aFaceList now contains whatever faces are the best style match for
444 // the requested style. If specifically-sized bitmap faces are supported,
445 // we need to additionally filter the list to choose the appropriate size.
447 // It would be slightly more efficient to integrate this directly into the
448 // face-selection algorithm above, but it's a rare case that doesn't apply
449 // at all to most font families.
451 // Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e.
452 // when using the gfxFcPlatformFontList implementation), so this filtering is
453 // not needed on other platforms.
455 // (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color
456 // Emoji don't count here; they package multiple bitmap sizes into a single
457 // OpenType wrapper, so they appear as a single "scalable" face in our list.)
458 if (anyNonScalable
) {
461 for (const auto& f
: aFaceList
) {
463 // Scalable face; no size distance to compute.
466 gfxFloat d
= fabs(gfxFloat(f
->mSize
) - aStyle
.size
);
467 if (!aIgnoreSizeTolerance
&& (d
* 5.0 > f
->mSize
)) {
468 continue; // Too far from the requested size, ignore.
470 // If we haven't found a "best" bitmap size yet, or if this is a better
471 // match, remember it.
472 if (!best
|| d
< dist
) {
477 // Discard all faces except the chosen "best" size; or if no pixel size was
478 // chosen, all except scalable faces.
479 // This may eliminate *all* faces in the family, if all were bitmaps and
480 // none was a good enough size match, in which case we'll fall back to the
481 // next font-family name.
482 aFaceList
.RemoveElementsBy([=](const auto& e
) { return e
->mSize
!= best
; });
487 Face
* Family::FindFaceForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
488 bool aIgnoreSizeTolerance
) const {
489 AutoTArray
<Face
*, 4> faces
;
490 FindAllFacesForStyle(aList
, aStyle
, faces
, aIgnoreSizeTolerance
);
491 return faces
.IsEmpty() ? nullptr : faces
[0];
494 void Family::SearchAllFontsForChar(FontList
* aList
,
495 GlobalFontMatch
* aMatchData
) {
496 auto* charmap
= mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
498 // If the face list is not yet initialized, or if character maps have
499 // not been loaded, go ahead and do this now (by sending a message to the
500 // parent process, if we're running in a child).
501 // After this, all faces should have their mCharacterMap set up, and the
502 // family's mCharacterMap should also be set; but in the code below we
503 // don't assume this all succeeded, so it still checks.
504 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this,
508 charmap
= mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
510 if (charmap
&& !charmap
->test(aMatchData
->mCh
)) {
514 uint32_t numFaces
= NumFaces();
515 uint32_t charMapsLoaded
= 0; // number of faces whose charmap is loaded
516 Pointer
* facePtrs
= Faces(aList
);
520 for (uint32_t i
= 0; i
< numFaces
; i
++) {
521 auto* face
= facePtrs
[i
].ToPtr
<Face
>(aList
);
525 MOZ_ASSERT(face
->HasValidDescriptor());
526 // Get the face's character map, if available (may be null!)
527 charmap
= face
->mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
531 // Check style distance if the char is supported, or if charmap not known
532 // (so that we don't trigger cmap-loading for faces that would be a worse
533 // match than what we've already found).
534 if (!charmap
|| charmap
->test(aMatchData
->mCh
)) {
535 double distance
= WSSDistance(face
, aMatchData
->mStyle
);
536 if (distance
< aMatchData
->mMatchDistance
) {
537 // It's a better style match: get a fontEntry, and if we haven't
538 // already checked character coverage, do it now (note that
539 // HasCharacter() will trigger loading the fontEntry's cmap, if
541 RefPtr
<gfxFontEntry
> fe
=
542 gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face
,
547 if (!charmap
&& !fe
->HasCharacter(aMatchData
->mCh
)) {
550 if (aMatchData
->mPresentation
!= eFontPresentation::Any
) {
551 RefPtr
<gfxFont
> font
= fe
->FindOrMakeFont(&aMatchData
->mStyle
);
556 font
->HasColorGlyphFor(aMatchData
->mCh
, aMatchData
->mNextCh
);
557 if (hasColorGlyph
!= PrefersColor(aMatchData
->mPresentation
)) {
558 distance
+= kPresentationMismatch
;
559 if (distance
>= aMatchData
->mMatchDistance
) {
564 aMatchData
->mBestMatch
= fe
;
565 aMatchData
->mMatchDistance
= distance
;
566 aMatchData
->mMatchedSharedFamily
= this;
570 if (mCharacterMap
.IsNull() && charMapsLoaded
== numFaces
) {
571 SetupFamilyCharMap(aList
);
575 void Family::SetFacePtrs(FontList
* aList
, nsTArray
<Pointer
>& aFaces
) {
576 if (aFaces
.Length() >= 2 && aFaces
.Length() <= 4) {
577 // Check whether the faces meet the criteria for a "simple" family: no more
578 // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
579 // them at the appropriate slots in mFaces and set the mIsSimple flag to
580 // accelerate font-matching.
581 bool isSimple
= true;
582 Pointer slots
[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
584 for (const Pointer
& fp
: aFaces
) {
585 auto* f
= fp
.ToPtr
<const Face
>(aList
);
586 if (!f
->mWeight
.IsSingle() || !f
->mStyle
.IsSingle() ||
587 !f
->mStretch
.IsSingle()) {
591 if (!f
->mStretch
.Min().IsNormal()) {
596 if (f
->mWeight
.Min().IsBold()) {
599 if (f
->mStyle
.Min().IsItalic() || f
->mStyle
.Min().IsOblique()) {
602 if (!slots
[slot
].IsNull()) {
609 size_t size
= 4 * sizeof(Pointer
);
610 mFaces
= aList
->Alloc(size
);
611 memcpy(mFaces
.ToPtr(aList
, size
), slots
, size
);
617 size_t size
= aFaces
.Length() * sizeof(Pointer
);
618 mFaces
= aList
->Alloc(size
);
619 memcpy(mFaces
.ToPtr(aList
, size
), aFaces
.Elements(), size
);
620 mFaceCount
.store(aFaces
.Length());
623 void Family::SetupFamilyCharMap(FontList
* aList
) {
624 // Set the character map of the family to the union of all the face cmaps,
625 // to allow font fallback searches to more rapidly reject the family.
626 if (!mCharacterMap
.IsNull()) {
629 if (!XRE_IsParentProcess()) {
630 // |this| could be a Family record in either the Families() or Aliases()
631 // arrays; FindIndex will map it back to its index and which array.
632 std::pair
<uint32_t, bool> index
= FindIndex(aList
);
633 if (NS_IsMainThread()) {
634 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
635 aList
->GetGeneration(), index
.first
, index
.second
);
638 NS_DispatchToMainThread(NS_NewRunnableFunction(
639 "SetupFamilyCharMap callback",
640 [gen
= aList
->GetGeneration(), idx
= index
.first
,
641 alias
= index
.second
] {
642 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(gen
, idx
,
647 gfxSparseBitSet familyMap
;
648 Pointer firstMapShmPointer
;
649 const SharedBitSet
* firstMap
= nullptr;
651 Pointer
* faces
= Faces(aList
);
655 for (size_t i
= 0; i
< NumFaces(); i
++) {
656 auto* f
= faces
[i
].ToPtr
<const Face
>(aList
);
658 continue; // Skip missing face (in an incomplete "simple" family)
660 auto* faceMap
= f
->mCharacterMap
.ToPtr
<const SharedBitSet
>(aList
);
662 continue; // If there's a face where setting up the cmap failed, we skip
667 firstMapShmPointer
= f
->mCharacterMap
;
668 } else if (faceMap
!= firstMap
) {
670 familyMap
.Union(*firstMap
);
673 familyMap
.Union(*faceMap
);
676 // If we created a merged cmap, we need to save that on the family; or if we
677 // found no usable cmaps at all, we need to store the empty familyMap so that
678 // we won't repeatedly attempt this for an unusable family.
679 if (merged
|| firstMapShmPointer
.IsNull()) {
681 gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap
);
683 // If all [usable] faces had the same cmap, we can just share it.
684 mCharacterMap
= firstMapShmPointer
;
688 std::pair
<uint32_t, bool> Family::FindIndex(FontList
* aList
) const {
689 const auto* start
= aList
->Families();
690 const auto* end
= start
+ aList
->NumFamilies();
691 if (this >= start
&& this < end
) {
692 uint32_t index
= this - start
;
693 MOZ_RELEASE_ASSERT(start
+ index
== this, "misaligned Family ptr!");
694 return std::pair(index
, false);
697 start
= aList
->AliasFamilies();
698 end
= start
+ aList
->NumAliases();
699 if (this >= start
&& this < end
) {
700 uint32_t index
= this - start
;
701 MOZ_RELEASE_ASSERT(start
+ index
== this, "misaligned AliasFamily ptr!");
702 return std::pair(index
, true);
705 MOZ_CRASH("invalid font-list Family ptr!");
708 FontList::FontList(uint32_t aGeneration
) {
709 if (XRE_IsParentProcess()) {
710 // Create the initial shared block, and initialize Header
711 if (AppendShmBlock(SHM_BLOCK_SIZE
)) {
712 Header
& header
= GetHeader();
713 header
.mBlockHeader
.mAllocated
.store(sizeof(Header
));
714 header
.mGeneration
= aGeneration
;
715 header
.mFamilyCount
= 0;
716 header
.mBlockCount
.store(1);
717 header
.mAliasCount
.store(0);
718 header
.mLocalFaceCount
.store(0);
719 header
.mFamilies
= Pointer::Null();
720 header
.mAliases
= Pointer::Null();
721 header
.mLocalFaces
= Pointer::Null();
723 MOZ_CRASH("parent: failed to initialize FontList");
726 // Initialize using the list of shmem blocks passed by the parent via
727 // SetXPCOMProcessAttributes.
728 auto& blocks
= dom::ContentChild::GetSingleton()->SharedFontListBlocks();
729 for (auto& handle
: blocks
) {
730 auto newShm
= MakeUnique
<base::SharedMemory
>();
731 if (!newShm
->IsHandleValid(handle
)) {
732 // Bail out and let UpdateShmBlocks try to do its thing below.
735 if (!newShm
->SetHandle(std::move(handle
), true)) {
736 MOZ_CRASH("failed to set shm handle");
738 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
739 MOZ_CRASH("failed to map shared memory");
741 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
742 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
743 if (size
!= SHM_BLOCK_SIZE
) {
745 if (!newShm
->Map(size
) || !newShm
->memory()) {
746 MOZ_CRASH("failed to map shared memory");
749 mBlocks
.AppendElement(new ShmBlock(std::move(newShm
)));
752 // Update in case of any changes since the initial message was sent.
753 for (unsigned retryCount
= 0; retryCount
< 3; ++retryCount
) {
754 if (UpdateShmBlocks(false)) {
757 // The only reason for UpdateShmBlocks to fail is if the parent recreated
758 // the list after we read its first block, but before we finished getting
759 // them all, and so the generation check failed on a subsequent request.
760 // Discarding whatever we've got and retrying should get us a new,
761 // consistent set of memory blocks in this case. If this doesn't work
762 // after a couple of retries, bail out.
765 NS_WARNING("child: failed to initialize shared FontList");
769 FontList::~FontList() { DetachShmBlocks(); }
771 FontList::Header
& FontList::GetHeader() const MOZ_NO_THREAD_SAFETY_ANALYSIS
{
772 // We only need to lock if we're not on the main thread.
773 bool isMainThread
= NS_IsMainThread();
775 gfxPlatformFontList::PlatformFontList()->Lock();
778 // It's invalid to try and access this before the first block exists.
779 MOZ_ASSERT(mBlocks
.Length() > 0);
780 auto& result
= *static_cast<Header
*>(mBlocks
[0]->Memory());
783 gfxPlatformFontList::PlatformFontList()->Unlock();
789 bool FontList::AppendShmBlock(uint32_t aSizeNeeded
) {
790 MOZ_ASSERT(XRE_IsParentProcess());
791 uint32_t size
= std::max(aSizeNeeded
, SHM_BLOCK_SIZE
);
792 auto newShm
= MakeUnique
<base::SharedMemory
>();
793 if (!newShm
->CreateFreezeable(size
)) {
794 MOZ_CRASH("failed to create shared memory");
797 if (!newShm
->Map(size
) || !newShm
->memory()) {
798 MOZ_CRASH("failed to map shared memory");
801 auto readOnly
= MakeUnique
<base::SharedMemory
>();
802 if (!newShm
->ReadOnlyCopy(readOnly
.get())) {
803 MOZ_CRASH("failed to create read-only copy");
807 ShmBlock
* block
= new ShmBlock(std::move(newShm
));
808 block
->StoreAllocated(sizeof(BlockHeader
));
809 block
->BlockSize() = size
;
811 mBlocks
.AppendElement(block
);
812 GetHeader().mBlockCount
.store(mBlocks
.Length());
814 mReadOnlyShmems
.AppendElement(std::move(readOnly
));
816 // We don't need to broadcast the addition of the initial block,
817 // because child processes can't have initialized their list at all
818 // prior to the first block being set up.
819 if (mBlocks
.Length() > 1) {
820 if (NS_IsMainThread()) {
821 dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(),
822 mBlocks
.Length() - 1);
824 NS_DispatchToMainThread(NS_NewRunnableFunction(
825 "ShmBlockAdded callback",
826 [generation
= GetGeneration(), index
= mBlocks
.Length() - 1] {
827 dom::ContentParent::BroadcastShmBlockAdded(generation
, index
);
835 void FontList::ShmBlockAdded(uint32_t aGeneration
, uint32_t aIndex
,
836 base::SharedMemoryHandle aHandle
) {
837 MOZ_ASSERT(!XRE_IsParentProcess());
838 MOZ_ASSERT(mBlocks
.Length() > 0);
840 auto newShm
= MakeUnique
<base::SharedMemory
>();
841 if (!newShm
->IsHandleValid(aHandle
)) {
844 if (!newShm
->SetHandle(std::move(aHandle
), true)) {
845 MOZ_CRASH("failed to set shm handle");
848 if (aIndex
!= mBlocks
.Length()) {
851 if (aGeneration
!= GetGeneration()) {
855 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
856 MOZ_CRASH("failed to map shared memory");
859 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
860 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
861 if (size
!= SHM_BLOCK_SIZE
) {
863 if (!newShm
->Map(size
) || !newShm
->memory()) {
864 MOZ_CRASH("failed to map shared memory");
868 mBlocks
.AppendElement(new ShmBlock(std::move(newShm
)));
871 void FontList::DetachShmBlocks() {
872 for (auto& i
: mBlocks
) {
876 mReadOnlyShmems
.Clear();
879 FontList::ShmBlock
* FontList::GetBlockFromParent(uint32_t aIndex
) {
880 MOZ_ASSERT(!XRE_IsParentProcess());
881 // If we have no existing blocks, we don't want a generation check yet;
882 // the header in the first block will define the generation of this list
883 uint32_t generation
= aIndex
== 0 ? 0 : GetGeneration();
884 base::SharedMemoryHandle handle
= base::SharedMemory::NULLHandle();
885 if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
886 generation
, aIndex
, &handle
)) {
889 auto newShm
= MakeUnique
<base::SharedMemory
>();
890 if (!newShm
->IsHandleValid(handle
)) {
893 if (!newShm
->SetHandle(std::move(handle
), true)) {
894 MOZ_CRASH("failed to set shm handle");
896 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
897 MOZ_CRASH("failed to map shared memory");
899 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
900 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
901 if (size
!= SHM_BLOCK_SIZE
) {
903 if (!newShm
->Map(size
) || !newShm
->memory()) {
904 MOZ_CRASH("failed to map shared memory");
907 return new ShmBlock(std::move(newShm
));
910 // We don't take the lock when called from the constructor, so disable thread-
911 // safety analysis here.
912 bool FontList::UpdateShmBlocks(bool aMustLock
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
913 MOZ_ASSERT(!XRE_IsParentProcess());
915 gfxPlatformFontList::PlatformFontList()->Lock();
918 while (!mBlocks
.Length() || mBlocks
.Length() < GetHeader().mBlockCount
) {
919 ShmBlock
* newBlock
= GetBlockFromParent(mBlocks
.Length());
924 mBlocks
.AppendElement(newBlock
);
927 gfxPlatformFontList::PlatformFontList()->Unlock();
932 void FontList::ShareBlocksToProcess(nsTArray
<base::SharedMemoryHandle
>* aBlocks
,
933 base::ProcessId aPid
) {
934 MOZ_RELEASE_ASSERT(mReadOnlyShmems
.Length() == mBlocks
.Length());
935 for (auto& shmem
: mReadOnlyShmems
) {
936 auto handle
= shmem
->CloneHandle();
938 // If something went wrong here, we just bail out; the child will need to
939 // request the blocks as needed, at some performance cost. (Although in
940 // practice this may mean resources are so constrained the child process
941 // isn't really going to work at all. But that's not our problem here.)
945 aBlocks
->AppendElement(std::move(handle
));
949 base::SharedMemoryHandle
FontList::ShareBlockToProcess(uint32_t aIndex
,
950 base::ProcessId aPid
) {
951 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
952 MOZ_RELEASE_ASSERT(mReadOnlyShmems
.Length() == mBlocks
.Length());
953 MOZ_RELEASE_ASSERT(aIndex
< mReadOnlyShmems
.Length());
955 return mReadOnlyShmems
[aIndex
]->CloneHandle();
958 Pointer
FontList::Alloc(uint32_t aSize
) {
959 // Only the parent process does allocation.
960 MOZ_ASSERT(XRE_IsParentProcess());
962 // 4-byte alignment is good enough for anything we allocate in the font list,
963 // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
964 auto align
= [](uint32_t aSize
) -> size_t { return (aSize
+ 3u) & ~3u; };
966 aSize
= align(aSize
);
968 int32_t blockIndex
= -1;
969 uint32_t curAlloc
, size
;
971 if (aSize
< SHM_BLOCK_SIZE
- sizeof(BlockHeader
)) {
972 // Try to allocate in the most recently added block first, as this is
973 // highly likely to succeed; if not, try earlier blocks (to fill gaps).
974 const int32_t blockCount
= mBlocks
.Length();
975 for (blockIndex
= blockCount
- 1; blockIndex
>= 0; --blockIndex
) {
976 size
= mBlocks
[blockIndex
]->BlockSize();
977 curAlloc
= mBlocks
[blockIndex
]->Allocated();
978 if (size
- curAlloc
>= aSize
) {
984 if (blockIndex
< 0) {
985 // Couldn't find enough space (or the requested size is too large to use
986 // a part of a block): create a new block.
987 if (!AppendShmBlock(aSize
+ sizeof(BlockHeader
))) {
988 return Pointer::Null();
990 blockIndex
= mBlocks
.Length() - 1;
991 curAlloc
= mBlocks
[blockIndex
]->Allocated();
994 // We've found a block; allocate space from it, and return
995 mBlocks
[blockIndex
]->StoreAllocated(curAlloc
+ aSize
);
997 return Pointer(blockIndex
, curAlloc
);
1000 void FontList::SetFamilyNames(nsTArray
<Family::InitData
>& aFamilies
) {
1001 // Only the parent process should ever assign the list of families.
1002 MOZ_ASSERT(XRE_IsParentProcess());
1004 Header
& header
= GetHeader();
1005 MOZ_ASSERT(!header
.mFamilyCount
);
1007 gfxPlatformFontList::PlatformFontList()->ApplyWhitelist(aFamilies
);
1010 size_t count
= aFamilies
.Length();
1012 // Any font resources with an empty family-name will have been collected in
1013 // a family with empty name, and sorted to the start of the list. Such fonts
1014 // are not generally usable, or recognized as "installed", so we drop them.
1015 if (count
> 1 && aFamilies
[0].mKey
.IsEmpty()) {
1016 aFamilies
.RemoveElementAt(0);
1020 // Check for duplicate family entries (can occur if there is a bundled font
1021 // that has the same name as a system-installed one); in this case we keep
1022 // the bundled one as it will always be exposed.
1024 for (size_t i
= 1; i
< count
; ++i
) {
1025 if (aFamilies
[i
].mKey
.Equals(aFamilies
[i
- 1].mKey
)) {
1026 // Decide whether to discard the current entry or the preceding one
1028 aFamilies
[i
].mBundled
&& !aFamilies
[i
- 1].mBundled
? i
- 1 : i
;
1029 aFamilies
.RemoveElementAt(discard
);
1036 header
.mFamilies
= Alloc(count
* sizeof(Family
));
1037 if (header
.mFamilies
.IsNull()) {
1041 // We can't call Families() here because the mFamilyCount field has not yet
1043 auto* families
= header
.mFamilies
.ToArray
<Family
>(this, count
);
1044 for (size_t i
= 0; i
< count
; i
++) {
1045 (void)new (&families
[i
]) Family(this, aFamilies
[i
]);
1046 LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i
,
1047 aFamilies
[i
].mName
.get()));
1050 header
.mFamilyCount
= count
;
1053 void FontList::SetAliases(
1054 nsClassHashtable
<nsCStringHashKey
, AliasData
>& aAliasTable
) {
1055 MOZ_ASSERT(XRE_IsParentProcess());
1057 Header
& header
= GetHeader();
1059 // Build an array of Family::InitData records based on the entries in
1060 // aAliasTable, then sort them and store into the fontlist.
1061 nsTArray
<Family::InitData
> aliasArray
;
1062 aliasArray
.SetCapacity(aAliasTable
.Count());
1063 for (const auto& entry
: aAliasTable
) {
1064 aliasArray
.AppendElement(Family::InitData(
1065 entry
.GetKey(), entry
.GetData()->mBaseFamily
, entry
.GetData()->mIndex
,
1066 entry
.GetData()->mVisibility
, entry
.GetData()->mBundled
,
1067 entry
.GetData()->mBadUnderline
, entry
.GetData()->mForceClassic
, true));
1071 size_t count
= aliasArray
.Length();
1073 // Drop any entry with empty family-name as being unusable.
1074 if (count
&& aliasArray
[0].mKey
.IsEmpty()) {
1075 aliasArray
.RemoveElementAt(0);
1079 if (count
< header
.mAliasCount
) {
1080 // This shouldn't happen, but handle it safely by just bailing out.
1081 NS_WARNING("cannot reduce number of aliases");
1084 fontlist::Pointer ptr
= Alloc(count
* sizeof(Family
));
1085 auto* aliases
= ptr
.ToArray
<Family
>(this, count
);
1086 for (size_t i
= 0; i
< count
; i
++) {
1087 (void)new (&aliases
[i
]) Family(this, aliasArray
[i
]);
1088 LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i
,
1089 aliasArray
[i
].mKey
.get(), aliasArray
[i
].mName
.get()));
1090 aliases
[i
].SetFacePtrs(this, aAliasTable
.Get(aliasArray
[i
].mKey
)->mFaces
);
1091 if (LOG_FONTLIST_ENABLED()) {
1092 const auto& faces
= aAliasTable
.Get(aliasArray
[i
].mKey
)->mFaces
;
1093 for (unsigned j
= 0; j
< faces
.Length(); j
++) {
1094 auto* face
= faces
[j
].ToPtr
<const Face
>(this);
1095 const nsCString
& desc
= face
->mDescriptor
.AsString(this);
1096 nsAutoCString weight
, style
, stretch
;
1097 face
->mWeight
.ToString(weight
);
1098 face
->mStyle
.ToString(style
);
1099 face
->mStretch
.ToString(stretch
);
1101 ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
1103 desc
.get(), face
->mIndex
, weight
.get(), style
.get(),
1109 // Set the pointer before the count, so that any other process trying to read
1110 // will not risk out-of-bounds access to the array.
1111 header
.mAliases
= ptr
;
1112 header
.mAliasCount
.store(count
);
1115 void FontList::SetLocalNames(
1116 nsTHashMap
<nsCStringHashKey
, LocalFaceRec::InitData
>& aLocalNameTable
) {
1117 MOZ_ASSERT(XRE_IsParentProcess());
1118 Header
& header
= GetHeader();
1119 if (header
.mLocalFaceCount
> 0) {
1120 return; // already been done!
1122 auto faceArray
= ToTArray
<nsTArray
<nsCString
>>(aLocalNameTable
.Keys());
1124 size_t count
= faceArray
.Length();
1125 Family
* families
= Families();
1126 fontlist::Pointer ptr
= Alloc(count
* sizeof(LocalFaceRec
));
1127 auto* faces
= ptr
.ToArray
<LocalFaceRec
>(this, count
);
1128 for (size_t i
= 0; i
< count
; i
++) {
1129 (void)new (&faces
[i
]) LocalFaceRec();
1130 const auto& rec
= aLocalNameTable
.Get(faceArray
[i
]);
1131 faces
[i
].mKey
.Assign(faceArray
[i
], this);
1132 // Local face name records will refer to the canonical family name; we don't
1133 // need to search aliases here.
1134 const auto* family
= FindFamily(rec
.mFamilyName
, /*aPrimaryNameOnly*/ true);
1136 // Skip this record if the family was excluded by the font whitelist pref.
1139 faces
[i
].mFamilyIndex
= family
- families
;
1140 if (rec
.mFaceIndex
== uint32_t(-1)) {
1141 // The InitData record contains an mFaceDescriptor rather than an index,
1142 // so now we need to look for the appropriate index in the family.
1143 faces
[i
].mFaceIndex
= 0;
1144 const Pointer
* faceList
=
1145 static_cast<const Pointer
*>(family
->Faces(this));
1146 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
1147 if (!faceList
[j
].IsNull()) {
1148 auto* f
= faceList
[j
].ToPtr
<const Face
>(this);
1149 if (f
&& rec
.mFaceDescriptor
== f
->mDescriptor
.AsString(this)) {
1150 faces
[i
].mFaceIndex
= j
;
1156 faces
[i
].mFaceIndex
= rec
.mFaceIndex
;
1159 header
.mLocalFaces
= ptr
;
1160 header
.mLocalFaceCount
.store(count
);
1163 nsCString
FontList::LocalizedFamilyName(const Family
* aFamily
) {
1164 // If the given family was created for an alternate locale or legacy name,
1165 // search for a standard family that corresponds to it. This is a linear
1166 // search of the font list, but (a) this is only used to show names in
1167 // Preferences, so is not performance-critical for layout etc.; and (b) few
1168 // such family names are normally present anyway, the vast majority of fonts
1169 // just have a single family name and we return it directly.
1170 if (aFamily
->IsAltLocaleFamily()) {
1171 // Currently only the Windows backend actually does this; on other systems,
1172 // the family index is unused and will be kNoIndex for all fonts.
1173 if (aFamily
->Index() != Family::kNoIndex
) {
1174 const Family
* families
= Families();
1175 for (uint32_t i
= 0; i
< NumFamilies(); ++i
) {
1176 if (families
[i
].Index() == aFamily
->Index() &&
1177 families
[i
].IsBundled() == aFamily
->IsBundled() &&
1178 !families
[i
].IsAltLocaleFamily()) {
1179 return families
[i
].DisplayName().AsString(this);
1185 // For standard families (or if we failed to find the expected standard
1186 // family for some reason), just return the DisplayName.
1187 return aFamily
->DisplayName().AsString(this);
1190 Family
* FontList::FindFamily(const nsCString
& aName
, bool aPrimaryNameOnly
) {
1191 struct FamilyNameComparator
{
1192 FamilyNameComparator(FontList
* aList
, const nsCString
& aTarget
)
1193 : mList(aList
), mTarget(aTarget
) {}
1195 int operator()(const Family
& aVal
) const {
1196 return Compare(mTarget
,
1197 nsDependentCString(aVal
.Key().BeginReading(mList
)));
1202 const nsCString
& mTarget
;
1205 const Header
& header
= GetHeader();
1207 Family
* families
= Families();
1213 if (BinarySearchIf(families
, 0, header
.mFamilyCount
,
1214 FamilyNameComparator(this, aName
), &match
)) {
1215 return &families
[match
];
1218 if (aPrimaryNameOnly
) {
1222 if (header
.mAliasCount
) {
1223 Family
* aliases
= AliasFamilies();
1225 if (aliases
&& BinarySearchIf(aliases
, 0, header
.mAliasCount
,
1226 FamilyNameComparator(this, aName
), &match
)) {
1227 return &aliases
[match
];
1232 // For Windows only, because of how DWrite munges font family names in some
1234 // 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
1235 // and discussion on the OpenType list), try stripping a possible style-name
1236 // suffix from the end of the requested family name.
1237 // After the deferred font loader has finished, this is no longer needed as
1238 // the "real" family names will have been found in AliasFamilies() above.
1239 if (aName
.Contains(' ')) {
1240 auto pfl
= gfxPlatformFontList::PlatformFontList();
1241 pfl
->mLock
.AssertCurrentThreadIn();
1242 if (header
.mAliasCount
) {
1243 // Aliases have been fully loaded by the parent process, so just discard
1244 // any stray mAliasTable and mLocalNameTable entries from earlier calls
1245 // to this code, and return.
1246 pfl
->mAliasTable
.Clear();
1247 pfl
->mLocalNameTable
.Clear();
1251 // Do we already have an aliasData record for this name? If so, we just
1252 // return its base family.
1253 if (auto lookup
= pfl
->mAliasTable
.Lookup(aName
)) {
1254 return FindFamily(lookup
.Data()->mBaseFamily
, true);
1257 // Strip the style suffix (after last space in the name) to get a "base"
1259 const char* data
= aName
.BeginReading();
1260 int32_t index
= aName
.Length();
1261 while (--index
> 0) {
1262 if (data
[index
] == ' ') {
1269 nsAutoCString
base(Substring(aName
, 0, index
));
1270 if (BinarySearchIf(families
, 0, header
.mFamilyCount
,
1271 FamilyNameComparator(this, base
), &match
)) {
1272 // This may be a possible base family to satisfy the search; call
1273 // ReadFaceNamesForFamily and see if the desired name ends up in
1275 // Note that ReadFaceNamesForFamily may store entries in mAliasTable
1276 // (and mLocalNameTable), but if this is happening in a content
1277 // process (which is the common case) those entries will not be saved
1278 // into the shared font list; they're just used here until the "real"
1279 // alias list is ready, then discarded.
1280 Family
* baseFamily
= &families
[match
];
1281 pfl
->ReadFaceNamesForFamily(baseFamily
, false);
1282 if (auto lookup
= pfl
->mAliasTable
.Lookup(aName
)) {
1283 if (lookup
.Data()->mFaces
.Length() != baseFamily
->NumFaces()) {
1284 // If the alias family doesn't have all the faces of the base family,
1285 // then style matching may end up resolving to a face that isn't
1286 // supposed to be available in the legacy styled family. To ensure
1287 // such mis-styling will get fixed, we start the async font info
1288 // loader (if it hasn't yet been triggered), which will pull in the
1289 // full metadata we need and then force a reflow.
1290 pfl
->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true);
1301 LocalFaceRec
* FontList::FindLocalFace(const nsCString
& aName
) {
1302 struct FaceNameComparator
{
1303 FaceNameComparator(FontList
* aList
, const nsCString
& aTarget
)
1304 : mList(aList
), mTarget(aTarget
) {}
1306 int operator()(const LocalFaceRec
& aVal
) const {
1307 return Compare(mTarget
,
1308 nsDependentCString(aVal
.mKey
.BeginReading(mList
)));
1313 const nsCString
& mTarget
;
1316 Header
& header
= GetHeader();
1318 LocalFaceRec
* faces
= LocalFaces();
1320 if (faces
&& BinarySearchIf(faces
, 0, header
.mLocalFaceCount
,
1321 FaceNameComparator(this, aName
), &match
)) {
1322 return &faces
[match
];
1328 void FontList::SearchForLocalFace(const nsACString
& aName
, Family
** aFamily
,
1330 Header
& header
= GetHeader();
1331 MOZ_ASSERT(header
.mLocalFaceCount
== 0,
1332 "do not use when local face names are already set up!");
1334 ("(shared-fontlist) local face search for (%s)", aName
.BeginReading()));
1335 char initial
= aName
[0];
1336 Family
* families
= Families();
1340 for (uint32_t i
= 0; i
< header
.mFamilyCount
; i
++) {
1341 Family
* family
= &families
[i
];
1342 if (family
->Key().BeginReading(this)[0] != initial
) {
1345 LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
1346 family
->Key().AsString(this).BeginReading()));
1347 if (!family
->IsInitialized()) {
1348 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family
)) {
1352 Pointer
* faces
= family
->Faces(this);
1356 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
1357 auto* face
= faces
[j
].ToPtr
<Face
>(this);
1361 nsAutoCString psname
, fullname
;
1362 if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
1363 family
, face
, psname
, fullname
)) {
1364 LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
1365 psname
.get(), fullname
.get()));
1366 ToLowerCase(psname
);
1367 ToLowerCase(fullname
);
1368 if (aName
== psname
|| aName
== fullname
) {
1378 size_t FontList::SizeOfIncludingThis(
1379 mozilla::MallocSizeOf aMallocSizeOf
) const {
1380 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
1383 size_t FontList::SizeOfExcludingThis(
1384 mozilla::MallocSizeOf aMallocSizeOf
) const {
1385 size_t result
= mBlocks
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1386 for (const auto& b
: mBlocks
) {
1387 result
+= aMallocSizeOf(b
.get()) + aMallocSizeOf(b
->mShmem
.get());
1392 size_t FontList::AllocatedShmemSize() const {
1394 for (const auto& b
: mBlocks
) {
1395 result
+= b
->BlockSize();
1400 } // namespace fontlist
1401 } // namespace mozilla