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