Rename AtomicString to AtomString
[WebKit-https.git] / Source / WebCore / platform / graphics / FontCascadeFonts.cpp
1 /*
2  * Copyright (C) 2006, 2013-2015 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "FontCascadeFonts.h"
31
32 #include "FontCache.h"
33 #include "FontCascade.h"
34 #include "GlyphPage.h"
35
36 namespace WebCore {
37
38 class MixedFontGlyphPage {
39     WTF_MAKE_FAST_ALLOCATED;
40 public:
41     MixedFontGlyphPage(const GlyphPage* initialPage)
42     {
43         if (initialPage) {
44             for (unsigned i = 0; i < GlyphPage::size; ++i)
45                 setGlyphDataForIndex(i, initialPage->glyphDataForIndex(i));
46         }
47     }
48
49     GlyphData glyphDataForCharacter(UChar32 c) const
50     {
51         unsigned index = GlyphPage::indexForCodePoint(c);
52         ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size);
53         return { m_glyphs[index], m_fonts[index] };
54     }
55
56     void setGlyphDataForCharacter(UChar32 c, GlyphData glyphData)
57     {
58         setGlyphDataForIndex(GlyphPage::indexForCodePoint(c), glyphData);
59     }
60
61 private:
62     void setGlyphDataForIndex(unsigned index, const GlyphData& glyphData)
63     {
64         ASSERT_WITH_SECURITY_IMPLICATION(index < GlyphPage::size);
65         m_glyphs[index] = glyphData.glyph;
66         m_fonts[index] = glyphData.font;
67     }
68
69     Glyph m_glyphs[GlyphPage::size] { };
70     const Font* m_fonts[GlyphPage::size] { };
71 };
72
73 GlyphData FontCascadeFonts::GlyphPageCacheEntry::glyphDataForCharacter(UChar32 character)
74 {
75     ASSERT(!(m_singleFont && m_mixedFont));
76     if (m_singleFont)
77         return m_singleFont->glyphDataForCharacter(character);
78     if (m_mixedFont)
79         return m_mixedFont->glyphDataForCharacter(character);
80     return 0;
81 }
82
83 void FontCascadeFonts::GlyphPageCacheEntry::setGlyphDataForCharacter(UChar32 character, GlyphData glyphData)
84 {
85     ASSERT(!glyphDataForCharacter(character).glyph);
86     if (!m_mixedFont) {
87         m_mixedFont = std::make_unique<MixedFontGlyphPage>(m_singleFont.get());
88         m_singleFont = nullptr;
89     }
90     m_mixedFont->setGlyphDataForCharacter(character, glyphData);
91 }
92
93 void FontCascadeFonts::GlyphPageCacheEntry::setSingleFontPage(RefPtr<GlyphPage>&& page)
94 {
95     ASSERT(isNull());
96     m_singleFont = page;
97 }
98
99 FontCascadeFonts::FontCascadeFonts(RefPtr<FontSelector>&& fontSelector)
100     : m_cachedPrimaryFont(nullptr)
101     , m_fontSelector(fontSelector)
102     , m_fontSelectorVersion(m_fontSelector ? m_fontSelector->version() : 0)
103     , m_generation(FontCache::singleton().generation())
104 {
105 }
106
107 FontCascadeFonts::FontCascadeFonts(const FontPlatformData& platformData)
108     : m_cachedPrimaryFont(nullptr)
109     , m_fontSelectorVersion(0)
110     , m_generation(FontCache::singleton().generation())
111     , m_isForPlatformFont(true)
112 {
113     m_realizedFallbackRanges.append(FontRanges(FontCache::singleton().fontForPlatformData(platformData)));
114 }
115
116 FontCascadeFonts::~FontCascadeFonts() = default;
117
118 void FontCascadeFonts::determinePitch(const FontCascadeDescription& description)
119 {
120     auto& primaryRanges = realizeFallbackRangesAt(description, 0);
121     unsigned numRanges = primaryRanges.size();
122     if (numRanges == 1)
123         m_pitch = primaryRanges.fontForFirstRange().pitch();
124     else
125         m_pitch = VariablePitch;
126 }
127
128 bool FontCascadeFonts::isLoadingCustomFonts() const
129 {
130     for (auto& fontRanges : m_realizedFallbackRanges) {
131         if (fontRanges.isLoading())
132             return true;
133     }
134     return false;
135 }
136
137 static FontRanges realizeNextFallback(const FontCascadeDescription& description, unsigned& index, FontSelector* fontSelector)
138 {
139     ASSERT(index < description.effectiveFamilyCount());
140
141     auto& fontCache = FontCache::singleton();
142     while (index < description.effectiveFamilyCount()) {
143         auto visitor = WTF::makeVisitor([&](const AtomString& family) -> FontRanges {
144             if (family.isEmpty())
145                 return FontRanges();
146             if (fontSelector) {
147                 auto ranges = fontSelector->fontRangesForFamily(description, family);
148                 if (!ranges.isNull())
149                     return ranges;
150             }
151             if (auto font = fontCache.fontForFamily(description, family))
152                 return FontRanges(WTFMove(font));
153             return FontRanges();
154         }, [&](const FontFamilyPlatformSpecification& fontFamilySpecification) -> FontRanges {
155             return fontFamilySpecification.fontRanges(description);
156         });
157         const auto& currentFamily = description.effectiveFamilyAt(index++);
158         auto ranges = WTF::visit(visitor, currentFamily);
159         if (!ranges.isNull())
160             return ranges;
161     }
162     // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
163     // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
164     // Geeza Pro font.
165     for (auto& family : description.families()) {
166         if (auto font = fontCache.similarFont(description, family))
167             return FontRanges(WTFMove(font));
168     }
169     return { };
170 }
171
172 const FontRanges& FontCascadeFonts::realizeFallbackRangesAt(const FontCascadeDescription& description, unsigned index)
173 {
174     if (index < m_realizedFallbackRanges.size())
175         return m_realizedFallbackRanges[index];
176
177     ASSERT(index == m_realizedFallbackRanges.size());
178     ASSERT(FontCache::singleton().generation() == m_generation);
179
180     m_realizedFallbackRanges.append(FontRanges());
181     auto& fontRanges = m_realizedFallbackRanges.last();
182
183     if (!index) {
184         fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get());
185         if (fontRanges.isNull() && m_fontSelector)
186             fontRanges = m_fontSelector->fontRangesForFamily(description, standardFamily);
187         if (fontRanges.isNull())
188             fontRanges = FontRanges(FontCache::singleton().lastResortFallbackFont(description));
189         return fontRanges;
190     }
191
192     if (m_lastRealizedFallbackIndex < description.effectiveFamilyCount())
193         fontRanges = realizeNextFallback(description, m_lastRealizedFallbackIndex, m_fontSelector.get());
194
195     if (fontRanges.isNull() && m_fontSelector) {
196         ASSERT(m_lastRealizedFallbackIndex >= description.effectiveFamilyCount());
197
198         unsigned fontSelectorFallbackIndex = m_lastRealizedFallbackIndex - description.effectiveFamilyCount();
199         if (fontSelectorFallbackIndex == m_fontSelector->fallbackFontCount())
200             return fontRanges;
201         ++m_lastRealizedFallbackIndex;
202         fontRanges = FontRanges(m_fontSelector->fallbackFontAt(description, fontSelectorFallbackIndex));
203     }
204
205     return fontRanges;
206 }
207
208 static inline bool isInRange(UChar32 character, UChar32 lowerBound, UChar32 upperBound)
209 {
210     return character >= lowerBound && character <= upperBound;
211 }
212
213 static bool shouldIgnoreRotation(UChar32 character)
214 {
215     if (character == 0x000A7 || character == 0x000A9 || character == 0x000AE)
216         return true;
217
218     if (character == 0x000B6 || character == 0x000BC || character == 0x000BD || character == 0x000BE)
219         return true;
220
221     if (isInRange(character, 0x002E5, 0x002EB))
222         return true;
223
224     if (isInRange(character, 0x01100, 0x011FF) || isInRange(character, 0x01401, 0x0167F) || isInRange(character, 0x01800, 0x018FF))
225         return true;
226
227     if (character == 0x02016 || character == 0x02020 || character == 0x02021 || character == 0x2030 || character == 0x02031)
228         return true;
229
230     if (isInRange(character, 0x0203B, 0x0203D) || character == 0x02042 || character == 0x02044 || character == 0x02047
231         || character == 0x02048 || character == 0x02049 || character == 0x2051)
232         return true;
233
234     if (isInRange(character, 0x02065, 0x02069) || isInRange(character, 0x020DD, 0x020E0)
235         || isInRange(character, 0x020E2, 0x020E4) || isInRange(character, 0x02100, 0x02117)
236         || isInRange(character, 0x02119, 0x02131) || isInRange(character, 0x02133, 0x0213F))
237         return true;
238
239     if (isInRange(character, 0x02145, 0x0214A) || character == 0x0214C || character == 0x0214D
240         || isInRange(character, 0x0214F, 0x0218F))
241         return true;
242
243     if (isInRange(character, 0x02300, 0x02307) || isInRange(character, 0x0230C, 0x0231F)
244         || isInRange(character, 0x02322, 0x0232B) || isInRange(character, 0x0237D, 0x0239A)
245         || isInRange(character, 0x023B4, 0x023B6) || isInRange(character, 0x023BA, 0x023CF)
246         || isInRange(character, 0x023D1, 0x023DB) || isInRange(character, 0x023E2, 0x024FF))
247         return true;
248
249     if (isInRange(character, 0x025A0, 0x02619) || isInRange(character, 0x02620, 0x02767)
250         || isInRange(character, 0x02776, 0x02793) || isInRange(character, 0x02B12, 0x02B2F)
251         || isInRange(character, 0x02B4D, 0x02BFF) || isInRange(character, 0x02E80, 0x03007))
252         return true;
253
254     if (character == 0x03012 || character == 0x03013 || isInRange(character, 0x03020, 0x0302F)
255         || isInRange(character, 0x03031, 0x0309F) || isInRange(character, 0x030A1, 0x030FB)
256         || isInRange(character, 0x030FD, 0x0A4CF))
257         return true;
258
259     if (isInRange(character, 0x0A840, 0x0A87F) || isInRange(character, 0x0A960, 0x0A97F)
260         || isInRange(character, 0x0AC00, 0x0D7FF) || isInRange(character, 0x0E000, 0x0FAFF))
261         return true;
262
263     if (isInRange(character, 0x0FE10, 0x0FE1F) || isInRange(character, 0x0FE30, 0x0FE48)
264         || isInRange(character, 0x0FE50, 0x0FE57) || isInRange(character, 0x0FE5F, 0x0FE62)
265         || isInRange(character, 0x0FE67, 0x0FE6F))
266         return true;
267
268     if (isInRange(character, 0x0FF01, 0x0FF07) || isInRange(character, 0x0FF0A, 0x0FF0C)
269         || isInRange(character, 0x0FF0E, 0x0FF19) || character == 0x0FF1B || isInRange(character, 0x0FF1F, 0x0FF3A))
270         return true;
271
272     if (character == 0x0FF3C || character == 0x0FF3E)
273         return true;
274
275     if (isInRange(character, 0x0FF40, 0x0FF5A) || isInRange(character, 0x0FFE0, 0x0FFE2)
276         || isInRange(character, 0x0FFE4, 0x0FFE7) || isInRange(character, 0x0FFF0, 0x0FFF8)
277         || character == 0x0FFFD)
278         return true;
279
280     if (isInRange(character, 0x13000, 0x1342F) || isInRange(character, 0x1B000, 0x1B0FF)
281         || isInRange(character, 0x1D000, 0x1D1FF) || isInRange(character, 0x1D300, 0x1D37F)
282         || isInRange(character, 0x1F000, 0x1F64F) || isInRange(character, 0x1F680, 0x1F77F))
283         return true;
284
285     if (isInRange(character, 0x20000, 0x2FFFD) || isInRange(character, 0x30000, 0x3FFFD))
286         return true;
287
288     return false;
289 }
290
291 static GlyphData glyphDataForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, const GlyphData& data)
292 {
293     bool syntheticOblique = data.font->platformData().syntheticOblique();
294     if (orientation == NonCJKGlyphOrientation::Upright || shouldIgnoreRotation(character)) {
295         GlyphData uprightData = data.font->uprightOrientationFont().glyphDataForCharacter(character);
296         // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically
297         // to be upright. For synthetic oblique, however, we will always return the uprightData to ensure
298         // that non-CJK and CJK runs are broken up. This guarantees that vertical
299         // fonts without isTextOrientationFallback() set contain CJK characters only and thus we can get
300         // the oblique slant correct.
301         if (data.glyph == uprightData.glyph && !syntheticOblique)
302             return data;
303         // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that
304         // glyph, so we fall back to the upright data and use the horizontal glyph.
305         if (uprightData.font)
306             return uprightData;
307     } else if (orientation == NonCJKGlyphOrientation::Mixed) {
308         GlyphData verticalRightData = data.font->verticalRightOrientationFont().glyphDataForCharacter(character);
309
310         // If there is a baked-in rotated glyph, we will use it unless syntheticOblique is set. If
311         // synthetic oblique is set, we fall back to the horizontal glyph. This guarantees that vertical
312         // fonts without isTextOrientationFallback() set contain CJK characters only and thus we can get
313         // the oblique slant correct.
314         if (data.glyph != verticalRightData.glyph && !syntheticOblique)
315             return data;
316
317         // The glyphs are identical, meaning that we should just use the horizontal glyph.
318         if (verticalRightData.font)
319             return verticalRightData;
320     }
321     return data;
322 }
323
324 static const Font* findBestFallbackFont(FontCascadeFonts& fontCascadeFonts, const FontCascadeDescription& description, UChar32 character)
325 {
326     for (unsigned fallbackIndex = 0; ; ++fallbackIndex) {
327         auto& fontRanges = fontCascadeFonts.realizeFallbackRangesAt(description, fallbackIndex);
328         if (fontRanges.isNull())
329             break;
330         auto* currentFont = fontRanges.glyphDataForCharacter(character, ExternalResourceDownloadPolicy::Forbid).font;
331         if (!currentFont)
332             currentFont = &fontRanges.fontForFirstRange();
333
334         if (!currentFont->isInterstitial())
335             return currentFont;
336     }
337
338     return nullptr;
339 }
340
341 GlyphData FontCascadeFonts::glyphDataForSystemFallback(UChar32 character, const FontCascadeDescription& description, FontVariant variant, bool systemFallbackShouldBeInvisible)
342 {
343     const Font* font = findBestFallbackFont(*this, description, character);
344
345     if (!font)
346         font = &realizeFallbackRangesAt(description, 0).fontForFirstRange();
347
348     auto systemFallbackFont = font->systemFallbackFontForCharacter(character, description, m_isForPlatformFont ? IsForPlatformFont::Yes : IsForPlatformFont::No);
349     if (!systemFallbackFont)
350         return GlyphData();
351
352     if (systemFallbackShouldBeInvisible)
353         systemFallbackFont = const_cast<Font*>(&systemFallbackFont->invisibleFont());
354
355     if (systemFallbackFont->platformData().orientation() == FontOrientation::Vertical && !systemFallbackFont->hasVerticalGlyphs() && FontCascade::isCJKIdeographOrSymbol(character))
356         variant = BrokenIdeographVariant;
357
358     GlyphData fallbackGlyphData;
359     if (variant == NormalVariant)
360         fallbackGlyphData = systemFallbackFont->glyphDataForCharacter(character);
361     else
362         fallbackGlyphData = systemFallbackFont->variantFont(description, variant)->glyphDataForCharacter(character);
363
364     if (fallbackGlyphData.font && fallbackGlyphData.font->platformData().orientation() == FontOrientation::Vertical && !fallbackGlyphData.font->isTextOrientationFallback()) {
365         if (variant == NormalVariant && !FontCascade::isCJKIdeographOrSymbol(character))
366             fallbackGlyphData = glyphDataForNonCJKCharacterWithGlyphOrientation(character, description.nonCJKGlyphOrientation(), fallbackGlyphData);
367     }
368
369     // Keep the system fallback fonts we use alive.
370     if (fallbackGlyphData.glyph)
371         m_systemFallbackFontSet.add(WTFMove(systemFallbackFont));
372
373     return fallbackGlyphData;
374 }
375
376 enum class FallbackVisibility {
377     Immaterial,
378     Visible,
379     Invisible
380 };
381
382 static void opportunisticallyStartFontDataURLLoading(const FontCascadeDescription& description, FontSelector* fontSelector)
383 {
384     // It is a somewhat common practice for a font foundry to break up a single font into two fonts, each having a random half of
385     // the alphabet, and then encoding the two fonts as data: urls (with different font-family names).
386     // Therefore, if these two fonts don't get loaded at (nearly) the same time, there will be a flash of unintelligible text where
387     // only a random half of the letters are visible.
388     // This code attempts to pre-warm these data urls to make them load at closer to the same time. However, font loading is
389     // asynchronous, and this code doesn't actually fix the race - it just makes it more likely for the two fonts to tie in the race.
390     if (!fontSelector)
391         return;
392     for (unsigned i = 0; i < description.familyCount(); ++i)
393         fontSelector->opportunisticallyStartFontDataURLLoading(description, description.familyAt(i));
394 }
395
396 GlyphData FontCascadeFonts::glyphDataForVariant(UChar32 character, const FontCascadeDescription& description, FontVariant variant, unsigned fallbackIndex)
397 {
398     FallbackVisibility fallbackVisibility = FallbackVisibility::Immaterial;
399     ExternalResourceDownloadPolicy policy = ExternalResourceDownloadPolicy::Allow;
400     GlyphData loadingResult;
401     opportunisticallyStartFontDataURLLoading(description, m_fontSelector.get());
402     for (; ; ++fallbackIndex) {
403         auto& fontRanges = realizeFallbackRangesAt(description, fallbackIndex);
404         if (fontRanges.isNull())
405             break;
406
407         GlyphData data = fontRanges.glyphDataForCharacter(character, policy);
408         if (!data.font)
409             continue;
410
411         if (data.font->isInterstitial()) {
412             policy = ExternalResourceDownloadPolicy::Forbid;
413             if (fallbackVisibility == FallbackVisibility::Immaterial)
414                 fallbackVisibility = data.font->visibility() == Font::Visibility::Visible ? FallbackVisibility::Visible : FallbackVisibility::Invisible;
415             if (!loadingResult.font && data.glyph)
416                 loadingResult = data;
417             continue;
418         }
419
420         if (fallbackVisibility == FallbackVisibility::Invisible && data.font->visibility() == Font::Visibility::Visible)
421             data.font = &data.font->invisibleFont();
422
423         if (variant == NormalVariant) {
424             if (data.font->platformData().orientation() == FontOrientation::Vertical && !data.font->isTextOrientationFallback()) {
425                 if (!FontCascade::isCJKIdeographOrSymbol(character))
426                     return glyphDataForNonCJKCharacterWithGlyphOrientation(character, description.nonCJKGlyphOrientation(), data);
427
428                 if (!data.font->hasVerticalGlyphs()) {
429                     // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs
430                     // to make sure you get a square (even for broken glyphs like symbols used for punctuation).
431                     return glyphDataForVariant(character, description, BrokenIdeographVariant, fallbackIndex);
432                 }
433             }
434         } else {
435             // The variantFont function should not normally return 0.
436             // But if it does, we will just render the capital letter big.
437             if (const Font* variantFont = data.font->variantFont(description, variant))
438                 return variantFont->glyphDataForCharacter(character);
439         }
440
441         return data;
442     }
443
444     if (loadingResult.font)
445         return loadingResult;
446     return glyphDataForSystemFallback(character, description, variant, fallbackVisibility == FallbackVisibility::Invisible);
447 }
448
449 static RefPtr<GlyphPage> glyphPageFromFontRanges(unsigned pageNumber, const FontRanges& fontRanges)
450 {
451     const Font* font = nullptr;
452     UChar32 pageRangeFrom = pageNumber * GlyphPage::size;
453     UChar32 pageRangeTo = pageRangeFrom + GlyphPage::size - 1;
454     auto policy = ExternalResourceDownloadPolicy::Allow;
455     FallbackVisibility desiredVisibility = FallbackVisibility::Immaterial;
456     for (unsigned i = 0; i < fontRanges.size(); ++i) {
457         auto& range = fontRanges.rangeAt(i);
458         if (range.from() <= pageRangeFrom && pageRangeTo <= range.to()) {
459             font = range.font(policy);
460             if (!font)
461                 continue;
462             if (font->isInterstitial()) {
463                 if (desiredVisibility == FallbackVisibility::Immaterial) {
464                     auto fontVisibility = font->visibility();
465                     if (fontVisibility == Font::Visibility::Visible)
466                         desiredVisibility = FallbackVisibility::Visible;
467                     else {
468                         ASSERT(fontVisibility == Font::Visibility::Invisible);
469                         desiredVisibility = FallbackVisibility::Invisible;
470                     }
471                 }
472                 font = nullptr;
473                 policy = ExternalResourceDownloadPolicy::Forbid;
474                 continue;
475             }
476         }
477         break;
478     }
479     if (!font || font->platformData().orientation() == FontOrientation::Vertical)
480         return nullptr;
481
482     if (desiredVisibility == FallbackVisibility::Invisible && font->visibility() == Font::Visibility::Visible)
483         return const_cast<GlyphPage*>(font->invisibleFont().glyphPage(pageNumber));
484     return const_cast<GlyphPage*>(font->glyphPage(pageNumber));
485 }
486
487 GlyphData FontCascadeFonts::glyphDataForCharacter(UChar32 c, const FontCascadeDescription& description, FontVariant variant)
488 {
489     ASSERT(isMainThread());
490     ASSERT(variant != AutoVariant);
491
492     if (variant != NormalVariant)
493         return glyphDataForVariant(c, description, variant);
494
495     const unsigned pageNumber = GlyphPage::pageNumberForCodePoint(c);
496
497     auto& cacheEntry = pageNumber ? m_cachedPages.add(pageNumber, GlyphPageCacheEntry()).iterator->value : m_cachedPageZero;
498
499     // Initialize cache with a full page of glyph mappings from a single font.
500     if (cacheEntry.isNull())
501         cacheEntry.setSingleFontPage(glyphPageFromFontRanges(pageNumber, realizeFallbackRangesAt(description, 0)));
502
503     GlyphData glyphData = cacheEntry.glyphDataForCharacter(c);
504     if (!glyphData.glyph) {
505         // No glyph, resolve per-character.
506         ASSERT(variant == NormalVariant);
507         glyphData = glyphDataForVariant(c, description, variant);
508         // Cache the results.
509         cacheEntry.setGlyphDataForCharacter(c, glyphData);
510     }
511
512     return glyphData;
513 }
514
515 void FontCascadeFonts::pruneSystemFallbacks()
516 {
517     if (m_systemFallbackFontSet.isEmpty())
518         return;
519     // Mutable glyph pages may reference fallback fonts.
520     if (m_cachedPageZero.isMixedFont())
521         m_cachedPageZero = { };
522     m_cachedPages.removeIf([](auto& keyAndValue) {
523         return keyAndValue.value.isMixedFont();
524     });
525     m_systemFallbackFontSet.clear();
526 }
527
528 }