1/* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "gfxPlatformMac.h"
48#include "gfxMacPlatformFontList.h"
49#include "gfxMacFont.h"
50#include "gfxUserFontSet.h"
51#include "harfbuzz/hb.h"
52
53#include "nsServiceManagerUtils.h"
54#include "nsTArray.h"
55
56#include "nsDirectoryServiceUtils.h"
57#include "nsDirectoryServiceDefs.h"
58#include "nsAppDirectoryServiceDefs.h"
59#include "nsISimpleEnumerator.h"
60#include "nsCharTraits.h"
61#include "nsCocoaFeatures.h"
62#include "nsCocoaUtils.h"
63#include "gfxFontConstants.h"
64
65#include "mozilla/MemoryReporting.h"
66#include "mozilla/Preferences.h"
67#include "mozilla/Sprintf.h"
68#include "mozilla/Telemetry.h"
69#include "mozilla/gfx/2D.h"
70
71#include <unistd.h>
72#include <time.h>
73#include <dlfcn.h>
74
75using namespace mozilla;
76
77// indexes into the NSArray objects that the Cocoa font manager returns
78// as the available members of a family
79#define INDEX_FONT_POSTSCRIPT_NAME 0
80#define INDEX_FONT_FACE_NAME 1
81#define INDEX_FONT_WEIGHT 2
82#define INDEX_FONT_TRAITS 3
83
84static const int kAppleMaxWeight = 14;
85static const int kAppleExtraLightWeight = 3;
86static const int kAppleUltraLightWeight = 2;
87
88static const int gAppleWeightToCSSWeight[] = {
89    0,
90    1, // 1.
91    1, // 2.  W1, ultralight
92    2, // 3.  W2, extralight
93    3, // 4.  W3, light
94    4, // 5.  W4, semilight
95    5, // 6.  W5, medium
96    6, // 7.
97    6, // 8.  W6, semibold
98    7, // 9.  W7, bold
99    8, // 10. W8, extrabold
100    8, // 11.
101    9, // 12. W9, ultrabold
102    9, // 13
103    9  // 14
104};
105
106// cache Cocoa's "shared font manager" for performance
107static NSFontManager *sFontManager;
108
109static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
110{
111    aDist.SetLength([aSrc length]);
112    [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())];
113}
114
115static NSString* GetNSStringForString(const nsAString& aSrc)
116{
117    return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
118                                   length:aSrc.Length()];
119}
120
121#define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
122                               mozilla::LogLevel::Debug, args)
123#define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
124                                   gfxPlatform::GetLog(eGfxLog_fontlist), \
125                                   mozilla::LogLevel::Debug)
126#define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
127                                   gfxPlatform::GetLog(eGfxLog_cmapdata), \
128                                   mozilla::LogLevel::Debug)
129
130#pragma mark-
131
132// Complex scripts will not render correctly unless appropriate AAT or OT
133// layout tables are present.
134// For OpenType, we also check that the GSUB table supports the relevant
135// script tag, to avoid using things like Arial Unicode MS for Lao (it has
136// the characters, but lacks OpenType support).
137
138// TODO: consider whether we should move this to gfxFontEntry and do similar
139// cmap-masking on other platforms to avoid using fonts that won't shape
140// properly.
141
142nsresult
143MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
144{
145    // attempt this once, if errors occur leave a blank cmap
146    if (mCharacterMap) {
147        return NS_OK;
148    }
149
150    RefPtr<gfxCharacterMap> charmap;
151    nsresult rv;
152    bool symbolFont = false; // currently ignored
153
154    if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
155                                                        mUVSOffset,
156                                                        symbolFont))) {
157        rv = NS_OK;
158    } else {
159        uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
160        charmap = new gfxCharacterMap();
161        AutoTable cmapTable(this, kCMAP);
162
163        if (cmapTable) {
164            bool unicodeFont = false; // currently ignored
165            uint32_t cmapLen;
166            const uint8_t* cmapData =
167                reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
168                                                                  &cmapLen));
169            rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
170                                        *charmap, mUVSOffset,
171                                        unicodeFont, symbolFont);
172        } else {
173            rv = NS_ERROR_NOT_AVAILABLE;
174        }
175    }
176
177    if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
178        // We assume a Graphite font knows what it's doing,
179        // and provides whatever shaping is needed for the
180        // characters it supports, so only check/clear the
181        // complex-script ranges for non-Graphite fonts
182
183        // for layout support, check for the presence of mort/morx and/or
184        // opentype layout tables
185        bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
186                            HasFontTable(TRUETYPE_TAG('m','o','r','t'));
187        bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
188        bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
189        if (hasAATLayout && !(hasGSUB || hasGPOS)) {
190            mRequiresAAT = true; // prefer CoreText if font has no OTL tables
191        }
192
193        for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
194             sr->rangeStart; sr++) {
195            // check to see if the cmap includes complex script codepoints
196            if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
197                if (hasAATLayout) {
198                    // prefer CoreText for Apple's complex-script fonts,
199                    // even if they also have some OpenType tables
200                    // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
201                    mRequiresAAT = true;
202                    // and don't mask off complex-script ranges, we assume
203                    // the AAT tables will provide the necessary shaping
204                    continue;
205                }
206
207                // We check for GSUB here, as GPOS alone would not be ok.
208                if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
209                    continue;
210                }
211
212                charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
213            }
214        }
215
216        // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious
217        // blank glyphs for obscure Tibetan and Arabic-script codepoints.
218        // Blacklist these so that font fallback will not use them.
219        if (mRequiresAAT && (FamilyName().EqualsLiteral("Songti SC") ||
220                             FamilyName().EqualsLiteral("Songti TC") ||
221                             FamilyName().EqualsLiteral("STSong") ||
222        // Bug 1390980: on 10.11, the Kaiti fonts are also affected.
223                             FamilyName().EqualsLiteral("Kaiti SC") ||
224                             FamilyName().EqualsLiteral("Kaiti TC") ||
225                             FamilyName().EqualsLiteral("STKaiti"))) {
226            charmap->ClearRange(0x0f6b, 0x0f70);
227            charmap->ClearRange(0x0f8c, 0x0f8f);
228            charmap->clear(0x0f98);
229            charmap->clear(0x0fbd);
230            charmap->ClearRange(0x0fcd, 0x0fff);
231            charmap->clear(0x0620);
232            charmap->clear(0x065f);
233            charmap->ClearRange(0x06ee, 0x06ef);
234            charmap->clear(0x06ff);
235        }
236    }
237
238    mHasCmapTable = NS_SUCCEEDED(rv);
239    if (mHasCmapTable) {
240        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
241        mCharacterMap = pfl->FindCharMap(charmap);
242    } else {
243        // if error occurred, initialize to null cmap
244        mCharacterMap = new gfxCharacterMap();
245    }
246
247    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
248                  NS_ConvertUTF16toUTF8(mName).get(),
249                  charmap->SizeOfIncludingThis(moz_malloc_size_of),
250                  charmap->mHash, mCharacterMap == charmap ? " new" : ""));
251    if (LOG_CMAPDATA_ENABLED()) {
252        char prefix[256];
253        SprintfLiteral(prefix, "(cmapdata) name: %.220s",
254                       NS_ConvertUTF16toUTF8(mName).get());
255        charmap->Dump(prefix, eGfxLog_cmapdata);
256    }
257
258    return rv;
259}
260
261gfxFont*
262MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
263{
264    return new gfxMacFont(this, aFontStyle, aNeedsBold);
265}
266
267bool
268MacOSFontEntry::IsCFF()
269{
270    if (!mIsCFFInitialized) {
271        mIsCFFInitialized = true;
272        mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
273    }
274
275    return mIsCFF;
276}
277
278MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
279                               int32_t aWeight,
280                               bool aIsStandardFace,
281                               double aSizeHint)
282    : gfxFontEntry(aPostscriptName, aIsStandardFace),
283      mFontRef(NULL),
284      mSizeHint(aSizeHint),
285      mFontRefInitialized(false),
286      mRequiresAAT(false),
287      mIsCFF(false),
288      mIsCFFInitialized(false)
289{
290    mWeight = aWeight;
291}
292
293MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
294                               CGFontRef aFontRef,
295                               uint16_t aWeight, uint16_t aStretch,
296                               uint8_t aStyle,
297                               bool aIsDataUserFont,
298                               bool aIsLocalUserFont)
299    : gfxFontEntry(aPostscriptName, false),
300      mFontRef(NULL),
301      mSizeHint(0.0),
302      mFontRefInitialized(false),
303      mRequiresAAT(false),
304      mIsCFF(false),
305      mIsCFFInitialized(false)
306{
307    mFontRef = aFontRef;
308    mFontRefInitialized = true;
309    ::CFRetain(mFontRef);
310
311    mWeight = aWeight;
312    mStretch = aStretch;
313    mFixedPitch = false; // xxx - do we need this for downloaded fonts?
314    mStyle = aStyle;
315
316    NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
317                 "userfont is either a data font or a local font");
318    mIsDataUserFont = aIsDataUserFont;
319    mIsLocalUserFont = aIsLocalUserFont;
320}
321
322CGFontRef
323MacOSFontEntry::GetFontRef()
324{
325    if (!mFontRefInitialized) {
326        mFontRefInitialized = true;
327        NSString *psname = GetNSStringForString(mName);
328        mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
329        if (!mFontRef) {
330            // This happens on macOS 10.12 for font entry names that start with
331            // .AppleSystemUIFont. For those fonts, we need to go through NSFont
332            // to get the correct CGFontRef.
333            // Both the Text and the Display variant of the display font use
334            // .AppleSystemUIFontSomethingSomething as their member names.
335            // That's why we're carrying along mSizeHint to this place so that
336            // we get the variant that we want for this family.
337            NSFont* font = [NSFont fontWithName:psname size:mSizeHint];
338            if (font) {
339                mFontRef = CTFontCopyGraphicsFont((CTFontRef)font, nullptr);
340            }
341        }
342    }
343    return mFontRef;
344}
345
346// For a logging build, we wrap the CFDataRef in a FontTableRec so that we can
347// use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging
348// does not get this overhead.
349class FontTableRec {
350public:
351    explicit FontTableRec(CFDataRef aDataRef)
352        : mDataRef(aDataRef)
353    {
354        MOZ_COUNT_CTOR(FontTableRec);
355    }
356
357    ~FontTableRec() {
358        MOZ_COUNT_DTOR(FontTableRec);
359        ::CFRelease(mDataRef);
360    }
361
362private:
363    CFDataRef mDataRef;
364};
365
366/*static*/ void
367MacOSFontEntry::DestroyBlobFunc(void* aUserData)
368{
369#ifdef NS_BUILD_REFCNT_LOGGING
370    FontTableRec *ftr = static_cast<FontTableRec*>(aUserData);
371    delete ftr;
372#else
373    ::CFRelease((CFDataRef)aUserData);
374#endif
375}
376
377hb_blob_t *
378MacOSFontEntry::GetFontTable(uint32_t aTag)
379{
380    CGFontRef fontRef = GetFontRef();
381    if (!fontRef) {
382        return nullptr;
383    }
384
385    CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
386    if (dataRef) {
387        return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
388                              ::CFDataGetLength(dataRef),
389                              HB_MEMORY_MODE_READONLY,
390#ifdef NS_BUILD_REFCNT_LOGGING
391                              new FontTableRec(dataRef),
392#else
393                              (void*)dataRef,
394#endif
395                              DestroyBlobFunc);
396    }
397
398    return nullptr;
399}
400
401bool
402MacOSFontEntry::HasFontTable(uint32_t aTableTag)
403{
404    if (mAvailableTables.Count() == 0) {
405        nsAutoreleasePool localPool;
406
407        CGFontRef fontRef = GetFontRef();
408        if (!fontRef) {
409            return false;
410        }
411        CFArrayRef tags = ::CGFontCopyTableTags(fontRef);
412        if (!tags) {
413            return false;
414        }
415        int numTags = (int) ::CFArrayGetCount(tags);
416        for (int t = 0; t < numTags; t++) {
417            uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t);
418            mAvailableTables.PutEntry(tag);
419        }
420        ::CFRelease(tags);
421    }
422
423    return mAvailableTables.GetEntry(aTableTag);
424}
425
426void
427MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
428                                       FontListSizes* aSizes) const
429{
430    aSizes->mFontListSize += aMallocSizeOf(this);
431    AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
432}
433
434/* gfxMacFontFamily */
435#pragma mark-
436
437class gfxMacFontFamily : public gfxFontFamily
438{
439public:
440    explicit gfxMacFontFamily(nsAString& aName, double aSizeHint) :
441        gfxFontFamily(aName),
442        mSizeHint(aSizeHint)
443    {}
444
445    virtual ~gfxMacFontFamily() {}
446
447    virtual void LocalizedName(nsAString& aLocalizedName);
448
449    virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
450
451protected:
452    double mSizeHint;
453};
454
455void
456gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
457{
458    nsAutoreleasePool localPool;
459
460    if (!HasOtherFamilyNames()) {
461        aLocalizedName = mName;
462        return;
463    }
464
465    NSString *family = GetNSStringForString(mName);
466    NSString *localized = [sFontManager
467                           localizedNameForFamily:family
468                                             face:nil];
469
470    if (localized) {
471        GetStringForNSString(localized, aLocalizedName);
472        return;
473    }
474
475    // failed to get localized name, just use the canonical one
476    aLocalizedName = mName;
477}
478
479// Return the CSS weight value to use for the given face, overriding what
480// AppKit gives us (used to adjust families with bad weight values, see
481// bug 931426).
482// A return value of 0 indicates no override - use the existing weight.
483static inline int
484GetWeightOverride(const nsAString& aPSName)
485{
486    nsAutoCString prefName("font.weight-override.");
487    // The PostScript name is required to be ASCII; if it's not, the font is
488    // broken anyway, so we really don't care that this is lossy.
489    LossyAppendUTF16toASCII(aPSName, prefName);
490    return Preferences::GetInt(prefName.get(), 0);
491}
492
493void
494gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
495{
496    if (mHasStyles)
497        return;
498
499    nsAutoreleasePool localPool;
500
501    NSString *family = GetNSStringForString(mName);
502
503    // create a font entry for each face
504    NSArray *fontfaces = [sFontManager
505                          availableMembersOfFontFamily:family];  // returns an array of [psname, style name, weight, traits] elements, goofy api
506    int faceCount = [fontfaces count];
507    int faceIndex;
508
509    for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
510        NSArray *face = [fontfaces objectAtIndex:faceIndex];
511        NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
512        int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
513        uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
514        NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
515        bool isStandardFace = false;
516
517        if (appKitWeight == kAppleExtraLightWeight) {
518            // if the facename contains UltraLight, set the weight to the ultralight weight value
519            NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
520            if (range.location != NSNotFound) {
521                appKitWeight = kAppleUltraLightWeight;
522            }
523        }
524
525        // make a nsString
526        nsAutoString postscriptFontName;
527        GetStringForNSString(psname, postscriptFontName);
528
529        int32_t cssWeight = GetWeightOverride(postscriptFontName);
530        if (cssWeight) {
531            // scale down and clamp, to get a value from 1..9
532            cssWeight = ((cssWeight + 50) / 100);
533            cssWeight = std::max(1, std::min(cssWeight, 9));
534        } else {
535            cssWeight =
536                gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
537        }
538        cssWeight *= 100; // scale up to CSS values
539
540        if ([facename isEqualToString:@"Regular"] ||
541            [facename isEqualToString:@"Bold"] ||
542            [facename isEqualToString:@"Italic"] ||
543            [facename isEqualToString:@"Oblique"] ||
544            [facename isEqualToString:@"Bold Italic"] ||
545            [facename isEqualToString:@"Bold Oblique"])
546        {
547            isStandardFace = true;
548        }
549
550        // create a font entry
551        MacOSFontEntry *fontEntry =
552            new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace, mSizeHint);
553        if (!fontEntry) {
554            break;
555        }
556
557        // set additional properties based on the traits reported by Cocoa
558        if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
559            fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
560        } else if (macTraits & NSExpandedFontMask) {
561            fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
562        }
563        // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
564        // at least (see bug 611855), so check for style name endings as well
565        if ((macTraits & NSItalicFontMask) ||
566            [facename hasSuffix:@"Italic"] ||
567            [facename hasSuffix:@"Oblique"])
568        {
569            fontEntry->mStyle = NS_FONT_STYLE_ITALIC;
570        }
571        if (macTraits & NSFixedPitchFontMask) {
572            fontEntry->mFixedPitch = true;
573        }
574
575        if (LOG_FONTLIST_ENABLED()) {
576            LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
577                 " with style: %s weight: %d stretch: %d"
578                 " (apple-weight: %d macTraits: %8.8x)",
579                 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
580                 NS_ConvertUTF16toUTF8(Name()).get(),
581                 fontEntry->IsItalic() ? "italic" : "normal",
582                 cssWeight, fontEntry->Stretch(),
583                 appKitWeight, macTraits));
584        }
585
586        // insert into font entry array of family
587        AddFontEntry(fontEntry);
588    }
589
590    SortAvailableFonts();
591    SetHasStyles(true);
592
593    if (mIsBadUnderlineFamily) {
594        SetBadUnderlineFonts();
595    }
596}
597
598/* gfxSingleFaceMacFontFamily */
599#pragma mark-
600
601class gfxSingleFaceMacFontFamily : public gfxFontFamily
602{
603public:
604    explicit gfxSingleFaceMacFontFamily(nsAString& aName) :
605        gfxFontFamily(aName)
606    {
607        mFaceNamesInitialized = true; // omit from face name lists
608    }
609
610    virtual ~gfxSingleFaceMacFontFamily() {}
611
612    virtual void LocalizedName(nsAString& aLocalizedName);
613
614    virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
615};
616
617void
618gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
619{
620    nsAutoreleasePool localPool;
621
622    if (!HasOtherFamilyNames()) {
623        aLocalizedName = mName;
624        return;
625    }
626
627    gfxFontEntry *fe = mAvailableFonts[0];
628    NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
629                                   size:0.0];
630    if (font) {
631        NSString *localized = [font displayName];
632        if (localized) {
633            GetStringForNSString(localized, aLocalizedName);
634            return;
635        }
636    }
637
638    // failed to get localized name, just use the canonical one
639    aLocalizedName = mName;
640}
641
642void
643gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
644{
645    if (mOtherFamilyNamesInitialized) {
646        return;
647    }
648
649    gfxFontEntry *fe = mAvailableFonts[0];
650    if (!fe) {
651        return;
652    }
653
654    const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
655
656    gfxFontEntry::AutoTable nameTable(fe, kNAME);
657    if (!nameTable) {
658        return;
659    }
660
661    mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
662                                                       nameTable,
663                                                       true);
664
665    mOtherFamilyNamesInitialized = true;
666}
667
668/* gfxMacPlatformFontList */
669#pragma mark-
670
671gfxMacPlatformFontList::gfxMacPlatformFontList() :
672    gfxPlatformFontList(false),
673    mDefaultFont(nullptr),
674    mUseSizeSensitiveSystemFont(false)
675{
676#ifdef MOZ_BUNDLED_FONTS
677    ActivateBundledFonts();
678#endif
679
680    ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
681                                      this,
682                                      RegisteredFontsChangedNotificationCallback,
683                                      kCTFontManagerRegisteredFontsChangedNotification,
684                                      0,
685                                      CFNotificationSuspensionBehaviorDeliverImmediately);
686
687    // cache this in a static variable so that MacOSFontFamily objects
688    // don't have to repeatedly look it up
689    sFontManager = [NSFontManager sharedFontManager];
690}
691
692gfxMacPlatformFontList::~gfxMacPlatformFontList()
693{
694    if (mDefaultFont) {
695        ::CFRelease(mDefaultFont);
696    }
697}
698
699void
700gfxMacPlatformFontList::AddFamily(CFStringRef aFamily)
701{
702    NSString* family = (NSString*)aFamily;
703
704    // CTFontManager includes weird internal family names and
705    // LastResort, skip over those
706    if (!family || [family caseInsensitiveCompare:@"LastResort"] == NSOrderedSame) {
707        return;
708    }
709
710    bool hiddenSystemFont = [family hasPrefix:@"."];
711
712    FontFamilyTable& table =
713        hiddenSystemFont ? mSystemFontFamilies : mFontFamilies;
714
715    nsAutoString familyName;
716    nsCocoaUtils::GetStringForNSString(family, familyName);
717
718    double sizeHint = 0.0;
719    if (hiddenSystemFont && mUseSizeSensitiveSystemFont &&
720        mSystemDisplayFontFamilyName.Equals(familyName)) {
721        sizeHint = 128.0;
722    }
723
724    nsAutoString key;
725    ToLowerCase(familyName, key);
726
727    RefPtr<gfxFontFamily> familyEntry = new gfxMacFontFamily(familyName, sizeHint);
728    table.Put(key, familyEntry);
729
730    // check the bad underline blacklist
731    if (mBadUnderlineFamilyNames.Contains(key)) {
732        familyEntry->SetBadUnderlineFamily();
733    }
734}
735
736nsresult
737gfxMacPlatformFontList::InitFontListForPlatform()
738{
739    nsAutoreleasePool localPool;
740
741    Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
742
743    // reset system font list
744    mSystemFontFamilies.Clear();
745
746    // iterate over available families
747
748    InitSystemFontNames();
749
750    CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
751
752    for (NSString* familyName in (NSArray*)familyNames) {
753        AddFamily((CFStringRef)familyName);
754    }
755
756    CFRelease(familyNames);
757
758    InitSingleFaceList();
759
760    // to avoid full search of font name tables, seed the other names table with localized names from
761    // some of the prefs fonts which are accessed via their localized names.  changes in the pref fonts will only cause
762    // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
763    PreloadNamesList();
764
765    // start the delayed cmap loader
766    GetPrefsAndStartLoader();
767
768    return NS_OK;
769}
770
771void
772gfxMacPlatformFontList::InitSingleFaceList()
773{
774    AutoTArray<nsString, 10> singleFaceFonts;
775    gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
776
777    for (const auto& singleFaceFamily : singleFaceFonts) {
778        LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
779                      NS_ConvertUTF16toUTF8(singleFaceFamily).get()));
780        // Each entry in the "single face families" list is expected to be a
781        // colon-separated pair of FaceName:Family,
782        // where FaceName is the individual face name (psname) of a font
783        // that should be exposed as a separate family name,
784        // and Family is the standard family to which that face belongs.
785        // The only such face listed by default is
786        //    Osaka-Mono:Osaka
787        nsAutoString familyName(singleFaceFamily);
788        auto colon = familyName.FindChar(':');
789        if (colon == kNotFound) {
790            continue;
791        }
792
793        // Look for the parent family in the main font family list,
794        // and ensure we have loaded its list of available faces.
795        nsAutoString key(Substring(familyName, colon + 1));
796        ToLowerCase(key);
797        gfxFontFamily* family = mFontFamilies.GetWeak(key);
798        if (!family) {
799            continue;
800        }
801        family->FindStyleVariations();
802
803        // Truncate the entry from prefs at the colon, so now it is just the
804        // desired single-face-family name.
805        familyName.Truncate(colon);
806
807        // Look through the family's faces to see if this one is present.
808        const gfxFontEntry* fe = nullptr;
809        for (const auto& face : family->GetFontList()) {
810            if (face->Name().Equals(familyName)) {
811                fe = face;
812                break;
813            }
814        }
815        if (!fe) {
816            continue;
817        }
818
819        // We found the correct face, so create the single-face family record.
820        GenerateFontListKey(familyName, key);
821        LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
822                      NS_ConvertUTF16toUTF8(familyName).get(),
823                      NS_ConvertUTF16toUTF8(key).get()));
824
825        // add only if doesn't exist already
826        if (!mFontFamilies.GetWeak(key)) {
827            RefPtr<gfxFontFamily> familyEntry =
828                new gfxSingleFaceMacFontFamily(familyName);
829            // We need a separate font entry, because its family name will
830            // differ from the one we found in the main list.
831            MacOSFontEntry* fontEntry =
832                new MacOSFontEntry(fe->Name(), fe->mWeight, true,
833                                   static_cast<const MacOSFontEntry*>(fe)->
834                                       mSizeHint);
835            familyEntry->AddFontEntry(fontEntry);
836            familyEntry->SetHasStyles(true);
837            mFontFamilies.Put(key, familyEntry);
838            LOG_FONTLIST(("(fontlist-singleface) added new family\n",
839                          NS_ConvertUTF16toUTF8(familyName).get(),
840                          NS_ConvertUTF16toUTF8(key).get()));
841        }
842    }
843}
844
845// System fonts under OSX may contain weird "meta" names but if we create
846// a new font using just the Postscript name, the NSFont api returns an object
847// with the actual real family name. For example, under OSX 10.11:
848//
849// [[NSFont menuFontOfSize:8.0] familyName] ==> .AppleSystemUIFont
850// [[NSFont fontWithName:[[[NSFont menuFontOfSize:8.0] fontDescriptor] postscriptName]
851//          size:8.0] familyName] ==> .SF NS Text
852
853static NSString* GetRealFamilyName(NSFont* aFont)
854{
855    NSFont* f = [NSFont fontWithName: [[aFont fontDescriptor] postscriptName]
856                        size: 0.0];
857    return [f familyName];
858}
859
860// System fonts under OSX 10.11 use a combination of two families, one
861// for text sizes and another for larger, display sizes. Each has a
862// different number of weights. There aren't efficient API's for looking
863// this information up, so hard code the logic here but confirm via
864// debug assertions that the logic is correct.
865
866const CGFloat kTextDisplayCrossover = 20.0; // use text family below this size
867
868void
869gfxMacPlatformFontList::InitSystemFontNames()
870{
871    // system font under 10.11 are two distinct families for text/display sizes
872    if (nsCocoaFeatures::OnElCapitanOrLater()) {
873        mUseSizeSensitiveSystemFont = true;
874    }
875
876    // text font family
877    NSFont* sys = [NSFont systemFontOfSize: 0.0];
878    NSString* textFamilyName = GetRealFamilyName(sys);
879    nsAutoString familyName;
880    nsCocoaUtils::GetStringForNSString(textFamilyName, familyName);
881    mSystemTextFontFamilyName = familyName;
882
883    // display font family, if on OSX 10.11
884    if (mUseSizeSensitiveSystemFont) {
885        NSFont* displaySys = [NSFont systemFontOfSize: 128.0];
886        NSString* displayFamilyName = GetRealFamilyName(displaySys);
887        nsCocoaUtils::GetStringForNSString(displayFamilyName, familyName);
888        mSystemDisplayFontFamilyName = familyName;
889
890#if DEBUG
891        // confirm that the optical size switch is at 20.0
892        NS_ASSERTION([textFamilyName compare:displayFamilyName] != NSOrderedSame,
893                     "system text/display fonts are the same!");
894        NSString* fam19 = GetRealFamilyName([NSFont systemFontOfSize:
895                                             (kTextDisplayCrossover - 1.0)]);
896        NSString* fam20 = GetRealFamilyName([NSFont systemFontOfSize:
897                                             kTextDisplayCrossover]);
898        NS_ASSERTION(fam19 && fam20 && [fam19 compare:fam20] != NSOrderedSame,
899                     "system text/display font size switch point is not as expected!");
900#endif
901    }
902
903#ifdef DEBUG
904    // different system font API's always map to the same family under OSX, so
905    // just assume that and emit a warning if that ever changes
906    NSString *sysFamily = GetRealFamilyName([NSFont systemFontOfSize:0.0]);
907    if ([sysFamily compare:GetRealFamilyName([NSFont boldSystemFontOfSize:0.0])] != NSOrderedSame ||
908        [sysFamily compare:GetRealFamilyName([NSFont controlContentFontOfSize:0.0])] != NSOrderedSame ||
909        [sysFamily compare:GetRealFamilyName([NSFont menuBarFontOfSize:0.0])] != NSOrderedSame ||
910        [sysFamily compare:GetRealFamilyName([NSFont toolTipsFontOfSize:0.0])] != NSOrderedSame) {
911        NS_WARNING("system font types map to different font families"
912                   " -- please log a bug!!");
913    }
914#endif
915}
916
917gfxFontFamily*
918gfxMacPlatformFontList::FindSystemFontFamily(const nsAString& aFamily)
919{
920    nsAutoString key;
921    GenerateFontListKey(aFamily, key);
922
923    gfxFontFamily* familyEntry;
924
925    // lookup in hidden system family name list
926    if ((familyEntry = mSystemFontFamilies.GetWeak(key))) {
927        return CheckFamily(familyEntry);
928    }
929
930    // lookup in user-exposed family name list
931    if ((familyEntry = mFontFamilies.GetWeak(key))) {
932        return CheckFamily(familyEntry);
933    }
934
935    return nullptr;
936}
937
938bool
939gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
940{
941    gfxFontFamily *family = FindFamily(aFontName);
942    if (family) {
943        family->LocalizedName(aFamilyName);
944        return true;
945    }
946
947    return false;
948}
949
950void
951gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
952                                                                   void *observer,
953                                                                   CFStringRef name,
954                                                                   const void *object,
955                                                                   CFDictionaryRef userInfo)
956{
957    if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
958        return;
959    }
960
961    gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
962
963    // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
964    fl->UpdateFontList();
965
966    // modify a preference that will trigger reflow everywhere
967    fl->ForceGlobalReflow();
968}
969
970gfxFontEntry*
971gfxMacPlatformFontList::PlatformGlobalFontFallback(const uint32_t aCh,
972                                                   Script aRunScript,
973                                                   const gfxFontStyle* aMatchStyle,
974                                                   gfxFontFamily** aMatchedFamily)
975{
976    CFStringRef str;
977    UniChar ch[2];
978    CFIndex length = 1;
979
980    if (IS_IN_BMP(aCh)) {
981        ch[0] = aCh;
982        str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
983                                                   kCFAllocatorNull);
984    } else {
985        ch[0] = H_SURROGATE(aCh);
986        ch[1] = L_SURROGATE(aCh);
987        str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
988                                                   kCFAllocatorNull);
989        if (!str) {
990            return nullptr;
991        }
992        length = 2;
993    }
994
995    // use CoreText to find the fallback family
996
997    gfxFontEntry *fontEntry = nullptr;
998    CTFontRef fallback;
999    bool cantUseFallbackFont = false;
1000
1001    if (!mDefaultFont) {
1002        mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f,
1003                                              NULL);
1004    }
1005
1006    fallback = ::CTFontCreateForString(mDefaultFont, str,
1007                                       ::CFRangeMake(0, length));
1008
1009    if (fallback) {
1010        CFStringRef familyNameRef = ::CTFontCopyFamilyName(fallback);
1011        ::CFRelease(fallback);
1012
1013        if (familyNameRef &&
1014            ::CFStringCompare(familyNameRef, CFSTR("LastResort"),
1015                              kCFCompareCaseInsensitive) != kCFCompareEqualTo)
1016        {
1017            AutoTArray<UniChar, 1024> buffer;
1018            CFIndex familyNameLen = ::CFStringGetLength(familyNameRef);
1019            buffer.SetLength(familyNameLen+1);
1020            ::CFStringGetCharacters(familyNameRef, ::CFRangeMake(0, familyNameLen),
1021                                    buffer.Elements());
1022            buffer[familyNameLen] = 0;
1023            nsDependentString familyNameString(reinterpret_cast<char16_t*>(buffer.Elements()), familyNameLen);
1024
1025            bool needsBold;  // ignored in the system fallback case
1026
1027            gfxFontFamily *family = FindFamily(familyNameString);
1028            if (family) {
1029                fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
1030                if (fontEntry) {
1031                    if (fontEntry->HasCharacter(aCh)) {
1032                        *aMatchedFamily = family;
1033                    } else {
1034                        fontEntry = nullptr;
1035                        cantUseFallbackFont = true;
1036                    }
1037                }
1038            }
1039        }
1040
1041        if (familyNameRef) {
1042            ::CFRelease(familyNameRef);
1043        }
1044    }
1045
1046    if (cantUseFallbackFont) {
1047        Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
1048    }
1049
1050    ::CFRelease(str);
1051
1052    return fontEntry;
1053}
1054
1055gfxFontFamily*
1056gfxMacPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
1057{
1058    nsAutoreleasePool localPool;
1059
1060    NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
1061    nsAutoString familyName;
1062
1063    GetStringForNSString(defaultFamily, familyName);
1064    return FindFamily(familyName);
1065}
1066
1067int32_t
1068gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight)
1069{
1070    if (aAppleWeight < 1)
1071        aAppleWeight = 1;
1072    else if (aAppleWeight > kAppleMaxWeight)
1073        aAppleWeight = kAppleMaxWeight;
1074    return gAppleWeightToCSSWeight[aAppleWeight];
1075}
1076
1077gfxFontEntry*
1078gfxMacPlatformFontList::LookupLocalFont(const nsAString& aFontName,
1079                                        uint16_t aWeight,
1080                                        int16_t aStretch,
1081                                        uint8_t aStyle)
1082{
1083    nsAutoreleasePool localPool;
1084
1085    NSString *faceName = GetNSStringForString(aFontName);
1086    MacOSFontEntry *newFontEntry;
1087
1088    // lookup face based on postscript or full name
1089    CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
1090    if (!fontRef) {
1091        return nullptr;
1092    }
1093
1094    NS_ASSERTION(aWeight >= 100 && aWeight <= 900,
1095                 "bogus font weight value!");
1096
1097    newFontEntry =
1098        new MacOSFontEntry(aFontName, fontRef, aWeight, aStretch, aStyle,
1099                           false, true);
1100    ::CFRelease(fontRef);
1101
1102    return newFontEntry;
1103}
1104
1105static void ReleaseData(void *info, const void *data, size_t size)
1106{
1107    free((void*)data);
1108}
1109
1110gfxFontEntry*
1111gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName,
1112                                         uint16_t aWeight,
1113                                         int16_t aStretch,
1114                                         uint8_t aStyle,
1115                                         const uint8_t* aFontData,
1116                                         uint32_t aLength)
1117{
1118    NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
1119
1120    NS_ASSERTION(aWeight >= 100 && aWeight <= 900, "bogus font weight value!");
1121
1122    // create the font entry
1123    nsAutoString uniqueName;
1124
1125    nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
1126    if (NS_FAILED(rv)) {
1127        return nullptr;
1128    }
1129
1130    CGDataProviderRef provider =
1131        ::CGDataProviderCreateWithData(nullptr, aFontData, aLength,
1132                                       &ReleaseData);
1133    CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
1134    ::CGDataProviderRelease(provider);
1135
1136    if (!fontRef) {
1137        return nullptr;
1138    }
1139
1140    auto newFontEntry =
1141        MakeUnique<MacOSFontEntry>(uniqueName, fontRef, aWeight, aStretch,
1142                                   aStyle, true, false);
1143    ::CFRelease(fontRef);
1144
1145    // if succeeded and font cmap is good, return the new font
1146    if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
1147        return newFontEntry.release();
1148    }
1149
1150    // if something is funky about this font, delete immediately
1151
1152#if DEBUG
1153    NS_WARNING("downloaded font not loaded properly");
1154#endif
1155
1156    return nullptr;
1157}
1158
1159// Webkit code uses a system font meta name, so mimic that here
1160// WebCore/platform/graphics/mac/FontCacheMac.mm
1161static const char kSystemFont_system[] = "-apple-system";
1162
1163bool
1164gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
1165                                           nsTArray<gfxFontFamily*>* aOutput,
1166                                           gfxFontStyle* aStyle,
1167                                           gfxFloat aDevToCssSize)
1168{
1169    // search for special system font name, -apple-system
1170    if (aFamily.EqualsLiteral(kSystemFont_system)) {
1171        if (mUseSizeSensitiveSystemFont &&
1172            aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
1173            aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName));
1174            return true;
1175        }
1176        aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName));
1177        return true;
1178    }
1179
1180    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
1181                                                   aDevToCssSize);
1182}
1183
1184void
1185gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
1186                                         nsAString& aSystemFontName,
1187                                         gfxFontStyle &aFontStyle,
1188                                         float aDevPixPerCSSPixel)
1189{
1190    // code moved here from widget/cocoa/nsLookAndFeel.mm
1191    NSFont *font = nullptr;
1192    char* systemFontName = nullptr;
1193    switch (aSystemFontID) {
1194        case LookAndFeel::eFont_MessageBox:
1195        case LookAndFeel::eFont_StatusBar:
1196        case LookAndFeel::eFont_List:
1197        case LookAndFeel::eFont_Field:
1198        case LookAndFeel::eFont_Button:
1199        case LookAndFeel::eFont_Widget:
1200            font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
1201            systemFontName = (char*) kSystemFont_system;
1202            break;
1203
1204        case LookAndFeel::eFont_SmallCaption:
1205            font = [NSFont boldSystemFontOfSize:[NSFont smallSystemFontSize]];
1206            systemFontName = (char*) kSystemFont_system;
1207            break;
1208
1209        case LookAndFeel::eFont_Icon: // used in urlbar; tried labelFont, but too small
1210        case LookAndFeel::eFont_Workspace:
1211        case LookAndFeel::eFont_Desktop:
1212        case LookAndFeel::eFont_Info:
1213            font = [NSFont controlContentFontOfSize:0.0];
1214            systemFontName = (char*) kSystemFont_system;
1215            break;
1216
1217        case LookAndFeel::eFont_PullDownMenu:
1218            font = [NSFont menuBarFontOfSize:0.0];
1219            systemFontName = (char*) kSystemFont_system;
1220            break;
1221
1222        case LookAndFeel::eFont_Tooltips:
1223            font = [NSFont toolTipsFontOfSize:0.0];
1224            systemFontName = (char*) kSystemFont_system;
1225            break;
1226
1227        case LookAndFeel::eFont_Caption:
1228        case LookAndFeel::eFont_Menu:
1229        case LookAndFeel::eFont_Dialog:
1230        default:
1231            font = [NSFont systemFontOfSize:0.0];
1232            systemFontName = (char*) kSystemFont_system;
1233            break;
1234    }
1235    NS_ASSERTION(font, "system font not set");
1236    NS_ASSERTION(systemFontName, "system font name not set");
1237
1238    if (systemFontName) {
1239        aSystemFontName.AssignASCII(systemFontName);
1240    }
1241
1242    NSFontSymbolicTraits traits = [[font fontDescriptor] symbolicTraits];
1243    aFontStyle.style =
1244        (traits & NSFontItalicTrait) ?  NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL;
1245    aFontStyle.weight =
1246        (traits & NSFontBoldTrait) ? NS_FONT_WEIGHT_BOLD : NS_FONT_WEIGHT_NORMAL;
1247    aFontStyle.stretch =
1248        (traits & NSFontExpandedTrait) ?
1249            NS_FONT_STRETCH_EXPANDED : (traits & NSFontCondensedTrait) ?
1250                NS_FONT_STRETCH_CONDENSED : NS_FONT_STRETCH_NORMAL;
1251    // convert size from css pixels to device pixels
1252    aFontStyle.size = [font pointSize] * aDevPixPerCSSPixel;
1253    aFontStyle.systemFont = true;
1254}
1255
1256// used to load system-wide font info on off-main thread
1257class MacFontInfo : public FontInfoData {
1258public:
1259    MacFontInfo(bool aLoadOtherNames,
1260                bool aLoadFaceNames,
1261                bool aLoadCmaps) :
1262        FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
1263    {}
1264
1265    virtual ~MacFontInfo() {}
1266
1267    virtual void Load() {
1268        nsAutoreleasePool localPool;
1269        FontInfoData::Load();
1270    }
1271
1272    // loads font data for all members of a given family
1273    virtual void LoadFontFamilyData(const nsAString& aFamilyName);
1274};
1275
1276void
1277MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
1278{
1279    // family name ==> CTFontDescriptor
1280    NSString *famName = GetNSStringForString(aFamilyName);
1281    CFStringRef family = CFStringRef(famName);
1282
1283    CFMutableDictionaryRef attr =
1284        CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
1285                                  &kCFTypeDictionaryValueCallBacks);
1286    CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
1287    CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
1288    CFRelease(attr);
1289    CFArrayRef matchingFonts =
1290        CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
1291    CFRelease(fd);
1292    if (!matchingFonts) {
1293        return;
1294    }
1295
1296    nsTArray<nsString> otherFamilyNames;
1297    bool hasOtherFamilyNames = true;
1298
1299    // iterate over faces in the family
1300    int f, numFaces = (int) CFArrayGetCount(matchingFonts);
1301    for (f = 0; f < numFaces; f++) {
1302        mLoadStats.fonts++;
1303
1304        CTFontDescriptorRef faceDesc =
1305            (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
1306        if (!faceDesc) {
1307            continue;
1308        }
1309        CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
1310                                                           0.0, nullptr);
1311        if (!fontRef) {
1312            NS_WARNING("failed to create a CTFontRef");
1313            continue;
1314        }
1315
1316        if (mLoadCmaps) {
1317            // face name
1318            CFStringRef faceName = (CFStringRef)
1319                CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
1320
1321            AutoTArray<UniChar, 1024> buffer;
1322            CFIndex len = CFStringGetLength(faceName);
1323            buffer.SetLength(len+1);
1324            CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
1325                                    buffer.Elements());
1326            buffer[len] = 0;
1327            nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
1328                                  len);
1329
1330            // load the cmap data
1331            FontFaceData fontData;
1332            CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
1333                                                  kCTFontTableOptionNoOptions);
1334
1335            if (cmapTable) {
1336                const uint8_t *cmapData =
1337                    (const uint8_t*)CFDataGetBytePtr(cmapTable);
1338                uint32_t cmapLen = CFDataGetLength(cmapTable);
1339                RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1340                uint32_t offset;
1341                bool unicodeFont = false; // ignored
1342                bool symbolFont = false;
1343                nsresult rv;
1344
1345                rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
1346                                            unicodeFont, symbolFont);
1347                if (NS_SUCCEEDED(rv)) {
1348                    fontData.mCharacterMap = charmap;
1349                    fontData.mUVSOffset = offset;
1350                    fontData.mSymbolFont = symbolFont;
1351                    mLoadStats.cmaps++;
1352                }
1353                CFRelease(cmapTable);
1354            }
1355
1356            mFontFaceData.Put(fontName, fontData);
1357            CFRelease(faceName);
1358        }
1359
1360        if (mLoadOtherNames && hasOtherFamilyNames) {
1361            CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
1362                                                  kCTFontTableOptionNoOptions);
1363
1364            if (nameTable) {
1365                const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
1366                uint32_t nameLen = CFDataGetLength(nameTable);
1367                gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
1368                                                           nameData, nameLen,
1369                                                           otherFamilyNames,
1370                                                           false);
1371                hasOtherFamilyNames = otherFamilyNames.Length() != 0;
1372                CFRelease(nameTable);
1373            }
1374        }
1375
1376        CFRelease(fontRef);
1377    }
1378    CFRelease(matchingFonts);
1379
1380    // if found other names, insert them in the hash table
1381    if (otherFamilyNames.Length() != 0) {
1382        mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
1383        mLoadStats.othernames += otherFamilyNames.Length();
1384    }
1385}
1386
1387already_AddRefed<FontInfoData>
1388gfxMacPlatformFontList::CreateFontInfoData()
1389{
1390    bool loadCmaps = !UsesSystemFallback() ||
1391        gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1392
1393    RefPtr<MacFontInfo> fi =
1394        new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1395    return fi.forget();
1396}
1397
1398#ifdef MOZ_BUNDLED_FONTS
1399
1400void
1401gfxMacPlatformFontList::ActivateBundledFonts()
1402{
1403    nsCOMPtr<nsIFile> localDir;
1404    nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
1405    if (NS_FAILED(rv)) {
1406        return;
1407    }
1408    if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
1409        return;
1410    }
1411    bool isDir;
1412    if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
1413        return;
1414    }
1415
1416    nsCOMPtr<nsISimpleEnumerator> e;
1417    rv = localDir->GetDirectoryEntries(getter_AddRefs(e));
1418    if (NS_FAILED(rv)) {
1419        return;
1420    }
1421
1422    bool hasMore;
1423    while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
1424        nsCOMPtr<nsISupports> entry;
1425        if (NS_FAILED(e->GetNext(getter_AddRefs(entry)))) {
1426            break;
1427        }
1428        nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
1429        if (!file) {
1430            continue;
1431        }
1432        nsCString path;
1433        if (NS_FAILED(file->GetNativePath(path))) {
1434            continue;
1435        }
1436        CFURLRef fontURL =
1437            ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1438                                                      (uint8_t*)path.get(),
1439                                                      path.Length(),
1440                                                      false);
1441        if (fontURL) {
1442            CFErrorRef error = nullptr;
1443            ::CTFontManagerRegisterFontsForURL(fontURL,
1444                                               kCTFontManagerScopeProcess,
1445                                               &error);
1446            ::CFRelease(fontURL);
1447        }
1448    }
1449}
1450
1451#endif
1452