Bug 1744135 Part 2: Annotate macOS font loading with font name if a crash happens...
[gecko.git] / gfx / thebes / gfxMacPlatformFontList.mm
blob3d89c421fc2bb50633e065e52e2478ecb3b6486f
1 /* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * ***** BEGIN LICENSE BLOCK *****
3  * Version: BSD
4  *
5  * Copyright (C) 2006-2009 Mozilla Corporation.  All rights reserved.
6  *
7  * Contributor(s):
8  *   Vladimir Vukicevic <vladimir@pobox.com>
9  *   Masayuki Nakano <masayuki@d-toybox.com>
10  *   John Daggett <jdaggett@mozilla.com>
11  *   Jonathan Kew <jfkthame@gmail.com>
12  *
13  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  *
19  * 1.  Redistributions of source code must retain the above copyright
20  *     notice, this list of conditions and the following disclaimer.
21  * 2.  Redistributions in binary form must reproduce the above copyright
22  *     notice, this list of conditions and the following disclaimer in the
23  *     documentation and/or other materials provided with the distribution.
24  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
25  *     its contributors may be used to endorse or promote products derived
26  *     from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
29  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
32  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
35  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  * ***** END LICENSE BLOCK ***** */
41 #include "mozilla/Logging.h"
43 #include <algorithm>
45 #import <AppKit/AppKit.h>
47 #include "gfxFontConstants.h"
48 #include "gfxPlatformMac.h"
49 #include "gfxMacPlatformFontList.h"
50 #include "gfxMacFont.h"
51 #include "gfxUserFontSet.h"
52 #include "SharedFontList-impl.h"
54 #include "harfbuzz/hb.h"
56 #include "AppleUtils.h"
57 #include "MainThreadUtils.h"
58 #include "nsDirectoryServiceUtils.h"
59 #include "nsDirectoryServiceDefs.h"
60 #include "nsAppDirectoryServiceDefs.h"
61 #include "nsIDirectoryEnumerator.h"
62 #include "nsCharTraits.h"
63 #include "nsCocoaFeatures.h"
64 #include "nsCocoaUtils.h"
65 #include "nsComponentManagerUtils.h"
66 #include "nsServiceManagerUtils.h"
67 #include "nsTArray.h"
69 #include "mozilla/dom/ContentChild.h"
70 #include "mozilla/dom/ContentParent.h"
71 #include "mozilla/FontPropertyTypes.h"
72 #include "mozilla/MemoryReporting.h"
73 #include "mozilla/Preferences.h"
74 #include "mozilla/ProfilerLabels.h"
75 #include "mozilla/Sprintf.h"
76 #include "mozilla/StaticPrefs_gfx.h"
77 #include "mozilla/Telemetry.h"
78 #include "mozilla/gfx/2D.h"
80 #include <unistd.h>
81 #include <time.h>
82 #include <dlfcn.h>
84 #include "StandardFonts-macos.inc"
86 using namespace mozilla;
87 using namespace mozilla::gfx;
89 // indexes into the NSArray objects that the Cocoa font manager returns
90 // as the available members of a family
91 #define INDEX_FONT_POSTSCRIPT_NAME 0
92 #define INDEX_FONT_FACE_NAME 1
93 #define INDEX_FONT_WEIGHT 2
94 #define INDEX_FONT_TRAITS 3
96 static const int kAppleMaxWeight = 14;
97 static const int kAppleExtraLightWeight = 3;
98 static const int kAppleUltraLightWeight = 2;
100 static const int gAppleWeightToCSSWeight[] = {
101     0,
102     1,  // 1.
103     1,  // 2.  W1, ultralight
104     2,  // 3.  W2, extralight
105     3,  // 4.  W3, light
106     4,  // 5.  W4, semilight
107     5,  // 6.  W5, medium
108     6,  // 7.
109     6,  // 8.  W6, semibold
110     7,  // 9.  W7, bold
111     8,  // 10. W8, extrabold
112     8,  // 11.
113     9,  // 12. W9, ultrabold
114     9,  // 13
115     9   // 14
118 // cache Cocoa's "shared font manager" for performance
119 static NSFontManager* sFontManager;
121 static void GetStringForNSString(const NSString* aSrc, nsAString& aDest) {
122   aDest.SetLength([aSrc length]);
123   [aSrc getCharacters:reinterpret_cast<unichar*>(aDest.BeginWriting())
124                 range:NSMakeRange(0, [aSrc length])];
127 static NSString* GetNSStringForString(const nsAString& aSrc) {
128   return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
129                                  length:aSrc.Length()];
132 #define LOG_FONTLIST(args) \
133   MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args)
134 #define LOG_FONTLIST_ENABLED() \
135   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug)
136 #define LOG_CMAPDATA_ENABLED() \
137   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug)
139 #pragma mark -
141 // Complex scripts will not render correctly unless appropriate AAT or OT
142 // layout tables are present.
143 // For OpenType, we also check that the GSUB table supports the relevant
144 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
145 // the characters, but lacks OpenType support).
147 // TODO: consider whether we should move this to gfxFontEntry and do similar
148 // cmap-masking on other platforms to avoid using fonts that won't shape
149 // properly.
151 nsresult MacOSFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
152   // attempt this once, if errors occur leave a blank cmap
153   if (mCharacterMap || mShmemCharacterMap) {
154     return NS_OK;
155   }
157   RefPtr<gfxCharacterMap> charmap;
158   nsresult rv;
160   if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
161     rv = NS_OK;
162   } else {
163     uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
164     charmap = new gfxCharacterMap();
165     AutoTable cmapTable(this, kCMAP);
167     if (cmapTable) {
168       uint32_t cmapLen;
169       const uint8_t* cmapData =
170           reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable, &cmapLen));
171       rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, mUVSOffset);
172     } else {
173       rv = NS_ERROR_NOT_AVAILABLE;
174     }
175   }
177   if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) {
178     // For downloadable fonts, trust the author and don't
179     // try to munge the cmap based on script shaping support.
181     // We also assume a Graphite font knows what it's doing,
182     // and provides whatever shaping is needed for the
183     // characters it supports, so only check/clear the
184     // complex-script ranges for non-Graphite fonts
186     // for layout support, check for the presence of mort/morx/kerx and/or
187     // opentype layout tables
188     bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 'x')) ||
189                         HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 't'));
190     bool hasAppleKerning = HasFontTable(TRUETYPE_TAG('k', 'e', 'r', 'x'));
191     bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B'));
192     bool hasGPOS = HasFontTable(TRUETYPE_TAG('G', 'P', 'O', 'S'));
193     if ((hasAATLayout && !(hasGSUB || hasGPOS)) || hasAppleKerning) {
194       mRequiresAAT = true;  // prefer CoreText if font has no OTL tables,
195                             // or if it uses the Apple-specific 'kerx'
196                             // variant of kerning table
197     }
199     for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; sr->rangeStart; sr++) {
200       // check to see if the cmap includes complex script codepoints
201       if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
202         if (hasAATLayout) {
203           // prefer CoreText for Apple's complex-script fonts,
204           // even if they also have some OpenType tables
205           // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
206           mRequiresAAT = true;
207           // and don't mask off complex-script ranges, we assume
208           // the AAT tables will provide the necessary shaping
209           continue;
210         }
212         // We check for GSUB here, as GPOS alone would not be ok.
213         if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) {
214           continue;
215         }
217         charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
218       }
219     }
221     // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious
222     // blank glyphs for obscure Tibetan and Arabic-script codepoints.
223     // Blocklist these so that font fallback will not use them.
224     if (mRequiresAAT &&
225         (FamilyName().EqualsLiteral("Songti SC") || FamilyName().EqualsLiteral("Songti TC") ||
226          FamilyName().EqualsLiteral("STSong") ||
227          // Bug 1390980: on 10.11, the Kaiti fonts are also affected.
228          FamilyName().EqualsLiteral("Kaiti SC") || FamilyName().EqualsLiteral("Kaiti TC") ||
229          FamilyName().EqualsLiteral("STKaiti"))) {
230       charmap->ClearRange(0x0f6b, 0x0f70);
231       charmap->ClearRange(0x0f8c, 0x0f8f);
232       charmap->clear(0x0f98);
233       charmap->clear(0x0fbd);
234       charmap->ClearRange(0x0fcd, 0x0fff);
235       charmap->clear(0x0620);
236       charmap->clear(0x065f);
237       charmap->ClearRange(0x06ee, 0x06ef);
238       charmap->clear(0x06ff);
239     }
240   }
242   mHasCmapTable = NS_SUCCEEDED(rv);
243   if (mHasCmapTable) {
244     gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
245     fontlist::FontList* sharedFontList = pfl->SharedFontList();
246     if (!IsUserFont() && mShmemFace) {
247       mShmemFace->SetCharacterMap(sharedFontList, charmap);  // async
248       if (!TrySetShmemCharacterMap()) {
249         // Temporarily retain charmap, until the shared version is
250         // ready for use.
251         mCharacterMap = charmap;
252       }
253     } else {
254       mCharacterMap = pfl->FindCharMap(charmap);
255     }
256   } else {
257     // if error occurred, initialize to null cmap
258     mCharacterMap = new gfxCharacterMap();
259   }
261   LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n", mName.get(),
262                 charmap->SizeOfIncludingThis(moz_malloc_size_of), charmap->mHash,
263                 mCharacterMap == charmap ? " new" : ""));
264   if (LOG_CMAPDATA_ENABLED()) {
265     char prefix[256];
266     SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
267     charmap->Dump(prefix, eGfxLog_cmapdata);
268   }
270   return rv;
273 gfxFont* MacOSFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
274   RefPtr<UnscaledFontMac> unscaledFont(mUnscaledFont);
275   if (!unscaledFont) {
276     CGFontRef baseFont = GetFontRef();
277     if (!baseFont) {
278       return nullptr;
279     }
280     unscaledFont = new UnscaledFontMac(baseFont, mIsDataUserFont);
281     mUnscaledFont = unscaledFont;
282   }
284   return new gfxMacFont(unscaledFont, this, aFontStyle);
287 bool MacOSFontEntry::HasVariations() {
288   if (!mHasVariationsInitialized) {
289     mHasVariationsInitialized = true;
290     mHasVariations = gfxPlatform::GetPlatform()->HasVariationFontSupport() &&
291                      HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r'));
292   }
294   return mHasVariations;
297 void MacOSFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes) {
298   // We could do this by creating a CTFont and calling CTFontCopyVariationAxes,
299   // but it is expensive to instantiate a CTFont for every face just to set up
300   // the axis information.
301   // Instead we use gfxFontUtils to read the font tables directly.
302   gfxFontUtils::GetVariationData(this, &aVariationAxes, nullptr);
305 void MacOSFontEntry::GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances) {
306   // Core Text doesn't offer API for this, so we use gfxFontUtils to read the
307   // font tables directly.
308   gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
311 bool MacOSFontEntry::IsCFF() {
312   if (!mIsCFFInitialized) {
313     mIsCFFInitialized = true;
314     mIsCFF = HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '));
315   }
317   return mIsCFF;
320 MacOSFontEntry::MacOSFontEntry(const nsACString& aPostscriptName, WeightRange aWeight,
321                                bool aIsStandardFace, double aSizeHint)
322     : gfxFontEntry(aPostscriptName, aIsStandardFace),
323       mFontRef(NULL),
324       mSizeHint(aSizeHint),
325       mFontRefInitialized(false),
326       mRequiresAAT(false),
327       mIsCFF(false),
328       mIsCFFInitialized(false),
329       mHasVariations(false),
330       mHasVariationsInitialized(false),
331       mHasAATSmallCaps(false),
332       mHasAATSmallCapsInitialized(false) {
333   mWeightRange = aWeight;
334   mOpszAxis.mTag = 0;
337 MacOSFontEntry::MacOSFontEntry(const nsACString& aPostscriptName, CGFontRef aFontRef,
338                                WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle,
339                                bool aIsDataUserFont, bool aIsLocalUserFont)
340     : gfxFontEntry(aPostscriptName, false),
341       mFontRef(NULL),
342       mSizeHint(0.0),
343       mFontRefInitialized(false),
344       mRequiresAAT(false),
345       mIsCFF(false),
346       mIsCFFInitialized(false),
347       mHasVariations(false),
348       mHasVariationsInitialized(false),
349       mHasAATSmallCaps(false),
350       mHasAATSmallCapsInitialized(false) {
351   mFontRef = aFontRef;
352   mFontRefInitialized = true;
353   ::CFRetain(mFontRef);
355   mWeightRange = aWeight;
356   mStretchRange = aStretch;
357   mFixedPitch = false;  // xxx - do we need this for downloaded fonts?
358   mStyleRange = aStyle;
359   mOpszAxis.mTag = 0;
361   NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
362                "userfont is either a data font or a local font");
363   mIsDataUserFont = aIsDataUserFont;
364   mIsLocalUserFont = aIsLocalUserFont;
367 gfxFontEntry* MacOSFontEntry::Clone() const {
368   MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
369   MacOSFontEntry* fe = new MacOSFontEntry(Name(), Weight(), mStandardFace, mSizeHint);
370   fe->mStyleRange = mStyleRange;
371   fe->mStretchRange = mStretchRange;
372   fe->mFixedPitch = mFixedPitch;
373   return fe;
376 CGFontRef MacOSFontEntry::GetFontRef() {
377   if (!mFontRefInitialized) {
378     // Cache the CGFontRef, to be released by our destructor.
379     mFontRef = CreateOrCopyFontRef();
380     mFontRefInitialized = true;
381   }
382   // Return a non-retained reference; caller does not need to release.
383   return mFontRef;
386 CGFontRef MacOSFontEntry::CreateOrCopyFontRef() {
387   if (mFontRef) {
388     // We have a cached CGFont, just add a reference. Caller must
389     // release, but we'll still own our reference.
390     ::CGFontRetain(mFontRef);
391     return mFontRef;
392   }
393   // Create a new CGFont; caller will own the only reference to it.
394   NSString* psname = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
395   CGFontRef ref = CGFontCreateWithFontName(CFStringRef(psname));
396   if (!ref) {
397     CrashReporter::AutoAnnotateCrashReport autoFontName(CrashReporter::Annotation::FontName,
398                                                         psname);
400     // This happens on macOS 10.12 for font entry names that start with
401     // .AppleSystemUIFont. For those fonts, we need to go through NSFont
402     // to get the correct CGFontRef.
403     // Both the Text and the Display variant of the display font use
404     // .AppleSystemUIFontSomethingSomething as their member names.
405     // That's why we're carrying along mSizeHint to this place so that
406     // we get the variant that we want for this family.
407     NSFont* font = [NSFont fontWithName:psname size:mSizeHint];
408     if (font) {
409       ref = CTFontCopyGraphicsFont((CTFontRef)font, nullptr);
410     }
411   }
412   return ref;  // Not saved in mFontRef; caller will own the reference
415 // For a logging build, we wrap the CFDataRef in a FontTableRec so that we can
416 // use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging
417 // does not get this overhead.
418 class FontTableRec {
419  public:
420   explicit FontTableRec(CFDataRef aDataRef) : mDataRef(aDataRef) { MOZ_COUNT_CTOR(FontTableRec); }
422   ~FontTableRec() {
423     MOZ_COUNT_DTOR(FontTableRec);
424     ::CFRelease(mDataRef);
425   }
427  private:
428   CFDataRef mDataRef;
431 /*static*/ void MacOSFontEntry::DestroyBlobFunc(void* aUserData) {
432 #ifdef NS_BUILD_REFCNT_LOGGING
433   FontTableRec* ftr = static_cast<FontTableRec*>(aUserData);
434   delete ftr;
435 #else
436   ::CFRelease((CFDataRef)aUserData);
437 #endif
440 hb_blob_t* MacOSFontEntry::GetFontTable(uint32_t aTag) {
441   AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef();
442   if (!fontRef) {
443     return nullptr;
444   }
446   CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
447   if (dataRef) {
448     return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef), ::CFDataGetLength(dataRef),
449                           HB_MEMORY_MODE_READONLY,
450 #ifdef NS_BUILD_REFCNT_LOGGING
451                           new FontTableRec(dataRef),
452 #else
453                           (void*)dataRef,
454 #endif
455                           DestroyBlobFunc);
456   }
458   return nullptr;
461 bool MacOSFontEntry::HasFontTable(uint32_t aTableTag) {
462   if (mAvailableTables.Count() == 0) {
463     nsAutoreleasePool localPool;
465     AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef();
466     if (!fontRef) {
467       return false;
468     }
469     AutoCFRelease<CFArrayRef> tags = ::CGFontCopyTableTags(fontRef);
470     if (!tags) {
471       return false;
472     }
473     int numTags = (int)::CFArrayGetCount(tags);
474     for (int t = 0; t < numTags; t++) {
475       uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t);
476       mAvailableTables.PutEntry(tag);
477     }
478   }
480   return mAvailableTables.GetEntry(aTableTag);
483 static bool CheckForAATSmallCaps(CFArrayRef aFeatures) {
484   // Walk the array of feature descriptors from the font, and see whether
485   // a small-caps feature setting is available.
486   // Just bail out (returning false) if at any point we fail to find the
487   // expected dictionary keys, etc; if the font has bad data, we don't even
488   // try to search the rest of it.
489   auto numFeatures = CFArrayGetCount(aFeatures);
490   for (auto f = 0; f < numFeatures; ++f) {
491     auto featureDict = (CFDictionaryRef)CFArrayGetValueAtIndex(aFeatures, f);
492     if (!featureDict) {
493       return false;
494     }
495     auto featureNum =
496         (CFNumberRef)CFDictionaryGetValue(featureDict, CFSTR("CTFeatureTypeIdentifier"));
497     if (!featureNum) {
498       return false;
499     }
500     int16_t featureType;
501     if (!CFNumberGetValue(featureNum, kCFNumberSInt16Type, &featureType)) {
502       return false;
503     }
504     if (featureType == kLetterCaseType || featureType == kLowerCaseType) {
505       // Which selector to look for, depending whether we've found the
506       // legacy LetterCase feature or the new LowerCase one.
507       const uint16_t smallCaps =
508           (featureType == kLetterCaseType) ? kSmallCapsSelector : kLowerCaseSmallCapsSelector;
509       auto selectors =
510           (CFArrayRef)CFDictionaryGetValue(featureDict, CFSTR("CTFeatureTypeSelectors"));
511       if (!selectors) {
512         return false;
513       }
514       auto numSelectors = CFArrayGetCount(selectors);
515       for (auto s = 0; s < numSelectors; s++) {
516         auto selectorDict = (CFDictionaryRef)CFArrayGetValueAtIndex(selectors, s);
517         if (!selectorDict) {
518           return false;
519         }
520         auto selectorNum =
521             (CFNumberRef)CFDictionaryGetValue(selectorDict, CFSTR("CTFeatureSelectorIdentifier"));
522         if (!selectorNum) {
523           return false;
524         }
525         int16_t selectorValue;
526         if (!CFNumberGetValue(selectorNum, kCFNumberSInt16Type, &selectorValue)) {
527           return false;
528         }
529         if (selectorValue == smallCaps) {
530           return true;
531         }
532       }
533     }
534   }
535   return false;
538 bool MacOSFontEntry::SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag) {
539   // If we're going to shape with Core Text, we don't support added
540   // OpenType features (aside from any CT applies by default), except
541   // for 'smcp' which we map to an AAT feature selector.
542   if (RequiresAATLayout()) {
543     if (aFeatureTag != HB_TAG('s', 'm', 'c', 'p')) {
544       return false;
545     }
546     if (mHasAATSmallCapsInitialized) {
547       return mHasAATSmallCaps;
548     }
549     mHasAATSmallCapsInitialized = true;
550     CGFontRef cgFont = GetFontRef();
551     if (!cgFont) {
552       return mHasAATSmallCaps;
553     }
554     AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr);
555     if (ctFont) {
556       AutoCFRelease<CFArrayRef> features = CTFontCopyFeatures(ctFont);
557       if (features) {
558         mHasAATSmallCaps = CheckForAATSmallCaps(features);
559       }
560     }
561     return mHasAATSmallCaps;
562   }
563   return gfxFontEntry::SupportsOpenTypeFeature(aScript, aFeatureTag);
566 void MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
567                                             FontListSizes* aSizes) const {
568   aSizes->mFontListSize += aMallocSizeOf(this);
569   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
572 /* gfxMacFontFamily */
573 #pragma mark -
575 class gfxMacFontFamily final : public gfxFontFamily {
576  public:
577   gfxMacFontFamily(const nsACString& aName, FontVisibility aVisibility, double aSizeHint)
578       : gfxFontFamily(aName, aVisibility), mSizeHint(aSizeHint) {}
580   virtual ~gfxMacFontFamily() = default;
582   virtual void LocalizedName(nsACString& aLocalizedName);
584   virtual void FindStyleVariations(FontInfoData* aFontInfoData = nullptr);
586  protected:
587   double mSizeHint;
590 void gfxMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
591   nsAutoreleasePool localPool;
593   // It's unsafe to call HasOtherFamilyNames off the main thread because
594   // it entrains FindStyleVariations, which calls GetWeightOverride, which
595   // retrieves prefs.  And the pref names can change (via user overrides),
596   // so we can't use StaticPrefs to access them.
597   if (NS_IsMainThread() && !HasOtherFamilyNames()) {
598     aLocalizedName = mName;
599     return;
600   }
602   NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
603   NSString* localized = [sFontManager localizedNameForFamily:family face:nil];
605   if (localized) {
606     nsAutoString locName;
607     GetStringForNSString(localized, locName);
608     CopyUTF16toUTF8(locName, aLocalizedName);
609     return;
610   }
612   // failed to get localized name, just use the canonical one
613   aLocalizedName = mName;
616 // Return the CSS weight value to use for the given face, overriding what
617 // AppKit gives us (used to adjust families with bad weight values, see
618 // bug 931426).
619 // A return value of 0 indicates no override - use the existing weight.
620 static inline int GetWeightOverride(const nsAString& aPSName) {
621   nsAutoCString prefName("font.weight-override.");
622   // The PostScript name is required to be ASCII; if it's not, the font is
623   // broken anyway, so we really don't care that this is lossy.
624   LossyAppendUTF16toASCII(aPSName, prefName);
625   return Preferences::GetInt(prefName.get(), 0);
628 void gfxMacFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
629   if (mHasStyles) {
630     return;
631   }
633   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxMacFontFamily::FindStyleVariations", LAYOUT, mName);
635   nsAutoreleasePool localPool;
637   NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
639   // create a font entry for each face
640   NSArray* fontfaces = [sFontManager
641       availableMembersOfFontFamily:family];  // returns an array of [psname, style name, weight,
642                                              // traits] elements, goofy api
643   int faceCount = [fontfaces count];
644   int faceIndex;
646   for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
647     NSArray* face = [fontfaces objectAtIndex:faceIndex];
648     NSString* psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
649     int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
650     uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
651     NSString* facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
652     bool isStandardFace = false;
654     if (appKitWeight == kAppleExtraLightWeight) {
655       // if the facename contains UltraLight, set the weight to the ultralight weight value
656       NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
657       if (range.location != NSNotFound) {
658         appKitWeight = kAppleUltraLightWeight;
659       }
660     }
662     // make a nsString
663     nsAutoString postscriptFontName;
664     GetStringForNSString(psname, postscriptFontName);
666     int32_t cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
667     // If we are on the startup-time InitFontList thread, we skip this as it
668     // wants to retrieve faces for the default font family, but cannot safely
669     // call the Preferences APIs. Fortunately, the default font doesn't actually
670     // depend on a weight override pref.
671     if (!gfxPlatformFontList::IsInitFontListThread()) {
672       int32_t weightOverride = GetWeightOverride(postscriptFontName);
673       if (weightOverride) {
674         // scale down and clamp, to get a value from 1..9
675         cssWeight = ((weightOverride + 50) / 100);
676         cssWeight = std::max(1, std::min(cssWeight, 9));
677       }
678     }
679     cssWeight *= 100;  // scale up to CSS values
681     if ([facename isEqualToString:@"Regular"] || [facename isEqualToString:@"Bold"] ||
682         [facename isEqualToString:@"Italic"] || [facename isEqualToString:@"Oblique"] ||
683         [facename isEqualToString:@"Bold Italic"] || [facename isEqualToString:@"Bold Oblique"]) {
684       isStandardFace = true;
685     }
687     // create a font entry
688     MacOSFontEntry* fontEntry =
689         new MacOSFontEntry(NS_ConvertUTF16toUTF8(postscriptFontName),
690                            WeightRange(FontWeight(cssWeight)), isStandardFace, mSizeHint);
691     if (!fontEntry) {
692       break;
693     }
695     // set additional properties based on the traits reported by Cocoa
696     if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
697       fontEntry->mStretchRange = StretchRange(FontStretch::Condensed());
698     } else if (macTraits & NSExpandedFontMask) {
699       fontEntry->mStretchRange = StretchRange(FontStretch::Expanded());
700     }
701     // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
702     // at least (see bug 611855), so check for style name endings as well
703     if ((macTraits & NSItalicFontMask) || [facename hasSuffix:@"Italic"] ||
704         [facename hasSuffix:@"Oblique"]) {
705       fontEntry->mStyleRange = SlantStyleRange(FontSlantStyle::Italic());
706     }
707     if (macTraits & NSFixedPitchFontMask) {
708       fontEntry->mFixedPitch = true;
709     }
711     if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
712       fontEntry->SetupVariationRanges();
713     }
715     if (LOG_FONTLIST_ENABLED()) {
716       nsAutoCString weightString;
717       fontEntry->Weight().ToString(weightString);
718       nsAutoCString stretchString;
719       fontEntry->Stretch().ToString(stretchString);
720       LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
721                     " with style: %s weight: %s stretch: %s"
722                     " (apple-weight: %d macTraits: %8.8x)",
723                     fontEntry->Name().get(), Name().get(),
724                     fontEntry->IsItalic() ? "italic" : "normal", weightString.get(),
725                     stretchString.get(), appKitWeight, macTraits));
726     }
728     // insert into font entry array of family
729     AddFontEntry(fontEntry);
730   }
732   SortAvailableFonts();
733   SetHasStyles(true);
735   if (mIsBadUnderlineFamily) {
736     SetBadUnderlineFonts();
737   }
739   CheckForSimpleFamily();
742 /* gfxSingleFaceMacFontFamily */
743 #pragma mark -
745 class gfxSingleFaceMacFontFamily final : public gfxFontFamily {
746  public:
747   gfxSingleFaceMacFontFamily(const nsACString& aName, FontVisibility aVisibility)
748       : gfxFontFamily(aName, aVisibility) {
749     mFaceNamesInitialized = true;  // omit from face name lists
750   }
752   virtual ~gfxSingleFaceMacFontFamily() = default;
754   virtual void LocalizedName(nsACString& aLocalizedName);
756   virtual void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList);
758   virtual bool IsSingleFaceFamily() const { return true; }
761 void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
762   nsAutoreleasePool localPool;
764   if (!HasOtherFamilyNames()) {
765     aLocalizedName = mName;
766     return;
767   }
769   gfxFontEntry* fe = mAvailableFonts[0];
770   NSFont* font = [NSFont fontWithName:GetNSStringForString(NS_ConvertUTF8toUTF16(fe->Name()))
771                                  size:0.0];
772   if (font) {
773     NSString* localized = [font displayName];
774     if (localized) {
775       nsAutoString locName;
776       GetStringForNSString(localized, locName);
777       CopyUTF16toUTF8(locName, aLocalizedName);
778       return;
779     }
780   }
782   // failed to get localized name, just use the canonical one
783   aLocalizedName = mName;
786 void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) {
787   if (mOtherFamilyNamesInitialized) {
788     return;
789   }
791   gfxFontEntry* fe = mAvailableFonts[0];
792   if (!fe) {
793     return;
794   }
796   const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
798   gfxFontEntry::AutoTable nameTable(fe, kNAME);
799   if (!nameTable) {
800     return;
801   }
803   mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true);
805   mOtherFamilyNamesInitialized = true;
808 /* gfxMacPlatformFontList */
809 #pragma mark -
811 gfxMacPlatformFontList::gfxMacPlatformFontList()
812     : gfxPlatformFontList(false), mDefaultFont(nullptr), mUseSizeSensitiveSystemFont(false) {
813   CheckFamilyList(kBaseFonts);
815   // cache this in a static variable so that MacOSFontFamily objects
816   // don't have to repeatedly look it up
817   sFontManager = [NSFontManager sharedFontManager];
819   // Load the font-list preferences now, so that we don't have to do it from
820   // Init[Shared]FontListForPlatform, which may be called off-main-thread.
821   gfxFontUtils::GetPrefsFontList("font.single-face-list", mSingleFaceFonts);
822   gfxFontUtils::GetPrefsFontList("font.preload-names-list", mPreloadFonts);
825 gfxMacPlatformFontList::~gfxMacPlatformFontList() {
826   if (XRE_IsParentProcess()) {
827     ::CFNotificationCenterRemoveObserver(::CFNotificationCenterGetLocalCenter(), this,
828                                          kCTFontManagerRegisteredFontsChangedNotification, 0);
829   }
831   if (mDefaultFont) {
832     ::CFRelease(mDefaultFont);
833   }
836 void gfxMacPlatformFontList::AddFamily(const nsACString& aFamilyName, FontVisibility aVisibility) {
837   double sizeHint = 0.0;
838   if (aVisibility == FontVisibility::Hidden && mUseSizeSensitiveSystemFont &&
839       mSystemDisplayFontFamilyName.Equals(aFamilyName)) {
840     sizeHint = 128.0;
841   }
843   nsAutoCString key;
844   ToLowerCase(aFamilyName, key);
846   RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(aFamilyName, aVisibility, sizeHint);
847   mFontFamilies.InsertOrUpdate(key, RefPtr{familyEntry});
849   // check the bad underline blocklist
850   if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
851     familyEntry->SetBadUnderlineFamily();
852   }
855 FontVisibility gfxMacPlatformFontList::GetVisibilityForFamily(const nsACString& aName) const {
856   if (aName[0] == '.' || aName.LowerCaseEqualsLiteral("lastresort")) {
857     return FontVisibility::Hidden;
858   }
859   if (FamilyInList(aName, kBaseFonts)) {
860     return FontVisibility::Base;
861   }
862   return FontVisibility::User;
865 void gfxMacPlatformFontList::AddFamily(CFStringRef aFamily) {
866   NSString* family = (NSString*)aFamily;
868   // CTFontManager includes weird internal family names and
869   // LastResort, skip over those
870   if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame ||
871       [family caseInsensitiveCompare:@".LastResort"] == NSOrderedSame) {
872     return;
873   }
875   nsAutoString familyName;
876   nsCocoaUtils::GetStringForNSString(family, familyName);
878   NS_ConvertUTF16toUTF8 nameUtf8(familyName);
879   AddFamily(nameUtf8, GetVisibilityForFamily(nameUtf8));
882 void gfxMacPlatformFontList::ReadSystemFontList(dom::SystemFontList* aList) {
883   // Note: We rely on the records for mSystemTextFontFamilyName and
884   // mSystemDisplayFontFamilyName (if present) being *before* the main
885   // font list, so that those names are known in the content process
886   // by the time we add the actual family records to the font list.
887   aList->entries().AppendElement(FontFamilyListEntry(
888       mSystemTextFontFamilyName, FontVisibility::Unknown, kTextSizeSystemFontFamily));
889   if (mUseSizeSensitiveSystemFont) {
890     aList->entries().AppendElement(FontFamilyListEntry(
891         mSystemDisplayFontFamilyName, FontVisibility::Unknown, kDisplaySizeSystemFontFamily));
892   }
893   // Now collect the list of available families, with visibility attributes.
894   for (auto f = mFontFamilies.Iter(); !f.Done(); f.Next()) {
895     auto macFamily = f.Data().get();
896     if (macFamily->IsSingleFaceFamily()) {
897       continue;  // skip, this will be recreated separately in the child
898     }
899     aList->entries().AppendElement(
900         FontFamilyListEntry(macFamily->Name(), macFamily->Visibility(), kStandardFontFamily));
901   }
904 void gfxMacPlatformFontList::PreloadNamesList() {
905   uint32_t numFonts = mPreloadFonts.Length();
906   for (uint32_t i = 0; i < numFonts; i++) {
907     nsAutoCString key;
908     GenerateFontListKey(mPreloadFonts[i], key);
910     // only search canonical names!
911     gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
912     if (familyEntry) {
913       familyEntry->ReadOtherFamilyNames(this);
914     }
915   }
918 nsresult gfxMacPlatformFontList::InitFontListForPlatform() {
919   nsAutoreleasePool localPool;
921   // The font registration thread was created early in startup, to give the
922   // system a head start on activating all the supplemental-language fonts.
923   // Here, we need to wait until it has finished its work.
924   gfxPlatformMac::WaitForFontRegistration();
926   Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
928   InitSystemFontNames();
930   if (XRE_IsParentProcess()) {
931     static bool firstTime = true;
932     if (firstTime) {
933       ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(), this,
934                                         RegisteredFontsChangedNotificationCallback,
935                                         kCTFontManagerRegisteredFontsChangedNotification, 0,
936                                         CFNotificationSuspensionBehaviorDeliverImmediately);
937       firstTime = false;
938     }
940     // We're not a content process, so get the available fonts directly
941     // from Core Text.
942     AutoCFRelease<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames();
943     for (NSString* familyName in (NSArray*)(CFArrayRef)familyNames) {
944       AddFamily((CFStringRef)familyName);
945     }
946   } else {
947     // Content process: use font list passed from the chrome process via
948     // the GetXPCOMProcessAttributes message, because it's much faster than
949     // querying Core Text again in the child.
950     auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
951     for (FontFamilyListEntry& ffe : fontList.entries()) {
952       switch (ffe.entryType()) {
953         case kStandardFontFamily:
954           // On Catalina or later, we pre-initialize system font-family entries
955           // in InitSystemFontNames(), so we can just skip them here.
956           if (nsCocoaFeatures::OnCatalinaOrLater() &&
957               (ffe.familyName() == mSystemTextFontFamilyName ||
958                ffe.familyName() == mSystemDisplayFontFamilyName)) {
959             continue;
960           }
961           AddFamily(ffe.familyName(), ffe.visibility());
962           break;
963         case kTextSizeSystemFontFamily:
964           mSystemTextFontFamilyName = ffe.familyName();
965           break;
966         case kDisplaySizeSystemFontFamily:
967           mSystemDisplayFontFamilyName = ffe.familyName();
968           mUseSizeSensitiveSystemFont = true;
969           break;
970       }
971     }
972     fontList.entries().Clear();
973   }
975   InitSingleFaceList();
977   // to avoid full search of font name tables, seed the other names table with localized names from
978   // some of the prefs fonts which are accessed via their localized names.  changes in the pref
979   // fonts will only cause a font lookup miss earlier. this is a simple optimization, it's not
980   // required for correctness
981   PreloadNamesList();
983   // start the delayed cmap loader
984   GetPrefsAndStartLoader();
986   return NS_OK;
989 void gfxMacPlatformFontList::InitSharedFontListForPlatform() {
990   nsAutoreleasePool localPool;
992   gfxPlatformMac::WaitForFontRegistration();
994   InitSystemFontNames();
996   if (XRE_IsParentProcess()) {
997     // Only the parent process listens for OS font-changed notifications;
998     // after rebuilding its list, it will update the content processes.
999     static bool firstTime = true;
1000     if (firstTime) {
1001       ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(), this,
1002                                         RegisteredFontsChangedNotificationCallback,
1003                                         kCTFontManagerRegisteredFontsChangedNotification, 0,
1004                                         CFNotificationSuspensionBehaviorDeliverImmediately);
1005       firstTime = false;
1006     }
1008     AutoCFRelease<CFArrayRef> familyNames = CTFontManagerCopyAvailableFontFamilyNames();
1009     nsTArray<fontlist::Family::InitData> families;
1010     for (NSString* familyName in (NSArray*)(CFArrayRef)familyNames) {
1011       nsAutoString name16;
1012       GetStringForNSString(familyName, name16);
1013       NS_ConvertUTF16toUTF8 name(name16);
1014       nsAutoCString key;
1015       GenerateFontListKey(name, key);
1016       families.AppendElement(fontlist::Family::InitData(key, name, fontlist::Family::kNoIndex,
1017                                                         GetVisibilityForFamily(name)));
1018     }
1019     SharedFontList()->SetFamilyNames(families);
1020     InitAliasesForSingleFaceList();
1021     GetPrefsAndStartLoader();
1022   }
1025 void gfxMacPlatformFontList::InitAliasesForSingleFaceList() {
1026   for (const auto& familyName : mSingleFaceFonts) {
1027     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
1028     // Each entry in the "single face families" list is expected to be a
1029     // colon-separated pair of FaceName:Family,
1030     // where FaceName is the individual face name (psname) of a font
1031     // that should be exposed as a separate family name,
1032     // and Family is the standard family to which that face belongs.
1033     // The only such face listed by default is
1034     //    Osaka-Mono:Osaka
1035     auto colon = familyName.FindChar(':');
1036     if (colon == kNotFound) {
1037       continue;
1038     }
1040     // Look for the parent family in the main font family list,
1041     // and ensure we have loaded its list of available faces.
1042     nsAutoCString key;
1043     GenerateFontListKey(Substring(familyName, colon + 1), key);
1044     fontlist::Family* family = SharedFontList()->FindFamily(key);
1045     if (!family) {
1046       // The parent family is not present, so just ignore this entry.
1047       continue;
1048     }
1049     if (!family->IsInitialized()) {
1050       if (!gfxPlatformFontList::InitializeFamily(family)) {
1051         // This shouldn't ever fail, but if it does, we can safely ignore it.
1052         MOZ_ASSERT(false, "failed to initialize font family");
1053         continue;
1054       }
1055     }
1057     // Truncate the entry from prefs at the colon, so now it is just the
1058     // desired single-face-family name.
1059     nsAutoCString aliasName(Substring(familyName, 0, colon));
1061     // Look through the family's faces to see if this one is present.
1062     fontlist::FontList* list = SharedFontList();
1063     const fontlist::Pointer* facePtrs = family->Faces(list);
1064     for (size_t i = 0; i < family->NumFaces(); i++) {
1065       if (facePtrs[i].IsNull()) {
1066         continue;
1067       }
1068       auto face = static_cast<const fontlist::Face*>(facePtrs[i].ToPtr(list));
1069       if (face->mDescriptor.AsString(list).Equals(aliasName)) {
1070         // Found it! Create an entry in the Alias table.
1071         GenerateFontListKey(aliasName, key);
1072         if (SharedFontList()->FindFamily(key) || mAliasTable.Get(key)) {
1073           // If the family name is already known, something's misconfigured;
1074           // just ignore it.
1075           MOZ_ASSERT(false, "single-face family already known");
1076           break;
1077         }
1078         auto aliasData = mAliasTable.GetOrInsertNew(key);
1079         // The "alias" here isn't based on an existing family, so we don't call
1080         // aliasData->InitFromFamily(); the various flags are left as defaults.
1081         aliasData->mFaces.AppendElement(facePtrs[i]);
1082         aliasData->mBaseFamily = aliasName;
1083         aliasData->mVisibility = family->Visibility();
1084         break;
1085       }
1086     }
1087   }
1088   if (!mAliasTable.IsEmpty()) {
1089     // This will be updated when the font loader completes, but we require
1090     // at least the Osaka-Mono alias to be available immediately.
1091     SharedFontList()->SetAliases(mAliasTable);
1092   }
1095 void gfxMacPlatformFontList::InitSingleFaceList() {
1096   for (const auto& familyName : mSingleFaceFonts) {
1097     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
1098     // Each entry in the "single face families" list is expected to be a
1099     // colon-separated pair of FaceName:Family,
1100     // where FaceName is the individual face name (psname) of a font
1101     // that should be exposed as a separate family name,
1102     // and Family is the standard family to which that face belongs.
1103     // The only such face listed by default is
1104     //    Osaka-Mono:Osaka
1105     auto colon = familyName.FindChar(':');
1106     if (colon == kNotFound) {
1107       continue;
1108     }
1110     // Look for the parent family in the main font family list,
1111     // and ensure we have loaded its list of available faces.
1112     nsAutoCString key(Substring(familyName, colon + 1));
1113     ToLowerCase(key);
1114     gfxFontFamily* family = mFontFamilies.GetWeak(key);
1115     if (!family || family->IsHidden()) {
1116       continue;
1117     }
1118     family->FindStyleVariations();
1120     // Truncate the entry from prefs at the colon, so now it is just the
1121     // desired single-face-family name.
1122     nsAutoCString aliasName(Substring(familyName, 0, colon));
1124     // Look through the family's faces to see if this one is present.
1125     const gfxFontEntry* fe = nullptr;
1126     for (const auto& face : family->GetFontList()) {
1127       if (face->Name().Equals(aliasName)) {
1128         fe = face;
1129         break;
1130       }
1131     }
1132     if (!fe) {
1133       continue;
1134     }
1136     // We found the correct face, so create the single-face family record.
1137     GenerateFontListKey(aliasName, key);
1138     LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n", aliasName.get(), key.get()));
1140     // add only if doesn't exist already
1141     if (!mFontFamilies.GetWeak(key)) {
1142       RefPtr<gfxFontFamily> familyEntry =
1143           new gfxSingleFaceMacFontFamily(aliasName, family->Visibility());
1144       // We need a separate font entry, because its family name will
1145       // differ from the one we found in the main list.
1146       MacOSFontEntry* fontEntry = new MacOSFontEntry(
1147           fe->Name(), fe->Weight(), true, static_cast<const MacOSFontEntry*>(fe)->mSizeHint);
1148       familyEntry->AddFontEntry(fontEntry);
1149       familyEntry->SetHasStyles(true);
1150       mFontFamilies.InsertOrUpdate(key, std::move(familyEntry));
1151       LOG_FONTLIST(
1152           ("(fontlist-singleface) added new family: %s, key: %s\n", aliasName.get(), key.get()));
1153     }
1154   }
1157 // System fonts under OSX may contain weird "meta" names but if we create
1158 // a new font using just the Postscript name, the NSFont api returns an object
1159 // with the actual real family name. For example, under OSX 10.11:
1161 // [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont
1162 // [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName]
1163 //          size:8.0] familyName] ==> .SF NS Text
1165 static NSString* GetRealFamilyName(NSFont* aFont) {
1166   NSString* psName = [[aFont fontDescriptor] postscriptName];
1167   // With newer macOS versions and SDKs (e.g. when compiled against SDK 10.15),
1168   // [NSFont fontWithName:] fails for hidden system fonts, because the underlying
1169   // Core Text functions it uses reject such names and tell us to use the special
1170   // CTFontCreateUIFontForLanguage API instead.
1171   // To work around this, as we don't yet work directly with the CTFontUIFontType
1172   // identifiers, we create a Core Graphics font (as it doesn't reject system font
1173   // names), and use this to create a Core Text font that we can query for the
1174   // family name.
1175   // Eventually we should move to using CTFontUIFontType constants to identify
1176   // system fonts, and eliminate the need to instantiate them (indirectly) from
1177   // their postscript names.
1178   AutoCFRelease<CGFontRef> cgFont = CGFontCreateWithFontName(CFStringRef(psName));
1179   if (!cgFont) {
1180     return [aFont familyName];
1181   }
1183   AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr);
1184   if (!ctFont) {
1185     return [aFont familyName];
1186   }
1187   NSString* familyName = (NSString*)CTFontCopyFamilyName(ctFont);
1189   return [familyName autorelease];
1192 // Create a gfxFontFamily that corresponds to the "system" font name,
1193 // and populate it with the given font face. We only use this on Catalina or later,
1194 // so we expect the system font to be a variable-weight face rather than requiring
1195 // a number of discrete faces of different weights.
1196 static gfxFontFamily* CreateFamilyForSystemFont(NSFont* aFont, const nsACString& aFamilyName) {
1197   gfxFontFamily* familyEntry = new gfxFontFamily(aFamilyName, FontVisibility::Unknown);
1199   NSString* psNameNS = [[aFont fontDescriptor] postscriptName];
1200   nsAutoString nameUTF16;
1201   nsAutoCString psName;
1202   nsCocoaUtils::GetStringForNSString(psNameNS, nameUTF16);
1203   CopyUTF16toUTF8(nameUTF16, psName);
1205   MacOSFontEntry* fe = new MacOSFontEntry(psName, WeightRange(FontWeight::Normal()), true, 0.0);
1206   MOZ_ASSERT(gfxPlatform::GetPlatform()->HasVariationFontSupport());
1207   fe->SetupVariationRanges();
1209   familyEntry->AddFontEntry(fe);
1210   familyEntry->SetHasStyles(true);
1212   return familyEntry;
1215 // System fonts under OSX 10.11 use a combination of two families, one
1216 // for text sizes and another for larger, display sizes. Each has a
1217 // different number of weights. There aren't efficient API's for looking
1218 // this information up, so hard code the logic here but confirm via
1219 // debug assertions that the logic is correct.
1221 const CGFloat kTextDisplayCrossover = 20.0;  // use text family below this size
1223 void gfxMacPlatformFontList::InitSystemFontNames() {
1224   // On Catalina+, the system font uses optical sizing rather than individual
1225   // faces, so we don't need to look for a separate display-sized face.
1226   mUseSizeSensitiveSystemFont = !nsCocoaFeatures::OnCatalinaOrLater();
1228   // text font family
1229   NSFont* sys = [NSFont systemFontOfSize:0.0];
1230   NSString* textFamilyName = GetRealFamilyName(sys);
1231   nsAutoString familyName;
1232   nsCocoaUtils::GetStringForNSString(textFamilyName, familyName);
1233   CopyUTF16toUTF8(familyName, mSystemTextFontFamilyName);
1235   // On Catalina or later, we store an in-process gfxFontFamily for the system font
1236   // even if using the shared fontlist to manage "normal" fonts, because the hidden
1237   // system fonts may be excluded from the font list altogether.
1238   if (nsCocoaFeatures::OnCatalinaOrLater()) {
1239     RefPtr<gfxFontFamily> fam = CreateFamilyForSystemFont(sys, mSystemTextFontFamilyName);
1240     if (fam) {
1241       nsAutoCString key;
1242       GenerateFontListKey(mSystemTextFontFamilyName, key);
1243       mFontFamilies.InsertOrUpdate(key, std::move(fam));
1244     }
1245   }
1247   // display font family, if on OSX 10.11 - 10.14
1248   if (mUseSizeSensitiveSystemFont) {
1249     NSFont* displaySys = [NSFont systemFontOfSize:128.0];
1250     NSString* displayFamilyName = GetRealFamilyName(displaySys);
1251     if ([displayFamilyName isEqualToString:textFamilyName]) {
1252       mUseSizeSensitiveSystemFont = false;
1253     } else {
1254       nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName);
1255       CopyUTF16toUTF8(familyName, mSystemDisplayFontFamilyName);
1256     }
1257   }
1259 #ifdef DEBUG
1260   // different system font API's always map to the same family under OSX, so
1261   // just assume that and emit a warning if that ever changes
1262   NSString* sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]);
1263   if ([sysFamily compare:GetRealFamilyName([NSFont boldSystemFontOfSize:0.0])] != NSOrderedSame ||
1264       [sysFamily compare:GetRealFamilyName([NSFont controlContentFontOfSize:0.0])] !=
1265           NSOrderedSame ||
1266       [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] != NSOrderedSame ||
1267       [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] != NSOrderedSame) {
1268     NS_WARNING("system font types map to different font families"
1269                " -- please log a bug!!");
1270   }
1271 #endif
1274 gfxFontFamily* gfxMacPlatformFontList::FindSystemFontFamily(const nsACString& aFamily) {
1275   nsAutoCString key;
1276   GenerateFontListKey(aFamily, key);
1278   gfxFontFamily* familyEntry;
1279   if ((familyEntry = mFontFamilies.GetWeak(key))) {
1280     return CheckFamily(familyEntry);
1281   }
1283   return nullptr;
1286 void gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(
1287     CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object,
1288     CFDictionaryRef userInfo) {
1289   if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
1290     return;
1291   }
1293   gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
1294   if (!fl->IsInitialized()) {
1295     return;
1296   }
1298   // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
1299   fl->UpdateFontList();
1301   gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes);
1302   dom::ContentParent::NotifyUpdatedFonts(true);
1305 gfxFontEntry* gfxMacPlatformFontList::PlatformGlobalFontFallback(nsPresContext* aPresContext,
1306                                                                  const uint32_t aCh,
1307                                                                  Script aRunScript,
1308                                                                  const gfxFontStyle* aMatchStyle,
1309                                                                  FontFamily& aMatchedFamily) {
1310   CFStringRef str;
1311   UniChar ch[2];
1312   CFIndex length = 1;
1314   if (IS_IN_BMP(aCh)) {
1315     ch[0] = aCh;
1316     str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1, kCFAllocatorNull);
1317   } else {
1318     ch[0] = H_SURROGATE(aCh);
1319     ch[1] = L_SURROGATE(aCh);
1320     str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2, kCFAllocatorNull);
1321     if (!str) {
1322       return nullptr;
1323     }
1324     length = 2;
1325   }
1327   // use CoreText to find the fallback family
1329   gfxFontEntry* fontEntry = nullptr;
1330   bool cantUseFallbackFont = false;
1332   if (!mDefaultFont) {
1333     mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, NULL);
1334   }
1336   AutoCFRelease<CTFontRef> fallback =
1337       ::CTFontCreateForString(mDefaultFont, str, ::CFRangeMake(0, length));
1339   if (fallback) {
1340     AutoCFRelease<CFStringRef> familyNameRef = ::CTFontCopyFamilyName(fallback);
1342     if (familyNameRef &&
1343         ::CFStringCompare(familyNameRef, CFSTR("LastResort"), kCFCompareCaseInsensitive) !=
1344             kCFCompareEqualTo &&
1345         ::CFStringCompare(familyNameRef, CFSTR(".LastResort"), kCFCompareCaseInsensitive) !=
1346             kCFCompareEqualTo) {
1347       AutoTArray<UniChar, 1024> buffer;
1348       CFIndex familyNameLen = ::CFStringGetLength(familyNameRef);
1349       buffer.SetLength(familyNameLen + 1);
1350       ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen), buffer.Elements());
1351       buffer[familyNameLen] = 0;
1352       NS_ConvertUTF16toUTF8 familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()),
1353                                              familyNameLen);
1355       if (SharedFontList()) {
1356         fontlist::Family* family = FindSharedFamily(aPresContext, familyNameString);
1357         if (family) {
1358           fontlist::Face* face = family->FindFaceForStyle(SharedFontList(), *aMatchStyle);
1359           if (face) {
1360             fontEntry = GetOrCreateFontEntry(face, family);
1361           }
1362           if (fontEntry) {
1363             if (fontEntry->HasCharacter(aCh)) {
1364               aMatchedFamily = FontFamily(family);
1365             } else {
1366               fontEntry = nullptr;
1367               cantUseFallbackFont = true;
1368             }
1369           }
1370         }
1371       }
1373       // The macOS system font does not appear in the shared font list, so if
1374       // we didn't find the fallback font above, we should also check for an
1375       // unshared fontFamily in the system list.
1376       if (!fontEntry) {
1377         gfxFontFamily* family = FindSystemFontFamily(familyNameString);
1378         if (family) {
1379           fontEntry = family->FindFontForStyle(*aMatchStyle);
1380           if (fontEntry) {
1381             if (fontEntry->HasCharacter(aCh)) {
1382               aMatchedFamily = FontFamily(family);
1383             } else {
1384               fontEntry = nullptr;
1385               cantUseFallbackFont = true;
1386             }
1387           }
1388         }
1389       }
1390     }
1391   }
1393   if (cantUseFallbackFont) {
1394     Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
1395   }
1397   ::CFRelease(str);
1399   return fontEntry;
1402 FontFamily gfxMacPlatformFontList::GetDefaultFontForPlatform(nsPresContext* aPresContext,
1403                                                              const gfxFontStyle* aStyle,
1404                                                              nsAtom* aLanguage) {
1405   nsAutoreleasePool localPool;
1407   NSString* defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
1408   nsAutoString familyName;
1410   GetStringForNSString(defaultFamily, familyName);
1411   return FindFamily(aPresContext, NS_ConvertUTF16toUTF8(familyName));
1414 int32_t gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight) {
1415   if (aAppleWeight < 1)
1416     aAppleWeight = 1;
1417   else if (aAppleWeight > kAppleMaxWeight)
1418     aAppleWeight = kAppleMaxWeight;
1419   return gAppleWeightToCSSWeight[aAppleWeight];
1422 gfxFontEntry* gfxMacPlatformFontList::LookupLocalFont(nsPresContext* aPresContext,
1423                                                       const nsACString& aFontName,
1424                                                       WeightRange aWeightForEntry,
1425                                                       StretchRange aStretchForEntry,
1426                                                       SlantStyleRange aStyleForEntry) {
1427   if (aFontName.IsEmpty() || aFontName[0] == '.') {
1428     return nullptr;
1429   }
1431   nsAutoreleasePool localPool;
1433   NSString* faceName = GetNSStringForString(NS_ConvertUTF8toUTF16(aFontName));
1435   // lookup face based on postscript or full name
1436   AutoCFRelease<CGFontRef> fontRef = CGFontCreateWithFontName(CFStringRef(faceName));
1437   if (!fontRef) {
1438     return nullptr;
1439   }
1441   // It's possible for CGFontCreateWithFontName to return a font that has been
1442   // deactivated/uninstalled, or a font that is excluded from the font list due
1443   // to CSS font-visibility restriction. So we need to check whether this font is
1444   // allowed to be used.
1446   // CGFontRef doesn't offer a family-name API, so we go via a CTFontRef.
1447   AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(fontRef, 0.0, nullptr, nullptr);
1448   if (!ctFont) {
1449     return nullptr;
1450   }
1451   AutoCFRelease<CFStringRef> name = CTFontCopyFamilyName(ctFont);
1453   // Convert the family name to a key suitable for font-list lookup (8-bit, lowercased).
1454   nsAutoCString key;
1455   // CFStringGetLength is in UTF-16 code units. The maximum this count can expand
1456   // when converted to UTF-8 is 3x. We add 1 to ensure there will also be space for
1457   // null-termination of the resulting C string.
1458   key.SetLength((CFStringGetLength(name) + 1) * 3);
1459   if (!CFStringGetCString(name, key.BeginWriting(), key.Length(), kCFStringEncodingUTF8)) {
1460     // This shouldn't ever happen, but if it does we just bail.
1461     NS_WARNING("Failed to get family name?");
1462     key.Truncate(0);
1463   }
1464   if (key.IsEmpty()) {
1465     return nullptr;
1466   }
1467   // Reset our string length to match the actual C string we got, which will usually
1468   // be much shorter than the maximal buffer we allocated.
1469   key.Truncate(strlen(key.get()));
1470   ToLowerCase(key);
1471   // If the family can't be looked up, this font is not available for use.
1472   FontFamily family = FindFamily(aPresContext, key);
1473   if (family.IsNull()) {
1474     return nullptr;
1475   }
1477   return new MacOSFontEntry(aFontName, fontRef, aWeightForEntry, aStretchForEntry, aStyleForEntry,
1478                             false, true);
1481 static void ReleaseData(void* info, const void* data, size_t size) { free((void*)data); }
1483 gfxFontEntry* gfxMacPlatformFontList::MakePlatformFont(const nsACString& aFontName,
1484                                                        WeightRange aWeightForEntry,
1485                                                        StretchRange aStretchForEntry,
1486                                                        SlantStyleRange aStyleForEntry,
1487                                                        const uint8_t* aFontData, uint32_t aLength) {
1488   NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
1490   // create the font entry
1491   nsAutoString uniqueName;
1493   nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
1494   if (NS_FAILED(rv)) {
1495     return nullptr;
1496   }
1498   AutoCFRelease<CGDataProviderRef> provider =
1499       ::CGDataProviderCreateWithData(nullptr, aFontData, aLength, &ReleaseData);
1500   AutoCFRelease<CGFontRef> fontRef = ::CGFontCreateWithDataProvider(provider);
1501   if (!fontRef) {
1502     return nullptr;
1503   }
1505   auto newFontEntry =
1506       MakeUnique<MacOSFontEntry>(NS_ConvertUTF16toUTF8(uniqueName), fontRef, aWeightForEntry,
1507                                  aStretchForEntry, aStyleForEntry, true, false);
1508   return newFontEntry.release();
1511 // Webkit code uses a system font meta name, so mimic that here
1512 // WebCore/platform/graphics/mac/FontCacheMac.mm
1513 static const char kSystemFont_system[] = "-apple-system";
1515 bool gfxMacPlatformFontList::FindAndAddFamilies(nsPresContext* aPresContext,
1516                                                 StyleGenericFontFamily aGeneric,
1517                                                 const nsACString& aFamily,
1518                                                 nsTArray<FamilyAndGeneric>* aOutput,
1519                                                 FindFamiliesFlags aFlags, gfxFontStyle* aStyle,
1520                                                 nsAtom* aLanguage, gfxFloat aDevToCssSize) {
1521   if (aFamily.EqualsLiteral(kSystemFont_system)) {
1522     // Search for special system font name, -apple-system. This is not done via
1523     // the shared fontlist on Catalina or later, because the hidden system font
1524     // may not be included there; we create a separate gfxFontFamily to manage
1525     // this family.
1526     const nsCString& systemFontFamilyName =
1527         mUseSizeSensitiveSystemFont && aStyle &&
1528                 (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover
1529             ? mSystemDisplayFontFamilyName
1530             : mSystemTextFontFamilyName;
1531     if (SharedFontList() && !nsCocoaFeatures::OnCatalinaOrLater()) {
1532       FindFamiliesFlags flags = aFlags | FindFamiliesFlags::eSearchHiddenFamilies;
1533       return gfxPlatformFontList::FindAndAddFamilies(aPresContext, aGeneric, systemFontFamilyName,
1534                                                      aOutput, flags, aStyle, aLanguage,
1535                                                      aDevToCssSize);
1536     } else {
1537       if (auto* fam = FindSystemFontFamily(systemFontFamilyName)) {
1538         aOutput->AppendElement(fam);
1539         return true;
1540       }
1541     }
1542     return false;
1543   }
1545   return gfxPlatformFontList::FindAndAddFamilies(aPresContext, aGeneric, aFamily, aOutput, aFlags,
1546                                                  aStyle, aLanguage, aDevToCssSize);
1549 void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
1550                                               nsACString& aSystemFontName,
1551                                               gfxFontStyle& aFontStyle) {
1552   // Provide a local pool because we may be called from stylo threads.
1553   nsAutoreleasePool localPool;
1555   // code moved here from widget/cocoa/nsLookAndFeel.mm
1556   NSFont* font = nullptr;
1557   char* systemFontName = nullptr;
1558   switch (aSystemFontID) {
1559     case LookAndFeel::FontID::MessageBox:
1560     case LookAndFeel::FontID::StatusBar:
1561     case LookAndFeel::FontID::MozList:
1562     case LookAndFeel::FontID::MozField:
1563     case LookAndFeel::FontID::MozButton:
1564       font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
1565       systemFontName = (char*)kSystemFont_system;
1566       break;
1568     case LookAndFeel::FontID::SmallCaption:
1569       font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
1570       systemFontName = (char*)kSystemFont_system;
1571       break;
1573     case LookAndFeel::FontID::Icon:  // used in urlbar; tried labelFont, but too small
1574     case LookAndFeel::FontID::MozWorkspace:
1575     case LookAndFeel::FontID::MozDesktop:
1576     case LookAndFeel::FontID::MozInfo:
1577       font = [NSFont controlContentFontOfSize:0.0];
1578       systemFontName = (char*)kSystemFont_system;
1579       break;
1581     case LookAndFeel::FontID::MozPullDownMenu:
1582       font = [NSFont menuBarFontOfSize:0.0];
1583       systemFontName = (char*)kSystemFont_system;
1584       break;
1586     case LookAndFeel::FontID::Caption:
1587     case LookAndFeel::FontID::Menu:
1588     case LookAndFeel::FontID::MozDialog:
1589     default:
1590       font = [NSFont systemFontOfSize:0.0];
1591       systemFontName = (char*)kSystemFont_system;
1592       break;
1593   }
1594   NS_ASSERTION(font, "system font not set");
1595   NS_ASSERTION(systemFontName, "system font name not set");
1597   if (systemFontName) {
1598     aSystemFontName.AssignASCII(systemFontName);
1599   }
1601   NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits];
1602   aFontStyle.style =
1603       (traits & NSFontItalicTrait) ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
1604   aFontStyle.weight = (traits & NSFontBoldTrait) ? FontWeight::Bold() : FontWeight::Normal();
1605   aFontStyle.stretch = (traits & NSFontExpandedTrait)    ? FontStretch::Expanded()
1606                        : (traits & NSFontCondensedTrait) ? FontStretch::Condensed()
1607                                                          : FontStretch::Normal();
1608   aFontStyle.size = [font pointSize];
1609   aFontStyle.systemFont = true;
1612 // used to load system-wide font info on off-main thread
1613 class MacFontInfo final : public FontInfoData {
1614  public:
1615   MacFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps)
1616       : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) {}
1618   virtual ~MacFontInfo() = default;
1620   virtual void Load() {
1621     nsAutoreleasePool localPool;
1622     FontInfoData::Load();
1623   }
1625   // loads font data for all members of a given family
1626   virtual void LoadFontFamilyData(const nsACString& aFamilyName);
1629 void MacFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
1630   CrashReporter::AutoAnnotateCrashReport autoFontName(CrashReporter::Annotation::FontName,
1631                                                       aFamilyName);
1633   // family name ==> CTFontDescriptor
1634   NSString* famName = GetNSStringForString(NS_ConvertUTF8toUTF16(aFamilyName));
1635   CFStringRef family = CFStringRef(famName);
1637   AutoCFRelease<CFMutableDictionaryRef> attr = CFDictionaryCreateMutable(
1638       NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1639   CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
1640   AutoCFRelease<CTFontDescriptorRef> fd = CTFontDescriptorCreateWithAttributes(attr);
1641   AutoCFRelease<CFArrayRef> matchingFonts = CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
1642   if (!matchingFonts) {
1643     return;
1644   }
1646   nsTArray<nsCString> otherFamilyNames;
1647   bool hasOtherFamilyNames = true;
1649   // iterate over faces in the family
1650   int f, numFaces = (int)CFArrayGetCount(matchingFonts);
1651   for (f = 0; f < numFaces; f++) {
1652     mLoadStats.fonts++;
1654     CTFontDescriptorRef faceDesc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
1655     if (!faceDesc) {
1656       continue;
1657     }
1658     AutoCFRelease<CTFontRef> fontRef = CTFontCreateWithFontDescriptor(faceDesc, 0.0, nullptr);
1659     if (!fontRef) {
1660       NS_WARNING("failed to create a CTFontRef");
1661       continue;
1662     }
1664     if (mLoadCmaps) {
1665       // face name
1666       AutoCFRelease<CFStringRef> faceName =
1667           (CFStringRef)CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
1669       AutoTArray<UniChar, 1024> buffer;
1670       CFIndex len = CFStringGetLength(faceName);
1671       buffer.SetLength(len + 1);
1672       CFStringGetCharacters(faceName, ::CFRangeMake(0, len), buffer.Elements());
1673       buffer[len] = 0;
1674       NS_ConvertUTF16toUTF8 fontName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
1676       // load the cmap data
1677       FontFaceData fontData;
1678       AutoCFRelease<CFDataRef> cmapTable =
1679           CTFontCopyTable(fontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions);
1681       if (cmapTable) {
1682         const uint8_t* cmapData = (const uint8_t*)CFDataGetBytePtr(cmapTable);
1683         uint32_t cmapLen = CFDataGetLength(cmapTable);
1684         RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1685         uint32_t offset;
1686         nsresult rv;
1688         rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset);
1689         if (NS_SUCCEEDED(rv)) {
1690           fontData.mCharacterMap = charmap;
1691           fontData.mUVSOffset = offset;
1692           mLoadStats.cmaps++;
1693         }
1694       }
1696       mFontFaceData.InsertOrUpdate(fontName, fontData);
1697     }
1699     if (mLoadOtherNames && hasOtherFamilyNames) {
1700       AutoCFRelease<CFDataRef> nameTable =
1701           CTFontCopyTable(fontRef, kCTFontTableName, kCTFontTableOptionNoOptions);
1703       if (nameTable) {
1704         const char* nameData = (const char*)CFDataGetBytePtr(nameTable);
1705         uint32_t nameLen = CFDataGetLength(nameTable);
1706         gfxFontUtils::ReadOtherFamilyNamesForFace(aFamilyName, nameData, nameLen, otherFamilyNames,
1707                                                   false);
1708         hasOtherFamilyNames = otherFamilyNames.Length() != 0;
1709       }
1710     }
1711   }
1713   // if found other names, insert them in the hash table
1714   if (otherFamilyNames.Length() != 0) {
1715     mOtherFamilyNames.InsertOrUpdate(aFamilyName, otherFamilyNames);
1716     mLoadStats.othernames += otherFamilyNames.Length();
1717   }
1720 already_AddRefed<FontInfoData> gfxMacPlatformFontList::CreateFontInfoData() {
1721   bool loadCmaps =
1722       !UsesSystemFallback() || gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1724   RefPtr<MacFontInfo> fi = new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1725   return fi.forget();
1728 gfxFontFamily* gfxMacPlatformFontList::CreateFontFamily(const nsACString& aName,
1729                                                         FontVisibility aVisibility) const {
1730   return new gfxMacFontFamily(aName, aVisibility, 0.0);
1733 gfxFontEntry* gfxMacPlatformFontList::CreateFontEntry(fontlist::Face* aFace,
1734                                                       const fontlist::Family* aFamily) {
1735   MacOSFontEntry* fe =
1736       new MacOSFontEntry(aFace->mDescriptor.AsString(SharedFontList()), aFace->mWeight, false,
1737                          0.0);  // XXX standardFace, sizeHint
1738   fe->InitializeFrom(aFace, aFamily);
1739   return fe;
1742 void gfxMacPlatformFontList::GetFacesInitDataForFamily(const fontlist::Family* aFamily,
1743                                                        nsTArray<fontlist::Face::InitData>& aFaces,
1744                                                        bool aLoadCmaps) const {
1745   nsAutoreleasePool localPool;
1747   NS_ConvertUTF8toUTF16 name(aFamily->Key().AsString(SharedFontList()));
1748   NSString* family = GetNSStringForString(name);
1750   // returns an array of [psname, style name, weight, traits] elements, goofy api
1751   NSArray* fontfaces = [sFontManager availableMembersOfFontFamily:family];
1752   int faceCount = [fontfaces count];
1753   for (int faceIndex = 0; faceIndex < faceCount; faceIndex++) {
1754     NSArray* face = [fontfaces objectAtIndex:faceIndex];
1755     NSString* psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
1756     int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
1757     uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
1758     NSString* facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
1760     if (appKitWeight == kAppleExtraLightWeight) {
1761       // if the facename contains UltraLight, set the weight to the ultralight weight value
1762       NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
1763       if (range.location != NSNotFound) {
1764         appKitWeight = kAppleUltraLightWeight;
1765       }
1766     }
1768     // make a nsString
1769     nsAutoString postscriptFontName;
1770     GetStringForNSString(psname, postscriptFontName);
1772     int32_t cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
1773     if (PR_GetCurrentThread() != sInitFontListThread) {
1774       int32_t weightOverride = GetWeightOverride(postscriptFontName);
1775       if (weightOverride) {
1776         // scale down and clamp, to get a value from 1..9
1777         cssWeight = ((weightOverride + 50) / 100);
1778         cssWeight = std::max(1, std::min(cssWeight, 9));
1779       }
1780     }
1781     cssWeight *= 100;  // scale up to CSS values
1783     StretchRange stretch(FontStretch::Normal());
1784     if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
1785       stretch = StretchRange(FontStretch::Condensed());
1786     } else if (macTraits & NSExpandedFontMask) {
1787       stretch = StretchRange(FontStretch::Expanded());
1788     }
1789     // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
1790     // at least (see bug 611855), so check for style name endings as well
1791     SlantStyleRange slantStyle(FontSlantStyle::Normal());
1792     if ((macTraits & NSItalicFontMask) || [facename hasSuffix:@"Italic"] ||
1793         [facename hasSuffix:@"Oblique"]) {
1794       slantStyle = SlantStyleRange(FontSlantStyle::Italic());
1795     }
1797     bool fixedPitch = (macTraits & NSFixedPitchFontMask) ? true : false;
1799     RefPtr<gfxCharacterMap> charmap;
1800     if (aLoadCmaps) {
1801       AutoCFRelease<CGFontRef> font = CGFontCreateWithFontName(CFStringRef(psname));
1802       if (font) {
1803         uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
1804         AutoCFRelease<CFDataRef> data = CGFontCopyTableForTag(font, kCMAP);
1805         if (data) {
1806           uint32_t offset;
1807           charmap = new gfxCharacterMap();
1808           gfxFontUtils::ReadCMAP(CFDataGetBytePtr(data), CFDataGetLength(data), *charmap, offset);
1809         }
1810       }
1811     }
1813     // Ensure that a face named "Regular" goes to the front of the list, so it
1814     // will take precedence over other faces with the same style attributes but
1815     // a different name (such as "Outline").
1816     auto data = fontlist::Face::InitData{
1817         NS_ConvertUTF16toUTF8(postscriptFontName),
1818         0,
1819         fixedPitch,
1820         WeightRange(FontWeight(cssWeight)),
1821         stretch,
1822         slantStyle,
1823         charmap,
1824     };
1825     if ([facename caseInsensitiveCompare:@"Regular"] == NSOrderedSame) {
1826       aFaces.InsertElementAt(0, std::move(data));
1827     } else {
1828       aFaces.AppendElement(std::move(data));
1829     }
1830   }
1833 void gfxMacPlatformFontList::ReadFaceNamesForFamily(fontlist::Family* aFamily,
1834                                                     bool aNeedFullnamePostscriptNames) {
1835   if (!aFamily->IsInitialized()) {
1836     if (!InitializeFamily(aFamily)) {
1837       return;
1838     }
1839   }
1840   const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
1841   fontlist::FontList* list = SharedFontList();
1842   nsAutoCString canonicalName(aFamily->DisplayName().AsString(list));
1843   const fontlist::Pointer* facePtrs = aFamily->Faces(list);
1844   for (uint32_t i = 0, n = aFamily->NumFaces(); i < n; i++) {
1845     auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
1846     if (!face) {
1847       continue;
1848     }
1849     nsAutoCString name(face->mDescriptor.AsString(list));
1850     // We create a temporary MacOSFontEntry just to read family names from the
1851     // 'name' table in the font resource. The style attributes here are ignored
1852     // as this entry is not used for font style matching.
1853     // The size hint might be used to select which face is accessed in the case
1854     // of the macOS UI font; see MacOSFontEntry::GetFontRef(). We pass 16.0 in
1855     // order to get a standard text-size face in this case, although it's
1856     // unlikely to matter for the purpose of just reading family names.
1857     auto fe = MakeUnique<MacOSFontEntry>(name, WeightRange(FontWeight::Normal()), false, 16.0);
1858     if (!fe) {
1859       continue;
1860     }
1861     gfxFontEntry::AutoTable nameTable(fe.get(), kNAME);
1862     if (!nameTable) {
1863       continue;
1864     }
1865     uint32_t dataLength;
1866     const char* nameData = hb_blob_get_data(nameTable, &dataLength);
1867     AutoTArray<nsCString, 4> otherFamilyNames;
1868     gfxFontUtils::ReadOtherFamilyNamesForFace(canonicalName, nameData, dataLength, otherFamilyNames,
1869                                               false);
1870     for (const auto& alias : otherFamilyNames) {
1871       nsAutoCString key;
1872       GenerateFontListKey(alias, key);
1873       auto aliasData = mAliasTable.GetOrInsertNew(key);
1874       aliasData->InitFromFamily(aFamily, canonicalName);
1875       aliasData->mFaces.AppendElement(facePtrs[i]);
1876     }
1877   }