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 ***** */
40
41#include "mozilla/Logging.h"
42
43#include <algorithm>
44
45#import <AppKit/AppKit.h>
46
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"
53
54#include "harfbuzz/hb.h"
55
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"
68
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"
79
80#include <unistd.h>
81#include <time.h>
82#include <dlfcn.h>
83
84#include "StandardFonts-macos.inc"
85
86using namespace mozilla;
87using namespace mozilla::gfx;
88
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
95
96static const int kAppleMaxWeight = 14;
97static const int kAppleExtraLightWeight = 3;
98static const int kAppleUltraLightWeight = 2;
99
100static 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
116};
117
118// cache Cocoa's "shared font manager" for performance
119static NSFontManager* sFontManager;
120
121static 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])];
125}
126
127static NSString* GetNSStringForString(const nsAString& aSrc) {
128  return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
129                                 length:aSrc.Length()];
130}
131
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)
138
139#pragma mark -
140
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).
146
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.
150
151nsresult MacOSFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
152  // attempt this once, if errors occur leave a blank cmap
153  if (mCharacterMap || mShmemCharacterMap) {
154    return NS_OK;
155  }
156
157  RefPtr<gfxCharacterMap> charmap;
158  nsresult rv;
159
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);
166
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  }
176
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.
180
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
185
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    }
198
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        }
211
212        // We check for GSUB here, as GPOS alone would not be ok.
213        if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) {
214          continue;
215        }
216
217        charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
218      }
219    }
220
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  }
241
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  }
260
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  }
269
270  return rv;
271}
272
273gfxFont* 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  }
283
284  return new gfxMacFont(unscaledFont, this, aFontStyle);
285}
286
287bool MacOSFontEntry::HasVariations() {
288  if (!mHasVariationsInitialized) {
289    mHasVariationsInitialized = true;
290    mHasVariations = gfxPlatform::GetPlatform()->HasVariationFontSupport() &&
291                     HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r'));
292  }
293
294  return mHasVariations;
295}
296
297void 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);
303}
304
305void 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);
309}
310
311bool MacOSFontEntry::IsCFF() {
312  if (!mIsCFFInitialized) {
313    mIsCFFInitialized = true;
314    mIsCFF = HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '));
315  }
316
317  return mIsCFF;
318}
319
320MacOSFontEntry::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;
335}
336
337MacOSFontEntry::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);
354
355  mWeightRange = aWeight;
356  mStretchRange = aStretch;
357  mFixedPitch = false;  // xxx - do we need this for downloaded fonts?
358  mStyleRange = aStyle;
359  mOpszAxis.mTag = 0;
360
361  NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
362               "userfont is either a data font or a local font");
363  mIsDataUserFont = aIsDataUserFont;
364  mIsLocalUserFont = aIsLocalUserFont;
365}
366
367gfxFontEntry* 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;
374}
375
376CGFontRef 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;
384}
385
386CGFontRef 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
394  CrashReporter::AutoAnnotateCrashReport autoFontName(CrashReporter::Annotation::FontName, mName);
395
396  // Create a new CGFont; caller will own the only reference to it.
397  NSString* psname = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
398  CGFontRef ref = CGFontCreateWithFontName(CFStringRef(psname));
399  if (!ref) {
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
413}
414
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.
418class FontTableRec {
419 public:
420  explicit FontTableRec(CFDataRef aDataRef) : mDataRef(aDataRef) { MOZ_COUNT_CTOR(FontTableRec); }
421
422  ~FontTableRec() {
423    MOZ_COUNT_DTOR(FontTableRec);
424    ::CFRelease(mDataRef);
425  }
426
427 private:
428  CFDataRef mDataRef;
429};
430
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
438}
439
440hb_blob_t* MacOSFontEntry::GetFontTable(uint32_t aTag) {
441  AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef();
442  if (!fontRef) {
443    return nullptr;
444  }
445
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  }
457
458  return nullptr;
459}
460
461bool MacOSFontEntry::HasFontTable(uint32_t aTableTag) {
462  if (mAvailableTables.Count() == 0) {
463    nsAutoreleasePool localPool;
464
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  }
479
480  return mAvailableTables.GetEntry(aTableTag);
481}
482
483static 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;
536}
537
538bool 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);
564}
565
566void MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
567                                            FontListSizes* aSizes) const {
568  aSizes->mFontListSize += aMallocSizeOf(this);
569  AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
570}
571
572/* gfxMacFontFamily */
573#pragma mark -
574
575class gfxMacFontFamily final : public gfxFontFamily {
576 public:
577  gfxMacFontFamily(const nsACString& aName, FontVisibility aVisibility, double aSizeHint)
578      : gfxFontFamily(aName, aVisibility), mSizeHint(aSizeHint) {}
579
580  virtual ~gfxMacFontFamily() = default;
581
582  virtual void LocalizedName(nsACString& aLocalizedName);
583
584  virtual void FindStyleVariations(FontInfoData* aFontInfoData = nullptr);
585
586 protected:
587  double mSizeHint;
588};
589
590void gfxMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
591  nsAutoreleasePool localPool;
592
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  }
601
602  NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
603  NSString* localized = [sFontManager localizedNameForFamily:family face:nil];
604
605  if (localized) {
606    nsAutoString locName;
607    GetStringForNSString(localized, locName);
608    CopyUTF16toUTF8(locName, aLocalizedName);
609    return;
610  }
611
612  // failed to get localized name, just use the canonical one
613  aLocalizedName = mName;
614}
615
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.
620static 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);
626}
627
628void gfxMacFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
629  if (mHasStyles) {
630    return;
631  }
632
633  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("gfxMacFontFamily::FindStyleVariations", LAYOUT, mName);
634
635  nsAutoreleasePool localPool;
636
637  NSString* family = GetNSStringForString(NS_ConvertUTF8toUTF16(mName));
638
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;
645
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;
653
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    }
661
662    // make a nsString
663    nsAutoString postscriptFontName;
664    GetStringForNSString(psname, postscriptFontName);
665
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
680
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    }
686
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    }
694
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    }
710
711    if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
712      fontEntry->SetupVariationRanges();
713    }
714
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    }
727
728    // insert into font entry array of family
729    AddFontEntry(fontEntry);
730  }
731
732  SortAvailableFonts();
733  SetHasStyles(true);
734
735  if (mIsBadUnderlineFamily) {
736    SetBadUnderlineFonts();
737  }
738
739  CheckForSimpleFamily();
740}
741
742/* gfxSingleFaceMacFontFamily */
743#pragma mark -
744
745class 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  }
751
752  virtual ~gfxSingleFaceMacFontFamily() = default;
753
754  virtual void LocalizedName(nsACString& aLocalizedName);
755
756  virtual void ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList);
757
758  virtual bool IsSingleFaceFamily() const { return true; }
759};
760
761void gfxSingleFaceMacFontFamily::LocalizedName(nsACString& aLocalizedName) {
762  nsAutoreleasePool localPool;
763
764  if (!HasOtherFamilyNames()) {
765    aLocalizedName = mName;
766    return;
767  }
768
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  }
781
782  // failed to get localized name, just use the canonical one
783  aLocalizedName = mName;
784}
785
786void gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList* aPlatformFontList) {
787  if (mOtherFamilyNamesInitialized) {
788    return;
789  }
790
791  gfxFontEntry* fe = mAvailableFonts[0];
792  if (!fe) {
793    return;
794  }
795
796  const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
797
798  gfxFontEntry::AutoTable nameTable(fe, kNAME);
799  if (!nameTable) {
800    return;
801  }
802
803  mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable, true);
804
805  mOtherFamilyNamesInitialized = true;
806}
807
808/* gfxMacPlatformFontList */
809#pragma mark -
810
811gfxMacPlatformFontList::gfxMacPlatformFontList()
812    : gfxPlatformFontList(false), mDefaultFont(nullptr), mUseSizeSensitiveSystemFont(false) {
813  CheckFamilyList(kBaseFonts);
814
815  // cache this in a static variable so that MacOSFontFamily objects
816  // don't have to repeatedly look it up
817  sFontManager = [NSFontManager sharedFontManager];
818
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);
823}
824
825gfxMacPlatformFontList::~gfxMacPlatformFontList() {
826  if (XRE_IsParentProcess()) {
827    ::CFNotificationCenterRemoveObserver(::CFNotificationCenterGetLocalCenter(), this,
828                                         kCTFontManagerRegisteredFontsChangedNotification, 0);
829  }
830
831  if (mDefaultFont) {
832    ::CFRelease(mDefaultFont);
833  }
834}
835
836void 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  }
842
843  nsAutoCString key;
844  ToLowerCase(aFamilyName, key);
845
846  RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(aFamilyName, aVisibility, sizeHint);
847  mFontFamilies.InsertOrUpdate(key, RefPtr{familyEntry});
848
849  // check the bad underline blocklist
850  if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
851    familyEntry->SetBadUnderlineFamily();
852  }
853}
854
855FontVisibility 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;
863}
864
865void gfxMacPlatformFontList::AddFamily(CFStringRef aFamily) {
866  NSString* family = (NSString*)aFamily;
867
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  }
874
875  nsAutoString familyName;
876  nsCocoaUtils::GetStringForNSString(family, familyName);
877
878  NS_ConvertUTF16toUTF8 nameUtf8(familyName);
879  AddFamily(nameUtf8, GetVisibilityForFamily(nameUtf8));
880}
881
882void 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  }
902}
903
904void 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);
909
910    // only search canonical names!
911    gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
912    if (familyEntry) {
913      familyEntry->ReadOtherFamilyNames(this);
914    }
915  }
916}
917
918nsresult gfxMacPlatformFontList::InitFontListForPlatform() {
919  nsAutoreleasePool localPool;
920
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();
925
926  Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
927
928  InitSystemFontNames();
929
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    }
939
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  }
974
975  InitSingleFaceList();
976
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();
982
983  // start the delayed cmap loader
984  GetPrefsAndStartLoader();
985
986  return NS_OK;
987}
988
989void gfxMacPlatformFontList::InitSharedFontListForPlatform() {
990  nsAutoreleasePool localPool;
991
992  gfxPlatformMac::WaitForFontRegistration();
993
994  InitSystemFontNames();
995
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    }
1007
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  }
1023}
1024
1025void 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    }
1039
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    }
1056
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));
1060
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  }
1093}
1094
1095void 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    }
1109
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();
1119
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));
1123
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    }
1135
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()));
1139
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  }
1155}
1156
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:
1160//
1161// [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont
1162// [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName]
1163//          size:8.0] familyName] ==> .SF NS Text
1164
1165static 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  }
1182
1183  AutoCFRelease<CTFontRef> ctFont = CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr);
1184  if (!ctFont) {
1185    return [aFont familyName];
1186  }
1187  NSString* familyName = (NSString*)CTFontCopyFamilyName(ctFont);
1188
1189  return [familyName autorelease];
1190}
1191
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.
1196static gfxFontFamily* CreateFamilyForSystemFont(NSFont* aFont, const nsACString& aFamilyName) {
1197  gfxFontFamily* familyEntry = new gfxFontFamily(aFamilyName, FontVisibility::Unknown);
1198
1199  NSString* psNameNS = [[aFont fontDescriptor] postscriptName];
1200  nsAutoString nameUTF16;
1201  nsAutoCString psName;
1202  nsCocoaUtils::GetStringForNSString(psNameNS, nameUTF16);
1203  CopyUTF16toUTF8(nameUTF16, psName);
1204
1205  MacOSFontEntry* fe = new MacOSFontEntry(psName, WeightRange(FontWeight::Normal()), true, 0.0);
1206  MOZ_ASSERT(gfxPlatform::GetPlatform()->HasVariationFontSupport());
1207  fe->SetupVariationRanges();
1208
1209  familyEntry->AddFontEntry(fe);
1210  familyEntry->SetHasStyles(true);
1211
1212  return familyEntry;
1213}
1214
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.
1220
1221const CGFloat kTextDisplayCrossover = 20.0;  // use text family below this size
1222
1223void 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();
1227
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);
1234
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  }
1246
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  }
1258
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
1272}
1273
1274gfxFontFamily* gfxMacPlatformFontList::FindSystemFontFamily(const nsACString& aFamily) {
1275  nsAutoCString key;
1276  GenerateFontListKey(aFamily, key);
1277
1278  gfxFontFamily* familyEntry;
1279  if ((familyEntry = mFontFamilies.GetWeak(key))) {
1280    return CheckFamily(familyEntry);
1281  }
1282
1283  return nullptr;
1284}
1285
1286void gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(
1287    CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object,
1288    CFDictionaryRef userInfo) {
1289  if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
1290    return;
1291  }
1292
1293  gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
1294  if (!fl->IsInitialized()) {
1295    return;
1296  }
1297
1298  // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
1299  fl->UpdateFontList();
1300
1301  gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes);
1302  dom::ContentParent::NotifyUpdatedFonts(true);
1303}
1304
1305gfxFontEntry* 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;
1313
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  }
1326
1327  // use CoreText to find the fallback family
1328
1329  gfxFontEntry* fontEntry = nullptr;
1330  bool cantUseFallbackFont = false;
1331
1332  if (!mDefaultFont) {
1333    mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, NULL);
1334  }
1335
1336  AutoCFRelease<CTFontRef> fallback =
1337      ::CTFontCreateForString(mDefaultFont, str, ::CFRangeMake(0, length));
1338
1339  if (fallback) {
1340    AutoCFRelease<CFStringRef> familyNameRef = ::CTFontCopyFamilyName(fallback);
1341
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);
1354
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      }
1372
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  }
1392
1393  if (cantUseFallbackFont) {
1394    Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
1395  }
1396
1397  ::CFRelease(str);
1398
1399  return fontEntry;
1400}
1401
1402FontFamily gfxMacPlatformFontList::GetDefaultFontForPlatform(nsPresContext* aPresContext,
1403                                                             const gfxFontStyle* aStyle,
1404                                                             nsAtom* aLanguage) {
1405  nsAutoreleasePool localPool;
1406
1407  NSString* defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
1408  nsAutoString familyName;
1409
1410  GetStringForNSString(defaultFamily, familyName);
1411  return FindFamily(aPresContext, NS_ConvertUTF16toUTF8(familyName));
1412}
1413
1414int32_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];
1420}
1421
1422gfxFontEntry* 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  }
1430
1431  nsAutoreleasePool localPool;
1432
1433  NSString* faceName = GetNSStringForString(NS_ConvertUTF8toUTF16(aFontName));
1434
1435  // lookup face based on postscript or full name
1436  AutoCFRelease<CGFontRef> fontRef = CGFontCreateWithFontName(CFStringRef(faceName));
1437  if (!fontRef) {
1438    return nullptr;
1439  }
1440
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.
1445
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);
1452
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  }
1476
1477  return new MacOSFontEntry(aFontName, fontRef, aWeightForEntry, aStretchForEntry, aStyleForEntry,
1478                            false, true);
1479}
1480
1481static void ReleaseData(void* info, const void* data, size_t size) { free((void*)data); }
1482
1483gfxFontEntry* 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");
1489
1490  // create the font entry
1491  nsAutoString uniqueName;
1492
1493  nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
1494  if (NS_FAILED(rv)) {
1495    return nullptr;
1496  }
1497
1498  AutoCFRelease<CGDataProviderRef> provider =
1499      ::CGDataProviderCreateWithData(nullptr, aFontData, aLength, &ReleaseData);
1500  AutoCFRelease<CGFontRef> fontRef = ::CGFontCreateWithDataProvider(provider);
1501  if (!fontRef) {
1502    return nullptr;
1503  }
1504
1505  auto newFontEntry =
1506      MakeUnique<MacOSFontEntry>(NS_ConvertUTF16toUTF8(uniqueName), fontRef, aWeightForEntry,
1507                                 aStretchForEntry, aStyleForEntry, true, false);
1508  return newFontEntry.release();
1509}
1510
1511// Webkit code uses a system font meta name, so mimic that here
1512// WebCore/platform/graphics/mac/FontCacheMac.mm
1513static const char kSystemFont_system[] = "-apple-system";
1514
1515bool 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  }
1544
1545  return gfxPlatformFontList::FindAndAddFamilies(aPresContext, aGeneric, aFamily, aOutput, aFlags,
1546                                                 aStyle, aLanguage, aDevToCssSize);
1547}
1548
1549void 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;
1554
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;
1567
1568    case LookAndFeel::FontID::SmallCaption:
1569      font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
1570      systemFontName = (char*)kSystemFont_system;
1571      break;
1572
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;
1580
1581    case LookAndFeel::FontID::MozPullDownMenu:
1582      font = [NSFont menuBarFontOfSize:0.0];
1583      systemFontName = (char*)kSystemFont_system;
1584      break;
1585
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");
1596
1597  if (systemFontName) {
1598    aSystemFontName.AssignASCII(systemFontName);
1599  }
1600
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;
1610}
1611
1612// used to load system-wide font info on off-main thread
1613class MacFontInfo final : public FontInfoData {
1614 public:
1615  MacFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps)
1616      : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps) {}
1617
1618  virtual ~MacFontInfo() = default;
1619
1620  virtual void Load() {
1621    nsAutoreleasePool localPool;
1622    FontInfoData::Load();
1623  }
1624
1625  // loads font data for all members of a given family
1626  virtual void LoadFontFamilyData(const nsACString& aFamilyName);
1627};
1628
1629void MacFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
1630  CrashReporter::AutoAnnotateCrashReport autoFontName(CrashReporter::Annotation::FontName,
1631                                                      aFamilyName);
1632
1633  // family name ==> CTFontDescriptor
1634  NSString* famName = GetNSStringForString(NS_ConvertUTF8toUTF16(aFamilyName));
1635  CFStringRef family = CFStringRef(famName);
1636
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  }
1645
1646  nsTArray<nsCString> otherFamilyNames;
1647  bool hasOtherFamilyNames = true;
1648
1649  // iterate over faces in the family
1650  int f, numFaces = (int)CFArrayGetCount(matchingFonts);
1651  for (f = 0; f < numFaces; f++) {
1652    mLoadStats.fonts++;
1653
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    }
1663
1664    if (mLoadCmaps) {
1665      // face name
1666      AutoCFRelease<CFStringRef> faceName =
1667          (CFStringRef)CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
1668
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);
1675
1676      // load the cmap data
1677      FontFaceData fontData;
1678      AutoCFRelease<CFDataRef> cmapTable =
1679          CTFontCopyTable(fontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions);
1680
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;
1687
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      }
1695
1696      mFontFaceData.InsertOrUpdate(fontName, fontData);
1697    }
1698
1699    if (mLoadOtherNames && hasOtherFamilyNames) {
1700      AutoCFRelease<CFDataRef> nameTable =
1701          CTFontCopyTable(fontRef, kCTFontTableName, kCTFontTableOptionNoOptions);
1702
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  }
1712
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  }
1718}
1719
1720already_AddRefed<FontInfoData> gfxMacPlatformFontList::CreateFontInfoData() {
1721  bool loadCmaps =
1722      !UsesSystemFallback() || gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1723
1724  RefPtr<MacFontInfo> fi = new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1725  return fi.forget();
1726}
1727
1728gfxFontFamily* gfxMacPlatformFontList::CreateFontFamily(const nsACString& aName,
1729                                                        FontVisibility aVisibility) const {
1730  return new gfxMacFontFamily(aName, aVisibility, 0.0);
1731}
1732
1733gfxFontEntry* 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;
1740}
1741
1742void gfxMacPlatformFontList::GetFacesInitDataForFamily(const fontlist::Family* aFamily,
1743                                                       nsTArray<fontlist::Face::InitData>& aFaces,
1744                                                       bool aLoadCmaps) const {
1745  nsAutoreleasePool localPool;
1746
1747  NS_ConvertUTF8toUTF16 name(aFamily->Key().AsString(SharedFontList()));
1748  NSString* family = GetNSStringForString(name);
1749
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];
1759
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    }
1767
1768    // make a nsString
1769    nsAutoString postscriptFontName;
1770    GetStringForNSString(psname, postscriptFontName);
1771
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
1782
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    }
1796
1797    bool fixedPitch = (macTraits & NSFixedPitchFontMask) ? true : false;
1798
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    }
1812
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  }
1831}
1832
1833void 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  }
1878}
1879