Bug 1567650 [wpt PR 17950] - [ElementTiming] Replace responseEnd with loadTime, a...
[gecko.git] / gfx / thebes / SharedFontList.cpp
blobc0f6cdf31845985746d0fb1201c7ba3ca89b2c1b
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"
8 #include "gfxFont.h"
9 #include "prerror.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)
18 namespace mozilla {
19 namespace fontlist {
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
34 // the result
35 return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist;
38 void* Pointer::ToPtr(FontList* aFontList) const {
39 if (IsNull()) {
40 return nullptr;
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
45 // our mBlocks list.
46 if (block >= aFontList->mBlocks.Length()) {
47 if (XRE_IsParentProcess()) {
48 // Shouldn't happen! A content process tried to pass a bad Pointer?
49 return nullptr;
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()) {
58 return nullptr;
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);
73 p[mLength] = '\0';
76 Family::Family(FontList* aList, const InitData& aData)
77 : mFaceCount(0),
78 mKey(aList, aData.mKey),
79 mName(aList, aData.mName),
80 mCharacterMap(Pointer::Null()),
81 mFaces(Pointer::Null()),
82 mIndex(aData.mIndex),
83 mIsHidden(aData.mHidden),
84 mIsBadUnderlineFamily(aData.mBadUnderline),
85 mIsForceClassic(aData.mForceClassic),
86 mIsSimple(false) {
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);
96 return;
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!
106 return;
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
118 isSimple = true;
119 for (const auto& f : aFaces) {
120 if (!f.mWeight.IsSingle() || !f.mStretch.IsSingle() ||
121 !f.mStyle.IsSingle()) {
122 isSimple = false;
123 break;
125 if (!f.mStretch.Min().IsNormal()) {
126 isSimple = false;
127 break;
129 // Figure out which slot (0-3) this face belongs in
130 size_t slot = 0;
131 static_assert((kBoldMask | kItalicMask) == 0b11, "bad bold/italic bits");
132 if (f.mWeight.Min().IsBold()) {
133 slot |= kBoldMask;
135 if (f.mStyle.Min().IsItalic() || f.mStyle.Min().IsOblique()) {
136 slot |= kItalicMask;
138 if (slots[slot]) {
139 // More than one face mapped to the same slot - not a simple family!
140 isSimple = false;
141 break;
143 slots[slot] = &f;
145 if (isSimple) {
146 // Ensure all 4 slots will exist, even if some are empty.
147 count = 4;
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();
158 } else {
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]);
162 facePtrs[i] = fp;
166 mIsSimple = isSimple;
167 mFaces = p;
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);
177 LOG_FONTLIST(
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()) {
191 return;
194 Pointer* facePtrs = Faces(aList);
195 if (!facePtrs) {
196 return;
199 // If the family has only one face, we simply return it; no further
200 // checking needed.
201 if (NumFaces() == 1) {
202 MOZ_ASSERT(!facePtrs[0].IsNull());
203 aFaceList.AppendElement(static_cast<Face*>(facePtrs[0].ToPtr(aList)));
204 return;
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
213 // and/or widths.
215 if (mIsSimple) {
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
219 // present.
220 bool wantBold = aStyle.weight >= FontWeight(600);
221 bool wantItalic = !aStyle.style.IsNormal();
222 uint8_t faceIndex =
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);
229 return;
232 // order to check fallback faces in a simple family, depending on requested
233 // style
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
245 // actually exists
246 face = static_cast<Face*>(facePtrs[order[trial]].ToPtr(aList));
247 if (face && face->HasValidDescriptor()) {
248 aFaceList.AppendElement(face);
249 return;
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
266 // be added.
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) {
275 matched = face;
276 if (!aFaceList.IsEmpty()) {
277 aFaceList.Clear();
279 minDistance = distance;
280 } else if (distance == minDistance) {
281 if (matched) {
282 aFaceList.AppendElement(matched);
284 matched = face;
288 MOZ_ASSERT(matched, "didn't match a font within a family");
289 if (matched) {
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)) {
306 return;
308 if (!IsInitialized()) {
309 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(this)) {
310 return;
313 uint32_t numFaces = NumFaces();
314 uint32_t charMapsLoaded = 0; // number of faces whose charmap is loaded
315 Pointer* facePtrs = Faces(aList);
316 if (!facePtrs) {
317 return;
319 for (uint32_t i = 0; i < numFaces; i++) {
320 Face* face = static_cast<Face*>(facePtrs[i].ToPtr(aList));
321 if (!face) {
322 continue;
324 MOZ_ASSERT(face->HasValidDescriptor());
325 // Get the face's character map, if available (may be null!)
326 charmap =
327 static_cast<const SharedBitSet*>(face->mCharacterMap.ToPtr(aList));
328 if (charmap) {
329 ++charMapsLoaded;
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
340 // needed).
341 RefPtr<gfxFontEntry> fe =
342 gfxPlatformFontList::PlatformFontList()->GetOrCreateFontEntry(face,
343 this);
344 if (!fe) {
345 continue;
347 if (!charmap && !fe->HasCharacter(aMatchData->mCh)) {
348 continue;
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(),
369 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()) {
374 isSimple = false;
375 break;
377 if (!f->mStretch.Min().IsNormal()) {
378 isSimple = false;
379 break;
381 size_t slot = 0;
382 if (f->mWeight.Min().IsBold()) {
383 slot |= kBoldMask;
385 if (f->mStyle.Min().IsItalic() || f->mStyle.Min().IsOblique()) {
386 slot |= kItalicMask;
388 if (!slots[slot].IsNull()) {
389 isSimple = false;
390 break;
392 slots[slot] = fp;
394 if (isSimple) {
395 size_t size = 4 * sizeof(Pointer);
396 mFaces = aList->Alloc(size);
397 memcpy(mFaces.ToPtr(aList), slots, size);
398 mFaceCount.store(4);
399 mIsSimple = true;
400 return;
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()
414 // arrays
415 dom::ContentChild::GetSingleton()->SendSetupFamilyCharMap(
416 aList->GetGeneration(), aList->ToSharedPointer(this));
417 return;
419 gfxSparseBitSet familyMap;
420 Pointer firstMapShmPointer;
421 SharedBitSet* firstMap = nullptr;
422 bool merged = false;
423 Pointer* faces = Faces(aList);
424 if (!faces) {
425 return;
427 for (size_t i = 0; i < NumFaces(); i++) {
428 auto f = static_cast<Face*>(faces[i].ToPtr(aList));
429 if (!f) {
430 continue;
432 auto faceMap = static_cast<SharedBitSet*>(f->mCharacterMap.ToPtr(aList));
433 MOZ_ASSERT(faceMap);
434 if (!firstMap) {
435 firstMap = faceMap;
436 firstMapShmPointer = f->mCharacterMap;
437 } else if (faceMap != firstMap) {
438 if (!merged) {
439 familyMap.Union(*firstMap);
440 merged = true;
442 familyMap.Union(*faceMap);
445 if (merged) {
446 mCharacterMap =
447 gfxPlatformFontList::PlatformFontList()->GetShmemCharMap(&familyMap);
448 } else {
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();
467 } else {
468 MOZ_CRASH("parent: failed to initialize FontList");
470 } else {
471 for (unsigned retryCount = 0; retryCount < 3; ++retryCount) {
472 if (UpdateShmBlocks()) {
473 return;
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.
481 DetachShmBlocks();
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");
494 return false;
496 if (!newShm->Map(SHM_BLOCK_SIZE)) {
497 MOZ_CRASH("failed to map shared memory");
500 char* addr = static_cast<char*>(newShm->memory());
501 if (!addr) {
502 MOZ_CRASH("null shared memory?");
503 return false;
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());
513 return true;
516 void FontList::DetachShmBlocks() {
517 for (auto& i : mBlocks) {
518 i->mShmem = nullptr;
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)) {
531 return nullptr;
533 RefPtr<ipc::SharedMemoryBasic> newShm = new ipc::SharedMemoryBasic();
534 if (!newShm->IsHandleValid(handle)) {
535 return nullptr;
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());
545 if (!addr) {
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());
555 if (!newBlock) {
556 return false;
558 mBlocks.AppendElement(newBlock);
560 return true;
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);
583 int32_t blockIndex;
584 uint32_t curAlloc;
585 while (true) {
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) {
592 break;
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;
607 break;
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()) {
623 return;
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());
646 ToLowerCase(key);
647 aliasArray.AppendElement(Family::InitData(key, i.Key()));
649 aliasArray.Sort();
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");
655 return;
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);
673 LOG_FONTLIST(
674 ("(shared-fontlist) face (%s) index %u, weight %s, style %s, "
675 "stretch %s",
676 desc.get(), face->mIndex, weight.get(), style.get(),
677 stretch.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>&
690 aLocalNameTable) {
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());
701 faceArray.Sort();
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));
726 private:
727 FontList* mList;
728 const nsCString& mTarget;
731 Header& header = GetHeader();
733 Family* families = Families();
734 size_t match;
735 if (BinarySearchIf(families, 0, header.mFamilyCount,
736 FamilyNameComparator(this, aName), &match)) {
737 return &families[match];
740 if (header.mAliasCount) {
741 families = AliasFamilies();
742 size_t match;
743 if (BinarySearchIf(families, 0, header.mAliasCount,
744 FamilyNameComparator(this, aName), &match)) {
745 return &families[match];
749 return nullptr;
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));
761 private:
762 FontList* mList;
763 const nsCString& mTarget;
766 Header& header = GetHeader();
768 LocalFaceRec* faces = LocalFaces();
769 size_t match;
770 if (BinarySearchIf(faces, 0, header.mLocalFaceCount,
771 FaceNameComparator(this, aName), &match)) {
772 return &faces[match];
775 return nullptr;
778 void FontList::SearchForLocalFace(const nsACString& aName, Family** aFamily,
779 Face** aFace) {
780 Header& header = GetHeader();
781 MOZ_ASSERT(header.mLocalFaceCount == 0,
782 "do not use when local face names are already set up!");
783 LOG_FONTLIST(
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) {
790 continue;
792 LOG_FONTLIST(("(shared-fontlist) checking family (%s)",
793 family->Key().AsString(this).BeginReading()));
794 if (!family->IsInitialized()) {
795 if (!gfxPlatformFontList::PlatformFontList()->InitializeFamily(family)) {
796 continue;
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));
802 if (!face) {
803 continue;
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()));
810 ToLowerCase(psname);
811 ToLowerCase(fullname);
812 if (aName == psname || aName == fullname) {
813 *aFamily = family;
814 *aFace = face;
815 return;
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