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
) const {
46 uint32_t block
= Block();
47 // If the Pointer refers to a block we have not yet mapped in this process,
48 // we first need to retrieve new block handle(s) from the parent and update
50 auto& blocks
= aFontList
->mBlocks
;
51 if (block
>= blocks
.Length()) {
52 if (XRE_IsParentProcess()) {
53 // Shouldn't happen! A content process tried to pass a bad Pointer?
56 // UpdateShmBlocks can fail, if the parent has replaced the font list with
57 // a new generation. In that case we just return null, and whatever font
58 // the content process was trying to use will appear unusable for now. It's
59 // about to receive a notification of the new font list anyhow, at which
60 // point it will flush its caches and reflow everything, so the temporary
61 // failure of this font will be forgotten.
62 // We also return null if we're not on the main thread, as we cannot safely
63 // do the IPC messaging needed here.
64 if (!NS_IsMainThread() || !aFontList
->UpdateShmBlocks()) {
67 MOZ_ASSERT(block
< blocks
.Length(), "failure in UpdateShmBlocks?");
68 // This is wallpapering bug 1667977; it's unclear if we will always survive
69 // this, as the content process may be unable to shape/render text if all
70 // font lookups are failing.
71 // In at least some cases, however, this can occur transiently while the
72 // font list is being rebuilt by the parent; content will then be notified
73 // that the list has changed, and should refresh everything successfully.
74 if (block
>= blocks
.Length()) {
78 return static_cast<char*>(blocks
[block
]->Memory()) + Offset();
81 void String::Assign(const nsACString
& aString
, FontList
* aList
) {
82 // We only assign to previously-empty strings; they are never modified
83 // after initial assignment.
84 MOZ_ASSERT(mPointer
.IsNull());
85 mLength
= aString
.Length();
86 mPointer
= aList
->Alloc(mLength
+ 1);
87 char* p
= static_cast<char*>(mPointer
.ToPtr(aList
));
88 std::memcpy(p
, aString
.BeginReading(), mLength
);
92 Family::Family(FontList
* aList
, const InitData
& aData
)
94 mKey(aList
, aData
.mKey
),
95 mName(aList
, aData
.mName
),
96 mCharacterMap(Pointer::Null()),
97 mFaces(Pointer::Null()),
99 mVisibility(aData
.mVisibility
),
101 mIsBundled(aData
.mBundled
),
102 mIsBadUnderlineFamily(aData
.mBadUnderline
),
103 mIsForceClassic(aData
.mForceClassic
),
104 mIsAltLocale(aData
.mAltLocale
) {}
106 class SetCharMapRunnable
: public mozilla::Runnable
{
108 SetCharMapRunnable(uint32_t aListGeneration
, Face
* aFace
,
109 gfxCharacterMap
* aCharMap
)
110 : Runnable("SetCharMapRunnable"),
111 mListGeneration(aListGeneration
),
113 mCharMap(aCharMap
) {}
115 NS_IMETHOD
Run() override
{
116 auto* list
= gfxPlatformFontList::PlatformFontList()->SharedFontList();
117 if (!list
|| list
->GetGeneration() != mListGeneration
) {
120 dom::ContentChild::GetSingleton()->SendSetCharacterMap(
121 mListGeneration
, list
->ToSharedPointer(mFace
), *mCharMap
);
126 uint32_t mListGeneration
;
128 RefPtr
<gfxCharacterMap
> mCharMap
;
131 void Face::SetCharacterMap(FontList
* aList
, gfxCharacterMap
* aCharMap
) {
132 if (!XRE_IsParentProcess()) {
133 if (NS_IsMainThread()) {
134 Pointer ptr
= aList
->ToSharedPointer(this);
135 dom::ContentChild::GetSingleton()->SendSetCharacterMap(
136 aList
->GetGeneration(), ptr
, *aCharMap
);
138 NS_DispatchToMainThread(
139 new SetCharMapRunnable(aList
->GetGeneration(), this, aCharMap
));
143 auto pfl
= gfxPlatformFontList::PlatformFontList();
144 mCharacterMap
= pfl
->GetShmemCharMap(aCharMap
);
147 void Family::AddFaces(FontList
* aList
, const nsTArray
<Face::InitData
>& aFaces
) {
148 MOZ_ASSERT(XRE_IsParentProcess());
149 if (mFaceCount
> 0) {
150 // Already initialized!
154 uint32_t count
= aFaces
.Length();
155 bool isSimple
= false;
156 // A family is "simple" (i.e. simplified style selection may be used instead
157 // of the full CSS font-matching algorithm) if there is at maximum one normal,
158 // bold, italic, and bold-italic face; in this case, they are stored at known
159 // positions in the mFaces array.
160 const Face::InitData
* slots
[4] = {nullptr, nullptr, nullptr, nullptr};
161 if (count
>= 2 && count
<= 4) {
162 // Check if this can be treated as a "simple" family
164 for (const auto& f
: aFaces
) {
165 if (!f
.mWeight
.IsSingle() || !f
.mStretch
.IsSingle() ||
166 !f
.mStyle
.IsSingle()) {
170 if (!f
.mStretch
.Min().IsNormal()) {
174 // Figure out which slot (0-3) this face belongs in
176 static_assert((kBoldMask
| kItalicMask
) == 0b11, "bad bold/italic bits");
177 if (f
.mWeight
.Min().IsBold()) {
180 if (f
.mStyle
.Min().IsItalic() || f
.mStyle
.Min().IsOblique()) {
184 // More than one face mapped to the same slot - not a simple family!
191 // Ensure all 4 slots will exist, even if some are empty.
196 // Allocate space for the face records, and initialize them.
197 // coverity[suspicious_sizeof]
198 Pointer p
= aList
->Alloc(count
* sizeof(Pointer
));
199 auto facePtrs
= static_cast<Pointer
*>(p
.ToPtr(aList
));
200 for (size_t i
= 0; i
< count
; i
++) {
201 if (isSimple
&& !slots
[i
]) {
202 facePtrs
[i
] = Pointer::Null();
204 const auto* initData
= isSimple
? slots
[i
] : &aFaces
[i
];
205 Pointer fp
= aList
->Alloc(sizeof(Face
));
206 auto* face
= static_cast<Face
*>(fp
.ToPtr(aList
));
207 (void)new (face
) Face(aList
, *initData
);
209 if (initData
->mCharMap
) {
210 face
->SetCharacterMap(aList
, initData
->mCharMap
);
215 mIsSimple
= isSimple
;
217 mFaceCount
.store(count
);
219 if (LOG_FONTLIST_ENABLED()) {
220 const nsCString
& fam
= DisplayName().AsString(aList
);
221 for (unsigned j
= 0; j
< aFaces
.Length(); j
++) {
222 nsAutoCString weight
, style
, stretch
;
223 aFaces
[j
].mWeight
.ToString(weight
);
224 aFaces
[j
].mStyle
.ToString(style
);
225 aFaces
[j
].mStretch
.ToString(stretch
);
227 ("(shared-fontlist) family (%s) added face (%s) index %u, weight "
228 "%s, style %s, stretch %s",
229 fam
.get(), aFaces
[j
].mDescriptor
.get(), aFaces
[j
].mIndex
,
230 weight
.get(), style
.get(), stretch
.get()));
235 bool Family::FindAllFacesForStyleInternal(FontList
* aList
,
236 const gfxFontStyle
& aStyle
,
237 nsTArray
<Face
*>& aFaceList
) const {
238 MOZ_ASSERT(aFaceList
.IsEmpty());
239 if (!IsInitialized()) {
243 Pointer
* facePtrs
= Faces(aList
);
248 // Depending on the kind of family, we have to do varying amounts of work
249 // to figure out what face(s) to use for the requested style properties.
251 // If the family has only one face, we simply use it; no further style
252 // checking needed. (However, for bitmap fonts we may still need to check
253 // whether the size is acceptable.)
254 if (NumFaces() == 1) {
255 MOZ_ASSERT(!facePtrs
[0].IsNull());
256 Face
* face
= static_cast<Face
*>(facePtrs
[0].ToPtr(aList
));
257 if (face
&& face
->HasValidDescriptor()) {
258 aFaceList
.AppendElement(face
);
259 #ifdef MOZ_WIDGET_GTK
268 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
269 // or some subset of these. In this case, we have exactly 4 entries in
270 // mAvailableFonts, stored in the above order; note that some of the entries
271 // may be nullptr. We can then pick the required entry based on whether the
272 // request is for bold or non-bold, italic or non-italic, without running
273 // the more complex matching algorithm used for larger families with many
274 // weights and/or widths.
277 // Family has no more than the "standard" 4 faces, at fixed indexes;
278 // calculate which one we want.
279 // Note that we cannot simply return it as not all 4 faces are necessarily
281 bool wantBold
= aStyle
.weight
>= FontWeight(600);
282 bool wantItalic
= !aStyle
.style
.IsNormal();
284 (wantItalic
? kItalicMask
: 0) | (wantBold
? kBoldMask
: 0);
286 // If the desired style is available, use it directly.
287 Face
* face
= static_cast<Face
*>(facePtrs
[faceIndex
].ToPtr(aList
));
288 if (face
&& face
->HasValidDescriptor()) {
289 aFaceList
.AppendElement(face
);
290 #ifdef MOZ_WIDGET_GTK
298 // Order to check fallback faces in a simple family, depending on the
300 static const uint8_t simpleFallbacks
[4][3] = {
301 {kBoldFaceIndex
, kItalicFaceIndex
,
302 kBoldItalicFaceIndex
}, // fallback sequence for Regular
303 {kRegularFaceIndex
, kBoldItalicFaceIndex
, kItalicFaceIndex
}, // Bold
304 {kBoldItalicFaceIndex
, kRegularFaceIndex
, kBoldFaceIndex
}, // Italic
305 {kItalicFaceIndex
, kBoldFaceIndex
, kRegularFaceIndex
} // BoldItalic
307 const uint8_t* order
= simpleFallbacks
[faceIndex
];
309 for (uint8_t trial
= 0; trial
< 3; ++trial
) {
310 // check remaining faces in order of preference to find the first that
312 face
= static_cast<Face
*>(facePtrs
[order
[trial
]].ToPtr(aList
));
313 if (face
&& face
->HasValidDescriptor()) {
314 aFaceList
.AppendElement(face
);
315 #ifdef MOZ_WIDGET_GTK
324 // We can only reach here if we failed to resolve the face pointer, which
325 // can happen if we're on a stylo thread and caught the font list being
326 // updated; in that case we just fail quietly and let font fallback do
327 // something for the time being.
331 // Pick the font(s) that are closest to the desired weight, style, and
332 // stretch. Iterate over all fonts, measuring the weight/style distance.
333 // Because of unicode-range values, there may be more than one font for a
334 // given but the 99% use case is only a single font entry per
335 // weight/style/stretch distance value. To optimize this, only add entries
336 // to the matched font array when another entry already has the same
337 // weight/style/stretch distance and add the last matched font entry. For
338 // normal platform fonts with a single font entry for each
339 // weight/style/stretch combination, only the last matched font entry will
341 double minDistance
= INFINITY
;
342 Face
* matched
= nullptr;
343 // Keep track of whether we've included any non-scalable font resources in
345 bool anyNonScalable
= false;
346 for (uint32_t i
= 0; i
< NumFaces(); i
++) {
347 Face
* face
= static_cast<Face
*>(facePtrs
[i
].ToPtr(aList
));
349 // weight/style/stretch priority: stretch >> style >> weight
350 double distance
= WSSDistance(face
, aStyle
);
351 if (distance
< minDistance
) {
353 if (!aFaceList
.IsEmpty()) {
356 minDistance
= distance
;
357 } else if (distance
== minDistance
) {
359 aFaceList
.AppendElement(matched
);
360 #ifdef MOZ_WIDGET_GTK
361 if (matched
->mSize
) {
362 anyNonScalable
= true;
371 MOZ_ASSERT(matched
, "didn't match a font within a family");
373 aFaceList
.AppendElement(matched
);
374 #ifdef MOZ_WIDGET_GTK
375 if (matched
->mSize
) {
376 anyNonScalable
= true;
381 return anyNonScalable
;
384 void Family::FindAllFacesForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
385 nsTArray
<Face
*>& aFaceList
,
386 bool aIgnoreSizeTolerance
) const {
387 #ifdef MOZ_WIDGET_GTK
388 bool anyNonScalable
=
392 FindAllFacesForStyleInternal(aList
, aStyle
, aFaceList
);
394 #ifdef MOZ_WIDGET_GTK
395 // aFaceList now contains whatever faces are the best style match for
396 // the requested style. If specifically-sized bitmap faces are supported,
397 // we need to additionally filter the list to choose the appropriate size.
399 // It would be slightly more efficient to integrate this directly into the
400 // face-selection algorithm above, but it's a rare case that doesn't apply
401 // at all to most font families.
403 // Currently we only support pixel-sized bitmap font faces on Linux/Gtk (i.e.
404 // when using the gfxFcPlatformFontList implementation), so this filtering is
405 // not needed on other platforms.
407 // (Note that color-bitmap emoji fonts like Apple Color Emoji or Noto Color
408 // Emoji don't count here; they package multiple bitmap sizes into a single
409 // OpenType wrapper, so they appear as a single "scalable" face in our list.)
410 if (anyNonScalable
) {
413 for (const auto& f
: aFaceList
) {
415 // Scalable face; no size distance to compute.
418 gfxFloat d
= fabs(gfxFloat(f
->mSize
) - aStyle
.size
);
419 if (!aIgnoreSizeTolerance
&& (d
* 5.0 > f
->mSize
)) {
420 continue; // Too far from the requested size, ignore.
422 // If we haven't found a "best" bitmap size yet, or if this is a better
423 // match, remember it.
424 if (!best
|| d
< dist
) {
429 // Discard all faces except the chosen "best" size; or if no pixel size was
430 // chosen, all except scalable faces.
431 // This may eliminate *all* faces in the family, if all were bitmaps and
432 // none was a good enough size match, in which case we'll fall back to the
433 // next font-family name.
434 aFaceList
.RemoveElementsBy([=](const auto& e
) { return e
->mSize
!= best
; });
439 Face
* Family::FindFaceForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
440 bool aIgnoreSizeTolerance
) const {
441 AutoTArray
<Face
*, 4> faces
;
442 FindAllFacesForStyle(aList
, aStyle
, faces
, aIgnoreSizeTolerance
);
443 return faces
.IsEmpty() ? nullptr : faces
[0];
446 void Family::SearchAllFontsForChar(FontList
* aList
,
447 GlobalFontMatch
* aMatchData
) {
448 const SharedBitSet
* charmap
=
449 static_cast<const SharedBitSet
*>(mCharacterMap
.ToPtr(aList
));
451 // If the face list is not yet initialized, or if character maps have
452 // not been loaded, go ahead and do this now (by sending a message to the
453 // parent process, if we're running in a child).
454 // After this, all faces should have their mCharacterMap set up, and the
455 // family's mCharacterMap should also be set; but in the code below we
456 // don't assume this all succeeded, so it still checks.
457 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this,
461 charmap
= static_cast<const SharedBitSet
*>(mCharacterMap
.ToPtr(aList
));
463 if (charmap
&& !charmap
->test(aMatchData
->mCh
)) {
467 uint32_t numFaces
= NumFaces();
468 uint32_t charMapsLoaded
= 0; // number of faces whose charmap is loaded
469 Pointer
* facePtrs
= Faces(aList
);
473 for (uint32_t i
= 0; i
< numFaces
; i
++) {
474 Face
* face
= static_cast<Face
*>(facePtrs
[i
].ToPtr(aList
));
478 MOZ_ASSERT(face
->HasValidDescriptor());
479 // Get the face's character map, if available (may be null!)
481 static_cast<const SharedBitSet
*>(face
->mCharacterMap
.ToPtr(aList
));
485 // Check style distance if the char is supported, or if charmap not known
486 // (so that we don't trigger cmap-loading for faces that would be a worse
487 // match than what we've already found).
488 if (!charmap
|| charmap
->test(aMatchData
->mCh
)) {
489 double distance
= WSSDistance(face
, aMatchData
->mStyle
);
490 if (distance
< aMatchData
->mMatchDistance
) {
491 // It's a better style match: get a fontEntry, and if we haven't
492 // already checked character coverage, do it now (note that
493 // HasCharacter() will trigger loading the fontEntry's cmap, if
495 RefPtr
<gfxFontEntry
> fe
=
496 gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face
,
501 if (!charmap
&& !fe
->HasCharacter(aMatchData
->mCh
)) {
504 if (aMatchData
->mPresentation
!= eFontPresentation::Any
) {
505 RefPtr
<gfxFont
> font
= fe
->FindOrMakeFont(&aMatchData
->mStyle
);
510 font
->HasColorGlyphFor(aMatchData
->mCh
, aMatchData
->mNextCh
);
511 if (hasColorGlyph
!= PrefersColor(aMatchData
->mPresentation
)) {
512 distance
+= kPresentationMismatch
;
513 if (distance
>= aMatchData
->mMatchDistance
) {
518 aMatchData
->mBestMatch
= fe
;
519 aMatchData
->mMatchDistance
= distance
;
520 aMatchData
->mMatchedSharedFamily
= this;
524 if (mCharacterMap
.IsNull() && charMapsLoaded
== numFaces
) {
525 SetupFamilyCharMap(aList
);
529 void Family::SetFacePtrs(FontList
* aList
, nsTArray
<Pointer
>& aFaces
) {
530 if (aFaces
.Length() >= 2 && aFaces
.Length() <= 4) {
531 // Check whether the faces meet the criteria for a "simple" family: no more
532 // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
533 // them at the appropriate slots in mFaces and set the mIsSimple flag to
534 // accelerate font-matching.
535 bool isSimple
= true;
536 Pointer slots
[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
538 for (const Pointer
& fp
: aFaces
) {
539 const Face
* f
= static_cast<const Face
*>(fp
.ToPtr(aList
));
540 if (!f
->mWeight
.IsSingle() || !f
->mStyle
.IsSingle() ||
541 !f
->mStretch
.IsSingle()) {
545 if (!f
->mStretch
.Min().IsNormal()) {
550 if (f
->mWeight
.Min().IsBold()) {
553 if (f
->mStyle
.Min().IsItalic() || f
->mStyle
.Min().IsOblique()) {
556 if (!slots
[slot
].IsNull()) {
563 size_t size
= 4 * sizeof(Pointer
);
564 mFaces
= aList
->Alloc(size
);
565 memcpy(mFaces
.ToPtr(aList
), slots
, size
);
571 size_t size
= aFaces
.Length() * sizeof(Pointer
);
572 mFaces
= aList
->Alloc(size
);
573 memcpy(mFaces
.ToPtr(aList
), aFaces
.Elements(), size
);
574 mFaceCount
.store(aFaces
.Length());
577 void Family::SetupFamilyCharMap(FontList
* aList
) {
578 // Set the character map of the family to the union of all the face cmaps,
579 // to allow font fallback searches to more rapidly reject the family.
580 if (!mCharacterMap
.IsNull()) {
583 if (!XRE_IsParentProcess()) {
584 // |this| could be a Family record in either the Families() or Aliases()
586 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
587 aList
->GetGeneration(), aList
->ToSharedPointer(this));
590 gfxSparseBitSet familyMap
;
591 Pointer firstMapShmPointer
;
592 SharedBitSet
* firstMap
= nullptr;
594 Pointer
* faces
= Faces(aList
);
598 for (size_t i
= 0; i
< NumFaces(); i
++) {
599 auto f
= static_cast<Face
*>(faces
[i
].ToPtr(aList
));
601 continue; // Skip missing face (in an incomplete "simple" family)
603 auto faceMap
= static_cast<SharedBitSet
*>(f
->mCharacterMap
.ToPtr(aList
));
605 continue; // If there's a face where setting up the cmap failed, we skip
610 firstMapShmPointer
= f
->mCharacterMap
;
611 } else if (faceMap
!= firstMap
) {
613 familyMap
.Union(*firstMap
);
616 familyMap
.Union(*faceMap
);
619 // If we created a merged cmap, we need to save that on the family; or if we
620 // found no usable cmaps at all, we need to store the empty familyMap so that
621 // we won't repeatedly attempt this for an unusable family.
622 if (merged
|| firstMapShmPointer
.IsNull()) {
624 gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap
);
626 // If all [usable] faces had the same cmap, we can just share it.
627 mCharacterMap
= firstMapShmPointer
;
631 FontList::FontList(uint32_t aGeneration
) {
632 if (XRE_IsParentProcess()) {
633 // Create the initial shared block, and initialize Header
634 if (AppendShmBlock(SHM_BLOCK_SIZE
)) {
635 Header
& header
= GetHeader();
636 header
.mBlockHeader
.mAllocated
= sizeof(Header
);
637 header
.mGeneration
= aGeneration
;
638 header
.mFamilyCount
= 0;
639 header
.mBlockCount
.store(1);
640 header
.mAliasCount
.store(0);
641 header
.mLocalFaceCount
.store(0);
642 header
.mFamilies
= Pointer::Null();
643 header
.mAliases
= Pointer::Null();
644 header
.mLocalFaces
= Pointer::Null();
646 MOZ_CRASH("parent: failed to initialize FontList");
649 // Initialize using the list of shmem blocks passed by the parent via
650 // SetXPCOMProcessAttributes.
651 auto& blocks
= dom::ContentChild::GetSingleton()->SharedFontListBlocks();
652 for (auto handle
: blocks
) {
653 auto newShm
= MakeUnique
<base::SharedMemory
>();
654 if (!newShm
->IsHandleValid(handle
)) {
655 // Bail out and let UpdateShmBlocks try to do its thing below.
658 if (!newShm
->SetHandle(handle
, true)) {
659 MOZ_CRASH("failed to set shm handle");
661 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
662 MOZ_CRASH("failed to map shared memory");
664 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
665 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
666 if (size
!= SHM_BLOCK_SIZE
) {
668 if (!newShm
->Map(size
) || !newShm
->memory()) {
669 MOZ_CRASH("failed to map shared memory");
672 mBlocks
.AppendElement(new ShmBlock(std::move(newShm
)));
675 // Update in case of any changes since the initial message was sent.
676 for (unsigned retryCount
= 0; retryCount
< 3; ++retryCount
) {
677 if (UpdateShmBlocks()) {
680 // The only reason for UpdateShmBlocks to fail is if the parent recreated
681 // the list after we read its first block, but before we finished getting
682 // them all, and so the generation check failed on a subsequent request.
683 // Discarding whatever we've got and retrying should get us a new,
684 // consistent set of memory blocks in this case. If this doesn't work
685 // after a couple of retries, bail out.
688 NS_WARNING("child: failed to initialize shared FontList");
692 FontList::~FontList() { DetachShmBlocks(); }
694 bool FontList::AppendShmBlock(uint32_t aSizeNeeded
) {
695 MOZ_ASSERT(XRE_IsParentProcess());
696 uint32_t size
= std::max(aSizeNeeded
, SHM_BLOCK_SIZE
);
697 auto newShm
= MakeUnique
<base::SharedMemory
>();
698 if (!newShm
->CreateFreezeable(size
)) {
699 MOZ_CRASH("failed to create shared memory");
702 if (!newShm
->Map(size
) || !newShm
->memory()) {
703 MOZ_CRASH("failed to map shared memory");
706 auto readOnly
= MakeUnique
<base::SharedMemory
>();
707 if (!newShm
->ReadOnlyCopy(readOnly
.get())) {
708 MOZ_CRASH("failed to create read-only copy");
712 ShmBlock
* block
= new ShmBlock(std::move(newShm
));
713 block
->Allocated() = sizeof(BlockHeader
);
714 block
->BlockSize() = size
;
716 mBlocks
.AppendElement(block
);
717 GetHeader().mBlockCount
.store(mBlocks
.Length());
719 mReadOnlyShmems
.AppendElement(std::move(readOnly
));
721 // We don't need to broadcast the addition of the initial block,
722 // because child processes can't have initialized their list at all
723 // prior to the first block being set up.
724 if (mBlocks
.Length() > 1) {
725 if (NS_IsMainThread()) {
726 dom::ContentParent::BroadcastShmBlockAdded(GetGeneration(),
727 mBlocks
.Length() - 1);
729 NS_DispatchToMainThread(NS_NewRunnableFunction(
730 "ShmBlockAdded callback",
731 [generation
= GetGeneration(), index
= mBlocks
.Length() - 1] {
732 dom::ContentParent::BroadcastShmBlockAdded(generation
, index
);
740 void FontList::ShmBlockAdded(uint32_t aGeneration
, uint32_t aIndex
,
741 base::SharedMemoryHandle aHandle
) {
742 MOZ_ASSERT(!XRE_IsParentProcess());
743 MOZ_ASSERT(mBlocks
.Length() > 0);
745 auto newShm
= MakeUnique
<base::SharedMemory
>();
746 if (!newShm
->IsHandleValid(aHandle
)) {
749 if (!newShm
->SetHandle(aHandle
, true)) {
750 MOZ_CRASH("failed to set shm handle");
753 if (aIndex
!= mBlocks
.Length()) {
756 if (aGeneration
!= GetGeneration()) {
760 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
761 MOZ_CRASH("failed to map shared memory");
764 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
765 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
766 if (size
!= SHM_BLOCK_SIZE
) {
768 if (!newShm
->Map(size
) || !newShm
->memory()) {
769 MOZ_CRASH("failed to map shared memory");
773 mBlocks
.AppendElement(new ShmBlock(std::move(newShm
)));
776 void FontList::DetachShmBlocks() {
777 for (auto& i
: mBlocks
) {
781 mReadOnlyShmems
.Clear();
784 FontList::ShmBlock
* FontList::GetBlockFromParent(uint32_t aIndex
) {
785 MOZ_ASSERT(!XRE_IsParentProcess());
786 // If we have no existing blocks, we don't want a generation check yet;
787 // the header in the first block will define the generation of this list
788 uint32_t generation
= aIndex
== 0 ? 0 : GetGeneration();
789 base::SharedMemoryHandle handle
= base::SharedMemory::NULLHandle();
790 if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
791 generation
, aIndex
, &handle
)) {
794 auto newShm
= MakeUnique
<base::SharedMemory
>();
795 if (!newShm
->IsHandleValid(handle
)) {
798 if (!newShm
->SetHandle(handle
, true)) {
799 MOZ_CRASH("failed to set shm handle");
801 if (!newShm
->Map(SHM_BLOCK_SIZE
) || !newShm
->memory()) {
802 MOZ_CRASH("failed to map shared memory");
804 uint32_t size
= static_cast<BlockHeader
*>(newShm
->memory())->mBlockSize
;
805 MOZ_ASSERT(size
>= SHM_BLOCK_SIZE
);
806 if (size
!= SHM_BLOCK_SIZE
) {
808 if (!newShm
->Map(size
) || !newShm
->memory()) {
809 MOZ_CRASH("failed to map shared memory");
812 return new ShmBlock(std::move(newShm
));
815 bool FontList::UpdateShmBlocks() {
816 MOZ_ASSERT(!XRE_IsParentProcess());
817 while (!mBlocks
.Length() || mBlocks
.Length() < GetHeader().mBlockCount
) {
818 ShmBlock
* newBlock
= GetBlockFromParent(mBlocks
.Length());
822 mBlocks
.AppendElement(newBlock
);
827 void FontList::ShareBlocksToProcess(nsTArray
<base::SharedMemoryHandle
>* aBlocks
,
828 base::ProcessId aPid
) {
829 MOZ_RELEASE_ASSERT(mReadOnlyShmems
.Length() == mBlocks
.Length());
830 for (auto& shmem
: mReadOnlyShmems
) {
831 base::SharedMemoryHandle
* handle
=
832 aBlocks
->AppendElement(base::SharedMemory::NULLHandle());
833 if (!shmem
->ShareToProcess(aPid
, handle
)) {
834 // If something went wrong here, we just bail out; the child will need to
835 // request the blocks as needed, at some performance cost. (Although in
836 // practice this may mean resources are so constrained the child process
837 // isn't really going to work at all. But that's not our problem here.)
844 base::SharedMemoryHandle
FontList::ShareBlockToProcess(uint32_t aIndex
,
845 base::ProcessId aPid
) {
846 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
847 MOZ_RELEASE_ASSERT(mReadOnlyShmems
.Length() == mBlocks
.Length());
848 MOZ_RELEASE_ASSERT(aIndex
< mReadOnlyShmems
.Length());
850 base::SharedMemoryHandle handle
= base::SharedMemory::NULLHandle();
851 if (mReadOnlyShmems
[aIndex
]->ShareToProcess(aPid
, &handle
)) {
855 return base::SharedMemory::NULLHandle();
858 Pointer
FontList::Alloc(uint32_t aSize
) {
859 // Only the parent process does allocation.
860 MOZ_ASSERT(XRE_IsParentProcess());
862 // 4-byte alignment is good enough for anything we allocate in the font list,
863 // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
864 auto align
= [](uint32_t aSize
) -> size_t { return (aSize
+ 3u) & ~3u; };
866 aSize
= align(aSize
);
868 int32_t blockIndex
= -1;
869 uint32_t curAlloc
, size
;
871 if (aSize
< SHM_BLOCK_SIZE
- sizeof(BlockHeader
)) {
872 // Try to allocate in the most recently added block first, as this is
873 // highly likely to succeed; if not, try earlier blocks (to fill gaps).
874 const int32_t blockCount
= mBlocks
.Length();
875 for (blockIndex
= blockCount
- 1; blockIndex
>= 0; --blockIndex
) {
876 size
= mBlocks
[blockIndex
]->BlockSize();
877 curAlloc
= mBlocks
[blockIndex
]->Allocated();
878 if (size
- curAlloc
>= aSize
) {
884 if (blockIndex
< 0) {
885 // Couldn't find enough space (or the requested size is too large to use
886 // a part of a block): create a new block.
887 if (!AppendShmBlock(aSize
+ sizeof(BlockHeader
))) {
888 return Pointer::Null();
890 blockIndex
= mBlocks
.Length() - 1;
891 curAlloc
= mBlocks
[blockIndex
]->Allocated();
894 // We've found a block; allocate space from it, and return
895 mBlocks
[blockIndex
]->Allocated() = curAlloc
+ aSize
;
897 return Pointer(blockIndex
, curAlloc
);
900 void FontList::SetFamilyNames(nsTArray
<Family::InitData
>& aFamilies
) {
901 // Only the parent process should ever assign the list of families.
902 MOZ_ASSERT(XRE_IsParentProcess());
904 Header
& header
= GetHeader();
905 MOZ_ASSERT(!header
.mFamilyCount
);
907 gfxPlatformFontList::PlatformFontList()->ApplyWhitelist(aFamilies
);
910 size_t count
= aFamilies
.Length();
912 // Check for duplicate family entries (can occur if there is a bundled font
913 // that has the same name as a system-installed one); in this case we keep
914 // the bundled one as it will always be exposed.
916 const nsCString
* prevKey
= &aFamilies
[0].mKey
;
917 for (size_t i
= 1; i
< count
; ++i
) {
918 if (aFamilies
[i
].mKey
.Equals(*prevKey
)) {
919 // Decide whether to discard the current entry or the preceding one
921 aFamilies
[i
].mBundled
&& !aFamilies
[i
- 1].mBundled
? i
- 1 : i
;
922 aFamilies
.RemoveElementAt(discard
);
929 header
.mFamilies
= Alloc(count
* sizeof(Family
));
930 if (header
.mFamilies
.IsNull()) {
934 Family
* families
= static_cast<Family
*>(header
.mFamilies
.ToPtr(this));
935 for (size_t i
= 0; i
< count
; i
++) {
936 (void)new (&families
[i
]) Family(this, aFamilies
[i
]);
937 LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i
,
938 aFamilies
[i
].mName
.get()));
941 header
.mFamilyCount
= count
;
944 void FontList::SetAliases(
945 nsClassHashtable
<nsCStringHashKey
, AliasData
>& aAliasTable
) {
946 MOZ_ASSERT(XRE_IsParentProcess());
948 Header
& header
= GetHeader();
950 // Build an array of Family::InitData records based on the entries in
951 // aAliasTable, then sort them and store into the fontlist.
952 nsTArray
<Family::InitData
> aliasArray
;
953 aliasArray
.SetCapacity(aAliasTable
.Count());
954 for (const auto& entry
: aAliasTable
) {
955 aliasArray
.AppendElement(Family::InitData(
956 entry
.GetKey(), entry
.GetData()->mBaseFamily
, entry
.GetData()->mIndex
,
957 entry
.GetData()->mVisibility
, entry
.GetData()->mBundled
,
958 entry
.GetData()->mBadUnderline
, entry
.GetData()->mForceClassic
, true));
962 size_t count
= aliasArray
.Length();
963 if (count
< header
.mAliasCount
) {
964 // This shouldn't happen, but handle it safely by just bailing out.
965 NS_WARNING("cannot reduce number of aliases");
968 fontlist::Pointer ptr
= Alloc(count
* sizeof(Family
));
969 Family
* aliases
= static_cast<Family
*>(ptr
.ToPtr(this));
970 for (size_t i
= 0; i
< count
; i
++) {
971 (void)new (&aliases
[i
]) Family(this, aliasArray
[i
]);
972 LOG_FONTLIST(("(shared-fontlist) alias family %u (%s: %s)", (unsigned)i
,
973 aliasArray
[i
].mKey
.get(), aliasArray
[i
].mName
.get()));
974 aliases
[i
].SetFacePtrs(this, aAliasTable
.Get(aliasArray
[i
].mKey
)->mFaces
);
975 if (LOG_FONTLIST_ENABLED()) {
976 const auto& faces
= aAliasTable
.Get(aliasArray
[i
].mKey
)->mFaces
;
977 for (unsigned j
= 0; j
< faces
.Length(); j
++) {
978 auto face
= static_cast<const fontlist::Face
*>(faces
[j
].ToPtr(this));
979 const nsCString
& desc
= face
->mDescriptor
.AsString(this);
980 nsAutoCString weight
, style
, stretch
;
981 face
->mWeight
.ToString(weight
);
982 face
->mStyle
.ToString(style
);
983 face
->mStretch
.ToString(stretch
);
985 ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
987 desc
.get(), face
->mIndex
, weight
.get(), style
.get(),
993 // Set the pointer before the count, so that any other process trying to read
994 // will not risk out-of-bounds access to the array.
995 header
.mAliases
= ptr
;
996 header
.mAliasCount
.store(count
);
999 void FontList::SetLocalNames(
1000 nsTHashMap
<nsCStringHashKey
, LocalFaceRec::InitData
>& aLocalNameTable
) {
1001 MOZ_ASSERT(XRE_IsParentProcess());
1002 Header
& header
= GetHeader();
1003 if (header
.mLocalFaceCount
> 0) {
1004 return; // already been done!
1006 auto faceArray
= ToTArray
<nsTArray
<nsCString
>>(aLocalNameTable
.Keys());
1008 size_t count
= faceArray
.Length();
1009 Family
* families
= Families();
1010 fontlist::Pointer ptr
= Alloc(count
* sizeof(LocalFaceRec
));
1011 LocalFaceRec
* faces
= static_cast<LocalFaceRec
*>(ptr
.ToPtr(this));
1012 for (size_t i
= 0; i
< count
; i
++) {
1013 (void)new (&faces
[i
]) LocalFaceRec();
1014 const auto& rec
= aLocalNameTable
.Get(faceArray
[i
]);
1015 faces
[i
].mKey
.Assign(faceArray
[i
], this);
1016 // Local face name records will refer to the canonical family name; we don't
1017 // need to search aliases here.
1018 const auto* family
= FindFamily(rec
.mFamilyName
, /*aPrimaryNameOnly*/ true);
1020 // Skip this record if the family was excluded by the font whitelist pref.
1023 faces
[i
].mFamilyIndex
= family
- families
;
1024 if (rec
.mFaceIndex
== uint32_t(-1)) {
1025 // The InitData record contains an mFaceDescriptor rather than an index,
1026 // so now we need to look for the appropriate index in the family.
1027 faces
[i
].mFaceIndex
= 0;
1028 const Pointer
* faceList
=
1029 static_cast<const Pointer
*>(family
->Faces(this));
1030 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
1031 if (!faceList
[j
].IsNull()) {
1032 const Face
* f
= static_cast<const Face
*>(faceList
[j
].ToPtr(this));
1033 if (f
&& rec
.mFaceDescriptor
== f
->mDescriptor
.AsString(this)) {
1034 faces
[i
].mFaceIndex
= j
;
1040 faces
[i
].mFaceIndex
= rec
.mFaceIndex
;
1043 header
.mLocalFaces
= ptr
;
1044 header
.mLocalFaceCount
.store(count
);
1047 nsCString
FontList::LocalizedFamilyName(const Family
* aFamily
) {
1048 // If the given family was created for an alternate locale or legacy name,
1049 // search for a standard family that corresponds to it. This is a linear
1050 // search of the font list, but (a) this is only used to show names in
1051 // Preferences, so is not performance-critical for layout etc.; and (b) few
1052 // such family names are normally present anyway, the vast majority of fonts
1053 // just have a single family name and we return it directly.
1054 if (aFamily
->IsAltLocaleFamily()) {
1055 // Currently only the Windows backend actually does this; on other systems,
1056 // the family index is unused and will be kNoIndex for all fonts.
1057 if (aFamily
->Index() != Family::kNoIndex
) {
1058 const Family
* families
= Families();
1059 for (uint32_t i
= 0; i
< NumFamilies(); ++i
) {
1060 if (families
[i
].Index() == aFamily
->Index() &&
1061 families
[i
].IsBundled() == aFamily
->IsBundled() &&
1062 !families
[i
].IsAltLocaleFamily()) {
1063 return families
[i
].DisplayName().AsString(this);
1069 // For standard families (or if we failed to find the expected standard
1070 // family for some reason), just return the DisplayName.
1071 return aFamily
->DisplayName().AsString(this);
1074 Family
* FontList::FindFamily(const nsCString
& aName
, bool aPrimaryNameOnly
) {
1075 struct FamilyNameComparator
{
1076 FamilyNameComparator(FontList
* aList
, const nsCString
& aTarget
)
1077 : mList(aList
), mTarget(aTarget
) {}
1079 int operator()(const Family
& aVal
) const {
1080 return mTarget
.Compare(aVal
.Key().BeginReading(mList
));
1085 const nsCString
& mTarget
;
1088 Header
& header
= GetHeader();
1090 Family
* families
= Families();
1096 if (BinarySearchIf(families
, 0, header
.mFamilyCount
,
1097 FamilyNameComparator(this, aName
), &match
)) {
1098 return &families
[match
];
1101 if (aPrimaryNameOnly
) {
1105 if (header
.mAliasCount
) {
1106 Family
* aliases
= AliasFamilies();
1108 if (aliases
&& BinarySearchIf(aliases
, 0, header
.mAliasCount
,
1109 FamilyNameComparator(this, aName
), &match
)) {
1110 return &aliases
[match
];
1115 // For Windows only, because of how DWrite munges font family names in some
1117 // 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
1118 // and discussion on the OpenType list), try stripping a possible style-name
1119 // suffix from the end of the requested family name.
1120 // After the deferred font loader has finished, this is no longer needed as
1121 // the "real" family names will have been found in AliasFamilies() above.
1122 if (aName
.Contains(' ')) {
1123 auto pfl
= gfxPlatformFontList::PlatformFontList();
1124 if (header
.mAliasCount
) {
1125 // Aliases have been fully loaded by the parent process, so just discard
1126 // any stray mAliasTable and mLocalNameTable entries from earlier calls
1127 // to this code, and return.
1128 pfl
->mAliasTable
.Clear();
1129 pfl
->mLocalNameTable
.Clear();
1133 // Do we already have an aliasData record for this name? If so, we just
1134 // return its base family.
1135 if (auto lookup
= pfl
->mAliasTable
.Lookup(aName
)) {
1136 return FindFamily(lookup
.Data()->mBaseFamily
, true);
1139 // Strip the style suffix (after last space in the name) to get a "base"
1141 const char* data
= aName
.BeginReading();
1142 int32_t index
= aName
.Length();
1143 while (--index
> 0) {
1144 if (data
[index
] == ' ') {
1151 nsAutoCString
base(Substring(aName
, 0, index
));
1152 if (BinarySearchIf(families
, 0, header
.mFamilyCount
,
1153 FamilyNameComparator(this, base
), &match
)) {
1154 // This may be a possible base family to satisfy the search; call
1155 // ReadFaceNamesForFamily and see if the desired name ends up in
1157 // Note that ReadFaceNamesForFamily may store entries in mAliasTable
1158 // (and mLocalNameTable), but if this is happening in a content
1159 // process (which is the common case) those entries will not be saved
1160 // into the shared font list; they're just used here until the "real"
1161 // alias list is ready, then discarded.
1162 Family
* baseFamily
= &families
[match
];
1163 pfl
->ReadFaceNamesForFamily(baseFamily
, false);
1164 if (auto lookup
= pfl
->mAliasTable
.Lookup(aName
)) {
1165 if (lookup
.Data()->mFaces
.Length() != baseFamily
->NumFaces()) {
1166 // If the alias family doesn't have all the faces of the base family,
1167 // then style matching may end up resolving to a face that isn't
1168 // supposed to be available in the legacy styled family. To ensure
1169 // such mis-styling will get fixed, we start the async font info
1170 // loader (if it hasn't yet been triggered), which will pull in the
1171 // full metadata we need and then force a reflow.
1172 pfl
->InitOtherFamilyNames(/* aDeferOtherFamilyNamesLoading */ true);
1183 LocalFaceRec
* FontList::FindLocalFace(const nsCString
& aName
) {
1184 struct FaceNameComparator
{
1185 FaceNameComparator(FontList
* aList
, const nsCString
& aTarget
)
1186 : mList(aList
), mTarget(aTarget
) {}
1188 int operator()(const LocalFaceRec
& aVal
) const {
1189 return mTarget
.Compare(aVal
.mKey
.BeginReading(mList
));
1194 const nsCString
& mTarget
;
1197 Header
& header
= GetHeader();
1199 LocalFaceRec
* faces
= LocalFaces();
1201 if (faces
&& BinarySearchIf(faces
, 0, header
.mLocalFaceCount
,
1202 FaceNameComparator(this, aName
), &match
)) {
1203 return &faces
[match
];
1209 void FontList::SearchForLocalFace(const nsACString
& aName
, Family
** aFamily
,
1211 Header
& header
= GetHeader();
1212 MOZ_ASSERT(header
.mLocalFaceCount
== 0,
1213 "do not use when local face names are already set up!");
1215 ("(shared-fontlist) local face search for (%s)", aName
.BeginReading()));
1216 char initial
= aName
[0];
1217 Family
* families
= Families();
1221 for (uint32_t i
= 0; i
< header
.mFamilyCount
; i
++) {
1222 Family
* family
= &families
[i
];
1223 if (family
->Key().BeginReading(this)[0] != initial
) {
1226 LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
1227 family
->Key().AsString(this).BeginReading()));
1228 if (!family
->IsInitialized()) {
1229 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family
)) {
1233 Pointer
* faces
= family
->Faces(this);
1237 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
1238 Face
* face
= static_cast<Face
*>(faces
[j
].ToPtr(this));
1242 nsAutoCString psname
, fullname
;
1243 if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
1244 family
, face
, psname
, fullname
)) {
1245 LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
1246 psname
.get(), fullname
.get()));
1247 ToLowerCase(psname
);
1248 ToLowerCase(fullname
);
1249 if (aName
== psname
|| aName
== fullname
) {
1259 Pointer
FontList::ToSharedPointer(const void* aPtr
) {
1260 const char* p
= (const char*)aPtr
;
1261 const uint32_t blockCount
= mBlocks
.Length();
1262 for (uint32_t i
= 0; i
< blockCount
; ++i
) {
1263 const char* blockAddr
= (const char*)mBlocks
[i
]->Memory();
1264 if (p
>= blockAddr
&& p
< blockAddr
+ SHM_BLOCK_SIZE
) {
1265 return Pointer(i
, p
- blockAddr
);
1268 MOZ_DIAGNOSTIC_ASSERT(false, "invalid shared-memory pointer");
1269 return Pointer::Null();
1272 size_t FontList::SizeOfIncludingThis(
1273 mozilla::MallocSizeOf aMallocSizeOf
) const {
1274 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
1277 size_t FontList::SizeOfExcludingThis(
1278 mozilla::MallocSizeOf aMallocSizeOf
) const {
1279 size_t result
= mBlocks
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
1280 for (const auto& b
: mBlocks
) {
1281 result
+= aMallocSizeOf(b
.get()) + aMallocSizeOf(b
->mShmem
.get());
1286 size_t FontList::AllocatedShmemSize() const {
1288 for (const auto& b
: mBlocks
) {
1289 result
+= b
->BlockSize();
1294 } // namespace fontlist
1295 } // namespace mozilla