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"
10 #include "mozilla/dom/ContentChild.h"
11 #include "mozilla/Logging.h"
13 #define LOG_FONTLIST(args) \
14 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
15 #define LOG_FONTLIST_ENABLED() \
16 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
21 static double WSSDistance(const Face
* aFace
, const gfxFontStyle
& aStyle
) {
22 double stretchDist
= StretchDistance(aFace
->mStretch
, aStyle
.stretch
);
23 double styleDist
= StyleDistance(aFace
->mStyle
, aStyle
.style
);
24 double weightDist
= WeightDistance(aFace
->mWeight
, aStyle
.weight
);
26 // Sanity-check that the distances are within the expected range
27 // (update if implementation of the distance functions is changed).
28 MOZ_ASSERT(stretchDist
>= 0.0 && stretchDist
<= 2000.0);
29 MOZ_ASSERT(styleDist
>= 0.0 && styleDist
<= 500.0);
30 MOZ_ASSERT(weightDist
>= 0.0 && weightDist
<= 1600.0);
32 // weight/style/stretch priority: stretch >> style >> weight
33 // so we multiply the stretch and style values to make them dominate
35 return stretchDist
* 1.0e8
+ styleDist
* 1.0e4
+ weightDist
;
38 void* Pointer::ToPtr(FontList
* aFontList
) const {
42 uint32_t block
= Block();
43 // If the Pointer refers to a block we have not yet mapped in this process,
44 // we first need to retrieve new block handle(s) from the parent and update
46 if (block
>= aFontList
->mBlocks
.Length()) {
47 if (XRE_IsParentProcess()) {
48 // Shouldn't happen! A content process tried to pass a bad Pointer?
51 // UpdateShmBlocks can fail, if the parent has replaced the font list with
52 // a new generation. In that case we just return null, and whatever font
53 // the content process was trying to use will appear unusable for now. It's
54 // about to receive a notification of the new font list anyhow, at which
55 // point it will flush its caches and reflow everything, so the temporary
56 // failure of this font will be forgotten.
57 if (!aFontList
->UpdateShmBlocks()) {
60 MOZ_ASSERT(block
< aFontList
->mBlocks
.Length());
62 return static_cast<char*>(aFontList
->mBlocks
[block
]->mAddr
) + Offset();
65 void String::Assign(const nsACString
& aString
, FontList
* aList
) {
66 // We only assign to previously-empty strings; they are never modified
67 // after initial assignment.
68 MOZ_ASSERT(mPointer
.IsNull());
69 mLength
= aString
.Length();
70 mPointer
= aList
->Alloc(mLength
+ 1);
71 char* p
= static_cast<char*>(mPointer
.ToPtr(aList
));
72 std::memcpy(p
, aString
.BeginReading(), mLength
);
76 Family::Family(FontList
* aList
, const InitData
& aData
)
78 mKey(aList
, aData
.mKey
),
79 mName(aList
, aData
.mName
),
80 mCharacterMap(Pointer::Null()),
81 mFaces(Pointer::Null()),
83 mIsHidden(aData
.mHidden
),
84 mIsBadUnderlineFamily(aData
.mBadUnderline
),
85 mIsForceClassic(aData
.mForceClassic
),
87 MOZ_ASSERT(aData
.mIndex
<= 0x7fffffffu
);
88 mIndex
= aData
.mIndex
| (aData
.mBundled
? 0x80000000u
: 0u);
91 void Face::SetCharacterMap(FontList
* aList
, const gfxSparseBitSet
* aCharMap
) {
92 if (!XRE_IsParentProcess()) {
93 Pointer ptr
= aList
->ToSharedPointer(this);
94 dom::ContentChild::GetSingleton()->SendSetCharacterMap(
95 aList
->GetGeneration(), ptr
, *aCharMap
);
98 auto pfl
= gfxPlatformFontList::PlatformFontList();
99 mCharacterMap
= pfl
->GetShmemCharMap(aCharMap
);
102 void Family::AddFaces(FontList
* aList
, const nsTArray
<Face::InitData
>& aFaces
) {
103 MOZ_ASSERT(XRE_IsParentProcess());
104 if (mFaceCount
> 0) {
105 // Already initialized!
109 uint32_t count
= aFaces
.Length();
110 bool isSimple
= false;
111 // A family is "simple" (i.e. simplified style selection may be used instead
112 // of the full CSS font-matching algorithm) if there is at maximum one normal,
113 // bold, italic, and bold-italic face; in this case, they are stored at known
114 // positions in the mFaces array.
115 const Face::InitData
* slots
[4] = {nullptr, nullptr, nullptr, nullptr};
116 if (count
>= 2 && count
<= 4) {
117 // Check if this can be treated as a "simple" family
119 for (const auto& f
: aFaces
) {
120 if (!f
.mWeight
.IsSingle() || !f
.mStretch
.IsSingle() ||
121 !f
.mStyle
.IsSingle()) {
125 if (!f
.mStretch
.Min().IsNormal()) {
129 // Figure out which slot (0-3) this face belongs in
131 static_assert((kBoldMask
| kItalicMask
) == 0b11, "bad bold/italic bits");
132 if (f
.mWeight
.Min().IsBold()) {
135 if (f
.mStyle
.Min().IsItalic() || f
.mStyle
.Min().IsOblique()) {
139 // More than one face mapped to the same slot - not a simple family!
146 // Ensure all 4 slots will exist, even if some are empty.
151 // Allocate space for the face records, and initialize them.
152 // coverity[suspicious_sizeof]
153 Pointer p
= aList
->Alloc(count
* sizeof(Pointer
));
154 auto facePtrs
= static_cast<Pointer
*>(p
.ToPtr(aList
));
155 for (size_t i
= 0; i
< count
; i
++) {
156 if (isSimple
&& !slots
[i
]) {
157 facePtrs
[i
] = Pointer::Null();
159 Pointer fp
= aList
->Alloc(sizeof(Face
));
160 auto face
= static_cast<Face
*>(fp
.ToPtr(aList
));
161 (void)new (face
) Face(aList
, isSimple
? *slots
[i
] : aFaces
[i
]);
166 mIsSimple
= isSimple
;
168 mFaceCount
.store(count
);
170 if (LOG_FONTLIST_ENABLED()) {
171 const nsCString
& fam
= DisplayName().AsString(aList
);
172 for (unsigned j
= 0; j
< aFaces
.Length(); j
++) {
173 nsAutoCString weight
, style
, stretch
;
174 aFaces
[j
].mWeight
.ToString(weight
);
175 aFaces
[j
].mStyle
.ToString(style
);
176 aFaces
[j
].mStretch
.ToString(stretch
);
178 ("(shared-fontlist) family (%s) added face (%s) index %u, weight "
179 "%s, style %s, stretch %s",
180 fam
.get(), aFaces
[j
].mDescriptor
.get(), aFaces
[j
].mIndex
,
181 weight
.get(), style
.get(), stretch
.get()));
186 void Family::FindAllFacesForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
187 nsTArray
<Face
*>& aFaceList
,
188 bool aIgnoreSizeTolerance
) const {
189 MOZ_ASSERT(aFaceList
.IsEmpty());
190 if (!IsInitialized()) {
194 Pointer
* facePtrs
= Faces(aList
);
199 // If the family has only one face, we simply return it; no further
201 if (NumFaces() == 1) {
202 MOZ_ASSERT(!facePtrs
[0].IsNull());
203 aFaceList
.AppendElement(static_cast<Face
*>(facePtrs
[0].ToPtr(aList
)));
207 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
208 // or some subset of these. In this case, we have exactly 4 entries in
209 // mAvailableFonts, stored in the above order; note that some of the entries
210 // may be nullptr. We can then pick the required entry based on whether the
211 // request is for bold or non-bold, italic or non-italic, without running the
212 // more complex matching algorithm used for larger families with many weights
216 // Family has no more than the "standard" 4 faces, at fixed indexes;
217 // calculate which one we want.
218 // Note that we cannot simply return it as not all 4 faces are necessarily
220 bool wantBold
= aStyle
.weight
>= FontWeight(600);
221 bool wantItalic
= !aStyle
.style
.IsNormal();
223 (wantItalic
? kItalicMask
: 0) | (wantBold
? kBoldMask
: 0);
225 // if the desired style is available, return it directly
226 Face
* face
= static_cast<Face
*>(facePtrs
[faceIndex
].ToPtr(aList
));
227 if (face
&& face
->HasValidDescriptor()) {
228 aFaceList
.AppendElement(face
);
232 // order to check fallback faces in a simple family, depending on requested
234 static const uint8_t simpleFallbacks
[4][3] = {
235 {kBoldFaceIndex
, kItalicFaceIndex
,
236 kBoldItalicFaceIndex
}, // fallback sequence for Regular
237 {kRegularFaceIndex
, kBoldItalicFaceIndex
, kItalicFaceIndex
}, // Bold
238 {kBoldItalicFaceIndex
, kRegularFaceIndex
, kBoldFaceIndex
}, // Italic
239 {kItalicFaceIndex
, kBoldFaceIndex
, kRegularFaceIndex
} // BoldItalic
241 const uint8_t* order
= simpleFallbacks
[faceIndex
];
243 for (uint8_t trial
= 0; trial
< 3; ++trial
) {
244 // check remaining faces in order of preference to find the first that
246 face
= static_cast<Face
*>(facePtrs
[order
[trial
]].ToPtr(aList
));
247 if (face
&& face
->HasValidDescriptor()) {
248 aFaceList
.AppendElement(face
);
253 // this can't happen unless we have totally broken the font-list manager!
254 MOZ_ASSERT_UNREACHABLE("no face found in simple font family!");
257 // Pick the font(s) that are closest to the desired weight, style, and
258 // stretch. Iterate over all fonts, measuring the weight/style distance.
259 // Because of unicode-range values, there may be more than one font for a
260 // given but the 99% use case is only a single font entry per
261 // weight/style/stretch distance value. To optimize this, only add entries
262 // to the matched font array when another entry already has the same
263 // weight/style/stretch distance and add the last matched font entry. For
264 // normal platform fonts with a single font entry for each
265 // weight/style/stretch combination, only the last matched font entry will
268 double minDistance
= INFINITY
;
269 Face
* matched
= nullptr;
270 for (uint32_t i
= 0; i
< NumFaces(); i
++) {
271 Face
* face
= static_cast<Face
*>(facePtrs
[i
].ToPtr(aList
));
272 // weight/style/stretch priority: stretch >> style >> weight
273 double distance
= WSSDistance(face
, aStyle
);
274 if (distance
< minDistance
) {
276 if (!aFaceList
.IsEmpty()) {
279 minDistance
= distance
;
280 } else if (distance
== minDistance
) {
282 aFaceList
.AppendElement(matched
);
288 MOZ_ASSERT(matched
, "didn't match a font within a family");
290 aFaceList
.AppendElement(matched
);
294 Face
* Family::FindFaceForStyle(FontList
* aList
, const gfxFontStyle
& aStyle
,
295 bool aIgnoreSizeTolerance
) const {
296 AutoTArray
<Face
*, 4> faces
;
297 FindAllFacesForStyle(aList
, aStyle
, faces
, aIgnoreSizeTolerance
);
298 return faces
.IsEmpty() ? nullptr : faces
[0];
301 void Family::SearchAllFontsForChar(FontList
* aList
,
302 GlobalFontMatch
* aMatchData
) {
303 const SharedBitSet
* charmap
=
304 static_cast<const SharedBitSet
*>(mCharacterMap
.ToPtr(aList
));
305 if (charmap
&& !charmap
->test(aMatchData
->mCh
)) {
308 if (!IsInitialized()) {
309 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this)) {
313 uint32_t numFaces
= NumFaces();
314 uint32_t charMapsLoaded
= 0; // number of faces whose charmap is loaded
315 Pointer
* facePtrs
= Faces(aList
);
319 for (uint32_t i
= 0; i
< numFaces
; i
++) {
320 Face
* face
= static_cast<Face
*>(facePtrs
[i
].ToPtr(aList
));
324 MOZ_ASSERT(face
->HasValidDescriptor());
325 // Get the face's character map, if available (may be null!)
327 static_cast<const SharedBitSet
*>(face
->mCharacterMap
.ToPtr(aList
));
331 // Check style distance if the char is supported, or if charmap not known
332 // (so that we don't trigger cmap-loading for faces that would be a worse
333 // match than what we've already found).
334 if (!charmap
|| charmap
->test(aMatchData
->mCh
)) {
335 double distance
= WSSDistance(face
, aMatchData
->mStyle
);
336 if (distance
< aMatchData
->mMatchDistance
) {
337 // It's a better style match: get a fontEntry, and if we haven't
338 // already checked character coverage, do it now (note that
339 // HasCharacter() will trigger loading the fontEntry's cmap, if
341 RefPtr
<gfxFontEntry
> fe
=
342 gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face
,
347 if (!charmap
&& !fe
->HasCharacter(aMatchData
->mCh
)) {
350 aMatchData
->mBestMatch
= fe
;
351 aMatchData
->mMatchDistance
= distance
;
352 aMatchData
->mMatchedSharedFamily
= this;
356 if (mCharacterMap
.IsNull() && charMapsLoaded
== numFaces
) {
357 SetupFamilyCharMap(aList
);
361 void Family::SetFacePtrs(FontList
* aList
, nsTArray
<Pointer
>& aFaces
) {
362 if (aFaces
.Length() >= 2 && aFaces
.Length() <= 4) {
363 // Check whether the faces meet the criteria for a "simple" family: no more
364 // than one each of Regular, Bold, Italic, BoldItalic styles. If so, store
365 // them at the appropriate slots in mFaces and set the mIsSimple flag to
366 // accelerate font-matching.
367 bool isSimple
= true;
368 Pointer slots
[4] = {Pointer::Null(), Pointer::Null(), Pointer::Null(),
370 for (const Pointer
& fp
: aFaces
) {
371 const Face
* f
= static_cast<const Face
*>(fp
.ToPtr(aList
));
372 if (!f
->mWeight
.IsSingle() || !f
->mStyle
.IsSingle() ||
373 !f
->mStretch
.IsSingle()) {
377 if (!f
->mStretch
.Min().IsNormal()) {
382 if (f
->mWeight
.Min().IsBold()) {
385 if (f
->mStyle
.Min().IsItalic() || f
->mStyle
.Min().IsOblique()) {
388 if (!slots
[slot
].IsNull()) {
395 size_t size
= 4 * sizeof(Pointer
);
396 mFaces
= aList
->Alloc(size
);
397 memcpy(mFaces
.ToPtr(aList
), slots
, size
);
403 size_t size
= aFaces
.Length() * sizeof(Pointer
);
404 mFaces
= aList
->Alloc(size
);
405 memcpy(mFaces
.ToPtr(aList
), aFaces
.Elements(), size
);
406 mFaceCount
.store(aFaces
.Length());
409 void Family::SetupFamilyCharMap(FontList
* aList
) {
410 // Set the character map of the family to the union of all the face cmaps,
411 // to allow font fallback searches to more rapidly reject the family.
412 if (!XRE_IsParentProcess()) {
413 // |this| could be a Family record in either the Families() or Aliases()
415 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
416 aList
->GetGeneration(), aList
->ToSharedPointer(this));
419 gfxSparseBitSet familyMap
;
420 Pointer firstMapShmPointer
;
421 SharedBitSet
* firstMap
= nullptr;
423 Pointer
* faces
= Faces(aList
);
427 for (size_t i
= 0; i
< NumFaces(); i
++) {
428 auto f
= static_cast<Face
*>(faces
[i
].ToPtr(aList
));
432 auto faceMap
= static_cast<SharedBitSet
*>(f
->mCharacterMap
.ToPtr(aList
));
436 firstMapShmPointer
= f
->mCharacterMap
;
437 } else if (faceMap
!= firstMap
) {
439 familyMap
.Union(*firstMap
);
442 familyMap
.Union(*faceMap
);
447 gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap
);
449 mCharacterMap
= firstMapShmPointer
;
453 FontList::FontList(uint32_t aGeneration
) {
454 if (XRE_IsParentProcess()) {
455 // Create the initial shared block, and initialize Header
456 if (AppendShmBlock()) {
457 Header
& header
= GetHeader();
458 header
.mAllocated
.store(sizeof(Header
));
459 header
.mGeneration
= aGeneration
;
460 header
.mFamilyCount
= 0;
461 header
.mBlockCount
.store(1);
462 header
.mAliasCount
.store(0);
463 header
.mLocalFaceCount
.store(0);
464 header
.mFamilies
= Pointer::Null();
465 header
.mAliases
= Pointer::Null();
466 header
.mLocalFaces
= Pointer::Null();
468 MOZ_CRASH("parent: failed to initialize FontList");
471 for (unsigned retryCount
= 0; retryCount
< 3; ++retryCount
) {
472 if (UpdateShmBlocks()) {
475 // The only reason for UpdateShmBlocks to fail is if the parent recreated
476 // the list after we read its first block, but before we finished getting
477 // them all, and so the generation check failed on a subsequent request.
478 // Discarding whatever we've got and retrying should get us a new,
479 // consistent set of memory blocks in this case. If this doesn't work
480 // after a couple of retries, bail out.
483 NS_WARNING("child: failed to initialize shared FontList");
487 FontList::~FontList() { DetachShmBlocks(); }
489 bool FontList::AppendShmBlock() {
490 MOZ_ASSERT(XRE_IsParentProcess());
491 ipc::SharedMemoryBasic
* newShm
= new ipc::SharedMemoryBasic();
492 if (!newShm
->Create(SHM_BLOCK_SIZE
)) {
493 MOZ_CRASH("failed to create shared memory");
496 if (!newShm
->Map(SHM_BLOCK_SIZE
)) {
497 MOZ_CRASH("failed to map shared memory");
500 char* addr
= static_cast<char*>(newShm
->memory());
502 MOZ_CRASH("null shared memory?");
506 ShmBlock
* block
= new ShmBlock(newShm
, addr
);
507 // Allocate space for the Allocated() header field present in all blocks
508 block
->Allocated().store(4);
510 mBlocks
.AppendElement(block
);
511 GetHeader().mBlockCount
.store(mBlocks
.Length());
516 void FontList::DetachShmBlocks() {
517 for (auto& i
: mBlocks
) {
520 mBlocks
.SetLength(0);
523 FontList::ShmBlock
* FontList::GetBlockFromParent(uint32_t aIndex
) {
524 MOZ_ASSERT(!XRE_IsParentProcess());
525 // If we have no existing blocks, we don't want a generation check yet;
526 // the header in the first block will define the generation of this list
527 uint32_t generation
= aIndex
== 0 ? 0 : GetGeneration();
528 ipc::SharedMemoryBasic::Handle handle
= ipc::SharedMemoryBasic::NULLHandle();
529 if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
530 generation
, aIndex
, &handle
)) {
533 RefPtr
<ipc::SharedMemoryBasic
> newShm
= new ipc::SharedMemoryBasic();
534 if (!newShm
->IsHandleValid(handle
)) {
537 if (!newShm
->SetHandle(handle
,
538 mozilla::ipc::SharedMemoryBasic::RightsReadOnly
)) {
539 MOZ_CRASH("failed to set shm handle");
541 if (!newShm
->Map(SHM_BLOCK_SIZE
)) {
542 MOZ_CRASH("failed to map shared memory");
544 char* addr
= static_cast<char*>(newShm
->memory());
546 MOZ_CRASH("null shared memory?");
548 return new ShmBlock(newShm
, addr
);
551 bool FontList::UpdateShmBlocks() {
552 MOZ_ASSERT(!XRE_IsParentProcess());
553 while (!mBlocks
.Length() || mBlocks
.Length() < GetHeader().mBlockCount
) {
554 ShmBlock
* newBlock
= GetBlockFromParent(mBlocks
.Length());
558 mBlocks
.AppendElement(newBlock
);
563 // The block size MUST be sufficient to allocate the largest possible
564 // SharedBitSet in a single contiguous block, following its own
565 // Allocated() field.
566 static_assert(FontList::SHM_BLOCK_SIZE
>= 4 + SharedBitSet::kMaxSize
,
567 "may not be able to allocate a SharedBitSet");
569 Pointer
FontList::Alloc(uint32_t aSize
) {
570 // Only the parent process does allocation.
571 MOZ_ASSERT(XRE_IsParentProcess());
573 // 4-byte alignment is good enough for anything we allocate in the font list,
574 // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
575 auto align
= [](uint32_t aSize
) -> size_t { return (aSize
+ 3u) & ~3u; };
577 // There's a limit to the size of object we can allocate: the block size,
578 // minus the 4-byte mAllocated header field at the start of the block.
579 MOZ_DIAGNOSTIC_ASSERT(aSize
<= SHM_BLOCK_SIZE
- 4);
581 aSize
= align(aSize
);
586 // Try to allocate in the most recently added block first, as this is
587 // highly likely to succeed; if not, try earlier blocks (to fill gaps).
588 const int32_t blockCount
= mBlocks
.Length();
589 for (blockIndex
= blockCount
- 1; blockIndex
>= 0; --blockIndex
) {
590 curAlloc
= mBlocks
[blockIndex
]->Allocated();
591 if (SHM_BLOCK_SIZE
- curAlloc
>= aSize
) {
596 if (blockIndex
< 0) {
597 // Couldn't find enough space: create a new block, and retry.
598 if (!AppendShmBlock()) {
599 return Pointer::Null();
601 continue; // retry; this will check the newly-added block first,
602 // which must succeed because it's empty
605 // We've found a block; allocate space from it, and return
606 mBlocks
[blockIndex
]->Allocated() = curAlloc
+ aSize
;
610 return Pointer(blockIndex
, curAlloc
);
613 void FontList::SetFamilyNames(const nsTArray
<Family::InitData
>& aFamilies
) {
614 // Only the parent process should ever assign the list of families.
615 MOZ_ASSERT(XRE_IsParentProcess());
617 Header
& header
= GetHeader();
618 MOZ_ASSERT(!header
.mFamilyCount
);
620 size_t count
= aFamilies
.Length();
621 header
.mFamilies
= Alloc(count
* sizeof(Family
));
622 if (header
.mFamilies
.IsNull()) {
626 Family
* families
= static_cast<Family
*>(header
.mFamilies
.ToPtr(this));
627 for (size_t i
= 0; i
< count
; i
++) {
628 (void)new (&families
[i
]) Family(this, aFamilies
[i
]);
629 LOG_FONTLIST(("(shared-fontlist) family %u (%s)", (unsigned)i
,
630 aFamilies
[i
].mName
.get()));
633 header
.mFamilyCount
= count
;
636 void FontList::SetAliases(
637 nsClassHashtable
<nsCStringHashKey
, nsTArray
<Pointer
>>& aAliasTable
) {
638 MOZ_ASSERT(XRE_IsParentProcess());
640 Header
& header
= GetHeader();
642 nsTArray
<Family::InitData
> aliasArray
;
643 aliasArray
.SetCapacity(aAliasTable
.Count());
644 for (auto i
= aAliasTable
.Iter(); !i
.Done(); i
.Next()) {
645 nsAutoCString
key(i
.Key());
647 aliasArray
.AppendElement(Family::InitData(key
, i
.Key()));
651 size_t count
= aliasArray
.Length();
652 if (count
< header
.mAliasCount
) {
653 // This shouldn't happen, but handle it safely by just bailing out.
654 NS_WARNING("cannot reduce number of aliases");
657 fontlist::Pointer ptr
= Alloc(count
* sizeof(Family
));
658 Family
* aliases
= static_cast<Family
*>(ptr
.ToPtr(this));
659 for (size_t i
= 0; i
< count
; i
++) {
660 (void)new (&aliases
[i
]) Family(this, aliasArray
[i
]);
661 LOG_FONTLIST(("(shared-fontlist) alias family %u (%s)", (unsigned)i
,
662 aliasArray
[i
].mName
.get()));
663 aliases
[i
].SetFacePtrs(this, *aAliasTable
.Get(aliasArray
[i
].mName
));
664 if (LOG_FONTLIST_ENABLED()) {
665 const auto& faces
= *aAliasTable
.Get(aliasArray
[i
].mName
);
666 for (unsigned j
= 0; j
< faces
.Length(); j
++) {
667 auto face
= static_cast<const fontlist::Face
*>(faces
[j
].ToPtr(this));
668 const nsCString
& desc
= face
->mDescriptor
.AsString(this);
669 nsAutoCString weight
, style
, stretch
;
670 face
->mWeight
.ToString(weight
);
671 face
->mStyle
.ToString(style
);
672 face
->mStretch
.ToString(stretch
);
674 ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
676 desc
.get(), face
->mIndex
, weight
.get(), style
.get(),
682 // Set the pointer before the count, so that any other process trying to read
683 // will not risk out-of-bounds access to the array.
684 header
.mAliases
= ptr
;
685 header
.mAliasCount
.store(count
);
688 void FontList::SetLocalNames(
689 nsDataHashtable
<nsCStringHashKey
, LocalFaceRec::InitData
>&
691 MOZ_ASSERT(XRE_IsParentProcess());
692 Header
& header
= GetHeader();
693 if (header
.mLocalFaceCount
> 0) {
694 return; // already been done!
696 nsTArray
<nsCString
> faceArray
;
697 faceArray
.SetCapacity(aLocalNameTable
.Count());
698 for (auto i
= aLocalNameTable
.Iter(); !i
.Done(); i
.Next()) {
699 faceArray
.AppendElement(i
.Key());
702 size_t count
= faceArray
.Length();
703 Family
* families
= Families();
704 fontlist::Pointer ptr
= Alloc(count
* sizeof(LocalFaceRec
));
705 LocalFaceRec
* faces
= static_cast<LocalFaceRec
*>(ptr
.ToPtr(this));
706 for (size_t i
= 0; i
< count
; i
++) {
707 (void)new (&faces
[i
]) LocalFaceRec();
708 const auto& rec
= aLocalNameTable
.Get(faceArray
[i
]);
709 faces
[i
].mKey
.Assign(faceArray
[i
], this);
710 faces
[i
].mFamilyIndex
= FindFamily(rec
.mFamilyName
) - families
;
711 faces
[i
].mFaceIndex
= rec
.mFaceIndex
;
713 header
.mLocalFaces
= ptr
;
714 header
.mLocalFaceCount
.store(count
);
717 Family
* FontList::FindFamily(const nsCString
& aName
) {
718 struct FamilyNameComparator
{
719 FamilyNameComparator(FontList
* aList
, const nsCString
& aTarget
)
720 : mList(aList
), mTarget(aTarget
) {}
722 int operator()(const Family
& aVal
) const {
723 return mTarget
.Compare(aVal
.Key().BeginReading(mList
));
728 const nsCString
& mTarget
;
731 Header
& header
= GetHeader();
733 Family
* families
= Families();
735 if (BinarySearchIf(families
, 0, header
.mFamilyCount
,
736 FamilyNameComparator(this, aName
), &match
)) {
737 return &families
[match
];
740 if (header
.mAliasCount
) {
741 families
= AliasFamilies();
743 if (BinarySearchIf(families
, 0, header
.mAliasCount
,
744 FamilyNameComparator(this, aName
), &match
)) {
745 return &families
[match
];
752 LocalFaceRec
* FontList::FindLocalFace(const nsCString
& aName
) {
753 struct FaceNameComparator
{
754 FaceNameComparator(FontList
* aList
, const nsCString
& aTarget
)
755 : mList(aList
), mTarget(aTarget
) {}
757 int operator()(const LocalFaceRec
& aVal
) const {
758 return mTarget
.Compare(aVal
.mKey
.BeginReading(mList
));
763 const nsCString
& mTarget
;
766 Header
& header
= GetHeader();
768 LocalFaceRec
* faces
= LocalFaces();
770 if (BinarySearchIf(faces
, 0, header
.mLocalFaceCount
,
771 FaceNameComparator(this, aName
), &match
)) {
772 return &faces
[match
];
778 void FontList::SearchForLocalFace(const nsACString
& aName
, Family
** aFamily
,
780 Header
& header
= GetHeader();
781 MOZ_ASSERT(header
.mLocalFaceCount
== 0,
782 "do not use when local face names are already set up!");
784 ("(shared-fontlist) local face search for (%s)", aName
.BeginReading()));
785 char initial
= aName
[0];
786 Family
* families
= Families();
787 for (uint32_t i
= 0; i
< header
.mFamilyCount
; i
++) {
788 Family
* family
= &families
[i
];
789 if (family
->Key().AsString(this)[0] != initial
) {
792 LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
793 family
->Key().AsString(this).BeginReading()));
794 if (!family
->IsInitialized()) {
795 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family
)) {
799 Pointer
* faces
= family
->Faces(this);
800 for (uint32_t j
= 0; j
< family
->NumFaces(); j
++) {
801 Face
* face
= static_cast<Face
*>(faces
[j
].ToPtr(this));
805 nsAutoCString psname
, fullname
;
806 if (gfxPlatformFontList::PlatformFontList()->ReadFaceNames(
807 family
, face
, psname
, fullname
)) {
808 LOG_FONTLIST(("(shared-fontlist) read psname (%s) fullname (%s)",
809 psname
.get(), fullname
.get()));
811 ToLowerCase(fullname
);
812 if (aName
== psname
|| aName
== fullname
) {
822 Pointer
FontList::ToSharedPointer(const void* aPtr
) {
823 const char* p
= (const char*)aPtr
;
824 const uint32_t blockCount
= mBlocks
.Length();
825 for (uint32_t i
= 0; i
< blockCount
; ++i
) {
826 const char* blockAddr
= (const char*)mBlocks
[i
]->mAddr
;
827 if (p
>= blockAddr
&& p
< blockAddr
+ SHM_BLOCK_SIZE
) {
828 return Pointer(i
, p
- blockAddr
);
831 MOZ_ASSERT_UNREACHABLE("invalid shared-memory pointer");
832 return Pointer::Null();
835 } // namespace fontlist
836 } // namespace mozilla