No bug - tagging 2af34b4c9adf8c8defd3251b569af9c38cc0a429 with FIREFOX_BETA_124_BASE...
[gecko.git] / gfx / thebes / gfxMacPlatformFontList.mm
blobe36c9c8a254905d225484562456b99ca52412785
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
8 #include <algorithm>
10 #import <AppKit/AppKit.h>
12 #include "gfxFontConstants.h"
13 #include "gfxPlatformMac.h"
14 #include "gfxMacPlatformFontList.h"
15 #include "gfxMacFont.h"
16 #include "gfxUserFontSet.h"
17 #include "SharedFontList-impl.h"
19 #include "harfbuzz/hb.h"
21 #include "AppleUtils.h"
22 #include "MainThreadUtils.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 #include "nsIDirectoryEnumerator.h"
27 #include "nsCharTraits.h"
28 #include "nsCocoaUtils.h"
29 #include "nsComponentManagerUtils.h"
30 #include "nsServiceManagerUtils.h"
31 #include "nsTArray.h"
33 #include "mozilla/dom/ContentChild.h"
34 #include "mozilla/dom/ContentParent.h"
35 #include "mozilla/FontPropertyTypes.h"
36 #include "mozilla/MemoryReporting.h"
37 #include "mozilla/Preferences.h"
38 #include "mozilla/ProfilerLabels.h"
39 #include "mozilla/Sprintf.h"
40 #include "mozilla/StaticPrefs_gfx.h"
41 #include "mozilla/Telemetry.h"
42 #include "mozilla/gfx/2D.h"
44 #include <unistd.h>
45 #include <time.h>
46 #include <dlfcn.h>
48 #include "StandardFonts-macos.inc"
50 using namespace mozilla;
51 using namespace mozilla::gfx;
53 // cache Cocoa's "shared font manager" for performance
54 static NSFontManager* sFontManager;
56 static void GetStringForNSString(const NSString* aSrc, nsAString& aDest) {
57   aDest.SetLength(aSrc.length);
58   [aSrc getCharacters:reinterpret_cast<unichar*>(aDest.BeginWriting())
59                 range:NSMakeRange(0, aSrc.length)];
62 static NSString* GetNSStringForString(const nsAString& aSrc) {
63   return [NSString
64       stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
65                     length:aSrc.Length()];
68 #define LOG_FONTLIST(args) \
69   MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args)
70 #define LOG_FONTLIST_ENABLED() \
71   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug)
72 #define LOG_CMAPDATA_ENABLED() \
73   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug)
75 class gfxMacFontFamily final : public CTFontFamily {
76  public:
77   gfxMacFontFamily(const nsACString& aName, NSFont* aSystemFont)
78       : CTFontFamily(aName, FontVisibility::Unknown),
79         mForSystemFont(aSystemFont) {
80     // I don't think the system font instance is at much risk of being deleted,
81     // but to be on the safe side let's retain a reference until we're finished
82     // using it for lazy initialization.
83     [mForSystemFont retain];
84   }
86   void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
87       MOZ_REQUIRES(mLock) override;
89  protected:
90   // If non-null, this is a family representing the system UI font, and should
91   // use the given NSFont as the basis for initialization as the normal
92   // font-manager APIs based on family name won't handle it.
93   NSFont* mForSystemFont = nullptr;
96 void gfxMacFontFamily::FindStyleVariationsLocked(FontInfoData* aFontInfoData) {
97   if (mHasStyles) {
98     return;
99   }
101   AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxMacFontFamily::FindStyleVariations",
102                                         LAYOUT, mName);
104   nsAutoreleasePool localPool;
106   if (mForSystemFont) {
107     MOZ_ASSERT(gfxPlatform::HasVariationFontSupport());
109     auto addToFamily = [&](NSFont* aNSFont) MOZ_REQUIRES(mLock) {
110       NSString* psNameNS = aNSFont.fontDescriptor.postscriptName;
111       nsAutoString nameUTF16;
112       nsAutoCString psName;
113       nsCocoaUtils::GetStringForNSString(psNameNS, nameUTF16);
114       CopyUTF16toUTF8(nameUTF16, psName);
116       auto* fe =
117           new CTFontEntry(psName, WeightRange(FontWeight::NORMAL), true, 0.0);
119       // Set the appropriate style, assuming it may not have a variation range.
120       fe->mStyleRange = SlantStyleRange(
121           (aNSFont.fontDescriptor.symbolicTraits & NSFontItalicTrait)
122               ? FontSlantStyle::ITALIC
123               : FontSlantStyle::NORMAL);
125       // Set up weight (and width, if present) ranges.
126       fe->SetupVariationRanges();
127       AddFontEntryLocked(fe);
128     };
130     addToFamily(mForSystemFont);
132     // See if there is a corresponding italic face, and add it to the family.
133     NSFont* italicFont = [sFontManager convertFont:mForSystemFont
134                                        toHaveTrait:NSItalicFontMask];
135     if (italicFont != mForSystemFont) {
136       addToFamily(italicFont);
137     }
139     [mForSystemFont release];
140     mForSystemFont = nullptr;
141     SetHasStyles(true);
143     return;
144   }
146   CTFontFamily::FindStyleVariationsLocked(aFontInfoData);
149 /* gfxSingleFaceMacFontFamily */
151 class gfxSingleFaceMacFontFamily final : public gfxFontFamily {
152  public:
153   gfxSingleFaceMacFontFamily(const nsACString& aName,
154                              FontVisibility aVisibility)
155       : gfxFontFamily(aName, aVisibility) {
156     mFaceNamesInitialized = true;  // omit from face name lists
157   }
159   virtual ~gfxSingleFaceMacFontFamily() = default;
161   void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
162       MOZ_REQUIRES(mLock) override{};
164   void LocalizedName(nsACString& aLocalizedName) override;
166   void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) override;
168   bool IsSingleFaceFamily() const override { return true; }
171 void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
172   nsAutoreleasePool localPool;
174   AutoReadLock lock(mLock);
176   if (!HasOtherFamilyNames()) {
177     aLocalizedName = mName;
178     return;
179   }
181   gfxFontEntry* fe = mAvailableFonts[0];
182   NSFont* font = [NSFont
183       fontWithName:GetNSStringForString(NS_ConvertUTF8toUTF16(fe->Name()))
184               size:0.0];
185   if (font) {
186     NSString* localized = [font displayName];
187     if (localized) {
188       nsAutoString locName;
189       GetStringForNSString(localized, locName);
190       CopyUTF16toUTF8(locName, aLocalizedName);
191       return;
192     }
193   }
195   // failed to get localized name, just use the canonical one
196   aLocalizedName = mName;
199 void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(
200     gfxPlatformFontList* aPlatformFontList) {
201   AutoWriteLock lock(mLock);
202   if (mOtherFamilyNamesInitialized) {
203     return;
204   }
206   gfxFontEntry* fe = mAvailableFonts[0];
207   if (!fe) {
208     return;
209   }
211   const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
213   gfxFontEntry::AutoTable nameTable(fe, kNAME);
214   if (!nameTable) {
215     return;
216   }
218   mHasOtherFamilyNames =
219       ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true);
221   mOtherFamilyNamesInitialized = true;
224 /* gfxMacPlatformFontList */
225 #pragma mark -
227 gfxMacPlatformFontList::gfxMacPlatformFontList() : CoreTextFontList() {
228   CheckFamilyList(kBaseFonts);
230   // cache this in a static variable so that gfxMacFontFamily objects
231   // don't have to repeatedly look it up
232   sFontManager = [NSFontManager sharedFontManager];
234   // Load the font-list preferences now, so that we don't have to do it from
235   // Init[Shared]FontListForPlatform, which may be called off-main-thread.
236   gfxFontUtils::GetPrefsFontList("font.single-face-list", mSingleFaceFonts);
239 FontVisibility gfxMacPlatformFontList::GetVisibilityForFamily(
240     const nsACString& aName) const {
241   if (aName[0] == '.' || aName.LowerCaseEqualsLiteral("lastresort")) {
242     return FontVisibility::Hidden;
243   }
244   if (FamilyInList(aName, kBaseFonts)) {
245     return FontVisibility::Base;
246   }
247 #ifdef MOZ_BUNDLED_FONTS
248   if (mBundledFamilies.Contains(aName)) {
249     return FontVisibility::Base;
250   }
251 #endif
252   return FontVisibility::User;
255 bool gfxMacPlatformFontList::DeprecatedFamilyIsAvailable(
256     const nsACString& aName) {
257   NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(aName));
258   return [[sFontManager availableMembersOfFontFamily:family] count] > 0;
261 void gfxMacPlatformFontList::InitAliasesForSingleFaceList() {
262   for (const auto& familyName : mSingleFaceFonts) {
263     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
264     // Each entry in the "single face families" list is expected to be a
265     // colon-separated pair of FaceName:Family,
266     // where FaceName is the individual face name (psname) of a font
267     // that should be exposed as a separate family name,
268     // and Family is the standard family to which that face belongs.
269     // The only such face listed by default is
270     //    Osaka-Mono:Osaka
271     auto colon = familyName.FindChar(':');
272     if (colon == kNotFound) {
273       continue;
274     }
276     // Look for the parent family in the main font family list,
277     // and ensure we have loaded its list of available faces.
278     nsAutoCString key;
279     GenerateFontListKey(Substring(familyName, colon + 1), key);
280     fontlist::Family* family = SharedFontList()->FindFamily(key);
281     if (!family) {
282       // The parent family is not present, so just ignore this entry.
283       continue;
284     }
285     if (!family->IsInitialized()) {
286       if (!gfxPlatformFontList::InitializeFamily(family)) {
287         // This shouldn't ever fail, but if it does, we can safely ignore it.
288         MOZ_ASSERT(false, "failed to initialize font family");
289         continue;
290       }
291     }
293     // Truncate the entry from prefs at the colon, so now it is just the
294     // desired single-face-family name.
295     nsAutoCString aliasName(Substring(familyName, 0, colon));
297     // Look through the family's faces to see if this one is present.
298     fontlist::FontList* list = SharedFontList();
299     const fontlist::Pointer* facePtrs = family->Faces(list);
300     for (size_t i = 0; i < family->NumFaces(); i++) {
301       if (facePtrs[i].IsNull()) {
302         continue;
303       }
304       auto* face = facePtrs[i].ToPtr<const fontlist::Face>(list);
305       if (face->mDescriptor.AsString(list).Equals(aliasName)) {
306         // Found it! Create an entry in the Alias table.
307         GenerateFontListKey(aliasName, key);
308         if (SharedFontList()->FindFamily(key) || mAliasTable.Get(key)) {
309           // If the family name is already known, something's misconfigured;
310           // just ignore it.
311           MOZ_ASSERT(false, "single-face family already known");
312           break;
313         }
314         auto aliasData = mAliasTable.GetOrInsertNew(key);
315         // The "alias" here isn't based on an existing family, so we don't call
316         // aliasData->InitFromFamily(); the various flags are left as defaults.
317         aliasData->mFaces.AppendElement(facePtrs[i]);
318         aliasData->mBaseFamily = aliasName;
319         aliasData->mVisibility = family->Visibility();
320         break;
321       }
322     }
323   }
324   if (!mAliasTable.IsEmpty()) {
325     // This will be updated when the font loader completes, but we require
326     // at least the Osaka-Mono alias to be available immediately.
327     SharedFontList()->SetAliases(mAliasTable);
328   }
331 void gfxMacPlatformFontList::InitSingleFaceList() {
332   for (const auto& familyName : mSingleFaceFonts) {
333     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
334     // Each entry in the "single face families" list is expected to be a
335     // colon-separated pair of FaceName:Family,
336     // where FaceName is the individual face name (psname) of a font
337     // that should be exposed as a separate family name,
338     // and Family is the standard family to which that face belongs.
339     // The only such face listed by default is
340     //    Osaka-Mono:Osaka
341     auto colon = familyName.FindChar(':');
342     if (colon == kNotFound) {
343       continue;
344     }
346     // Look for the parent family in the main font family list,
347     // and ensure we have loaded its list of available faces.
348     nsAutoCString key(Substring(familyName, colon + 1));
349     ToLowerCase(key);
350     gfxFontFamily* family = mFontFamilies.GetWeak(key);
351     if (!family || family->IsHidden()) {
352       continue;
353     }
354     family->FindStyleVariations();
356     // Truncate the entry from prefs at the colon, so now it is just the
357     // desired single-face-family name.
358     nsAutoCString aliasName(Substring(familyName, 0, colon));
360     // Look through the family's faces to see if this one is present.
361     const gfxFontEntry* fe = nullptr;
362     family->ReadLock();
363     for (const auto& face : family->GetFontList()) {
364       if (face->Name().Equals(aliasName)) {
365         fe = face;
366         break;
367       }
368     }
369     family->ReadUnlock();
370     if (!fe) {
371       continue;
372     }
374     // We found the correct face, so create the single-face family record.
375     GenerateFontListKey(aliasName, key);
376     LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
377                   aliasName.get(), key.get()));
379     // add only if doesn't exist already
380     if (!mFontFamilies.GetWeak(key)) {
381       RefPtr<gfxFontFamily> familyEntry =
382           new gfxSingleFaceMacFontFamily(aliasName, family->Visibility());
383       // We need a separate font entry, because its family name will
384       // differ from the one we found in the main list.
385       CTFontEntry* fontEntry =
386           new CTFontEntry(fe->Name(), fe->Weight(), true,
387                           static_cast<const CTFontEntry*>(fe)->mSizeHint);
388       familyEntry->AddFontEntry(fontEntry);
389       familyEntry->SetHasStyles(true);
390       mFontFamilies.InsertOrUpdate(key, std::move(familyEntry));
391       LOG_FONTLIST(("(fontlist-singleface) added new family: %s, key: %s\n",
392                     aliasName.get(), key.get()));
393     }
394   }
397 // System fonts under OSX may contain weird "meta" names but if we create
398 // a new font using just the Postscript name, the NSFont api returns an object
399 // with the actual real family name. For example, under OSX 10.11:
401 // [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont
402 // [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor]
403 // postscriptName]
404 //          size:8.0] familyName] ==> .SF NS Text
406 static NSString* GetRealFamilyName(NSFont* aFont) {
407   NSString* psName = [[aFont fontDescriptor] postscriptName];
408   // With newer macOS versions and SDKs (e.g. when compiled against SDK 10.15),
409   // [NSFont fontWithName:] fails for hidden system fonts, because the
410   // underlying Core Text functions it uses reject such names and tell us to use
411   // the special CTFontCreateUIFontForLanguage API instead. To work around this,
412   // as we don't yet work directly with the CTFontUIFontType identifiers, we
413   // create a Core Graphics font (as it doesn't reject system font names), and
414   // use this to create a Core Text font that we can query for the family name.
415   // Eventually we should move to using CTFontUIFontType constants to identify
416   // system fonts, and eliminate the need to instantiate them (indirectly) from
417   // their postscript names.
418   AutoCFRelease<CGFontRef> cgFont =
419       CGFontCreateWithFontName(CFStringRef(psName));
420   if (!cgFont) {
421     return [aFont familyName];
422   }
424   AutoCFRelease<CTFontRef> ctFont =
425       CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr);
426   if (!ctFont) {
427     return [aFont familyName];
428   }
429   NSString* familyName = (NSString*)CTFontCopyFamilyName(ctFont);
431   return [familyName autorelease];
434 void gfxMacPlatformFontList::InitSystemFontNames() {
435   // text font family
436   NSFont* sys = [NSFont systemFontOfSize:0.0];
437   NSString* textFamilyName = GetRealFamilyName(sys);
438   nsAutoString familyName;
439   nsCocoaUtils::GetStringForNSString(textFamilyName, familyName);
440   CopyUTF16toUTF8(familyName, mSystemFontFamilyName);
442   // We store an in-process gfxFontFamily for the system font even if using the
443   // shared fontlist to manage "normal" fonts, because the hidden system fonts
444   // may be excluded from the font list altogether. This family will be
445   // populated based on the given NSFont.
446   RefPtr<gfxFontFamily> fam = new gfxMacFontFamily(mSystemFontFamilyName, sys);
447   if (fam) {
448     nsAutoCString key;
449     GenerateFontListKey(mSystemFontFamilyName, key);
450     mFontFamilies.InsertOrUpdate(key, std::move(fam));
451   }
453 #ifdef DEBUG
454   // different system font API's always map to the same family under OSX, so
455   // just assume that and emit a warning if that ever changes
456   NSString* sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]);
457   if ([sysFamily compare:GetRealFamilyName([NSFont
458                              boldSystemFontOfSize:0.0])] != NSOrderedSame ||
459       [sysFamily compare:GetRealFamilyName([NSFont
460                              controlContentFontOfSize:0.0])] != NSOrderedSame ||
461       [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] !=
462           NSOrderedSame ||
463       [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] !=
464           NSOrderedSame) {
465     NS_WARNING("system font types map to different font families"
466                " -- please log a bug!!");
467   }
468 #endif
471 FontFamily gfxMacPlatformFontList::GetDefaultFontForPlatform(
472     nsPresContext* aPresContext, const gfxFontStyle* aStyle,
473     nsAtom* aLanguage) {
474   nsAutoreleasePool localPool;
476   NSString* defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
477   nsAutoString familyName;
479   GetStringForNSString(defaultFamily, familyName);
480   return FindFamily(aPresContext, NS_ConvertUTF16toUTF8(familyName));
483 void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
484                                               nsACString& aSystemFontName,
485                                               gfxFontStyle& aFontStyle) {
486   // Provide a local pool because we may be called from stylo threads.
487   nsAutoreleasePool localPool;
489   // code moved here from widget/cocoa/nsLookAndFeel.mm
490   NSFont* font = nullptr;
491   switch (aSystemFontID) {
492     case LookAndFeel::FontID::MessageBox:
493     case LookAndFeel::FontID::StatusBar:
494     case LookAndFeel::FontID::MozList:
495     case LookAndFeel::FontID::MozField:
496     case LookAndFeel::FontID::MozButton:
497       font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize];
498       break;
500     case LookAndFeel::FontID::SmallCaption:
501       font = [NSFont boldSystemFontOfSize:NSFont.smallSystemFontSize];
502       break;
504     case LookAndFeel::FontID::Icon:  // used in urlbar; tried labelFont, but too
505                                      // small
506       font = [NSFont controlContentFontOfSize:0.0];
507       break;
509     case LookAndFeel::FontID::MozPullDownMenu:
510       font = [NSFont menuBarFontOfSize:0.0];
511       break;
513     case LookAndFeel::FontID::Caption:
514     case LookAndFeel::FontID::Menu:
515     default:
516       font = [NSFont systemFontOfSize:0.0];
517       break;
518   }
519   NS_ASSERTION(font, "system font not set");
521   aSystemFontName.AssignASCII("-apple-system");
523   NSFontSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
524   aFontStyle.style = (traits & NSFontItalicTrait) ? FontSlantStyle::ITALIC
525                                                   : FontSlantStyle::NORMAL;
526   aFontStyle.weight =
527       (traits & NSFontBoldTrait) ? FontWeight::BOLD : FontWeight::NORMAL;
528   aFontStyle.stretch = (traits & NSFontExpandedTrait) ? FontStretch::EXPANDED
529                        : (traits & NSFontCondensedTrait)
530                            ? FontStretch::CONDENSED
531                            : FontStretch::NORMAL;
532   aFontStyle.size = font.pointSize;
533   aFontStyle.systemFont = true;