Merge autoland to mozilla-central. a=merge
[gecko.git] / gfx / thebes / gfxMacPlatformFontList.mm
blob6d2a6666df10dba76046926ce40ad09d7db29387
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 /* gfxSingleFaceMacFontFamily */
77 class gfxSingleFaceMacFontFamily final : public gfxFontFamily {
78  public:
79   gfxSingleFaceMacFontFamily(const nsACString& aName,
80                              FontVisibility aVisibility)
81       : gfxFontFamily(aName, aVisibility) {
82     mFaceNamesInitialized = true;  // omit from face name lists
83   }
85   virtual ~gfxSingleFaceMacFontFamily() = default;
87   void FindStyleVariationsLocked(FontInfoData* aFontInfoData = nullptr)
88       MOZ_REQUIRES(mLock) override {};
90   void LocalizedName(nsACString& aLocalizedName) override;
92   void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) override;
94   bool IsSingleFaceFamily() const override { return true; }
97 void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
98   nsAutoreleasePool localPool;
100   AutoReadLock lock(mLock);
102   if (!HasOtherFamilyNames()) {
103     aLocalizedName = mName;
104     return;
105   }
107   gfxFontEntry* fe = mAvailableFonts[0];
108   NSFont* font = [NSFont
109       fontWithName:GetNSStringForString(NS_ConvertUTF8toUTF16(fe->Name()))
110               size:0.0];
111   if (font) {
112     NSString* localized = [font displayName];
113     if (localized) {
114       nsAutoString locName;
115       GetStringForNSString(localized, locName);
116       CopyUTF16toUTF8(locName, aLocalizedName);
117       return;
118     }
119   }
121   // failed to get localized name, just use the canonical one
122   aLocalizedName = mName;
125 void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(
126     gfxPlatformFontList* aPlatformFontList) {
127   AutoWriteLock lock(mLock);
128   if (mOtherFamilyNamesInitialized) {
129     return;
130   }
132   gfxFontEntry* fe = mAvailableFonts[0];
133   if (!fe) {
134     return;
135   }
137   const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
139   gfxFontEntry::AutoTable nameTable(fe, kNAME);
140   if (!nameTable) {
141     return;
142   }
144   mHasOtherFamilyNames =
145       ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true);
147   mOtherFamilyNamesInitialized = true;
150 /* gfxMacPlatformFontList */
151 #pragma mark -
153 gfxMacPlatformFontList::gfxMacPlatformFontList() : CoreTextFontList() {
154   CheckFamilyList(kBaseFonts);
156   // cache this in a static variable so that gfxMacFontFamily objects
157   // don't have to repeatedly look it up
158   sFontManager = [NSFontManager sharedFontManager];
160   // Load the font-list preferences now, so that we don't have to do it from
161   // Init[Shared]FontListForPlatform, which may be called off-main-thread.
162   gfxFontUtils::GetPrefsFontList("font.single-face-list", mSingleFaceFonts);
165 FontVisibility gfxMacPlatformFontList::GetVisibilityForFamily(
166     const nsACString& aName) const {
167   if (aName[0] == '.' || aName.LowerCaseEqualsLiteral("lastresort")) {
168     return FontVisibility::Hidden;
169   }
170   if (FamilyInList(aName, kBaseFonts)) {
171     return FontVisibility::Base;
172   }
173 #ifdef MOZ_BUNDLED_FONTS
174   if (mBundledFamilies.Contains(aName)) {
175     return FontVisibility::Base;
176   }
177 #endif
178   return FontVisibility::User;
181 nsTArray<std::pair<const char**, uint32_t>>
182 gfxMacPlatformFontList::GetFilteredPlatformFontLists() {
183   nsTArray<std::pair<const char**, uint32_t>> fontLists;
185   fontLists.AppendElement(std::make_pair(kBaseFonts, ArrayLength(kBaseFonts)));
187   return fontLists;
190 bool gfxMacPlatformFontList::DeprecatedFamilyIsAvailable(
191     const nsACString& aName) {
192   NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(aName));
193   return [[sFontManager availableMembersOfFontFamily:family] count] > 0;
196 void gfxMacPlatformFontList::InitAliasesForSingleFaceList() {
197   for (const auto& familyName : mSingleFaceFonts) {
198     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
199     // Each entry in the "single face families" list is expected to be a
200     // colon-separated pair of FaceName:Family,
201     // where FaceName is the individual face name (psname) of a font
202     // that should be exposed as a separate family name,
203     // and Family is the standard family to which that face belongs.
204     // The only such face listed by default is
205     //    Osaka-Mono:Osaka
206     auto colon = familyName.FindChar(':');
207     if (colon == kNotFound) {
208       continue;
209     }
211     // Look for the parent family in the main font family list,
212     // and ensure we have loaded its list of available faces.
213     nsAutoCString key;
214     GenerateFontListKey(Substring(familyName, colon + 1), key);
215     fontlist::Family* family = SharedFontList()->FindFamily(key);
216     if (!family) {
217       // The parent family is not present, so just ignore this entry.
218       continue;
219     }
220     if (!family->IsInitialized()) {
221       if (!gfxPlatformFontList::InitializeFamily(family)) {
222         // This shouldn't ever fail, but if it does, we can safely ignore it.
223         MOZ_ASSERT(false, "failed to initialize font family");
224         continue;
225       }
226     }
228     // Truncate the entry from prefs at the colon, so now it is just the
229     // desired single-face-family name.
230     nsAutoCString aliasName(Substring(familyName, 0, colon));
232     // Look through the family's faces to see if this one is present.
233     fontlist::FontList* list = SharedFontList();
234     const fontlist::Pointer* facePtrs = family->Faces(list);
235     for (size_t i = 0; i < family->NumFaces(); i++) {
236       if (facePtrs[i].IsNull()) {
237         continue;
238       }
239       auto* face = facePtrs[i].ToPtr<const fontlist::Face>(list);
240       if (face->mDescriptor.AsString(list).Equals(aliasName)) {
241         // Found it! Create an entry in the Alias table.
242         GenerateFontListKey(aliasName, key);
243         if (SharedFontList()->FindFamily(key) || mAliasTable.Get(key)) {
244           // If the family name is already known, something's misconfigured;
245           // just ignore it.
246           MOZ_ASSERT(false, "single-face family already known");
247           break;
248         }
249         auto aliasData = mAliasTable.GetOrInsertNew(key);
250         // The "alias" here isn't based on an existing family, so we don't call
251         // aliasData->InitFromFamily(); the various flags are left as defaults.
252         aliasData->mFaces.AppendElement(facePtrs[i]);
253         aliasData->mBaseFamily = aliasName;
254         aliasData->mVisibility = family->Visibility();
255         break;
256       }
257     }
258   }
259   if (!mAliasTable.IsEmpty()) {
260     // This will be updated when the font loader completes, but we require
261     // at least the Osaka-Mono alias to be available immediately.
262     SharedFontList()->SetAliases(mAliasTable);
263   }
266 void gfxMacPlatformFontList::InitSingleFaceList() {
267   for (const auto& familyName : mSingleFaceFonts) {
268     LOG_FONTLIST(("(fontlist-singleface) face name: %s\n", familyName.get()));
269     // Each entry in the "single face families" list is expected to be a
270     // colon-separated pair of FaceName:Family,
271     // where FaceName is the individual face name (psname) of a font
272     // that should be exposed as a separate family name,
273     // and Family is the standard family to which that face belongs.
274     // The only such face listed by default is
275     //    Osaka-Mono:Osaka
276     auto colon = familyName.FindChar(':');
277     if (colon == kNotFound) {
278       continue;
279     }
281     // Look for the parent family in the main font family list,
282     // and ensure we have loaded its list of available faces.
283     nsAutoCString key(Substring(familyName, colon + 1));
284     ToLowerCase(key);
285     gfxFontFamily* family = mFontFamilies.GetWeak(key);
286     if (!family || family->IsHidden()) {
287       continue;
288     }
289     family->FindStyleVariations();
291     // Truncate the entry from prefs at the colon, so now it is just the
292     // desired single-face-family name.
293     nsAutoCString aliasName(Substring(familyName, 0, colon));
295     // Look through the family's faces to see if this one is present.
296     const gfxFontEntry* fe = nullptr;
297     family->ReadLock();
298     for (const auto& face : family->GetFontList()) {
299       if (face->Name().Equals(aliasName)) {
300         fe = face;
301         break;
302       }
303     }
304     family->ReadUnlock();
305     if (!fe) {
306       continue;
307     }
309     // We found the correct face, so create the single-face family record.
310     GenerateFontListKey(aliasName, key);
311     LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
312                   aliasName.get(), key.get()));
314     // add only if doesn't exist already
315     if (!mFontFamilies.GetWeak(key)) {
316       RefPtr<gfxFontFamily> familyEntry =
317           new gfxSingleFaceMacFontFamily(aliasName, family->Visibility());
318       // We need a separate font entry, because its family name will
319       // differ from the one we found in the main list.
320       CTFontEntry* fontEntry =
321           new CTFontEntry(fe->Name(), fe->Weight(), true,
322                           static_cast<const CTFontEntry*>(fe)->mSizeHint);
323       familyEntry->AddFontEntry(fontEntry);
324       familyEntry->SetHasStyles(true);
325       mFontFamilies.InsertOrUpdate(key, std::move(familyEntry));
326       LOG_FONTLIST(("(fontlist-singleface) added new family: %s, key: %s\n",
327                     aliasName.get(), key.get()));
328     }
329   }
332 void gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
333                                               nsACString& aSystemFontName,
334                                               gfxFontStyle& aFontStyle) {
335   // Provide a local pool because we may be called from stylo threads.
336   nsAutoreleasePool localPool;
338   // code moved here from widget/cocoa/nsLookAndFeel.mm
339   NSFont* font = nullptr;
340   switch (aSystemFontID) {
341     case LookAndFeel::FontID::MessageBox:
342     case LookAndFeel::FontID::StatusBar:
343     case LookAndFeel::FontID::MozList:
344     case LookAndFeel::FontID::MozField:
345     case LookAndFeel::FontID::MozButton:
346       font = [NSFont systemFontOfSize:NSFont.smallSystemFontSize];
347       break;
349     case LookAndFeel::FontID::SmallCaption:
350       font = [NSFont boldSystemFontOfSize:NSFont.smallSystemFontSize];
351       break;
353     case LookAndFeel::FontID::Icon:  // used in urlbar; tried labelFont, but too
354                                      // small
355       font = [NSFont controlContentFontOfSize:0.0];
356       break;
358     case LookAndFeel::FontID::MozPullDownMenu:
359       font = [NSFont menuBarFontOfSize:0.0];
360       break;
362     case LookAndFeel::FontID::Caption:
363     case LookAndFeel::FontID::Menu:
364     default:
365       font = [NSFont systemFontOfSize:0.0];
366       break;
367   }
368   NS_ASSERTION(font, "system font not set");
370   aSystemFontName.AssignASCII("-apple-system");
372   NSFontSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
373   aFontStyle.style = (traits & NSFontItalicTrait) ? FontSlantStyle::ITALIC
374                                                   : FontSlantStyle::NORMAL;
375   aFontStyle.weight =
376       (traits & NSFontBoldTrait) ? FontWeight::BOLD : FontWeight::NORMAL;
377   aFontStyle.stretch = (traits & NSFontExpandedTrait) ? FontStretch::EXPANDED
378                        : (traits & NSFontCondensedTrait)
379                            ? FontStretch::CONDENSED
380                            : FontStretch::NORMAL;
381   aFontStyle.size = font.pointSize;
382   aFontStyle.systemFont = true;