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