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