[Cocoa] Add SPI to disallow user-installed fonts
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / FontCacheCoreText.cpp
1 /*
2  * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "FontCache.h"
28
29 #include "Font.h"
30 #include <pal/spi/cocoa/CoreTextSPI.h>
31
32 #include <CoreText/SFNTLayoutTypes.h>
33
34 #include <wtf/HashSet.h>
35 #include <wtf/MainThread.h>
36 #include <wtf/NeverDestroyed.h>
37
38 #define SHOULD_USE_CORE_TEXT_FONT_LOOKUP (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
39 #define HAS_CORE_TEXT_WIDTH_ATTRIBUTE ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000))
40 #define CAN_DISALLOW_USER_INSTALLED_FONTS ((PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400))
41
42 namespace WebCore {
43
44 static inline void appendRawTrueTypeFeature(CFMutableArrayRef features, int type, int selector)
45 {
46     auto typeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &type));
47     auto selectorNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &selector));
48     CFTypeRef featureKeys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey };
49     CFTypeRef featureValues[] = { typeNumber.get(), selectorNumber.get() };
50     auto feature = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, featureKeys, featureValues, WTF_ARRAY_LENGTH(featureKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
51     CFArrayAppendValue(features, feature.get());
52 }
53
54 static inline bool tagEquals(FontTag tag, const char comparison[4])
55 {
56     return equalIgnoringASCIICase(tag.data(), comparison, 4);
57 }
58
59 static inline void appendTrueTypeFeature(CFMutableArrayRef features, const FontFeature& feature)
60 {
61     if (tagEquals(feature.tag(), "liga") || tagEquals(feature.tag(), "clig")) {
62         if (feature.enabled()) {
63             appendRawTrueTypeFeature(features, kLigaturesType, kCommonLigaturesOnSelector);
64             appendRawTrueTypeFeature(features, kLigaturesType, kContextualLigaturesOnSelector);
65         } else {
66             appendRawTrueTypeFeature(features, kLigaturesType, kCommonLigaturesOffSelector);
67             appendRawTrueTypeFeature(features, kLigaturesType, kContextualLigaturesOffSelector);
68         }
69     } else if (tagEquals(feature.tag(), "dlig")) {
70         if (feature.enabled())
71             appendRawTrueTypeFeature(features, kLigaturesType, kRareLigaturesOnSelector);
72         else
73             appendRawTrueTypeFeature(features, kLigaturesType, kRareLigaturesOffSelector);
74     } else if (tagEquals(feature.tag(), "hlig")) {
75         if (feature.enabled())
76             appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOnSelector);
77         else
78             appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOffSelector);
79     } else if (tagEquals(feature.tag(), "calt")) {
80         if (feature.enabled())
81             appendRawTrueTypeFeature(features, kContextualAlternatesType, kContextualAlternatesOnSelector);
82         else
83             appendRawTrueTypeFeature(features, kContextualAlternatesType, kContextualAlternatesOffSelector);
84     } else if (tagEquals(feature.tag(), "subs") && feature.enabled())
85         appendRawTrueTypeFeature(features, kVerticalPositionType, kInferiorsSelector);
86     else if (tagEquals(feature.tag(), "sups") && feature.enabled())
87         appendRawTrueTypeFeature(features, kVerticalPositionType, kSuperiorsSelector);
88     else if (tagEquals(feature.tag(), "smcp") && feature.enabled())
89         appendRawTrueTypeFeature(features, kLowerCaseType, kLowerCaseSmallCapsSelector);
90     else if (tagEquals(feature.tag(), "c2sc") && feature.enabled())
91         appendRawTrueTypeFeature(features, kUpperCaseType, kUpperCaseSmallCapsSelector);
92     else if (tagEquals(feature.tag(), "pcap") && feature.enabled())
93         appendRawTrueTypeFeature(features, kLowerCaseType, kLowerCasePetiteCapsSelector);
94     else if (tagEquals(feature.tag(), "c2pc") && feature.enabled())
95         appendRawTrueTypeFeature(features, kUpperCaseType, kUpperCasePetiteCapsSelector);
96     else if (tagEquals(feature.tag(), "unic") && feature.enabled())
97         appendRawTrueTypeFeature(features, kLetterCaseType, 14);
98     else if (tagEquals(feature.tag(), "titl") && feature.enabled())
99         appendRawTrueTypeFeature(features, kStyleOptionsType, kTitlingCapsSelector);
100     else if (tagEquals(feature.tag(), "lnum") && feature.enabled())
101         appendRawTrueTypeFeature(features, kNumberCaseType, kUpperCaseNumbersSelector);
102     else if (tagEquals(feature.tag(), "onum") && feature.enabled())
103         appendRawTrueTypeFeature(features, kNumberCaseType, kLowerCaseNumbersSelector);
104     else if (tagEquals(feature.tag(), "pnum") && feature.enabled())
105         appendRawTrueTypeFeature(features, kNumberSpacingType, kProportionalNumbersSelector);
106     else if (tagEquals(feature.tag(), "tnum") && feature.enabled())
107         appendRawTrueTypeFeature(features, kNumberSpacingType, kMonospacedNumbersSelector);
108     else if (tagEquals(feature.tag(), "frac") && feature.enabled())
109         appendRawTrueTypeFeature(features, kFractionsType, kDiagonalFractionsSelector);
110     else if (tagEquals(feature.tag(), "afrc") && feature.enabled())
111         appendRawTrueTypeFeature(features, kFractionsType, kVerticalFractionsSelector);
112     else if (tagEquals(feature.tag(), "ordn") && feature.enabled())
113         appendRawTrueTypeFeature(features, kVerticalPositionType, kOrdinalsSelector);
114     else if (tagEquals(feature.tag(), "zero") && feature.enabled())
115         appendRawTrueTypeFeature(features, kTypographicExtrasType, kSlashedZeroOnSelector);
116     else if (tagEquals(feature.tag(), "hist") && feature.enabled())
117         appendRawTrueTypeFeature(features, kLigaturesType, kHistoricalLigaturesOnSelector);
118     else if (tagEquals(feature.tag(), "jp78") && feature.enabled())
119         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1978CharactersSelector);
120     else if (tagEquals(feature.tag(), "jp83") && feature.enabled())
121         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1983CharactersSelector);
122     else if (tagEquals(feature.tag(), "jp90") && feature.enabled())
123         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS1990CharactersSelector);
124     else if (tagEquals(feature.tag(), "jp04") && feature.enabled())
125         appendRawTrueTypeFeature(features, kCharacterShapeType, kJIS2004CharactersSelector);
126     else if (tagEquals(feature.tag(), "smpl") && feature.enabled())
127         appendRawTrueTypeFeature(features, kCharacterShapeType, kSimplifiedCharactersSelector);
128     else if (tagEquals(feature.tag(), "trad") && feature.enabled())
129         appendRawTrueTypeFeature(features, kCharacterShapeType, kTraditionalCharactersSelector);
130     else if (tagEquals(feature.tag(), "fwid") && feature.enabled())
131         appendRawTrueTypeFeature(features, kTextSpacingType, kMonospacedTextSelector);
132     else if (tagEquals(feature.tag(), "pwid") && feature.enabled())
133         appendRawTrueTypeFeature(features, kTextSpacingType, kProportionalTextSelector);
134     else if (tagEquals(feature.tag(), "ruby") && feature.enabled())
135         appendRawTrueTypeFeature(features, kRubyKanaType, kRubyKanaOnSelector);
136 }
137
138 static inline void appendOpenTypeFeature(CFMutableArrayRef features, const FontFeature& feature)
139 {
140     auto featureKey = adoptCF(CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(feature.tag().data()), feature.tag().size() * sizeof(FontTag::value_type), kCFStringEncodingASCII, false));
141     int rawFeatureValue = feature.value();
142     auto featureValue = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &rawFeatureValue));
143     CFTypeRef featureDictionaryKeys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue };
144     CFTypeRef featureDictionaryValues[] = { featureKey.get(), featureValue.get() };
145     auto featureDictionary = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, featureDictionaryKeys, featureDictionaryValues, WTF_ARRAY_LENGTH(featureDictionaryValues), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
146     CFArrayAppendValue(features, featureDictionary.get());
147 }
148
149 typedef HashMap<FontTag, int, FourCharacterTagHash, FourCharacterTagHashTraits> FeaturesMap;
150 #if ENABLE(VARIATION_FONTS)
151 typedef HashMap<FontTag, float, FourCharacterTagHash, FourCharacterTagHashTraits> VariationsMap;
152 #endif
153
154 static FeaturesMap computeFeatureSettingsFromVariants(const FontVariantSettings& variantSettings)
155 {
156     FeaturesMap result;
157
158     switch (variantSettings.commonLigatures) {
159     case FontVariantLigatures::Normal:
160         break;
161     case FontVariantLigatures::Yes:
162         result.add(fontFeatureTag("liga"), 1);
163         result.add(fontFeatureTag("clig"), 1);
164         break;
165     case FontVariantLigatures::No:
166         result.add(fontFeatureTag("liga"), 0);
167         result.add(fontFeatureTag("clig"), 0);
168         break;
169     default:
170         ASSERT_NOT_REACHED();
171     }
172
173     switch (variantSettings.discretionaryLigatures) {
174     case FontVariantLigatures::Normal:
175         break;
176     case FontVariantLigatures::Yes:
177         result.add(fontFeatureTag("dlig"), 1);
178         break;
179     case FontVariantLigatures::No:
180         result.add(fontFeatureTag("dlig"), 0);
181         break;
182     default:
183         ASSERT_NOT_REACHED();
184     }
185
186     switch (variantSettings.historicalLigatures) {
187     case FontVariantLigatures::Normal:
188         break;
189     case FontVariantLigatures::Yes:
190         result.add(fontFeatureTag("hlig"), 1);
191         break;
192     case FontVariantLigatures::No:
193         result.add(fontFeatureTag("hlig"), 0);
194         break;
195     default:
196         ASSERT_NOT_REACHED();
197     }
198
199     switch (variantSettings.contextualAlternates) {
200     case FontVariantLigatures::Normal:
201         break;
202     case FontVariantLigatures::Yes:
203         result.add(fontFeatureTag("calt"), 1);
204         break;
205     case FontVariantLigatures::No:
206         result.add(fontFeatureTag("calt"), 0);
207         break;
208     default:
209         ASSERT_NOT_REACHED();
210     }
211
212     switch (variantSettings.position) {
213     case FontVariantPosition::Normal:
214         break;
215     case FontVariantPosition::Subscript:
216         result.add(fontFeatureTag("subs"), 1);
217         break;
218     case FontVariantPosition::Superscript:
219         result.add(fontFeatureTag("sups"), 1);
220         break;
221     default:
222         ASSERT_NOT_REACHED();
223     }
224
225     switch (variantSettings.caps) {
226     case FontVariantCaps::Normal:
227         break;
228     case FontVariantCaps::AllSmall:
229         result.add(fontFeatureTag("c2sc"), 1);
230         FALLTHROUGH;
231     case FontVariantCaps::Small:
232         result.add(fontFeatureTag("smcp"), 1);
233         break;
234     case FontVariantCaps::AllPetite:
235         result.add(fontFeatureTag("c2pc"), 1);
236         FALLTHROUGH;
237     case FontVariantCaps::Petite:
238         result.add(fontFeatureTag("pcap"), 1);
239         break;
240     case FontVariantCaps::Unicase:
241         result.add(fontFeatureTag("unic"), 1);
242         break;
243     case FontVariantCaps::Titling:
244         result.add(fontFeatureTag("titl"), 1);
245         break;
246     default:
247         ASSERT_NOT_REACHED();
248     }
249
250     switch (variantSettings.numericFigure) {
251     case FontVariantNumericFigure::Normal:
252         break;
253     case FontVariantNumericFigure::LiningNumbers:
254         result.add(fontFeatureTag("lnum"), 1);
255         break;
256     case FontVariantNumericFigure::OldStyleNumbers:
257         result.add(fontFeatureTag("onum"), 1);
258         break;
259     default:
260         ASSERT_NOT_REACHED();
261     }
262
263     switch (variantSettings.numericSpacing) {
264     case FontVariantNumericSpacing::Normal:
265         break;
266     case FontVariantNumericSpacing::ProportionalNumbers:
267         result.add(fontFeatureTag("pnum"), 1);
268         break;
269     case FontVariantNumericSpacing::TabularNumbers:
270         result.add(fontFeatureTag("tnum"), 1);
271         break;
272     default:
273         ASSERT_NOT_REACHED();
274     }
275
276     switch (variantSettings.numericFraction) {
277     case FontVariantNumericFraction::Normal:
278         break;
279     case FontVariantNumericFraction::DiagonalFractions:
280         result.add(fontFeatureTag("frac"), 1);
281         break;
282     case FontVariantNumericFraction::StackedFractions:
283         result.add(fontFeatureTag("afrc"), 1);
284         break;
285     default:
286         ASSERT_NOT_REACHED();
287     }
288
289     switch (variantSettings.numericOrdinal) {
290     case FontVariantNumericOrdinal::Normal:
291         break;
292     case FontVariantNumericOrdinal::Yes:
293         result.add(fontFeatureTag("ordn"), 1);
294         break;
295     default:
296         ASSERT_NOT_REACHED();
297     }
298
299     switch (variantSettings.numericSlashedZero) {
300     case FontVariantNumericSlashedZero::Normal:
301         break;
302     case FontVariantNumericSlashedZero::Yes:
303         result.add(fontFeatureTag("zero"), 1);
304         break;
305     default:
306         ASSERT_NOT_REACHED();
307     }
308
309     switch (variantSettings.alternates) {
310     case FontVariantAlternates::Normal:
311         break;
312     case FontVariantAlternates::HistoricalForms:
313         result.add(fontFeatureTag("hist"), 1);
314         break;
315     default:
316         ASSERT_NOT_REACHED();
317     }
318
319     switch (variantSettings.eastAsianVariant) {
320     case FontVariantEastAsianVariant::Normal:
321         break;
322     case FontVariantEastAsianVariant::Jis78:
323         result.add(fontFeatureTag("jp78"), 1);
324         break;
325     case FontVariantEastAsianVariant::Jis83:
326         result.add(fontFeatureTag("jp83"), 1);
327         break;
328     case FontVariantEastAsianVariant::Jis90:
329         result.add(fontFeatureTag("jp90"), 1);
330         break;
331     case FontVariantEastAsianVariant::Jis04:
332         result.add(fontFeatureTag("jp04"), 1);
333         break;
334     case FontVariantEastAsianVariant::Simplified:
335         result.add(fontFeatureTag("smpl"), 1);
336         break;
337     case FontVariantEastAsianVariant::Traditional:
338         result.add(fontFeatureTag("trad"), 1);
339         break;
340     default:
341         ASSERT_NOT_REACHED();
342     }
343
344     switch (variantSettings.eastAsianWidth) {
345     case FontVariantEastAsianWidth::Normal:
346         break;
347     case FontVariantEastAsianWidth::Full:
348         result.add(fontFeatureTag("fwid"), 1);
349         break;
350     case FontVariantEastAsianWidth::Proportional:
351         result.add(fontFeatureTag("pwid"), 1);
352         break;
353     default:
354         ASSERT_NOT_REACHED();
355     }
356
357     switch (variantSettings.eastAsianRuby) {
358     case FontVariantEastAsianRuby::Normal:
359         break;
360     case FontVariantEastAsianRuby::Yes:
361         result.add(fontFeatureTag("ruby"), 1);
362         break;
363     default:
364         ASSERT_NOT_REACHED();
365     }
366
367     return result;
368 }
369
370 #if ENABLE(VARIATION_FONTS)
371 struct VariationDefaults {
372     float defaultValue;
373     float minimumValue;
374     float maximumValue;
375 };
376
377 typedef HashMap<FontTag, VariationDefaults, FourCharacterTagHash, FourCharacterTagHashTraits> VariationDefaultsMap;
378
379 static VariationDefaultsMap defaultVariationValues(CTFontRef font)
380 {
381     VariationDefaultsMap result;
382     auto axes = adoptCF(CTFontCopyVariationAxes(font));
383     if (!axes)
384         return result;
385     auto size = CFArrayGetCount(axes.get());
386     for (CFIndex i = 0; i < size; ++i) {
387         CFDictionaryRef axis = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(axes.get(), i));
388         CFNumberRef axisIdentifier = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey));
389         CFNumberRef defaultValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisDefaultValueKey));
390         CFNumberRef minimumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey));
391         CFNumberRef maximumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey));
392         uint32_t rawAxisIdentifier = 0;
393         Boolean success = CFNumberGetValue(axisIdentifier, kCFNumberSInt32Type, &rawAxisIdentifier);
394         ASSERT_UNUSED(success, success);
395         float rawDefaultValue = 0;
396         float rawMinimumValue = 0;
397         float rawMaximumValue = 0;
398         CFNumberGetValue(defaultValue, kCFNumberFloatType, &rawDefaultValue);
399         CFNumberGetValue(minimumValue, kCFNumberFloatType, &rawMinimumValue);
400         CFNumberGetValue(maximumValue, kCFNumberFloatType, &rawMaximumValue);
401
402         if (rawMinimumValue > rawMaximumValue)
403             std::swap(rawMinimumValue, rawMaximumValue);
404
405         auto b1 = rawAxisIdentifier >> 24;
406         auto b2 = (rawAxisIdentifier & 0xFF0000) >> 16;
407         auto b3 = (rawAxisIdentifier & 0xFF00) >> 8;
408         auto b4 = rawAxisIdentifier & 0xFF;
409         FontTag resultKey = {{ static_cast<char>(b1), static_cast<char>(b2), static_cast<char>(b3), static_cast<char>(b4) }};
410         VariationDefaults resultValues = { rawDefaultValue, rawMinimumValue, rawMaximumValue };
411         result.set(resultKey, resultValues);
412     }
413     return result;
414 }
415
416 static inline bool fontIsSystemFont(CTFontRef font)
417 {
418     if (CTFontDescriptorIsSystemUIFont(adoptCF(CTFontCopyFontDescriptor(font)).get()))
419         return true;
420     auto name = adoptCF(CTFontCopyPostScriptName(font));
421     return CFStringGetLength(name.get()) > 0 && CFStringGetCharacterAtIndex(name.get(), 0) == '.';
422 }
423
424 // These values were calculated by performing a linear regression on the CSS weights/widths/slopes and Core Text weights/widths/slopes of San Francisco.
425 // FIXME: <rdar://problem/31312602> Get the real values from Core Text.
426 static inline float normalizeWeight(float value)
427 {
428     return 523.7 * value - 109.3;
429 }
430
431 static inline float normalizeSlope(float value)
432 {
433     return value * 300;
434 }
435
436 static inline float denormalizeWeight(float value)
437 {
438     return (value + 109.3) / 523.7;
439 }
440
441 static inline float denormalizeSlope(float value)
442 {
443     return value / 300;
444 }
445
446 static inline float denormalizeVariationWidth(float value)
447 {
448     if (value <= 125)
449         return value / 100;
450     if (value <= 150)
451         return (value + 125) / 200;
452     return (value + 400) / 400;
453 }
454 #endif
455
456 #if ENABLE(VARIATION_FONTS) || !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
457 static inline float normalizeVariationWidth(float value)
458 {
459     if (value <= 1.25)
460         return value * 100;
461     if (value <= 1.375)
462         return value * 200 - 125;
463     return value * 400 - 400;
464 }
465 #endif
466
467 #if !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
468 static inline float normalizeWidth(float value)
469 {
470     return normalizeVariationWidth(value + 1);
471 }
472 #endif
473
474 struct FontType {
475     FontType(CTFontRef font)
476     {
477         auto tables = adoptCF(CTFontCopyAvailableTables(font, kCTFontTableOptionNoOptions));
478         if (!tables)
479             return;
480         auto size = CFArrayGetCount(tables.get());
481         for (CFIndex i = 0; i < size; ++i) {
482             // This is so yucky.
483             // https://developer.apple.com/reference/coretext/1510774-ctfontcopyavailabletables
484             // "The returned set will contain unboxed values, which can be extracted like so:"
485             // "CTFontTableTag tag = (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tags, index);"
486             CTFontTableTag tableTag = static_cast<CTFontTableTag>(reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tables.get(), i)));
487             switch (tableTag) {
488             case 'fvar':
489                 if (variationType == VariationType::NotVariable)
490                     variationType = VariationType::TrueTypeGX;
491                 break;
492             case 'STAT':
493                 variationType = VariationType::OpenType18;
494                 break;
495             case 'morx':
496             case 'mort':
497                 aatShaping = true;
498                 break;
499             case 'GPOS':
500             case 'GSUB':
501                 openTypeShaping = true;
502                 break;
503             }
504         }
505     }
506
507     enum class VariationType {
508         NotVariable,
509         TrueTypeGX,
510         OpenType18
511     };
512     VariationType variationType { VariationType::NotVariable };
513     bool openTypeShaping { false };
514     bool aatShaping { false };
515 };
516
517 RetainPtr<CTFontRef> preparePlatformFont(CTFontRef originalFont, const FontDescription& fontDescription, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities, float size, bool applyWeightWidthSlopeVariations)
518 {
519     bool alwaysAddVariations = false;
520
521     // FIXME: Remove when <rdar://problem/29859207> is fixed
522 #if ENABLE(VARIATION_FONTS)
523     auto defaultValues = defaultVariationValues(originalFont);
524     alwaysAddVariations = !defaultValues.isEmpty();
525
526     auto fontSelectionRequest = fontDescription.fontSelectionRequest();
527     auto fontOpticalSizing = fontDescription.opticalSizing();
528     auto fontStyleAxis = fontDescription.fontStyleAxis();
529 #else
530     UNUSED_PARAM(fontFaceCapabilities);
531     UNUSED_PARAM(size);
532     UNUSED_PARAM(applyWeightWidthSlopeVariations);
533 #endif
534
535     const auto& features = fontDescription.featureSettings();
536     const auto& variantSettings = fontDescription.variantSettings();
537     const auto& variations = fontDescription.variationSettings();
538     auto textRenderingMode = fontDescription.textRenderingMode();
539
540     if (!originalFont || (!features.size() && (!alwaysAddVariations && variations.isEmpty()) && (textRenderingMode == AutoTextRendering) && variantSettings.isAllNormal()
541         && (!fontFaceFeatures || !fontFaceFeatures->size()) && (!fontFaceVariantSettings || fontFaceVariantSettings->isAllNormal())))
542         return originalFont;
543
544     // This algorithm is described at http://www.w3.org/TR/css3-fonts/#feature-precedence
545     FeaturesMap featuresToBeApplied;
546
547     // Step 1: CoreText handles default features (such as required ligatures).
548
549     // Step 2: Consult with font-variant-* inside @font-face
550     if (fontFaceVariantSettings)
551         featuresToBeApplied = computeFeatureSettingsFromVariants(*fontFaceVariantSettings);
552
553     // Step 3: Consult with font-feature-settings inside @font-face
554     if (fontFaceFeatures) {
555         for (auto& fontFaceFeature : *fontFaceFeatures)
556             featuresToBeApplied.set(fontFaceFeature.tag(), fontFaceFeature.value());
557     }
558
559     // Step 4: Font-variant
560     for (auto& newFeature : computeFeatureSettingsFromVariants(variantSettings))
561         featuresToBeApplied.set(newFeature.key, newFeature.value);
562
563     // Step 5: Other properties (text-rendering)
564     if (textRenderingMode == OptimizeSpeed) {
565         featuresToBeApplied.set(fontFeatureTag("liga"), 0);
566         featuresToBeApplied.set(fontFeatureTag("clig"), 0);
567         featuresToBeApplied.set(fontFeatureTag("dlig"), 0);
568         featuresToBeApplied.set(fontFeatureTag("hlig"), 0);
569         featuresToBeApplied.set(fontFeatureTag("calt"), 0);
570     }
571
572     // Step 6: Font-feature-settings
573     for (auto& newFeature : features)
574         featuresToBeApplied.set(newFeature.tag(), newFeature.value());
575
576     FontType fontType(originalFont);
577
578 #if ENABLE(VARIATION_FONTS)
579     VariationsMap variationsToBeApplied;
580
581     bool needsConversion = fontType.variationType == FontType::VariationType::TrueTypeGX;
582
583     auto applyVariation = [&](const FontTag& tag, float value) {
584         auto iterator = defaultValues.find(tag);
585         if (iterator == defaultValues.end())
586             return;
587         float valueToApply = clampTo(value, iterator->value.minimumValue, iterator->value.maximumValue);
588         variationsToBeApplied.set(tag, valueToApply);
589     };
590
591     // The system font is somewhat magical. Don't mess with its variations.
592     if (applyWeightWidthSlopeVariations && !fontIsSystemFont(originalFont)) {
593         float weight = fontSelectionRequest.weight;
594         float width = fontSelectionRequest.width;
595         float slope = fontSelectionRequest.slope;
596         if (auto weightValue = fontFaceCapabilities.weight)
597             weight = std::max(std::min(weight, static_cast<float>(weightValue->maximum)), static_cast<float>(weightValue->minimum));
598         if (auto widthValue = fontFaceCapabilities.width)
599             width = std::max(std::min(width, static_cast<float>(widthValue->maximum)), static_cast<float>(widthValue->minimum));
600         if (auto slopeValue = fontFaceCapabilities.weight)
601             slope = std::max(std::min(slope, static_cast<float>(slopeValue->maximum)), static_cast<float>(slopeValue->minimum));
602         if (needsConversion) {
603             weight = denormalizeWeight(weight);
604             width = denormalizeVariationWidth(width);
605             slope = denormalizeSlope(slope);
606         }
607         applyVariation({{'w', 'g', 'h', 't'}}, weight);
608         applyVariation({{'w', 'd', 't', 'h'}}, width);
609         if (fontStyleAxis == FontStyleAxis::ital)
610             applyVariation({{'i', 't', 'a', 'l'}}, 1);
611         else
612             applyVariation({{'s', 'l', 'n', 't'}}, slope);
613     }
614
615     if (fontOpticalSizing == FontOpticalSizing::Enabled) {
616         const float pxToPtRatio = 3.0f / 4;
617         applyVariation({{'o', 'p', 's', 'z'}}, size * pxToPtRatio);
618     }
619
620     for (auto& newVariation : variations)
621         applyVariation(newVariation.tag(), newVariation.value());
622
623 #endif // ENABLE(VARIATION_FONTS)
624
625     auto attributes = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
626     if (!featuresToBeApplied.isEmpty()) {
627         auto featureArray = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, features.size(), &kCFTypeArrayCallBacks));
628         for (auto& p : featuresToBeApplied) {
629             auto feature = FontFeature(p.key, p.value);
630             if (fontType.aatShaping)
631                 appendTrueTypeFeature(featureArray.get(), feature);
632             if (fontType.openTypeShaping)
633                 appendOpenTypeFeature(featureArray.get(), feature);
634         }
635         CFDictionaryAddValue(attributes.get(), kCTFontFeatureSettingsAttribute, featureArray.get());
636     }
637
638 #if ENABLE(VARIATION_FONTS)
639     if (!variationsToBeApplied.isEmpty()) {
640         auto variationDictionary = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
641         for (auto& p : variationsToBeApplied) {
642             long long bitwiseTag = p.key[0] << 24 | p.key[1] << 16 | p.key[2] << 8 | p.key[3];
643             auto tagNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &bitwiseTag));
644             auto valueNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &p.value));
645             CFDictionarySetValue(variationDictionary.get(), tagNumber.get(), valueNumber.get());
646         }
647         CFDictionaryAddValue(attributes.get(), kCTFontVariationAttribute, variationDictionary.get());
648     }
649 #endif
650
651     if (textRenderingMode == OptimizeLegibility) {
652         CGFloat size = CTFontGetSize(originalFont);
653         auto sizeNumber = adoptCF(CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &size));
654         CFDictionaryAddValue(attributes.get(), kCTFontOpticalSizeAttribute, sizeNumber.get());
655     }
656     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
657     auto result = adoptCF(CTFontCreateCopyWithAttributes(originalFont, CTFontGetSize(originalFont), nullptr, descriptor.get()));
658     return result;
659 }
660
661 RefPtr<Font> FontCache::similarFont(const FontDescription& description, const AtomicString& family)
662 {
663     // Attempt to find an appropriate font using a match based on the presence of keywords in
664     // the requested names. For example, we'll match any name that contains "Arabic" to Geeza Pro.
665     if (family.isEmpty())
666         return nullptr;
667
668 #if PLATFORM(IOS)
669     // Substitute the default monospace font for well-known monospace fonts.
670     if (equalLettersIgnoringASCIICase(family, "monaco") || equalLettersIgnoringASCIICase(family, "menlo")) {
671         static NeverDestroyed<AtomicString> courier("courier", AtomicString::ConstructFromLiteral);
672         return fontForFamily(description, courier);
673     }
674
675     // Substitute Verdana for Lucida Grande.
676     if (equalLettersIgnoringASCIICase(family, "lucida grande")) {
677         static NeverDestroyed<AtomicString> verdana("verdana", AtomicString::ConstructFromLiteral);
678         return fontForFamily(description, verdana);
679     }
680 #endif
681
682     static NeverDestroyed<String> arabic(MAKE_STATIC_STRING_IMPL("Arabic"));
683     static NeverDestroyed<String> pashto(MAKE_STATIC_STRING_IMPL("Pashto"));
684     static NeverDestroyed<String> urdu(MAKE_STATIC_STRING_IMPL("Urdu"));
685     static String* matchWords[3] = { &arabic.get(), &pashto.get(), &urdu.get() };
686     static NeverDestroyed<AtomicString> geezaPlain("GeezaPro", AtomicString::ConstructFromLiteral);
687     static NeverDestroyed<AtomicString> geezaBold("GeezaPro-Bold", AtomicString::ConstructFromLiteral);
688     for (String* matchWord : matchWords) {
689         if (family.containsIgnoringASCIICase(*matchWord))
690             return fontForFamily(description, isFontWeightBold(description.weight()) ? geezaBold : geezaPlain);
691     }
692     return nullptr;
693 }
694
695 #if !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
696 static float stretchFromCoreTextTraits(CFDictionaryRef traits)
697 {
698     auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits, kCTFontWidthTrait));
699     if (!widthNumber)
700         return normalStretchValue();
701
702     float ctWidth;
703     auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
704     ASSERT_UNUSED(success, success);
705     return normalizeWidth(ctWidth);
706 }
707 #endif
708
709 static void invalidateFontCache();
710
711 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void *, CFDictionaryRef)
712 {
713     ASSERT_UNUSED(observer, observer == &FontCache::singleton());
714
715     invalidateFontCache();
716 }
717
718 void FontCache::platformInit()
719 {
720     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, &fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately);
721
722 #if PLATFORM(MAC)
723     CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
724     const CFStringRef notificationName = kCFLocaleCurrentLocaleDidChangeNotification;
725 #else
726     CFNotificationCenterRef center = CFNotificationCenterGetDarwinNotifyCenter();
727     const CFStringRef notificationName = CFSTR("com.apple.language.changed");
728 #endif
729     CFNotificationCenterAddObserver(center, this, &fontCacheRegisteredFontsChangedNotificationCallback, notificationName, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately);
730 }
731
732 Vector<String> FontCache::systemFontFamilies()
733 {
734     // FIXME: <rdar://problem/21890188>
735     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, nullptr, nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
736     auto emptyFontDescriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
737     auto matchedDescriptors = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(emptyFontDescriptor.get(), nullptr));
738     if (!matchedDescriptors)
739         return { };
740
741     CFIndex numMatches = CFArrayGetCount(matchedDescriptors.get());
742     if (!numMatches)
743         return { };
744
745     HashSet<String> visited;
746     for (CFIndex i = 0; i < numMatches; ++i) {
747         auto fontDescriptor = static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matchedDescriptors.get(), i));
748         if (auto familyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontFamilyNameAttribute))))
749             visited.add(familyName.get());
750     }
751
752     return copyToVector(visited);
753 }
754
755 static CTFontSymbolicTraits computeTraits(const FontDescription& fontDescription)
756 {
757     CTFontSymbolicTraits traits = 0;
758     if (fontDescription.italic())
759         traits |= kCTFontTraitItalic;
760     if (isFontWeightBold(fontDescription.weight()))
761         traits |= kCTFontTraitBold;
762     return traits;
763 }
764
765 SynthesisPair computeNecessarySynthesis(CTFontRef font, const FontDescription& fontDescription, bool isPlatformFont)
766 {
767 #if PLATFORM(IOS)
768     if (CTFontIsAppleColorEmoji(font))
769         return SynthesisPair(false, false);
770 #endif
771
772     if (isPlatformFont)
773         return SynthesisPair(false, false);
774
775     CTFontSymbolicTraits desiredTraits = computeTraits(fontDescription);
776
777     CTFontSymbolicTraits actualTraits = 0;
778     if (isFontWeightBold(fontDescription.weight()) || isItalic(fontDescription.italic()))
779         actualTraits = CTFontGetSymbolicTraits(font);
780
781     bool needsSyntheticBold = (fontDescription.fontSynthesis() & FontSynthesisWeight) && (desiredTraits & kCTFontTraitBold) && !(actualTraits & kCTFontTraitBold);
782     bool needsSyntheticOblique = (fontDescription.fontSynthesis() & FontSynthesisStyle) && (desiredTraits & kCTFontTraitItalic) && !(actualTraits & kCTFontTraitItalic);
783
784     return SynthesisPair(needsSyntheticBold, needsSyntheticOblique);
785 }
786
787 typedef HashSet<String, ASCIICaseInsensitiveHash> Whitelist;
788 static Whitelist& fontWhitelist()
789 {
790     static NeverDestroyed<Whitelist> whitelist;
791     return whitelist;
792 }
793
794 void FontCache::setFontWhitelist(const Vector<String>& inputWhitelist)
795 {
796     Whitelist& whitelist = fontWhitelist();
797     whitelist.clear();
798     for (auto& item : inputWhitelist)
799         whitelist.add(item);
800 }
801
802 static inline bool isSystemFont(const AtomicString& family)
803 {
804     // AtomicString's operator[] handles out-of-bounds by returning 0.
805     return family[0] == '.';
806 }
807
808 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
809 static float fontWeightFromCoreText(CGFloat weight)
810 {
811     if (weight < -0.6)
812         return 100;
813     if (weight < -0.365)
814         return 200;
815     if (weight < -0.115)
816         return 300;
817     if (weight <  0.130)
818         return 400;
819     if (weight <  0.235)
820         return 500;
821     if (weight <  0.350)
822         return 600;
823     if (weight <  0.500)
824         return 700;
825     if (weight <  0.700)
826         return 800;
827     return 900;
828 }
829 #endif
830
831 class FontDatabase {
832 public:
833 #if !CAN_DISALLOW_USER_INSTALLED_FONTS
834     static FontDatabase& singleton()
835     {
836         static NeverDestroyed<FontDatabase> database(FontCache::AllowUserInstalledFonts::Yes);
837         return database;
838     }
839 #endif
840
841     static FontDatabase& singletonAllowingUserInstalledFonts()
842     {
843 #if CAN_DISALLOW_USER_INSTALLED_FONTS
844         static NeverDestroyed<FontDatabase> database(FontCache::AllowUserInstalledFonts::Yes);
845         return database;
846 #else
847         return singleton();
848 #endif
849     }
850
851     static FontDatabase& singletonDisallowingUserInstalledFonts()
852     {
853 #if CAN_DISALLOW_USER_INSTALLED_FONTS
854         static NeverDestroyed<FontDatabase> database(FontCache::AllowUserInstalledFonts::No);
855         return database;
856 #else
857         return singleton();
858 #endif
859     }
860
861     FontDatabase(const FontDatabase&) = delete;
862     FontDatabase& operator=(const FontDatabase&) = delete;
863
864     struct InstalledFont {
865         InstalledFont() = default;
866
867         InstalledFont(CTFontDescriptorRef fontDescriptor)
868             : fontDescriptor(fontDescriptor)
869             , capabilities(capabilitiesForFontDescriptor(fontDescriptor))
870         {
871         }
872
873         RetainPtr<CTFontDescriptorRef> fontDescriptor;
874         FontSelectionCapabilities capabilities;
875     };
876
877     struct InstalledFontFamily {
878         InstalledFontFamily() = default;
879
880         explicit InstalledFontFamily(Vector<InstalledFont>&& installedFonts)
881             : installedFonts(WTFMove(installedFonts))
882         {
883             for (auto& font : this->installedFonts)
884                 expand(font);
885         }
886
887         void expand(const InstalledFont& installedFont)
888         {
889             capabilities.expand(installedFont.capabilities);
890         }
891
892         bool isEmpty() const
893         {
894             return installedFonts.isEmpty();
895         }
896
897         size_t size() const
898         {
899             return installedFonts.size();
900         }
901
902         Vector<InstalledFont> installedFonts;
903         FontSelectionCapabilities capabilities;
904     };
905
906     const InstalledFontFamily& collectionForFamily(const String& familyName)
907     {
908         auto folded = familyName.foldCase();
909         return m_familyNameToFontDescriptors.ensure(folded, [&] {
910             auto familyNameString = folded.createCFString();
911             RetainPtr<CFDictionaryRef> attributes;
912 #if CAN_DISALLOW_USER_INSTALLED_FONTS
913             if (m_allowUserInstalledFonts == FontCache::AllowUserInstalledFonts::No) {
914                 CFTypeRef keys[] = { kCTFontFamilyNameAttribute, kCTFontUserInstalledAttribute };
915                 CFTypeRef values[] = { familyNameString.get(), kCFBooleanFalse };
916                 attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
917             } else
918 #endif
919             {
920                 CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
921                 CFTypeRef values[] = { familyNameString.get() };
922                 attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
923             }
924             auto fontDescriptorToMatch = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
925             if (auto matches = adoptCF(CTFontDescriptorCreateMatchingFontDescriptors(fontDescriptorToMatch.get(), nullptr))) {
926                 auto count = CFArrayGetCount(matches.get());
927                 Vector<InstalledFont> result;
928                 result.reserveInitialCapacity(count);
929                 for (CFIndex i = 0; i < count; ++i) {
930                     InstalledFont installedFont(static_cast<CTFontDescriptorRef>(CFArrayGetValueAtIndex(matches.get(), i)));
931                     result.uncheckedAppend(WTFMove(installedFont));
932                 }
933                 return InstalledFontFamily(WTFMove(result));
934             }
935             return InstalledFontFamily();
936         }).iterator->value;
937     }
938
939     const InstalledFont& fontForPostScriptName(const AtomicString& postScriptName)
940     {
941         const auto& folded = FontCascadeDescription::foldedFamilyName(postScriptName);
942         return m_postScriptNameToFontDescriptors.ensure(folded, [&] {
943             auto postScriptNameString = folded.createCFString();
944 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
945             CFStringRef nameAttribute = kCTFontPostScriptNameAttribute;
946 #else
947             CFStringRef nameAttribute = kCTFontNameAttribute;
948 #endif
949             RetainPtr<CFDictionaryRef> attributes;
950 #if CAN_DISALLOW_USER_INSTALLED_FONTS
951             if (m_allowUserInstalledFonts == FontCache::AllowUserInstalledFonts::No) {
952                 CFTypeRef keys[] = { kCTFontEnabledAttribute, nameAttribute, kCTFontUserInstalledAttribute };
953                 CFTypeRef values[] = { kCFBooleanTrue, postScriptNameString.get(), kCFBooleanFalse };
954                 attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
955             } else
956 #endif
957             {
958                 CFTypeRef keys[] = { kCTFontEnabledAttribute, nameAttribute };
959                 CFTypeRef values[] = { kCFBooleanTrue, postScriptNameString.get() };
960                 attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
961             }
962             auto fontDescriptorToMatch = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
963             auto match = adoptCF(static_cast<CTFontDescriptorRef>(CTFontDescriptorCreateMatchingFontDescriptor(fontDescriptorToMatch.get(), nullptr)));
964             return InstalledFont(match.get());
965         }).iterator->value;
966     }
967
968     void clear()
969     {
970         m_familyNameToFontDescriptors.clear();
971         m_postScriptNameToFontDescriptors.clear();
972     }
973
974 private:
975     friend class NeverDestroyed<FontDatabase>;
976
977     FontDatabase(FontCache::AllowUserInstalledFonts allowUserInstalledFonts)
978         : m_allowUserInstalledFonts(allowUserInstalledFonts)
979     {
980     }
981
982     HashMap<String, InstalledFontFamily> m_familyNameToFontDescriptors;
983     HashMap<String, InstalledFont> m_postScriptNameToFontDescriptors;
984     FontCache::AllowUserInstalledFonts m_allowUserInstalledFonts;
985 };
986
987 // Because this struct holds intermediate values which may be in the compressed -1 - 1 GX range, we don't want to use the relatively large
988 // quantization of FontSelectionValue. Instead, do this logic with floats.
989 struct MinMax {
990     float minimum;
991     float maximum;
992 };
993
994 struct VariationCapabilities {
995     std::optional<MinMax> weight;
996     std::optional<MinMax> width;
997     std::optional<MinMax> slope;
998 };
999
1000 #if ENABLE(VARIATION_FONTS)
1001 static std::optional<MinMax> extractVariationBounds(CFDictionaryRef axis)
1002 {
1003     CFNumberRef minimumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey));
1004     CFNumberRef maximumValue = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey));
1005     float rawMinimumValue = 0;
1006     float rawMaximumValue = 0;
1007     CFNumberGetValue(minimumValue, kCFNumberFloatType, &rawMinimumValue);
1008     CFNumberGetValue(maximumValue, kCFNumberFloatType, &rawMaximumValue);
1009     if (rawMinimumValue < rawMaximumValue)
1010         return {{ rawMinimumValue, rawMaximumValue }};
1011     return std::nullopt;
1012 }
1013 #endif
1014
1015 static VariationCapabilities variationCapabilitiesForFontDescriptor(CTFontDescriptorRef fontDescriptor)
1016 {
1017     VariationCapabilities result;
1018
1019 #if ENABLE(VARIATION_FONTS)
1020     if (!adoptCF(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontVariationAttribute)))
1021         return result;
1022
1023     auto font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor, 0, nullptr));
1024     auto variations = adoptCF(CTFontCopyVariationAxes(font.get()));
1025     if (!variations)
1026         return result;
1027
1028     auto axisCount = CFArrayGetCount(variations.get());
1029     if (!axisCount)
1030         return result;
1031
1032     for (CFIndex i = 0; i < axisCount; ++i) {
1033         CFDictionaryRef axis = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(variations.get(), i));
1034         CFNumberRef axisIdentifier = static_cast<CFNumberRef>(CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey));
1035         uint32_t rawAxisIdentifier = 0;
1036         Boolean success = CFNumberGetValue(axisIdentifier, kCFNumberSInt32Type, &rawAxisIdentifier);
1037         ASSERT_UNUSED(success, success);
1038         if (rawAxisIdentifier == 0x77676874) // 'wght'
1039             result.weight = extractVariationBounds(axis);
1040         else if (rawAxisIdentifier == 0x77647468) // 'wdth'
1041             result.width = extractVariationBounds(axis);
1042         else if (rawAxisIdentifier == 0x736C6E74) // 'slnt'
1043             result.slope = extractVariationBounds(axis);
1044     }
1045
1046     bool optOutFromGXNormalization = false;
1047 #if ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000))
1048     optOutFromGXNormalization = CTFontDescriptorIsSystemUIFont(fontDescriptor);
1049 #endif
1050
1051     if (FontType(font.get()).variationType == FontType::VariationType::TrueTypeGX && !optOutFromGXNormalization) {
1052         if (result.weight)
1053             result.weight = {{ normalizeWeight(result.weight.value().minimum), normalizeWeight(result.weight.value().maximum) }};
1054         if (result.width)
1055             result.width = {{ normalizeVariationWidth(result.width.value().minimum), normalizeVariationWidth(result.width.value().maximum) }};
1056         if (result.slope)
1057             result.slope = {{ normalizeSlope(result.slope.value().minimum), normalizeSlope(result.slope.value().maximum) }};
1058     }
1059
1060     auto minimum = static_cast<float>(FontSelectionValue::minimumValue());
1061     auto maximum = static_cast<float>(FontSelectionValue::maximumValue());
1062     if (result.weight && (result.weight.value().minimum < minimum || result.weight.value().maximum > maximum))
1063         result.weight = { };
1064     if (result.width && (result.width.value().minimum < minimum || result.width.value().maximum > maximum))
1065         result.width = { };
1066     if (result.slope && (result.slope.value().minimum < minimum || result.slope.value().maximum > maximum))
1067         result.slope = { };
1068 #else
1069     UNUSED_PARAM(fontDescriptor);
1070 #endif
1071
1072     return result;
1073 }
1074
1075 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP || HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1076 static float getCSSAttribute(CTFontDescriptorRef fontDescriptor, const CFStringRef attribute, float fallback)
1077 {
1078     auto number = adoptCF(static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute(fontDescriptor, attribute)));
1079     if (!number)
1080         return fallback;
1081     float cssValue;
1082     auto success = CFNumberGetValue(number.get(), kCFNumberFloatType, &cssValue);
1083     ASSERT_UNUSED(success, success);
1084     return cssValue;
1085 }
1086 #endif
1087
1088 FontSelectionCapabilities capabilitiesForFontDescriptor(CTFontDescriptorRef fontDescriptor)
1089 {
1090     if (!fontDescriptor)
1091         return { };
1092
1093     VariationCapabilities variationCapabilities = variationCapabilitiesForFontDescriptor(fontDescriptor);
1094
1095 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP || !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1096     bool weightOrWidthComeFromTraits = !variationCapabilities.weight || !variationCapabilities.width;
1097 #else
1098     bool weightOrWidthComeFromTraits = false;
1099 #endif
1100
1101     if (!variationCapabilities.slope || weightOrWidthComeFromTraits) {
1102         auto traits = adoptCF(static_cast<CFDictionaryRef>(CTFontDescriptorCopyAttribute(fontDescriptor, kCTFontTraitsAttribute)));
1103         if (traits) {
1104 #if !HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1105             if (!variationCapabilities.width) {
1106                 auto widthValue = stretchFromCoreTextTraits(traits.get());
1107                 variationCapabilities.width = {{ widthValue, widthValue }};
1108             }
1109 #endif
1110
1111             if (!variationCapabilities.slope) {
1112                 auto symbolicTraitsNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
1113                 if (symbolicTraitsNumber) {
1114                     int32_t symbolicTraits;
1115                     auto success = CFNumberGetValue(symbolicTraitsNumber, kCFNumberSInt32Type, &symbolicTraits);
1116                     ASSERT_UNUSED(success, success);
1117                     auto slopeValue = static_cast<float>(symbolicTraits & kCTFontTraitItalic ? italicValue() : normalItalicValue());
1118                     variationCapabilities.slope = {{ slopeValue, slopeValue }};
1119                 } else
1120                     variationCapabilities.slope = {{ static_cast<float>(normalItalicValue()), static_cast<float>(normalItalicValue()) }};
1121             }
1122
1123 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1124             if (!variationCapabilities.weight) {
1125                 auto weightNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontWeightTrait));
1126                 if (weightNumber) {
1127                     CGFloat ctWeight;
1128                     auto success = CFNumberGetValue(weightNumber, kCFNumberCGFloatType, &ctWeight);
1129                     ASSERT_UNUSED(success, success);
1130                     auto weightValue = fontWeightFromCoreText(ctWeight);
1131                     variationCapabilities.weight = {{ weightValue, weightValue }};
1132                 } else
1133                     variationCapabilities.weight = {{ static_cast<float>(normalWeightValue()), static_cast<float>(normalWeightValue()) }};
1134             }
1135 #endif
1136         }
1137     }
1138
1139 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1140     if (!variationCapabilities.weight) {
1141         auto value = getCSSAttribute(fontDescriptor, kCTFontCSSWeightAttribute, static_cast<float>(normalWeightValue()));
1142         variationCapabilities.weight = {{ value, value }};
1143     }
1144 #endif
1145
1146 #if HAS_CORE_TEXT_WIDTH_ATTRIBUTE
1147     if (!variationCapabilities.width) {
1148         auto value = getCSSAttribute(fontDescriptor, kCTFontCSSWidthAttribute, static_cast<float>(normalStretchValue()));
1149         variationCapabilities.width = {{ value, value }};
1150     }
1151 #endif
1152
1153     FontSelectionCapabilities result = {{ FontSelectionValue(variationCapabilities.weight.value().minimum), FontSelectionValue(variationCapabilities.weight.value().maximum) },
1154         { FontSelectionValue(variationCapabilities.width.value().minimum), FontSelectionValue(variationCapabilities.width.value().maximum) },
1155         { FontSelectionValue(variationCapabilities.slope.value().minimum), FontSelectionValue(variationCapabilities.slope.value().maximum) }};
1156     ASSERT(result.weight.isValid());
1157     ASSERT(result.width.isValid());
1158     ASSERT(result.slope.isValid());
1159     return result;
1160 }
1161
1162 #if !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1163 static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, FontSelectionRequest fontSelectionRequest)
1164 {
1165     Vector<FontSelectionCapabilities> capabilities;
1166     capabilities.reserveInitialCapacity(familyFonts.size());
1167     for (auto& font : familyFonts.installedFonts)
1168         capabilities.uncheckedAppend(font.capabilities);
1169     FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequest, capabilities, familyFonts.capabilities);
1170     return &familyFonts.installedFonts[fontSelectionAlgorithm.indexOfBestCapabilities()];
1171 }
1172 #endif
1173
1174 Vector<FontSelectionCapabilities> FontCache::getFontSelectionCapabilitiesInFamily(const AtomicString& familyName, AllowUserInstalledFonts allowUserInstalledFonts)
1175 {
1176     auto& fontDatabase = allowUserInstalledFonts == AllowUserInstalledFonts::Yes ? FontDatabase::singletonAllowingUserInstalledFonts() : FontDatabase::singletonDisallowingUserInstalledFonts();
1177     const auto& fonts = fontDatabase.collectionForFamily(familyName.string());
1178     if (fonts.isEmpty())
1179         return { };
1180
1181     Vector<FontSelectionCapabilities> result;
1182     result.reserveInitialCapacity(fonts.size());
1183     for (const auto& font : fonts.installedFonts)
1184         result.uncheckedAppend(font.capabilities);
1185     return result;
1186 }
1187
1188 struct FontLookup {
1189     RetainPtr<CTFontRef> result;
1190     bool createdFromPostScriptName { false };
1191 };
1192
1193 static FontLookup platformFontLookupWithFamily(const AtomicString& family, FontSelectionRequest request, float size, bool mayRepresentUserInstalledFont)
1194 {
1195     const auto& whitelist = fontWhitelist();
1196     if (!isSystemFont(family) && whitelist.size() && !whitelist.contains(family))
1197         return { nullptr };
1198
1199 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
1200     UNUSED_PARAM(mayRepresentUserInstalledFont);
1201     CTFontSymbolicTraits traits = (isFontWeightBold(request.weight) ? kCTFontTraitBold : 0) | (isItalic(request.slope) ? kCTFontTraitItalic : 0);
1202     return { adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), static_cast<float>(request.weight), traits, size)) };
1203 #else
1204     auto& fontDatabase = mayRepresentUserInstalledFont ? FontDatabase::singletonAllowingUserInstalledFonts() : FontDatabase::singletonDisallowingUserInstalledFonts();
1205     const auto& familyFonts = fontDatabase.collectionForFamily(family.string());
1206     if (familyFonts.isEmpty()) {
1207         // The CSS spec states that font-family only accepts a name of an actual font family. However, in WebKit, we claim to also
1208         // support supplying a PostScript name instead. However, this creates problems when the other properties (font-weight,
1209         // font-style) disagree with the traits of the PostScript-named font. The solution we have come up with is, when the default
1210         // values for font-weight and font-style are supplied, honor the PostScript name, but if font-weight specifies bold or
1211         // font-style specifies italic, then we run the regular matching algorithm on the family of the PostScript font. This way,
1212         // if content simply states "font-family: PostScriptName;" without specifying the other font properties, it will be honored,
1213         // but if a <b> appears as a descendent element, it will be honored too.
1214         const auto& postScriptFont = fontDatabase.fontForPostScriptName(family);
1215         if (!postScriptFont.fontDescriptor)
1216             return { nullptr };
1217         if ((isItalic(request.slope) && !isItalic(postScriptFont.capabilities.slope.maximum))
1218             || (isFontWeightBold(request.weight) && !isFontWeightBold(postScriptFont.capabilities.weight.maximum))) {
1219             auto postScriptFamilyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
1220             if (!postScriptFamilyName)
1221                 return { nullptr };
1222             const auto& familyFonts = fontDatabase.collectionForFamily(String(postScriptFamilyName.get()));
1223             if (familyFonts.isEmpty())
1224                 return { nullptr };
1225             if (const auto* installedFont = findClosestFont(familyFonts, request)) {
1226                 if (!installedFont->fontDescriptor)
1227                     return { nullptr };
1228                 return { adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr)), true };
1229             }
1230             return { nullptr };
1231         }
1232         return { adoptCF(CTFontCreateWithFontDescriptor(postScriptFont.fontDescriptor.get(), size, nullptr)), true };
1233     }
1234
1235     if (const auto* installedFont = findClosestFont(familyFonts, request))
1236         return { adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr)), false };
1237
1238     return { nullptr };
1239 #endif
1240 }
1241
1242 static void invalidateFontCache()
1243 {
1244     if (!isMainThread()) {
1245         callOnMainThread([] {
1246             invalidateFontCache();
1247         });
1248         return;
1249     }
1250
1251     FontDescription::invalidateCaches();
1252
1253     FontDatabase::singletonAllowingUserInstalledFonts().clear();
1254     FontDatabase::singletonDisallowingUserInstalledFonts().clear();
1255
1256     FontCache::singleton().invalidate();
1257 }
1258
1259 static RetainPtr<CTFontRef> fontWithFamily(const AtomicString& family, const FontDescription& fontDescription, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities, float size)
1260 {
1261     if (family.isEmpty())
1262         return nullptr;
1263
1264     const auto& request = fontDescription.fontSelectionRequest();
1265     FontLookup fontLookup;
1266     fontLookup.result = platformFontWithFamilySpecialCase(family, request, size);
1267     if (!fontLookup.result)
1268         fontLookup = platformFontLookupWithFamily(family, request, size, fontDescription.mayRepresentUserInstalledFont());
1269     return preparePlatformFont(fontLookup.result.get(), fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, size, !fontLookup.createdFromPostScriptName);
1270 }
1271
1272 #if PLATFORM(MAC)
1273 static bool shouldAutoActivateFontIfNeeded(const AtomicString& family)
1274 {
1275 #ifndef NDEBUG
1276     // This cache is not thread safe so the following assertion is there to
1277     // make sure this function is always called from the same thread.
1278     static Thread* initThread = &Thread::current();
1279     ASSERT(initThread == &Thread::current());
1280 #endif
1281
1282     static NeverDestroyed<HashSet<AtomicString>> knownFamilies;
1283     static const unsigned maxCacheSize = 128;
1284     ASSERT(knownFamilies.get().size() <= maxCacheSize);
1285     if (knownFamilies.get().size() == maxCacheSize)
1286         knownFamilies.get().remove(knownFamilies.get().begin());
1287
1288     // Only attempt to auto-activate fonts once for performance reasons.
1289     return knownFamilies.get().add(family).isNewEntry;
1290 }
1291
1292 static void autoActivateFont(const String& name, CGFloat size)
1293 {
1294     auto fontName = name.createCFString();
1295     CFTypeRef keys[] = { kCTFontNameAttribute };
1296     CFTypeRef values[] = { fontName.get() };
1297     auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1298     auto descriptor = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
1299     if (auto newFont = CTFontCreateWithFontDescriptor(descriptor.get(), size, nullptr))
1300         CFRelease(newFont);
1301 }
1302 #endif
1303
1304 std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities)
1305 {
1306     float size = fontDescription.computedPixelSize();
1307
1308     auto font = fontWithFamily(family, fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, size);
1309
1310 #if PLATFORM(MAC)
1311     if (!font) {
1312         if (!shouldAutoActivateFontIfNeeded(family))
1313             return nullptr;
1314
1315         // Auto activate the font before looking for it a second time.
1316         // Ignore the result because we want to use our own algorithm to actually find the font.
1317         autoActivateFont(family.string(), size);
1318
1319         font = fontWithFamily(family, fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, size);
1320     }
1321 #endif
1322
1323     if (!font)
1324         return nullptr;
1325
1326     bool syntheticBold, syntheticOblique;
1327     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(font.get(), fontDescription).boldObliquePair();
1328
1329     return std::make_unique<FontPlatformData>(font.get(), size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant(), fontDescription.textRenderingMode());
1330 }
1331
1332 typedef HashSet<RetainPtr<CTFontRef>, WTF::RetainPtrObjectHash<CTFontRef>, WTF::RetainPtrObjectHashTraits<CTFontRef>> FallbackDedupSet;
1333 static FallbackDedupSet& fallbackDedupSet()
1334 {
1335     static NeverDestroyed<FallbackDedupSet> dedupSet;
1336     return dedupSet.get();
1337 }
1338
1339 void FontCache::platformPurgeInactiveFontData()
1340 {
1341     Vector<CTFontRef> toRemove;
1342     for (auto& font : fallbackDedupSet()) {
1343         if (CFGetRetainCount(font.get()) == 1)
1344             toRemove.append(font.get());
1345     }
1346     for (auto& font : toRemove)
1347         fallbackDedupSet().remove(font);
1348 }
1349
1350 #if PLATFORM(IOS)
1351 static inline bool isArabicCharacter(UChar character)
1352 {
1353     return character >= 0x0600 && character <= 0x06FF;
1354 }
1355 #endif
1356
1357 static RetainPtr<CTFontRef> lookupFallbackFont(CTFontRef font, FontSelectionValue fontWeight, const AtomicString& locale, const UChar* characters, unsigned length)
1358 {
1359     ASSERT(length > 0);
1360
1361     RetainPtr<CFStringRef> localeString;
1362 #if (PLATFORM(IOS) && TARGET_OS_IOS) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
1363     if (!locale.isNull())
1364         localeString = locale.string().createCFString();
1365 #else
1366     UNUSED_PARAM(locale);
1367 #endif
1368
1369     CFIndex coveredLength = 0;
1370     RetainPtr<CTFontRef> result;
1371 #if !USE_PLATFORM_SYSTEM_FALLBACK_LIST && ((PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200) || (PLATFORM(IOS) && TARGET_OS_IOS))
1372     result = adoptCF(CTFontCreatePhysicalFontForCharactersWithLanguage(font, characters, length, localeString.get(), &coveredLength));
1373 #else
1374     result = adoptCF(CTFontCreateForCharactersWithLanguage(font, characters, length, localeString.get(), &coveredLength));
1375 #endif
1376
1377 #if PLATFORM(IOS)
1378     // Callers of this function won't include multiple code points. "Length" is to know how many code units
1379     // are in the code point.
1380     UChar firstCharacter = characters[0];
1381     if (isArabicCharacter(firstCharacter)) {
1382         auto familyName = adoptCF(static_cast<CFStringRef>(CTFontCopyAttribute(result.get(), kCTFontFamilyNameAttribute)));
1383         if (fontFamilyShouldNotBeUsedForArabic(familyName.get())) {
1384             CFStringRef newFamilyName = isFontWeightBold(fontWeight) ? CFSTR("GeezaPro-Bold") : CFSTR("GeezaPro");
1385             CFTypeRef keys[] = { kCTFontNameAttribute };
1386             CFTypeRef values[] = { newFamilyName };
1387             auto attributes = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, keys, values, WTF_ARRAY_LENGTH(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1388             auto modification = adoptCF(CTFontDescriptorCreateWithAttributes(attributes.get()));
1389             result = adoptCF(CTFontCreateCopyWithAttributes(result.get(), CTFontGetSize(result.get()), nullptr, modification.get()));
1390         }
1391     }
1392 #else
1393     UNUSED_PARAM(fontWeight);
1394 #endif
1395
1396     return result;
1397 }
1398
1399 RefPtr<Font> FontCache::systemFallbackForCharacters(const FontDescription& description, const Font* originalFontData, bool isPlatformFont, const UChar* characters, unsigned length)
1400 {
1401 #if PLATFORM(IOS)
1402     if (length && requiresCustomFallbackFont(*characters)) {
1403         auto* fallback = getCustomFallbackFont(*characters, description);
1404         if (!fallback)
1405             return nullptr;
1406         return fontForPlatformData(*fallback);
1407     }
1408 #endif
1409
1410     const FontPlatformData& platformData = originalFontData->platformData();
1411     auto result = lookupFallbackFont(platformData.font(), description.weight(), description.locale(), characters, length);
1412     result = preparePlatformFont(result.get(), description, nullptr, nullptr, { }, description.computedSize());
1413     if (!result)
1414         return lastResortFallbackFont(description);
1415
1416     // FontCascade::drawGlyphBuffer() requires that there are no duplicate Font objects which refer to the same thing. This is enforced in
1417     // FontCache::fontForPlatformData(), where our equality check is based on hashing the FontPlatformData, whose hash includes the raw CoreText
1418     // font pointer.
1419     CTFontRef substituteFont = fallbackDedupSet().add(result).iterator->get();
1420
1421     bool syntheticBold, syntheticOblique;
1422     std::tie(syntheticBold, syntheticOblique) = computeNecessarySynthesis(substituteFont, description, isPlatformFont).boldObliquePair();
1423
1424     FontPlatformData alternateFont(substituteFont, platformData.size(), syntheticBold, syntheticOblique, platformData.orientation(), platformData.widthVariant(), platformData.textRenderingMode());
1425
1426     return fontForPlatformData(alternateFont);
1427 }
1428
1429 const AtomicString& FontCache::platformAlternateFamilyName(const AtomicString& familyName)
1430 {
1431     static const UChar heitiString[] = { 0x9ed1, 0x4f53 };
1432     static const UChar songtiString[] = { 0x5b8b, 0x4f53 };
1433     static const UChar weiruanXinXiMingTi[] = { 0x5fae, 0x8edf, 0x65b0, 0x7d30, 0x660e, 0x9ad4 };
1434     static const UChar weiruanYaHeiString[] = { 0x5fae, 0x8f6f, 0x96c5, 0x9ed1 };
1435     static const UChar weiruanZhengHeitiString[] = { 0x5fae, 0x8edf, 0x6b63, 0x9ed1, 0x9ad4 };
1436
1437     static NeverDestroyed<AtomicString> songtiSC("Songti SC", AtomicString::ConstructFromLiteral);
1438     static NeverDestroyed<AtomicString> songtiTC("Songti TC", AtomicString::ConstructFromLiteral);
1439     static NeverDestroyed<AtomicString> heitiSCReplacement("PingFang SC", AtomicString::ConstructFromLiteral);
1440     static NeverDestroyed<AtomicString> heitiTCReplacement("PingFang TC", AtomicString::ConstructFromLiteral);
1441
1442     switch (familyName.length()) {
1443     case 2:
1444         if (equal(familyName, songtiString))
1445             return songtiSC;
1446         if (equal(familyName, heitiString))
1447             return heitiSCReplacement;
1448         break;
1449     case 4:
1450         if (equal(familyName, weiruanYaHeiString))
1451             return heitiSCReplacement;
1452         break;
1453     case 5:
1454         if (equal(familyName, weiruanZhengHeitiString))
1455             return heitiTCReplacement;
1456         break;
1457     case 6:
1458         if (equalLettersIgnoringASCIICase(familyName, "simsun"))
1459             return songtiSC;
1460         if (equal(familyName, weiruanXinXiMingTi))
1461             return songtiTC;
1462         break;
1463     case 10:
1464         if (equalLettersIgnoringASCIICase(familyName, "ms mingliu"))
1465             return songtiTC;
1466         if (equalIgnoringASCIICase(familyName, "\\5b8b\\4f53"))
1467             return songtiSC;
1468         break;
1469     case 18:
1470         if (equalLettersIgnoringASCIICase(familyName, "microsoft jhenghei"))
1471             return heitiTCReplacement;
1472         break;
1473     }
1474
1475     return nullAtom();
1476 }
1477
1478 }